use actix_web::{ get, post, web::{Data, Json, Path}, HttpRequest, Responder, ResponseError, }; use serde::Serialize; use serde_json::to_string; use crate::{devices::Devices, tasmota::Tasmota}; use std::fmt::{Display, Formatter, Result as FmtResult}; #[derive(Serialize)] struct DeviceState { name: String, power: bool, led: bool, power_draw: f32, } #[derive(Debug)] struct MyError { msg: String, } impl Display for MyError { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { write!(f, "Error: {}", self.msg) } } impl ResponseError for MyError {} impl From for MyError { fn from(value: anyhow::Error) -> Self { MyError { msg: value.to_string(), } } } #[get("/")] async fn index(req: HttpRequest) -> &'static str { println!("REQ: {:?}", req); "Hello world!\r\n" } #[get("/devices")] async fn device_query(devices: Data) -> Result { devices .to_json() .map(|json| Json(json)) .map_err(|err| MyError::from(err)) } async fn tasmota_info(tasmota: &Tasmota) -> anyhow::Result { let led = tasmota.led_state().await?; let power = tasmota.power_state().await?; let power_draw = tasmota.read_power_usage().await?; Ok(to_string(&DeviceState { name: tasmota.name().to_string(), power, led, power_draw, })?) } #[get("/plug_state/{plug}")] async fn plug_state( plug: Path, plugs: Data>, ) -> Result { let plug_name = plug.into_inner(); if let Some(tasmota) = plugs.iter().find(|tasmota| tasmota.name() == plug_name) { return Ok(tasmota_info(tasmota) .await .map(|s| Json(s)) .map_err(|err| MyError::from(err))?); } Err(MyError { msg: format!("plug ({plug_name}) not found"), }) } #[post("/plug/{plug}/{action}")] async fn change_plug_state( plug: Path, action: Path, plugs: Data>, ) -> Result { let plug_name = plug.into_inner(); if let Some(tasmota) = plugs.iter().find(|tasmota| tasmota.name() == plug_name) { match action.into_inner().as_str() { "led_on" => tasmota .turn_on_led() .await .map_err(|err| MyError::from(err))?, "led_off" => tasmota .turn_off_led() .await .map_err(|err| MyError::from(err))?, "power_on" => tasmota .switch_on() .await .map_err(|err| MyError::from(err))?, "power_off" => tasmota .switch_off() .await .map_err(|err| MyError::from(err))?, _ => (), } return Ok("Ok"); } Err(MyError { msg: format!("plug ({plug_name}) not found"), }) }