feat(mqtt): add device health telemetry and migrate to v2 topic spec
Two efforts that landed together because the v2 topic work extends tables the health-telemetry effort added days earlier in the same files/functions, making them impractical to separate cleanly: Health/diagnostics telemetry (schema, Jul 13-17): - New Postgres tables: device_alert_events, device_boot_events, device_ping_samples, device_diagnostics_reports, plus a `source` column on device_logs to distinguish log origins - Query/service layer in pg_mqtt.py and database/__init__.py for inserting and listing this history, plus a "latest metrics" endpoint combining most-recent diagnostics + ping RTT per device - mqtt/router.py gains list endpoints for alert/boot/ping/diagnostics history, consumed by the upcoming Health tab MQTT v2 topic migration (Sep 21): - Heartbeat payload flattened per vesper_mqtt_topic_spec_v2.md, adding rssi/free_heap/state/ok fields - Command replies move to control/ack, device-initiated events to control/reports; mqtt/client.py subscribes to the new topic set and runs a ping_loop (wired up in main.py) for RTT sampling - mqtt/logger.py and pg_mqtt.py updated to parse and persist the new payload shape alongside the legacy fields for backwards compatibility Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
"""device_alert_events
|
||||
|
||||
Adds an insert-only history log of device alert transitions (WARNING/CRITICAL/
|
||||
FAILED only — CLEARED is not logged here). device_alerts remains the current-
|
||||
state table and is untouched; this is purely additive, used to answer "when
|
||||
was the most recent issue on this device" even after it has been resolved.
|
||||
|
||||
Also adds an optional rssi column to heartbeats, for a signal-strength
|
||||
indicator once firmware starts reporting it on the heartbeat payload.
|
||||
|
||||
Revision ID: d4e5f6a7b8c9
|
||||
Revises: c3d4e5f6a7b8
|
||||
Create Date: 2026-07-13 00:00:00.000000
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision: str = "d4e5f6a7b8c9"
|
||||
down_revision: Union[str, None] = "c3d4e5f6a7b8"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"device_alert_events",
|
||||
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
|
||||
sa.Column("device_serial", sa.String(128), nullable=False),
|
||||
sa.Column("subsystem", sa.String(128), nullable=False),
|
||||
sa.Column("state", sa.String(64), nullable=False),
|
||||
sa.Column("message", sa.Text(), nullable=True),
|
||||
sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=False,
|
||||
server_default=sa.func.now()),
|
||||
)
|
||||
op.create_index(
|
||||
"idx_device_alert_events_serial_occurred",
|
||||
"device_alert_events",
|
||||
["device_serial", sa.text("occurred_at DESC")],
|
||||
)
|
||||
|
||||
op.add_column("heartbeats", sa.Column("rssi", sa.Integer(), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("heartbeats", "rssi")
|
||||
op.drop_index("idx_device_alert_events_serial_occurred", table_name="device_alert_events")
|
||||
op.drop_table("device_alert_events")
|
||||
Reference in New Issue
Block a user