mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-11-03 11:52:22 +01:00
Follow-up work on the neighbours feature, plus one crash found while reviewing it. The crash: `mqtt_brokers.neighbors` is a settings block, but the per-broker key of the same name is a boolean and config.yaml.example documents both, so `neighbors: true` under mqtt_brokers is an easy hand-edit to make. Every reader called .get() on it directly, raising AttributeError inside NeighborScopeHelper.refresh_config -- which is built during daemon init, so the daemon would not start at all. A shared neighbors_config_block() accessor now ignores a non-mapping (with one startup warning instead of per-tick noise) and the API save rewrites it as a proper block. Payload counters, mirroring firmware buildNeighborsMessage: - total_neighbors and queried_neighbors. The latter cannot be derived from `status`: a neighbour the sweep never reached reports `timeout` exactly like one that was asked and stayed silent. ScopeResult therefore carries a `transmitted` flag, set only past the point where the injector confirms the request is on air. - Firmware's third field, `truncated`, is deliberately not emitted -- it reports a fixed PSRAM JSON buffer overflowing, which openhop does not have. total_neighbors here consequently always equals the published row count. - self.default_scope: the region this node stamps on outgoing floods, or `*` when unset. Read from live config so `region default <name>` applies without a restart, and `#`-stripped like the scopes string beside it. Firmware tracks this internally but does not publish it, so this field is openhop-only. POST /api/publish_neighbors runs a cycle immediately, the HTTP twin of the `discover.scopes` mesh CLI command. Authenticated by default along with the rest of /api, and it schedules the cycle on the event loop rather than holding the request open for the minutes a cycle takes. Schedule persistence: _next_publish_at is monotonic and _last_publish_at was in-memory only, so every restart read as "due" and spent a discovery broadcast plus one serialized scope query per neighbour. A generic daemon_state table (migration 14) now records the last publish and start() resumes from it, never sooner than a five-minute grace window and never later than one interval. Two details this had to get right: - The schedule keys off the last *successful* publish. A failed publish also sets _last_publish_at and reschedules on the short retry delay; persisting that would silently turn the retry into a full interval. - _tick's disabled branch no longer clears the schedule before the feature has been enabled in this process. At boot the MQTT connections may not be up, so enabled() can briefly be false, and clearing there would discard the restored schedule and re-run the sweep anyway -- the exact thing this prevents.