#[derive(Debug, Clone, Serialize, Deserialize)] pub struct HomeAssistantSettings { #[serde(default)] pub url: String, #[serde(default)] pub token: String, #[serde(default)] pub default_entity_id: String, /// Optional outdoor temperature sensor used only as an assist signal. #[serde(default)] pub outdoor_entity_id: String, /// Maximum accepted age of Home Assistant sensor samples. #[serde(default = "default_sensor_stale_after")] pub sensor_stale_after_seconds: u64, /// Accept self-signed/expired certificates for local Home Assistant HTTPS. #[serde(default)] pub allow_invalid_tls: bool, /// Friendly labels used only by the controller UI/charts; entity_id remains the storage key. #[serde(default)] pub sensor_aliases: BTreeMap, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InfluxDbSettings { #[serde(default)] pub enabled: bool, /// InfluxDB API generation: `1` or `2`. #[serde(default = "default_influx_version")] pub version: String, #[serde(default)] pub url: String, /// InfluxDB 1.x database name. #[serde(default = "default_influx_database")] pub database: String, #[serde(default)] pub username: String, #[serde(default)] pub password: String, /// InfluxDB 2.x organization. #[serde(default)] pub org: String, /// InfluxDB 2.x bucket. #[serde(default = "default_influx_database")] pub bucket: String, #[serde(default)] pub token: String, /// Queries older than this age are read from InfluxDB when it is enabled. #[serde(default = "default_influx_threshold_days")] pub history_threshold_days: u32, } impl Default for InfluxDbSettings { fn default() -> Self { Self { enabled: false, version: default_influx_version(), url: String::new(), database: default_influx_database(), username: String::new(), password: String::new(), org: String::new(), bucket: default_influx_database(), token: String::new(), history_threshold_days: default_influx_threshold_days(), } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NotificationAlertTypes { /// Home Assistant sensor exceeded the configured freshness window. #[serde(default = "default_true")] pub stale_sensor: bool, /// Other Home Assistant sensor errors (missing/unavailable/invalid value). #[serde(default = "default_true")] pub sensor_errors: bool, /// Device connectivity, polling and communication problems. #[serde(default = "default_true")] pub communication: bool, /// Zone failed to reach its target within the configured timeout. #[serde(default = "default_true")] pub target_timeout: bool, /// Automation execution errors and conflicts. #[serde(default = "default_true")] pub automation: bool, /// Thermostat/group/device control failures and sensor discrepancies. #[serde(default = "default_true")] pub control_errors: bool, /// Informational state changes sent when notification mode is "important". #[serde(default = "default_true")] pub important_events: bool, /// Any warning/error not matched by one of the categories above. #[serde(default = "default_true")] pub other: bool, } impl Default for NotificationAlertTypes { fn default() -> Self { Self { stale_sensor: true, sensor_errors: true, communication: true, target_timeout: true, automation: true, control_errors: true, important_events: true, other: true, } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NotificationSettings { #[serde(default)] pub enabled: bool, /// problems = warnings/errors/anomalies, important = problems plus important state changes. #[serde(default = "default_notification_mode")] pub mode: String, /// pushover, slack, discord #[serde(default = "default_notification_provider")] pub provider: String, #[serde(default)] pub pushover_app_token: String, #[serde(default)] pub pushover_user_key: String, #[serde(default)] pub slack_webhook_url: String, #[serde(default)] pub discord_webhook_url: String, #[serde(default = "default_notification_cooldown")] pub cooldown_seconds: u64, #[serde(default = "default_notification_failure_threshold")] pub communication_failure_threshold: u32, #[serde(default = "default_notification_target_timeout")] pub target_timeout_minutes: u32, /// Fine-grained selection of which alert categories may be delivered. #[serde(default)] pub alert_types: NotificationAlertTypes, } fn default_notification_mode() -> String { "problems".into() } fn default_notification_provider() -> String { "pushover".into() } fn default_notification_cooldown() -> u64 { 300 } fn default_notification_failure_threshold() -> u32 { 3 } fn default_notification_target_timeout() -> u32 { 60 } impl Default for NotificationSettings { fn default() -> Self { Self { enabled: false, mode: default_notification_mode(), provider: default_notification_provider(), pushover_app_token: String::new(), pushover_user_key: String::new(), slack_webhook_url: String::new(), discord_webhook_url: String::new(), cooldown_seconds: default_notification_cooldown(), communication_failure_threshold: default_notification_failure_threshold(), target_timeout_minutes: default_notification_target_timeout(), alert_types: NotificationAlertTypes::default(), } } } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct DebugSettings { #[serde(default)] pub overlay_enabled: bool, #[serde(default)] pub gree_frames: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NightModeSettings { #[serde(default)] pub enabled: bool, #[serde(default = "default_night_start")] pub start_time: String, #[serde(default = "default_night_end")] pub end_time: String, /// Maximum fan speed used by thermostat control during night hours (1=Low..5=High). #[serde(default = "default_night_max_fan_speed")] pub max_fan_speed: u8, /// Ask compatible GREE units to keep Quiet enabled during the whole night window. #[serde(default = "default_true")] pub force_quiet: bool, /// Use the unit's native Sleep function during the night window when it is supported. #[serde(default = "default_true")] pub use_native_sleep: bool, } impl Default for NightModeSettings { fn default() -> Self { Self { enabled: false, start_time: default_night_start(), end_time: default_night_end(), max_fan_speed: default_night_max_fan_speed(), force_quiet: true, use_native_sleep: true, } } }