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"
|
name = "Bathroom light"
|
||||||
room = "Bathroom"
|
room = "Bathroom"
|
||||||
topic = "zigbee2mqtt/bathroom/light"
|
topic = "zigbee2mqtt/bathroom/light"
|
||||||
timeout = 5
|
timeout = 60
|
||||||
|
|
||||||
[devices.workbench_charger]
|
[devices.workbench_charger]
|
||||||
type = "IkeaOutlet"
|
type = "IkeaOutlet"
|
||||||
|
@ -76,10 +76,11 @@ ip = "10.0.0.182"
|
||||||
[devices.living_audio]
|
[devices.living_audio]
|
||||||
type = "AudioSetup"
|
type = "AudioSetup"
|
||||||
topic = "zigbee2mqtt/living/remote"
|
topic = "zigbee2mqtt/living/remote"
|
||||||
mixer = "light_sensor"
|
mixer = "living_mixer"
|
||||||
speakers = "living_speakers"
|
speakers = "living_speakers"
|
||||||
|
|
||||||
[devices.hallway_frontdoor]
|
[devices.hallway_frontdoor]
|
||||||
type = "ContactSensor"
|
type = "ContactSensor"
|
||||||
topic = "zigbee2mqtt/hallway/frontdoor"
|
topic = "zigbee2mqtt/hallway/frontdoor"
|
||||||
presence = { timeout = 10 }
|
presence = { timeout = 10 }
|
||||||
|
lights = { lights = ["bathroom_light"], timeout = 10 }
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use google_home::traits::OnOff;
|
||||||
use rumqttc::{has_wildcards, AsyncClient};
|
use rumqttc::{has_wildcards, AsyncClient};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
@ -8,13 +9,14 @@ use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{CreateDevice, MqttDeviceConfig},
|
config::{CreateDevice, MqttDeviceConfig},
|
||||||
device_manager::DeviceManager,
|
device_manager::{DeviceManager, WrappedDevice},
|
||||||
devices::DEFAULT_PRESENCE,
|
devices::{As, DEFAULT_PRESENCE},
|
||||||
error::{CreateDeviceError, MissingWildcard},
|
error::{CreateDeviceError, MissingWildcard},
|
||||||
event::EventChannel,
|
event::EventChannel,
|
||||||
event::OnMqtt,
|
event::OnMqtt,
|
||||||
event::OnPresence,
|
event::OnPresence,
|
||||||
messages::{ContactMessage, PresenceMessage},
|
messages::{ContactMessage, PresenceMessage},
|
||||||
|
traits::Timeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Device;
|
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)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct ContactSensorConfig {
|
pub struct ContactSensorConfig {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
mqtt: MqttDeviceConfig,
|
mqtt: MqttDeviceConfig,
|
||||||
presence: Option<PresenceDeviceConfig>,
|
presence: Option<PresenceDeviceConfig>,
|
||||||
|
lights: Option<LightsConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Lights {
|
||||||
|
lights: Vec<(WrappedDevice, bool)>,
|
||||||
|
timeout: Duration, // Timeout in seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -71,6 +86,8 @@ pub struct ContactSensor {
|
||||||
overall_presence: bool,
|
overall_presence: bool,
|
||||||
is_closed: bool,
|
is_closed: bool,
|
||||||
handle: Option<JoinHandle<()>>,
|
handle: Option<JoinHandle<()>>,
|
||||||
|
|
||||||
|
lights: Option<Lights>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -83,7 +100,7 @@ impl CreateDevice for ContactSensor {
|
||||||
_event_channel: &EventChannel,
|
_event_channel: &EventChannel,
|
||||||
client: &AsyncClient,
|
client: &AsyncClient,
|
||||||
presence_topic: &str,
|
presence_topic: &str,
|
||||||
_device_manager: &DeviceManager,
|
device_manager: &DeviceManager,
|
||||||
) -> Result<Self, CreateDeviceError> {
|
) -> Result<Self, CreateDeviceError> {
|
||||||
trace!(id = identifier, "Setting up ContactSensor");
|
trace!(id = identifier, "Setting up ContactSensor");
|
||||||
|
|
||||||
|
@ -92,6 +109,32 @@ impl CreateDevice for ContactSensor {
|
||||||
.map(|p| p.generate_topic("contact", identifier, presence_topic))
|
.map(|p| p.generate_topic("contact", identifier, presence_topic))
|
||||||
.transpose()?;
|
.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 {
|
Ok(Self {
|
||||||
identifier: identifier.to_owned(),
|
identifier: identifier.to_owned(),
|
||||||
mqtt: config.mqtt,
|
mqtt: config.mqtt,
|
||||||
|
@ -100,6 +143,7 @@ impl CreateDevice for ContactSensor {
|
||||||
overall_presence: DEFAULT_PRESENCE,
|
overall_presence: DEFAULT_PRESENCE,
|
||||||
is_closed: true,
|
is_closed: true,
|
||||||
handle: None,
|
handle: None,
|
||||||
|
lights,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,6 +183,25 @@ impl OnMqtt for ContactSensor {
|
||||||
debug!(id = self.identifier, "Updating state to {is_closed}");
|
debug!(id = self.identifier, "Updating state to {is_closed}");
|
||||||
self.is_closed = 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
|
// Check if this contact sensor works as a presence device
|
||||||
// If not we are done here
|
// If not we are done here
|
||||||
let presence = match &self.presence {
|
let presence = match &self.presence {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user