From f367c49fb922330b235f9e5c2b2997c851e35068 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 6 Nov 2016 00:58:29 +0100 Subject: [PATCH 1/4] Sonos fix for slow update (#4232) * Sonos fix for slow update * fix auto update on discovery * fix unittest --- .../components/media_player/sonos.py | 29 ++++++++++--------- tests/components/media_player/test_sonos.py | 23 ++++++++++----- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index 39b9559aa59..89f5d7b07ed 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -80,7 +80,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if player.is_visible: device = SonosDevice(hass, player) - add_devices([device]) + add_devices([device], True) if not DEVICES: register_services(hass) DEVICES.append(device) @@ -106,7 +106,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return False DEVICES = [SonosDevice(hass, p) for p in players] - add_devices(DEVICES) + add_devices(DEVICES, True) register_services(hass) _LOGGER.info('Added %s Sonos speakers', len(players)) return True @@ -256,6 +256,7 @@ class SonosDevice(MediaPlayerDevice): self.hass = hass self.volume_increment = 5 + self._unique_id = player.uid self._player = player self._player_volume = None self._player_volume_muted = None @@ -278,7 +279,8 @@ class SonosDevice(MediaPlayerDevice): self._current_track_is_radio_stream = False self._queue = None self._last_avtransport_event = None - self.update() + self._is_playing_line_in = None + self._is_playing_tv = None self.soco_snapshot = Snapshot(self._player) @property @@ -286,14 +288,10 @@ class SonosDevice(MediaPlayerDevice): """Polling needed.""" return True - def update_sonos(self, now): - """Update state, called by track_utc_time_change.""" - self.update_ha_state(True) - @property def unique_id(self): """Return an unique ID.""" - return self._player.uid + return self._unique_id @property def name(self): @@ -354,6 +352,9 @@ class SonosDevice(MediaPlayerDevice): if is_available: + self._is_playing_tv = self._player.is_playing_tv + self._is_playing_line_in = self._player.is_playing_line_in + track_info = None if self._last_avtransport_event: variables = self._last_avtransport_event.variables @@ -511,10 +512,10 @@ class SonosDevice(MediaPlayerDevice): # update state of the whole group # pylint: disable=protected-access for device in [x for x in DEVICES if x._coordinator == self]: - if device.entity_id: - device.update_ha_state(False) + if device.entity_id is not self.entity_id: + self.hass.add_job(device.async_update_ha_state) - if self._queue is None and self.entity_id: + if self._queue is None: self._subscribe_to_player_events() else: self._player_volume = None @@ -534,6 +535,8 @@ class SonosDevice(MediaPlayerDevice): self._support_previous_track = False self._support_next_track = False self._support_pause = False + self._is_playing_tv = False + self._is_playing_line_in = False self._last_avtransport_event = None @@ -713,9 +716,9 @@ class SonosDevice(MediaPlayerDevice): @property def source(self): """Name of the current input source.""" - if self._player.is_playing_line_in: + if self._is_playing_line_in: return SUPPORT_SOURCE_LINEIN - if self._player.is_playing_tv: + if self._is_playing_tv: return SUPPORT_SOURCE_TV return None diff --git a/tests/components/media_player/test_sonos.py b/tests/components/media_player/test_sonos.py index dfd308d5459..b170f14c372 100644 --- a/tests/components/media_player/test_sonos.py +++ b/tests/components/media_player/test_sonos.py @@ -92,6 +92,13 @@ class SoCoMock(): return "RINCON_XXXXXXXXXXXXXXXXX" +def fake_add_device(devices, update_befor_add=False): + """Fake add device / update.""" + if update_befor_add: + for speaker in devices: + speaker.update() + + class TestSonosMediaPlayer(unittest.TestCase): """Test the media_player module.""" @@ -117,7 +124,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, {}, mock.MagicMock(), '192.0.2.1') + sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') # Ensure registration took place (#2558) self.assertEqual(len(sonos.DEVICES), 1) @@ -129,7 +136,7 @@ class TestSonosMediaPlayer(unittest.TestCase): """Test a single address config'd by the HASS config file.""" sonos.setup_platform(self.hass, {'hosts': '192.0.2.1'}, - mock.MagicMock()) + fake_add_device) # Ensure registration took place (#2558) self.assertEqual(len(sonos.DEVICES), 1) @@ -140,7 +147,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, {}, mock.MagicMock()) + sonos.setup_platform(self.hass, {}, fake_add_device) self.assertEqual(len(sonos.DEVICES), 1) self.assertEqual(sonos.DEVICES[0].name, 'Kitchen') @@ -149,7 +156,7 @@ class TestSonosMediaPlayer(unittest.TestCase): @mock.patch.object(SoCoMock, 'partymode') def test_sonos_group_players(self, partymodeMock, *args): """Ensuring soco methods called for sonos_group_players service.""" - sonos.setup_platform(self.hass, {}, mock.MagicMock(), '192.0.2.1') + sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') device = sonos.DEVICES[-1] partymodeMock.return_value = True device.group_players() @@ -161,7 +168,7 @@ class TestSonosMediaPlayer(unittest.TestCase): @mock.patch.object(SoCoMock, 'unjoin') def test_sonos_unjoin(self, unjoinMock, *args): """Ensuring soco methods called for sonos_unjoin service.""" - sonos.setup_platform(self.hass, {}, mock.MagicMock(), '192.0.2.1') + sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') device = sonos.DEVICES[-1] unjoinMock.return_value = True device.unjoin() @@ -173,7 +180,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, {}, mock.MagicMock(), '192.0.2.1') + sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') device = sonos.DEVICES[-1] device.set_sleep_timer(30) set_sleep_timerMock.assert_called_once_with(30) @@ -193,7 +200,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, {}, mock.MagicMock(), '192.0.2.1') + sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') device = sonos.DEVICES[-1] snapshotMock.return_value = True device.snapshot() @@ -205,7 +212,7 @@ class TestSonosMediaPlayer(unittest.TestCase): @mock.patch.object(soco.snapshot.Snapshot, 'restore') def test_sonos_restore(self, restoreMock, *args): """Ensuring soco methods called for sonos_restor service.""" - sonos.setup_platform(self.hass, {}, mock.MagicMock(), '192.0.2.1') + sonos.setup_platform(self.hass, {}, fake_add_device, '192.0.2.1') device = sonos.DEVICES[-1] restoreMock.return_value = True device.restore() From 28861221aea3b0d6deaf977c21eea7a7bd04a03a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 5 Nov 2016 15:29:22 -0700 Subject: [PATCH 2/4] Remove chunked encoding (#4230) --- homeassistant/components/camera/__init__.py | 1 - homeassistant/components/camera/ffmpeg.py | 1 - homeassistant/components/camera/mjpeg.py | 1 - homeassistant/components/camera/synology.py | 1 - 4 files changed, 4 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index d02e7954349..6724598419f 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -101,7 +101,6 @@ class Camera(Entity): response.content_type = ('multipart/x-mixed-replace; ' 'boundary=--jpegboundary') - response.enable_chunked_encoding() yield from response.prepare(request) def write(img_bytes): diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/camera/ffmpeg.py index 8e238bfdea7..c3f0ffbfe0b 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -75,7 +75,6 @@ class FFmpegCamera(Camera): response = web.StreamResponse() response.content_type = 'multipart/x-mixed-replace;boundary=ffserver' - response.enable_chunked_encoding() yield from response.prepare(request) diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index 81759fa86df..e8799d1be34 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -112,7 +112,6 @@ class MjpegCamera(Camera): response = web.StreamResponse() response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) - response.enable_chunked_encoding() yield from response.prepare(request) diff --git a/homeassistant/components/camera/synology.py b/homeassistant/components/camera/synology.py index 4abdf8d22dd..4ca63c16d7d 100644 --- a/homeassistant/components/camera/synology.py +++ b/homeassistant/components/camera/synology.py @@ -271,7 +271,6 @@ class SynologyCamera(Camera): response = web.StreamResponse() response.content_type = stream.headers.get(CONTENT_TYPE_HEADER) - response.enable_chunked_encoding() yield from response.prepare(request) From 20e1b3eae08b9052b44e9f54d5ed8c5d76e15033 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 5 Nov 2016 13:28:11 -0700 Subject: [PATCH 3/4] Fix radiotherm I/O inside properties (#4227) --- homeassistant/components/climate/radiotherm.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/climate/radiotherm.py b/homeassistant/components/climate/radiotherm.py index c2d712e19bd..5fa3f891aac 100644 --- a/homeassistant/components/climate/radiotherm.py +++ b/homeassistant/components/climate/radiotherm.py @@ -69,6 +69,8 @@ class RadioThermostat(ClimateDevice): self._current_temperature = None self._current_operation = STATE_IDLE self._name = None + self._fmode = None + self._tmode = None self.hold_temp = hold_temp self.update() self._operation_list = [STATE_AUTO, STATE_COOL, STATE_HEAT, STATE_OFF] @@ -87,8 +89,8 @@ class RadioThermostat(ClimateDevice): def device_state_attributes(self): """Return the device specific state attributes.""" return { - ATTR_FAN: self.device.fmode['human'], - ATTR_MODE: self.device.tmode['human'] + ATTR_FAN: self._fmode, + ATTR_MODE: self._tmode, } @property @@ -115,10 +117,13 @@ class RadioThermostat(ClimateDevice): """Update the data from the thermostat.""" self._current_temperature = self.device.temp['raw'] self._name = self.device.name['raw'] - if self.device.tmode['human'] == 'Cool': + self._fmode = self.device.fmode['human'] + self._tmode = self.device.tmode['human'] + + if self._tmode == 'Cool': self._target_temperature = self.device.t_cool['raw'] self._current_operation = STATE_COOL - elif self.device.tmode['human'] == 'Heat': + elif self._tmode == 'Heat': self._target_temperature = self.device.t_heat['raw'] self._current_operation = STATE_HEAT else: From af297aa0dccd41fa7e5b35f86163a41bc7fcfa49 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 5 Nov 2016 17:00:06 -0700 Subject: [PATCH 4/4] Version bump to 0.32.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index ec05a7e24ac..299e437be3c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 32 -PATCH_VERSION = '0' +PATCH_VERSION = '1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 4, 2)