From df25feab375639decd035d22221376bb86b0f04a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 26 Oct 2020 23:42:12 -0500 Subject: [PATCH] Convert somfy to use DataUpdateCoordinator (#42434) --- homeassistant/components/somfy/__init__.py | 71 +++++++++++----------- homeassistant/components/somfy/const.py | 3 + homeassistant/components/somfy/cover.py | 40 ++++++------ homeassistant/components/somfy/switch.py | 15 +++-- 4 files changed, 69 insertions(+), 60 deletions(-) diff --git a/homeassistant/components/somfy/__init__.py b/homeassistant/components/somfy/__init__.py index 0a683a75374..dd6e3340b17 100644 --- a/homeassistant/components/somfy/__init__.py +++ b/homeassistant/components/somfy/__init__.py @@ -4,7 +4,6 @@ from datetime import timedelta import logging from pymfy.api.devices.category import Category -from requests import HTTPError import voluptuous as vol from homeassistant.components.somfy import config_flow @@ -17,22 +16,19 @@ from homeassistant.helpers import ( ) from homeassistant.helpers.entity import Entity from homeassistant.helpers.typing import HomeAssistantType -from homeassistant.util import Throttle +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, +) from . import api -from .const import DOMAIN - -API = "api" - -DEVICES = "devices" +from .const import API, CONF_OPTIMISTIC, COORDINATOR, DOMAIN _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(minutes=1) -CONF_OPTIMISTIC = "optimistic" - SOMFY_AUTH_CALLBACK_PATH = "/auth/somfy/callback" SOMFY_AUTH_START = "/auth/somfy" @@ -88,15 +84,32 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): ) ) - hass.data[DOMAIN][API] = api.ConfigEntrySomfyApi(hass, entry, implementation) - hass.data[DOMAIN][DEVICES] = [] + data = hass.data[DOMAIN] + data[API] = api.ConfigEntrySomfyApi(hass, entry, implementation) - await update_all_devices(hass) + async def _update_all_devices(): + """Update all the devices.""" + devices = await hass.async_add_executor_job(data[API].get_devices) + return {dev.id: dev for dev in devices} + + coordinator = DataUpdateCoordinator( + hass, + _LOGGER, + name="somfy device update", + update_method=_update_all_devices, + update_interval=SCAN_INTERVAL, + ) + data[COORDINATOR] = coordinator + + await coordinator.async_refresh() device_registry = await dr.async_get_registry(hass) - devices = hass.data[DOMAIN][DEVICES] - hubs = [device for device in devices if Category.HUB.value in device.categories] + hubs = [ + device + for device in coordinator.data.values() + if Category.HUB.value in device.categories + ] for hub in hubs: device_registry.async_get_or_create( @@ -127,18 +140,24 @@ async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry): return True -class SomfyEntity(Entity): +class SomfyEntity(CoordinatorEntity, Entity): """Representation of a generic Somfy device.""" - def __init__(self, device, somfy_api): + def __init__(self, coordinator, device_id, somfy_api): """Initialize the Somfy device.""" - self.device = device + super().__init__(coordinator) + self._id = device_id self.api = somfy_api + @property + def device(self): + """Return data for the device id.""" + return self.coordinator.data[self._id] + @property def unique_id(self): """Return the unique id base on the id returned by Somfy.""" - return self.device.id + return self._id @property def name(self): @@ -160,12 +179,6 @@ class SomfyEntity(Entity): "manufacturer": "Somfy", } - async def async_update(self): - """Update the device with the latest data.""" - await update_all_devices(self.hass) - devices = self.hass.data[DOMAIN][DEVICES] - self.device = next((d for d in devices if d.id == self.device.id), self.device) - def has_capability(self, capability): """Test if device has a capability.""" capabilities = self.device.capabilities @@ -175,13 +188,3 @@ class SomfyEntity(Entity): def assumed_state(self): """Return if the device has an assumed state.""" return not bool(self.device.states) - - -@Throttle(SCAN_INTERVAL) -async def update_all_devices(hass): - """Update all the devices.""" - try: - data = hass.data[DOMAIN] - data[DEVICES] = await hass.async_add_executor_job(data[API].get_devices) - except HTTPError as err: - _LOGGER.warning("Cannot update devices: %s", err.response.status_code) diff --git a/homeassistant/components/somfy/const.py b/homeassistant/components/somfy/const.py index 8765e37e6d6..aca93be66cb 100644 --- a/homeassistant/components/somfy/const.py +++ b/homeassistant/components/somfy/const.py @@ -1,3 +1,6 @@ """Define constants for the Somfy component.""" DOMAIN = "somfy" +COORDINATOR = "coordinator" +API = "api" +CONF_OPTIMISTIC = "optimistic" diff --git a/homeassistant/components/somfy/cover.py b/homeassistant/components/somfy/cover.py index c577ecdc484..acceb860ce7 100644 --- a/homeassistant/components/somfy/cover.py +++ b/homeassistant/components/somfy/cover.py @@ -13,7 +13,8 @@ from homeassistant.components.cover import ( from homeassistant.const import STATE_CLOSED, STATE_OPEN from homeassistant.helpers.restore_state import RestoreEntity -from . import API, CONF_OPTIMISTIC, DEVICES, DOMAIN, SomfyEntity +from . import SomfyEntity +from .const import API, CONF_OPTIMISTIC, COORDINATOR, DOMAIN BLIND_DEVICE_CATEGORIES = {Category.INTERIOR_BLIND.value, Category.EXTERIOR_BLIND.value} SHUTTER_DEVICE_CATEGORIES = {Category.EXTERIOR_BLIND.value} @@ -29,14 +30,14 @@ async def async_setup_entry(hass, config_entry, async_add_entities): def get_covers(): """Retrieve covers.""" - devices = hass.data[DOMAIN][DEVICES] + domain_data = hass.data[DOMAIN] + coordinator = domain_data[COORDINATOR] + api = domain_data[API] return [ - SomfyCover( - cover, hass.data[DOMAIN][API], hass.data[DOMAIN][CONF_OPTIMISTIC] - ) - for cover in devices - if SUPPORTED_CATEGORIES & set(cover.categories) + SomfyCover(coordinator, device_id, api, domain_data[CONF_OPTIMISTIC]) + for device_id, device in coordinator.data.items() + if SUPPORTED_CATEGORIES & set(device.categories) ] async_add_entities(await hass.async_add_executor_job(get_covers)) @@ -45,11 +46,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities): class SomfyCover(SomfyEntity, RestoreEntity, CoverEntity): """Representation of a Somfy cover device.""" - def __init__(self, device, api, optimistic): + def __init__(self, coordinator, device_id, api, optimistic): """Initialize the Somfy device.""" - super().__init__(device, api) + super().__init__(coordinator, device_id, api) self.cover = Blind(self.device, self.api) - self.categories = set(device.categories) + self.categories = set(self.device.categories) self.optimistic = optimistic self._closed = None self._is_opening = None @@ -163,14 +164,13 @@ class SomfyCover(SomfyEntity, RestoreEntity, CoverEntity): async def async_added_to_hass(self): """Complete the initialization.""" await super().async_added_to_hass() - if self.optimistic: - # Restore the last state if we use optimistic - last_state = await self.async_get_last_state() + if not self.optimistic: + return + # Restore the last state if we use optimistic + last_state = await self.async_get_last_state() - if last_state is not None and last_state.state in ( - STATE_OPEN, - STATE_CLOSED, - ): - self._closed = last_state.state == STATE_CLOSED - - await self.async_update() + if last_state is not None and last_state.state in ( + STATE_OPEN, + STATE_CLOSED, + ): + self._closed = last_state.state == STATE_CLOSED diff --git a/homeassistant/components/somfy/switch.py b/homeassistant/components/somfy/switch.py index e96c91ecaea..2a81775cc22 100644 --- a/homeassistant/components/somfy/switch.py +++ b/homeassistant/components/somfy/switch.py @@ -4,7 +4,8 @@ from pymfy.api.devices.category import Category from homeassistant.components.switch import SwitchEntity -from . import API, DEVICES, DOMAIN, SomfyEntity +from . import SomfyEntity +from .const import API, COORDINATOR, DOMAIN async def async_setup_entry(hass, config_entry, async_add_entities): @@ -12,11 +13,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities): def get_shutters(): """Retrieve switches.""" - devices = hass.data[DOMAIN][DEVICES] + domain_data = hass.data[DOMAIN] + coordinator = domain_data[COORDINATOR] + api = domain_data[API] return [ - SomfyCameraShutter(device, hass.data[DOMAIN][API]) - for device in devices + SomfyCameraShutter(coordinator, device_id, api) + for device_id, device in coordinator.data.items() if Category.CAMERA.value in device.categories ] @@ -26,9 +29,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): class SomfyCameraShutter(SomfyEntity, SwitchEntity): """Representation of a Somfy Camera Shutter device.""" - def __init__(self, device, api): + def __init__(self, coordinator, device_id, api): """Initialize the Somfy device.""" - super().__init__(device, api) + super().__init__(coordinator, device_id, api) self.shutter = CameraProtect(self.device, self.api) async def async_update(self):