@@ -11,7 +11,7 @@ local SonosConnection = require "api.sonos_connection"
1111--- Information on an entire Sonos system ("household"), such as its current groups, list of players, etc.
1212--- @field public id HouseholdId
1313--- @field public groups table<GroupId,SonosGroupObject> All of the current groups in the system
14- --- @field public players table<PlayerId ,{ player : SonosPlayerObject , device : SonosDeviceInfoObject } > All of the current players in the system
14+ --- @field public players table<PlayerId ,{ player : SonosPlayerObject } > All of the current players in the system
1515--- @field public bonded_players table<PlayerId,boolean> PlayerID ' s in this map that map to true are non-primary bonded players, and not controllable.
1616--- @field public player_to_group table<PlayerId,GroupId> quick lookup from Player ID -> Group ID
1717--- @field public st_devices table<PlayerId,string> Player ID -> ST Device Record UUID information for the household
7777local _STATE = {
7878 --- @type Households
7979 households = make_households_table (),
80- --- @type table<string , { sonos_device : SonosDeviceInfoObject , player : SonosPlayerObject , group : SonosGroupObject , household : SonosHousehold } >
80+ --- @type table<string , { sonos_device_id : PlayerId , player : SonosPlayerObject , group : SonosGroupObject , household : SonosHousehold } >
8181 device_record_map = {},
8282}
8383
@@ -91,6 +91,7 @@ SonosState.__index = SonosState
9191function SonosState :associate_device_record (device , info )
9292 local household_id = info .ssdp_info .household_id
9393 local group_id = info .ssdp_info .group_id
94+ -- This is the device id even if the device is a secondary in a bonded set
9495 local player_id = info .discovery_info .playerId
9596
9697 local household = _STATE .households [household_id ]
@@ -120,9 +121,8 @@ function SonosState:associate_device_record(device, info)
120121 end
121122
122123 local player = (household .players [player_id ] or {}).player
123- local sonos_device = (household .players [player_id ] or {}).device
124124
125- if not ( player and sonos_device ) then
125+ if not player then
126126 log .error (
127127 string.format (
128128 " No record of Sonos player for device %s" ,
@@ -132,12 +132,12 @@ function SonosState:associate_device_record(device, info)
132132 return
133133 end
134134
135- household .st_devices [sonos_device . id ] = device .id
135+ household .st_devices [player_id ] = device .id
136136
137137 _STATE .device_record_map [device .id ] =
138- { sonos_device = sonos_device , group = group , player = player , household = household }
138+ { sonos_device_id = player_id , group = group , player = player , household = household }
139139
140- local bonded = household .bonded_players [sonos_device . id or {} ] and true or false
140+ local bonded = household .bonded_players [player_id ] and true or false
141141
142142 local sw_gen_changed = utils .update_field_if_changed (
143143 device ,
@@ -179,7 +179,7 @@ function SonosState:associate_device_record(device, info)
179179 local player_id_changed = utils .update_field_if_changed (
180180 device ,
181181 PlayerFields .PLAYER_ID ,
182- sonos_device . id ,
182+ player_id ,
183183 { persist = true }
184184 )
185185
@@ -306,6 +306,40 @@ function SonosState:update_device_record_from_state(household_id, device)
306306 self :update_device_record_group_info (household , current_mapping .group , device )
307307end
308308
309+ -- Helper function for when updating household info
310+ local function update_device_info (driver , player , household , known_bonded_players , sonos_device_id )
311+ household .players [sonos_device_id ] = { player = player }
312+ local previously_bonded = known_bonded_players [sonos_device_id ] and true or false
313+ local currently_bonded
314+ local group_id
315+
316+ -- The primary bonded player will have the same id as the top level player id
317+ if sonos_device_id == player .id then
318+ currently_bonded = false
319+ else
320+ currently_bonded = true
321+ end
322+ group_id = household .player_to_group [player .id ]
323+ household .player_to_group [sonos_device_id ] = group_id
324+ household .bonded_players [sonos_device_id ] = currently_bonded
325+
326+ local maybe_device_id = household .st_devices [sonos_device_id ]
327+ if maybe_device_id then
328+ _STATE .device_record_map [maybe_device_id ] = _STATE .device_record_map [maybe_device_id ] or {}
329+ _STATE .device_record_map [maybe_device_id ].household = household
330+ _STATE .device_record_map [maybe_device_id ].group = household .groups [group_id ]
331+ _STATE .device_record_map [maybe_device_id ].player = player
332+ _STATE .device_record_map [maybe_device_id ].sonos_device_id = sonos_device_id
333+ if previously_bonded ~= currently_bonded then
334+ local target_device = driver :get_device_info (maybe_device_id )
335+ if target_device then
336+ target_device :set_field (PlayerFields .BONDED , currently_bonded , { persist = false })
337+ driver :update_bonded_device_tracking (target_device )
338+ end
339+ end
340+ end
341+ end
342+
309343--- @param id HouseholdId
310344--- @param groups_event SonosGroupsResponseBody
311345--- @param driver SonosDriver
@@ -323,41 +357,29 @@ function SonosState:update_household_info(id, groups_event, driver)
323357 end
324358 end
325359
360+ -- Iterate through the players and track all the devices associated with them
361+ -- for bonded set tracking.
362+ local log_devices_error = false
326363 for _ , player in ipairs (players ) do
327- for _ , device in ipairs (player .devices ) do
328- household .players [device .id ] = { player = player , device = device }
329- local previously_bonded = known_bonded_players [device .id ] and true or false
330- local currently_bonded
331- local group_id
332- -- non-primary bonded players are excluded from a group's list of PlayerID's so we use the group membership
333- -- of the primary device
334- if type (device .primaryDeviceId ) == " string" and device .primaryDeviceId ~= " " then
335- currently_bonded = true
336- group_id = household .player_to_group [device .primaryDeviceId ]
337- else
338- currently_bonded = false
339- group_id = household .player_to_group [device .id ]
364+ -- Prefer devices because deviceIds is deprecated but all we care about is
365+ -- the ID so either way is fine.
366+ if player .devices then
367+ for _ , device in ipairs (player .devices ) do
368+ update_device_info (driver , player , household , known_bonded_players , device .id )
340369 end
341- household .player_to_group [device .id ] = group_id
342- household .bonded_players [device .id ] = currently_bonded
343-
344- local maybe_device_id = household .st_devices [device .id ]
345- if maybe_device_id then
346- _STATE .device_record_map [maybe_device_id ] = _STATE .device_record_map [maybe_device_id ] or {}
347- _STATE .device_record_map [maybe_device_id ].household = household
348- _STATE .device_record_map [maybe_device_id ].group = household .groups [group_id ]
349- _STATE .device_record_map [maybe_device_id ].player = player
350- _STATE .device_record_map [maybe_device_id ].sonos_device = device
351- if previously_bonded ~= currently_bonded then
352- local target_device = driver :get_device_info (maybe_device_id )
353- if target_device then
354- target_device :set_field (PlayerFields .BONDED , currently_bonded , { persist = false })
355- driver :update_bonded_device_tracking (target_device )
356- end
357- end
370+ elseif player .deviceIds then
371+ for _ , device_id in ipairs (player .deviceIds ) do
372+ update_device_info (driver , player , household , known_bonded_players , device_id )
358373 end
374+ else
375+ log_devices_error = true
376+ -- We can still track the primary player in this case
377+ update_device_info (driver , player , household , known_bonded_players , player .id )
359378 end
360379 end
380+ if log_devices_error then
381+ log .warn_with ( { hub_logs = true }, " Group event contained neither devices nor deviceIds in player" )
382+ end
361383
362384 household .id = id
363385 _STATE .households [id ] = household
@@ -489,7 +511,7 @@ function SonosState:get_sonos_ids_for_device(device)
489511
490512 -- player id *should* be stable
491513 if not player_id then
492- player_id = sonos_objects .sonos_device . id
514+ player_id = sonos_objects .sonos_device_id
493515 device :set_field (PlayerFields .PLAYER_ID , player_id , { persist = true })
494516 end
495517
0 commit comments