Contact sensor can now turn on device when opened and turn them off again after a timeout
This commit is contained in:
parent
12ca577a65
commit
b97b682a5e
|
@ -42,7 +42,7 @@ outlet_type = "Light"
|
|||
name = "Bathroom light"
|
||||
room = "Bathroom"
|
||||
topic = "zigbee2mqtt/bathroom/light"
|
||||
timeout = 5
|
||||
timeout = 60
|
||||
|
||||
[devices.workbench_charger]
|
||||
type = "IkeaOutlet"
|
||||
|
@ -76,10 +76,11 @@ ip = "10.0.0.182"
|
|||
[devices.living_audio]
|
||||
type = "AudioSetup"
|
||||
topic = "zigbee2mqtt/living/remote"
|
||||
mixer = "light_sensor"
|
||||
mixer = "living_mixer"
|
||||
speakers = "living_speakers"
|
||||
|
||||
[devices.hallway_frontdoor]
|
||||
type = "ContactSensor"
|
||||
topic = "zigbee2mqtt/hallway/frontdoor"
|
||||
presence = { timeout = 10 }
|
||||
lights = { lights = ["bathroom_light"], timeout = 10 }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use google_home::traits::OnOff;
|
||||
use rumqttc::{has_wildcards, AsyncClient};
|
||||
use serde::Deserialize;
|
||||
use tokio::task::JoinHandle;
|
||||
|
@ -8,13 +9,14 @@ use tracing::{debug, error, trace, warn};
|
|||
|
||||
use crate::{
|
||||
config::{CreateDevice, MqttDeviceConfig},
|
||||
device_manager::DeviceManager,
|
||||
devices::DEFAULT_PRESENCE,
|
||||
device_manager::{DeviceManager, WrappedDevice},
|
||||
devices::{As, DEFAULT_PRESENCE},
|
||||
error::{CreateDeviceError, MissingWildcard},
|
||||
event::EventChannel,
|
||||
event::OnMqtt,
|
||||
event::OnPresence,
|
||||
messages::{ContactMessage, PresenceMessage},
|
||||
traits::Timeout,
|
||||
};
|
||||
|
||||
use super::Device;
|
||||
|
@ -54,11 +56,24 @@ impl PresenceDeviceConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct LightsConfig {
|
||||
lights: Vec<String>,
|
||||
timeout: u64, // Timeout in seconds
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct ContactSensorConfig {
|
||||
#[serde(flatten)]
|
||||
mqtt: MqttDeviceConfig,
|
||||
presence: Option<PresenceDeviceConfig>,
|
||||
lights: Option<LightsConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Lights {
|
||||
lights: Vec<(WrappedDevice, bool)>,
|
||||
timeout: Duration, // Timeout in seconds
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -71,6 +86,8 @@ pub struct ContactSensor {
|
|||
overall_presence: bool,
|
||||
is_closed: bool,
|
||||
handle: Option<JoinHandle<()>>,
|
||||
|
||||
lights: Option<Lights>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -83,7 +100,7 @@ impl CreateDevice for ContactSensor {
|
|||
_event_channel: &EventChannel,
|
||||
client: &AsyncClient,
|
||||
presence_topic: &str,
|
||||
_device_manager: &DeviceManager,
|
||||
device_manager: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError> {
|
||||
trace!(id = identifier, "Setting up ContactSensor");
|
||||
|
||||
|
@ -92,6 +109,32 @@ impl CreateDevice for ContactSensor {
|
|||
.map(|p| p.generate_topic("contact", identifier, presence_topic))
|
||||
.transpose()?;
|
||||
|
||||
let lights = if let Some(lights_config) = config.lights {
|
||||
let mut lights = Vec::new();
|
||||
for name in lights_config.lights {
|
||||
let light = device_manager
|
||||
.get(&name)
|
||||
.await
|
||||
.ok_or(CreateDeviceError::DeviceDoesNotExist(name.clone()))?;
|
||||
|
||||
{
|
||||
let light = light.read().await;
|
||||
if As::<dyn OnOff>::cast(light.as_ref()).is_none() {
|
||||
return Err(CreateDeviceError::OnOffExpected(name));
|
||||
}
|
||||
}
|
||||
|
||||
lights.push((light, false));
|
||||
}
|
||||
|
||||
Some(Lights {
|
||||
lights,
|
||||
timeout: Duration::from_secs(lights_config.timeout),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
identifier: identifier.to_owned(),
|
||||
mqtt: config.mqtt,
|
||||
|
@ -100,6 +143,7 @@ impl CreateDevice for ContactSensor {
|
|||
overall_presence: DEFAULT_PRESENCE,
|
||||
is_closed: true,
|
||||
handle: None,
|
||||
lights,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -139,6 +183,25 @@ impl OnMqtt for ContactSensor {
|
|||
debug!(id = self.identifier, "Updating state to {is_closed}");
|
||||
self.is_closed = is_closed;
|
||||
|
||||
if let Some(lights) = &mut self.lights {
|
||||
if !self.is_closed {
|
||||
for (light, previous) in &mut lights.lights {
|
||||
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();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (light, previous) in &lights.lights {
|
||||
let mut light = light.write().await;
|
||||
if !previous && let Some(light) = As::<dyn Timeout>::cast_mut(light.as_mut()) {
|
||||
light.start_timeout(lights.timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this contact sensor works as a presence device
|
||||
// If not we are done here
|
||||
let presence = match &self.presence {
|
||||
|
|
Loading…
Reference in New Issue
Block a user