feat: implement mqtt ingestion, parsing, and archive pipeline
This commit is contained in:
78
backend/app/mqtt/ingestor.py
Normal file
78
backend/app/mqtt/ingestor.py
Normal file
@@ -0,0 +1,78 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user