From 513e276bbaba4c1d750cad4253db5c79fb341ec5 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Tue, 17 May 2022 08:32:28 +0200 Subject: [PATCH] Avoid polling fjaraskupan if no broadcast is received (#71969) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Switch to subclassed coordinator * Avoid polling fjäråskupan only do on request We still want to timeout the availability if no data was received. * Remove unused variable * Update homeassistant/components/fjaraskupan/__init__.py Co-authored-by: Paulus Schoutsen Co-authored-by: Paulus Schoutsen --- .../components/fjaraskupan/__init__.py | 91 ++++++++++++------- .../components/fjaraskupan/binary_sensor.py | 21 ++--- homeassistant/components/fjaraskupan/fan.py | 17 ++-- homeassistant/components/fjaraskupan/light.py | 21 ++--- .../components/fjaraskupan/number.py | 19 ++-- .../components/fjaraskupan/sensor.py | 21 ++--- 6 files changed, 92 insertions(+), 98 deletions(-) diff --git a/homeassistant/components/fjaraskupan/__init__.py b/homeassistant/components/fjaraskupan/__init__.py index 64962a746f7..488139b080b 100644 --- a/homeassistant/components/fjaraskupan/__init__.py +++ b/homeassistant/components/fjaraskupan/__init__.py @@ -20,7 +20,7 @@ from homeassistant.helpers.dispatcher import ( ) from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import DISPATCH_DETECTION, DOMAIN @@ -35,13 +35,48 @@ PLATFORMS = [ _LOGGER = logging.getLogger(__name__) -@dataclass -class DeviceState: - """Store state of a device.""" +class Coordinator(DataUpdateCoordinator[State]): + """Update coordinator for each device.""" - device: Device - coordinator: DataUpdateCoordinator[State] - device_info: DeviceInfo + def __init__( + self, hass: HomeAssistant, device: Device, device_info: DeviceInfo + ) -> None: + """Initialize the coordinator.""" + self.device = device + self.device_info = device_info + self._refresh_was_scheduled = False + + super().__init__( + hass, _LOGGER, name="Fjäråskupan", update_interval=timedelta(seconds=120) + ) + + async def _async_refresh( + self, + log_failures: bool = True, + raise_on_auth_failed: bool = False, + scheduled: bool = False, + ) -> None: + self._refresh_was_scheduled = scheduled + await super()._async_refresh( + log_failures=log_failures, + raise_on_auth_failed=raise_on_auth_failed, + scheduled=scheduled, + ) + + async def _async_update_data(self) -> State: + """Handle an explicit update request.""" + if self._refresh_was_scheduled: + raise UpdateFailed("No data received within schedule.") + + await self.device.update() + return self.device.state + + def detection_callback( + self, ble_device: BLEDevice, advertisement_data: AdvertisementData + ) -> None: + """Handle a new announcement of data.""" + self.device.detection_callback(ble_device, advertisement_data) + self.async_set_updated_data(self.device.state) @dataclass @@ -49,7 +84,7 @@ class EntryState: """Store state of config entry.""" scanner: BleakScanner - devices: dict[str, DeviceState] + coordinators: dict[str, Coordinator] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -64,13 +99,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def detection_callback( ble_device: BLEDevice, advertisement_data: AdvertisementData ) -> None: - if data := state.devices.get(ble_device.address): + if data := state.coordinators.get(ble_device.address): _LOGGER.debug( "Update: %s %s - %s", ble_device.name, ble_device, advertisement_data ) - data.device.detection_callback(ble_device, advertisement_data) - data.coordinator.async_set_updated_data(data.device.state) + data.detection_callback(ble_device, advertisement_data) else: if not device_filter(ble_device, advertisement_data): return @@ -80,31 +114,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) device = Device(ble_device) - device.detection_callback(ble_device, advertisement_data) - - async def async_update_data(): - """Handle an explicit update request.""" - await device.update() - return device.state - - coordinator: DataUpdateCoordinator[State] = DataUpdateCoordinator( - hass, - logger=_LOGGER, - name="Fjaraskupan Updater", - update_interval=timedelta(seconds=120), - update_method=async_update_data, - ) - coordinator.async_set_updated_data(device.state) - device_info = DeviceInfo( identifiers={(DOMAIN, ble_device.address)}, manufacturer="Fjäråskupan", name="Fjäråskupan", ) - device_state = DeviceState(device, coordinator, device_info) - state.devices[ble_device.address] = device_state + + coordinator: Coordinator = Coordinator(hass, device, device_info) + coordinator.detection_callback(ble_device, advertisement_data) + + state.coordinators[ble_device.address] = coordinator async_dispatcher_send( - hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", device_state + hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", coordinator ) scanner.register_detection_callback(detection_callback) @@ -119,20 +140,20 @@ def async_setup_entry_platform( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback, - constructor: Callable[[DeviceState], list[Entity]], + constructor: Callable[[Coordinator], list[Entity]], ) -> None: """Set up a platform with added entities.""" entry_state: EntryState = hass.data[DOMAIN][entry.entry_id] async_add_entities( entity - for device_state in entry_state.devices.values() - for entity in constructor(device_state) + for coordinator in entry_state.coordinators.values() + for entity in constructor(coordinator) ) @callback - def _detection(device_state: DeviceState) -> None: - async_add_entities(constructor(device_state)) + def _detection(coordinator: Coordinator) -> None: + async_add_entities(constructor(coordinator)) entry.async_on_unload( async_dispatcher_connect( diff --git a/homeassistant/components/fjaraskupan/binary_sensor.py b/homeassistant/components/fjaraskupan/binary_sensor.py index f1672530c45..ef4f64c5ecd 100644 --- a/homeassistant/components/fjaraskupan/binary_sensor.py +++ b/homeassistant/components/fjaraskupan/binary_sensor.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from fjaraskupan import Device, State +from fjaraskupan import Device from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -15,12 +15,9 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform @dataclass @@ -53,12 +50,12 @@ async def async_setup_entry( ) -> None: """Set up sensors dynamically through discovery.""" - def _constructor(device_state: DeviceState) -> list[Entity]: + def _constructor(coordinator: Coordinator) -> list[Entity]: return [ BinarySensor( - device_state.coordinator, - device_state.device, - device_state.device_info, + coordinator, + coordinator.device, + coordinator.device_info, entity_description, ) for entity_description in SENSORS @@ -67,14 +64,14 @@ async def async_setup_entry( async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class BinarySensor(CoordinatorEntity[DataUpdateCoordinator[State]], BinarySensorEntity): +class BinarySensor(CoordinatorEntity[Coordinator], BinarySensorEntity): """Grease filter sensor.""" entity_description: EntityDescription def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, entity_description: EntityDescription, diff --git a/homeassistant/components/fjaraskupan/fan.py b/homeassistant/components/fjaraskupan/fan.py index a8f8e13f3da..fcd95090400 100644 --- a/homeassistant/components/fjaraskupan/fan.py +++ b/homeassistant/components/fjaraskupan/fan.py @@ -16,16 +16,13 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util.percentage import ( ordered_list_item_to_percentage, percentage_to_ordered_list_item, ) -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform ORDERED_NAMED_FAN_SPEEDS = ["1", "2", "3", "4", "5", "6", "7", "8"] @@ -58,22 +55,20 @@ async def async_setup_entry( ) -> None: """Set up sensors dynamically through discovery.""" - def _constructor(device_state: DeviceState): - return [ - Fan(device_state.coordinator, device_state.device, device_state.device_info) - ] + def _constructor(coordinator: Coordinator): + return [Fan(coordinator, coordinator.device, coordinator.device_info)] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class Fan(CoordinatorEntity[DataUpdateCoordinator[State]], FanEntity): +class Fan(CoordinatorEntity[Coordinator], FanEntity): """Fan entity.""" _attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, ) -> None: diff --git a/homeassistant/components/fjaraskupan/light.py b/homeassistant/components/fjaraskupan/light.py index 6c3e6e458f8..9d52c5cac82 100644 --- a/homeassistant/components/fjaraskupan/light.py +++ b/homeassistant/components/fjaraskupan/light.py @@ -1,19 +1,16 @@ """Support for lights.""" from __future__ import annotations -from fjaraskupan import COMMAND_LIGHT_ON_OFF, Device, State +from fjaraskupan import COMMAND_LIGHT_ON_OFF, Device from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform async def async_setup_entry( @@ -23,22 +20,18 @@ async def async_setup_entry( ) -> None: """Set up tuya sensors dynamically through tuya discovery.""" - def _constructor(device_state: DeviceState) -> list[Entity]: - return [ - Light( - device_state.coordinator, device_state.device, device_state.device_info - ) - ] + def _constructor(coordinator: Coordinator) -> list[Entity]: + return [Light(coordinator, coordinator.device, coordinator.device_info)] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class Light(CoordinatorEntity[DataUpdateCoordinator[State]], LightEntity): +class Light(CoordinatorEntity[Coordinator], LightEntity): """Light device.""" def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, ) -> None: diff --git a/homeassistant/components/fjaraskupan/number.py b/homeassistant/components/fjaraskupan/number.py index bbde9bd8898..6314b9c9cc1 100644 --- a/homeassistant/components/fjaraskupan/number.py +++ b/homeassistant/components/fjaraskupan/number.py @@ -1,7 +1,7 @@ """Support for sensors.""" from __future__ import annotations -from fjaraskupan import Device, State +from fjaraskupan import Device from homeassistant.components.number import NumberEntity from homeassistant.config_entries import ConfigEntry @@ -9,12 +9,9 @@ from homeassistant.const import TIME_MINUTES from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform async def async_setup_entry( @@ -24,19 +21,17 @@ async def async_setup_entry( ) -> None: """Set up number entities dynamically through discovery.""" - def _constructor(device_state: DeviceState) -> list[Entity]: + def _constructor(coordinator: Coordinator) -> list[Entity]: return [ PeriodicVentingTime( - device_state.coordinator, device_state.device, device_state.device_info + coordinator, coordinator.device, coordinator.device_info ), ] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class PeriodicVentingTime( - CoordinatorEntity[DataUpdateCoordinator[State]], NumberEntity -): +class PeriodicVentingTime(CoordinatorEntity[Coordinator], NumberEntity): """Periodic Venting.""" _attr_max_value: float = 59 @@ -47,7 +42,7 @@ class PeriodicVentingTime( def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, ) -> None: diff --git a/homeassistant/components/fjaraskupan/sensor.py b/homeassistant/components/fjaraskupan/sensor.py index fbd9d5f6d08..e4dbffab38e 100644 --- a/homeassistant/components/fjaraskupan/sensor.py +++ b/homeassistant/components/fjaraskupan/sensor.py @@ -1,7 +1,7 @@ """Support for sensors.""" from __future__ import annotations -from fjaraskupan import Device, State +from fjaraskupan import Device from homeassistant.components.sensor import ( SensorDeviceClass, @@ -14,12 +14,9 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity import DeviceInfo, Entity, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import DeviceState, async_setup_entry_platform +from . import Coordinator, async_setup_entry_platform async def async_setup_entry( @@ -29,22 +26,18 @@ async def async_setup_entry( ) -> None: """Set up sensors dynamically through discovery.""" - def _constructor(device_state: DeviceState) -> list[Entity]: - return [ - RssiSensor( - device_state.coordinator, device_state.device, device_state.device_info - ) - ] + def _constructor(coordinator: Coordinator) -> list[Entity]: + return [RssiSensor(coordinator, coordinator.device, coordinator.device_info)] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class RssiSensor(CoordinatorEntity[DataUpdateCoordinator[State]], SensorEntity): +class RssiSensor(CoordinatorEntity[Coordinator], SensorEntity): """Sensor device.""" def __init__( self, - coordinator: DataUpdateCoordinator[State], + coordinator: Coordinator, device: Device, device_info: DeviceInfo, ) -> None: