You can now add remotes to IkeaOutlets
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
1ba20c3390
commit
15cde02a8d
|
@ -62,6 +62,7 @@ name = "Kettle"
|
||||||
room = "Kitchen"
|
room = "Kitchen"
|
||||||
topic = "zigbee2mqtt/kitchen/kettle"
|
topic = "zigbee2mqtt/kitchen/kettle"
|
||||||
timeout = 300
|
timeout = 300
|
||||||
|
remotes = [{ topic = "zigbee2mqtt/bedroom/remote" }]
|
||||||
|
|
||||||
|
|
||||||
[device.bathroom_light]
|
[device.bathroom_light]
|
||||||
|
|
|
@ -64,7 +64,7 @@ name = "Kettle"
|
||||||
room = "Kitchen"
|
room = "Kitchen"
|
||||||
topic = "zigbee2mqtt/kitchen/kettle"
|
topic = "zigbee2mqtt/kitchen/kettle"
|
||||||
timeout = 5
|
timeout = 5
|
||||||
|
remotes = [{ topic = "zigbee2mqtt/bedroom/remote" }]
|
||||||
|
|
||||||
[device.bathroom_light]
|
[device.bathroom_light]
|
||||||
type = "IkeaOutlet"
|
type = "IkeaOutlet"
|
||||||
|
|
|
@ -7,7 +7,7 @@ use google_home::{
|
||||||
types::Type,
|
types::Type,
|
||||||
GoogleHomeDevice,
|
GoogleHomeDevice,
|
||||||
};
|
};
|
||||||
use rumqttc::{AsyncClient, Publish};
|
use rumqttc::{matches, AsyncClient, Publish};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_with::serde_as;
|
use serde_with::serde_as;
|
||||||
use serde_with::DurationSeconds;
|
use serde_with::DurationSeconds;
|
||||||
|
@ -21,7 +21,7 @@ use crate::devices::Device;
|
||||||
use crate::error::DeviceConfigError;
|
use crate::error::DeviceConfigError;
|
||||||
use crate::event::OnMqtt;
|
use crate::event::OnMqtt;
|
||||||
use crate::event::OnPresence;
|
use crate::event::OnPresence;
|
||||||
use crate::messages::OnOffMessage;
|
use crate::messages::{OnOffMessage, RemoteAction, RemoteMessage};
|
||||||
use crate::traits::Timeout;
|
use crate::traits::Timeout;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Copy)]
|
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Copy)]
|
||||||
|
@ -43,6 +43,8 @@ pub struct IkeaOutletConfig {
|
||||||
outlet_type: OutletType,
|
outlet_type: OutletType,
|
||||||
#[serde_as(as = "Option<DurationSeconds>")]
|
#[serde_as(as = "Option<DurationSeconds>")]
|
||||||
timeout: Option<Duration>, // Timeout in seconds
|
timeout: Option<Duration>, // Timeout in seconds
|
||||||
|
#[serde(default)]
|
||||||
|
pub remotes: Vec<MqttDeviceConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_outlet_type() -> OutletType {
|
fn default_outlet_type() -> OutletType {
|
||||||
|
@ -69,6 +71,7 @@ impl DeviceConfig for IkeaOutletConfig {
|
||||||
mqtt: self.mqtt,
|
mqtt: self.mqtt,
|
||||||
outlet_type: self.outlet_type,
|
outlet_type: self.outlet_type,
|
||||||
timeout: self.timeout,
|
timeout: self.timeout,
|
||||||
|
remotes: self.remotes,
|
||||||
client: ext.client.clone(),
|
client: ext.client.clone(),
|
||||||
last_known_state: false,
|
last_known_state: false,
|
||||||
handle: None,
|
handle: None,
|
||||||
|
@ -85,6 +88,7 @@ struct IkeaOutlet {
|
||||||
mqtt: MqttDeviceConfig,
|
mqtt: MqttDeviceConfig,
|
||||||
outlet_type: OutletType,
|
outlet_type: OutletType,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
|
remotes: Vec<MqttDeviceConfig>,
|
||||||
|
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
last_known_state: bool,
|
last_known_state: bool,
|
||||||
|
@ -117,33 +121,59 @@ impl Device for IkeaOutlet {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl OnMqtt for IkeaOutlet {
|
impl OnMqtt for IkeaOutlet {
|
||||||
fn topics(&self) -> Vec<&str> {
|
fn topics(&self) -> Vec<&str> {
|
||||||
vec![&self.mqtt.topic]
|
let mut topics: Vec<_> = self
|
||||||
|
.remotes
|
||||||
|
.iter()
|
||||||
|
.map(|mqtt| mqtt.topic.as_str())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
topics.push(&self.mqtt.topic);
|
||||||
|
|
||||||
|
topics
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_mqtt(&mut self, message: Publish) {
|
async fn on_mqtt(&mut self, message: Publish) {
|
||||||
// Update the internal state based on what the device has reported
|
// Check if the message is from the deviec itself or from a remote
|
||||||
let state = match OnOffMessage::try_from(message) {
|
if matches(&message.topic, &self.mqtt.topic) {
|
||||||
Ok(state) => state.state(),
|
// Update the internal state based on what the device has reported
|
||||||
Err(err) => {
|
let state = match OnOffMessage::try_from(message) {
|
||||||
error!(id = self.identifier, "Failed to parse message: {err}");
|
Ok(state) => state.state(),
|
||||||
|
Err(err) => {
|
||||||
|
error!(id = self.identifier, "Failed to parse message: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// No need to do anything if the state has not changed
|
||||||
|
if state == self.last_known_state {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// No need to do anything if the state has not changed
|
// Abort any timer that is currently running
|
||||||
if state == self.last_known_state {
|
self.stop_timeout().await.unwrap();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Abort any timer that is currently running
|
debug!(id = self.identifier, "Updating state to {state}");
|
||||||
self.stop_timeout().await.unwrap();
|
self.last_known_state = state;
|
||||||
|
|
||||||
debug!(id = self.identifier, "Updating state to {state}");
|
// If this is a kettle start a timeout for turning it of again
|
||||||
self.last_known_state = state;
|
if state && let Some(timeout) = self.timeout {
|
||||||
|
self.start_timeout(timeout).await.unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let action = match RemoteMessage::try_from(message) {
|
||||||
|
Ok(message) => message.action(),
|
||||||
|
Err(err) => {
|
||||||
|
error!(id = self.identifier, "Failed to parse message: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// If this is a kettle start a timeout for turning it of again
|
match action {
|
||||||
if state && let Some(timeout) = self.timeout {
|
RemoteAction::On => self.set_on(true).await.unwrap(),
|
||||||
self.start_timeout(timeout).await.unwrap();
|
RemoteAction::BrightnessMoveUp => self.set_on(false).await.unwrap(),
|
||||||
|
RemoteAction::BrightnessStop => { /* Ignore this action */ },
|
||||||
|
_ => warn!("Expected ikea shortcut button which only supports 'on' and 'brightness_move_up', got: {action:?}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user