This commit is contained in:
Mateusz Gruszczyński
2026-08-23 23:32:26 +02:00
parent b23578a0c8
commit f569b7db14
42 changed files with 597 additions and 4104 deletions
+90 -2
View File
@@ -5,7 +5,7 @@ use rusqlite::{params, Connection, OptionalExtension};
use serde::{de::DeserializeOwned, Serialize};
use serde_json::Value;
use crate::{
models::{ApiTokenInfo, Automation, Device, EventLog, Reading, RuntimeSettings, Schedule, Zone},
models::{ApiTokenInfo, Automation, Device, EventLog, Reading, RuntimeSettings, Schedule, Zone, ZoneReading},
queries,
};
@@ -75,6 +75,7 @@ impl Db {
let mut conn = self.lock()?;
let tx = conn.transaction()?;
tx.execute(queries::DELETE_DEVICE_READINGS, [id])?;
tx.execute(queries::DELETE_ZONE_READINGS_BY_DEVICE_ID, [id])?;
tx.execute(queries::DELETE_ZONES_BY_DEVICE_ID, [id])?;
let changed = tx.execute(queries::DELETE_DEVICE, [id])? > 0;
tx.commit()?;
@@ -103,6 +104,7 @@ impl Db {
let mut conn = self.lock()?;
let tx = conn.transaction()?;
tx.execute(queries::DELETE_SCHEDULES_BY_ZONE_ID, [id])?;
tx.execute(queries::DELETE_ZONE_READINGS_BY_ZONE_ID, [id])?;
let changed = tx.execute(queries::DELETE_ZONE, [id])? > 0;
tx.commit()?;
Ok(changed)
@@ -236,7 +238,93 @@ impl Db {
pub fn prune_readings(&self, retention_days: i64) -> Result<u64> {
let before = Utc::now() - Duration::days(retention_days.max(1));
let conn = self.lock()?;
Ok(conn.execute(queries::PRUNE_READINGS, [before.to_rfc3339()])? as u64)
let device = conn.execute(queries::PRUNE_READINGS, [before.to_rfc3339()])? as u64;
let zone = conn.execute(queries::PRUNE_ZONE_READINGS, [before.to_rfc3339()])? as u64;
Ok(device + zone)
}
pub fn add_zone_reading_if_due(&self, reading: &ZoneReading, min_interval_seconds: i64) -> Result<bool> {
let cutoff = reading.timestamp.clone() - Duration::seconds(min_interval_seconds.max(1));
let conn = self.lock()?;
let changed = conn.execute(
queries::INSERT_ZONE_READING_IF_DUE,
params![
reading.zone_id,
reading.device_id,
reading.timestamp.to_rfc3339(),
reading.gree_temperature,
reading.external_temperature,
reading.control_temperature,
reading.target_temperature,
reading.device_setpoint,
reading.outdoor_temperature,
reading.power as i64,
reading.mode,
reading.fan_speed as i64,
reading.demand as i64,
reading.control_source,
reading.active_preset,
cutoff.to_rfc3339(),
],
)?;
Ok(changed > 0)
}
pub fn list_zone_readings(&self, zone_id: Option<&str>, since: DateTime<Utc>, limit: u32) -> Result<Vec<ZoneReading>> {
let conn = self.lock()?;
let limit = limit.clamp(1, 20_000) as i64;
let mut rows_out = Vec::new();
if let Some(zone_id) = zone_id {
let mut stmt = conn.prepare(queries::LIST_ZONE_READINGS_BY_ZONE)?;
let rows = stmt.query_map(params![zone_id, since.to_rfc3339(), limit], Self::map_zone_reading)?;
for row in rows { rows_out.push(row?); }
} else {
let mut stmt = conn.prepare(queries::LIST_ZONE_READINGS_ALL)?;
let rows = stmt.query_map(params![since.to_rfc3339(), limit], Self::map_zone_reading)?;
for row in rows { rows_out.push(row?); }
}
Ok(rows_out)
}
pub fn list_zone_history(&self, zone_id: Option<&str>, since: DateTime<Utc>, bucket_seconds: i64, limit: u32) -> Result<Vec<ZoneReading>> {
let conn = self.lock()?;
let bucket_seconds = bucket_seconds.max(1);
let limit = limit.clamp(1, 20_000) as i64;
let mut rows_out = Vec::new();
if let Some(zone_id) = zone_id {
let mut stmt = conn.prepare(queries::LIST_ZONE_HISTORY_BY_ZONE_BUCKETED)?;
let rows = stmt.query_map(params![zone_id, since.to_rfc3339(), bucket_seconds, limit], Self::map_zone_reading)?;
for row in rows { rows_out.push(row?); }
} else {
let mut stmt = conn.prepare(queries::LIST_ZONE_HISTORY_ALL_BUCKETED)?;
let rows = stmt.query_map(params![since.to_rfc3339(), bucket_seconds, limit], Self::map_zone_reading)?;
for row in rows { rows_out.push(row?); }
}
Ok(rows_out)
}
fn map_zone_reading(row: &rusqlite::Row<'_>) -> rusqlite::Result<ZoneReading> {
let timestamp: String = row.get(3)?;
Ok(ZoneReading {
id: row.get(0)?,
zone_id: row.get(1)?,
device_id: row.get(2)?,
timestamp: DateTime::parse_from_rfc3339(&timestamp)
.map(|v| v.with_timezone(&Utc))
.unwrap_or_else(|_| Utc::now()),
gree_temperature: row.get(4)?,
external_temperature: row.get(5)?,
control_temperature: row.get(6)?,
target_temperature: row.get(7)?,
device_setpoint: row.get(8)?,
outdoor_temperature: row.get(9)?,
power: row.get::<_, i64>(10)? != 0,
mode: row.get(11)?,
fan_speed: row.get::<_, i64>(12)?.clamp(0, 255) as u8,
demand: row.get::<_, i64>(13)? != 0,
control_source: row.get(14)?,
active_preset: row.get(15)?,
})
}
pub fn log_event(&self, level: &str, kind: &str, message: &str, metadata: &Value) -> Result<i64> {