Testing fetch
This commit is contained in:
parent
a8b79ba727
commit
adc19c845c
10 changed files with 44 additions and 372 deletions
|
@ -15,6 +15,5 @@ futures = "0.3.28"
|
||||||
tokio = { version = "1.33.0", features=["macros", "rt-multi-thread"] }
|
tokio = { version = "1.33.0", features=["macros", "rt-multi-thread"] }
|
||||||
chrono = "0.4.31"
|
chrono = "0.4.31"
|
||||||
actix-web = "4.4.0"
|
actix-web = "4.4.0"
|
||||||
actix-files = "0.6.2"
|
|
||||||
midea = { git = "https://gavania.de/hodasemi/Midea.git" }
|
midea = { git = "https://gavania.de/hodasemi/Midea.git" }
|
||||||
actix-cors = "0.6.4"
|
actix-cors = "0.6.4"
|
3
build.sh
3
build.sh
|
@ -9,5 +9,4 @@ cargo build --release
|
||||||
mkdir -p server
|
mkdir -p server
|
||||||
|
|
||||||
cp devices.conf server/
|
cp devices.conf server/
|
||||||
cp target/release/home_server server/
|
cp target/release/home_server server/
|
||||||
cp -r resources server/
|
|
|
@ -21,44 +21,54 @@ class Category {
|
||||||
|
|
||||||
final List<Category> categories = [];
|
final List<Category> categories = [];
|
||||||
|
|
||||||
print("debug print");
|
|
||||||
print(response.body);
|
print(response.body);
|
||||||
|
print("body type: ${response.body.runtimeType}");
|
||||||
|
|
||||||
final Map<String, List<List<dynamic>>> json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
|
|
||||||
for (MapEntry<String, List<List<dynamic>>> category_entry in json.entries) {
|
print(json);
|
||||||
final Category category = Category(category_entry.key);
|
print("json type: ${json.runtimeType}");
|
||||||
|
|
||||||
for (List<dynamic> device_info in category_entry.value) {
|
final Map<String, List<List<dynamic>>> test = jsonDecode(json);
|
||||||
final String device_id = device_info[0];
|
|
||||||
final String? device_descriptor = device_info[1];
|
|
||||||
final bool power_control = device_info[2];
|
|
||||||
|
|
||||||
final response =
|
print(test);
|
||||||
await http.get(Uri.parse("$base_url/plug_state/$device_id"));
|
print("test type: ${test.runtimeType}");
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
// final Map<String, List<List<dynamic>>> json = jsonDecode(response.body);
|
||||||
throw Exception("Failed to fetch plug_state for $device_id");
|
|
||||||
}
|
|
||||||
|
|
||||||
print(response.body);
|
// for (MapEntry<String, List<List<dynamic>>> category_entry in json.entries) {
|
||||||
|
// final Category category = Category(category_entry.key);
|
||||||
|
|
||||||
final Map<String, dynamic> device_state = jsonDecode(response.body);
|
// for (List<dynamic> device_info in category_entry.value) {
|
||||||
|
// final String device_id = device_info[0];
|
||||||
|
// final String? device_descriptor = device_info[1];
|
||||||
|
// final bool power_control = device_info[2];
|
||||||
|
|
||||||
final Device device = Device(
|
// final response =
|
||||||
device_id,
|
// await http.get(Uri.parse("$base_url/plug_state/$device_id"));
|
||||||
device_descriptor,
|
|
||||||
device_state["led"],
|
|
||||||
device_state["power"],
|
|
||||||
device_state["power_draw"],
|
|
||||||
power_control && device_state["power_draw"] < 15,
|
|
||||||
);
|
|
||||||
|
|
||||||
category.devices.add(device);
|
// if (response.statusCode != 200) {
|
||||||
}
|
// throw Exception("Failed to fetch plug_state for $device_id");
|
||||||
|
// }
|
||||||
|
|
||||||
categories.add(category);
|
// print(response.body);
|
||||||
}
|
|
||||||
|
// final Map<String, dynamic> device_state = jsonDecode(response.body);
|
||||||
|
|
||||||
|
// final Device device = Device(
|
||||||
|
// device_id,
|
||||||
|
// device_descriptor,
|
||||||
|
// device_state["led"],
|
||||||
|
// device_state["power"],
|
||||||
|
// device_state["power_draw"],
|
||||||
|
// power_control && device_state["power_draw"] < 15,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// category.devices.add(device);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// categories.add(category);
|
||||||
|
// }
|
||||||
|
|
||||||
return categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,8 @@ class MyHomePage extends StatefulWidget {
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Future<List<Category>> fut = Category.fetch("smart.gavania.de");
|
final Future<List<Category>> fut = Category.fetch("127.0.0.1:8062");
|
||||||
|
// final Future<List<Category>> fut = Category.fetch("smart.gavania.de");
|
||||||
|
|
||||||
List<Widget> children = List.empty();
|
List<Widget> children = List.empty();
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
td>*:not(:last-child) {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
|
@ -1,263 +0,0 @@
|
||||||
function group_name({ name }) {
|
|
||||||
return (
|
|
||||||
<tr>
|
|
||||||
<td>{name}</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function table_header() {
|
|
||||||
return (
|
|
||||||
<tr>
|
|
||||||
<td>Name</td>
|
|
||||||
<td>LED</td>
|
|
||||||
<td>Power</td>
|
|
||||||
<td>Power Draw</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function table_row({ row_data }) {
|
|
||||||
return (
|
|
||||||
<tr>
|
|
||||||
{/* Name */}
|
|
||||||
<td>
|
|
||||||
<input id={"name_" + row_data.device_id} readonly="true">{row_data.device_name}</input>
|
|
||||||
<button on_click={async () => { await change_name(row_data.device_id) }}>
|
|
||||||
<i class="fa fa-pencil-square-o"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{/* LED */}
|
|
||||||
<td>
|
|
||||||
<label id={"led_" + row_data.device_id}>{row_data.led_state}</label>
|
|
||||||
<button on_click={async () => { await led_on(row_data.device_id) }}>On</button>
|
|
||||||
<button on_click={async () => { await led_off(row_data.device_id) }}>Off</button>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{/* Power */}
|
|
||||||
<td>
|
|
||||||
<label id={"power_" + row_data.device_id}>{row_data.power_state}</label>
|
|
||||||
{
|
|
||||||
row_data.power_control &&
|
|
||||||
<button on_click={async () => { await power_on(row_data.device_id) }}>On</button> &&
|
|
||||||
<button on_click={async () => { await power_off(row_data.device_id) }}>Off</button>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{/* Power Draw */}
|
|
||||||
<td>
|
|
||||||
<label>{row_data.power_draw} W</label>
|
|
||||||
<button><i class="fa fa-line-chart"></i></button>
|
|
||||||
</td>
|
|
||||||
</tr >
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function create_table(group, device_data) {
|
|
||||||
return (
|
|
||||||
<table>
|
|
||||||
<group_name name={group} />
|
|
||||||
<table_header />
|
|
||||||
<table_row row_data={device_data} />
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function MyApp() {
|
|
||||||
const response = await fetch(
|
|
||||||
"/devices",
|
|
||||||
{
|
|
||||||
method: "GET"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let json = JSON.parse(await response.json());
|
|
||||||
|
|
||||||
for (const [group_name, devices] of Object.entries(json)) {
|
|
||||||
let device_infos = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < devices.length; i++) {
|
|
||||||
let device_id = devices[i][0];
|
|
||||||
let device_descriptor;
|
|
||||||
|
|
||||||
if (devices[i][1] == null) {
|
|
||||||
device_descriptor = device_id
|
|
||||||
} else {
|
|
||||||
device_descriptor = devices[i][1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// get plug status
|
|
||||||
const device_status_response = await fetch(
|
|
||||||
"/plug_state/" + device_id,
|
|
||||||
{
|
|
||||||
method: "GET"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let device_state = JSON.parse(await device_status_response.json());
|
|
||||||
|
|
||||||
let device_info = {
|
|
||||||
device_id: device_id,
|
|
||||||
device_descriptor: device_descriptor,
|
|
||||||
led_state: device_state["led"],
|
|
||||||
power_state: device_state["power"],
|
|
||||||
power_control: devices[i][2] && device_state["power_draw"] < 15,
|
|
||||||
power_draw: device_state["power_draw"],
|
|
||||||
};
|
|
||||||
|
|
||||||
device_infos.push(device_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("main").appendChild(create_table(group_name, device_infos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function change_name(device_id) {
|
|
||||||
let device_name = document.getElementById("name_" + device_id);
|
|
||||||
|
|
||||||
if (device_name.readOnly) {
|
|
||||||
device_name.readOnly = false;
|
|
||||||
device_name.focus();
|
|
||||||
} else {
|
|
||||||
device_name.readOnly = true;
|
|
||||||
await change_device_name(device_id, device_name.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function change_plug_state(plug, module, state) {
|
|
||||||
const response = await fetch(
|
|
||||||
"/plug/" + plug + "/" + module + "_" + state,
|
|
||||||
{
|
|
||||||
method: "POST"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const device_status_response = await fetch(
|
|
||||||
"/plug_state/" + plug,
|
|
||||||
{
|
|
||||||
method: "GET"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let device_state = JSON.parse(await device_status_response.json());
|
|
||||||
|
|
||||||
document.getElementById(module + "_" + plug).innerHTML = device_state[module];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function led_on(plug) {
|
|
||||||
await change_plug_state(plug, "led", "on")
|
|
||||||
}
|
|
||||||
|
|
||||||
async function led_off(plug) {
|
|
||||||
await change_plug_state(plug, "led", "off")
|
|
||||||
}
|
|
||||||
|
|
||||||
async function power_on(plug) {
|
|
||||||
await change_plug_state(plug, "power", "on")
|
|
||||||
}
|
|
||||||
|
|
||||||
async function power_off(plug) {
|
|
||||||
// get plug status
|
|
||||||
const device_status_response = await fetch(
|
|
||||||
"/plug_state/" + device_id,
|
|
||||||
{
|
|
||||||
method: "GET"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let device_state = JSON.parse(await device_status_response.json());
|
|
||||||
|
|
||||||
if (device_state["power_draw"] < 15) {
|
|
||||||
await change_plug_state(plug, "power", "off")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function change_device_name(plug, name) {
|
|
||||||
const response = await fetch(
|
|
||||||
"/device_name/" + plug + "/" + name,
|
|
||||||
{
|
|
||||||
method: "POST"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error(response.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function render_graph(plug, name) {
|
|
||||||
// remove old graph, if present
|
|
||||||
let old = document.getElementById("chart");
|
|
||||||
|
|
||||||
if (old !== null) {
|
|
||||||
old.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// get start date
|
|
||||||
let start_text = document.getElementById("start").value;
|
|
||||||
let start_date = parseInt(new Date(start_text).getTime() / 1000).toFixed(0);
|
|
||||||
|
|
||||||
// get end date1
|
|
||||||
let end_text = document.getElementById("end").value;
|
|
||||||
let end_date = parseInt(new Date(end_text).getTime() / 1000).toFixed(0);
|
|
||||||
|
|
||||||
// create new chart div
|
|
||||||
let chart = document.createElement('canvas');
|
|
||||||
chart.id = "chart";
|
|
||||||
|
|
||||||
const response = await fetch(
|
|
||||||
"/plug_data/" + plug + "/" + start_date + "/" + end_date + "/" + "hourly",
|
|
||||||
{
|
|
||||||
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(new Date(time * 1000));
|
|
||||||
y.push(watts);
|
|
||||||
}
|
|
||||||
|
|
||||||
const chart_data = {
|
|
||||||
labels: x,
|
|
||||||
datasets: [{
|
|
||||||
label: name,
|
|
||||||
data: y,
|
|
||||||
fill: false,
|
|
||||||
borderColor: 'rgb(75, 192, 192)',
|
|
||||||
tension: 0.1
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
new Chart(chart, {
|
|
||||||
type: 'line',
|
|
||||||
data: chart_data,
|
|
||||||
options: {
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
displayFormats: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
locale: 'de-DE'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById("graph").appendChild(chart);
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<!-- Required meta tags -->
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
||||||
|
|
||||||
<title>Smart Homeserver</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css"
|
|
||||||
integrity="sha256-XoaMnoYC5TH6/+ihMEnospgm0J1PM/nioxbOUdnM8HY=" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/purecss@3.0.0/build/pure-min.css"
|
|
||||||
integrity="sha384-X38yfunGUhNzHpBaEBsWLO+A0HDYOQi8ufWDkZ0k9e0eXz/tH3II7uKZ9msv++Ls" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="https://cdn.korzh.com/metroui/v4/css/metro-all.min.css">
|
|
||||||
<link href="/css/index.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"
|
|
||||||
integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ=="
|
|
||||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"
|
|
||||||
integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ=="
|
|
||||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="main"></div>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
||||||
<script
|
|
||||||
src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="/js/main.js"></script>
|
|
||||||
<script src="https://cdn.korzh.com/metroui/v4/js/metro.min.js"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<input id="start" type="text" data-role="calendarpicker">
|
|
||||||
<input id="end" type="text" data-role="calendarpicker">
|
|
||||||
<div id="graph">
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{from_str, to_string, to_string_pretty};
|
use serde_json::{from_str, to_string, to_string_pretty};
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ pub struct Devices {
|
||||||
|
|
||||||
impl Devices {
|
impl Devices {
|
||||||
pub async fn read(file: &str) -> Result<Self> {
|
pub async fn read(file: &str) -> Result<Self> {
|
||||||
Ok(from_str(&fs::read_to_string(file)?)?)
|
Ok(from_str(
|
||||||
|
&fs::read_to_string(file).context(format!("{file}"))?,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
|
|
@ -14,7 +14,6 @@ mod tasmota;
|
||||||
mod web_server;
|
mod web_server;
|
||||||
|
|
||||||
use actix_cors::Cors;
|
use actix_cors::Cors;
|
||||||
use actix_files::Files;
|
|
||||||
use actix_web::{web::Data, App, HttpServer};
|
use actix_web::{web::Data, App, HttpServer};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use devices::Devices;
|
use devices::Devices;
|
||||||
|
@ -72,10 +71,6 @@ async fn run_web_server(
|
||||||
.app_data(Data::new(db.clone()))
|
.app_data(Data::new(db.clone()))
|
||||||
.app_data(Data::new(plugs.clone()))
|
.app_data(Data::new(plugs.clone()))
|
||||||
.app_data(Data::new(dishwasher.clone()))
|
.app_data(Data::new(dishwasher.clone()))
|
||||||
.service(Files::new("/images", "resources/images/").show_files_listing())
|
|
||||||
.service(Files::new("/css", "resources/css").show_files_listing())
|
|
||||||
.service(Files::new("/js", "resources/js").show_files_listing())
|
|
||||||
.service(index)
|
|
||||||
.service(device_query)
|
.service(device_query)
|
||||||
.service(plug_state)
|
.service(plug_state)
|
||||||
.service(change_plug_state)
|
.service(change_plug_state)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use actix_files::NamedFile;
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
get, post,
|
get, post,
|
||||||
web::{Data, Json, Path},
|
web::{Data, Json, Path},
|
||||||
|
@ -65,11 +64,6 @@ impl Resolution {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
async fn index() -> Result<NamedFile, impl ResponseError> {
|
|
||||||
NamedFile::open("resources/static/index.html")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/devices")]
|
#[get("/devices")]
|
||||||
async fn device_query(
|
async fn device_query(
|
||||||
db: Data<Arc<Mutex<DataBase>>>,
|
db: Data<Arc<Mutex<DataBase>>>,
|
||||||
|
@ -272,25 +266,6 @@ mod test {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[actix_web::test]
|
|
||||||
async fn test_index_get() {
|
|
||||||
let app = test::init_service(App::new().service(index)).await;
|
|
||||||
let req = test::TestRequest::default()
|
|
||||||
.insert_header(ContentType::plaintext())
|
|
||||||
.to_request();
|
|
||||||
let resp = test::call_service(&app, req).await;
|
|
||||||
|
|
||||||
let status = resp.status();
|
|
||||||
let body = resp.into_body();
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
status.is_success(),
|
|
||||||
"status: {:?}, error: {:?}",
|
|
||||||
status,
|
|
||||||
body
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn test_led_on_off() {
|
async fn test_led_on_off() {
|
||||||
let app = test::init_service(
|
let app = test::init_service(
|
||||||
|
|
Loading…
Reference in a new issue