Skip to content

fix: reconnect MQTT subscription with backoff after a dropped connection - #49

Open
jasperchan wants to merge 1 commit into
Lash-L:mainfrom
jasperchan:jc/mqtt-reconnect
Open

jasperchan wants to merge 1 commit into
Lash-L:mainfrom
jasperchan:jc/mqtt-reconnect

Conversation

@jasperchan

Copy link
Copy Markdown

Fixes #47 (the "subscription never retries" half; the logger half overlaps with #48, happy to rebase on it if that lands first).

Problem

subscribe_mqtt runs a single aiomqtt.Client session. Any MqttError is logged and the task simply ends, and nothing re-establishes the subscription until schedule_reauthorization fires (~2 h 55 min later) or the user reloads the config entry. In the meantime Home Assistant silently stops receiving activity_state pushes (entities look available but freeze) and every command raises after the 30 s wait in send_command.

Seen today on HA 2026.10.4 / python-snoo 0.8.3: one MQTT connection for <serial> failed: Disconnected during message iteration at 13:31 and then total silence until a manual reload at 13:44. The code path is unchanged in 0.11.0.

Change

  • subscribe_mqtt now loops. Each attempt runs one session (_run_mqtt_session); a connect failure or a dropped connection is retried with exponential backoff (1 s → 300 s), and the backoff resets once a connection has stayed up for 60 s.
  • Each attempt reads the current tokens.aws_id, so a token refreshed by schedule_reauthorization is used on the next connect. Cancellation (from disconnect / the refresh path) still ends the task as before.
  • _client_map bookkeeping is unchanged in spirit: the entry is removed whenever the session ends and re-added on reconnect, so send_command waits for a live client instead of publishing into a dead one.
  • A failing callback or an unparsable payload is logged instead of tearing the connection down.
  • Lifecycle logging goes through the module logger (python_snoo.snoo) instead of the root logger, so logger: python_snoo: debug in HA actually shows it.

Tests

Added tests/test_mqtt_reconnect.py with a scripted fake aiomqtt.Client covering: connect failure then success, mid-stream disconnect, _client_map state across a drop, cancellation stopping the loop, and a refreshed token being picked up by the next attempt. pytest and pre-commit run --all-files pass locally.

https://claude.ai/code/session_01Kjt2zjjJbbcYXsbXpzBUQk

subscribe_mqtt ran a single aiomqtt session: any MqttError (timeout on
connect, "Disconnected during message iteration") was logged and the task
ended. Nothing re-established the subscription until the scheduled Cognito
token refresh (~2.9 h later) or a manual config-entry reload, so Home
Assistant silently stopped receiving activity_state pushes and every command
failed after the 30 s wait in send_command. Fixes Lash-L#47.

- subscribe_mqtt now loops: each attempt runs one session via
  _run_mqtt_session and a failure is retried with exponential backoff
  (1 s .. 300 s), reset once a connection has stayed up for 60 s.
- Each attempt reads the current id token, so a refreshed token is used
  on the next connect.
- Cancellation (disconnect / schedule_reauthorization) still ends the task.
- A failing callback or unparsable payload is logged instead of dropping
  the connection.
- Log through the module logger instead of the root logger, so
  `python_snoo` debug logging shows the connection lifecycle.
- Add pytest coverage for connect failure, mid-stream disconnect,
  _client_map bookkeeping, cancellation and token pick-up.

Claude-Session: https://claude.ai/code/session_01Kjt2zjjJbbcYXsbXpzBUQk

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MQTT connect times out on every attempt (no CONNACK) — entities stay unavailable, subscription never retries

1 participant