Compare commits
No commits in common. "4bb49a381b23e9791839803c9cde146d0d6dc86c" and "9719c461368b318ae0bc476c7e94e2dbbef88e84" have entirely different histories.
4bb49a381b
...
9719c46136
67
config.lua
67
config.lua
|
@ -84,60 +84,25 @@ automation.device_manager:add(living_mixer)
|
||||||
local living_speakers = KasaOutlet.new({ identifier = "living_speakers", ip = "10.0.0.127" })
|
local living_speakers = KasaOutlet.new({ identifier = "living_speakers", ip = "10.0.0.127" })
|
||||||
automation.device_manager:add(living_speakers)
|
automation.device_manager:add(living_speakers)
|
||||||
|
|
||||||
automation.device_manager:add(IkeaRemote.new({
|
automation.device_manager:add(AudioSetup.new({
|
||||||
name = "Remote",
|
identifier = "living_audio",
|
||||||
room = "Living",
|
|
||||||
client = mqtt_client,
|
|
||||||
topic = mqtt_z2m("living/remote"),
|
topic = mqtt_z2m("living/remote"),
|
||||||
single_button = true,
|
client = mqtt_client,
|
||||||
callback = function(on)
|
mixer = living_mixer,
|
||||||
if on then
|
speakers = living_speakers,
|
||||||
if living_mixer:is_on() then
|
|
||||||
living_mixer:set_on(false)
|
|
||||||
living_speakers:set_on(false)
|
|
||||||
else
|
|
||||||
living_mixer:set_on(true)
|
|
||||||
living_speakers:set_on(true)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if not living_mixer:is_on() then
|
|
||||||
living_mixer:set_on(true)
|
|
||||||
else
|
|
||||||
living_speakers:set_on(not living_speakers:is_on())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
local kettle = IkeaOutlet.new({
|
automation.device_manager:add(IkeaOutlet.new({
|
||||||
outlet_type = "Kettle",
|
outlet_type = "Kettle",
|
||||||
name = "Kettle",
|
name = "Kettle",
|
||||||
room = "Kitchen",
|
room = "Kitchen",
|
||||||
topic = mqtt_z2m("kitchen/kettle"),
|
topic = mqtt_z2m("kitchen/kettle"),
|
||||||
client = mqtt_client,
|
client = mqtt_client,
|
||||||
timeout = debug and 5 or 300,
|
timeout = debug and 5 or 300,
|
||||||
})
|
remotes = {
|
||||||
automation.device_manager:add(kettle)
|
{ topic = mqtt_z2m("bedroom/remote") },
|
||||||
function set_kettle(on)
|
{ topic = mqtt_z2m("kitchen/remote") },
|
||||||
kettle:set_on(on)
|
},
|
||||||
end
|
|
||||||
|
|
||||||
automation.device_manager:add(IkeaRemote.new({
|
|
||||||
name = "Remote",
|
|
||||||
room = "Bedroom",
|
|
||||||
client = mqtt_client,
|
|
||||||
topic = mqtt_z2m("bedroom/remote"),
|
|
||||||
single_button = true,
|
|
||||||
callback = set_kettle,
|
|
||||||
}))
|
|
||||||
|
|
||||||
automation.device_manager:add(IkeaRemote.new({
|
|
||||||
name = "Remote",
|
|
||||||
room = "Kitchen",
|
|
||||||
client = mqtt_client,
|
|
||||||
topic = mqtt_z2m("kitchen/remote"),
|
|
||||||
single_button = true,
|
|
||||||
callback = set_kettle,
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
automation.device_manager:add(IkeaOutlet.new({
|
automation.device_manager:add(IkeaOutlet.new({
|
||||||
|
@ -180,18 +145,12 @@ local hallway_lights = HueGroup.new({
|
||||||
group_id = 81,
|
group_id = 81,
|
||||||
scene_id = "3qWKxGVadXFFG4o",
|
scene_id = "3qWKxGVadXFFG4o",
|
||||||
timer_id = 1,
|
timer_id = 1,
|
||||||
|
remotes = {
|
||||||
|
{ topic = mqtt_z2m("hallway/remote") },
|
||||||
|
},
|
||||||
client = mqtt_client,
|
client = mqtt_client,
|
||||||
})
|
})
|
||||||
automation.device_manager:add(hallway_lights)
|
automation.device_manager:add(hallway_lights)
|
||||||
automation.device_manager:add(IkeaRemote.new({
|
|
||||||
name = "Remote",
|
|
||||||
room = "Hallway",
|
|
||||||
client = mqtt_client,
|
|
||||||
topic = mqtt_z2m("hallway/remote"),
|
|
||||||
callback = function(on)
|
|
||||||
hallway_lights:set_on(on)
|
|
||||||
end,
|
|
||||||
}))
|
|
||||||
|
|
||||||
automation.device_manager:add(ContactSensor.new({
|
automation.device_manager:add(ContactSensor.new({
|
||||||
identifier = "hallway_frontdoor",
|
identifier = "hallway_frontdoor",
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use mlua::{FromLua, IntoLua};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ActionCallback<T> {
|
|
||||||
uuid: uuid::Uuid,
|
|
||||||
lua: mlua::Lua,
|
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> FromLua for ActionCallback<T> {
|
|
||||||
fn from_lua(value: mlua::Value, lua: &mlua::Lua) -> mlua::Result<Self> {
|
|
||||||
let uuid = uuid::Uuid::new_v4();
|
|
||||||
lua.set_named_registry_value(&uuid.to_string(), value)?;
|
|
||||||
|
|
||||||
Ok(ActionCallback {
|
|
||||||
uuid,
|
|
||||||
lua: lua.clone(),
|
|
||||||
phantom: PhantomData::<T>,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Return proper error here
|
|
||||||
impl<T> ActionCallback<T>
|
|
||||||
where
|
|
||||||
T: IntoLua + Sync + Send + Clone + Copy + 'static,
|
|
||||||
{
|
|
||||||
pub async fn call(&self, state: T) {
|
|
||||||
let uuid = self.uuid;
|
|
||||||
|
|
||||||
let callback: mlua::Value = self.lua.named_registry_value(&uuid.to_string()).unwrap();
|
|
||||||
match callback {
|
|
||||||
mlua::Value::Function(f) => f.call_async::<()>(state).await.unwrap(),
|
|
||||||
_ => todo!("Only functions are currently supported"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
126
src/devices/audio_setup.rs
Normal file
126
src/devices/audio_setup.rs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use automation_macro::LuaDeviceConfig;
|
||||||
|
use google_home::traits::OnOff;
|
||||||
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
|
use super::{Device, LuaDeviceCreate};
|
||||||
|
use crate::config::MqttDeviceConfig;
|
||||||
|
use crate::error::DeviceConfigError;
|
||||||
|
use crate::event::{OnMqtt, OnPresence};
|
||||||
|
use crate::messages::{RemoteAction, RemoteMessage};
|
||||||
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
|
pub struct Config {
|
||||||
|
pub identifier: String,
|
||||||
|
#[device_config(flatten)]
|
||||||
|
pub mqtt: MqttDeviceConfig,
|
||||||
|
#[device_config(from_lua)]
|
||||||
|
pub mixer: Box<dyn Device>,
|
||||||
|
#[device_config(from_lua)]
|
||||||
|
pub speakers: Box<dyn Device>,
|
||||||
|
#[device_config(from_lua)]
|
||||||
|
pub client: WrappedAsyncClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AudioSetup {
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LuaDeviceCreate for AudioSetup {
|
||||||
|
type Config = Config;
|
||||||
|
type Error = DeviceConfigError;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
|
trace!(id = config.identifier, "Setting up AudioSetup");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mixer_id = config.mixer.get_id().to_owned();
|
||||||
|
if (config.mixer.cast() as Option<&dyn OnOff>).is_none() {
|
||||||
|
return Err(DeviceConfigError::MissingTrait(mixer_id, "OnOff".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let speakers_id = config.speakers.get_id().to_owned();
|
||||||
|
if (config.speakers.cast() as Option<&dyn OnOff>).is_none() {
|
||||||
|
return Err(DeviceConfigError::MissingTrait(speakers_id, "OnOff".into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config
|
||||||
|
.client
|
||||||
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(AudioSetup { config })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for AudioSetup {
|
||||||
|
fn get_id(&self) -> String {
|
||||||
|
self.config.identifier.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl OnMqtt for AudioSetup {
|
||||||
|
async fn on_mqtt(&self, message: rumqttc::Publish) {
|
||||||
|
if !rumqttc::matches(&message.topic, &self.config.mqtt.topic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let action = match RemoteMessage::try_from(message) {
|
||||||
|
Ok(message) => message.action(),
|
||||||
|
Err(err) => {
|
||||||
|
error!(id = self.get_id(), "Failed to parse message: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let (Some(mixer), Some(speakers)) = (
|
||||||
|
self.config.mixer.cast() as Option<&dyn OnOff>,
|
||||||
|
self.config.speakers.cast() as Option<&dyn OnOff>,
|
||||||
|
) {
|
||||||
|
match action {
|
||||||
|
RemoteAction::On => {
|
||||||
|
if mixer.on().await.unwrap() {
|
||||||
|
speakers.set_on(false).await.unwrap();
|
||||||
|
mixer.set_on(false).await.unwrap();
|
||||||
|
} else {
|
||||||
|
speakers.set_on(true).await.unwrap();
|
||||||
|
mixer.set_on(true).await.unwrap();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RemoteAction::BrightnessMoveUp => {
|
||||||
|
if !mixer.on().await.unwrap() {
|
||||||
|
mixer.set_on(true).await.unwrap();
|
||||||
|
} else if speakers.on().await.unwrap() {
|
||||||
|
speakers.set_on(false).await.unwrap();
|
||||||
|
} else {
|
||||||
|
speakers.set_on(true).await.unwrap();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RemoteAction::BrightnessStop => { /* Ignore this action */ },
|
||||||
|
_ => warn!("Expected ikea shortcut button which only supports 'on' and 'brightness_move_up', got: {action:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl OnPresence for AudioSetup {
|
||||||
|
async fn on_presence(&self, presence: bool) {
|
||||||
|
if let (Some(mixer), Some(speakers)) = (
|
||||||
|
self.config.mixer.cast() as Option<&dyn OnOff>,
|
||||||
|
self.config.speakers.cast() as Option<&dyn OnOff>,
|
||||||
|
) {
|
||||||
|
// Turn off the audio setup when we leave the house
|
||||||
|
if !presence {
|
||||||
|
debug!(id = self.get_id(), "Turning devices off");
|
||||||
|
speakers.set_on(false).await.unwrap();
|
||||||
|
mixer.set_on(false).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,9 +6,13 @@ use async_trait::async_trait;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
use google_home::traits::OnOff;
|
use google_home::traits::OnOff;
|
||||||
use tracing::{error, trace, warn};
|
use rumqttc::{Publish, SubscribeFilter};
|
||||||
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::{Device, LuaDeviceCreate};
|
use super::{Device, LuaDeviceCreate};
|
||||||
|
use crate::config::MqttDeviceConfig;
|
||||||
|
use crate::event::OnMqtt;
|
||||||
|
use crate::messages::{RemoteAction, RemoteMessage};
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
use crate::traits::Timeout;
|
use crate::traits::Timeout;
|
||||||
|
|
||||||
|
@ -21,6 +25,8 @@ pub struct Config {
|
||||||
pub group_id: isize,
|
pub group_id: isize,
|
||||||
pub timer_id: isize,
|
pub timer_id: isize,
|
||||||
pub scene_id: String,
|
pub scene_id: String,
|
||||||
|
#[device_config(default)]
|
||||||
|
pub remotes: Vec<MqttDeviceConfig>,
|
||||||
#[device_config(from_lua)]
|
#[device_config(from_lua)]
|
||||||
pub client: WrappedAsyncClient,
|
pub client: WrappedAsyncClient,
|
||||||
}
|
}
|
||||||
|
@ -39,6 +45,16 @@ impl LuaDeviceCreate for HueGroup {
|
||||||
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.identifier, "Setting up AudioSetup");
|
trace!(id = config.identifier, "Setting up AudioSetup");
|
||||||
|
|
||||||
|
if !config.remotes.is_empty() {
|
||||||
|
config
|
||||||
|
.client
|
||||||
|
.subscribe_many(config.remotes.iter().map(|remote| SubscribeFilter {
|
||||||
|
path: remote.topic.clone(),
|
||||||
|
qos: rumqttc::QoS::AtLeastOnce,
|
||||||
|
}))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self { config })
|
Ok(Self { config })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,6 +83,38 @@ impl Device for HueGroup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl OnMqtt for HueGroup {
|
||||||
|
async fn on_mqtt(&self, message: Publish) {
|
||||||
|
if !self
|
||||||
|
.config
|
||||||
|
.remotes
|
||||||
|
.iter()
|
||||||
|
.any(|remote| rumqttc::matches(&message.topic, &remote.topic))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let action = match RemoteMessage::try_from(message) {
|
||||||
|
Ok(message) => message.action(),
|
||||||
|
Err(err) => {
|
||||||
|
error!(id = self.get_id(), "Failed to parse message: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("Action: {action:#?}");
|
||||||
|
|
||||||
|
match action {
|
||||||
|
RemoteAction::On | RemoteAction::BrightnessMoveUp => self.set_on(true).await.unwrap(),
|
||||||
|
RemoteAction::Off | RemoteAction::BrightnessMoveDown => {
|
||||||
|
self.set_on(false).await.unwrap()
|
||||||
|
}
|
||||||
|
RemoteAction::BrightnessStop => { /* Ignore this action */ }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl OnOff for HueGroup {
|
impl OnOff for HueGroup {
|
||||||
async fn set_on(&self, on: bool) -> Result<(), ErrorCode> {
|
async fn set_on(&self, on: bool) -> Result<(), ErrorCode> {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use google_home::device;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
use google_home::traits::{self, OnOff};
|
use google_home::traits::{self, OnOff};
|
||||||
use google_home::types::Type;
|
use google_home::types::Type;
|
||||||
use rumqttc::{matches, Publish};
|
use rumqttc::{matches, Publish, SubscribeFilter};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
@ -18,7 +18,7 @@ use super::LuaDeviceCreate;
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
use crate::config::{InfoConfig, MqttDeviceConfig};
|
||||||
use crate::devices::Device;
|
use crate::devices::Device;
|
||||||
use crate::event::{OnMqtt, OnPresence};
|
use crate::event::{OnMqtt, OnPresence};
|
||||||
use crate::messages::OnOffMessage;
|
use crate::messages::{OnOffMessage, RemoteAction, RemoteMessage};
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
use crate::traits::Timeout;
|
use crate::traits::Timeout;
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@ pub struct Config {
|
||||||
pub outlet_type: OutletType,
|
pub outlet_type: OutletType,
|
||||||
#[device_config(default, with(|t: Option<_>| t.map(Duration::from_secs)))]
|
#[device_config(default, with(|t: Option<_>| t.map(Duration::from_secs)))]
|
||||||
pub timeout: Option<Duration>,
|
pub timeout: Option<Duration>,
|
||||||
|
#[device_config(default)]
|
||||||
|
pub remotes: Vec<MqttDeviceConfig>,
|
||||||
|
|
||||||
#[device_config(from_lua)]
|
#[device_config(from_lua)]
|
||||||
pub client: WrappedAsyncClient,
|
pub client: WrappedAsyncClient,
|
||||||
|
@ -76,6 +78,16 @@ impl LuaDeviceCreate for IkeaOutlet {
|
||||||
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.info.identifier(), "Setting up IkeaOutlet");
|
trace!(id = config.info.identifier(), "Setting up IkeaOutlet");
|
||||||
|
|
||||||
|
if !config.remotes.is_empty() {
|
||||||
|
config
|
||||||
|
.client
|
||||||
|
.subscribe_many(config.remotes.iter().map(|remote| SubscribeFilter {
|
||||||
|
path: remote.topic.clone(),
|
||||||
|
qos: rumqttc::QoS::AtLeastOnce,
|
||||||
|
}))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
.client
|
.client
|
||||||
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
|
@ -126,6 +138,26 @@ impl OnMqtt for IkeaOutlet {
|
||||||
if state && let Some(timeout) = self.config.timeout {
|
if state && let Some(timeout) = self.config.timeout {
|
||||||
self.start_timeout(timeout).await.unwrap();
|
self.start_timeout(timeout).await.unwrap();
|
||||||
}
|
}
|
||||||
|
} else if self
|
||||||
|
.config
|
||||||
|
.remotes
|
||||||
|
.iter()
|
||||||
|
.any(|remote| rumqttc::matches(&message.topic, &remote.topic))
|
||||||
|
{
|
||||||
|
let action = match RemoteMessage::try_from(message) {
|
||||||
|
Ok(message) => message.action(),
|
||||||
|
Err(err) => {
|
||||||
|
error!(id = Device::get_id(self), "Failed to parse message: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match action {
|
||||||
|
RemoteAction::On => self.set_on(true).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:?}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
use automation_macro::LuaDeviceConfig;
|
|
||||||
use axum::async_trait;
|
|
||||||
use rumqttc::{matches, Publish};
|
|
||||||
use tracing::{debug, error, trace};
|
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
|
||||||
use crate::action_callback::ActionCallback;
|
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
|
||||||
use crate::devices::Device;
|
|
||||||
use crate::event::OnMqtt;
|
|
||||||
use crate::messages::RemoteMessage;
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
|
||||||
pub struct Config {
|
|
||||||
#[device_config(flatten)]
|
|
||||||
pub info: InfoConfig,
|
|
||||||
|
|
||||||
#[device_config(default)]
|
|
||||||
pub single_button: bool,
|
|
||||||
|
|
||||||
#[device_config(flatten)]
|
|
||||||
pub mqtt: MqttDeviceConfig,
|
|
||||||
|
|
||||||
#[device_config(from_lua)]
|
|
||||||
pub client: WrappedAsyncClient,
|
|
||||||
|
|
||||||
#[device_config(from_lua)]
|
|
||||||
pub callback: ActionCallback<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct IkeaRemote {
|
|
||||||
config: Config,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Device for IkeaRemote {
|
|
||||||
fn get_id(&self) -> String {
|
|
||||||
self.config.info.identifier()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl LuaDeviceCreate for IkeaRemote {
|
|
||||||
type Config = Config;
|
|
||||||
type Error = rumqttc::ClientError;
|
|
||||||
|
|
||||||
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
|
||||||
trace!(id = config.info.identifier(), "Setting up IkeaRemote");
|
|
||||||
|
|
||||||
config
|
|
||||||
.client
|
|
||||||
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Self { config })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl OnMqtt for IkeaRemote {
|
|
||||||
async fn on_mqtt(&self, message: Publish) {
|
|
||||||
// Check if the message is from the deviec itself or from a remote
|
|
||||||
debug!(id = Device::get_id(self), "Mqtt message received");
|
|
||||||
if matches(&message.topic, &self.config.mqtt.topic) {
|
|
||||||
let action = match RemoteMessage::try_from(message) {
|
|
||||||
Ok(message) => message.action(),
|
|
||||||
Err(err) => {
|
|
||||||
error!(id = Device::get_id(self), "Failed to parse message: {err}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
debug!(id = Device::get_id(self), "Remote action = {:?}", action);
|
|
||||||
|
|
||||||
let on = if self.config.single_button {
|
|
||||||
match action {
|
|
||||||
crate::messages::RemoteAction::On => Some(true),
|
|
||||||
crate::messages::RemoteAction::BrightnessMoveUp => Some(false),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match action {
|
|
||||||
crate::messages::RemoteAction::On => Some(true),
|
|
||||||
crate::messages::RemoteAction::Off => Some(false),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(on) = on {
|
|
||||||
self.config.callback.call(on).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,15 +6,14 @@ use async_trait::async_trait;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use bytes::{Buf, BufMut};
|
use bytes::{Buf, BufMut};
|
||||||
use google_home::errors::{self, DeviceError};
|
use google_home::errors::{self, DeviceError};
|
||||||
use google_home::traits::OnOff;
|
use google_home::traits;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tracing::{debug, trace};
|
use tracing::trace;
|
||||||
|
|
||||||
use super::{Device, LuaDeviceCreate};
|
use super::{Device, LuaDeviceCreate};
|
||||||
use crate::event::OnPresence;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
@ -207,7 +206,7 @@ impl Response {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl OnOff for KasaOutlet {
|
impl traits::OnOff for KasaOutlet {
|
||||||
async fn on(&self) -> Result<bool, errors::ErrorCode> {
|
async fn on(&self) -> Result<bool, errors::ErrorCode> {
|
||||||
let mut stream = TcpStream::connect(self.config.addr)
|
let mut stream = TcpStream::connect(self.config.addr)
|
||||||
.await
|
.await
|
||||||
|
@ -276,13 +275,3 @@ impl OnOff for KasaOutlet {
|
||||||
.or(Err(DeviceError::TransientError.into()))
|
.or(Err(DeviceError::TransientError.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl OnPresence for KasaOutlet {
|
|
||||||
async fn on_presence(&self, presence: bool) {
|
|
||||||
if !presence {
|
|
||||||
debug!(id = Device::get_id(self), "Turning device off");
|
|
||||||
self.set_on(false).await.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
mod air_filter;
|
mod air_filter;
|
||||||
|
mod audio_setup;
|
||||||
mod contact_sensor;
|
mod contact_sensor;
|
||||||
mod debug_bridge;
|
mod debug_bridge;
|
||||||
mod hue_bridge;
|
mod hue_bridge;
|
||||||
mod hue_group;
|
mod hue_group;
|
||||||
mod ikea_outlet;
|
mod ikea_outlet;
|
||||||
mod ikea_remote;
|
|
||||||
mod kasa_outlet;
|
mod kasa_outlet;
|
||||||
mod light_sensor;
|
mod light_sensor;
|
||||||
mod ntfy;
|
mod ntfy;
|
||||||
|
@ -22,12 +22,12 @@ use google_home::traits::OnOff;
|
||||||
use mlua::ObjectLike;
|
use mlua::ObjectLike;
|
||||||
|
|
||||||
pub use self::air_filter::AirFilter;
|
pub use self::air_filter::AirFilter;
|
||||||
|
pub use self::audio_setup::AudioSetup;
|
||||||
pub use self::contact_sensor::ContactSensor;
|
pub use self::contact_sensor::ContactSensor;
|
||||||
pub use self::debug_bridge::DebugBridge;
|
pub use self::debug_bridge::DebugBridge;
|
||||||
pub use self::hue_bridge::HueBridge;
|
pub use self::hue_bridge::HueBridge;
|
||||||
pub use self::hue_group::HueGroup;
|
pub use self::hue_group::HueGroup;
|
||||||
pub use self::ikea_outlet::IkeaOutlet;
|
pub use self::ikea_outlet::IkeaOutlet;
|
||||||
pub use self::ikea_remote::IkeaRemote;
|
|
||||||
pub use self::kasa_outlet::KasaOutlet;
|
pub use self::kasa_outlet::KasaOutlet;
|
||||||
pub use self::light_sensor::LightSensor;
|
pub use self::light_sensor::LightSensor;
|
||||||
pub use self::ntfy::{Notification, Ntfy};
|
pub use self::ntfy::{Notification, Ntfy};
|
||||||
|
@ -98,12 +98,12 @@ macro_rules! impl_device {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_device!(AirFilter);
|
impl_device!(AirFilter);
|
||||||
|
impl_device!(AudioSetup);
|
||||||
impl_device!(ContactSensor);
|
impl_device!(ContactSensor);
|
||||||
impl_device!(DebugBridge);
|
impl_device!(DebugBridge);
|
||||||
impl_device!(HueBridge);
|
impl_device!(HueBridge);
|
||||||
impl_device!(HueGroup);
|
impl_device!(HueGroup);
|
||||||
impl_device!(IkeaOutlet);
|
impl_device!(IkeaOutlet);
|
||||||
impl_device!(IkeaRemote);
|
|
||||||
impl_device!(KasaOutlet);
|
impl_device!(KasaOutlet);
|
||||||
impl_device!(LightSensor);
|
impl_device!(LightSensor);
|
||||||
impl_device!(Ntfy);
|
impl_device!(Ntfy);
|
||||||
|
@ -113,12 +113,12 @@ impl_device!(Washer);
|
||||||
|
|
||||||
pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
|
pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
|
||||||
register_device!(lua, AirFilter);
|
register_device!(lua, AirFilter);
|
||||||
|
register_device!(lua, AudioSetup);
|
||||||
register_device!(lua, ContactSensor);
|
register_device!(lua, ContactSensor);
|
||||||
register_device!(lua, DebugBridge);
|
register_device!(lua, DebugBridge);
|
||||||
register_device!(lua, HueBridge);
|
register_device!(lua, HueBridge);
|
||||||
register_device!(lua, HueGroup);
|
register_device!(lua, HueGroup);
|
||||||
register_device!(lua, IkeaOutlet);
|
register_device!(lua, IkeaOutlet);
|
||||||
register_device!(lua, IkeaRemote);
|
|
||||||
register_device!(lua, KasaOutlet);
|
register_device!(lua, KasaOutlet);
|
||||||
register_device!(lua, LightSensor);
|
register_device!(lua, LightSensor);
|
||||||
register_device!(lua, Ntfy);
|
register_device!(lua, Ntfy);
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#![feature(specialization)]
|
#![feature(specialization)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
|
||||||
pub mod action_callback;
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod device_manager;
|
pub mod device_manager;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user