초기 커밋

This commit is contained in:
2026-03-01 07:44:19 +09:00
commit 09359f30be
146 changed files with 6120 additions and 0 deletions

View File

View File

@@ -0,0 +1,26 @@
from __future__ import annotations
from fastapi_mqtt import FastMQTT, MQTTConfig
from app.core.config import settings
from app.communication.mqtt.topics import SUBSCRIBE_TOPICS
mqtt_config = MQTTConfig(
host=settings.MQTT_HOST,
port=settings.MQTT_PORT,
username=settings.MQTT_USERNAME or None,
password=settings.MQTT_PASSWORD or None,
keepalive=60,
)
mqtt = FastMQTT(config=mqtt_config)
async def mqtt_startup() -> None:
await mqtt.mqtt_startup()
for topic in SUBSCRIBE_TOPICS:
mqtt.client.subscribe(topic)
async def mqtt_shutdown() -> None:
await mqtt.mqtt_shutdown()

View File

@@ -0,0 +1,86 @@
from __future__ import annotations
import json
import structlog
from app.communication.mqtt.client import mqtt
from app.models.mongodb.device_log import DeviceLog
from app.models.mongodb.telemetry import TelemetryData
logger = structlog.get_logger("mqtt")
def _extract_device_uid(topic: str) -> str:
parts = topic.split("/")
return parts[1] if len(parts) >= 3 else "unknown"
@mqtt.on_message()
async def on_message(client, topic: str, payload: bytes, qos: int, properties) -> None: # type: ignore[no-untyped-def]
device_uid = _extract_device_uid(topic)
try:
data = json.loads(payload.decode())
except (json.JSONDecodeError, UnicodeDecodeError):
logger.warning("invalid_mqtt_payload", topic=topic)
return
if "/telemetry" in topic:
await _handle_telemetry(device_uid, data)
elif "/status" in topic:
await _handle_status(device_uid, data)
elif "/log" in topic:
await _handle_log(device_uid, data)
elif "/response" in topic:
await _handle_response(device_uid, data)
async def _handle_telemetry(device_uid: str, data: dict) -> None:
telemetry = TelemetryData(device_id=device_uid, metrics=data)
await telemetry.insert()
# Broadcast via Socket.IO
from app.communication.socketio.server import sio
await sio.emit(
"telemetry",
{"device_uid": device_uid, "data": data},
namespace="/monitoring",
)
logger.debug("telemetry_saved", device_uid=device_uid)
async def _handle_status(device_uid: str, data: dict) -> None:
log = DeviceLog(device_id=device_uid, event_type="status_change", payload=data)
await log.insert()
from app.communication.socketio.server import sio
await sio.emit(
"device_status",
{"device_uid": device_uid, "status": data},
namespace="/device",
)
logger.debug("status_update", device_uid=device_uid)
async def _handle_log(device_uid: str, data: dict) -> None:
log = DeviceLog(
device_id=device_uid,
event_type=data.get("event_type", "log"),
payload=data,
)
await log.insert()
logger.debug("device_log_saved", device_uid=device_uid)
async def _handle_response(device_uid: str, data: dict) -> None:
from app.communication.socketio.server import sio
await sio.emit(
"device_response",
{"device_uid": device_uid, "data": data},
namespace="/device",
)
logger.debug("device_response", device_uid=device_uid)

View File

@@ -0,0 +1,21 @@
from __future__ import annotations
import json
from app.communication.mqtt.client import mqtt
from app.communication.mqtt.topics import DEVICE_COMMAND, DEVICE_CONFIG, DEVICE_OTA
async def publish_command(device_uid: str, command: dict) -> None:
topic = DEVICE_COMMAND.format(device_uid=device_uid)
mqtt.client.publish(topic, json.dumps(command))
async def publish_config(device_uid: str, config: dict) -> None:
topic = DEVICE_CONFIG.format(device_uid=device_uid)
mqtt.client.publish(topic, json.dumps(config))
async def publish_ota(device_uid: str, ota_info: dict) -> None:
topic = DEVICE_OTA.format(device_uid=device_uid)
mqtt.client.publish(topic, json.dumps(ota_info))

View File

@@ -0,0 +1,25 @@
from __future__ import annotations
# ── Device → Server ──────────────────────────────────
DEVICE_TELEMETRY = "devices/{device_uid}/telemetry"
DEVICE_STATUS = "devices/{device_uid}/status"
DEVICE_LOG = "devices/{device_uid}/log"
DEVICE_RESPONSE = "devices/{device_uid}/response"
# ── Server → Device ──────────────────────────────────
DEVICE_COMMAND = "devices/{device_uid}/command"
DEVICE_CONFIG = "devices/{device_uid}/config"
DEVICE_OTA = "devices/{device_uid}/ota"
# ── Wildcard subscriptions ───────────────────────────
SUB_ALL_TELEMETRY = "devices/+/telemetry"
SUB_ALL_STATUS = "devices/+/status"
SUB_ALL_LOG = "devices/+/log"
SUB_ALL_RESPONSE = "devices/+/response"
SUBSCRIBE_TOPICS = [
SUB_ALL_TELEMETRY,
SUB_ALL_STATUS,
SUB_ALL_LOG,
SUB_ALL_RESPONSE,
]