first commit

This commit is contained in:
Mateusz Gruszczyński
2026-08-23 21:34:07 +02:00
commit 1d3dcba1a9
62 changed files with 12456 additions and 0 deletions
+317
View File
@@ -0,0 +1,317 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
fn default_true() -> bool { true }
fn default_port() -> u16 { 7000 }
fn default_protocol() -> u8 { 1 }
fn default_mode() -> String { "cool".into() }
fn default_fan() -> u8 { 0 }
fn default_target() -> f64 { 24.0 }
fn default_hysteresis() -> f64 { 0.6 }
fn default_external_sensor_weight() -> f64 { 0.4 }
fn default_max_sensor_difference() -> f64 { 3.0 }
fn default_control_temperature_source() -> String { "device".into() }
fn default_min_cycle() -> u64 { 180 }
fn default_cooldown() -> u64 { 300 }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Device {
pub id: String,
pub mac: String,
pub name: String,
pub ip: String,
#[serde(default = "default_port")]
pub port: u16,
#[serde(default = "default_protocol")]
pub protocol_version: u8,
#[serde(default)]
pub model: String,
#[serde(default)]
pub firmware: String,
#[serde(default)]
pub key: Option<String>,
#[serde(default)]
pub cid: Option<String>,
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub simulated: bool,
#[serde(default)]
pub power: bool,
#[serde(default = "default_mode")]
pub mode: String,
#[serde(default = "default_target")]
pub target_temperature: f64,
#[serde(default = "default_fan")]
pub fan_speed: u8,
#[serde(default)]
pub swing_vertical: bool,
#[serde(default)]
pub swing_horizontal: bool,
#[serde(default)]
pub quiet: bool,
#[serde(default)]
pub turbo: bool,
#[serde(default)]
pub light: bool,
#[serde(default)]
pub current_temperature: Option<f64>,
#[serde(default)]
pub outdoor_temperature: Option<f64>,
#[serde(default)]
pub online: bool,
#[serde(default)]
pub last_seen: Option<DateTime<Utc>>,
#[serde(default)]
pub last_error: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl Device {
pub fn simulated_default() -> Self {
let now = Utc::now();
Self {
id: "sim-salon".into(),
mac: "SIM000000001".into(),
name: "Living Room (simulator)".into(),
ip: "127.0.0.1".into(),
port: 7000,
protocol_version: 1,
model: "GREE-SIM".into(),
firmware: "sim-1.0".into(),
key: None,
cid: Some("gree-controller".into()),
enabled: true,
simulated: true,
power: false,
mode: "cool".into(),
target_temperature: 23.0,
fan_speed: 0,
swing_vertical: false,
swing_horizontal: false,
quiet: false,
turbo: false,
light: true,
current_temperature: Some(26.0),
outdoor_temperature: Some(30.0),
online: true,
last_seen: Some(now),
last_error: None,
created_at: now,
updated_at: now,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DevicePatch {
pub name: Option<String>,
pub ip: Option<String>,
pub port: Option<u16>,
pub protocol_version: Option<u8>,
pub key: Option<Option<String>>,
pub enabled: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DeviceCommand {
pub power: Option<bool>,
pub mode: Option<String>,
pub target_temperature: Option<f64>,
pub fan_speed: Option<u8>,
pub swing_vertical: Option<bool>,
pub swing_horizontal: Option<bool>,
pub quiet: Option<bool>,
pub turbo: Option<bool>,
pub light: Option<bool>,
}
impl DeviceCommand {
pub fn apply(&self, device: &mut Device) {
if let Some(v) = self.power { device.power = v; }
if let Some(v) = &self.mode { device.mode = v.clone(); }
if let Some(v) = self.target_temperature { device.target_temperature = v.clamp(8.0, 32.0); }
if let Some(v) = self.fan_speed { device.fan_speed = v.min(5); }
if let Some(v) = self.swing_vertical { device.swing_vertical = v; }
if let Some(v) = self.swing_horizontal { device.swing_horizontal = v; }
if let Some(v) = self.quiet { device.quiet = v; }
if let Some(v) = self.turbo { device.turbo = v; }
if let Some(v) = self.light { device.light = v; }
device.updated_at = Utc::now();
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Zone {
pub id: String,
pub name: String,
pub device_id: String,
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_mode")]
pub mode: String,
#[serde(default = "default_target")]
pub setpoint: f64,
#[serde(default = "default_hysteresis")]
pub hysteresis: f64,
#[serde(default = "default_min_cycle")]
pub min_on_seconds: u64,
#[serde(default = "default_min_cycle")]
pub min_off_seconds: u64,
#[serde(default = "default_sensor_source")]
pub sensor_source: String,
#[serde(default)]
pub ha_entity_id: Option<String>,
/// Weight of the optional room sensor when sensor_source is `combined`.
#[serde(default = "default_external_sensor_weight")]
pub external_sensor_weight: f64,
/// If GREE and external sensor differ more than this, the controller falls back to GREE.
#[serde(default = "default_max_sensor_difference")]
pub max_sensor_difference: f64,
/// Temperature reported by the GREE indoor sensor during the last zone cycle.
#[serde(default)]
pub device_temperature: Option<f64>,
/// Temperature reported by the per-zone external Home Assistant sensor.
#[serde(default)]
pub external_temperature: Option<f64>,
/// Temperature actually used by the zone controller.
#[serde(default)]
pub current_temperature: Option<f64>,
/// `device`, `external`, `combined`, `device_fallback`, or `device_discrepancy_fallback`.
#[serde(default = "default_control_temperature_source")]
pub control_temperature_source: String,
#[serde(default)]
pub demand: bool,
#[serde(default)]
pub last_action_at: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
fn default_sensor_source() -> String { "device".into() }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Schedule {
pub id: String,
pub zone_id: String,
pub name: String,
#[serde(default = "default_true")]
pub enabled: bool,
/// ISO weekday numbers, Monday=1, Sunday=7.
pub weekdays: Vec<u32>,
/// Local time HH:MM.
pub start_time: String,
/// Local time HH:MM. Ranges crossing midnight are supported.
pub end_time: String,
pub setpoint: f64,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Automation {
pub id: String,
pub name: String,
#[serde(default = "default_true")]
pub enabled: bool,
/// temperature_above, temperature_below, time
pub trigger_kind: String,
#[serde(default)]
pub trigger_device_id: Option<String>,
#[serde(default)]
pub threshold: Option<f64>,
#[serde(default)]
pub at_time: Option<String>,
pub action_device_id: String,
#[serde(default)]
pub action: DeviceCommand,
#[serde(default = "default_cooldown")]
pub cooldown_seconds: u64,
#[serde(default)]
pub last_fired_at: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Reading {
pub id: i64,
pub device_id: String,
pub timestamp: DateTime<Utc>,
pub indoor_temperature: Option<f64>,
pub outdoor_temperature: Option<f64>,
pub target_temperature: f64,
pub power: bool,
pub source: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventLog {
pub id: i64,
pub timestamp: DateTime<Utc>,
pub level: String,
pub kind: String,
pub message: String,
pub metadata: Value,
}
#[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,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuntimeSettings {
pub controller_id: String,
pub simulator_enabled: bool,
pub poll_interval_seconds: u64,
pub zone_interval_seconds: u64,
pub discovery_timeout_ms: u64,
pub discovery_broadcast: String,
pub home_assistant: HomeAssistantSettings,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiscoveryRequest {
#[serde(default)]
pub timeout_ms: Option<u64>,
#[serde(default)]
pub broadcast: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualDeviceRequest {
pub name: String,
pub mac: String,
pub ip: String,
#[serde(default = "default_port")]
pub port: u16,
#[serde(default = "default_protocol")]
pub protocol_version: u8,
#[serde(default)]
pub key: Option<String>,
#[serde(default)]
pub simulated: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiTokenInfo {
pub id: String,
pub name: String,
pub token_prefix: String,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiEvent {
pub event: String,
pub timestamp: DateTime<Utc>,
pub data: Value,
}