from __future__ import annotations import logging import paho.mqtt.client as mqtt # type: ignore[import-not-found,import-untyped] from sqlmodel import Session from app.core.config import settings from app.core.db import engine from app.mqtt.repository import insert_raw_message from app.mqtt.topic import extract_device_id logger = logging.getLogger(__name__) def _on_connect( client: mqtt.Client, _userdata: object, _flags: dict[str, int], reason_code: int, _properties: mqtt.Properties | None = None, ) -> None: if reason_code != 0: logger.error("MQTT connect failed: rc=%s", reason_code) return logger.info( "Connected to MQTT broker %s:%s", settings.MQTT_HOST, settings.MQTT_PORT ) client.subscribe(settings.MQTT_TOPIC_PATTERN, qos=1) def _on_message( _client: mqtt.Client, _userdata: object, message: mqtt.MQTTMessage, ) -> None: try: device_id = extract_device_id(message.topic) except ValueError: logger.warning("Skipping unexpected topic: %s", message.topic) return with Session(engine) as session: insert_raw_message( session=session, device_id=device_id, topic=message.topic, payload=bytes(message.payload), qos=message.qos, retain=message.retain, ) def create_client() -> mqtt.Client: client = mqtt.Client( callback_api_version=mqtt.CallbackAPIVersion.VERSION2, client_id=settings.MQTT_CLIENT_ID, clean_session=False, ) if settings.MQTT_USERNAME: client.username_pw_set(settings.MQTT_USERNAME, settings.MQTT_PASSWORD) client.on_connect = _on_connect client.on_message = _on_message client.reconnect_delay_set(min_delay=1, max_delay=30) return client def run() -> None: logging.basicConfig(level=logging.INFO) client = create_client() client.connect(settings.MQTT_HOST, settings.MQTT_PORT, keepalive=60) client.loop_forever() if __name__ == "__main__": run()