From 4bb49a381b23e9791839803c9cde146d0d6dc86c Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Sat, 30 Nov 2024 06:06:30 +0100 Subject: [PATCH] Use IkeaRemote to control devices and completely replace AudioSetup --- config.lua | 67 ++++++++++++++++---- src/devices/audio_setup.rs | 126 ------------------------------------- src/devices/hue_group.rs | 50 +-------------- src/devices/ikea_outlet.rs | 36 +---------- src/devices/kasa_outlet.rs | 17 ++++- src/devices/mod.rs | 4 -- 6 files changed, 71 insertions(+), 229 deletions(-) delete mode 100644 src/devices/audio_setup.rs diff --git a/config.lua b/config.lua index 1e67b57..20d7217 100644 --- a/config.lua +++ b/config.lua @@ -84,25 +84,60 @@ automation.device_manager:add(living_mixer) local living_speakers = KasaOutlet.new({ identifier = "living_speakers", ip = "10.0.0.127" }) automation.device_manager:add(living_speakers) -automation.device_manager:add(AudioSetup.new({ - identifier = "living_audio", - topic = mqtt_z2m("living/remote"), +automation.device_manager:add(IkeaRemote.new({ + name = "Remote", + room = "Living", client = mqtt_client, - mixer = living_mixer, - speakers = living_speakers, + topic = mqtt_z2m("living/remote"), + single_button = true, + callback = function(on) + if on then + if living_mixer:is_on() then + living_mixer:set_on(false) + living_speakers:set_on(false) + else + living_mixer:set_on(true) + living_speakers:set_on(true) + end + else + if not living_mixer:is_on() then + living_mixer:set_on(true) + else + living_speakers:set_on(not living_speakers:is_on()) + end + end + end, })) -automation.device_manager:add(IkeaOutlet.new({ +local kettle = IkeaOutlet.new({ outlet_type = "Kettle", name = "Kettle", room = "Kitchen", topic = mqtt_z2m("kitchen/kettle"), client = mqtt_client, timeout = debug and 5 or 300, - remotes = { - { topic = mqtt_z2m("bedroom/remote") }, - { topic = mqtt_z2m("kitchen/remote") }, - }, +}) +automation.device_manager:add(kettle) +function set_kettle(on) + kettle:set_on(on) +end + +automation.device_manager:add(IkeaRemote.new({ + name = "Remote", + room = "Bedroom", + client = mqtt_client, + topic = mqtt_z2m("bedroom/remote"), + single_button = true, + callback = set_kettle, +})) + +automation.device_manager:add(IkeaRemote.new({ + name = "Remote", + room = "Kitchen", + client = mqtt_client, + topic = mqtt_z2m("kitchen/remote"), + single_button = true, + callback = set_kettle, })) automation.device_manager:add(IkeaOutlet.new({ @@ -145,12 +180,18 @@ local hallway_lights = HueGroup.new({ group_id = 81, scene_id = "3qWKxGVadXFFG4o", timer_id = 1, - remotes = { - { topic = mqtt_z2m("hallway/remote") }, - }, client = mqtt_client, }) automation.device_manager:add(hallway_lights) +automation.device_manager:add(IkeaRemote.new({ + name = "Remote", + room = "Hallway", + client = mqtt_client, + topic = mqtt_z2m("hallway/remote"), + callback = function(on) + hallway_lights:set_on(on) + end, +})) automation.device_manager:add(ContactSensor.new({ identifier = "hallway_frontdoor", diff --git a/src/devices/audio_setup.rs b/src/devices/audio_setup.rs deleted file mode 100644 index 0022fb7..0000000 --- a/src/devices/audio_setup.rs +++ /dev/null @@ -1,126 +0,0 @@ -use async_trait::async_trait; -use automation_macro::LuaDeviceConfig; -use google_home::traits::OnOff; -use tracing::{debug, error, trace, warn}; - -use super::{Device, LuaDeviceCreate}; -use crate::config::MqttDeviceConfig; -use crate::error::DeviceConfigError; -use crate::event::{OnMqtt, OnPresence}; -use crate::messages::{RemoteAction, RemoteMessage}; -use crate::mqtt::WrappedAsyncClient; - -#[derive(Debug, Clone, LuaDeviceConfig)] -pub struct Config { - pub identifier: String, - #[device_config(flatten)] - pub mqtt: MqttDeviceConfig, - #[device_config(from_lua)] - pub mixer: Box, - #[device_config(from_lua)] - pub speakers: Box, - #[device_config(from_lua)] - pub client: WrappedAsyncClient, -} - -#[derive(Debug, Clone)] -pub struct AudioSetup { - config: Config, -} - -#[async_trait] -impl LuaDeviceCreate for AudioSetup { - type Config = Config; - type Error = DeviceConfigError; - - async fn create(config: Self::Config) -> Result { - trace!(id = config.identifier, "Setting up AudioSetup"); - - { - let mixer_id = config.mixer.get_id().to_owned(); - if (config.mixer.cast() as Option<&dyn OnOff>).is_none() { - return Err(DeviceConfigError::MissingTrait(mixer_id, "OnOff".into())); - } - - let speakers_id = config.speakers.get_id().to_owned(); - if (config.speakers.cast() as Option<&dyn OnOff>).is_none() { - return Err(DeviceConfigError::MissingTrait(speakers_id, "OnOff".into())); - } - } - - config - .client - .subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce) - .await?; - - Ok(AudioSetup { config }) - } -} - -impl Device for AudioSetup { - fn get_id(&self) -> String { - self.config.identifier.clone() - } -} - -#[async_trait] -impl OnMqtt for AudioSetup { - async fn on_mqtt(&self, message: rumqttc::Publish) { - if !rumqttc::matches(&message.topic, &self.config.mqtt.topic) { - return; - } - - let action = match RemoteMessage::try_from(message) { - Ok(message) => message.action(), - Err(err) => { - error!(id = self.get_id(), "Failed to parse message: {err}"); - return; - } - }; - - if let (Some(mixer), Some(speakers)) = ( - self.config.mixer.cast() as Option<&dyn OnOff>, - self.config.speakers.cast() as Option<&dyn OnOff>, - ) { - match action { - RemoteAction::On => { - if mixer.on().await.unwrap() { - speakers.set_on(false).await.unwrap(); - mixer.set_on(false).await.unwrap(); - } else { - speakers.set_on(true).await.unwrap(); - mixer.set_on(true).await.unwrap(); - } - }, - RemoteAction::BrightnessMoveUp => { - if !mixer.on().await.unwrap() { - mixer.set_on(true).await.unwrap(); - } else if speakers.on().await.unwrap() { - speakers.set_on(false).await.unwrap(); - } else { - speakers.set_on(true).await.unwrap(); - } - }, - RemoteAction::BrightnessStop => { /* Ignore this action */ }, - _ => warn!("Expected ikea shortcut button which only supports 'on' and 'brightness_move_up', got: {action:?}") - } - } - } -} - -#[async_trait] -impl OnPresence for AudioSetup { - async fn on_presence(&self, presence: bool) { - if let (Some(mixer), Some(speakers)) = ( - self.config.mixer.cast() as Option<&dyn OnOff>, - self.config.speakers.cast() as Option<&dyn OnOff>, - ) { - // Turn off the audio setup when we leave the house - if !presence { - debug!(id = self.get_id(), "Turning devices off"); - speakers.set_on(false).await.unwrap(); - mixer.set_on(false).await.unwrap(); - } - } - } -} diff --git a/src/devices/hue_group.rs b/src/devices/hue_group.rs index d98b3cb..fc5c4dd 100644 --- a/src/devices/hue_group.rs +++ b/src/devices/hue_group.rs @@ -6,13 +6,9 @@ use async_trait::async_trait; use automation_macro::LuaDeviceConfig; use google_home::errors::ErrorCode; use google_home::traits::OnOff; -use rumqttc::{Publish, SubscribeFilter}; -use tracing::{debug, error, trace, warn}; +use tracing::{error, trace, warn}; use super::{Device, LuaDeviceCreate}; -use crate::config::MqttDeviceConfig; -use crate::event::OnMqtt; -use crate::messages::{RemoteAction, RemoteMessage}; use crate::mqtt::WrappedAsyncClient; use crate::traits::Timeout; @@ -25,8 +21,6 @@ pub struct Config { pub group_id: isize, pub timer_id: isize, pub scene_id: String, - #[device_config(default)] - pub remotes: Vec, #[device_config(from_lua)] pub client: WrappedAsyncClient, } @@ -45,16 +39,6 @@ impl LuaDeviceCreate for HueGroup { async fn create(config: Self::Config) -> Result { trace!(id = config.identifier, "Setting up AudioSetup"); - if !config.remotes.is_empty() { - config - .client - .subscribe_many(config.remotes.iter().map(|remote| SubscribeFilter { - path: remote.topic.clone(), - qos: rumqttc::QoS::AtLeastOnce, - })) - .await?; - } - Ok(Self { config }) } } @@ -83,38 +67,6 @@ impl Device for HueGroup { } } -#[async_trait] -impl OnMqtt for HueGroup { - async fn on_mqtt(&self, message: Publish) { - if !self - .config - .remotes - .iter() - .any(|remote| rumqttc::matches(&message.topic, &remote.topic)) - { - return; - } - - let action = match RemoteMessage::try_from(message) { - Ok(message) => message.action(), - Err(err) => { - error!(id = self.get_id(), "Failed to parse message: {err}"); - return; - } - }; - - debug!("Action: {action:#?}"); - - match action { - RemoteAction::On | RemoteAction::BrightnessMoveUp => self.set_on(true).await.unwrap(), - RemoteAction::Off | RemoteAction::BrightnessMoveDown => { - self.set_on(false).await.unwrap() - } - RemoteAction::BrightnessStop => { /* Ignore this action */ } - }; - } -} - #[async_trait] impl OnOff for HueGroup { async fn set_on(&self, on: bool) -> Result<(), ErrorCode> { diff --git a/src/devices/ikea_outlet.rs b/src/devices/ikea_outlet.rs index 862730f..5ae3c3b 100644 --- a/src/devices/ikea_outlet.rs +++ b/src/devices/ikea_outlet.rs @@ -8,7 +8,7 @@ use google_home::device; use google_home::errors::ErrorCode; use google_home::traits::{self, OnOff}; use google_home::types::Type; -use rumqttc::{matches, Publish, SubscribeFilter}; +use rumqttc::{matches, Publish}; use serde::Deserialize; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use tokio::task::JoinHandle; @@ -18,7 +18,7 @@ use super::LuaDeviceCreate; use crate::config::{InfoConfig, MqttDeviceConfig}; use crate::devices::Device; use crate::event::{OnMqtt, OnPresence}; -use crate::messages::{OnOffMessage, RemoteAction, RemoteMessage}; +use crate::messages::OnOffMessage; use crate::mqtt::WrappedAsyncClient; use crate::traits::Timeout; @@ -40,8 +40,6 @@ pub struct Config { pub outlet_type: OutletType, #[device_config(default, with(|t: Option<_>| t.map(Duration::from_secs)))] pub timeout: Option, - #[device_config(default)] - pub remotes: Vec, #[device_config(from_lua)] pub client: WrappedAsyncClient, @@ -78,16 +76,6 @@ impl LuaDeviceCreate for IkeaOutlet { async fn create(config: Self::Config) -> Result { trace!(id = config.info.identifier(), "Setting up IkeaOutlet"); - if !config.remotes.is_empty() { - config - .client - .subscribe_many(config.remotes.iter().map(|remote| SubscribeFilter { - path: remote.topic.clone(), - qos: rumqttc::QoS::AtLeastOnce, - })) - .await?; - } - config .client .subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce) @@ -138,26 +126,6 @@ impl OnMqtt for IkeaOutlet { if state && let Some(timeout) = self.config.timeout { self.start_timeout(timeout).await.unwrap(); } - } else if self - .config - .remotes - .iter() - .any(|remote| rumqttc::matches(&message.topic, &remote.topic)) - { - let action = match RemoteMessage::try_from(message) { - Ok(message) => message.action(), - Err(err) => { - error!(id = Device::get_id(self), "Failed to parse message: {err}"); - return; - } - }; - - match action { - RemoteAction::On => self.set_on(true).await.unwrap(), - RemoteAction::BrightnessMoveUp => self.set_on(false).await.unwrap(), - RemoteAction::BrightnessStop => { /* Ignore this action */ }, - _ => warn!("Expected ikea shortcut button which only supports 'on' and 'brightness_move_up', got: {action:?}") - } } } } diff --git a/src/devices/kasa_outlet.rs b/src/devices/kasa_outlet.rs index f510b7c..4f0530e 100644 --- a/src/devices/kasa_outlet.rs +++ b/src/devices/kasa_outlet.rs @@ -6,14 +6,15 @@ use async_trait::async_trait; use automation_macro::LuaDeviceConfig; use bytes::{Buf, BufMut}; use google_home::errors::{self, DeviceError}; -use google_home::traits; +use google_home::traits::OnOff; use serde::{Deserialize, Serialize}; use thiserror::Error; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; -use tracing::trace; +use tracing::{debug, trace}; use super::{Device, LuaDeviceCreate}; +use crate::event::OnPresence; #[derive(Debug, Clone, LuaDeviceConfig)] pub struct Config { @@ -206,7 +207,7 @@ impl Response { } #[async_trait] -impl traits::OnOff for KasaOutlet { +impl OnOff for KasaOutlet { async fn on(&self) -> Result { let mut stream = TcpStream::connect(self.config.addr) .await @@ -275,3 +276,13 @@ impl traits::OnOff for KasaOutlet { .or(Err(DeviceError::TransientError.into())) } } + +#[async_trait] +impl OnPresence for KasaOutlet { + async fn on_presence(&self, presence: bool) { + if !presence { + debug!(id = Device::get_id(self), "Turning device off"); + self.set_on(false).await.ok(); + } + } +} diff --git a/src/devices/mod.rs b/src/devices/mod.rs index 597e52b..cb39ac1 100644 --- a/src/devices/mod.rs +++ b/src/devices/mod.rs @@ -1,5 +1,4 @@ mod air_filter; -mod audio_setup; mod contact_sensor; mod debug_bridge; mod hue_bridge; @@ -23,7 +22,6 @@ use google_home::traits::OnOff; use mlua::ObjectLike; pub use self::air_filter::AirFilter; -pub use self::audio_setup::AudioSetup; pub use self::contact_sensor::ContactSensor; pub use self::debug_bridge::DebugBridge; pub use self::hue_bridge::HueBridge; @@ -100,7 +98,6 @@ macro_rules! impl_device { } impl_device!(AirFilter); -impl_device!(AudioSetup); impl_device!(ContactSensor); impl_device!(DebugBridge); impl_device!(HueBridge); @@ -116,7 +113,6 @@ impl_device!(Washer); pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> { register_device!(lua, AirFilter); - register_device!(lua, AudioSetup); register_device!(lua, ContactSensor); register_device!(lua, DebugBridge); register_device!(lua, HueBridge);