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:
Anders Melchiorsen 2018-03-09 00:39:31 +01:00 committed by Paulus Schoutsen
parent eaf525d41d
commit 44e4f8d1ba
2 changed files with 29 additions and 26 deletions

View File

@ -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')
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
if entity_ids:
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(',')

View File

@ -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]