diff --git a/config.lua b/config.lua index ace9b39..468055d 100644 --- a/config.lua +++ b/config.lua @@ -13,6 +13,15 @@ local function mqtt_automation(topic) return "automation/" .. topic 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({ topic = automation.util.get_env("NTFY_TOPIC"), event_channel = automation.event_channel, @@ -20,14 +29,14 @@ automation.device_manager:add(Ntfy.new({ automation.device_manager:add(Presence.new({ topic = "automation_dev/presence/+/#", - client = automation.mqtt_client, + client = mqtt_client, event_channel = automation.event_channel, })) automation.device_manager:add(DebugBridge.new({ identifier = "debug_bridge", topic = mqtt_automation("debug"), - client = automation.mqtt_client, + client = mqtt_client, })) local hue_ip = "10.0.0.146" @@ -46,7 +55,7 @@ automation.device_manager:add(HueBridge.new({ automation.device_manager:add(LightSensor.new({ identifier = "living_light_sensor", topic = mqtt_z2m("living/light"), - client = automation.mqtt_client, + client = mqtt_client, min = 22000, max = 23500, event_channel = automation.event_channel, @@ -56,7 +65,7 @@ automation.device_manager:add(WakeOnLAN.new({ name = "Zeus", room = "Living Room", topic = mqtt_automation("appliance/living_room/zeus"), - client = automation.mqtt_client, + client = mqtt_client, mac_address = "30:9c:23:60:9c:13", broadcast_ip = "10.0.0.255", })) @@ -69,7 +78,7 @@ automation.device_manager:add(living_speakers) automation.device_manager:add(AudioSetup.new({ identifier = "living_audio", topic = mqtt_z2m("living/remote"), - client = automation.mqtt_client, + client = mqtt_client, mixer = living_mixer, speakers = living_speakers, })) @@ -79,7 +88,7 @@ automation.device_manager:add(IkeaOutlet.new({ name = "Kettle", room = "Kitchen", topic = mqtt_z2m("kitchen/kettle"), - client = automation.mqtt_client, + client = mqtt_client, timeout = debug and 5 or 300, remotes = { { topic = mqtt_z2m("bedroom/remote") }, @@ -92,14 +101,14 @@ automation.device_manager:add(IkeaOutlet.new({ name = "Light", room = "Bathroom", topic = mqtt_z2m("batchroom/light"), - client = automation.mqtt_client, + client = mqtt_client, timeout = debug and 60 or 45 * 60, })) automation.device_manager:add(Washer.new({ identifier = "bathroom_washer", topic = mqtt_z2m("batchroom/washer"), - client = automation.mqtt_client, + client = mqtt_client, threshold = 1, event_channel = automation.event_channel, })) @@ -109,7 +118,7 @@ automation.device_manager:add(IkeaOutlet.new({ name = "Charger", room = "Workbench", topic = mqtt_z2m("workbench/charger"), - client = automation.mqtt_client, + client = mqtt_client, timeout = debug and 5 or 20 * 3600, })) @@ -117,7 +126,7 @@ automation.device_manager:add(IkeaOutlet.new({ name = "Outlet", room = "Workbench", topic = mqtt_z2m("workbench/outlet"), - client = automation.mqtt_client, + client = mqtt_client, })) local hallway_lights = automation.device_manager:add(HueGroup.new({ @@ -130,13 +139,13 @@ local hallway_lights = automation.device_manager:add(HueGroup.new({ remotes = { { topic = mqtt_z2m("hallway/remote") }, }, - client = automation.mqtt_client, + client = mqtt_client, })) automation.device_manager:add(ContactSensor.new({ identifier = "hallway_frontdoor", topic = mqtt_z2m("hallway/frontdoor"), - client = automation.mqtt_client, + client = mqtt_client, presence = { topic = mqtt_automation("presence/contact/frontdoor"), timeout = debug and 10 or 15 * 60, @@ -151,7 +160,7 @@ local bedroom_air_filter = automation.device_manager:add(AirFilter.new({ name = "Air Filter", room = "Bedroom", topic = "pico/filter/bedroom", - client = automation.mqtt_client, + client = mqtt_client, })) -- TODO: Use the wrapped device bedroom_air_filter instead of the string diff --git a/config/config.yml b/config/config.yml index 28ec0f0..2f04777 100644 --- a/config/config.yml +++ b/config/config.yml @@ -1,9 +1,2 @@ openid: base_url: "https://login.huizinga.dev/api/oidc" - -mqtt: - host: "mosquitto" - port: 8883 - client_name: "automation_rs" - username: "mqtt" - password: "${MQTT_PASSWORD}" diff --git a/config/zeus.dev.yml b/config/zeus.dev.yml index 10860f2..2f04777 100644 --- a/config/zeus.dev.yml +++ b/config/zeus.dev.yml @@ -1,10 +1,2 @@ openid: 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 diff --git a/src/config.rs b/src/config.rs index 06b1187..5be2e12 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ use std::time::Duration; use regex::{Captures, Regex}; use rumqttc::{MqttOptions, Transport}; -use serde::{Deserialize, Deserializer}; +use serde::Deserialize; use tracing::debug; use crate::auth::OpenIDConfig; @@ -13,8 +13,6 @@ use crate::error::{ConfigParseError, MissingEnv}; #[derive(Debug, Deserialize)] pub struct Config { pub openid: OpenIDConfig, - #[serde(deserialize_with = "deserialize_mqtt_options")] - pub mqtt: MqttOptions, #[serde(default)] pub fullfillment: FullfillmentConfig, } @@ -44,13 +42,6 @@ impl From for MqttOptions { } } -fn deserialize_mqtt_options<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - Ok(MqttOptions::from(MqttConfig::deserialize(deserializer)?)) -} - #[derive(Debug, Deserialize)] pub struct FullfillmentConfig { #[serde(default = "default_fullfillment_ip")] diff --git a/src/main.rs b/src/main.rs index d43e3d3..f2008de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::{fs, process}; use automation::auth::{OpenIDConfig, User}; -use automation::config::Config; +use automation::config::{Config, MqttConfig}; use automation::device_manager::DeviceManager; use automation::devices::{ AirFilter, AudioSetup, ContactSensor, DebugBridge, HueBridge, HueGroup, IkeaOutlet, KasaOutlet, @@ -17,6 +17,7 @@ use axum::routing::post; use axum::{Json, Router}; use dotenvy::dotenv; use google_home::{GoogleHome, Request}; +use mlua::LuaSerdeExt; use rumqttc::AsyncClient; 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()); 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 let device_manager = DeviceManager::new(); - let event_channel = device_manager.event_channel(); - // Lua testing { let lua = mlua::Lua::new(); @@ -74,9 +69,20 @@ async fn app() -> anyhow::Result<()> { }); 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("mqtt_client", WrappedAsyncClient(client.clone()))?; automation.set("event_channel", device_manager.event_channel())?; 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 let fullfillment = Router::new().route( "/google_home",