Replaced impl_cast with a new and improved trait
All checks were successful
Build and deploy automation_rs / Build automation_rs (push) Successful in 4m0s
Build and deploy automation_rs / Build Docker image (push) Successful in 52s
Build and deploy automation_rs / Deploy Docker container (push) Successful in 28s

With this trait the impl_cast macros are no longer needed, simplifying
everything.
This commit also improved how the actual casting itself is handled.
This commit is contained in:
2024-05-05 00:33:21 +02:00
parent cde9654a78
commit 3689a52afd
16 changed files with 174 additions and 332 deletions

View File

@@ -12,7 +12,7 @@ use tokio_cron_scheduler::{Job, JobScheduler};
use tracing::{debug, error, instrument, trace};
use crate::devices::{
AirFilterConfig, As, AudioSetupConfig, ContactSensorConfig, DebugBridgeConfig, Device,
AirFilterConfig, AudioSetupConfig, ContactSensorConfig, DebugBridgeConfig, Device,
HueBridgeConfig, HueGroupConfig, IkeaOutletConfig, KasaOutletConfig, LightSensorConfig,
WakeOnLANConfig, WasherConfig,
};
@@ -106,22 +106,22 @@ impl DeviceManager {
let device = manager.get(&target).await.unwrap();
match action {
Action::On => {
As::<dyn OnOff>::cast_mut(
device.write().await.as_mut(),
)
.unwrap()
.set_on(true)
.await
.unwrap();
let mut device = device.write().await;
let device: Option<&mut dyn OnOff> =
device.as_mut().cast_mut();
if let Some(device) = device {
device.set_on(true).await.unwrap();
}
}
Action::Off => {
As::<dyn OnOff>::cast_mut(
device.write().await.as_mut(),
)
.unwrap()
.set_on(false)
.await
.unwrap();
let mut device = device.write().await;
let device: Option<&mut dyn OnOff> =
device.as_mut().cast_mut();
if let Some(device) = device {
device.set_on(false).await.unwrap();
}
}
}
}
@@ -142,14 +142,17 @@ impl DeviceManager {
debug!(id, "Adding device");
// If the device listens to mqtt, subscribe to the topics
if let Some(device) = As::<dyn OnMqtt>::cast(device.as_ref()) {
for topic in device.topics() {
trace!(id, topic, "Subscribing to topic");
if let Err(err) = self.client.subscribe(topic, QoS::AtLeastOnce).await {
// NOTE: Pretty sure that this can only happen if the mqtt client if no longer
// running
error!(id, topic, "Failed to subscribe to topic: {err}");
{
// If the device listens to mqtt, subscribe to the topics
let device: Option<&dyn OnMqtt> = device.as_ref().cast();
if let Some(device) = device {
for topic in device.topics() {
trace!(id, topic, "Subscribing to topic");
if let Err(err) = self.client.subscribe(topic, QoS::AtLeastOnce).await {
// NOTE: Pretty sure that this can only happen if the mqtt client if no longer
// running
error!(id, topic, "Failed to subscribe to topic: {err}");
}
}
}
}
@@ -199,8 +202,8 @@ impl DeviceManager {
let message = message.clone();
async move {
let mut device = device.write().await;
let device = device.as_mut();
if let Some(device) = As::<dyn OnMqtt>::cast_mut(device) {
let device: Option<&mut dyn OnMqtt> = device.as_mut().cast_mut();
if let Some(device) = device {
let subscribed = device
.topics()
.iter()
@@ -220,8 +223,8 @@ impl DeviceManager {
let devices = self.devices.read().await;
let iter = devices.iter().map(|(id, device)| async move {
let mut device = device.write().await;
let device = device.as_mut();
if let Some(device) = As::<dyn OnDarkness>::cast_mut(device) {
let device: Option<&mut dyn OnDarkness> = device.as_mut().cast_mut();
if let Some(device) = device {
trace!(id, "Handling");
device.on_darkness(dark).await;
}
@@ -233,8 +236,8 @@ impl DeviceManager {
let devices = self.devices.read().await;
let iter = devices.iter().map(|(id, device)| async move {
let mut device = device.write().await;
let device = device.as_mut();
if let Some(device) = As::<dyn OnPresence>::cast_mut(device) {
let device: Option<&mut dyn OnPresence> = device.as_mut().cast_mut();
if let Some(device) = device {
trace!(id, "Handling");
device.on_presence(presence).await;
}
@@ -248,8 +251,8 @@ impl DeviceManager {
let notification = notification.clone();
async move {
let mut device = device.write().await;
let device = device.as_mut();
if let Some(device) = As::<dyn OnNotification>::cast_mut(device) {
let device: Option<&mut dyn OnNotification> = device.as_mut().cast_mut();
if let Some(device) = device {
trace!(id, "Handling");
device.on_notification(notification).await;
}

View File

@@ -6,7 +6,6 @@ use tracing::{debug, error, trace, warn};
use super::Device;
use crate::config::MqttDeviceConfig;
use crate::device_manager::{ConfigExternal, DeviceConfig, WrappedDevice};
use crate::devices::As;
use crate::error::DeviceConfigError;
use crate::event::{OnMqtt, OnPresence};
use crate::messages::{RemoteAction, RemoteMessage};
@@ -39,8 +38,11 @@ impl DeviceConfig for AudioSetupConfig {
self.mixer.clone(),
))?;
if !As::<dyn OnOff>::is(mixer.read().await.as_ref()) {
return Err(DeviceConfigError::MissingTrait(self.mixer, "OnOff".into()));
{
let mixer = mixer.read().await;
if (mixer.as_ref().cast() as Option<&dyn OnOff>).is_none() {
return Err(DeviceConfigError::MissingTrait(self.mixer, "OnOff".into()));
}
}
let speakers =
@@ -52,11 +54,11 @@ impl DeviceConfig for AudioSetupConfig {
self.speakers.clone(),
))?;
if !As::<dyn OnOff>::is(speakers.read().await.as_ref()) {
return Err(DeviceConfigError::MissingTrait(
self.speakers,
"OnOff".into(),
));
{
let speakers = speakers.read().await;
if (speakers.as_ref().cast() as Option<&dyn OnOff>).is_none() {
return Err(DeviceConfigError::MissingTrait(self.mixer, "OnOff".into()));
}
}
let device = AudioSetup {
@@ -103,8 +105,8 @@ impl OnMqtt for AudioSetup {
let mut mixer = self.mixer.write().await;
let mut speakers = self.speakers.write().await;
if let (Some(mixer), Some(speakers)) = (
As::<dyn OnOff>::cast_mut(mixer.as_mut()),
As::<dyn OnOff>::cast_mut(speakers.as_mut()),
mixer.as_mut().cast_mut() as Option<&mut dyn OnOff>,
speakers.as_mut().cast_mut() as Option<&mut dyn OnOff>,
) {
match action {
RemoteAction::On => {
@@ -137,10 +139,9 @@ impl OnPresence for AudioSetup {
async fn on_presence(&mut self, presence: bool) {
let mut mixer = self.mixer.write().await;
let mut speakers = self.speakers.write().await;
if let (Some(mixer), Some(speakers)) = (
As::<dyn OnOff>::cast_mut(mixer.as_mut()),
As::<dyn OnOff>::cast_mut(speakers.as_mut()),
mixer.as_mut().cast_mut() as Option<&mut dyn OnOff>,
speakers.as_mut().cast_mut() as Option<&mut dyn OnOff>,
) {
// Turn off the audio setup when we leave the house
if !presence {

View File

@@ -11,7 +11,7 @@ use tracing::{debug, error, trace, warn};
use super::Device;
use crate::config::MqttDeviceConfig;
use crate::device_manager::{ConfigExternal, DeviceConfig, WrappedDevice};
use crate::devices::{As, DEFAULT_PRESENCE};
use crate::devices::DEFAULT_PRESENCE;
use crate::error::DeviceConfigError;
use crate::event::{OnMqtt, OnPresence};
use crate::messages::{ContactMessage, PresenceMessage};
@@ -60,20 +60,23 @@ impl DeviceConfig for ContactSensorConfig {
DeviceConfigError::MissingChild(device_name.into(), "OnOff".into()),
)?;
if !As::<dyn OnOff>::is(device.read().await.as_ref()) {
return Err(DeviceConfigError::MissingTrait(
device_name.into(),
"OnOff".into(),
));
}
if !trigger_config.timeout.is_zero()
&& !As::<dyn Timeout>::is(device.read().await.as_ref())
{
return Err(DeviceConfigError::MissingTrait(
device_name.into(),
"Timeout".into(),
));
let device = device.read().await;
if (device.as_ref().cast() as Option<&dyn OnOff>).is_none() {
return Err(DeviceConfigError::MissingTrait(
device_name.into(),
"OnOff".into(),
));
}
if trigger_config.timeout.is_zero()
&& (device.as_ref().cast() as Option<&dyn Timeout>).is_none()
{
return Err(DeviceConfigError::MissingTrait(
device_name.into(),
"Timeout".into(),
));
}
}
devices.push((device, false));
@@ -161,7 +164,7 @@ impl OnMqtt for ContactSensor {
if !self.is_closed {
for (light, previous) in &mut trigger.devices {
let mut light = light.write().await;
if let Some(light) = As::<dyn OnOff>::cast_mut(light.as_mut()) {
if let Some(light) = light.as_mut().cast_mut() as Option<&mut dyn OnOff> {
*previous = light.is_on().await.unwrap();
light.set_on(true).await.ok();
}
@@ -172,10 +175,12 @@ impl OnMqtt for ContactSensor {
if !previous {
// If the timeout is zero just turn the light off directly
if trigger.timeout.is_zero()
&& let Some(light) = As::<dyn OnOff>::cast_mut(light.as_mut())
&& let Some(light) = light.as_mut().cast_mut() as Option<&mut dyn OnOff>
{
light.set_on(false).await.ok();
} else if let Some(light) = As::<dyn Timeout>::cast_mut(light.as_mut()) {
} else if let Some(light) =
light.as_mut().cast_mut() as Option<&mut dyn Timeout>
{
light.start_timeout(trigger.timeout).await.unwrap();
}
// TODO: Put a warning/error on creation if either of this has to option to fail

View File

@@ -12,8 +12,11 @@ mod presence;
mod wake_on_lan;
mod washer;
use google_home::device::AsGoogleHomeDevice;
use std::fmt::Debug;
use automation_cast::Cast;
use google_home::traits::OnOff;
use google_home::GoogleHomeDevice;
pub use self::air_filter::AirFilterConfig;
pub use self::audio_setup::AudioSetupConfig;
@@ -31,7 +34,18 @@ pub use self::washer::WasherConfig;
use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence};
use crate::traits::Timeout;
#[impl_cast::device(As: OnMqtt + OnPresence + OnDarkness + OnNotification + OnOff + Timeout)]
pub trait Device: AsGoogleHomeDevice + std::fmt::Debug + Sync + Send {
pub trait Device:
Debug
+ Sync
+ Send
+ Cast<dyn GoogleHomeDevice>
+ Cast<dyn OnMqtt>
+ Cast<dyn OnMqtt>
+ Cast<dyn OnPresence>
+ Cast<dyn OnDarkness>
+ Cast<dyn OnNotification>
+ Cast<dyn OnOff>
+ Cast<dyn Timeout>
{
fn get_id(&self) -> &str;
}

View File

@@ -1,5 +1,4 @@
use async_trait::async_trait;
use impl_cast::device_trait;
use rumqttc::Publish;
use tokio::sync::mpsc;
@@ -32,26 +31,22 @@ impl EventChannel {
}
#[async_trait]
#[device_trait]
pub trait OnMqtt {
pub trait OnMqtt: Sync + Send {
fn topics(&self) -> Vec<&str>;
async fn on_mqtt(&mut self, message: Publish);
}
#[async_trait]
#[device_trait]
pub trait OnPresence {
pub trait OnPresence: Sync + Send {
async fn on_presence(&mut self, presence: bool);
}
#[async_trait]
#[device_trait]
pub trait OnDarkness {
pub trait OnDarkness: Sync + Send {
async fn on_darkness(&mut self, dark: bool);
}
#[async_trait]
#[device_trait]
pub trait OnNotification {
pub trait OnNotification: Sync + Send {
async fn on_notification(&mut self, notification: Notification);
}

View File

@@ -2,11 +2,9 @@ use std::time::Duration;
use anyhow::Result;
use async_trait::async_trait;
use impl_cast::device_trait;
#[async_trait]
#[device_trait]
pub trait Timeout {
pub trait Timeout: Sync + Send {
async fn start_timeout(&mut self, _timeout: Duration) -> Result<()>;
async fn stop_timeout(&mut self) -> Result<()>;
}