From 9d8ba6af967f86ed3097e83413754ceedd4ce52f Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 16 Feb 2021 15:28:25 +0100 Subject: [PATCH] Use update coordinator for Xioami Miio subdevices (#46251) Co-authored-by: Martin Hjelmare --- .../components/xiaomi_miio/__init__.py | 35 +++++++++++++++++-- .../xiaomi_miio/alarm_control_panel.py | 4 +-- homeassistant/components/xiaomi_miio/const.py | 2 ++ .../components/xiaomi_miio/gateway.py | 22 +++--------- homeassistant/components/xiaomi_miio/light.py | 2 +- .../components/xiaomi_miio/sensor.py | 29 +++++++-------- 6 files changed, 55 insertions(+), 39 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/__init__.py b/homeassistant/components/xiaomi_miio/__init__.py index e81c35d39e4..273fc53da5a 100644 --- a/homeassistant/components/xiaomi_miio/__init__.py +++ b/homeassistant/components/xiaomi_miio/__init__.py @@ -1,7 +1,13 @@ """Support for Xiaomi Miio.""" +from datetime import timedelta +import logging + +from miio.gateway import GatewayException + from homeassistant import config_entries, core from homeassistant.const import CONF_HOST, CONF_TOKEN from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import ( CONF_DEVICE, @@ -9,10 +15,13 @@ from .const import ( CONF_GATEWAY, CONF_MODEL, DOMAIN, + KEY_COORDINATOR, MODELS_SWITCH, ) from .gateway import ConnectXiaomiGateway +_LOGGER = logging.getLogger(__name__) + GATEWAY_PLATFORMS = ["alarm_control_panel", "sensor", "light"] SWITCH_PLATFORMS = ["switch"] @@ -56,8 +65,6 @@ async def async_setup_gateway_entry( return False gateway_info = gateway.gateway_info - hass.data[DOMAIN][entry.entry_id] = gateway.gateway_device - gateway_model = f"{gateway_info.model}-{gateway_info.hardware_version}" device_registry = await dr.async_get_registry(hass) @@ -71,6 +78,30 @@ async def async_setup_gateway_entry( sw_version=gateway_info.firmware_version, ) + async def async_update_data(): + """Fetch data from the subdevice.""" + try: + for sub_device in gateway.gateway_device.devices.values(): + await hass.async_add_executor_job(sub_device.update) + except GatewayException as ex: + raise UpdateFailed("Got exception while fetching the state") from ex + + # Create update coordinator + coordinator = DataUpdateCoordinator( + hass, + _LOGGER, + # Name of the data. For logging purposes. + name=name, + update_method=async_update_data, + # Polling interval. Will only be polled if there are subscribers. + update_interval=timedelta(seconds=10), + ) + + hass.data[DOMAIN][entry.entry_id] = { + CONF_GATEWAY: gateway.gateway_device, + KEY_COORDINATOR: coordinator, + } + for component in GATEWAY_PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component) diff --git a/homeassistant/components/xiaomi_miio/alarm_control_panel.py b/homeassistant/components/xiaomi_miio/alarm_control_panel.py index 6880202cbd6..26421770771 100644 --- a/homeassistant/components/xiaomi_miio/alarm_control_panel.py +++ b/homeassistant/components/xiaomi_miio/alarm_control_panel.py @@ -15,7 +15,7 @@ from homeassistant.const import ( STATE_ALARM_DISARMED, ) -from .const import DOMAIN +from .const import CONF_GATEWAY, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -27,7 +27,7 @@ XIAOMI_STATE_ARMING_VALUE = "oning" async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the Xiaomi Gateway Alarm from a config entry.""" entities = [] - gateway = hass.data[DOMAIN][config_entry.entry_id] + gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY] entity = XiaomiGatewayAlarm( gateway, f"{config_entry.title} Alarm", diff --git a/homeassistant/components/xiaomi_miio/const.py b/homeassistant/components/xiaomi_miio/const.py index 3726f7f709d..c0ddb698340 100644 --- a/homeassistant/components/xiaomi_miio/const.py +++ b/homeassistant/components/xiaomi_miio/const.py @@ -7,6 +7,8 @@ CONF_DEVICE = "device" CONF_MODEL = "model" CONF_MAC = "mac" +KEY_COORDINATOR = "coordinator" + MODELS_GATEWAY = ["lumi.gateway", "lumi.acpartner"] MODELS_SWITCH = [ "chuangmi.plug.v1", diff --git a/homeassistant/components/xiaomi_miio/gateway.py b/homeassistant/components/xiaomi_miio/gateway.py index eb2f4cdf2eb..356b19dc89a 100644 --- a/homeassistant/components/xiaomi_miio/gateway.py +++ b/homeassistant/components/xiaomi_miio/gateway.py @@ -4,6 +4,7 @@ import logging from miio import DeviceException, gateway from homeassistant.helpers.entity import Entity +from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN @@ -56,16 +57,16 @@ class ConnectXiaomiGateway: return True -class XiaomiGatewayDevice(Entity): +class XiaomiGatewayDevice(CoordinatorEntity, Entity): """Representation of a base Xiaomi Gateway Device.""" - def __init__(self, sub_device, entry): + def __init__(self, coordinator, sub_device, entry): """Initialize the Xiaomi Gateway Device.""" + super().__init__(coordinator) self._sub_device = sub_device self._entry = entry self._unique_id = sub_device.sid self._name = f"{sub_device.name} ({sub_device.sid})" - self._available = False @property def unique_id(self): @@ -88,18 +89,3 @@ class XiaomiGatewayDevice(Entity): "model": self._sub_device.model, "sw_version": self._sub_device.firmware_version, } - - @property - def available(self): - """Return true when state is known.""" - return self._available - - async def async_update(self): - """Fetch state from the sub device.""" - try: - await self.hass.async_add_executor_job(self._sub_device.update) - self._available = True - except gateway.GatewayException as ex: - if self._available: - self._available = False - _LOGGER.error("Got exception while fetching the state: %s", ex) diff --git a/homeassistant/components/xiaomi_miio/light.py b/homeassistant/components/xiaomi_miio/light.py index efe67a370c4..7f168cf0e3e 100644 --- a/homeassistant/components/xiaomi_miio/light.py +++ b/homeassistant/components/xiaomi_miio/light.py @@ -130,7 +130,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): entities = [] if config_entry.data[CONF_FLOW_TYPE] == CONF_GATEWAY: - gateway = hass.data[DOMAIN][config_entry.entry_id] + gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY] # Gateway light if gateway.model not in [ GATEWAY_MODEL_AC_V1, diff --git a/homeassistant/components/xiaomi_miio/sensor.py b/homeassistant/components/xiaomi_miio/sensor.py index ab4df8cd982..821fe164ea9 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -2,13 +2,13 @@ from dataclasses import dataclass import logging -from miio import AirQualityMonitor, DeviceException # pylint: disable=import-error +from miio import AirQualityMonitor # pylint: disable=import-error +from miio import DeviceException from miio.gateway import ( GATEWAY_MODEL_AC_V1, GATEWAY_MODEL_AC_V2, GATEWAY_MODEL_AC_V3, GATEWAY_MODEL_EU, - DeviceType, GatewayException, ) import voluptuous as vol @@ -31,7 +31,7 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity -from .const import CONF_FLOW_TYPE, CONF_GATEWAY, DOMAIN +from .const import CONF_FLOW_TYPE, CONF_GATEWAY, DOMAIN, KEY_COORDINATOR from .gateway import XiaomiGatewayDevice _LOGGER = logging.getLogger(__name__) @@ -87,7 +87,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): entities = [] if config_entry.data[CONF_FLOW_TYPE] == CONF_GATEWAY: - gateway = hass.data[DOMAIN][config_entry.entry_id] + gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY] # Gateway illuminance sensor if gateway.model not in [ GATEWAY_MODEL_AC_V1, @@ -102,16 +102,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ) # Gateway sub devices sub_devices = gateway.devices + coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR] for sub_device in sub_devices.values(): - sensor_variables = None - if sub_device.type == DeviceType.SensorHT: - sensor_variables = ["temperature", "humidity"] - if sub_device.type == DeviceType.AqaraHT: - sensor_variables = ["temperature", "humidity", "pressure"] - if sensor_variables is not None: + sensor_variables = set(sub_device.status) & set(GATEWAY_SENSOR_TYPES) + if sensor_variables: entities.extend( [ - XiaomiGatewaySensor(sub_device, config_entry, variable) + XiaomiGatewaySensor( + coordinator, sub_device, config_entry, variable + ) for variable in sensor_variables ] ) @@ -240,9 +239,9 @@ class XiaomiAirQualityMonitor(Entity): class XiaomiGatewaySensor(XiaomiGatewayDevice): """Representation of a XiaomiGatewaySensor.""" - def __init__(self, sub_device, entry, data_key): + def __init__(self, coordinator, sub_device, entry, data_key): """Initialize the XiaomiSensor.""" - super().__init__(sub_device, entry) + super().__init__(coordinator, sub_device, entry) self._data_key = data_key self._unique_id = f"{sub_device.sid}-{data_key}" self._name = f"{data_key} ({sub_device.sid})".capitalize() @@ -288,9 +287,7 @@ class XiaomiGatewayIlluminanceSensor(Entity): @property def device_info(self): """Return the device info of the gateway.""" - return { - "identifiers": {(DOMAIN, self._gateway_device_id)}, - } + return {"identifiers": {(DOMAIN, self._gateway_device_id)}} @property def name(self):