Added initial basic washer integration
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
690090016e
commit
ee00959e8a
|
@ -83,3 +83,7 @@ speakers = "living_speakers"
|
|||
type = "ContactSensor"
|
||||
topic = "zigbee2mqtt/hallway/frontdoor"
|
||||
presence = { timeout = 900 }
|
||||
|
||||
[devices.bathroom_washer]
|
||||
type = "Washer"
|
||||
topic = "zigbee2mqtt/bathroom/washer"
|
||||
|
|
|
@ -84,3 +84,7 @@ type = "ContactSensor"
|
|||
topic = "zigbee2mqtt/hallway/frontdoor"
|
||||
presence = { timeout = 10 }
|
||||
lights = { lights = ["bathroom_light"], timeout = 10 }
|
||||
|
||||
[devices.bathroom_washer]
|
||||
type = "Washer"
|
||||
topic = "zigbee2mqtt/bathroom/washer"
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
device_manager::DeviceManager,
|
||||
devices::{
|
||||
AudioSetup, ContactSensor, DebugBridgeConfig, Device, HueBridgeConfig, IkeaOutlet,
|
||||
KasaOutlet, LightSensorConfig, PresenceConfig, WakeOnLAN,
|
||||
KasaOutlet, LightSensorConfig, PresenceConfig, WakeOnLAN, Washer,
|
||||
},
|
||||
error::{ConfigParseError, CreateDeviceError, MissingEnv},
|
||||
event::EventChannel,
|
||||
|
@ -130,6 +130,7 @@ pub enum DeviceConfig {
|
|||
IkeaOutlet(<IkeaOutlet as CreateDevice>::Config),
|
||||
KasaOutlet(<KasaOutlet as CreateDevice>::Config),
|
||||
WakeOnLAN(<WakeOnLAN as CreateDevice>::Config),
|
||||
Washer(<Washer as CreateDevice>::Config),
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -200,7 +201,8 @@ impl DeviceConfig {
|
|||
ContactSensor,
|
||||
IkeaOutlet,
|
||||
KasaOutlet,
|
||||
WakeOnLAN
|
||||
WakeOnLAN,
|
||||
Washer
|
||||
]
|
||||
})
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ mod light_sensor;
|
|||
mod ntfy;
|
||||
mod presence;
|
||||
mod wake_on_lan;
|
||||
mod washer;
|
||||
|
||||
pub use self::audio_setup::AudioSetup;
|
||||
pub use self::contact_sensor::ContactSensor;
|
||||
|
@ -19,6 +20,7 @@ pub use self::light_sensor::{LightSensor, LightSensorConfig};
|
|||
pub use self::ntfy::{Notification, Ntfy};
|
||||
pub use self::presence::{Presence, PresenceConfig, DEFAULT_PRESENCE};
|
||||
pub use self::wake_on_lan::WakeOnLAN;
|
||||
pub use self::washer::Washer;
|
||||
|
||||
use google_home::{device::AsGoogleHomeDevice, traits::OnOff};
|
||||
|
||||
|
|
98
src/devices/washer.rs
Normal file
98
src/devices/washer.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use async_trait::async_trait;
|
||||
use rumqttc::{AsyncClient, Publish};
|
||||
use serde::Deserialize;
|
||||
use tracing::{error, warn};
|
||||
|
||||
use crate::{
|
||||
config::{CreateDevice, MqttDeviceConfig},
|
||||
device_manager::DeviceManager,
|
||||
error::CreateDeviceError,
|
||||
event::{Event, EventChannel, OnMqtt},
|
||||
messages::PowerMessage,
|
||||
};
|
||||
|
||||
use super::{ntfy::Priority, Device, Notification};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct WasherConfig {
|
||||
#[serde(flatten)]
|
||||
mqtt: MqttDeviceConfig,
|
||||
}
|
||||
|
||||
// TODO: Add google home integration
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Washer {
|
||||
identifier: String,
|
||||
mqtt: MqttDeviceConfig,
|
||||
|
||||
event_channel: EventChannel,
|
||||
running: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CreateDevice for Washer {
|
||||
type Config = WasherConfig;
|
||||
|
||||
async fn create(
|
||||
identifier: &str,
|
||||
config: Self::Config,
|
||||
event_channel: &EventChannel,
|
||||
_client: &AsyncClient,
|
||||
_presence_topic: &str,
|
||||
_device_manager: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError> {
|
||||
Ok(Self {
|
||||
identifier: identifier.to_owned(),
|
||||
mqtt: config.mqtt,
|
||||
event_channel: event_channel.clone(),
|
||||
running: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Washer {
|
||||
fn get_id(&self) -> &str {
|
||||
&self.identifier
|
||||
}
|
||||
}
|
||||
|
||||
#[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;
|
||||
}
|
||||
};
|
||||
|
||||
if self.running && power < 1.0 {
|
||||
// The washer is done running
|
||||
self.running = false;
|
||||
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 !self.running && power >= 1.0 {
|
||||
// We just started washing
|
||||
self.running = true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ pub enum Event {
|
|||
pub type Sender = mpsc::Sender<Event>;
|
||||
pub type Receiver = mpsc::Receiver<Event>;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EventChannel(Sender);
|
||||
|
||||
impl EventChannel {
|
||||
|
|
|
@ -119,7 +119,7 @@ impl TryFrom<Publish> for PresenceMessage {
|
|||
}
|
||||
}
|
||||
|
||||
// Message use to report the state of a light sensor
|
||||
// Message used to report the state of a light sensor
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct BrightnessMessage {
|
||||
illuminance: isize,
|
||||
|
@ -194,3 +194,24 @@ impl TryFrom<Publish> for DarknessMessage {
|
|||
.or(Err(ParseError::InvalidPayload(message.payload.clone())))
|
||||
}
|
||||
}
|
||||
|
||||
// Message used to report the power draw a smart plug
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PowerMessage {
|
||||
power: f32,
|
||||
}
|
||||
|
||||
impl PowerMessage {
|
||||
pub fn power(&self) -> f32 {
|
||||
self.power
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Publish> for PowerMessage {
|
||||
type Error = ParseError;
|
||||
|
||||
fn try_from(message: Publish) -> Result<Self, Self::Error> {
|
||||
serde_json::from_slice(&message.payload)
|
||||
.or(Err(ParseError::InvalidPayload(message.payload.clone())))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user