Use helper types to process config input into the right type
All checks were successful
Build and deploy automation_rs / Build automation_rs (push) Successful in 5m18s
Build and deploy automation_rs / Build Docker image (push) Successful in 41s
Build and deploy automation_rs / Deploy Docker container (push) Has been skipped

This commit is contained in:
Dreaded_X 2024-04-25 05:38:21 +02:00
parent 54c4b70d19
commit 396ac4a9f1
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4
4 changed files with 54 additions and 39 deletions

View File

@ -3,6 +3,7 @@ use std::time::Duration;
use async_trait::async_trait;
use automation_macro::{LuaDevice, LuaDeviceConfig};
use google_home::traits::OnOff;
use mlua::FromLua;
use tokio::task::JoinHandle;
use tracing::{debug, error, trace, warn};
@ -26,10 +27,25 @@ pub struct PresenceDeviceConfig {
pub timeout: Duration,
}
#[derive(Debug, Clone)]
struct TriggerDevicesHelper(Vec<WrappedDevice>);
impl<'lua> FromLua<'lua> for TriggerDevicesHelper {
fn from_lua(value: mlua::Value<'lua>, lua: &'lua mlua::Lua) -> mlua::Result<Self> {
Ok(TriggerDevicesHelper(mlua::FromLua::from_lua(value, lua)?))
}
}
impl From<TriggerDevicesHelper> for Vec<(WrappedDevice, bool)> {
fn from(value: TriggerDevicesHelper) -> Self {
value.0.into_iter().map(|device| (device, false)).collect()
}
}
#[derive(Debug, Clone, LuaDeviceConfig)]
pub struct TriggerConfig {
#[device_config(user_data)]
devices: Vec<WrappedDevice>,
#[device_config(user_data, with = "TriggerDevicesHelper")]
devices: Vec<(WrappedDevice, bool)>,
#[device_config(with = "Option<DurationSeconds>")]
pub timeout: Option<Duration>,
}
@ -51,30 +67,20 @@ impl DeviceConfig for ContactSensorConfig {
async fn create(&self, identifier: &str) -> Result<Box<dyn Device>, DeviceConfigError> {
trace!(id = identifier, "Setting up ContactSensor");
let trigger = if let Some(trigger_config) = &self.trigger {
let mut devices = Vec::new();
for device in &trigger_config.devices {
// Make sure the devices implement the required traits
if let Some(trigger) = &self.trigger {
for (device, _) in &trigger.devices {
let id = device.read().await.get_id().to_owned();
if !As::<dyn OnOff>::is(device.read().await.as_ref()) {
return Err(DeviceConfigError::MissingTrait(id, "OnOff".into()));
}
if trigger_config.timeout.is_none()
&& !As::<dyn Timeout>::is(device.read().await.as_ref())
if trigger.timeout.is_none() && !As::<dyn Timeout>::is(device.read().await.as_ref())
{
return Err(DeviceConfigError::MissingTrait(id, "Timeout".into()));
}
devices.push((device.clone(), false));
}
Some(Trigger {
devices,
timeout: trigger_config.timeout,
})
} else {
None
};
}
let device = ContactSensor {
identifier: identifier.into(),
@ -82,19 +88,12 @@ impl DeviceConfig for ContactSensorConfig {
overall_presence: DEFAULT_PRESENCE,
is_closed: true,
handle: None,
trigger,
};
Ok(Box::new(device))
}
}
#[derive(Debug)]
struct Trigger {
devices: Vec<(WrappedDevice, bool)>,
timeout: Option<Duration>,
}
#[derive(Debug, LuaDevice)]
pub struct ContactSensor {
identifier: String,
@ -104,8 +103,6 @@ pub struct ContactSensor {
overall_presence: bool,
is_closed: bool,
handle: Option<JoinHandle<()>>,
trigger: Option<Trigger>,
}
impl Device for ContactSensor {
@ -143,11 +140,12 @@ impl OnMqtt for ContactSensor {
debug!(id = self.identifier, "Updating state to {is_closed}");
self.is_closed = is_closed;
if let Some(trigger) = &mut self.trigger {
if let Some(trigger) = &mut self.config.trigger {
if !self.is_closed {
for (light, _) in &mut trigger.devices {
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()) {
*previous = light.is_on().await.unwrap();
light.set_on(true).await.ok();
}
}

View File

@ -7,7 +7,8 @@ use crate::config::MqttDeviceConfig;
use crate::device_manager::DeviceConfig;
use crate::devices::Device;
use crate::error::DeviceConfigError;
use crate::event::{self, Event, EventChannel, OnMqtt};
use crate::event::{self, Event, OnMqtt};
use crate::helper::TxHelper;
use crate::messages::BrightnessMessage;
#[derive(Debug, Clone, LuaDeviceConfig)]
@ -16,8 +17,8 @@ pub struct LightSensorConfig {
pub mqtt: MqttDeviceConfig,
pub min: isize,
pub max: isize,
#[device_config(user_data)]
pub event_channel: EventChannel,
#[device_config(rename = "event_channel", user_data, with = "TxHelper")]
pub tx: event::Sender,
}
pub const DEFAULT: bool = false;
@ -30,7 +31,6 @@ impl DeviceConfig for LightSensorConfig {
let device = LightSensor {
identifier: identifier.into(),
// Add helper type that does this conversion for us
tx: self.event_channel.get_tx(),
config: self.clone(),
is_dark: DEFAULT,
};
@ -45,7 +45,6 @@ pub struct LightSensor {
#[config]
config: LightSensorConfig,
tx: event::Sender,
is_dark: bool,
}
@ -91,7 +90,7 @@ impl OnMqtt for LightSensor {
debug!("Dark state has changed: {is_dark}");
self.is_dark = is_dark;
if self.tx.send(Event::Darkness(is_dark)).await.is_err() {
if self.config.tx.send(Event::Darkness(is_dark)).await.is_err() {
warn!("There are no receivers on the event channel");
}
}

View File

@ -8,7 +8,8 @@ use super::{Device, Notification};
use crate::config::MqttDeviceConfig;
use crate::device_manager::DeviceConfig;
use crate::error::DeviceConfigError;
use crate::event::{Event, EventChannel, OnMqtt};
use crate::event::{self, Event, OnMqtt};
use crate::helper::TxHelper;
use crate::messages::PowerMessage;
#[derive(Debug, Clone, LuaDeviceConfig)]
@ -17,8 +18,8 @@ pub struct WasherConfig {
mqtt: MqttDeviceConfig,
// Power in Watt
threshold: f32,
#[device_config(user_data)]
event_channel: EventChannel,
#[device_config(rename = "event_channel", user_data, with = "TxHelper")]
pub tx: event::Sender,
}
#[async_trait]
@ -91,8 +92,7 @@ impl OnMqtt for Washer {
if self
.config
.event_channel
.get_tx()
.tx
.send(Event::Ntfy(notification))
.await
.is_err()

View File

@ -1,8 +1,11 @@
use std::net::{Ipv4Addr, SocketAddr};
use std::time::Duration;
use mlua::FromLua;
use serde::Deserialize;
use crate::event::{self, EventChannel};
#[derive(Debug, Deserialize)]
pub struct DurationSeconds(u64);
@ -20,3 +23,18 @@ impl<const PORT: u16> From<Ipv4SocketAddr<PORT>> for SocketAddr {
Self::from((ip.0, PORT))
}
}
#[derive(Debug, Clone)]
pub struct TxHelper(EventChannel);
impl<'lua> FromLua<'lua> for TxHelper {
fn from_lua(value: mlua::Value<'lua>, lua: &'lua mlua::Lua) -> mlua::Result<Self> {
Ok(TxHelper(mlua::FromLua::from_lua(value, lua)?))
}
}
impl From<TxHelper> for event::Sender {
fn from(value: TxHelper) -> Self {
value.0.get_tx()
}
}