diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index a03a2e1db97..9ea33b4c396 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -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(',') diff --git a/tests/components/media_player/test_sonos.py b/tests/components/media_player/test_sonos.py index f1a0f4a82fc..3470c79ad64 100644 --- a/tests/components/media_player/test_sonos.py +++ b/tests/components/media_player/test_sonos.py @@ -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]