Add offline and ntfy alerts

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-09 20:28:42 -04:00
co-authored by Copilot
parent 8b664667e5
commit 96332adab6
12 changed files with 1098 additions and 21 deletions
+73
View File
@@ -99,6 +99,18 @@ SQL_CREATE_TRIP_BUILD_STATE_TABLE = """CREATE TABLE IF NOT EXISTS trip_build_sta
BUILD_VERSION integer NOT NULL DEFAULT 1
);"""
SQL_CREATE_ALERT_STATE_TABLE = """CREATE TABLE IF NOT EXISTS alert_state (
ALERT_KEY text PRIMARY KEY,
ACTIVE integer NOT NULL DEFAULT 0,
ACTIVE_SINCE real,
LAST_ATTEMPT_AT real,
LAST_SENT_AT real,
RECOVERY_PENDING integer NOT NULL DEFAULT 0,
LAST_RECOVERY_AT real,
LAST_ERROR text,
LAST_VALUE text
);"""
def create_conn(db_file):
"""Open a SQLite connection to db_file, creating its parent folder if needed.
@@ -143,6 +155,7 @@ def get_conn():
create_table(_conn, SQL_CREATE_DASHCAM_HASH_CACHE_TABLE)
create_table(_conn, SQL_CREATE_TRIPS_TABLE)
create_table(_conn, SQL_CREATE_TRIP_BUILD_STATE_TABLE)
create_table(_conn, SQL_CREATE_ALERT_STATE_TABLE)
_migrate_tracking_table(_conn)
_migrate_trips_table(_conn)
_migrate_trip_build_state_table(_conn)
@@ -392,6 +405,66 @@ def set_setting(key, value):
conn.commit()
def get_alert_states():
conn = get_conn()
if conn is None:
return {}
with _conn_lock:
rows = conn.execute(
"""SELECT ALERT_KEY, ACTIVE, ACTIVE_SINCE, LAST_ATTEMPT_AT,
LAST_SENT_AT, RECOVERY_PENDING, LAST_RECOVERY_AT,
LAST_ERROR, LAST_VALUE
FROM alert_state"""
).fetchall()
return {
row[0]: {
"active": bool(row[1]),
"active_since": row[2],
"last_attempt_at": row[3],
"last_sent_at": row[4],
"recovery_pending": bool(row[5]),
"last_recovery_at": row[6],
"last_error": row[7],
"last_value": row[8],
}
for row in rows
}
def save_alert_state(alert_key, state):
conn = get_conn()
if conn is None:
raise RuntimeError("SQLite connection unavailable")
with _conn_lock:
conn.execute(
"""INSERT INTO alert_state (
ALERT_KEY, ACTIVE, ACTIVE_SINCE, LAST_ATTEMPT_AT,
LAST_SENT_AT, RECOVERY_PENDING, LAST_RECOVERY_AT,
LAST_ERROR, LAST_VALUE
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(ALERT_KEY) DO UPDATE SET
ACTIVE = excluded.ACTIVE,
ACTIVE_SINCE = excluded.ACTIVE_SINCE,
LAST_ATTEMPT_AT = excluded.LAST_ATTEMPT_AT,
LAST_SENT_AT = excluded.LAST_SENT_AT,
RECOVERY_PENDING = excluded.RECOVERY_PENDING,
LAST_RECOVERY_AT = excluded.LAST_RECOVERY_AT,
LAST_ERROR = excluded.LAST_ERROR,
LAST_VALUE = excluded.LAST_VALUE""",
(
alert_key,
1 if state["active"] else 0,
state["active_since"],
state["last_attempt_at"],
state["last_sent_at"],
1 if state["recovery_pending"] else 0,
state["last_recovery_at"],
state["last_error"],
state["last_value"],
),
)
conn.commit()
def get_unsent_tracking_rows(limit=100):
"""Return up to `limit` tracking rows not yet uploaded to the remote server,
oldest first, as a list of dicts ready to be sent as JSON."""