From 8a62bc92376996d6e9a1c4d66d2e4448c8245b01 Mon Sep 17 00:00:00 2001 From: ehendrix23 Date: Thu, 6 Dec 2018 23:26:49 -0700 Subject: [PATCH] Set directv unavailable state when errors returned for longer then a minute (#19014) * Fix unavailable Change setting to unavailable when getting request exceptions only after 1 minute has past since 1st occurrence. * Put common code in _check_state_available Put common code to determine if available should be set to False in method _check_state_available --- .../components/media_player/directv.py | 40 ++++++++++++++++--- tests/components/media_player/test_directv.py | 24 ++++++++++- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/media_player/directv.py b/homeassistant/components/media_player/directv.py index d8c67e372b2..707014328c6 100644 --- a/homeassistant/components/media_player/directv.py +++ b/homeassistant/components/media_player/directv.py @@ -133,6 +133,7 @@ class DirecTvDevice(MediaPlayerDevice): self._is_client = device != '0' self._assumed_state = None self._available = False + self._first_error_timestamp = None if self._is_client: _LOGGER.debug("Created DirecTV client %s for device %s", @@ -142,7 +143,7 @@ class DirecTvDevice(MediaPlayerDevice): def update(self): """Retrieve latest state.""" - _LOGGER.debug("Updating status for %s", self._name) + _LOGGER.debug("%s: Updating status", self.entity_id) try: self._available = True self._is_standby = self.dtv.get_standby() @@ -156,6 +157,7 @@ class DirecTvDevice(MediaPlayerDevice): else: self._current = self.dtv.get_tuned() if self._current['status']['code'] == 200: + self._first_error_timestamp = None self._is_recorded = self._current.get('uniqueId')\ is not None self._paused = self._last_position == \ @@ -165,15 +167,41 @@ class DirecTvDevice(MediaPlayerDevice): self._last_update = dt_util.utcnow() if not self._paused \ or self._last_update is None else self._last_update else: - self._available = False + # If an error is received then only set to unavailable if + # this started at least 1 minute ago. + log_message = "{}: Invalid status {} received".format( + self.entity_id, + self._current['status']['code'] + ) + if self._check_state_available(): + _LOGGER.debug(log_message) + else: + _LOGGER.error(log_message) + except requests.RequestException as ex: - _LOGGER.error("Request error trying to update current status for" - " %s. %s", self._name, ex) - self._available = False - except Exception: + _LOGGER.error("%s: Request error trying to update current status: " + "%s", self.entity_id, ex) + self._check_state_available() + + except Exception as ex: + _LOGGER.error("%s: Exception trying to update current status: %s", + self.entity_id, ex) self._available = False + if not self._first_error_timestamp: + self._first_error_timestamp = dt_util.utcnow() raise + def _check_state_available(self): + """Set to unavailable if issue been occurring over 1 minute.""" + if not self._first_error_timestamp: + self._first_error_timestamp = dt_util.utcnow() + else: + tdelta = dt_util.utcnow() - self._first_error_timestamp + if tdelta.total_seconds() >= 60: + self._available = False + + return self._available + @property def device_state_attributes(self): """Return device specific state attributes.""" diff --git a/tests/components/media_player/test_directv.py b/tests/components/media_player/test_directv.py index 951f1319cc0..d8e561d8d2a 100644 --- a/tests/components/media_player/test_directv.py +++ b/tests/components/media_player/test_directv.py @@ -515,7 +515,7 @@ async def test_available(hass, platforms, main_dtv, mock_now): state = hass.states.get(MAIN_ENTITY_ID) assert state.state != STATE_UNAVAILABLE - # Make update fail (i.e. DVR offline) + # Make update fail 1st time next_update = next_update + timedelta(minutes=5) with patch.object( main_dtv, 'get_standby', side_effect=requests.RequestException), \ @@ -523,6 +523,28 @@ async def test_available(hass, platforms, main_dtv, mock_now): async_fire_time_changed(hass, next_update) await hass.async_block_till_done() + state = hass.states.get(MAIN_ENTITY_ID) + assert state.state != STATE_UNAVAILABLE + + # Make update fail 2nd time within 1 minute + next_update = next_update + timedelta(seconds=30) + with patch.object( + main_dtv, 'get_standby', side_effect=requests.RequestException), \ + patch('homeassistant.util.dt.utcnow', return_value=next_update): + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + + state = hass.states.get(MAIN_ENTITY_ID) + assert state.state != STATE_UNAVAILABLE + + # Make update fail 3rd time more then a minute after 1st failure + next_update = next_update + timedelta(minutes=1) + with patch.object( + main_dtv, 'get_standby', side_effect=requests.RequestException), \ + patch('homeassistant.util.dt.utcnow', return_value=next_update): + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + state = hass.states.get(MAIN_ENTITY_ID) assert state.state == STATE_UNAVAILABLE