Test tasmota interface
This commit is contained in:
parent
254806a1ae
commit
4ae0c30171
3 changed files with 170 additions and 13 deletions
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug",
|
||||
"program": "${workspaceFolder}/<executable file>",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"plugs": [
|
||||
"Dev1",
|
||||
"Dev2"
|
||||
"Tasmota-Plug-1",
|
||||
"Tasmota-Plug-2",
|
||||
"Tasmota-Plug-3",
|
||||
"Tasmota-Plug-4"
|
||||
]
|
||||
}
|
161
src/tasmota.rs
161
src/tasmota.rs
|
@ -1,4 +1,6 @@
|
|||
use anyhow::Result;
|
||||
use serde::Deserialize;
|
||||
use serde_json::from_str;
|
||||
|
||||
pub struct Tasmota {
|
||||
device: String,
|
||||
|
@ -15,11 +17,18 @@ impl Tasmota {
|
|||
&self.device
|
||||
}
|
||||
|
||||
fn command(&self, command: &str) -> String {
|
||||
format!("http://{}/cm?cmnd={}", self.device, command)
|
||||
fn command<'a>(&self, command: impl IntoIterator<Item = &'a str>) -> String {
|
||||
let mut str = String::new();
|
||||
|
||||
for s in command.into_iter() {
|
||||
str += s;
|
||||
str += "%20";
|
||||
}
|
||||
|
||||
async fn post(&self, command: &str) -> Result<String> {
|
||||
format!("http://{}/cm?cmnd={}", self.device, str)
|
||||
}
|
||||
|
||||
async fn post<'a>(&self, command: impl IntoIterator<Item = &'a str>) -> Result<String> {
|
||||
Ok(reqwest::Client::new()
|
||||
.post(&self.command(command))
|
||||
.send()
|
||||
|
@ -28,7 +37,7 @@ impl Tasmota {
|
|||
.await?)
|
||||
}
|
||||
|
||||
async fn get(&self, command: &str) -> Result<String> {
|
||||
async fn get<'a>(&self, command: impl IntoIterator<Item = &'a str>) -> Result<String> {
|
||||
Ok(reqwest::Client::new()
|
||||
.get(&self.command(command))
|
||||
.send()
|
||||
|
@ -38,32 +47,162 @@ impl Tasmota {
|
|||
}
|
||||
|
||||
pub async fn turn_on_led(&self) -> Result<()> {
|
||||
self.post("LedPower=1").await?;
|
||||
self.post(["LedPower", "1"]).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn turn_off_led(&self) -> Result<()> {
|
||||
self.post("LedPower=2").await?;
|
||||
self.post(["LedPower", "0"]).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn switch_on(&self) -> Result<()> {
|
||||
self.post("Power0=1").await?;
|
||||
self.post(["Power0", "1"]).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn switch_off(&self) -> Result<()> {
|
||||
self.post("Power0=0").await?;
|
||||
self.post(["Power0", "0"]).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_power_usage(&self) -> Result<f32> {
|
||||
let res = self.get("Status=8").await?;
|
||||
pub async fn power_state(&self) -> Result<bool> {
|
||||
let res = self.get(["Power0"]).await?;
|
||||
|
||||
Ok(res.parse()?)
|
||||
let state = PowerState::from_str(&res)?;
|
||||
|
||||
Ok(state.is_on())
|
||||
}
|
||||
|
||||
pub async fn read_power_usage(&self) -> Result<f32> {
|
||||
let res = self.get(["Status", "8"]).await?;
|
||||
|
||||
let status = Status::from_str(&res)?;
|
||||
|
||||
Ok(status.StatusSNS.ENERGY.Power)
|
||||
}
|
||||
|
||||
pub async fn led_state(&self) -> Result<bool> {
|
||||
let res = self.get(["LedState"]).await?;
|
||||
|
||||
let state = LedState::from_str(&res)?;
|
||||
|
||||
Ok(state.LedState != 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
use super::*;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_connection() -> Result<()> {
|
||||
let dev = Tasmota::new("Tasmota-Plug-1");
|
||||
|
||||
let power = dev.read_power_usage().await?;
|
||||
|
||||
println!("{power}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_toggle() -> Result<()> {
|
||||
let dev = Tasmota::new("Tasmota-Plug-4");
|
||||
|
||||
dev.switch_off().await?;
|
||||
assert_eq!(dev.power_state().await?, false);
|
||||
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
|
||||
dev.switch_on().await?;
|
||||
assert_eq!(dev.power_state().await?, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_led() -> Result<()> {
|
||||
let dev = Tasmota::new("Tasmota-Plug-4");
|
||||
|
||||
dev.turn_off_led().await?;
|
||||
assert_eq!(dev.led_state().await?, false);
|
||||
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
|
||||
dev.turn_on_led().await?;
|
||||
assert_eq!(dev.led_state().await?, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct Status {
|
||||
pub StatusSNS: StatusSNS,
|
||||
}
|
||||
|
||||
impl Status {
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
Ok(from_str(s)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct StatusSNS {
|
||||
pub Time: String,
|
||||
pub ENERGY: Energy,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct Energy {
|
||||
pub TotalStartTime: String,
|
||||
pub Total: f32,
|
||||
pub Yesterday: f32,
|
||||
pub Today: f32,
|
||||
pub Power: f32,
|
||||
pub ApparentPower: u32,
|
||||
pub ReactivePower: u32,
|
||||
pub Factor: f32,
|
||||
pub Voltage: u32,
|
||||
pub Current: f32,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct LedState {
|
||||
LedState: u8,
|
||||
}
|
||||
|
||||
impl LedState {
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
Ok(from_str(s)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct PowerState {
|
||||
POWER: String,
|
||||
}
|
||||
|
||||
impl PowerState {
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
Ok(from_str(s)?)
|
||||
}
|
||||
|
||||
pub fn is_on(&self) -> bool {
|
||||
self.POWER == "ON"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue