automation_rs/src/devices/washer.rs

121 lines
3.2 KiB
Rust

use async_trait::async_trait;
use automation_macro::LuaDevice;
use rumqttc::Publish;
use serde::Deserialize;
use tracing::{debug, error, warn};
use super::ntfy::Priority;
use super::{Device, Notification};
use crate::config::MqttDeviceConfig;
use crate::device_manager::{ConfigExternal, DeviceConfig};
use crate::error::DeviceConfigError;
use crate::event::{Event, EventChannel, OnMqtt};
use crate::messages::PowerMessage;
#[derive(Debug, Clone, Deserialize)]
pub struct WasherConfig {
#[serde(flatten)]
mqtt: MqttDeviceConfig,
threshold: f32, // Power in Watt
}
#[async_trait]
impl DeviceConfig for WasherConfig {
async fn create(
&self,
identifier: &str,
ext: &ConfigExternal,
) -> Result<Box<dyn Device>, DeviceConfigError> {
let device = Washer {
identifier: identifier.into(),
config: self.clone(),
event_channel: ext.event_channel.clone(),
running: 0,
};
Ok(Box::new(device))
}
}
// TODO: Add google home integration
#[derive(Debug, LuaDevice)]
pub struct Washer {
identifier: String,
#[config]
config: WasherConfig,
event_channel: EventChannel,
running: isize,
}
impl Device for Washer {
fn get_id(&self) -> &str {
&self.identifier
}
}
// The washer needs to have a power draw above the theshold multiple times before the washer is
// actually marked as running
// This helps prevent false positives
const HYSTERESIS: isize = 10;
#[async_trait]
impl OnMqtt for Washer {
fn topics(&self) -> Vec<&str> {
vec![&self.config.mqtt.topic]
}
async fn on_mqtt(&mut self, message: Publish) {
let power = match PowerMessage::try_from(message) {
Ok(state) => state.power(),
Err(err) => {
error!(id = self.identifier, "Failed to parse message: {err}");
return;
}
};
// debug!(id = self.identifier, power, "Washer state update");
if power < self.config.threshold && self.running >= HYSTERESIS {
// The washer is done running
debug!(
id = self.identifier,
power,
threshold = self.config.threshold,
"Washer is done"
);
self.running = 0;
let notification = Notification::new()
.set_title("Laundy is done")
.set_message("Don't forget to hang it!")
.add_tag("womans_clothes")
.set_priority(Priority::High);
if self
.event_channel
.get_tx()
.send(Event::Ntfy(notification))
.await
.is_err()
{
warn!("There are no receivers on the event channel");
}
} else if power < self.config.threshold {
// Prevent false positives
self.running = 0;
} else if power >= self.config.threshold && self.running < HYSTERESIS {
// Washer could be starting
debug!(
id = self.identifier,
power,
threshold = self.config.threshold,
"Washer is starting"
);
self.running += 1;
}
}
}