M8-R02: report DSMR MQTT source health
This commit is contained in:
@@ -248,6 +248,7 @@ class MqttManager:
|
||||
tls_enabled: bool,
|
||||
subscriptions: dict[str, Callable[[bytes], None]],
|
||||
base_client_id: str = "home-automation",
|
||||
state_handler: Callable[[str], None] | None = None,
|
||||
) -> bool:
|
||||
"""Replace one source-owned client and its handlers.
|
||||
|
||||
@@ -259,6 +260,7 @@ class MqttManager:
|
||||
with self._source_lifecycle_lock:
|
||||
self._stop_source_client(source_id)
|
||||
if not host:
|
||||
self._report_source_state(state_handler, "error", source_id)
|
||||
return False
|
||||
self._next_source_generation += 1
|
||||
generation = self._next_source_generation
|
||||
@@ -279,14 +281,18 @@ class MqttManager:
|
||||
if not self._is_current_source_client(source_id, generation, connected_client):
|
||||
return
|
||||
if reason_code.is_failure:
|
||||
self._source_connected.discard(source_id)
|
||||
logger.warning("DSMR MQTT connection refused for source_id=%s", source_id)
|
||||
return
|
||||
self._source_connected.add(source_id)
|
||||
for topic in captured_subscriptions:
|
||||
try:
|
||||
connected_client.subscribe(topic)
|
||||
except Exception:
|
||||
logger.exception("DSMR MQTT re-subscribe failed for source_id=%s", source_id)
|
||||
state = "error"
|
||||
else:
|
||||
self._source_connected.add(source_id)
|
||||
state = "online"
|
||||
for topic in captured_subscriptions:
|
||||
try:
|
||||
connected_client.subscribe(topic)
|
||||
except Exception:
|
||||
logger.exception("DSMR MQTT re-subscribe failed for source_id=%s", source_id)
|
||||
self._report_source_state(state_handler, state, source_id)
|
||||
|
||||
def _on_disconnect(
|
||||
disconnected_client: mqtt.Client,
|
||||
@@ -299,6 +305,7 @@ class MqttManager:
|
||||
if not self._is_current_source_client(source_id, generation, disconnected_client):
|
||||
return
|
||||
self._source_connected.discard(source_id)
|
||||
self._report_source_state(state_handler, "error", source_id)
|
||||
|
||||
def _on_message(
|
||||
message_client: mqtt.Client,
|
||||
@@ -334,6 +341,7 @@ class MqttManager:
|
||||
client.tls_set()
|
||||
except Exception:
|
||||
logger.exception("DSMR MQTT TLS setup failed for source_id=%s", source_id)
|
||||
self._report_source_state(state_handler, "error", source_id)
|
||||
return False
|
||||
if username:
|
||||
client.username_pw_set(username=username, password=password or None)
|
||||
@@ -345,11 +353,13 @@ class MqttManager:
|
||||
self._source_subscriptions[source_id] = captured_subscriptions
|
||||
self._source_generations[source_id] = generation
|
||||
self._source_states[source_id] = _SourceClientState(client, generation)
|
||||
self._report_source_state(state_handler, "connecting", source_id)
|
||||
client.loop_start()
|
||||
try:
|
||||
client.connect(host=host, port=port, keepalive=60)
|
||||
except Exception:
|
||||
logger.exception("DSMR MQTT connect failed (source_id=%s, host=%s)", source_id, host)
|
||||
self._report_source_state(state_handler, "error", source_id)
|
||||
self._stop_source_client(source_id)
|
||||
return False
|
||||
return True
|
||||
@@ -541,6 +551,18 @@ class MqttManager:
|
||||
and self._source_clients.get(source_id) is client
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _report_source_state(
|
||||
state_handler: Callable[[str], None] | None, state: str, source_id: int
|
||||
) -> None:
|
||||
"""Invoke an optional health callback without exposing connection credentials."""
|
||||
if state_handler is None:
|
||||
return
|
||||
try:
|
||||
state_handler(state)
|
||||
except Exception:
|
||||
logger.exception("DSMR MQTT source state update failed for source_id=%s", source_id)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Module-level singleton — shared across lifespan and route handlers
|
||||
|
||||
Reference in New Issue
Block a user