From ecef0f6e937fa9b6d6cc5494042a275941644c62 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 19 Jan 2020 13:39:16 -0800 Subject: [PATCH] Catch all Ring timeout errors (#30960) * Catch more Ring errors * Fix comment & Disable wifi entities by default --- homeassistant/components/ring/__init__.py | 30 ++++++++++++++++++++--- homeassistant/components/ring/light.py | 9 ++++++- homeassistant/components/ring/sensor.py | 12 ++++----- homeassistant/components/ring/switch.py | 9 ++++++- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/ring/__init__.py b/homeassistant/components/ring/__init__.py index 57148cc15af..34aa9f6b0ec 100644 --- a/homeassistant/components/ring/__init__.py +++ b/homeassistant/components/ring/__init__.py @@ -7,6 +7,7 @@ from pathlib import Path from typing import Optional from oauthlib.oauth2 import AccessDeniedError +import requests from ring_doorbell import Auth, Ring import voluptuous as vol @@ -95,13 +96,19 @@ async def async_setup_entry(hass, entry): "api": ring, "devices": ring.devices(), "device_data": GlobalDataUpdater( - hass, entry.entry_id, ring, "update_devices", timedelta(minutes=1) + hass, "device", entry.entry_id, ring, "update_devices", timedelta(minutes=1) ), "dings_data": GlobalDataUpdater( - hass, entry.entry_id, ring, "update_dings", timedelta(seconds=5) + hass, + "active dings", + entry.entry_id, + ring, + "update_dings", + timedelta(seconds=5), ), "history_data": DeviceDataUpdater( hass, + "history", entry.entry_id, ring, lambda device: device.history(limit=10), @@ -109,6 +116,7 @@ async def async_setup_entry(hass, entry): ), "health_data": DeviceDataUpdater( hass, + "health", entry.entry_id, ring, lambda device: device.update_health_data(), @@ -168,6 +176,7 @@ class GlobalDataUpdater: def __init__( self, hass: HomeAssistant, + data_type: str, config_entry_id: str, ring: Ring, update_method: str, @@ -175,6 +184,7 @@ class GlobalDataUpdater: ): """Initialize global data updater.""" self.hass = hass + self.data_type = data_type self.config_entry_id = config_entry_id self.ring = ring self.update_method = update_method @@ -215,6 +225,11 @@ class GlobalDataUpdater: _LOGGER.error("Ring access token is no longer valid. Set up Ring again") await self.hass.config_entries.async_unload(self.config_entry_id) return + except requests.Timeout: + _LOGGER.warning( + "Time out fetching Ring %s data", self.data_type, + ) + return for update_callback in self.listeners: update_callback() @@ -226,12 +241,14 @@ class DeviceDataUpdater: def __init__( self, hass: HomeAssistant, + data_type: str, config_entry_id: str, ring: Ring, update_method: str, update_interval: timedelta, ): """Initialize device data updater.""" + self.data_type = data_type self.hass = hass self.config_entry_id = config_entry_id self.ring = ring @@ -282,7 +299,7 @@ class DeviceDataUpdater: def refresh_all(self, _=None): """Refresh all registered devices.""" - for info in self.devices.values(): + for device_id, info in self.devices.items(): try: data = info["data"] = self.update_method(info["device"]) except AccessDeniedError: @@ -291,6 +308,13 @@ class DeviceDataUpdater: self.hass.config_entries.async_unload(self.config_entry_id) ) return + except requests.Timeout: + _LOGGER.warning( + "Time out fetching Ring %s data for device %s", + self.data_type, + device_id, + ) + continue for update_callback in info["update_callbacks"]: self.hass.loop.call_soon_threadsafe(update_callback, data) diff --git a/homeassistant/components/ring/light.py b/homeassistant/components/ring/light.py index 86ef55af16d..fa47ac35ee3 100644 --- a/homeassistant/components/ring/light.py +++ b/homeassistant/components/ring/light.py @@ -2,6 +2,8 @@ from datetime import timedelta import logging +import requests + from homeassistant.components.light import Light from homeassistant.core import callback import homeassistant.util.dt as dt_util @@ -72,7 +74,12 @@ class RingLight(RingEntityMixin, Light): def _set_light(self, new_state): """Update light state, and causes Home Assistant to correctly update.""" - self._device.lights = new_state + try: + self._device.lights = new_state + except requests.Timeout: + _LOGGER.error("Time out setting %s light to %s", self.entity_id, new_state) + return + self._light_on = new_state == ON_STATE self._no_updates_until = dt_util.utcnow() + SKIP_UPDATES_DELAY self.async_schedule_update_ha_state() diff --git a/homeassistant/components/ring/sensor.py b/homeassistant/components/ring/sensor.py index 2b921dddd2f..329077a18e7 100644 --- a/homeassistant/components/ring/sensor.py +++ b/homeassistant/components/ring/sensor.py @@ -15,9 +15,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): """Set up a sensor for a Ring device.""" devices = hass.data[DOMAIN][config_entry.entry_id]["devices"] - # Makes a ton of requests. We will make this a config entry option in the future - wifi_enabled = False - sensors = [] for device_type in ("chimes", "doorbots", "authorized_doorbots", "stickup_cams"): @@ -25,9 +22,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): if device_type not in SENSOR_TYPES[sensor_type][1]: continue - if not wifi_enabled and sensor_type.startswith("wifi_"): - continue - for device in devices[device_type]: if device_type == "battery" and device.battery_life is None: continue @@ -124,6 +118,12 @@ class HealthDataRingSensor(RingSensor): """Call update method.""" self.async_write_ha_state() + @property + def entity_registry_enabled_default(self) -> bool: + """Return if the entity should be enabled when first added to the entity registry.""" + # These sensors are data hungry and not useful. Disable by default. + return False + @property def state(self): """Return the state of the sensor.""" diff --git a/homeassistant/components/ring/switch.py b/homeassistant/components/ring/switch.py index 65eed83d98e..e2f1882adf6 100644 --- a/homeassistant/components/ring/switch.py +++ b/homeassistant/components/ring/switch.py @@ -2,6 +2,8 @@ from datetime import timedelta import logging +import requests + from homeassistant.components.switch import SwitchDevice from homeassistant.core import callback import homeassistant.util.dt as dt_util @@ -74,7 +76,12 @@ class SirenSwitch(BaseRingSwitch): def _set_switch(self, new_state): """Update switch state, and causes Home Assistant to correctly update.""" - self._device.siren = new_state + try: + self._device.siren = new_state + except requests.Timeout: + _LOGGER.error("Time out setting %s siren to %s", self.entity_id, new_state) + return + self._siren_on = new_state > 0 self._no_updates_until = dt_util.utcnow() + SKIP_UPDATES_DELAY self.schedule_update_ha_state()