SVG Graph, compressed statics ... and much more
This commit is contained in:
1
build.rs
1
build.rs
@@ -1,3 +1,4 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rustc-link-arg-bins=-Tlinkall.x");
|
println!("cargo:rustc-link-arg-bins=-Tlinkall.x");
|
||||||
|
println!("cargo:rustc-env=CARGO_PKG_VERSION_SAFE={}", env!("CARGO_PKG_VERSION").replace(".", "_"));
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/httpd.rs
13
src/httpd.rs
@@ -12,6 +12,9 @@ use include_file_compress::include_file_compress_deflate;
|
|||||||
|
|
||||||
use crate::database::DATAPOINT_BUFFER;
|
use crate::database::DATAPOINT_BUFFER;
|
||||||
|
|
||||||
|
const STYLES_CSS_FILENAME: &str = concat!("/styles-", env!("CARGO_PKG_VERSION_SAFE"), ".css");
|
||||||
|
const APP_JS_FILENAME: &str = concat!("/app-", env!("CARGO_PKG_VERSION_SAFE"), ".css");
|
||||||
|
|
||||||
static PICO_CONFIG : picoserve::Config<Duration> = picoserve::Config::new(
|
static PICO_CONFIG : picoserve::Config<Duration> = picoserve::Config::new(
|
||||||
picoserve::Timeouts {
|
picoserve::Timeouts {
|
||||||
start_read_request: Some(Duration::from_secs(5)),
|
start_read_request: Some(Duration::from_secs(5)),
|
||||||
@@ -52,9 +55,9 @@ async fn web_task(
|
|||||||
picoserve::Router::new()
|
picoserve::Router::new()
|
||||||
.nest("/api/v1", api_router)
|
.nest("/api/v1", api_router)
|
||||||
.route("/", get(|| async { index() }))
|
.route("/", get(|| async { index() }))
|
||||||
.route("/styles.css", get_service(File::with_content_type_and_headers(&CSS_CONTENT_TYPE,
|
.route(STYLES_CSS_FILENAME, get_service(File::with_content_type_and_headers(&CSS_CONTENT_TYPE,
|
||||||
include_file_compress_deflate!("src/static/styles.css", 5), DEFLATE_CACHEABLE_CONTENT_HEADERS)))
|
include_file_compress_deflate!("src/static/styles.css", 5), DEFLATE_CACHEABLE_CONTENT_HEADERS)))
|
||||||
.route("/app.js", get_service(File::with_content_type_and_headers(&JS_CONTENT_TYPE,
|
.route(APP_JS_FILENAME, get_service(File::with_content_type_and_headers(&JS_CONTENT_TYPE,
|
||||||
include_file_compress_deflate!("src/static/app.js", 5), DEFLATE_CACHEABLE_CONTENT_HEADERS)))
|
include_file_compress_deflate!("src/static/app.js", 5), DEFLATE_CACHEABLE_CONTENT_HEADERS)))
|
||||||
.nest("/images", image_router);
|
.nest("/images", image_router);
|
||||||
|
|
||||||
@@ -81,8 +84,8 @@ fn page(heading: &str, content: Markup) -> Markup {
|
|||||||
(DOCTYPE)
|
(DOCTYPE)
|
||||||
html {
|
html {
|
||||||
head {
|
head {
|
||||||
link rel="stylesheet" type="text/css" href="/styles.css";
|
link rel="stylesheet" type="text/css" href=(STYLES_CSS_FILENAME);
|
||||||
script src="app.js" {};
|
script src=(APP_JS_FILENAME) {};
|
||||||
title { (heading) }
|
title { (heading) }
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
@@ -191,7 +194,7 @@ fn latest() -> impl IntoResponse {
|
|||||||
fn index() -> impl IntoResponse {
|
fn index() -> impl IntoResponse {
|
||||||
page("Wallas 22GB Wifi Extension", html! {
|
page("Wallas 22GB Wifi Extension", html! {
|
||||||
p .intro { "Beta version of ESP32C3 based Wifi extension to the Wallas 361062 Control Panel for the DT/GB Heaters" }
|
p .intro { "Beta version of ESP32C3 based Wifi extension to the Wallas 361062 Control Panel for the DT/GB Heaters" }
|
||||||
|
p #graph { "Waiting for data ..." }
|
||||||
p #latest { "Waiting for latest reading ..." }
|
p #latest { "Waiting for latest reading ..." }
|
||||||
// p { "Time is now " (get_now()) }
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ mod database;
|
|||||||
use database::database_spawn;
|
use database::database_spawn;
|
||||||
|
|
||||||
|
|
||||||
|
/// CARGO_PKG_VERSION
|
||||||
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
pub const VERSION_SAFE: &str = env!("CARGO_PKG_VERSION_SAFE");
|
||||||
|
|
||||||
/// SSID for WiFi network
|
/// SSID for WiFi network
|
||||||
const WIFI_SSID: &str = env!("WIFI_SSID");
|
const WIFI_SSID: &str = env!("WIFI_SSID");
|
||||||
|
|
||||||
@@ -78,7 +82,7 @@ const MAX_CONCURRENT_SOCKETS: usize = HTTPD_SOCKETS + DHCP_SOCKETS + DNS_SOCKETS
|
|||||||
#[main]
|
#[main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
|
|
||||||
info!("booting firmware");
|
info!("booting firmware version {}", VERSION);
|
||||||
|
|
||||||
if let Err(error) = main_fallible(spawner).await {
|
if let Err(error) = main_fallible(spawner).await {
|
||||||
error!("Error while running firmware: {error:?}");
|
error!("Error while running firmware: {error:?}");
|
||||||
|
|||||||
@@ -4,20 +4,69 @@ function sleep(ms) {
|
|||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => DOMLoaded(), false);
|
document.addEventListener("DOMContentLoaded", () => LoadCurrent(), false);
|
||||||
|
document.addEventListener("DOMContentLoaded", () => LoadGraph(), false);
|
||||||
|
|
||||||
async function DOMLoaded() {
|
async function LoadCurrent() {
|
||||||
await sleep(1000);
|
await sleep(500);
|
||||||
while (true) {
|
while (true) {
|
||||||
fetch("/api/v1/latest").then((response) => response.json()).then((json) => {
|
fetch("/api/v1/latest").then((response) => response.json()).then((json) => {
|
||||||
if (json.time) {
|
if (json.time) {
|
||||||
time = json.time;
|
time = json.time;
|
||||||
temperature = json.temperature;
|
temperature = json.temperature;
|
||||||
target = json.target;
|
target = json.target;
|
||||||
document.getElementById("latest").innerHTML = `Temperature was ${temperature}°C at ${time} UTC, target temperature was ${target}°C`;
|
document.getElementById("latest").innerHTML = `Temperature was ${temperature}°C at ${time} UTC, target temperature was ${target}°C`;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
await sleep(10000);
|
await sleep(10000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const svg_xmlns = "http://www.w3.org/2000/svg";
|
||||||
|
function appendSVGElement(parent, name, attributes) {
|
||||||
|
var elem = document.createElementNS(svg_xmlns, name);
|
||||||
|
for (var aname in attributes) {
|
||||||
|
elem.setAttributeNS(null, aname, attributes[aname]);
|
||||||
|
}
|
||||||
|
parent.appendChild(elem);
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function LoadGraph() {
|
||||||
|
await sleep(1000);
|
||||||
|
while (true) {
|
||||||
|
fetch("/api/v1/allreadings").then((response) => response.json()).then((json) => {
|
||||||
|
if (json instanceof Array && json.length > 0) {
|
||||||
|
var graph = document.createDocumentFragment();
|
||||||
|
var svg = appendSVGElement(graph, "svg", {"width": "100%", "height": "100%", "viewBox": "0 0 100 35"});
|
||||||
|
var x = 99;
|
||||||
|
var target_points = [];
|
||||||
|
var temperature_points = [];
|
||||||
|
for (var i = json.length - 1; i > 0; i--) {
|
||||||
|
var r = json[i];
|
||||||
|
var temperature_y = 25 - r.temperature;
|
||||||
|
temperature_points.push(`${x},${temperature_y}`);
|
||||||
|
var target_y = 25 - r.target;
|
||||||
|
target_points.push(`${x},${target_y}`);
|
||||||
|
x = x - 1;
|
||||||
|
}
|
||||||
|
temperature_points_str = temperature_points.join(" ");
|
||||||
|
target_points_str = target_points.join(" ");
|
||||||
|
|
||||||
|
// zero line
|
||||||
|
appendSVGElement(svg, "polyline", {"stroke-width": "0.1", "fill": "none", "stroke":"#000080", "stroke-linecap": "round", "stroke-dasharray": "5,2", "points": "1,25 99,25"});
|
||||||
|
// ten line
|
||||||
|
appendSVGElement(svg, "polyline", {"stroke-width": "0.05", "fill": "none", "stroke":"#000070", "stroke-linecap": "round", "stroke-dasharray": "5,2", "points": "1,15 99,15"});
|
||||||
|
// twenty line
|
||||||
|
appendSVGElement(svg, "polyline", {"stroke-width": "0.05", "fill": "none", "stroke":"#000080", "stroke-linecap": "round", "stroke-dasharray": "5,2", "points": "1,5 99,5"});
|
||||||
|
|
||||||
|
target_polyline = appendSVGElement(svg, "polyline", {"stroke-width": "1", "fill": "none", "stroke": "#007900", "points": target_points_str});
|
||||||
|
temperature_polyline = appendSVGElement(svg, "polyline", {"stroke-width": "1", "fill": "none", "stroke": "#0079d9", "points": temperature_points_str});
|
||||||
|
|
||||||
|
document.getElementById("graph").replaceChildren(graph);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await sleep(10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ p.intro {
|
|||||||
padding: 0.2em;
|
padding: 0.2em;
|
||||||
margin: 0.3em 10vw 0.3em 10vw;
|
margin: 0.3em 10vw 0.3em 10vw;
|
||||||
}
|
}
|
||||||
|
p#graph {
|
||||||
|
border: 1px solid #B15C1B;
|
||||||
|
padding: 0.2em;
|
||||||
|
margin: 0.3em 15vw 0.3em 15vw;
|
||||||
|
height: 40vw;
|
||||||
|
line-height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
p#latest {
|
p#latest {
|
||||||
border: 1px solid #B15C1B;
|
border: 1px solid #B15C1B;
|
||||||
padding: 0.2em;
|
padding: 0.2em;
|
||||||
|
|||||||
Reference in New Issue
Block a user