Skip to content

sonos: fix offline grouped speakers - #3245

Merged
NoahCornell merged 1 commit into
mainfrom
bugfix/CHAD-19246
Sep 23, 2026
Merged

NoahCornell merged 1 commit into
mainfrom
bugfix/CHAD-19246

Conversation

@NoahCornell

@NoahCornell NoahCornell commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Check all that apply

Type of Change

  • WWST Certification Request
    • If this is your first time contributing code:
      • I have reviewed the README.md file
      • I have reviewed the CODE_OF_CONDUCT.md file
      • I have signed the CLA
    • I plan on entering a WWST Certification Request or have entered a request through the WWST Certification console at developer.smartthings.com
  • Bug fix
  • New feature
  • Refactor

Checklist

  • I have performed a self-review of my code
  • I have commented my code in hard-to-understand areas
  • I have verified my changes by testing with a device or have communicated a plan for testing
  • I am adding new behavior, such as adding a sub-driver, and have added and run new unit tests to cover the new behavior

Description of Change

Fixes a bug where a Sonos speaker in a group can get permanently stuck showing Offline in the SmartThings app after a network hiccup, even though its websocket connection is actually healthy and the driver continues receiving/emitting playback events for it. Recovery previously required a full driver restart.

Root Cause

SonosConnection uses an internal _initialized flag to track whether a connection has completed a full, successful start() cycle. The on_close handler correctly called device:offline() when a socket dropped, but never reset _initialized back to false.

Because self_running() factored _initialized into its own connectivity check, a connection could end up with _initialized stuck true from before the drop. If the other side of the relationship (the coordinator's socket, in a member's case) happened to reconnect independently before this connection's own reconnect loop got around to checking, is_running() could read true on its very first check — before start() had ever been called even once. Since the reconnect loop is while not is_running() do start() ... end, this meant the loop could exit without ever calling start(), so device:online() was never called and the device stayed marked offline indefinitely, while still passing through capability events via a group-relay mechanism unrelated to its own health state.

Fix

drivers/SmartThings/sonos/src/api/sonos_connection.lua:

  • on_close now resets self._initialized = false before marking the device offline, in addition to the existing device:offline() call.
  • self_running() no longer factors in _initialized — it's now a pure connectivity check (Router.is_connected(unique_key)).
  • is_running() now explicitly requires self._initialized, alongside self_running() and coordinator_running().
  • Fixed a pre-existing logging typo in is_running()'s debug output (coordinator = self_running → coordinator = coord_running), which had been printing the same value under both labels and made this bug harder to diagnose from logs.

Since _initialized can now only ever become true inside a successful start() call, and is guaranteed to start false after any real close, the reconnect loop's "exit without ever calling start()" path is now unreachable — is_running() cannot read true except at the exact point start() itself completes successfully.

Summary of Completed Tests

Reproduced the original bug and validated the fix against a live 2-speaker group on a physical hub, using iptables to selectively drop the coordinator's connection and inspecting driver logs (OnClose, is_running debug output, reconnect task behavior) to confirm the device now correctly reaches device:online() instead of stalling.

Testing Logs

Coordinator marked offline

21:57:22.247 [DEBUG] OnClose for Office 2
21:57:22.249 [DEBUG] Marking Office 2 as offline due to websocket closure
21:57:22.259 [DEBUG] Spawning reconnect task for 	Office 2

Non-coordinator marked offline

21:57:41.646 [DEBUG] OnClose for Office
21:57:41.648 [DEBUG] Marking Office as offline due to websocket closure
21:57:41.657 [DEBUG] Spawning reconnect task for 	Office

Non-coordinator task re-connecting both coordinator and non-coordinator websockets

21:57:54.290 [DEBUG] Opening websocket for player id sonos_imahpaqxiqsrwfpfsiygtx54yu.jnll7jw4lssrele3rwoc/rincon_5caafd5a194401400
21:57:54.299 [TRACE] Opening up websocket connection for host/port 192.168.50.157 1443
21:57:55.085 [DEBUG] Registering SonosConnection for device Office as listener for player sonos_imahpaqxiqsrwfpfsiygtx54yu.jnll7jw4lssrele3rwoc/rincon_5caafd5a194401400 websocket
21:57:55.096 [DEBUG] Open coordinator socket for: Office
21:57:55.110 [DEBUG] Opening websocket for player id sonos_imahpaqxiqsrwfpfsiygtx54yu.jnll7jw4lssrele3rwoc/rincon_b8e937353bbc01400
21:57:55.125 [TRACE] Opening up websocket connection for host/port 192.168.50.248 1443
21:57:56.175 [DEBUG] Registering SonosConnection for device Office as listener for player sonos_imahpaqxiqsrwfpfsiygtx54yu.jnll7jw4lssrele3rwoc/rincon_b8e937353bbc01400 websocket
21:57:56.178 [DEBUG] Refresh subscriptions on Office
21:57:56.183 [WARN] notice: get_oauth_token -> not connected
21:57:56.186 [WARN] notice: get_oauth_token -> not connected
21:57:56.190 [WARN] notice: get_oauth_token -> not connected
21:57:56.194 [WARN] notice: get_oauth_token -> not connected
21:57:56.197 [WARN] notice: get_oauth_token -> not connected
21:57:56.201 [WARN] notice: get_oauth_token -> not connected
21:57:56.205 [WARN] notice: get_oauth_token -> not connected
21:57:56.209 [DEBUG] Get Coordinator For Player={
  household_id="Sonos_IMAHPaqxiqSRwFPfsiYGTX54yU.JnLL7JW4lSSReLe3RwoC", 
  player_id="RINCON_5CAAFD5A194401400", 
}
21:57:56.214 [DEBUG] Get Group For Player={
  household_id="Sonos_IMAHPaqxiqSRwFPfsiYGTX54yU.JnLL7JW4lSSReLe3RwoC", 
  player_id="RINCON_5CAAFD5A194401400", 
}
21:57:56.219 [DEBUG] Get Coordinator For Group={
  group_id="RINCON_B8E937353BBC01400:412838221", 
  household_id="Sonos_IMAHPaqxiqSRwFPfsiYGTX54yU.JnLL7JW4lSSReLe3RwoC", 
}
21:57:56.223 [DEBUG] Marking Office as online since all websockets connected

Coordinator set to online even though the websocket is already connected

21:58:20.346 [DEBUG] Office 2 all connections running? {coordinator=true, mine=true}
21:58:20.348 [DEBUG] Starting SonosConnection for Office 2
21:58:20.350 [TRACE] Creating TCP socket for REST Connection
21:58:20.358 [TRACE] Setting TCP socket timeout for REST Connection
21:58:20.360 [TRACE] Connecting TCP socket for REST Connection
21:58:20.373 [TRACE] Set Keepalive for TCP socket for REST Connection
21:58:20.514 [TRACE] Creating SSL wrapper for for REST Connection
21:58:20.525 [TRACE] Setting SSL socket timeout for REST Connection
21:58:20.527 [TRACE] Performing SSL handshake for for REST Connection
21:58:21.036 [DEBUG] Refresh subscriptions on Office 2
21:58:21.038 [WARN] notice: get_oauth_token -> not connected
21:58:21.040 [WARN] notice: get_oauth_token -> not connected
21:58:21.042 [WARN] notice: get_oauth_token -> not connected
21:58:21.044 [WARN] notice: get_oauth_token -> not connected
21:58:21.045 [WARN] notice: get_oauth_token -> not connected
21:58:21.046 [WARN] notice: get_oauth_token -> not connected
21:58:21.049 [WARN] notice: get_oauth_token -> not connected
21:58:21.050 [DEBUG] Get Coordinator For Player={
  household_id="Sonos_IMAHPaqxiqSRwFPfsiYGTX54yU.JnLL7JW4lSSReLe3RwoC", 
  player_id="RINCON_B8E937353BBC01400", 
}
21:58:21.051 [DEBUG] Get Group For Player={
  household_id="Sonos_IMAHPaqxiqSRwFPfsiYGTX54yU.JnLL7JW4lSSReLe3RwoC", 
  player_id="RINCON_B8E937353BBC01400", 
}
21:58:21.051 [DEBUG] Get Coordinator For Group={
  group_id="RINCON_B8E937353BBC01400:412838221", 
  household_id="Sonos_IMAHPaqxiqSRwFPfsiYGTX54yU.JnLL7JW4lSSReLe3RwoC", 
}
21:58:21.052 [DEBUG] Marking Office 2 as online since all websockets connected

@github-actions

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Channel deleted.

@github-actions

Copy link
Copy Markdown

Test Results

   73 files    541 suites   0s ⏱️
3 270 tests 3 270 ✅ 0 💤 0 ❌
5 259 runs  5 259 ✅ 0 💤 0 ❌

Results for commit 5703703.

@NoahCornell
NoahCornell merged commit 6cbdd73 into main Sep 23, 2026
15 of 21 checks passed
@NoahCornell
NoahCornell deleted the bugfix/CHAD-19246 branch September 23, 2026 15:04
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.

4 participants