v0.4.2
This commit is contained in:
@@ -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(×tamp)
|
||||
.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> {
|
||||
|
||||
Reference in New Issue
Block a user