use async_trait::async_trait; use automation_macro::{LuaDevice, LuaDeviceConfig}; use google_home::traits::OnOff; use tracing::{debug, error, trace, warn}; use super::Device; use crate::config::MqttDeviceConfig; use crate::device_manager::{DeviceConfig, WrappedDevice}; use crate::devices::As; use crate::error::DeviceConfigError; use crate::event::{OnMqtt, OnPresence}; use crate::messages::{RemoteAction, RemoteMessage}; #[derive(Debug, Clone, LuaDeviceConfig)] pub struct AudioSetupConfig { #[device_config(flatten)] mqtt: MqttDeviceConfig, #[device_config(from_lua)] mixer: WrappedDevice, #[device_config(from_lua)] speakers: WrappedDevice, } #[async_trait] impl DeviceConfig for AudioSetupConfig { async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { trace!(id = identifier, "Setting up AudioSetup"); let mixer_id = self.mixer.read().await.get_id().to_owned(); if !As::::is(self.mixer.read().await.as_ref()) { return Err(DeviceConfigError::MissingTrait(mixer_id, "OnOff".into())); } let speakers_id = self.speakers.read().await.get_id().to_owned(); if !As::::is(self.speakers.read().await.as_ref()) { return Err(DeviceConfigError::MissingTrait(speakers_id, "OnOff".into())); } let device = AudioSetup { identifier: identifier.into(), config: self.clone(), }; Ok(Box::new(device)) } } // TODO: We need a better way to store the children devices #[derive(Debug, LuaDevice)] pub struct AudioSetup { identifier: String, #[config] config: AudioSetupConfig, } impl Device for AudioSetup { fn get_id(&self) -> &str { &self.identifier } } #[async_trait] impl OnMqtt for AudioSetup { fn topics(&self) -> Vec<&str> { vec![&self.config.mqtt.topic] } async fn on_mqtt(&mut self, message: rumqttc::Publish) { let action = match RemoteMessage::try_from(message) { Ok(message) => message.action(), Err(err) => { error!(id = self.identifier, "Failed to parse message: {err}"); return; } }; let mut mixer = self.config.mixer.write().await; let mut speakers = self.config.speakers.write().await; if let (Some(mixer), Some(speakers)) = ( As::::cast_mut(mixer.as_mut()), As::::cast_mut(speakers.as_mut()), ) { match action { RemoteAction::On => { if mixer.is_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.is_on().await.unwrap() { mixer.set_on(true).await.unwrap(); } else if speakers.is_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(&mut self, presence: bool) { let mut mixer = self.config.mixer.write().await; let mut speakers = self.config.speakers.write().await; if let (Some(mixer), Some(speakers)) = ( As::::cast_mut(mixer.as_mut()), As::::cast_mut(speakers.as_mut()), ) { // Turn off the audio setup when we leave the house if !presence { debug!(id = self.identifier, "Turning devices off"); speakers.set_on(false).await.unwrap(); mixer.set_on(false).await.unwrap(); } } } }