Setting the presence mqtt topic is now optional, if not set it will generate an appropriate value automatically
This commit is contained in:
parent
cbcbd05613
commit
27a63b1a79
|
@ -52,5 +52,4 @@ speakers = "10.0.0.182"
|
||||||
[devices.hallway_frontdoor]
|
[devices.hallway_frontdoor]
|
||||||
type = "ContactSensor"
|
type = "ContactSensor"
|
||||||
topic = "zigbee2mqtt/hallway/frontdoor"
|
topic = "zigbee2mqtt/hallway/frontdoor"
|
||||||
# @TODO This should be automatically constructed from the identifier and presence topic
|
presence = { timeout = 10 }
|
||||||
presence = { topic = "automation_dev/presence/frontdoor", timeout = 10 }
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub struct OpenIDConfig {
|
||||||
pub base_url: String
|
pub base_url: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct MqttConfig {
|
pub struct MqttConfig {
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
@ -96,32 +96,43 @@ pub struct HueBridgeConfig {
|
||||||
pub flags: Flags,
|
pub flags: Flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct InfoConfig {
|
pub struct InfoConfig {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub room: Option<String>,
|
pub room: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct MqttDeviceConfig {
|
pub struct MqttDeviceConfig {
|
||||||
pub topic: String,
|
pub topic: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct KettleConfig {
|
pub struct KettleConfig {
|
||||||
pub timeout: Option<u64>, // Timeout in seconds
|
pub timeout: Option<u64>, // Timeout in seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct PresenceDeviceConfig {
|
pub struct PresenceDeviceConfig {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub mqtt: MqttDeviceConfig,
|
pub mqtt: Option<MqttDeviceConfig>,
|
||||||
// @TODO Maybe make this an option? That way if no timeout is set it will immediately turn the
|
// @TODO Maybe make this an option? That way if no timeout is set it will immediately turn the
|
||||||
// device off again?
|
// device off again?
|
||||||
pub timeout: u64 // Timeout in seconds
|
pub timeout: u64 // Timeout in seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
impl PresenceDeviceConfig {
|
||||||
|
/// Set the mqtt topic to an appropriate value if it is not already set
|
||||||
|
fn generate_topic(&mut self, identifier: &str, config: &Config) {
|
||||||
|
if self.mqtt.is_none() {
|
||||||
|
let topic = config.presence.topic.clone() + "/" + identifier;
|
||||||
|
trace!("Setting presence mqtt topic: {topic}");
|
||||||
|
self.mqtt = Some(MqttDeviceConfig { topic });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum Device {
|
pub enum Device {
|
||||||
IkeaOutlet {
|
IkeaOutlet {
|
||||||
|
@ -182,7 +193,7 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn into(self, identifier: String, client: AsyncClient) -> DeviceBox {
|
pub fn into(self, identifier: String, config: &Config, client: AsyncClient) -> DeviceBox {
|
||||||
match self {
|
match self {
|
||||||
Device::IkeaOutlet { info, mqtt, kettle } => {
|
Device::IkeaOutlet { info, mqtt, kettle } => {
|
||||||
trace!(id = identifier, "IkeaOutlet [{} in {:?}]", info.name, info.room);
|
trace!(id = identifier, "IkeaOutlet [{} in {:?}]", info.name, info.room);
|
||||||
|
@ -196,8 +207,11 @@ impl Device {
|
||||||
trace!(id = identifier, "AudioSetup [{}]", identifier);
|
trace!(id = identifier, "AudioSetup [{}]", identifier);
|
||||||
Box::new(AudioSetup::new(identifier, mqtt, mixer, speakers, client))
|
Box::new(AudioSetup::new(identifier, mqtt, mixer, speakers, client))
|
||||||
},
|
},
|
||||||
Device::ContactSensor { mqtt, presence } => {
|
Device::ContactSensor { mqtt, mut presence } => {
|
||||||
trace!(id = identifier, "ContactSensor [{}]", identifier);
|
trace!(id = identifier, "ContactSensor [{}]", identifier);
|
||||||
|
if let Some(presence) = &mut presence {
|
||||||
|
presence.generate_topic(&identifier, &config);
|
||||||
|
}
|
||||||
Box::new(ContactSensor::new(identifier, mqtt, presence, client))
|
Box::new(ContactSensor::new(identifier, mqtt, presence, client))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::time::Duration;
|
||||||
use pollster::FutureExt;
|
use pollster::FutureExt;
|
||||||
use rumqttc::AsyncClient;
|
use rumqttc::AsyncClient;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tracing::{error, debug};
|
use tracing::{error, debug, warn};
|
||||||
|
|
||||||
use crate::{config::{MqttDeviceConfig, PresenceDeviceConfig}, mqtt::{OnMqtt, ContactMessage, PresenceMessage}};
|
use crate::{config::{MqttDeviceConfig, PresenceDeviceConfig}, mqtt::{OnMqtt, ContactMessage, PresenceMessage}};
|
||||||
|
|
||||||
|
@ -68,17 +68,25 @@ impl OnMqtt for ContactSensor {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let topic = match &presence.mqtt {
|
||||||
|
Some(mqtt) => mqtt.topic.clone(),
|
||||||
|
None => {
|
||||||
|
warn!("Contact sensors is configured as a presence sensor, but no mqtt topic is specified");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if !is_closed {
|
if !is_closed {
|
||||||
// Activate presence and stop any timeout once we open the door
|
// Activate presence and stop any timeout once we open the door
|
||||||
if let Some(handle) = self.handle.take() {
|
if let Some(handle) = self.handle.take() {
|
||||||
handle.abort();
|
handle.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.client.publish(presence.mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce, false, serde_json::to_string(&PresenceMessage::new(true)).unwrap()).block_on().unwrap();
|
|
||||||
|
self.client.publish(topic, rumqttc::QoS::AtLeastOnce, false, serde_json::to_string(&PresenceMessage::new(true)).unwrap()).block_on().unwrap();
|
||||||
} else {
|
} else {
|
||||||
// Once the door is closed again we start a timeout for removing the presence
|
// Once the door is closed again we start a timeout for removing the presence
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
let topic = presence.mqtt.topic.clone();
|
|
||||||
let id = self.identifier.clone();
|
let id = self.identifier.clone();
|
||||||
let timeout = Duration::from_secs(presence.timeout);
|
let timeout = Duration::from_secs(presence.timeout);
|
||||||
self.handle = Some(
|
self.handle = Some(
|
||||||
|
|
29
src/main.rs
29
src/main.rs
|
@ -44,8 +44,9 @@ async fn main() {
|
||||||
info!("Starting automation_rs...");
|
info!("Starting automation_rs...");
|
||||||
|
|
||||||
// Configure MQTT
|
// Configure MQTT
|
||||||
let mut mqttoptions = MqttOptions::new("rust-test", config.mqtt.host, config.mqtt.port);
|
let mqtt = config.mqtt.clone();
|
||||||
mqttoptions.set_credentials(config.mqtt.username, config.mqtt.password);
|
let mut mqttoptions = MqttOptions::new("rust-test", mqtt.host, mqtt.port);
|
||||||
|
mqttoptions.set_credentials(mqtt.username, mqtt.password);
|
||||||
mqttoptions.set_keep_alive(Duration::from_secs(5));
|
mqttoptions.set_keep_alive(Duration::from_secs(5));
|
||||||
mqttoptions.set_transport(Transport::tls_with_default_config());
|
mqttoptions.set_transport(Transport::tls_with_default_config());
|
||||||
|
|
||||||
|
@ -57,6 +58,18 @@ async fn main() {
|
||||||
let devices = Arc::new(RwLock::new(Devices::new()));
|
let devices = Arc::new(RwLock::new(Devices::new()));
|
||||||
mqtt.add_listener(Arc::downgrade(&devices));
|
mqtt.add_listener(Arc::downgrade(&devices));
|
||||||
|
|
||||||
|
// Turn the config into actual devices and add them
|
||||||
|
config.devices.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(identifier, device_config)| {
|
||||||
|
// This can technically block, but this only happens during start-up, so should not be
|
||||||
|
// a problem
|
||||||
|
device_config.into(identifier, &config, client.clone())
|
||||||
|
})
|
||||||
|
.for_each(|device| {
|
||||||
|
devices.write().unwrap().add_device(device);
|
||||||
|
});
|
||||||
|
|
||||||
// Setup presence system
|
// Setup presence system
|
||||||
let mut presence = Presence::new(config.presence, client.clone());
|
let mut presence = Presence::new(config.presence, client.clone());
|
||||||
// Register devices as presence listener
|
// Register devices as presence listener
|
||||||
|
@ -82,18 +95,6 @@ async fn main() {
|
||||||
// Start mqtt, this spawns a seperate async task
|
// Start mqtt, this spawns a seperate async task
|
||||||
mqtt.start();
|
mqtt.start();
|
||||||
|
|
||||||
// Turn the config into actual devices and add them
|
|
||||||
config.devices
|
|
||||||
.into_iter()
|
|
||||||
.map(|(identifier, device_config)| {
|
|
||||||
// This can technically block, but this only happens during start-up, so should not be
|
|
||||||
// a problem
|
|
||||||
device_config.into(identifier, client.clone())
|
|
||||||
})
|
|
||||||
.for_each(|device| {
|
|
||||||
devices.write().unwrap().add_device(device);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create google home fullfillment route
|
// Create google home fullfillment route
|
||||||
let fullfillment = Router::new()
|
let fullfillment = Router::new()
|
||||||
.route("/google_home", post(async move |user: User, Json(payload): Json<Request>| {
|
.route("/google_home", post(async move |user: User, Json(payload): Json<Request>| {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user