Implement chart rendering
This commit is contained in:
parent
0c33986628
commit
4cf0c7ff48
6 changed files with 93 additions and 12 deletions
|
@ -51,6 +51,7 @@ async function startup() {
|
|||
|
||||
let row_device = document.createElement('tr');
|
||||
|
||||
// create device name column
|
||||
let device_name_entry = document.createElement('td');
|
||||
let device_name = document.createElement('input');
|
||||
device_name.value = device_descriptor;
|
||||
|
@ -73,6 +74,7 @@ async function startup() {
|
|||
device_name_entry.appendChild(device_name_edit);
|
||||
row_device.appendChild(device_name_entry);
|
||||
|
||||
// get plug status
|
||||
const device_status_response = await fetch(
|
||||
"/plug_state/" + device_id,
|
||||
{
|
||||
|
@ -82,6 +84,7 @@ async function startup() {
|
|||
|
||||
let device_state = JSON.parse(await device_status_response.json());
|
||||
|
||||
// create device led state column
|
||||
let device_led_state_entry = document.createElement('td');
|
||||
let device_led_state = document.createElement('label');
|
||||
device_led_state.innerText = device_state["led"];
|
||||
|
@ -98,6 +101,7 @@ async function startup() {
|
|||
device_led_state_entry.appendChild(device_led_off);
|
||||
row_device.appendChild(device_led_state_entry);
|
||||
|
||||
// create device power state column
|
||||
let device_power_state_entry = document.createElement('td');
|
||||
let device_power_state = document.createElement('label');
|
||||
device_power_state.innerText = device_state["power"];
|
||||
|
@ -114,11 +118,18 @@ async function startup() {
|
|||
device_power_state_entry.appendChild(device_power_off);
|
||||
row_device.appendChild(device_power_state_entry);
|
||||
|
||||
// create device power draw column
|
||||
let device_power_draw_entry = document.createElement('td');
|
||||
let device_power_draw_ = document.createElement('label');
|
||||
device_power_draw_.innerText = device_state["power_draw"] + " W";
|
||||
let device_power_draw = document.createElement('label');
|
||||
device_power_draw.innerText = device_state["power_draw"] + " W";
|
||||
let device_power_draw_graph_button = document.createElement('button');
|
||||
device_power_draw_graph_button.onclick = async () => { await render_graph(device_id) };
|
||||
let device_power_draw_graph_button_icon = document.createElement('i');
|
||||
device_power_draw_graph_button_icon.className = "fa fa-chart-bar";
|
||||
|
||||
device_power_draw_entry.appendChild(device_power_draw_);
|
||||
device_power_draw_graph_button.appendChild(device_power_draw_graph_button_icon);
|
||||
device_power_draw_entry.appendChild(device_power_draw);
|
||||
device_power_draw_entry.appendChild(device_power_draw_graph_button);
|
||||
row_device.appendChild(device_power_draw_entry);
|
||||
|
||||
table.appendChild(row_device);
|
||||
|
@ -163,7 +174,7 @@ async function power_on(plug) {
|
|||
}
|
||||
|
||||
async function power_off(plug) {
|
||||
await change_plug_state(plug, "pwoer", "off")
|
||||
await change_plug_state(plug, "power", "off")
|
||||
}
|
||||
|
||||
async function change_device_name(plug, name) {
|
||||
|
@ -177,4 +188,55 @@ async function change_device_name(plug, name) {
|
|||
if (!response.ok) {
|
||||
console.error(response.body);
|
||||
}
|
||||
}
|
||||
|
||||
async function render_graph(plug) {
|
||||
// remove old graph, if present
|
||||
let old = document.getElementById("chart");
|
||||
|
||||
if (old !== null) {
|
||||
old.remove();
|
||||
}
|
||||
|
||||
// create new chart div
|
||||
let chart = document.createElement('canvas');
|
||||
chart.id = "chart";
|
||||
|
||||
const response = await fetch(
|
||||
"/plug_data/" + plug,
|
||||
{
|
||||
method: "GET"
|
||||
}
|
||||
);
|
||||
|
||||
const j = await response.json();
|
||||
const data = JSON.parse(j);
|
||||
|
||||
let y = [];
|
||||
let x = [];
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let [time, watts] = data[i];
|
||||
|
||||
x.push(time);
|
||||
y.push(watts);
|
||||
}
|
||||
|
||||
const chart_data = {
|
||||
labels: x,
|
||||
datasets: [{
|
||||
label: plug,
|
||||
data: y,
|
||||
fill: false,
|
||||
borderColor: 'rgb(75, 192, 192)',
|
||||
tension: 0.1
|
||||
}]
|
||||
};
|
||||
|
||||
new Chart(chart, {
|
||||
type: 'line',
|
||||
data: chart_data,
|
||||
});
|
||||
|
||||
document.getElementById("chart_div").appendChild(chart);
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
<body>
|
||||
<div id="main"></div>
|
||||
<div id="chart_div"></div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script type="text/javascript" src="/js/main.js"></script>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -153,9 +153,11 @@ impl DataBase {
|
|||
self.sql
|
||||
.prepare(&format!(
|
||||
"
|
||||
SELECT time, watts
|
||||
SELECT data.time, data.watts
|
||||
FROM data
|
||||
WHERE device=\"{device}\"
|
||||
INNER JOIN devices
|
||||
ON data.device_id=devices.id
|
||||
WHERE devices.device=\"{device}\"
|
||||
"
|
||||
))?
|
||||
.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?
|
||||
|
|
|
@ -14,10 +14,6 @@ impl Devices {
|
|||
Ok(from_str(&fs::read_to_string(file)?)?)
|
||||
}
|
||||
|
||||
pub fn to_json(&self) -> Result<String> {
|
||||
Ok(to_string(self)?)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn save(&self, file: &str) -> Result<()> {
|
||||
fs::write(file, to_string_pretty(self)?)?;
|
||||
|
|
|
@ -18,7 +18,9 @@ use anyhow::Result;
|
|||
use devices::Devices;
|
||||
use futures::{future::try_join_all, try_join, Future};
|
||||
use tasmota::Tasmota;
|
||||
use web_server::{change_device_name, change_plug_state, device_query, index, plug_state};
|
||||
use web_server::{
|
||||
change_device_name, change_plug_state, device_query, index, plug_data, plug_state,
|
||||
};
|
||||
|
||||
fn since_epoch() -> Result<u64> {
|
||||
Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs())
|
||||
|
@ -64,6 +66,7 @@ async fn run_web_server(
|
|||
.service(plug_state)
|
||||
.service(change_plug_state)
|
||||
.service(change_device_name)
|
||||
.service(plug_data)
|
||||
})
|
||||
.bind(("0.0.0.0", 8062))
|
||||
.map_err(|err| anyhow::Error::msg(format!("failed binding to address: {err:#?}")))?
|
||||
|
|
|
@ -2,7 +2,7 @@ use actix_files::NamedFile;
|
|||
use actix_web::{
|
||||
get, post,
|
||||
web::{Data, Json, Path},
|
||||
Responder, ResponseError,
|
||||
Error, Responder, ResponseError,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use serde_json::to_string;
|
||||
|
@ -151,6 +151,22 @@ async fn change_plug_state(
|
|||
})
|
||||
}
|
||||
|
||||
#[get("/plug_data/{plug}")]
|
||||
async fn plug_data(
|
||||
param: Path<String>,
|
||||
db: Data<Arc<Mutex<DataBase>>>,
|
||||
) -> Result<impl Responder, Error> {
|
||||
let plug = param.into_inner();
|
||||
|
||||
let data = db
|
||||
.lock()
|
||||
.unwrap()
|
||||
.read(&plug)
|
||||
.map_err(|err| MyError::from(err))?;
|
||||
|
||||
Ok(Json(to_string(&data)?))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use actix_web::{http::header::ContentType, test, App};
|
||||
|
|
Loading…
Reference in a new issue