import logging import database as db logger = logging.getLogger("mqtt.logger") LEVEL_MAP = { "🟢 INFO": "INFO", "🟡 WARN": "WARN", "🔴 EROR": "ERROR", "INFO": "INFO", "WARN": "WARN", "ERROR": "ERROR", "EROR": "ERROR", } async def handle_message(serial: str, topic_type: str, payload: dict): try: if topic_type == "status/heartbeat": await _handle_heartbeat(serial, payload) elif topic_type == "status/alerts": await _handle_alerts(serial, payload) elif topic_type == "status/info": await _handle_info(serial, payload) elif topic_type == "logs": await _handle_log(serial, payload) elif topic_type == "data": await _handle_data_response(serial, payload) else: logger.debug(f"Unhandled topic type: {topic_type} for {serial}") except Exception as e: logger.error(f"Error handling {topic_type} for {serial}: {e}") async def _handle_heartbeat(serial: str, payload: dict): # Store silently — do not log as a visible event. # The console surfaces an alert only when the device goes silent (no heartbeat for 90s). inner = payload.get("payload", {}) await db.insert_heartbeat( device_serial=serial, device_id=inner.get("device_id", ""), firmware_version=inner.get("firmware_version", ""), ip_address=inner.get("ip_address", ""), gateway=inner.get("gateway", ""), uptime_ms=inner.get("uptime_ms", 0), uptime_display=inner.get("timestamp", ""), ) async def _handle_log(serial: str, payload: dict): raw_level = payload.get("level", "INFO") level = LEVEL_MAP.get(raw_level, "INFO") message = payload.get("message", "") device_timestamp = payload.get("timestamp") await db.insert_log( device_serial=serial, level=level, message=message, device_timestamp=device_timestamp, ) async def _handle_alerts(serial: str, payload: dict): subsystem = payload.get("subsystem", "") state = payload.get("state", "") if not subsystem or not state: logger.warning(f"Malformed alert payload from {serial}: {payload}") return if state == "CLEARED": await db.delete_alert(serial, subsystem) else: await db.upsert_alert(serial, subsystem, state, payload.get("msg")) async def _handle_info(serial: str, payload: dict): event_type = payload.get("type", "") data = payload.get("payload", {}) if event_type == "playback_started": logger.debug(f"{serial}: playback started — melody_uid={data.get('melody_uid')}") elif event_type == "playback_stopped": logger.debug(f"{serial}: playback stopped") else: logger.debug(f"{serial}: info event '{event_type}'") async def _handle_data_response(serial: str, payload: dict): status = payload.get("status", "") pending = await db.get_pending_command(serial) if pending: cmd_status = "success" if status == "SUCCESS" else "error" await db.update_command_response( command_id=pending["id"], status=cmd_status, response_payload=payload, ) else: logger.debug(f"Received data response for {serial} with no pending command")