import logging
|
import time
|
from typing import Any
|
|
from app.config import settings
|
from app.mqtt.init_dispatcher import InitTaskDispatcher
|
from app.mqtt.client import MqttService
|
from app.services.init_registry import InitRegistry
|
|
logger = logging.getLogger(__name__)
|
|
|
class AutoInitService:
|
def __init__(
|
self,
|
registry: InitRegistry,
|
dispatcher: InitTaskDispatcher,
|
mqtt: MqttService,
|
) -> None:
|
self.registry = registry
|
self.dispatcher = dispatcher
|
self.mqtt = mqtt
|
|
def _run_full_init(self, device_id: str) -> list[dict[str, Any]]:
|
commands = self.dispatcher.dispatch_device_init(device_id)
|
logger.info("Device init dispatched for %s (%d commands)", device_id, len(commands))
|
|
course_commands = self.dispatcher.dispatch_base_courses_sync(device_id)
|
commands.extend(course_commands)
|
logger.info(
|
"Base courses sync dispatched for %s (%d courseTask messages)",
|
device_id,
|
len(course_commands),
|
)
|
|
self.registry.update_record(
|
device_id,
|
courses_synced=True,
|
courses_sync_at=int(time.time()),
|
courses_sync_count=len(course_commands),
|
)
|
return commands
|
|
def on_device_state(self, device_id: str, payload: dict[str, Any], timestamp: int) -> list[dict[str, Any]] | None:
|
if not settings.auto_init_enabled:
|
return None
|
|
if self.registry.is_initialized(device_id):
|
return None
|
|
if not self.mqtt.connected:
|
logger.warning("Skip auto-init for %s: MQTT not connected", device_id)
|
return None
|
|
mode = payload.get("mode")
|
if not self.registry.try_claim(device_id, source="auto", mode=mode, timestamp=timestamp):
|
return None
|
|
try:
|
commands = self._run_full_init(device_id)
|
logger.info("Auto-init completed for device %s (total %d commands)", device_id, len(commands))
|
return commands
|
except Exception:
|
self.registry.remove(device_id)
|
logger.exception("Auto-init failed for device %s, registry entry rolled back", device_id)
|
raise
|
|
def dispatch_manual_init(self, device_id: str, force: bool = False) -> tuple[list[dict[str, Any]], str]:
|
if self.registry.is_initialized(device_id) and not force:
|
return [], "already_initialized"
|
|
if force:
|
self.registry.remove(device_id)
|
|
if not self.registry.try_claim(device_id, source="manual"):
|
return [], "already_initialized"
|
|
try:
|
commands = self._run_full_init(device_id)
|
return commands, "dispatched"
|
except Exception:
|
self.registry.remove(device_id)
|
raise
|