automation_rs/src/config.rs

125 lines
3.4 KiB
Rust

use std::{fs, error::Error, collections::HashMap};
use tracing::{debug, trace};
use rumqttc::AsyncClient;
use serde::Deserialize;
use crate::devices::{DeviceBox, IkeaOutlet, WakeOnLAN, AudioSetup};
// @TODO Configure more defaults
#[derive(Debug, Deserialize)]
pub struct Config {
pub mqtt: MqttConfig,
pub fullfillment: FullfillmentConfig,
#[serde(default)]
pub ntfy: NtfyConfig,
pub presence: MqttDeviceConfig,
#[serde(default)]
pub devices: HashMap<String, Device>
}
#[derive(Debug, Deserialize)]
pub struct MqttConfig {
pub host: String,
pub port: u16,
pub username: String,
pub password: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct FullfillmentConfig {
#[serde(default = "default_fullfillment_port")]
pub port: u16,
pub username: String,
}
fn default_fullfillment_port() -> u16 {
7878
}
#[derive(Debug, Deserialize)]
pub struct NtfyConfig {
#[serde(default = "default_ntfy_url")]
pub url: String,
pub topic: Option<String>,
}
fn default_ntfy_url() -> String {
"https://ntfy.sh".into()
}
impl Default for NtfyConfig {
fn default() -> Self {
Self { url: default_ntfy_url(), topic: None }
}
}
#[derive(Debug, Deserialize)]
pub struct InfoConfig {
pub name: String,
pub room: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct MqttDeviceConfig {
pub topic: String,
}
#[derive(Debug, Deserialize)]
pub struct KettleConfig {
pub timeout: Option<u64>, // Timeout in seconds
}
#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
pub enum Device {
IkeaOutlet {
info: InfoConfig,
mqtt: MqttDeviceConfig,
kettle: Option<KettleConfig>,
},
WakeOnLAN {
info: InfoConfig,
mqtt: MqttDeviceConfig,
mac_address: String,
},
AudioSetup {
mqtt: MqttDeviceConfig,
mixer: [u8; 4],
speakers: [u8; 4],
}
}
impl Config {
pub fn build(filename: &str) -> Result<Self, Box<dyn Error>> {
debug!("Loading config: {filename}");
let file = fs::read_to_string(filename)?;
let mut config: Self = toml::from_str(&file)?;
config.mqtt.password = Some(std::env::var("MQTT_PASSWORD").or(config.mqtt.password.ok_or("MQTT password needs to be set in either config [mqtt.password] or the environment [MQTT_PASSWORD]"))?);
config.ntfy.topic = Some(std::env::var("NTFY_TOPIC").or(config.ntfy.topic.ok_or("ntfy.sh topic needs to be set in either config [ntfy.url] or the environment [NTFY_TOPIC]!"))?);
Ok(config)
}
}
impl Device {
pub fn into(self, identifier: String, client: AsyncClient) -> DeviceBox {
match self {
Device::IkeaOutlet { info, mqtt, kettle } => {
trace!(id = identifier, "IkeaOutlet [{} in {:?}]", info.name, info.room);
Box::new(IkeaOutlet::new(identifier, info, mqtt, kettle, client))
},
Device::WakeOnLAN { info, mqtt, mac_address } => {
trace!(id = identifier, "WakeOnLan [{} in {:?}]", info.name, info.room);
Box::new(WakeOnLAN::new(identifier, info, mqtt, mac_address, client))
},
Device::AudioSetup { mqtt, mixer, speakers } => {
trace!(id = identifier, "AudioSetup [{}]", identifier);
Box::new(AudioSetup::new(identifier, mqtt, mixer, speakers, client))
},
}
}
}