Config is passed directly to IkeaOutlet and now supports turning off automatically after a specified amount of time
This commit is contained in:
parent
f735216dc4
commit
fb455b4e4c
|
@ -11,7 +11,7 @@ username="Dreaded_X"
|
|||
type = "IkeaOutlet"
|
||||
info = { name = "Kettle", room = "Kitchen" }
|
||||
zigbee = { topic = "zigbee2mqtt/kitchen/kettle" }
|
||||
kettle = {} # This is for future config
|
||||
kettle = { timeout = 5 }
|
||||
|
||||
[devices.living_workbench]
|
||||
type = "IkeaOutlet"
|
||||
|
|
|
@ -38,7 +38,7 @@ pub struct ZigbeeDeviceConfig {
|
|||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct KettleConfig {
|
||||
// @TODO Add options for the kettle
|
||||
pub timeout: Option<u64>, // Timeout in seconds
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
|
@ -1,37 +1,53 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use google_home::errors::ErrorCode;
|
||||
use google_home::{GoogleHomeDevice, device, types::Type, traits};
|
||||
use rumqttc::{AsyncClient, Publish};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use log::debug;
|
||||
use log::{debug, trace};
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
use crate::config::{KettleConfig, InfoConfig, ZigbeeDeviceConfig};
|
||||
use crate::devices::Device;
|
||||
use crate::mqtt::Listener;
|
||||
|
||||
pub struct IkeaOutlet {
|
||||
identifier: String,
|
||||
name: String,
|
||||
room: Option<String>,
|
||||
topic: String,
|
||||
|
||||
kettle: bool,
|
||||
info: InfoConfig,
|
||||
zigbee: ZigbeeDeviceConfig,
|
||||
kettle: Option<KettleConfig>,
|
||||
|
||||
client: AsyncClient,
|
||||
last_known_state: bool,
|
||||
handle: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl IkeaOutlet {
|
||||
pub fn new(identifier: String, name: String, room: Option<String>, kettle: bool, topic: String, client: AsyncClient) -> Self {
|
||||
pub fn new(identifier: String, info: InfoConfig, zigbee: ZigbeeDeviceConfig, kettle: Option<KettleConfig>, client: AsyncClient) -> Self {
|
||||
let c = client.clone();
|
||||
let t = topic.clone();
|
||||
let t = zigbee.topic.clone();
|
||||
// @TODO Handle potential errors here
|
||||
tokio::spawn(async move {
|
||||
c.subscribe(t, rumqttc::QoS::AtLeastOnce).await.unwrap();
|
||||
});
|
||||
|
||||
Self{ identifier, name, room, kettle, topic, client, last_known_state: false }
|
||||
Self{ identifier, info, zigbee, kettle, client, last_known_state: false, handle: None }
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_on(client: AsyncClient, topic: String, on: bool) {
|
||||
let message = StateMessage{
|
||||
state: if on {
|
||||
"ON".to_owned()
|
||||
} else {
|
||||
"OFF".to_owned()
|
||||
}
|
||||
};
|
||||
|
||||
// @TODO Handle potential errors here
|
||||
client.publish(topic + "/set", rumqttc::QoS::AtLeastOnce, false, serde_json::to_string(&message).unwrap()).await.unwrap();
|
||||
}
|
||||
|
||||
impl Device for IkeaOutlet {
|
||||
fn get_id(&self) -> String {
|
||||
self.identifier.clone()
|
||||
|
@ -59,19 +75,55 @@ impl From<&Publish> for StateMessage {
|
|||
impl Listener for IkeaOutlet {
|
||||
fn notify(&mut self, message: &Publish) {
|
||||
// Update the internal state based on what the device has reported
|
||||
if message.topic == self.topic {
|
||||
let state = StateMessage::from(message);
|
||||
if message.topic == self.zigbee.topic {
|
||||
let new_state = StateMessage::from(message).state == "ON";
|
||||
|
||||
let new_state = state.state == "ON";
|
||||
debug!("Updating state: {} => {}", self.last_known_state, new_state);
|
||||
// No need to do anything if the state has not changed
|
||||
if new_state == self.last_known_state {
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort any timer that is currently running
|
||||
if let Some(handle) = self.handle.take() {
|
||||
handle.abort();
|
||||
}
|
||||
|
||||
trace!("Updating state: {} => {}", self.last_known_state, new_state);
|
||||
self.last_known_state = new_state;
|
||||
|
||||
// If this is a kettle start a timeout for turning it of again
|
||||
if new_state {
|
||||
if let Some(kettle) = &self.kettle {
|
||||
if let Some(timeout) = kettle.timeout.clone() {
|
||||
let client = self.client.clone();
|
||||
let topic = self.zigbee.topic.clone();
|
||||
|
||||
// Turn the kettle of after the specified timeout
|
||||
// @TODO Impl Drop for IkeaOutlet that will abort the handle if the IkeaOutlet
|
||||
// get dropped
|
||||
self.handle = Some(
|
||||
tokio::spawn(async move {
|
||||
debug!("Starting timeout ({timeout}s) for kettle...");
|
||||
tokio::time::sleep(Duration::from_secs(timeout)).await;
|
||||
// @TODO We need to call set_on(false) in order to turn the device off
|
||||
// again, how are we going to do this?
|
||||
debug!("Turning kettle off!");
|
||||
set_on(client, topic, false).await;
|
||||
})
|
||||
);
|
||||
} else {
|
||||
trace!("Outlet is a kettle without timeout");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GoogleHomeDevice for IkeaOutlet {
|
||||
fn get_device_type(&self) -> Type {
|
||||
if self.kettle {
|
||||
if self.kettle.is_some() {
|
||||
Type::Kettle
|
||||
} else {
|
||||
Type::Outlet
|
||||
|
@ -79,7 +131,7 @@ impl GoogleHomeDevice for IkeaOutlet {
|
|||
}
|
||||
|
||||
fn get_device_name(&self) -> device::Name {
|
||||
device::Name::new(&self.name)
|
||||
device::Name::new(&self.info.name)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
|
@ -91,7 +143,7 @@ impl GoogleHomeDevice for IkeaOutlet {
|
|||
}
|
||||
|
||||
fn get_room_hint(&self) -> Option<String> {
|
||||
self.room.clone()
|
||||
self.info.room.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,19 +153,10 @@ impl traits::OnOff for IkeaOutlet {
|
|||
}
|
||||
|
||||
fn set_on(&mut self, on: bool) -> Result<(), ErrorCode> {
|
||||
let message = StateMessage{
|
||||
state: if on {
|
||||
"ON".to_owned()
|
||||
} else {
|
||||
"OFF".to_owned()
|
||||
}
|
||||
};
|
||||
|
||||
// @TODO Handle potential errors here
|
||||
let client = self.client.clone();
|
||||
let topic = self.topic.to_owned();
|
||||
let topic = self.zigbee.topic.clone();
|
||||
tokio::spawn(async move {
|
||||
client.publish(topic + "/set", rumqttc::QoS::AtLeastOnce, false, serde_json::to_string(&message).unwrap()).await.unwrap();
|
||||
set_on(client, topic, on).await;
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![feature(specialization)]
|
||||
pub mod devices;
|
||||
pub mod mqtt;
|
||||
pub mod config;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
mod config;
|
||||
|
||||
use std::{time::Duration, sync::{Arc, RwLock}, process, net::SocketAddr};
|
||||
|
||||
use config::Config;
|
||||
use automation::config::{Config, Device};
|
||||
use dotenv::dotenv;
|
||||
use warp::Filter;
|
||||
use rumqttc::{MqttOptions, Transport, AsyncClient};
|
||||
|
@ -59,9 +57,9 @@ async fn main() {
|
|||
debug!("Adding device {identifier}");
|
||||
|
||||
let device: automation::devices::DeviceBox = match device_config {
|
||||
config::Device::IkeaOutlet { info, zigbee, kettle } => {
|
||||
Device::IkeaOutlet { info, zigbee, kettle } => {
|
||||
trace!("\tIkeaOutlet [{} in {:?}]", info.name, info.room);
|
||||
Box::new(IkeaOutlet::new(identifier, info.name, info.room, kettle.is_some(), zigbee.topic, client.clone()))
|
||||
Box::new(IkeaOutlet::new(identifier, info, zigbee, kettle, client.clone()))
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user