Compare commits
No commits in common. "f577e9bfa5399aece5cbfc3a2efbc3c6f078776d" and "24815edd34320a57a084377213787f1204e77a44" have entirely different histories.
f577e9bfa5
...
24815edd34
|
@ -3,7 +3,7 @@ use std::time::Duration;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_lib::action_callback::ActionCallback;
|
use automation_lib::action_callback::ActionCallback;
|
||||||
use automation_lib::config::{InfoConfig, MqttDeviceConfig};
|
use automation_lib::config::MqttDeviceConfig;
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use automation_lib::error::DeviceConfigError;
|
use automation_lib::error::DeviceConfigError;
|
||||||
use automation_lib::event::{OnMqtt, OnPresence};
|
use automation_lib::event::{OnMqtt, OnPresence};
|
||||||
|
@ -11,22 +11,10 @@ use automation_lib::messages::{ContactMessage, PresenceMessage};
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_lib::presence::DEFAULT_PRESENCE;
|
use automation_lib::presence::DEFAULT_PRESENCE;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use google_home::device;
|
|
||||||
use google_home::errors::{DeviceError, ErrorCode};
|
|
||||||
use google_home::traits::OpenClose;
|
|
||||||
use google_home::types::Type;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Copy)]
|
|
||||||
pub enum SensorType {
|
|
||||||
Door,
|
|
||||||
Drawer,
|
|
||||||
Window,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: If we add more presence devices we might need to move this out of here
|
// NOTE: If we add more presence devices we might need to move this out of here
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct PresenceDeviceConfig {
|
pub struct PresenceDeviceConfig {
|
||||||
|
@ -38,16 +26,11 @@ pub struct PresenceDeviceConfig {
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[device_config(flatten)]
|
pub identifier: String,
|
||||||
pub info: InfoConfig,
|
|
||||||
#[device_config(flatten)]
|
#[device_config(flatten)]
|
||||||
pub mqtt: MqttDeviceConfig,
|
pub mqtt: MqttDeviceConfig,
|
||||||
#[device_config(from_lua, default)]
|
#[device_config(from_lua, default)]
|
||||||
pub presence: Option<PresenceDeviceConfig>,
|
pub presence: Option<PresenceDeviceConfig>,
|
||||||
|
|
||||||
#[device_config(default(SensorType::Window))]
|
|
||||||
pub sensor_type: SensorType,
|
|
||||||
|
|
||||||
#[device_config(from_lua, default)]
|
#[device_config(from_lua, default)]
|
||||||
pub callback: ActionCallback<ContactSensor, bool>,
|
pub callback: ActionCallback<ContactSensor, bool>,
|
||||||
#[device_config(from_lua)]
|
#[device_config(from_lua)]
|
||||||
|
@ -83,7 +66,7 @@ impl LuaDeviceCreate for ContactSensor {
|
||||||
type Error = DeviceConfigError;
|
type Error = DeviceConfigError;
|
||||||
|
|
||||||
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 ContactSensor");
|
trace!(id = config.identifier, "Setting up ContactSensor");
|
||||||
|
|
||||||
config
|
config
|
||||||
.client
|
.client
|
||||||
|
@ -103,60 +86,7 @@ impl LuaDeviceCreate for ContactSensor {
|
||||||
|
|
||||||
impl Device for ContactSensor {
|
impl Device for ContactSensor {
|
||||||
fn get_id(&self) -> String {
|
fn get_id(&self) -> String {
|
||||||
self.config.info.identifier()
|
self.config.identifier.clone()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl google_home::Device for ContactSensor {
|
|
||||||
fn get_device_type(&self) -> google_home::types::Type {
|
|
||||||
match self.config.sensor_type {
|
|
||||||
SensorType::Door => Type::Door,
|
|
||||||
SensorType::Drawer => Type::Drawer,
|
|
||||||
SensorType::Window => Type::Window,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_id(&self) -> String {
|
|
||||||
Device::get_id(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_device_name(&self) -> google_home::device::Name {
|
|
||||||
device::Name::new(&self.config.info.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_room_hint(&self) -> Option<&str> {
|
|
||||||
self.config.info.room.as_deref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn will_report_state(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_online(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl OpenClose for ContactSensor {
|
|
||||||
fn discrete_only_open_close(&self) -> Option<bool> {
|
|
||||||
Some(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn query_only_open_close(&self) -> Option<bool> {
|
|
||||||
Some(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn open_percent(&self) -> Result<u8, ErrorCode> {
|
|
||||||
if self.state().await.is_closed {
|
|
||||||
Ok(0)
|
|
||||||
} else {
|
|
||||||
Ok(100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_open_percent(&self, _open_percent: u8) -> Result<(), ErrorCode> {
|
|
||||||
Err(DeviceError::ActionNotAvailable.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,28 +96,6 @@ macro_rules! impl_device {
|
||||||
.unwrap())
|
.unwrap())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if impls::impls!($device: google_home::traits::OpenClose) {
|
|
||||||
// TODO: Make discrete_only_open_close and query_only_open_close static, that way we can
|
|
||||||
// add only the supported functions and drop _percet if discrete is true
|
|
||||||
methods.add_async_method("set_open_percent", |_lua, this, open_percent: u8| async move {
|
|
||||||
(this.deref().cast() as Option<&dyn google_home::traits::OpenClose>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.set_open_percent(open_percent)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method("open_percent", |_lua, this, _: ()| async move {
|
|
||||||
Ok((this.deref().cast() as Option<&dyn google_home::traits::OpenClose>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.open_percent()
|
|
||||||
.await
|
|
||||||
.unwrap())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
73
config.lua
73
config.lua
|
@ -86,7 +86,7 @@ automation.device_manager:add(living_speakers)
|
||||||
|
|
||||||
automation.device_manager:add(IkeaRemote.new({
|
automation.device_manager:add(IkeaRemote.new({
|
||||||
name = "Remote",
|
name = "Remote",
|
||||||
room = "Living Room",
|
room = "Living",
|
||||||
client = mqtt_client,
|
client = mqtt_client,
|
||||||
topic = mqtt_z2m("living/remote"),
|
topic = mqtt_z2m("living/remote"),
|
||||||
single_button = true,
|
single_button = true,
|
||||||
|
@ -216,42 +216,48 @@ automation.device_manager:add(HueSwitch.new({
|
||||||
|
|
||||||
local hallway_light_automation = {
|
local hallway_light_automation = {
|
||||||
timeout = Timeout.new(),
|
timeout = Timeout.new(),
|
||||||
forced = false,
|
state = {
|
||||||
|
door_open = false,
|
||||||
|
trash_open = false,
|
||||||
|
forced = false,
|
||||||
|
},
|
||||||
switch_callback = function(self, on)
|
switch_callback = function(self, on)
|
||||||
self.timeout:cancel()
|
self.timeout:cancel()
|
||||||
self.group.set_on(on)
|
self.group.set_on(on)
|
||||||
self.forced = on
|
self.state.forced = on
|
||||||
end,
|
end,
|
||||||
door_callback = function(self, open)
|
door_callback = function(self, open)
|
||||||
|
self.state.door_open = open
|
||||||
if open then
|
if open then
|
||||||
self.timeout:cancel()
|
self.timeout:cancel()
|
||||||
|
|
||||||
self.group.set_on(true)
|
self.group.set_on(true)
|
||||||
elseif not self.forced then
|
elseif not self.state.forced then
|
||||||
self.timeout:start(debug and 10 or 2 * 60, function()
|
self.timeout:start(debug and 10 or 2 * 60, function()
|
||||||
if self.trash:is_open_percent() == 0 then
|
if not self.state.trash_open then
|
||||||
self.group.set_on(false)
|
self.group.set_on(false)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
trash_callback = function(self, open)
|
trash_callback = function(self, open)
|
||||||
|
self.state.trash_open = open
|
||||||
if open then
|
if open then
|
||||||
self.group.set_on(true)
|
self.group.set_on(true)
|
||||||
else
|
else
|
||||||
if not self.timeout:is_waiting() and self.door:is_open_percent() == 0 and not self.forced then
|
if not self.timeout:is_waiting() and not self.state.door_open and not self.state.forced then
|
||||||
self.group.set_on(false)
|
self.group.set_on(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
light_callback = function(self, on)
|
light_callback = function(self, on)
|
||||||
if on and self.trash:is_open_percent() == 0 and self.door:is_open_percent() == 0 then
|
if on and not self.state.trash_open and not self.state.door_open then
|
||||||
-- If the door and trash are not open, that means the light got turned on manually
|
-- If the door and trash are not open, that means the light got turned on manually
|
||||||
self.timeout:cancel()
|
self.timeout:cancel()
|
||||||
self.orced = true
|
self.state.forced = true
|
||||||
elseif not on then
|
elseif not on then
|
||||||
-- The light is never forced when it is off
|
-- The light is never forced when it is off
|
||||||
self.forced = false
|
self.state.forced = false
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
@ -297,10 +303,8 @@ automation.device_manager:add(IkeaRemote.new({
|
||||||
hallway_light_automation:switch_callback(on)
|
hallway_light_automation:switch_callback(on)
|
||||||
end,
|
end,
|
||||||
}))
|
}))
|
||||||
local hallway_frontdoor = ContactSensor.new({
|
automation.device_manager:add(ContactSensor.new({
|
||||||
name = "Frontdoor",
|
identifier = "hallway_frontdoor",
|
||||||
room = "Hallway",
|
|
||||||
sensor_type = "Door",
|
|
||||||
topic = mqtt_z2m("hallway/frontdoor"),
|
topic = mqtt_z2m("hallway/frontdoor"),
|
||||||
client = mqtt_client,
|
client = mqtt_client,
|
||||||
presence = {
|
presence = {
|
||||||
|
@ -310,26 +314,19 @@ local hallway_frontdoor = ContactSensor.new({
|
||||||
callback = function(_, open)
|
callback = function(_, open)
|
||||||
hallway_light_automation:door_callback(open)
|
hallway_light_automation:door_callback(open)
|
||||||
end,
|
end,
|
||||||
})
|
}))
|
||||||
automation.device_manager:add(hallway_frontdoor)
|
automation.device_manager:add(ContactSensor.new({
|
||||||
hallway_lights_automation.door = hallway_frontdoor
|
identifier = "hallway_trash",
|
||||||
|
|
||||||
local hallway_trash = ContactSensor.new({
|
|
||||||
name = "Trash",
|
|
||||||
room = "Hallway",
|
|
||||||
sensor_type = "Drawer",
|
|
||||||
topic = mqtt_z2m("hallway/trash"),
|
topic = mqtt_z2m("hallway/trash"),
|
||||||
client = mqtt_client,
|
client = mqtt_client,
|
||||||
callback = function(_, open)
|
callback = function(_, open)
|
||||||
hallway_light_automation:trash_callback(open)
|
hallway_light_automation:trash_callback(open)
|
||||||
end,
|
end,
|
||||||
})
|
}))
|
||||||
automation.device_manager:add(hallway_trash)
|
|
||||||
hallway_lights_automation.trash = hallway_frontdoor
|
|
||||||
|
|
||||||
automation.device_manager:add(LightOnOff.new({
|
automation.device_manager:add(LightOnOff.new({
|
||||||
name = "Light",
|
name = "Light",
|
||||||
room = "Guest Room",
|
room = "Guest",
|
||||||
topic = mqtt_z2m("guest/light"),
|
topic = mqtt_z2m("guest/light"),
|
||||||
client = mqtt_client,
|
client = mqtt_client,
|
||||||
}))
|
}))
|
||||||
|
@ -342,32 +339,6 @@ local bedroom_air_filter = AirFilter.new({
|
||||||
})
|
})
|
||||||
automation.device_manager:add(bedroom_air_filter)
|
automation.device_manager:add(bedroom_air_filter)
|
||||||
|
|
||||||
automation.device_manager:add(ContactSensor.new({
|
|
||||||
name = "Balcony",
|
|
||||||
room = "Living Room",
|
|
||||||
sensor_type = "Door",
|
|
||||||
topic = mqtt_z2m("living/balcony"),
|
|
||||||
client = mqtt_client,
|
|
||||||
}))
|
|
||||||
automation.device_manager:add(ContactSensor.new({
|
|
||||||
name = "Window",
|
|
||||||
room = "Living Room",
|
|
||||||
topic = mqtt_z2m("living/window"),
|
|
||||||
client = mqtt_client,
|
|
||||||
}))
|
|
||||||
automation.device_manager:add(ContactSensor.new({
|
|
||||||
name = "Window",
|
|
||||||
room = "Bedroom",
|
|
||||||
topic = mqtt_z2m("bedroom/window"),
|
|
||||||
client = mqtt_client,
|
|
||||||
}))
|
|
||||||
automation.device_manager:add(ContactSensor.new({
|
|
||||||
name = "Window",
|
|
||||||
room = "Guest Room",
|
|
||||||
topic = mqtt_z2m("guest/window"),
|
|
||||||
client = mqtt_client,
|
|
||||||
}))
|
|
||||||
|
|
||||||
automation.device_manager:schedule("0 0 19 * * *", function()
|
automation.device_manager:schedule("0 0 19 * * *", function()
|
||||||
bedroom_air_filter:set_on(true)
|
bedroom_air_filter:set_on(true)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -14,13 +14,6 @@ traits! {
|
||||||
async fn on(&self) -> Result<bool, ErrorCode>,
|
async fn on(&self) -> Result<bool, ErrorCode>,
|
||||||
"action.devices.commands.OnOff" => async fn set_on(&self, on: bool) -> Result<(), ErrorCode>,
|
"action.devices.commands.OnOff" => async fn set_on(&self, on: bool) -> Result<(), ErrorCode>,
|
||||||
},
|
},
|
||||||
"action.devices.traits.OpenClose" => trait OpenClose {
|
|
||||||
discrete_only_open_close: Option<bool>,
|
|
||||||
command_only_open_close: Option<bool>,
|
|
||||||
query_only_open_close: Option<bool>,
|
|
||||||
async fn open_percent(&self) -> Result<u8, ErrorCode>,
|
|
||||||
"action.devices.commands.OpenClose" => async fn set_open_percent(&self, open_percent: u8) -> Result<(), ErrorCode>,
|
|
||||||
},
|
|
||||||
"action.devices.traits.Brightness" => trait Brightness {
|
"action.devices.traits.Brightness" => trait Brightness {
|
||||||
command_only_brightness: Option<bool>,
|
command_only_brightness: Option<bool>,
|
||||||
async fn brightness(&self) -> Result<u8, ErrorCode>,
|
async fn brightness(&self) -> Result<u8, ErrorCode>,
|
||||||
|
|
|
@ -12,10 +12,4 @@ pub enum Type {
|
||||||
Scene,
|
Scene,
|
||||||
#[serde(rename = "action.devices.types.AIRPURIFIER")]
|
#[serde(rename = "action.devices.types.AIRPURIFIER")]
|
||||||
AirPurifier,
|
AirPurifier,
|
||||||
#[serde(rename = "action.devices.types.DOOR")]
|
|
||||||
Door,
|
|
||||||
#[serde(rename = "action.devices.types.WINDOW")]
|
|
||||||
Window,
|
|
||||||
#[serde(rename = "action.devices.types.DRAWER")]
|
|
||||||
Drawer,
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user