Skip to content

Fix startup/reload hangs, restore lost actions, add HA-parity feature… - #17

Merged
DaveL17 merged 4 commits into
IndigoDomotics:mainfrom
Ghawken:fix/grouped-state-resync
Aug 8, 2026
Merged

Fix startup/reload hangs, restore lost actions, add HA-parity feature…#17
DaveL17 merged 4 commits into
IndigoDomotics:mainfrom
Ghawken:fix/grouped-state-resync

Conversation

@Ghawken

@Ghawken Ghawken commented Aug 7, 2026

Copy link
Copy Markdown

…s (2025.2.2)

Addresses the failure cascade reported in #16 and a series of related defects found reviewing the 2025.2.1 release.

== Blocking network I/O on the dispatch thread (config UI freeze, #16) == The plugin's main thread — which also services every Indigo config dialog — was spending minutes in serial, sometimes unbounded, connect timeouts to offline players (verified by process sampling: 100% of samples blocked in socket connect()). The config screen could not open at all.

  • New is_host_reachable() 2s TCP probe; deviceStartComm now defers unreachable players immediately (device shows "offline" error state) instead of stalling ~40s each, and a background retry loop (runConcurrentThread, previously commented out entirely) re-starts them automatically when the network returns.
  • Removed the redundant 2x discover(timeout=5) sweeps per device; reuse startup discovery or direct SoCo(ip) — identical semantics.
  • Bounded every unbounded HTTP call on hot paths: SOAPSend requests.post (was infinite connect timeout), initZones /status/zp, getModelName, rootZPIP status fetch.
  • Startup skips playlists/favorites load when the reference player is unreachable.
  • deviceStopComm: network unsubscribe only if the player answers a 1s probe; otherwise cancel local auto-renew. Offline players previously blocked plugin stop until Indigo force-killed it on reload.

== ZGT storm during announcements (#16) ==
Every zone change during an announcement walked all players with 5s fetches, melting down flaky networks (UPNP 1001, 20s read timeouts, "Host is down" on healthy speakers).

  • 3s debounce on refresh_group_topology_after_plugin_zone_change (only once the cache has first populated, so fast startup isn't starved).
  • 1s reachability probe + 30s negative cache per player in the ZGT walk.
  • Fixed "list.remove(x): x not in list" crash in the initZones error path.
  • "coord_dev missing" for players with no Indigo device now warns once per IP, then logs at debug.

== State-list sync ("state key Grouped not defined") == Devices created before Grouped/GROUP_* were added to Devices.xml kept a stale server state list; three duplicated code blocks tried to "create" states via updateStateOnServer (impossible) and errored on every start.

  • initialize_custom_states now calls stateListOrDisplayStateIdChanged() to resync from Devices.xml and returns a refreshed device; all callers updated; duplicated broken blocks removed.
  • Fixed startup summary reading nonexistent "GROUP_Grouped" state.
  • Guarded soco.discover() returning None in startup.

== Routed-subnet support (players on a different VLAN) ==

  • find_sonos_interface_ip: when no local interface sits on the configured Sonos subnet, resolve the routed source IP via the OS routing table — announcements and eventing now work across routed VLANs instead of erroring "No interface found on target Sonos subnet".
  • getSoCoDeviceByIP / get_soco_device: direct SoCo(ip) instead of multicast discovery sweeps that can never cross a router ("No SoCo devices discovered" spam eliminated).

== Restored lost/broken actions (all fell through to "Unknown or
unsupported action" or crashed) ==

  • ZP_RT_FavStation (RadioTime favourite stations — also used by alarms).
  • Bass, Treble (had no handler anywhere), Night Mode, Play Queue, Sleep Timer, TV input, Dump URI.
  • Pandora Thumbs Up/Down (actionPandoraThumbs method was missing).
  • Test SiriusXM Channel (malformed Actions.xml entry + missing callback).
  • Fixed dispatch-table signature mismatches: handleAction_ZP_SiriusXM (missing props — crashed on use) and handleAction_Q_Crossfade (arity).
  • Fixed py2 remnant urllib.unquote -> urllib.parse.unquote (crashed favourites playback).
  • Favourites router: x-sonosapi-hls: is generic HLS (Sonos Radio HD, Apple Music radio, ...) — only route to SiriusXM when the URI carries channel-linear:; other HLS/http favourites play via the generic URI+DIDL path. Removed stray early return when mode was empty.
  • SiriusXM handler accepts the GUID from favourite URI fragments.

== New HA-parity features ==

  • Equalizer actions: Speech Enhancement, Audio Delay (lip-sync 0-5), Surround on/off, Surround Level (TV/Music, -15..15), Music Playback Full Volume — RenderingControl SetEQ, mirroring Home Assistant/SoCo.
  • Native Sonos alarm management: new action lists household alarms live and enables/disables/toggles them (optional volume override).

== Cleanup ==

  • Removed both dead duplicate old_deviceStartComm methods (349 lines).
  • Removed invalid block from Actions.xml.
  • Downgraded routine warnings (ready-probe, cache misses) to debug.
  • Misc log-prefix corrections.

Fixes #16

Glenn Hawken and others added 2 commits August 7, 2026 16:42
…s (2025.2.2)

Addresses the failure cascade reported in IndigoDomotics#16 and a
series of related defects found reviewing the 2025.2.1 release.

== Blocking network I/O on the dispatch thread (config UI freeze, IndigoDomotics#16) ==
The plugin's main thread — which also services every Indigo config dialog —
was spending minutes in serial, sometimes unbounded, connect timeouts to
offline players (verified by process sampling: 100% of samples blocked in
socket connect()). The config screen could not open at all.

- New is_host_reachable() 2s TCP probe; deviceStartComm now defers
  unreachable players immediately (device shows "offline" error state)
  instead of stalling ~40s each, and a background retry loop
  (runConcurrentThread, previously commented out entirely) re-starts them
  automatically when the network returns.
- Removed the redundant 2x discover(timeout=5) sweeps per device; reuse
  startup discovery or direct SoCo(ip) — identical semantics.
- Bounded every unbounded HTTP call on hot paths: SOAPSend requests.post
  (was infinite connect timeout), initZones /status/zp, getModelName,
  rootZPIP status fetch.
- Startup skips playlists/favorites load when the reference player is
  unreachable.
- deviceStopComm: network unsubscribe only if the player answers a 1s
  probe; otherwise cancel local auto-renew. Offline players previously
  blocked plugin stop until Indigo force-killed it on reload.

== ZGT storm during announcements (IndigoDomotics#16) ==
Every zone change during an announcement walked all players with 5s
fetches, melting down flaky networks (UPNP 1001, 20s read timeouts,
"Host is down" on healthy speakers).
- 3s debounce on refresh_group_topology_after_plugin_zone_change (only
  once the cache has first populated, so fast startup isn't starved).
- 1s reachability probe + 30s negative cache per player in the ZGT walk.
- Fixed "list.remove(x): x not in list" crash in the initZones error path.
- "coord_dev missing" for players with no Indigo device now warns once
  per IP, then logs at debug.

== State-list sync ("state key Grouped not defined") ==
Devices created before Grouped/GROUP_* were added to Devices.xml kept a
stale server state list; three duplicated code blocks tried to "create"
states via updateStateOnServer (impossible) and errored on every start.
- initialize_custom_states now calls stateListOrDisplayStateIdChanged()
  to resync from Devices.xml and returns a refreshed device; all callers
  updated; duplicated broken blocks removed.
- Fixed startup summary reading nonexistent "GROUP_Grouped" state.
- Guarded soco.discover() returning None in startup.

== Routed-subnet support (players on a different VLAN) ==
- find_sonos_interface_ip: when no local interface sits on the configured
  Sonos subnet, resolve the routed source IP via the OS routing table —
  announcements and eventing now work across routed VLANs instead of
  erroring "No interface found on target Sonos subnet".
- getSoCoDeviceByIP / get_soco_device: direct SoCo(ip) instead of
  multicast discovery sweeps that can never cross a router ("No SoCo
  devices discovered" spam eliminated).

== Restored lost/broken actions (all fell through to "Unknown or
   unsupported action" or crashed) ==
- ZP_RT_FavStation (RadioTime favourite stations — also used by alarms).
- Bass, Treble (had no handler anywhere), Night Mode, Play Queue,
  Sleep Timer, TV input, Dump URI.
- Pandora Thumbs Up/Down (actionPandoraThumbs method was missing).
- Test SiriusXM Channel (malformed Actions.xml entry + missing callback).
- Fixed dispatch-table signature mismatches: handleAction_ZP_SiriusXM
  (missing props — crashed on use) and handleAction_Q_Crossfade (arity).
- Fixed py2 remnant urllib.unquote -> urllib.parse.unquote (crashed
  favourites playback).
- Favourites router: x-sonosapi-hls: is generic HLS (Sonos Radio HD,
  Apple Music radio, ...) — only route to SiriusXM when the URI carries
  channel-linear:<guid>; other HLS/http favourites play via the generic
  URI+DIDL path. Removed stray early return when mode was empty.
- SiriusXM handler accepts the GUID from favourite URI fragments.

== New HA-parity features ==
- Equalizer actions: Speech Enhancement, Audio Delay (lip-sync 0-5),
  Surround on/off, Surround Level (TV/Music, -15..15), Music Playback
  Full Volume — RenderingControl SetEQ, mirroring Home Assistant/SoCo.
- Native Sonos alarm management: new action lists household alarms live
  and enables/disables/toggles them (optional volume override).

== Cleanup ==
- Removed both dead duplicate old_deviceStartComm methods (349 lines).
- Removed invalid <ActionType> block from Actions.xml.
- Downgraded routine warnings (ready-probe, cache misses) to debug.
- Misc log-prefix corrections.

Fixes IndigoDomotics#16

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Production follow-up to the IndigoDomotics#16 work: with a single
player off the network, Indigo logged "timeout waiting for plugin
response ... CalcDeviceFunc" and the log filled with repeated 10s
DeviceProperties1.xml timeouts. Root cause: SoCo's .uid/.group/
.is_coordinator on a cold instance poll ZoneGroupState against the
player's OWN IP — for an offline player that is a full network timeout
on every access, and several hot loops (UID lookups, group evaluation,
topology dumps) access them per player on the dispatch thread.

- New _ip_probe_ok(): 1s reachability probe with the shared 30s
  negative cache; new safe_uid(): resolves a player UID from plugin
  cache -> SoCo's cache -> the Indigo device's ZP_LocalUID state ->
  (probe-gated) network. Offline players resolve instantly from state.
- get_soco_by_uuid(): use safe_uid — was burning a 10s timeout per
  offline player on EVERY uuid lookup (the main log spam).
- uuid map builder: safe_uid instead of raw soco.uid.
- dump_groups_to_log PASS1/PASS2: skip unreachable players before
  touching soco.group.
- Group-state reconcile loops (x3): probe-gate before is_coordinator/
  .group access.
- Cap soco.config.REQUEST_TIMEOUT at 5s (SoCo default is 20s).
- Bump PluginVersion to 2025.2.3.

Refs IndigoDomotics#16

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Ghawken

Ghawken commented Aug 7, 2026

Copy link
Copy Markdown
Author

Pushed 9ed3dfb (v2025.2.3) to this branch: production testing surfaced that a single offline player could still stall the plugin (Indigo logged timeout waiting for plugin response ... CalcDeviceFunc). SoCo's .uid/.group/.is_coordinator on a cold instance poll ZoneGroupState against the player's own IP — a full network timeout per access in several per-player hot loops. Now resolved via a cached/probe-gated safe_uid() (falls back to the Indigo device's stored ZP_LocalUID), reachability gates on the group-evaluation and topology-dump loops, and capping soco.config.REQUEST_TIMEOUT at 5s (default 20s).

Glenn Hawken and others added 2 commits August 7, 2026 17:05
SonosPlugin.__init__ assigns a local variable named 'soco' further down
(uuid map builder), which makes 'soco' local to the whole method — so the
new REQUEST_TIMEOUT line at the top raised "cannot access local variable
'soco' where it is not associated with a value" and the plugin could not
start. Alias the module (import soco.config as _soco_config) instead.

AST-scanned the rest of the file for the same module-shadowing
use-before-assignment pattern: no other instances.

Refs IndigoDomotics#16

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… new features

Replaces the changelog-style stub with a full README covering install,
configuration (incl. routed-VLAN setups), devices/states, the complete
actions catalog, the offline-resilience behaviour, restored actions,
the new Equalizer/soundbar and native Sonos alarm features, and
troubleshooting + version history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

Fixes Line-In Action update not working

2 participants