diff --git a/bambu_run/conf.py b/bambu_run/conf.py index dd4a020..10d62c3 100644 --- a/bambu_run/conf.py +++ b/bambu_run/conf.py @@ -81,5 +81,12 @@ class _Settings: def CLOUD_SYNC_DAYS(self): return get_setting("BAMBU_RUN_CLOUD_SYNC_DAYS", 30) + # Seconds of silence on the MQTT report topic that, once broken by a new + # message, is treated as "the printer was probably offline" and triggers + # a pushall re-sync instead of trusting the partial delta to fill in stale + # fields left over from before the gap. + @property + def MQTT_RESYNC_GAP_SECONDS(self): + return get_setting("BAMBU_RUN_MQTT_RESYNC_GAP_SECONDS", 90) app_settings = _Settings() diff --git a/bambu_run/mqtt_client.py b/bambu_run/mqtt_client.py index e8474d3..f26046f 100644 --- a/bambu_run/mqtt_client.py +++ b/bambu_run/mqtt_client.py @@ -21,6 +21,7 @@ import os import platform import sys import select +import time from contextlib import contextmanager from dataclasses import dataclass, field from datetime import datetime @@ -809,6 +810,7 @@ class BambuPrinter: self._accumulator = PrinterStateAccumulator() self._connected = False self._devices: List[Dict[str, Any]] = [] + self._last_message_at: Optional[float] = None def _get_fresh_token(self, verification_code_timeout: int = 300) -> str: """Get a fresh token using credentials.""" @@ -914,6 +916,30 @@ class BambuPrinter: """Internal MQTT message handler""" if not data: return + + # The printer publishes partial deltas most of the time; the accumulator + # merges them onto whatever it already has. If the printer went offline + # (power cycle) and just reconnected, the first delta after the gap would + # otherwise be merged onto stale pre-outage state, leaving fields like + # nozzle_temp frozen at their last value until some unrelated full report + # happens to refresh them. Detect the gap and force a full pushall so the + # accumulator gets a clean, complete state instead. + now = time.time() + if ( + self._last_message_at is not None + and now - self._last_message_at > app_settings.MQTT_RESYNC_GAP_SECONDS + and self._mqtt is not None + ): + try: + self._mqtt.request_full_status() + logger.info( + "MQTT report gap of %.0fs detected for %s; requested full status re-sync", + now - self._last_message_at, device_id, + ) + except Exception as e: + logger.warning("Full status re-sync request failed (non-fatal): %s", e) + self._last_message_at = now + state = self._accumulator.update(data) if self._on_update: self._on_update(state)