mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Fix Sonos group discovery (#12970)
* Avoid iterating sonos devices that are not yet added * Rebuild zone topology for each new device
This commit is contained in:
parent
eaf525d41d
commit
44e4f8d1ba
@ -114,6 +114,7 @@ class SonosData:
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the data."""
|
||||
self.uids = set()
|
||||
self.devices = []
|
||||
self.topology_lock = threading.Lock()
|
||||
|
||||
@ -148,15 +149,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
player = soco.SoCo(discovery_info.get('host'))
|
||||
|
||||
# If device already exists by config
|
||||
if player.uid in [x.unique_id for x in hass.data[DATA_SONOS].devices]:
|
||||
if player.uid in hass.data[DATA_SONOS].uids:
|
||||
return
|
||||
|
||||
if player.is_visible:
|
||||
device = SonosDevice(player)
|
||||
hass.data[DATA_SONOS].devices.append(device)
|
||||
add_devices([device])
|
||||
if len(hass.data[DATA_SONOS].devices) > 1:
|
||||
return
|
||||
hass.data[DATA_SONOS].uids.add(player.uid)
|
||||
add_devices([SonosDevice(player)])
|
||||
else:
|
||||
players = None
|
||||
hosts = config.get(CONF_HOSTS, None)
|
||||
@ -180,19 +178,17 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
_LOGGER.warning("No Sonos speakers found")
|
||||
return
|
||||
|
||||
hass.data[DATA_SONOS].devices = [SonosDevice(p) for p in players]
|
||||
add_devices(hass.data[DATA_SONOS].devices)
|
||||
hass.data[DATA_SONOS].uids.update([p.uid for p in players])
|
||||
add_devices([SonosDevice(p) for p in players])
|
||||
_LOGGER.debug("Added %s Sonos speakers", len(players))
|
||||
|
||||
def service_handle(service):
|
||||
"""Handle for services."""
|
||||
entity_ids = service.data.get('entity_id')
|
||||
|
||||
devices = hass.data[DATA_SONOS].devices
|
||||
if entity_ids:
|
||||
devices = [device for device in hass.data[DATA_SONOS].devices
|
||||
if device.entity_id in entity_ids]
|
||||
else:
|
||||
devices = hass.data[DATA_SONOS].devices
|
||||
devices = [d for d in devices if d.entity_id in entity_ids]
|
||||
|
||||
if service.service == SERVICE_JOIN:
|
||||
master = [device for device in hass.data[DATA_SONOS].devices
|
||||
@ -365,6 +361,7 @@ class SonosDevice(MediaPlayerDevice):
|
||||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Subscribe sonos events."""
|
||||
self.hass.data[DATA_SONOS].devices.append(self)
|
||||
self.hass.async_add_job(self._subscribe_to_player_events)
|
||||
|
||||
@property
|
||||
@ -435,6 +432,10 @@ class SonosDevice(MediaPlayerDevice):
|
||||
"""Add event subscriptions."""
|
||||
player = self.soco
|
||||
|
||||
# New player available, build the current group topology
|
||||
for device in self.hass.data[DATA_SONOS].devices:
|
||||
device.process_zonegrouptopology_event(None)
|
||||
|
||||
queue = _ProcessSonosEventQueue(self.process_avtransport_event)
|
||||
player.avTransport.subscribe(auto_renew=True, event_queue=queue)
|
||||
|
||||
@ -520,11 +521,11 @@ class SonosDevice(MediaPlayerDevice):
|
||||
|
||||
def process_zonegrouptopology_event(self, event):
|
||||
"""Process a zone group topology event coming from a player."""
|
||||
if not hasattr(event, 'zone_player_uui_ds_in_group'):
|
||||
if event and not hasattr(event, 'zone_player_uui_ds_in_group'):
|
||||
return
|
||||
|
||||
with self.hass.data[DATA_SONOS].topology_lock:
|
||||
group = event.zone_player_uui_ds_in_group
|
||||
group = event and event.zone_player_uui_ds_in_group
|
||||
if group:
|
||||
# New group information is pushed
|
||||
coordinator_uid, *slave_uids = group.split(',')
|
||||
|
@ -122,11 +122,13 @@ class SoCoMock():
|
||||
return
|
||||
|
||||
|
||||
def fake_add_device(devices, update_befor_add=False):
|
||||
"""Fake add device / update."""
|
||||
if update_befor_add:
|
||||
for speaker in devices:
|
||||
speaker.update()
|
||||
def add_devices_factory(hass):
|
||||
"""Add devices factory."""
|
||||
def add_devices(devices, update_befor_add=False):
|
||||
"""Fake add device."""
|
||||
hass.data[sonos.DATA_SONOS].devices = devices
|
||||
|
||||
return add_devices
|
||||
|
||||
|
||||
class TestSonosMediaPlayer(unittest.TestCase):
|
||||
@ -156,7 +158,7 @@ class TestSonosMediaPlayer(unittest.TestCase):
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
def test_ensure_setup_discovery(self, *args):
|
||||
"""Test a single device using the autodiscovery provided by HASS."""
|
||||
sonos.setup_platform(self.hass, {}, fake_add_device, {
|
||||
sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
|
||||
@ -260,7 +262,7 @@ class TestSonosMediaPlayer(unittest.TestCase):
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
def test_ensure_setup_sonos_discovery(self, *args):
|
||||
"""Test a single device using the autodiscovery provided by Sonos."""
|
||||
sonos.setup_platform(self.hass, {}, fake_add_device)
|
||||
sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass))
|
||||
devices = self.hass.data[sonos.DATA_SONOS].devices
|
||||
self.assertEqual(len(devices), 1)
|
||||
self.assertEqual(devices[0].name, 'Kitchen')
|
||||
@ -270,7 +272,7 @@ class TestSonosMediaPlayer(unittest.TestCase):
|
||||
@mock.patch.object(SoCoMock, 'set_sleep_timer')
|
||||
def test_sonos_set_sleep_timer(self, set_sleep_timerMock, *args):
|
||||
"""Ensuring soco methods called for sonos_set_sleep_timer service."""
|
||||
sonos.setup_platform(self.hass, {}, fake_add_device, {
|
||||
sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
device = self.hass.data[sonos.DATA_SONOS].devices[-1]
|
||||
@ -284,7 +286,7 @@ class TestSonosMediaPlayer(unittest.TestCase):
|
||||
@mock.patch.object(SoCoMock, 'set_sleep_timer')
|
||||
def test_sonos_clear_sleep_timer(self, set_sleep_timerMock, *args):
|
||||
"""Ensuring soco methods called for sonos_clear_sleep_timer service."""
|
||||
sonos.setup_platform(self.hass, {}, mock.MagicMock(), {
|
||||
sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
device = self.hass.data[sonos.DATA_SONOS].devices[-1]
|
||||
@ -298,7 +300,7 @@ class TestSonosMediaPlayer(unittest.TestCase):
|
||||
@mock.patch('socket.create_connection', side_effect=socket.error())
|
||||
def test_update_alarm(self, soco_mock, alarm_mock, *args):
|
||||
"""Ensuring soco methods called for sonos_set_sleep_timer service."""
|
||||
sonos.setup_platform(self.hass, {}, fake_add_device, {
|
||||
sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
device = self.hass.data[sonos.DATA_SONOS].devices[-1]
|
||||
@ -328,7 +330,7 @@ class TestSonosMediaPlayer(unittest.TestCase):
|
||||
@mock.patch.object(soco.snapshot.Snapshot, 'snapshot')
|
||||
def test_sonos_snapshot(self, snapshotMock, *args):
|
||||
"""Ensuring soco methods called for sonos_snapshot service."""
|
||||
sonos.setup_platform(self.hass, {}, fake_add_device, {
|
||||
sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
device = self.hass.data[sonos.DATA_SONOS].devices[-1]
|
||||
@ -346,7 +348,7 @@ class TestSonosMediaPlayer(unittest.TestCase):
|
||||
"""Ensuring soco methods called for sonos_restor service."""
|
||||
from soco.snapshot import Snapshot
|
||||
|
||||
sonos.setup_platform(self.hass, {}, fake_add_device, {
|
||||
sonos.setup_platform(self.hass, {}, add_devices_factory(self.hass), {
|
||||
'host': '192.0.2.1'
|
||||
})
|
||||
device = self.hass.data[sonos.DATA_SONOS].devices[-1]
|
||||
|
Loading…
x
Reference in New Issue
Block a user