import sqlite3 from contextlib import contextmanager from pathlib import Path from datetime import datetime, timezone import config INIT_SQL = """ CREATE TABLE IF NOT EXISTS sessions ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), ended_at TEXT, notes TEXT ); CREATE TABLE IF NOT EXISTS readings ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id INTEGER NOT NULL, received_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), device_id TEXT, mac TEXT, ble_connected INTEGER, battery INTEGER, unit TEXT, probe1_temp REAL, probe1_connected INTEGER, probe2_temp REAL, probe2_connected INTEGER, probe3_temp REAL, probe3_connected INTEGER, probe4_temp REAL, probe4_connected INTEGER, FOREIGN KEY (session_id) REFERENCES sessions(id) ); CREATE TABLE IF NOT EXISTS setpoints ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id INTEGER NOT NULL, probe_number INTEGER NOT NULL, label TEXT, target_temp REAL NOT NULL, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), FOREIGN KEY (session_id) REFERENCES sessions(id), UNIQUE(session_id, probe_number) ); CREATE INDEX IF NOT EXISTS idx_readings_session_time ON readings(session_id, received_at); """ def init_db(): Path(config.DATABASE_PATH).parent.mkdir(parents=True, exist_ok=True) with get_connection() as conn: conn.executescript(INIT_SQL) conn.commit() @contextmanager def get_connection(): conn = sqlite3.connect(config.DATABASE_PATH) conn.row_factory = sqlite3.Row try: yield conn finally: conn.close() def utc_now_iso(): return datetime.now(timezone.utc).isoformat() def create_session(name: str, notes: str = ""): with get_connection() as conn: cursor = conn.execute( "INSERT INTO sessions (name, notes) VALUES (?, ?)", (name, notes), ) conn.commit() return cursor.lastrowid def get_active_session(): with get_connection() as conn: row = conn.execute( "SELECT * FROM sessions WHERE ended_at IS NULL ORDER BY started_at DESC LIMIT 1" ).fetchone() return dict(row) if row else None def end_session(session_id: int): with get_connection() as conn: conn.execute( "UPDATE sessions SET ended_at = ? WHERE id = ?", (utc_now_iso(), session_id), ) conn.commit() def update_session_notes(session_id: int, notes: str): with get_connection() as conn: conn.execute( "UPDATE sessions SET notes = ? WHERE id = ?", (notes, session_id), ) conn.commit() def insert_reading(session_id: int, payload: dict): probes = payload.get("probes", []) temps = [] connected = [] for i in range(4): if i < len(probes) and probes[i].get("connected"): temps.append(probes[i].get("temperature")) connected.append(1) else: temps.append(None) connected.append(0) with get_connection() as conn: conn.execute( """ INSERT INTO readings ( session_id, received_at, device_id, mac, ble_connected, battery, unit, probe1_temp, probe1_connected, probe2_temp, probe2_connected, probe3_temp, probe3_connected, probe4_temp, probe4_connected ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( session_id, utc_now_iso(), payload.get("deviceId"), payload.get("mac"), 1 if payload.get("connected") else 0, payload.get("battery"), payload.get("unit"), temps[0], connected[0], temps[1], connected[1], temps[2], connected[2], temps[3], connected[3], ), ) conn.commit() def get_latest_reading(session_id: int): with get_connection() as conn: row = conn.execute( "SELECT * FROM readings WHERE session_id = ? ORDER BY received_at DESC LIMIT 1", (session_id,), ).fetchone() return dict(row) if row else None def get_readings(session_id: int, minutes: int = 60, probe_number: int = None): with get_connection() as conn: if probe_number is not None: column = f"probe{probe_number}_temp" rows = conn.execute( f""" SELECT received_at, {column} AS temp, probe{probe_number}_connected AS connected FROM readings WHERE session_id = ? AND received_at > datetime('now', '-{minutes} minutes') AND {column} IS NOT NULL ORDER BY received_at ASC """, (session_id,), ).fetchall() else: rows = conn.execute( f""" SELECT received_at, probe1_temp, probe1_connected, probe2_temp, probe2_connected, probe3_temp, probe3_connected, probe4_temp, probe4_connected FROM readings WHERE session_id = ? AND received_at > datetime('now', '-{minutes} minutes') ORDER BY received_at ASC """, (session_id,), ).fetchall() return [dict(row) for row in rows] def set_setpoint(session_id: int, probe_number: int, label: str, target_temp: float): with get_connection() as conn: conn.execute( """ INSERT INTO setpoints (session_id, probe_number, label, target_temp) VALUES (?, ?, ?, ?) ON CONFLICT(session_id, probe_number) DO UPDATE SET label = excluded.label, target_temp = excluded.target_temp, created_at = excluded.created_at """, (session_id, probe_number, label, target_temp), ) conn.commit() def get_setpoints(session_id: int): with get_connection() as conn: rows = conn.execute( "SELECT * FROM setpoints WHERE session_id = ? ORDER BY probe_number", (session_id,), ).fetchall() return [dict(row) for row in rows]