diff --git a/homeassistant/components/soundtouch/media_player.py b/homeassistant/components/soundtouch/media_player.py index 1d82c38d088..2f64a2d3605 100644 --- a/homeassistant/components/soundtouch/media_player.py +++ b/homeassistant/components/soundtouch/media_player.py @@ -437,15 +437,25 @@ class SoundTouchDevice(MediaPlayerDevice): # slaves for some reason. To compensate for this shortcoming we have to fetch # the zone info from the master when the current device is a slave until this is # fixed in the SoundTouch API or libsoundtouch, or of course until somebody has a - # better idea on how to fix this - if zone_status.is_master: + # better idea on how to fix this. + # In addition to this shortcoming, libsoundtouch seems to report the "is_master" + # property wrong on some slaves, so the only reliable way to detect if the current + # devices is the master, is by comparing the master_id of the zone with the device_id + if zone_status.master_id == self._device.config.device_id: return self._build_zone_info(self.entity_id, zone_status.slaves) - master_instance = self._get_instance_by_ip(zone_status.master_ip) - master_zone_status = master_instance.device.zone_status() - return self._build_zone_info( - master_instance.entity_id, master_zone_status.slaves - ) + # The master device has to be searched by it's ID and not IP since libsoundtouch / BOSE API + # do not return the IP of the master for some slave objects/responses + master_instance = self._get_instance_by_id(zone_status.master_id) + if master_instance is not None: + master_zone_status = master_instance.device.zone_status() + return self._build_zone_info( + master_instance.entity_id, master_zone_status.slaves + ) + + # We should never end up here since this means we haven't found a master device to get the + # correct zone info from. In this case, assume current device is master + return self._build_zone_info(self.entity_id, zone_status.slaves) def _get_instance_by_ip(self, ip_address): """Search and return a SoundTouchDevice instance by it's IP address.""" @@ -454,6 +464,13 @@ class SoundTouchDevice(MediaPlayerDevice): return instance return None + def _get_instance_by_id(self, instance_id): + """Search and return a SoundTouchDevice instance by it's ID (aka MAC address).""" + for instance in self.hass.data[DATA_SOUNDTOUCH]: + if instance and instance.device.config.device_id == instance_id: + return instance + return None + def _build_zone_info(self, master, zone_slaves): """Build the exposed zone attributes.""" slaves = [] diff --git a/tests/components/soundtouch/test_media_player.py b/tests/components/soundtouch/test_media_player.py index e69cec12ba3..ea580656b24 100644 --- a/tests/components/soundtouch/test_media_player.py +++ b/tests/components/soundtouch/test_media_player.py @@ -33,6 +33,8 @@ from homeassistant.setup import async_setup_component DEVICE_1_IP = "192.168.0.1" DEVICE_2_IP = "192.168.0.2" +DEVICE_1_ID = 1 +DEVICE_2_ID = 2 def get_config(host=DEVICE_1_IP, port=8090, name="soundtouch"): @@ -60,20 +62,22 @@ def one_device_fixture(): def two_zones_fixture(): """Mock one master and one slave.""" device_1 = MockDevice( + DEVICE_1_ID, MockZoneStatus( is_master=True, - master_id=1, + master_id=DEVICE_1_ID, master_ip=DEVICE_1_IP, slaves=[MockZoneSlave(DEVICE_2_IP)], - ) + ), ) device_2 = MockDevice( + DEVICE_2_ID, MockZoneStatus( is_master=False, - master_id=1, + master_id=DEVICE_1_ID, master_ip=DEVICE_1_IP, slaves=[MockZoneSlave(DEVICE_2_IP)], - ) + ), ) devices = {DEVICE_1_IP: device_1, DEVICE_2_IP: device_2} device_patch = patch( @@ -112,9 +116,9 @@ async def setup_soundtouch(hass, config): class MockDevice(STD): """Mock device.""" - def __init__(self, zone_status=None): + def __init__(self, id=None, zone_status=None): """Init the class.""" - self._config = MockConfig() + self._config = MockConfig(id) self._zone_status = zone_status or MockZoneStatus() def zone_status(self, refresh=True): @@ -125,9 +129,10 @@ class MockDevice(STD): class MockConfig(Config): """Mock config.""" - def __init__(self): + def __init__(self, id=None): """Init class.""" self._name = "name" + self._id = id or DEVICE_1_ID class MockZoneStatus(ZoneStatus):