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');
|
let row_device = document.createElement('tr');
|
||||||
|
|
||||||
|
// create device name column
|
||||||
let device_name_entry = document.createElement('td');
|
let device_name_entry = document.createElement('td');
|
||||||
let device_name = document.createElement('input');
|
let device_name = document.createElement('input');
|
||||||
device_name.value = device_descriptor;
|
device_name.value = device_descriptor;
|
||||||
|
@ -73,6 +74,7 @@ async function startup() {
|
||||||
device_name_entry.appendChild(device_name_edit);
|
device_name_entry.appendChild(device_name_edit);
|
||||||
row_device.appendChild(device_name_entry);
|
row_device.appendChild(device_name_entry);
|
||||||
|
|
||||||
|
// get plug status
|
||||||
const device_status_response = await fetch(
|
const device_status_response = await fetch(
|
||||||
"/plug_state/" + device_id,
|
"/plug_state/" + device_id,
|
||||||
{
|
{
|
||||||
|
@ -82,6 +84,7 @@ async function startup() {
|
||||||
|
|
||||||
let device_state = JSON.parse(await device_status_response.json());
|
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_entry = document.createElement('td');
|
||||||
let device_led_state = document.createElement('label');
|
let device_led_state = document.createElement('label');
|
||||||
device_led_state.innerText = device_state["led"];
|
device_led_state.innerText = device_state["led"];
|
||||||
|
@ -98,6 +101,7 @@ async function startup() {
|
||||||
device_led_state_entry.appendChild(device_led_off);
|
device_led_state_entry.appendChild(device_led_off);
|
||||||
row_device.appendChild(device_led_state_entry);
|
row_device.appendChild(device_led_state_entry);
|
||||||
|
|
||||||
|
// create device power state column
|
||||||
let device_power_state_entry = document.createElement('td');
|
let device_power_state_entry = document.createElement('td');
|
||||||
let device_power_state = document.createElement('label');
|
let device_power_state = document.createElement('label');
|
||||||
device_power_state.innerText = device_state["power"];
|
device_power_state.innerText = device_state["power"];
|
||||||
|
@ -114,11 +118,18 @@ async function startup() {
|
||||||
device_power_state_entry.appendChild(device_power_off);
|
device_power_state_entry.appendChild(device_power_off);
|
||||||
row_device.appendChild(device_power_state_entry);
|
row_device.appendChild(device_power_state_entry);
|
||||||
|
|
||||||
|
// create device power draw column
|
||||||
let device_power_draw_entry = document.createElement('td');
|
let device_power_draw_entry = document.createElement('td');
|
||||||
let device_power_draw_ = document.createElement('label');
|
let device_power_draw = document.createElement('label');
|
||||||
device_power_draw_.innerText = device_state["power_draw"] + " W";
|
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);
|
row_device.appendChild(device_power_draw_entry);
|
||||||
|
|
||||||
table.appendChild(row_device);
|
table.appendChild(row_device);
|
||||||
|
@ -163,7 +174,7 @@ async function power_on(plug) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function power_off(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) {
|
async function change_device_name(plug, name) {
|
||||||
|
@ -177,4 +188,55 @@ async function change_device_name(plug, name) {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.error(response.body);
|
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>
|
<body>
|
||||||
<div id="main"></div>
|
<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>
|
<script type="text/javascript" src="/js/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -153,9 +153,11 @@ impl DataBase {
|
||||||
self.sql
|
self.sql
|
||||||
.prepare(&format!(
|
.prepare(&format!(
|
||||||
"
|
"
|
||||||
SELECT time, watts
|
SELECT data.time, data.watts
|
||||||
FROM data
|
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)?)))?
|
.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?
|
||||||
|
|
|
@ -14,10 +14,6 @@ impl Devices {
|
||||||
Ok(from_str(&fs::read_to_string(file)?)?)
|
Ok(from_str(&fs::read_to_string(file)?)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_json(&self) -> Result<String> {
|
|
||||||
Ok(to_string(self)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn save(&self, file: &str) -> Result<()> {
|
pub fn save(&self, file: &str) -> Result<()> {
|
||||||
fs::write(file, to_string_pretty(self)?)?;
|
fs::write(file, to_string_pretty(self)?)?;
|
||||||
|
|
|
@ -18,7 +18,9 @@ use anyhow::Result;
|
||||||
use devices::Devices;
|
use devices::Devices;
|
||||||
use futures::{future::try_join_all, try_join, Future};
|
use futures::{future::try_join_all, try_join, Future};
|
||||||
use tasmota::Tasmota;
|
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> {
|
fn since_epoch() -> Result<u64> {
|
||||||
Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs())
|
Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs())
|
||||||
|
@ -64,6 +66,7 @@ async fn run_web_server(
|
||||||
.service(plug_state)
|
.service(plug_state)
|
||||||
.service(change_plug_state)
|
.service(change_plug_state)
|
||||||
.service(change_device_name)
|
.service(change_device_name)
|
||||||
|
.service(plug_data)
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", 8062))
|
.bind(("0.0.0.0", 8062))
|
||||||
.map_err(|err| anyhow::Error::msg(format!("failed binding to address: {err:#?}")))?
|
.map_err(|err| anyhow::Error::msg(format!("failed binding to address: {err:#?}")))?
|
||||||
|
|
|
@ -2,7 +2,7 @@ use actix_files::NamedFile;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
get, post,
|
get, post,
|
||||||
web::{Data, Json, Path},
|
web::{Data, Json, Path},
|
||||||
Responder, ResponseError,
|
Error, Responder, ResponseError,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::to_string;
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use actix_web::{http::header::ContentType, test, App};
|
use actix_web::{http::header::ContentType, test, App};
|
||||||
|
|
Loading…
Reference in a new issue