diff --git a/.editorconfig b/.editorconfig index b39b7e5..23f9d9f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,3 +2,7 @@ root = true [*] indent_style = tab + +[*.yml] +indent_style = space +indent_size = 4 diff --git a/Cargo.lock b/Cargo.lock index 321af23..0a5d579 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,9 +95,9 @@ dependencies = [ "serde_json", "serde_repr", "serde_with", + "serde_yaml", "thiserror", "tokio", - "toml", "tracing", "tracing-subscriber", "wakey", @@ -1349,18 +1349,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.183" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.183" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", @@ -1440,6 +1440,19 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "serde_yaml" +version = "0.9.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +dependencies = [ + "indexmap 2.0.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -1691,15 +1704,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "tonic" version = "0.9.2" @@ -1850,6 +1854,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + [[package]] name = "untrusted" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index 2f17989..8189dd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ impl_cast = { path = "./impl_cast", features = ["debug"] } google-home = { path = "./google-home" } paste = "1.0.10" tokio = { version = "1", features = ["rt-multi-thread"] } -toml = "0.5.10" dotenvy = "0.15.0" reqwest = { version = "0.11.13", features = [ "json", @@ -40,6 +39,7 @@ tracing-subscriber = "0.3.16" serde_with = "3.2.0" enum_dispatch = "0.3.12" indexmap = { version = "2.0.0", features = ["serde"] } +serde_yaml = "0.9.27" [patch.crates-io] wakey = { git = "https://git.huizinga.dev/Dreaded_X/wakey" } diff --git a/config/ares.dev.toml b/config/ares.dev.toml deleted file mode 100644 index d518059..0000000 --- a/config/ares.dev.toml +++ /dev/null @@ -1,61 +0,0 @@ -[openid] -base_url = "https://login.huizinga.dev/api/oidc" - -[mqtt] -host = "olympus.vpn.huizinga.dev" -port = 8883 -client_name = "automation-ares" -username = "mqtt" -password = "${MQTT_PASSWORD}" -tls = true - -[ntfy] -topic = "${NTFY_TOPIC}" - -[presence] -topic = "automation_dev/presence/+/#" - -# Devices -[device.debug_bridge] -type = "DebugBridge" -topic = "automation_dev/debug" - -[device.light_sensor] -type = "LightSensor" -topic = "zigbee2mqtt_dev/living/light" -min = 23_000 -max = 25_000 - -[device.kitchen_kettle] -type = "IkeaOutlet" -outlet_type = "Kettle" -name = "Kettle" -room = "Kitchen" -topic = "zigbee2mqtt/kitchen/kettle" -timeout = 5 - -[device.workbench_charger] -type = "IkeaOutlet" -outlet_type = "Charger" -name = "Charger" -room = "Workbench" -topic = "zigbee2mqtt/workbench/charger" -timeout = 5 - -[device.workbench_outlet] -type = "IkeaOutlet" -name = "Outlet" -room = "Workbench" -topic = "zigbee2mqtt/workbench/outlet" - -[device.living_zeus] -type = "WakeOnLAN" -name = "Zeus" -room = "Living Room" -topic = "automation/appliance/living_room/zeus" -mac_address = "30:9c:23:60:9c:13" - -[device.hallway_frontdoor] -type = "ContactSensor" -topic = "zigbee2mqtt/hallway/frontdoor" -presence = { topic = "automation_dev/presence/contact/frontdoor", timeout = 10 } diff --git a/config/ares.dev.yml b/config/ares.dev.yml new file mode 100644 index 0000000..45d321d --- /dev/null +++ b/config/ares.dev.yml @@ -0,0 +1,66 @@ +openid: + base_url: "https://login.huizinga.dev/api/oidc" + +mqtt: + host: "olympus.vpn.huizinga.dev" + port: 8883 + client_name: "automation-ares" + username: "mqtt" + password: "${MQTT_PASSWORD}" + tls: true + +ntfy: + topic: "${NTFY_TOPIC}" + +presence: + topic: "automation_dev/presence/+/#" + +devices: + debug_bridge: + !DebugBridge + topic: "automation_dev/debug" + + living_light_sensor: + !LightSensor + topic: "zigbee2mqtt_dev/living/light" + min: 23000 + max: 25000 + + kitchen_kettle: + !IkeaOutlet + outlet_type: "Kettle" + name: "Kettle" + room: "Kitchen" + topic: "zigbee2mqtt/kitchen/kettle" + timeout: 5 + remotes: + - topic: "zigbee2mqtt/bedroom/remote" + - topic: "zigbee2mqtt/kitchen/remote" + + workbench_charger: + !IkeaOutlet + outlet_type: "Charger" + name: "Charger" + room: "Workbench" + topic: "zigbee2mqtt/workbench/charger" + timeout: 5 + + workbench_outlet: + !IkeaOutlet + name: "Outlet" + room: "Workbench" + topic: "zigbee2mqtt/workbench/outlet" + + living_zeus: + !WakeOnLAN + name: "Zeus" + room: "Living Room" + topic: "automation/appliance/living_room/zeus" + mac_address: "30:9c:23:60:9c:13" + + hallway_frontdoor: + !ContactSensor + topic: "zigbee2mqtt/hallway/frontdoor" + presence: + topic: "automation_dev/presence/contact/frontdoor" + timeout: 10 diff --git a/config/config.example.toml b/config/config.example.toml deleted file mode 100644 index 40f5ec7..0000000 --- a/config/config.example.toml +++ /dev/null @@ -1,5 +0,0 @@ -[mqtt] -host="example.com" -port=1234 -username="username" -password="password" diff --git a/config/config.toml b/config/config.toml deleted file mode 100644 index 316831b..0000000 --- a/config/config.toml +++ /dev/null @@ -1,119 +0,0 @@ -[openid] -base_url = "https://login.huizinga.dev/api/oidc" - -[mqtt] -host = "mosquitto" -port = 8883 -client_name = "automation_rs" -username = "mqtt" -password = "${MQTT_PASSWORD}" - -[ntfy] -topic = "${NTFY_TOPIC}" - -[presence] -topic = "automation/presence/+/#" - -# Devices -[device.debug_bridge] -type = "DebugBridge" -topic = "automation/debug" - -[device.hue_bridge] -type = "HueBridge" -ip = "10.0.0.146" -login = "${HUE_TOKEN}" -flags = { presence = 41, darkness = 43 } - - -[device.living_light_sensor] -type = "LightSensor" -topic = "zigbee2mqtt/living/light" -min = 22_000 -max = 23_500 - -[device.living_zeus] -type = "WakeOnLAN" -name = "Zeus" -room = "Living Room" -topic = "automation/appliance/living_room/zeus" -mac_address = "30:9c:23:60:9c:13" -broadcast_ip = "10.0.0.255" - -[device.living_mixer] -type = "KasaOutlet" -ip = "10.0.0.49" - -[device.living_speakers] -type = "KasaOutlet" -ip = "10.0.0.182" - -[device.living_audio] -type = "AudioSetup" -topic = "zigbee2mqtt/living/remote" -mixer = "living_mixer" -speakers = "living_speakers" - - -[device.kitchen_kettle] -type = "IkeaOutlet" -outlet_type = "Kettle" -name = "Kettle" -room = "Kitchen" -topic = "zigbee2mqtt/kitchen/kettle" -timeout = 300 -remotes = [ - { topic = "zigbee2mqtt/bedroom/remote" }, - { topic = "zigbee2mqtt/kitchen/remote" }, -] - - -[device.bathroom_light] -type = "IkeaOutlet" -outlet_type = "Light" -name = "Light" -room = "Bathroom" -topic = "zigbee2mqtt/bathroom/light" -timeout = 2700 - -[device.bathroom_washer] -type = "Washer" -topic = "zigbee2mqtt/bathroom/washer" -threshold = 1 - - -[device.workbench_charger] -type = "IkeaOutlet" -outlet_type = "Charger" -name = "Charger" -room = "Workbench" -topic = "zigbee2mqtt/workbench/charger" -timeout = 72000 - -[device.workbench_outlet] -type = "IkeaOutlet" -name = "Outlet" -room = "Workbench" -topic = "zigbee2mqtt/workbench/outlet" - - -[device.hallway_lights] -type = "HueGroup" -ip = "10.0.0.146" -login = "${HUE_TOKEN}" -group_id = 81 -scene_id = "3qWKxGVadXFFG4o" -timer_id = 1 -remotes = [{ topic = "zigbee2mqtt/hallway/remote" }] - -[device.hallway_frontdoor] -type = "ContactSensor" -topic = "zigbee2mqtt/hallway/frontdoor" -presence = { topic = "automation/presence/contact/frontdoor", timeout = 900 } -trigger = { devices = ["hallway_lights"], timeout = 60 } - -[device.bedroom_air_filter] -type = "AirFilter" -name = "Air Filter" -room = "Bedroom" -topic = "pico/filter/test" diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 0000000..1696bbb --- /dev/null +++ b/config/config.yml @@ -0,0 +1,124 @@ +openid: + base_url: "https://login.huizinga.dev/api/oidc" + +mqtt: + host: "mosquitto" + port: 8883 + client_name: "automation_rs" + username: "mqtt" + password: "${MQTT_PASSWORD}" + +ntfy: + topic: "${NTFY_TOPIC}" + +presence: + topic: "automation/presence/+/#" + +devices: + debug_bridge: + !DebugBridge + topic: "automation/debug" + + hue_bridge: + !HueBridge + ip: &hue_ip "10.0.0.146" + login: &hue_token "${HUE_TOKEN}" + flags: { presence: 41, darkness: 43 } + + + living_light_sensor: + !LightSensor + topic: "zigbee2mqtt/living/light" + min: 22000 + max: 23500 + + living_zeus: + !WakeOnLAN + name: "Zeus" + room: "Living Room" + topic: "automation/appliance/living_room/zeus" + mac_address: "30:9c:23:60:9c:13" + broadcast_ip: "10.0.0.255" + + &mixer living_mixer: + !KasaOutlet + ip: "10.0.0.49" + + &speakers living_speakers: + !KasaOutlet + ip: "10.0.0.182" + + living_audio: + !AudioSetup + topic: "zigbee2mqtt/living/remote" + mixer: *mixer + speakers: *speakers + + + kitchen_kettle: + !IkeaOutlet + outlet_type: "Kettle" + name: "Kettle" + room: "Kitchen" + topic: "zigbee2mqtt/kitchen/kettle" + timeout: 300 + remotes: + - topic: "zigbee2mqtt/bedroom/remote" + - topic: "zigbee2mqtt/kitchen/remote" + + + bathroom_light: + !IkeaOutlet + type: "IkeaOutlet" + outlet_type: "Light" + name: "Light" + room: "Bathroom" + topic: "zigbee2mqtt/bathroom/light" + timeout: 2700 + + bathroom_washer: + !Washer + topic: "zigbee2mqtt/bathroom/washer" + threshold: 1 + + workbench_charger: + !IkeaOutlet + outlet_type: "Charger" + name: "Charger" + room: "Workbench" + topic: "zigbee2mqtt/workbench/charger" + timeout: 72000 + + workbench_outlet: + !IkeaOutlet + name: "Outlet" + room: "Workbench" + topic: "zigbee2mqtt/workbench/outlet" + + + hallway_lights: + !HueGroup + ip: *hue_ip + login: *hue_token + group_id: 81 + scene_id: "3qWKxGVadXFFG4o" + timer_id: 1 + remotes: + - topic: "zigbee2mqtt/hallway/remote" + + hallway_frontdoor: + !ContactSensor + topic: "zigbee2mqtt/hallway/frontdoor" + presence: + topic: "automation_dev/presence/contact/frontdoor" + timeout: 900 + trigger: + devices: ["hallway_lights"] + timeout: 60 + + + bedroom_air_filter: + !AirFilter + name: "Air Filter" + room: "Bedroom" + topic: "pio/filter/test" diff --git a/config/zeus.dev.toml b/config/zeus.dev.toml deleted file mode 100644 index 69d0b15..0000000 --- a/config/zeus.dev.toml +++ /dev/null @@ -1,120 +0,0 @@ -[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 - -[ntfy] -topic = "${NTFY_TOPIC}" - -[presence] -topic = "automation_dev/presence/+/#" - -# Devices -[device.debug_bridge] -type = "DebugBridge" -topic = "automation_dev/debug" - -[device.hue_bridge] -type = "HueBridge" -ip = "10.0.0.146" -login = "${HUE_TOKEN}" -flags = { presence = 41, darkness = 43 } - - -[device.living_light_sensor] -type = "LightSensor" -topic = "zigbee2mqtt_dev/living/light" -min = 23_000 -max = 25_000 -# TODO: Implement this: -trigger = ["hue_bridge", "debug_bridge"] - -[device.living_zeus] -type = "WakeOnLAN" -name = "Zeus" -room = "Living Room" -topic = "automation/appliance/living_room/zeus" -mac_address = "30:9c:23:60:9c:13" - -[device.living_mixer] -type = "KasaOutlet" -ip = "10.0.0.49" - -[device.living_speakers] -type = "KasaOutlet" -ip = "10.0.0.182" - -[device.living_audio] -type = "AudioSetup" -topic = "zigbee2mqtt/living/remote" -mixer = "living_mixer" -speakers = "living_speakers" - - -[device.kitchen_kettle] -type = "IkeaOutlet" -outlet_type = "Kettle" -name = "Kettle" -room = "Kitchen" -topic = "zigbee2mqtt/kitchen/kettle" -timeout = 5 -remotes = [ - { topic = "zigbee2mqtt/bedroom/remote" }, - { topic = "zigbee2mqtt/kitchen/remote" }, -] - -[device.bathroom_light] -type = "IkeaOutlet" -outlet_type = "Light" -name = "Bathroom light" -room = "Bathroom" -topic = "zigbee2mqtt/bathroom/light" -timeout = 60 - -[device.bathroom_washer] -type = "Washer" -topic = "zigbee2mqtt/bathroom/washer" -threshold = 1 - - -[device.workbench_charger] -type = "IkeaOutlet" -outlet_type = "Charger" -name = "Charger" -room = "Workbench" -topic = "zigbee2mqtt/workbench/charger" -timeout = 5 - -[device.workbench_outlet] -type = "IkeaOutlet" -name = "Outlet" -room = "Workbench" -topic = "zigbee2mqtt/workbench/outlet" - - -[device.hallway_lights] -type = "HueGroup" -ip = "10.0.0.146" -login = "${HUE_TOKEN}" -group_id = 81 -scene_id = "3qWKxGVadXFFG4o" -timer_id = 1 -remotes = [{ topic = "zigbee2mqtt/hallway/remote" }] - -[device.hallway_frontdoor] -type = "ContactSensor" -topic = "zigbee2mqtt/hallway/frontdoor" -presence = { topic = "automation_dev/presence/contact/frontdoor", timeout = 10 } -trigger = { devices = ["hallway_lights"], timeout = 10 } - -[device.bedroom_air_filter] -type = "AirFilter" -name = "Air Filter" -room = "Bedroom" -topic = "pico/filter/test" diff --git a/config/zeus.dev.yml b/config/zeus.dev.yml new file mode 100644 index 0000000..ffb6f92 --- /dev/null +++ b/config/zeus.dev.yml @@ -0,0 +1,124 @@ +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 + +ntfy: + topic: "${NTFY_TOPIC}" + +presence: + topic: "automation_dev/presence/+/#" + +devices: + debug_bridge: + !DebugBridge + topic: "automation_dev/debug" + + hue_bridge: + !HueBridge + ip: &hue_ip "10.0.0.146" + login: &hue_token "${HUE_TOKEN}" + flags: { presence: 41, darkness: 43 } + + + living_light_sensor: + !LightSensor + topic: "zigbee2mqtt_dev/living/light" + min: 23000 + max: 25000 + + living_zeus: + !WakeOnLAN + name: "Zeus" + room: "Living Room" + topic: "automation/appliance/living_room/zeus" + mac_address: "30:9c:23:60:9c:13" + + &mixer living_mixer: + !KasaOutlet + ip: "10.0.0.49" + + &speakers living_speakers: + !KasaOutlet + ip: "10.0.0.182" + + living_audio: + !AudioSetup + topic: "zigbee2mqtt/living/remote" + mixer: *mixer + speakers: *speakers + + + kitchen_kettle: + !IkeaOutlet + outlet_type: "Kettle" + name: "Kettle" + room: "Kitchen" + topic: "zigbee2mqtt/kitchen/kettle" + timeout: 5 + remotes: + - topic: "zigbee2mqtt/bedroom/remote" + - topic: "zigbee2mqtt/kitchen/remote" + + + bathroom_light: + !IkeaOutlet + type: "IkeaOutlet" + outlet_type: "Light" + name: "Light" + room: "Bathroom" + topic: "zigbee2mqtt/bathroom/light" + timeout: 60 + + bathroom_washer: + !Washer + topic: "zigbee2mqtt/bathroom/washer" + threshold: 1 + + workbench_charger: + !IkeaOutlet + outlet_type: "Charger" + name: "Charger" + room: "Workbench" + topic: "zigbee2mqtt/workbench/charger" + timeout: 5 + + workbench_outlet: + !IkeaOutlet + name: "Outlet" + room: "Workbench" + topic: "zigbee2mqtt/workbench/outlet" + + + hallway_lights: + !HueGroup + ip: *hue_ip + login: *hue_token + group_id: 81 + scene_id: "3qWKxGVadXFFG4o" + timer_id: 1 + remotes: + - topic: "zigbee2mqtt/hallway/remote" + + hallway_frontdoor: + !ContactSensor + topic: "zigbee2mqtt/hallway/frontdoor" + presence: + topic: "automation_dev/presence/contact/frontdoor" + timeout: 10 + trigger: + devices: ["hallway_lights"] + timeout: 10 + + + bedroom_air_filter: + !AirFilter + name: "Air Filter" + room: "Bedroom" + topic: "pio/filter/test" diff --git a/src/config.rs b/src/config.rs index 84a78ce..dd09abe 100644 --- a/src/config.rs +++ b/src/config.rs @@ -26,7 +26,6 @@ pub struct Config { pub fullfillment: FullfillmentConfig, pub ntfy: Option, pub presence: PresenceConfig, - #[serde(rename = "device")] pub devices: IndexMap, } @@ -137,7 +136,7 @@ impl Config { missing.has_missing()?; - let config: Config = toml::from_str(&file)?; + let config: Config = serde_yaml::from_str(&file)?; Ok(config) } diff --git a/src/device_manager.rs b/src/device_manager.rs index 3126c32..ba2d412 100644 --- a/src/device_manager.rs +++ b/src/device_manager.rs @@ -39,7 +39,6 @@ pub trait DeviceConfig { } #[derive(Debug, Deserialize)] -#[serde(tag = "type")] #[enum_dispatch(DeviceConfig)] pub enum DeviceConfigs { AirFilter(AirFilterConfig), diff --git a/src/error.rs b/src/error.rs index 6528be2..c0a2bad 100644 --- a/src/error.rs +++ b/src/error.rs @@ -71,7 +71,7 @@ pub enum ConfigParseError { #[error(transparent)] IoError(#[from] std::io::Error), #[error(transparent)] - DeserializeError(#[from] toml::de::Error), + YamlError(#[from] serde_yaml::Error), } // TODO: Would be nice to somehow get the line number of the expected wildcard topic diff --git a/src/main.rs b/src/main.rs index 6ea7114..f4c254a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,7 +50,7 @@ async fn app() -> anyhow::Result<()> { info!("Starting automation_rs..."); let config_filename = - std::env::var("AUTOMATION_CONFIG").unwrap_or("./config/config.toml".into()); + std::env::var("AUTOMATION_CONFIG").unwrap_or("./config/config.yml".into()); let config = Config::parse_file(&config_filename)?; // Create a mqtt client