automation_rs/src/devices/washer.rs
Dreaded_X 3134891751
All checks were successful
continuous-integration/drone/push Build is passing
Adjusted internal washer hysteresis to prevent false positives
2023-08-17 01:47:45 +02:00

123 lines
3.2 KiB
Rust

use async_trait::async_trait;
use rumqttc::Publish;
use serde::Deserialize;
use tracing::{debug, error, warn};
use crate::{
config::MqttDeviceConfig,
device_manager::{ConfigExternal, DeviceConfig},
error::DeviceConfigError,
event::{Event, EventChannel, OnMqtt},
messages::PowerMessage,
};
use super::{ntfy::Priority, Device, Notification};
#[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(),
mqtt: self.mqtt,
event_channel: ext.event_channel.clone(),
threshold: self.threshold,
running: 0,
};
Ok(Box::new(device))
}
}
// TODO: Add google home integration
#[derive(Debug)]
struct Washer {
identifier: String,
mqtt: MqttDeviceConfig,
event_channel: EventChannel,
threshold: f32,
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.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.threshold && self.running >= HYSTERESIS {
// The washer is done running
debug!(
id = self.identifier,
power,
threshold = self.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.threshold {
// Prevent false positives
self.running = 0;
} else if power >= self.threshold && self.running < HYSTERESIS {
// Washer could be starting
debug!(
id = self.identifier,
power,
threshold = self.threshold,
"Washer is starting"
);
self.running += 1;
}
}
}