Compare commits
3 Commits
e02edbcd28
...
2b62aca78a
Author | SHA1 | Date | |
---|---|---|---|
2b62aca78a | |||
40426862e5 | |||
c3bd05434c |
|
@ -1,30 +1,9 @@
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{Data, DataStruct, DeriveInput, Fields, FieldsNamed};
|
use syn::DeriveInput;
|
||||||
|
|
||||||
pub fn impl_lua_device_macro(ast: &DeriveInput) -> TokenStream {
|
pub fn impl_lua_device_macro(ast: &DeriveInput) -> TokenStream {
|
||||||
let name = &ast.ident;
|
let name = &ast.ident;
|
||||||
// TODO: Handle errors properly
|
|
||||||
// This includes making sure one, and only one config is specified
|
|
||||||
let config = if let Data::Struct(DataStruct {
|
|
||||||
fields: Fields::Named(FieldsNamed { ref named, .. }),
|
|
||||||
..
|
|
||||||
}) = ast.data
|
|
||||||
{
|
|
||||||
named
|
|
||||||
.iter()
|
|
||||||
.find(|&field| {
|
|
||||||
field
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.any(|attr| attr.path().is_ident("config"))
|
|
||||||
})
|
|
||||||
.map(|field| field.ty.clone())
|
|
||||||
.unwrap()
|
|
||||||
} else {
|
|
||||||
unimplemented!()
|
|
||||||
};
|
|
||||||
|
|
||||||
let gen = quote! {
|
let gen = quote! {
|
||||||
impl #name {
|
impl #name {
|
||||||
pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
|
pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
|
||||||
|
@ -34,8 +13,10 @@ pub fn impl_lua_device_macro(ast: &DeriveInput) -> TokenStream {
|
||||||
impl mlua::UserData for #name {
|
impl mlua::UserData for #name {
|
||||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_async_function("new", |lua, config: mlua::Value| async {
|
methods.add_async_function("new", |lua, config: mlua::Value| async {
|
||||||
let config: #config = mlua::FromLua::from_lua(config, lua)?;
|
let config = mlua::FromLua::from_lua(config, lua)?;
|
||||||
let device = #name::create(config).await.map_err(mlua::ExternalError::into_lua_err)?;
|
|
||||||
|
// TODO: Using crate:: could cause issues
|
||||||
|
let device: #name = crate::devices::LuaDeviceCreate::create(config).await.map_err(mlua::ExternalError::into_lua_err)?;
|
||||||
|
|
||||||
Ok(crate::device_manager::WrappedDevice::new(Box::new(device)))
|
Ok(crate::device_manager::WrappedDevice::new(Box::new(device)))
|
||||||
});
|
});
|
||||||
|
|
35
config.lua
35
config.lua
|
@ -13,6 +13,15 @@ local function mqtt_automation(topic)
|
||||||
return "automation/" .. topic
|
return "automation/" .. topic
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local mqtt_client = automation.create_mqtt_client({
|
||||||
|
host = debug and "olympus.lan.huizinga.dev" or "mosquitto",
|
||||||
|
port = 8883,
|
||||||
|
client_name = debug and "automation-debug" or "automation_rs",
|
||||||
|
username = "mqtt",
|
||||||
|
password = automation.util.get_env("MQTT_PASSWORD"),
|
||||||
|
tls = debug and true or false,
|
||||||
|
})
|
||||||
|
|
||||||
automation.device_manager:add(Ntfy.new({
|
automation.device_manager:add(Ntfy.new({
|
||||||
topic = automation.util.get_env("NTFY_TOPIC"),
|
topic = automation.util.get_env("NTFY_TOPIC"),
|
||||||
event_channel = automation.event_channel,
|
event_channel = automation.event_channel,
|
||||||
|
@ -20,14 +29,14 @@ automation.device_manager:add(Ntfy.new({
|
||||||
|
|
||||||
automation.device_manager:add(Presence.new({
|
automation.device_manager:add(Presence.new({
|
||||||
topic = "automation_dev/presence/+/#",
|
topic = "automation_dev/presence/+/#",
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
event_channel = automation.event_channel,
|
event_channel = automation.event_channel,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
automation.device_manager:add(DebugBridge.new({
|
automation.device_manager:add(DebugBridge.new({
|
||||||
identifier = "debug_bridge",
|
identifier = "debug_bridge",
|
||||||
topic = mqtt_automation("debug"),
|
topic = mqtt_automation("debug"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
local hue_ip = "10.0.0.146"
|
local hue_ip = "10.0.0.146"
|
||||||
|
@ -46,7 +55,7 @@ automation.device_manager:add(HueBridge.new({
|
||||||
automation.device_manager:add(LightSensor.new({
|
automation.device_manager:add(LightSensor.new({
|
||||||
identifier = "living_light_sensor",
|
identifier = "living_light_sensor",
|
||||||
topic = mqtt_z2m("living/light"),
|
topic = mqtt_z2m("living/light"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
min = 22000,
|
min = 22000,
|
||||||
max = 23500,
|
max = 23500,
|
||||||
event_channel = automation.event_channel,
|
event_channel = automation.event_channel,
|
||||||
|
@ -56,7 +65,7 @@ automation.device_manager:add(WakeOnLAN.new({
|
||||||
name = "Zeus",
|
name = "Zeus",
|
||||||
room = "Living Room",
|
room = "Living Room",
|
||||||
topic = mqtt_automation("appliance/living_room/zeus"),
|
topic = mqtt_automation("appliance/living_room/zeus"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
mac_address = "30:9c:23:60:9c:13",
|
mac_address = "30:9c:23:60:9c:13",
|
||||||
broadcast_ip = "10.0.0.255",
|
broadcast_ip = "10.0.0.255",
|
||||||
}))
|
}))
|
||||||
|
@ -69,7 +78,7 @@ automation.device_manager:add(living_speakers)
|
||||||
automation.device_manager:add(AudioSetup.new({
|
automation.device_manager:add(AudioSetup.new({
|
||||||
identifier = "living_audio",
|
identifier = "living_audio",
|
||||||
topic = mqtt_z2m("living/remote"),
|
topic = mqtt_z2m("living/remote"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
mixer = living_mixer,
|
mixer = living_mixer,
|
||||||
speakers = living_speakers,
|
speakers = living_speakers,
|
||||||
}))
|
}))
|
||||||
|
@ -79,7 +88,7 @@ automation.device_manager:add(IkeaOutlet.new({
|
||||||
name = "Kettle",
|
name = "Kettle",
|
||||||
room = "Kitchen",
|
room = "Kitchen",
|
||||||
topic = mqtt_z2m("kitchen/kettle"),
|
topic = mqtt_z2m("kitchen/kettle"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
timeout = debug and 5 or 300,
|
timeout = debug and 5 or 300,
|
||||||
remotes = {
|
remotes = {
|
||||||
{ topic = mqtt_z2m("bedroom/remote") },
|
{ topic = mqtt_z2m("bedroom/remote") },
|
||||||
|
@ -92,14 +101,14 @@ automation.device_manager:add(IkeaOutlet.new({
|
||||||
name = "Light",
|
name = "Light",
|
||||||
room = "Bathroom",
|
room = "Bathroom",
|
||||||
topic = mqtt_z2m("batchroom/light"),
|
topic = mqtt_z2m("batchroom/light"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
timeout = debug and 60 or 45 * 60,
|
timeout = debug and 60 or 45 * 60,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
automation.device_manager:add(Washer.new({
|
automation.device_manager:add(Washer.new({
|
||||||
identifier = "bathroom_washer",
|
identifier = "bathroom_washer",
|
||||||
topic = mqtt_z2m("batchroom/washer"),
|
topic = mqtt_z2m("batchroom/washer"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
threshold = 1,
|
threshold = 1,
|
||||||
event_channel = automation.event_channel,
|
event_channel = automation.event_channel,
|
||||||
}))
|
}))
|
||||||
|
@ -109,7 +118,7 @@ automation.device_manager:add(IkeaOutlet.new({
|
||||||
name = "Charger",
|
name = "Charger",
|
||||||
room = "Workbench",
|
room = "Workbench",
|
||||||
topic = mqtt_z2m("workbench/charger"),
|
topic = mqtt_z2m("workbench/charger"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
timeout = debug and 5 or 20 * 3600,
|
timeout = debug and 5 or 20 * 3600,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -117,7 +126,7 @@ automation.device_manager:add(IkeaOutlet.new({
|
||||||
name = "Outlet",
|
name = "Outlet",
|
||||||
room = "Workbench",
|
room = "Workbench",
|
||||||
topic = mqtt_z2m("workbench/outlet"),
|
topic = mqtt_z2m("workbench/outlet"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
local hallway_lights = automation.device_manager:add(HueGroup.new({
|
local hallway_lights = automation.device_manager:add(HueGroup.new({
|
||||||
|
@ -130,13 +139,13 @@ local hallway_lights = automation.device_manager:add(HueGroup.new({
|
||||||
remotes = {
|
remotes = {
|
||||||
{ topic = mqtt_z2m("hallway/remote") },
|
{ topic = mqtt_z2m("hallway/remote") },
|
||||||
},
|
},
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
automation.device_manager:add(ContactSensor.new({
|
automation.device_manager:add(ContactSensor.new({
|
||||||
identifier = "hallway_frontdoor",
|
identifier = "hallway_frontdoor",
|
||||||
topic = mqtt_z2m("hallway/frontdoor"),
|
topic = mqtt_z2m("hallway/frontdoor"),
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
presence = {
|
presence = {
|
||||||
topic = mqtt_automation("presence/contact/frontdoor"),
|
topic = mqtt_automation("presence/contact/frontdoor"),
|
||||||
timeout = debug and 10 or 15 * 60,
|
timeout = debug and 10 or 15 * 60,
|
||||||
|
@ -151,7 +160,7 @@ local bedroom_air_filter = automation.device_manager:add(AirFilter.new({
|
||||||
name = "Air Filter",
|
name = "Air Filter",
|
||||||
room = "Bedroom",
|
room = "Bedroom",
|
||||||
topic = "pico/filter/bedroom",
|
topic = "pico/filter/bedroom",
|
||||||
client = automation.mqtt_client,
|
client = mqtt_client,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
-- TODO: Use the wrapped device bedroom_air_filter instead of the string
|
-- TODO: Use the wrapped device bedroom_air_filter instead of the string
|
||||||
|
|
|
@ -1,9 +1,2 @@
|
||||||
openid:
|
openid:
|
||||||
base_url: "https://login.huizinga.dev/api/oidc"
|
base_url: "https://login.huizinga.dev/api/oidc"
|
||||||
|
|
||||||
mqtt:
|
|
||||||
host: "mosquitto"
|
|
||||||
port: 8883
|
|
||||||
client_name: "automation_rs"
|
|
||||||
username: "mqtt"
|
|
||||||
password: "${MQTT_PASSWORD}"
|
|
||||||
|
|
|
@ -1,10 +1,2 @@
|
||||||
openid:
|
openid:
|
||||||
base_url: "https://login.huizinga.dev/api/oidc"
|
base_url: "https://login.huizinga.dev/api/oidc"
|
||||||
|
|
||||||
mqtt:
|
|
||||||
host: "olympus.lan.huizinga.dev"
|
|
||||||
port: 8883
|
|
||||||
client_name: "automation-zeus"
|
|
||||||
username: "mqtt"
|
|
||||||
password: "${MQTT_PASSWORD}"
|
|
||||||
tls: true
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::time::Duration;
|
||||||
|
|
||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
use rumqttc::{MqttOptions, Transport};
|
use rumqttc::{MqttOptions, Transport};
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::Deserialize;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::auth::OpenIDConfig;
|
use crate::auth::OpenIDConfig;
|
||||||
|
@ -13,8 +13,6 @@ use crate::error::{ConfigParseError, MissingEnv};
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub openid: OpenIDConfig,
|
pub openid: OpenIDConfig,
|
||||||
#[serde(deserialize_with = "deserialize_mqtt_options")]
|
|
||||||
pub mqtt: MqttOptions,
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub fullfillment: FullfillmentConfig,
|
pub fullfillment: FullfillmentConfig,
|
||||||
}
|
}
|
||||||
|
@ -44,13 +42,6 @@ impl From<MqttConfig> for MqttOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_mqtt_options<'de, D>(deserializer: D) -> Result<MqttOptions, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
Ok(MqttOptions::from(MqttConfig::deserialize(deserializer)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct FullfillmentConfig {
|
pub struct FullfillmentConfig {
|
||||||
#[serde(default = "default_fullfillment_ip")]
|
#[serde(default = "default_fullfillment_ip")]
|
||||||
|
|
|
@ -8,9 +8,9 @@ use google_home::GoogleHomeDevice;
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
|
use super::LuaDeviceCreate;
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
use crate::config::{InfoConfig, MqttDeviceConfig};
|
||||||
use crate::devices::Device;
|
use crate::devices::Device;
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::OnMqtt;
|
use crate::event::OnMqtt;
|
||||||
use crate::messages::{AirFilterFanState, AirFilterState, SetAirFilterFanState};
|
use crate::messages::{AirFilterFanState, AirFilterState, SetAirFilterFanState};
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
@ -27,7 +27,6 @@ pub struct AirFilterConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct AirFilter {
|
pub struct AirFilter {
|
||||||
#[config]
|
|
||||||
config: AirFilterConfig,
|
config: AirFilterConfig,
|
||||||
|
|
||||||
last_known_state: AirFilterState,
|
last_known_state: AirFilterState,
|
||||||
|
@ -42,7 +41,7 @@ impl AirFilter {
|
||||||
self.config
|
self.config
|
||||||
.client
|
.client
|
||||||
.publish(
|
.publish(
|
||||||
topic.clone(),
|
&topic,
|
||||||
rumqttc::QoS::AtLeastOnce,
|
rumqttc::QoS::AtLeastOnce,
|
||||||
false,
|
false,
|
||||||
serde_json::to_string(&message).unwrap(),
|
serde_json::to_string(&message).unwrap(),
|
||||||
|
@ -53,13 +52,17 @@ impl AirFilter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AirFilter {
|
#[async_trait]
|
||||||
async fn create(config: AirFilterConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for AirFilter {
|
||||||
|
type Config = AirFilterConfig;
|
||||||
|
type Error = rumqttc::ClientError;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.info.identifier(), "Setting up AirFilter");
|
trace!(id = config.info.identifier(), "Setting up AirFilter");
|
||||||
|
|
||||||
config
|
config
|
||||||
.client
|
.client
|
||||||
.subscribe(config.mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce)
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use google_home::traits::OnOff;
|
use google_home::traits::OnOff;
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::Device;
|
use super::{Device, LuaDeviceCreate};
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::MqttDeviceConfig;
|
||||||
use crate::device_manager::WrappedDevice;
|
use crate::device_manager::WrappedDevice;
|
||||||
use crate::devices::As;
|
use crate::devices::As;
|
||||||
|
@ -27,12 +27,15 @@ pub struct AudioSetupConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct AudioSetup {
|
pub struct AudioSetup {
|
||||||
#[config]
|
|
||||||
config: AudioSetupConfig,
|
config: AudioSetupConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioSetup {
|
#[async_trait]
|
||||||
async fn create(config: AudioSetupConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for AudioSetup {
|
||||||
|
type Config = AudioSetupConfig;
|
||||||
|
type Error = DeviceConfigError;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.identifier, "Setting up AudioSetup");
|
trace!(id = config.identifier, "Setting up AudioSetup");
|
||||||
|
|
||||||
let mixer_id = config.mixer.read().await.get_id().to_owned();
|
let mixer_id = config.mixer.read().await.get_id().to_owned();
|
||||||
|
@ -47,7 +50,7 @@ impl AudioSetup {
|
||||||
|
|
||||||
config
|
config
|
||||||
.client
|
.client
|
||||||
.subscribe(config.mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce)
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(AudioSetup { config })
|
Ok(AudioSetup { config })
|
||||||
|
|
|
@ -7,7 +7,7 @@ use mlua::FromLua;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::Device;
|
use super::{Device, LuaDeviceCreate};
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::MqttDeviceConfig;
|
||||||
use crate::device_manager::WrappedDevice;
|
use crate::device_manager::WrappedDevice;
|
||||||
use crate::devices::{As, DEFAULT_PRESENCE};
|
use crate::devices::{As, DEFAULT_PRESENCE};
|
||||||
|
@ -64,7 +64,6 @@ pub struct ContactSensorConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct ContactSensor {
|
pub struct ContactSensor {
|
||||||
#[config]
|
|
||||||
config: ContactSensorConfig,
|
config: ContactSensorConfig,
|
||||||
|
|
||||||
overall_presence: bool,
|
overall_presence: bool,
|
||||||
|
@ -72,8 +71,12 @@ pub struct ContactSensor {
|
||||||
handle: Option<JoinHandle<()>>,
|
handle: Option<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContactSensor {
|
#[async_trait]
|
||||||
async fn create(config: ContactSensorConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for ContactSensor {
|
||||||
|
type Config = ContactSensorConfig;
|
||||||
|
type Error = DeviceConfigError;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.identifier, "Setting up ContactSensor");
|
trace!(id = config.identifier, "Setting up ContactSensor");
|
||||||
|
|
||||||
// Make sure the devices implement the required traits
|
// Make sure the devices implement the required traits
|
||||||
|
@ -93,7 +96,7 @@ impl ContactSensor {
|
||||||
|
|
||||||
config
|
config
|
||||||
.client
|
.client
|
||||||
.subscribe(config.mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce)
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -192,7 +195,7 @@ impl OnMqtt for ContactSensor {
|
||||||
self.config
|
self.config
|
||||||
.client
|
.client
|
||||||
.publish(
|
.publish(
|
||||||
presence.mqtt.topic.clone(),
|
&presence.mqtt.topic,
|
||||||
rumqttc::QoS::AtLeastOnce,
|
rumqttc::QoS::AtLeastOnce,
|
||||||
false,
|
false,
|
||||||
serde_json::to_string(&PresenceMessage::new(true)).unwrap(),
|
serde_json::to_string(&PresenceMessage::new(true)).unwrap(),
|
||||||
|
@ -217,7 +220,7 @@ impl OnMqtt for ContactSensor {
|
||||||
tokio::time::sleep(timeout).await;
|
tokio::time::sleep(timeout).await;
|
||||||
debug!(id, "Removing door device!");
|
debug!(id, "Removing door device!");
|
||||||
client
|
client
|
||||||
.publish(topic.clone(), rumqttc::QoS::AtLeastOnce, false, "")
|
.publish(&topic, rumqttc::QoS::AtLeastOnce, false, "")
|
||||||
.await
|
.await
|
||||||
.map_err(|err| warn!("Failed to publish presence on {topic}: {err}"))
|
.map_err(|err| warn!("Failed to publish presence on {topic}: {err}"))
|
||||||
.ok();
|
.ok();
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
use std::convert::Infallible;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use tracing::{trace, warn};
|
use tracing::{trace, warn};
|
||||||
|
|
||||||
|
use super::LuaDeviceCreate;
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::MqttDeviceConfig;
|
||||||
use crate::devices::Device;
|
use crate::devices::Device;
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::{OnDarkness, OnPresence};
|
use crate::event::{OnDarkness, OnPresence};
|
||||||
use crate::messages::{DarknessMessage, PresenceMessage};
|
use crate::messages::{DarknessMessage, PresenceMessage};
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
@ -20,12 +22,15 @@ pub struct DebugBridgeConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct DebugBridge {
|
pub struct DebugBridge {
|
||||||
#[config]
|
|
||||||
config: DebugBridgeConfig,
|
config: DebugBridgeConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugBridge {
|
#[async_trait]
|
||||||
async fn create(config: DebugBridgeConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for DebugBridge {
|
||||||
|
type Config = DebugBridgeConfig;
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.identifier, "Setting up DebugBridge");
|
trace!(id = config.identifier, "Setting up DebugBridge");
|
||||||
Ok(Self { config })
|
Ok(Self { config })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::convert::Infallible;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
@ -5,8 +6,8 @@ use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
|
||||||
|
use super::LuaDeviceCreate;
|
||||||
use crate::devices::Device;
|
use crate::devices::Device;
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::{OnDarkness, OnPresence};
|
use crate::event::{OnDarkness, OnPresence};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -32,7 +33,6 @@ pub struct HueBridgeConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct HueBridge {
|
pub struct HueBridge {
|
||||||
#[config]
|
|
||||||
config: HueBridgeConfig,
|
config: HueBridgeConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,12 +41,18 @@ struct FlagMessage {
|
||||||
flag: bool,
|
flag: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HueBridge {
|
#[async_trait]
|
||||||
async fn create(config: HueBridgeConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for HueBridge {
|
||||||
|
type Config = HueBridgeConfig;
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Infallible> {
|
||||||
trace!(id = config.identifier, "Setting up HueBridge");
|
trace!(id = config.identifier, "Setting up HueBridge");
|
||||||
Ok(Self { config })
|
Ok(Self { config })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HueBridge {
|
||||||
pub async fn set_flag(&self, flag: Flag, value: bool) {
|
pub async fn set_flag(&self, flag: Flag, value: bool) {
|
||||||
let flag_id = match flag {
|
let flag_id = match flag {
|
||||||
Flag::Presence => self.config.flags.presence,
|
Flag::Presence => self.config.flags.presence,
|
||||||
|
|
|
@ -9,9 +9,8 @@ use google_home::traits::OnOff;
|
||||||
use rumqttc::{Publish, SubscribeFilter};
|
use rumqttc::{Publish, SubscribeFilter};
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::Device;
|
use super::{Device, LuaDeviceCreate};
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::MqttDeviceConfig;
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::OnMqtt;
|
use crate::event::OnMqtt;
|
||||||
use crate::messages::{RemoteAction, RemoteMessage};
|
use crate::messages::{RemoteAction, RemoteMessage};
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
@ -34,13 +33,16 @@ pub struct HueGroupConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct HueGroup {
|
pub struct HueGroup {
|
||||||
#[config]
|
|
||||||
config: HueGroupConfig,
|
config: HueGroupConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Couple of helper function to get the correct urls
|
// Couple of helper function to get the correct urls
|
||||||
impl HueGroup {
|
#[async_trait]
|
||||||
async fn create(config: HueGroupConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for HueGroup {
|
||||||
|
type Config = HueGroupConfig;
|
||||||
|
type Error = rumqttc::ClientError;
|
||||||
|
|
||||||
|
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() {
|
if !config.remotes.is_empty() {
|
||||||
|
@ -55,7 +57,9 @@ impl HueGroup {
|
||||||
|
|
||||||
Ok(Self { config })
|
Ok(Self { config })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HueGroup {
|
||||||
fn url_base(&self) -> String {
|
fn url_base(&self) -> String {
|
||||||
format!("http://{}/api/{}", self.config.addr, self.config.login)
|
format!("http://{}/api/{}", self.config.addr, self.config.login)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,9 @@ use serde::Deserialize;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
|
use super::LuaDeviceCreate;
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
use crate::config::{InfoConfig, MqttDeviceConfig};
|
||||||
use crate::devices::Device;
|
use crate::devices::Device;
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::{OnMqtt, OnPresence};
|
use crate::event::{OnMqtt, OnPresence};
|
||||||
use crate::messages::{OnOffMessage, RemoteAction, RemoteMessage};
|
use crate::messages::{OnOffMessage, RemoteAction, RemoteMessage};
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
@ -47,7 +47,6 @@ pub struct IkeaOutletConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct IkeaOutlet {
|
pub struct IkeaOutlet {
|
||||||
#[config]
|
|
||||||
config: IkeaOutletConfig,
|
config: IkeaOutletConfig,
|
||||||
|
|
||||||
last_known_state: bool,
|
last_known_state: bool,
|
||||||
|
@ -61,7 +60,7 @@ async fn set_on(client: WrappedAsyncClient, topic: &str, on: bool) {
|
||||||
// TODO: Handle potential errors here
|
// TODO: Handle potential errors here
|
||||||
client
|
client
|
||||||
.publish(
|
.publish(
|
||||||
topic.clone(),
|
&topic,
|
||||||
rumqttc::QoS::AtLeastOnce,
|
rumqttc::QoS::AtLeastOnce,
|
||||||
false,
|
false,
|
||||||
serde_json::to_string(&message).unwrap(),
|
serde_json::to_string(&message).unwrap(),
|
||||||
|
@ -71,8 +70,12 @@ async fn set_on(client: WrappedAsyncClient, topic: &str, on: bool) {
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IkeaOutlet {
|
#[async_trait]
|
||||||
async fn create(config: IkeaOutletConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for IkeaOutlet {
|
||||||
|
type Config = IkeaOutletConfig;
|
||||||
|
type Error = rumqttc::ClientError;
|
||||||
|
|
||||||
|
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() {
|
if !config.remotes.is_empty() {
|
||||||
|
@ -85,6 +88,11 @@ impl IkeaOutlet {
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config
|
||||||
|
.client
|
||||||
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
config,
|
config,
|
||||||
last_known_state: false,
|
last_known_state: false,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::convert::Infallible;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
||||||
|
@ -12,8 +13,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use super::Device;
|
use super::{Device, LuaDeviceCreate};
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct KasaOutletConfig {
|
pub struct KasaOutletConfig {
|
||||||
|
@ -24,12 +24,15 @@ pub struct KasaOutletConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct KasaOutlet {
|
pub struct KasaOutlet {
|
||||||
#[config]
|
|
||||||
config: KasaOutletConfig,
|
config: KasaOutletConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KasaOutlet {
|
#[async_trait]
|
||||||
async fn create(config: KasaOutletConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for KasaOutlet {
|
||||||
|
type Config = KasaOutletConfig;
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.identifier, "Setting up KasaOutlet");
|
trace!(id = config.identifier, "Setting up KasaOutlet");
|
||||||
Ok(Self { config })
|
Ok(Self { config })
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
|
use super::LuaDeviceCreate;
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::MqttDeviceConfig;
|
||||||
use crate::devices::Device;
|
use crate::devices::Device;
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::{self, Event, EventChannel, OnMqtt};
|
use crate::event::{self, Event, EventChannel, OnMqtt};
|
||||||
use crate::messages::BrightnessMessage;
|
use crate::messages::BrightnessMessage;
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
@ -27,19 +27,22 @@ pub const DEFAULT: bool = false;
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct LightSensor {
|
pub struct LightSensor {
|
||||||
#[config]
|
|
||||||
config: LightSensorConfig,
|
config: LightSensorConfig,
|
||||||
|
|
||||||
is_dark: bool,
|
is_dark: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LightSensor {
|
#[async_trait]
|
||||||
async fn create(config: LightSensorConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for LightSensor {
|
||||||
|
type Config = LightSensorConfig;
|
||||||
|
type Error = rumqttc::ClientError;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.identifier, "Setting up LightSensor");
|
trace!(id = config.identifier, "Setting up LightSensor");
|
||||||
|
|
||||||
config
|
config
|
||||||
.client
|
.client
|
||||||
.subscribe(config.mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce)
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
|
@ -12,6 +12,7 @@ mod presence;
|
||||||
mod wake_on_lan;
|
mod wake_on_lan;
|
||||||
mod washer;
|
mod washer;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use google_home::device::AsGoogleHomeDevice;
|
use google_home::device::AsGoogleHomeDevice;
|
||||||
use google_home::traits::OnOff;
|
use google_home::traits::OnOff;
|
||||||
|
|
||||||
|
@ -31,6 +32,16 @@ pub use self::washer::*;
|
||||||
use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence};
|
use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence};
|
||||||
use crate::traits::Timeout;
|
use crate::traits::Timeout;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait LuaDeviceCreate {
|
||||||
|
type Config;
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
#[impl_cast::device(As: OnMqtt + OnPresence + OnDarkness + OnNotification + OnOff + Timeout)]
|
#[impl_cast::device(As: OnMqtt + OnPresence + OnDarkness + OnNotification + OnOff + Timeout)]
|
||||||
pub trait Device: AsGoogleHomeDevice + std::fmt::Debug + Sync + Send {
|
pub trait Device: AsGoogleHomeDevice + std::fmt::Debug + Sync + Send {
|
||||||
fn get_id(&self) -> String;
|
fn get_id(&self) -> String;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::convert::Infallible;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
|
@ -6,8 +7,8 @@ use serde::Serialize;
|
||||||
use serde_repr::*;
|
use serde_repr::*;
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
|
||||||
|
use super::LuaDeviceCreate;
|
||||||
use crate::devices::Device;
|
use crate::devices::Device;
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::{self, Event, EventChannel, OnNotification, OnPresence};
|
use crate::event::{self, Event, EventChannel, OnNotification, OnPresence};
|
||||||
|
|
||||||
#[derive(Debug, Serialize_repr, Clone, Copy)]
|
#[derive(Debug, Serialize_repr, Clone, Copy)]
|
||||||
|
@ -121,12 +122,15 @@ pub struct NtfyConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct Ntfy {
|
pub struct Ntfy {
|
||||||
#[config]
|
|
||||||
config: NtfyConfig,
|
config: NtfyConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ntfy {
|
#[async_trait]
|
||||||
async fn create(config: NtfyConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for Ntfy {
|
||||||
|
type Config = NtfyConfig;
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = "ntfy", "Setting up Ntfy");
|
trace!(id = "ntfy", "Setting up Ntfy");
|
||||||
Ok(Self { config })
|
Ok(Self { config })
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
|
use super::LuaDeviceCreate;
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::MqttDeviceConfig;
|
||||||
use crate::devices::Device;
|
use crate::devices::Device;
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::{self, Event, EventChannel, OnMqtt};
|
use crate::event::{self, Event, EventChannel, OnMqtt};
|
||||||
use crate::messages::PresenceMessage;
|
use crate::messages::PresenceMessage;
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
@ -26,19 +26,22 @@ pub const DEFAULT_PRESENCE: bool = false;
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct Presence {
|
pub struct Presence {
|
||||||
#[config]
|
|
||||||
config: PresenceConfig,
|
config: PresenceConfig,
|
||||||
devices: HashMap<String, bool>,
|
devices: HashMap<String, bool>,
|
||||||
current_overall_presence: bool,
|
current_overall_presence: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Presence {
|
#[async_trait]
|
||||||
async fn create(config: PresenceConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for Presence {
|
||||||
|
type Config = PresenceConfig;
|
||||||
|
type Error = rumqttc::ClientError;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = "ntfy", "Setting up Presence");
|
trace!(id = "ntfy", "Setting up Presence");
|
||||||
|
|
||||||
config
|
config
|
||||||
.client
|
.client
|
||||||
.subscribe(config.mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce)
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
|
@ -10,9 +10,8 @@ use google_home::{device, GoogleHomeDevice};
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tracing::{debug, error, trace};
|
use tracing::{debug, error, trace};
|
||||||
|
|
||||||
use super::Device;
|
use super::{Device, LuaDeviceCreate};
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
use crate::config::{InfoConfig, MqttDeviceConfig};
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::OnMqtt;
|
use crate::event::OnMqtt;
|
||||||
use crate::messages::ActivateMessage;
|
use crate::messages::ActivateMessage;
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
@ -32,17 +31,20 @@ pub struct WakeOnLANConfig {
|
||||||
|
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct WakeOnLAN {
|
pub struct WakeOnLAN {
|
||||||
#[config]
|
|
||||||
config: WakeOnLANConfig,
|
config: WakeOnLANConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WakeOnLAN {
|
#[async_trait]
|
||||||
async fn create(config: WakeOnLANConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for WakeOnLAN {
|
||||||
|
type Config = WakeOnLANConfig;
|
||||||
|
type Error = rumqttc::ClientError;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.info.identifier(), "Setting up WakeOnLAN");
|
trace!(id = config.info.identifier(), "Setting up WakeOnLAN");
|
||||||
|
|
||||||
config
|
config
|
||||||
.client
|
.client
|
||||||
.subscribe(config.mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce)
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Self { config })
|
Ok(Self { config })
|
||||||
|
|
|
@ -4,9 +4,8 @@ use rumqttc::Publish;
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::ntfy::Priority;
|
use super::ntfy::Priority;
|
||||||
use super::{Device, Notification};
|
use super::{Device, LuaDeviceCreate, Notification};
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::MqttDeviceConfig;
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::{self, Event, EventChannel, OnMqtt};
|
use crate::event::{self, Event, EventChannel, OnMqtt};
|
||||||
use crate::messages::PowerMessage;
|
use crate::messages::PowerMessage;
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
@ -27,19 +26,22 @@ pub struct WasherConfig {
|
||||||
// TODO: Add google home integration
|
// TODO: Add google home integration
|
||||||
#[derive(Debug, LuaDevice)]
|
#[derive(Debug, LuaDevice)]
|
||||||
pub struct Washer {
|
pub struct Washer {
|
||||||
#[config]
|
|
||||||
config: WasherConfig,
|
config: WasherConfig,
|
||||||
|
|
||||||
running: isize,
|
running: isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Washer {
|
#[async_trait]
|
||||||
async fn create(config: WasherConfig) -> Result<Self, DeviceConfigError> {
|
impl LuaDeviceCreate for Washer {
|
||||||
|
type Config = WasherConfig;
|
||||||
|
type Error = rumqttc::ClientError;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||||
trace!(id = config.identifier, "Setting up Washer");
|
trace!(id = config.identifier, "Setting up Washer");
|
||||||
|
|
||||||
config
|
config
|
||||||
.client
|
.client
|
||||||
.subscribe(config.mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce)
|
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Self { config, running: 0 })
|
Ok(Self { config, running: 0 })
|
||||||
|
|
|
@ -92,13 +92,9 @@ impl MissingWildcard {
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum DeviceConfigError {
|
pub enum DeviceConfigError {
|
||||||
#[error("Child '{1}' of device '{0}' does not exist")]
|
|
||||||
MissingChild(String, String),
|
|
||||||
#[error("Device '{0}' does not implement expected trait '{1}'")]
|
#[error("Device '{0}' does not implement expected trait '{1}'")]
|
||||||
MissingTrait(String, String),
|
MissingTrait(String, String),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
MissingWildcard(#[from] MissingWildcard),
|
|
||||||
#[error(transparent)]
|
|
||||||
MqttClientError(#[from] rumqttc::ClientError),
|
MqttClientError(#[from] rumqttc::ClientError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -2,7 +2,7 @@
|
||||||
use std::{fs, process};
|
use std::{fs, process};
|
||||||
|
|
||||||
use automation::auth::{OpenIDConfig, User};
|
use automation::auth::{OpenIDConfig, User};
|
||||||
use automation::config::Config;
|
use automation::config::{Config, MqttConfig};
|
||||||
use automation::device_manager::DeviceManager;
|
use automation::device_manager::DeviceManager;
|
||||||
use automation::devices::{
|
use automation::devices::{
|
||||||
AirFilter, AudioSetup, ContactSensor, DebugBridge, HueBridge, HueGroup, IkeaOutlet, KasaOutlet,
|
AirFilter, AudioSetup, ContactSensor, DebugBridge, HueBridge, HueGroup, IkeaOutlet, KasaOutlet,
|
||||||
|
@ -17,6 +17,7 @@ use axum::routing::post;
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
use google_home::{GoogleHome, Request};
|
use google_home::{GoogleHome, Request};
|
||||||
|
use mlua::LuaSerdeExt;
|
||||||
use rumqttc::AsyncClient;
|
use rumqttc::AsyncClient;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
|
@ -55,15 +56,9 @@ async fn app() -> anyhow::Result<()> {
|
||||||
std::env::var("AUTOMATION_CONFIG").unwrap_or("./config/config.yml".into());
|
std::env::var("AUTOMATION_CONFIG").unwrap_or("./config/config.yml".into());
|
||||||
let config = Config::parse_file(&config_filename)?;
|
let config = Config::parse_file(&config_filename)?;
|
||||||
|
|
||||||
// Create a mqtt client
|
|
||||||
// TODO: Since we wait with starting the eventloop we might fill the queue while setting up devices
|
|
||||||
let (client, eventloop) = AsyncClient::new(config.mqtt.clone(), 100);
|
|
||||||
|
|
||||||
// Setup the device handler
|
// Setup the device handler
|
||||||
let device_manager = DeviceManager::new();
|
let device_manager = DeviceManager::new();
|
||||||
|
|
||||||
let event_channel = device_manager.event_channel();
|
|
||||||
|
|
||||||
// Lua testing
|
// Lua testing
|
||||||
{
|
{
|
||||||
let lua = mlua::Lua::new();
|
let lua = mlua::Lua::new();
|
||||||
|
@ -74,9 +69,20 @@ async fn app() -> anyhow::Result<()> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let automation = lua.create_table()?;
|
let automation = lua.create_table()?;
|
||||||
|
let event_channel = device_manager.event_channel();
|
||||||
|
let create_mqtt_client = lua.create_function(move |lua, config: mlua::Value| {
|
||||||
|
let config: MqttConfig = lua.from_value(config)?;
|
||||||
|
|
||||||
|
// Create a mqtt client
|
||||||
|
// TODO: When starting up, the devices are not yet created, this could lead to a device being out of sync
|
||||||
|
let (client, eventloop) = AsyncClient::new(config.into(), 100);
|
||||||
|
mqtt::start(eventloop, &event_channel);
|
||||||
|
|
||||||
|
Ok(WrappedAsyncClient(client))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
automation.set("create_mqtt_client", create_mqtt_client)?;
|
||||||
automation.set("device_manager", device_manager.clone())?;
|
automation.set("device_manager", device_manager.clone())?;
|
||||||
automation.set("mqtt_client", WrappedAsyncClient(client.clone()))?;
|
|
||||||
automation.set("event_channel", device_manager.event_channel())?;
|
automation.set("event_channel", device_manager.event_channel())?;
|
||||||
|
|
||||||
let util = lua.create_table()?;
|
let util = lua.create_table()?;
|
||||||
|
@ -115,10 +121,6 @@ async fn app() -> anyhow::Result<()> {
|
||||||
}?;
|
}?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the mqtt eventloop and start listening for message
|
|
||||||
// NOTE: We wait until all the setup is done, as otherwise we might miss some messages
|
|
||||||
mqtt::start(eventloop, &event_channel);
|
|
||||||
|
|
||||||
// Create google home fullfillment route
|
// Create google home fullfillment route
|
||||||
let fullfillment = Router::new().route(
|
let fullfillment = Router::new().route(
|
||||||
"/google_home",
|
"/google_home",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user