From 2747da784c372e8cda40468f684149b711020402 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Wed, 28 Jun 2023 10:41:50 +0200 Subject: [PATCH] Move Fjaraskupan coordinator to separate file (#95342) * Move coordinator to separate file * Move coordinator to separate file * Move coordinator to separate file --- .coveragerc | 1 + .../components/fjaraskupan/__init__.py | 96 ++----------------- .../components/fjaraskupan/binary_sensor.py | 9 +- .../components/fjaraskupan/coordinator.py | 95 ++++++++++++++++++ homeassistant/components/fjaraskupan/fan.py | 9 +- homeassistant/components/fjaraskupan/light.py | 9 +- .../components/fjaraskupan/number.py | 9 +- .../components/fjaraskupan/sensor.py | 9 +- 8 files changed, 130 insertions(+), 107 deletions(-) create mode 100644 homeassistant/components/fjaraskupan/coordinator.py diff --git a/.coveragerc b/.coveragerc index 33a84516165..d05fff927e1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -361,6 +361,7 @@ omit = homeassistant/components/fixer/sensor.py homeassistant/components/fjaraskupan/__init__.py homeassistant/components/fjaraskupan/binary_sensor.py + homeassistant/components/fjaraskupan/coordinator.py homeassistant/components/fjaraskupan/fan.py homeassistant/components/fjaraskupan/light.py homeassistant/components/fjaraskupan/number.py diff --git a/homeassistant/components/fjaraskupan/__init__.py b/homeassistant/components/fjaraskupan/__init__.py index 044d17662cf..e867e624e8a 100644 --- a/homeassistant/components/fjaraskupan/__init__.py +++ b/homeassistant/components/fjaraskupan/__init__.py @@ -1,28 +1,23 @@ """The Fjäråskupan integration.""" from __future__ import annotations -from collections.abc import AsyncIterator, Callable -from contextlib import asynccontextmanager +from collections.abc import Callable from dataclasses import dataclass -from datetime import timedelta import logging -from fjaraskupan import Device, State +from fjaraskupan import Device from homeassistant.components.bluetooth import ( BluetoothCallbackMatcher, BluetoothChange, BluetoothScanningMode, BluetoothServiceInfoBleak, - async_address_present, - async_ble_device_from_address, async_rediscover_address, async_register_callback, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import device_registry as dr from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, @@ -30,14 +25,9 @@ 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, UpdateFailed from .const import DISPATCH_DETECTION, DOMAIN - - -class UnableToConnect(HomeAssistantError): - """Exception to indicate that we cannot connect to device.""" - +from .coordinator import FjaraskupanCoordinator PLATFORMS = [ Platform.BINARY_SENSOR, @@ -50,81 +40,11 @@ PLATFORMS = [ _LOGGER = logging.getLogger(__name__) -class Coordinator(DataUpdateCoordinator[State]): - """Update coordinator for each device.""" - - 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, - raise_on_entry_error: 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, - raise_on_entry_error=raise_on_entry_error, - ) - - async def _async_update_data(self) -> State: - """Handle an explicit update request.""" - if self._refresh_was_scheduled: - if async_address_present(self.hass, self.device.address, False): - return self.device.state - raise UpdateFailed( - "No data received within schedule, and device is no longer present" - ) - - if ( - ble_device := async_ble_device_from_address( - self.hass, self.device.address, True - ) - ) is None: - raise UpdateFailed("No connectable path to device") - async with self.device.connect(ble_device) as device: - await device.update() - return self.device.state - - def detection_callback(self, service_info: BluetoothServiceInfoBleak) -> None: - """Handle a new announcement of data.""" - self.device.detection_callback(service_info.device, service_info.advertisement) - self.async_set_updated_data(self.device.state) - - @asynccontextmanager - async def async_connect_and_update(self) -> AsyncIterator[Device]: - """Provide an up to date device for use during connections.""" - if ( - ble_device := async_ble_device_from_address( - self.hass, self.device.address, True - ) - ) is None: - raise UnableToConnect("No connectable path to device") - - async with self.device.connect(ble_device) as device: - yield device - - self.async_set_updated_data(self.device.state) - - @dataclass class EntryState: """Store state of config entry.""" - coordinators: dict[str, Coordinator] + coordinators: dict[str, FjaraskupanCoordinator] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @@ -153,7 +73,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: name="Fjäråskupan", ) - coordinator: Coordinator = Coordinator(hass, device, device_info) + coordinator: FjaraskupanCoordinator = FjaraskupanCoordinator( + hass, device, device_info + ) coordinator.detection_callback(service_info) state.coordinators[service_info.address] = coordinator @@ -183,7 +105,7 @@ def async_setup_entry_platform( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback, - constructor: Callable[[Coordinator], list[Entity]], + constructor: Callable[[FjaraskupanCoordinator], list[Entity]], ) -> None: """Set up a platform with added entities.""" @@ -195,7 +117,7 @@ def async_setup_entry_platform( ) @callback - def _detection(coordinator: Coordinator) -> None: + def _detection(coordinator: FjaraskupanCoordinator) -> None: async_add_entities(constructor(coordinator)) entry.async_on_unload( diff --git a/homeassistant/components/fjaraskupan/binary_sensor.py b/homeassistant/components/fjaraskupan/binary_sensor.py index 407bf663f70..8b641013eb4 100644 --- a/homeassistant/components/fjaraskupan/binary_sensor.py +++ b/homeassistant/components/fjaraskupan/binary_sensor.py @@ -17,7 +17,8 @@ from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import Coordinator, async_setup_entry_platform +from . import async_setup_entry_platform +from .coordinator import FjaraskupanCoordinator @dataclass @@ -50,7 +51,7 @@ async def async_setup_entry( ) -> None: """Set up sensors dynamically through discovery.""" - def _constructor(coordinator: Coordinator) -> list[Entity]: + def _constructor(coordinator: FjaraskupanCoordinator) -> list[Entity]: return [ BinarySensor( coordinator, @@ -64,7 +65,7 @@ async def async_setup_entry( async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class BinarySensor(CoordinatorEntity[Coordinator], BinarySensorEntity): +class BinarySensor(CoordinatorEntity[FjaraskupanCoordinator], BinarySensorEntity): """Grease filter sensor.""" entity_description: EntityDescription @@ -72,7 +73,7 @@ class BinarySensor(CoordinatorEntity[Coordinator], BinarySensorEntity): def __init__( self, - coordinator: Coordinator, + coordinator: FjaraskupanCoordinator, device: Device, device_info: DeviceInfo, entity_description: EntityDescription, diff --git a/homeassistant/components/fjaraskupan/coordinator.py b/homeassistant/components/fjaraskupan/coordinator.py new file mode 100644 index 00000000000..16e8157b094 --- /dev/null +++ b/homeassistant/components/fjaraskupan/coordinator.py @@ -0,0 +1,95 @@ +"""The Fjäråskupan data update coordinator.""" +from __future__ import annotations + +from collections.abc import AsyncIterator +from contextlib import asynccontextmanager +from datetime import timedelta +import logging + +from fjaraskupan import Device, State + +from homeassistant.components.bluetooth import ( + BluetoothServiceInfoBleak, + async_address_present, + async_ble_device_from_address, +) +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed + +_LOGGER = logging.getLogger(__name__) + + +class UnableToConnect(HomeAssistantError): + """Exception to indicate that we cannot connect to device.""" + + +class FjaraskupanCoordinator(DataUpdateCoordinator[State]): + """Update coordinator for each device.""" + + 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, + raise_on_entry_error: 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, + raise_on_entry_error=raise_on_entry_error, + ) + + async def _async_update_data(self) -> State: + """Handle an explicit update request.""" + if self._refresh_was_scheduled: + if async_address_present(self.hass, self.device.address, False): + return self.device.state + raise UpdateFailed( + "No data received within schedule, and device is no longer present" + ) + + if ( + ble_device := async_ble_device_from_address( + self.hass, self.device.address, True + ) + ) is None: + raise UpdateFailed("No connectable path to device") + async with self.device.connect(ble_device) as device: + await device.update() + return self.device.state + + def detection_callback(self, service_info: BluetoothServiceInfoBleak) -> None: + """Handle a new announcement of data.""" + self.device.detection_callback(service_info.device, service_info.advertisement) + self.async_set_updated_data(self.device.state) + + @asynccontextmanager + async def async_connect_and_update(self) -> AsyncIterator[Device]: + """Provide an up-to-date device for use during connections.""" + if ( + ble_device := async_ble_device_from_address( + self.hass, self.device.address, True + ) + ) is None: + raise UnableToConnect("No connectable path to device") + + async with self.device.connect(ble_device) as device: + yield device + + self.async_set_updated_data(self.device.state) diff --git a/homeassistant/components/fjaraskupan/fan.py b/homeassistant/components/fjaraskupan/fan.py index c78214ad429..e19a0965524 100644 --- a/homeassistant/components/fjaraskupan/fan.py +++ b/homeassistant/components/fjaraskupan/fan.py @@ -23,7 +23,8 @@ from homeassistant.util.percentage import ( percentage_to_ordered_list_item, ) -from . import Coordinator, async_setup_entry_platform +from . import async_setup_entry_platform +from .coordinator import FjaraskupanCoordinator ORDERED_NAMED_FAN_SPEEDS = ["1", "2", "3", "4", "5", "6", "7", "8"] @@ -54,13 +55,13 @@ async def async_setup_entry( ) -> None: """Set up sensors dynamically through discovery.""" - def _constructor(coordinator: Coordinator): + def _constructor(coordinator: FjaraskupanCoordinator): return [Fan(coordinator, coordinator.device_info)] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class Fan(CoordinatorEntity[Coordinator], FanEntity): +class Fan(CoordinatorEntity[FjaraskupanCoordinator], FanEntity): """Fan entity.""" _attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE @@ -69,7 +70,7 @@ class Fan(CoordinatorEntity[Coordinator], FanEntity): def __init__( self, - coordinator: Coordinator, + coordinator: FjaraskupanCoordinator, device_info: DeviceInfo, ) -> None: """Init fan entity.""" diff --git a/homeassistant/components/fjaraskupan/light.py b/homeassistant/components/fjaraskupan/light.py index 59f69759291..f4aa8c5a2dc 100644 --- a/homeassistant/components/fjaraskupan/light.py +++ b/homeassistant/components/fjaraskupan/light.py @@ -12,7 +12,8 @@ from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import Coordinator, async_setup_entry_platform +from . import async_setup_entry_platform +from .coordinator import FjaraskupanCoordinator async def async_setup_entry( @@ -22,13 +23,13 @@ async def async_setup_entry( ) -> None: """Set up tuya sensors dynamically through tuya discovery.""" - def _constructor(coordinator: Coordinator) -> list[Entity]: + def _constructor(coordinator: FjaraskupanCoordinator) -> list[Entity]: return [Light(coordinator, coordinator.device_info)] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class Light(CoordinatorEntity[Coordinator], LightEntity): +class Light(CoordinatorEntity[FjaraskupanCoordinator], LightEntity): """Light device.""" _attr_has_entity_name = True @@ -36,7 +37,7 @@ class Light(CoordinatorEntity[Coordinator], LightEntity): def __init__( self, - coordinator: Coordinator, + coordinator: FjaraskupanCoordinator, device_info: DeviceInfo, ) -> None: """Init light entity.""" diff --git a/homeassistant/components/fjaraskupan/number.py b/homeassistant/components/fjaraskupan/number.py index 0659aea9f14..46c5f6db90b 100644 --- a/homeassistant/components/fjaraskupan/number.py +++ b/homeassistant/components/fjaraskupan/number.py @@ -9,7 +9,8 @@ from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import Coordinator, async_setup_entry_platform +from . import async_setup_entry_platform +from .coordinator import FjaraskupanCoordinator async def async_setup_entry( @@ -19,7 +20,7 @@ async def async_setup_entry( ) -> None: """Set up number entities dynamically through discovery.""" - def _constructor(coordinator: Coordinator) -> list[Entity]: + def _constructor(coordinator: FjaraskupanCoordinator) -> list[Entity]: return [ PeriodicVentingTime(coordinator, coordinator.device_info), ] @@ -27,7 +28,7 @@ async def async_setup_entry( async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class PeriodicVentingTime(CoordinatorEntity[Coordinator], NumberEntity): +class PeriodicVentingTime(CoordinatorEntity[FjaraskupanCoordinator], NumberEntity): """Periodic Venting.""" _attr_has_entity_name = True @@ -41,7 +42,7 @@ class PeriodicVentingTime(CoordinatorEntity[Coordinator], NumberEntity): def __init__( self, - coordinator: Coordinator, + coordinator: FjaraskupanCoordinator, device_info: DeviceInfo, ) -> None: """Init number entities.""" diff --git a/homeassistant/components/fjaraskupan/sensor.py b/homeassistant/components/fjaraskupan/sensor.py index 0ffcfa9798b..e9bf84e0ed0 100644 --- a/homeassistant/components/fjaraskupan/sensor.py +++ b/homeassistant/components/fjaraskupan/sensor.py @@ -16,7 +16,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import Coordinator, async_setup_entry_platform +from . import async_setup_entry_platform +from .coordinator import FjaraskupanCoordinator async def async_setup_entry( @@ -26,13 +27,13 @@ async def async_setup_entry( ) -> None: """Set up sensors dynamically through discovery.""" - def _constructor(coordinator: Coordinator) -> list[Entity]: + def _constructor(coordinator: FjaraskupanCoordinator) -> list[Entity]: return [RssiSensor(coordinator, coordinator.device, coordinator.device_info)] async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) -class RssiSensor(CoordinatorEntity[Coordinator], SensorEntity): +class RssiSensor(CoordinatorEntity[FjaraskupanCoordinator], SensorEntity): """Sensor device.""" _attr_has_entity_name = True @@ -44,7 +45,7 @@ class RssiSensor(CoordinatorEntity[Coordinator], SensorEntity): def __init__( self, - coordinator: Coordinator, + coordinator: FjaraskupanCoordinator, device: Device, device_info: DeviceInfo, ) -> None: