1
mirror of https://github.com/home-assistant/core synced 2024-09-25 00:41:32 +02:00

Move Fjaraskupan coordinator to separate file (#95342)

* Move coordinator to separate file

* Move coordinator to separate file

* Move coordinator to separate file
This commit is contained in:
Joost Lekkerkerker 2023-06-28 10:41:50 +02:00 committed by GitHub
parent 98a94fea99
commit 2747da784c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 130 additions and 107 deletions

View File

@ -361,6 +361,7 @@ omit =
homeassistant/components/fixer/sensor.py homeassistant/components/fixer/sensor.py
homeassistant/components/fjaraskupan/__init__.py homeassistant/components/fjaraskupan/__init__.py
homeassistant/components/fjaraskupan/binary_sensor.py homeassistant/components/fjaraskupan/binary_sensor.py
homeassistant/components/fjaraskupan/coordinator.py
homeassistant/components/fjaraskupan/fan.py homeassistant/components/fjaraskupan/fan.py
homeassistant/components/fjaraskupan/light.py homeassistant/components/fjaraskupan/light.py
homeassistant/components/fjaraskupan/number.py homeassistant/components/fjaraskupan/number.py

View File

@ -1,28 +1,23 @@
"""The Fjäråskupan integration.""" """The Fjäråskupan integration."""
from __future__ import annotations from __future__ import annotations
from collections.abc import AsyncIterator, Callable from collections.abc import Callable
from contextlib import asynccontextmanager
from dataclasses import dataclass from dataclasses import dataclass
from datetime import timedelta
import logging import logging
from fjaraskupan import Device, State from fjaraskupan import Device
from homeassistant.components.bluetooth import ( from homeassistant.components.bluetooth import (
BluetoothCallbackMatcher, BluetoothCallbackMatcher,
BluetoothChange, BluetoothChange,
BluetoothScanningMode, BluetoothScanningMode,
BluetoothServiceInfoBleak, BluetoothServiceInfoBleak,
async_address_present,
async_ble_device_from_address,
async_rediscover_address, async_rediscover_address,
async_register_callback, async_register_callback,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.dispatcher import ( from homeassistant.helpers.dispatcher import (
async_dispatcher_connect, async_dispatcher_connect,
@ -30,14 +25,9 @@ from homeassistant.helpers.dispatcher import (
) )
from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DISPATCH_DETECTION, DOMAIN from .const import DISPATCH_DETECTION, DOMAIN
from .coordinator import FjaraskupanCoordinator
class UnableToConnect(HomeAssistantError):
"""Exception to indicate that we cannot connect to device."""
PLATFORMS = [ PLATFORMS = [
Platform.BINARY_SENSOR, Platform.BINARY_SENSOR,
@ -50,81 +40,11 @@ PLATFORMS = [
_LOGGER = logging.getLogger(__name__) _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 @dataclass
class EntryState: class EntryState:
"""Store state of config entry.""" """Store state of config entry."""
coordinators: dict[str, Coordinator] coordinators: dict[str, FjaraskupanCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: 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", name="Fjäråskupan",
) )
coordinator: Coordinator = Coordinator(hass, device, device_info) coordinator: FjaraskupanCoordinator = FjaraskupanCoordinator(
hass, device, device_info
)
coordinator.detection_callback(service_info) coordinator.detection_callback(service_info)
state.coordinators[service_info.address] = coordinator state.coordinators[service_info.address] = coordinator
@ -183,7 +105,7 @@ def async_setup_entry_platform(
hass: HomeAssistant, hass: HomeAssistant,
entry: ConfigEntry, entry: ConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
constructor: Callable[[Coordinator], list[Entity]], constructor: Callable[[FjaraskupanCoordinator], list[Entity]],
) -> None: ) -> None:
"""Set up a platform with added entities.""" """Set up a platform with added entities."""
@ -195,7 +117,7 @@ def async_setup_entry_platform(
) )
@callback @callback
def _detection(coordinator: Coordinator) -> None: def _detection(coordinator: FjaraskupanCoordinator) -> None:
async_add_entities(constructor(coordinator)) async_add_entities(constructor(coordinator))
entry.async_on_unload( entry.async_on_unload(

View File

@ -17,7 +17,8 @@ from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity 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 @dataclass
@ -50,7 +51,7 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up sensors dynamically through discovery.""" """Set up sensors dynamically through discovery."""
def _constructor(coordinator: Coordinator) -> list[Entity]: def _constructor(coordinator: FjaraskupanCoordinator) -> list[Entity]:
return [ return [
BinarySensor( BinarySensor(
coordinator, coordinator,
@ -64,7 +65,7 @@ async def async_setup_entry(
async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor)
class BinarySensor(CoordinatorEntity[Coordinator], BinarySensorEntity): class BinarySensor(CoordinatorEntity[FjaraskupanCoordinator], BinarySensorEntity):
"""Grease filter sensor.""" """Grease filter sensor."""
entity_description: EntityDescription entity_description: EntityDescription
@ -72,7 +73,7 @@ class BinarySensor(CoordinatorEntity[Coordinator], BinarySensorEntity):
def __init__( def __init__(
self, self,
coordinator: Coordinator, coordinator: FjaraskupanCoordinator,
device: Device, device: Device,
device_info: DeviceInfo, device_info: DeviceInfo,
entity_description: EntityDescription, entity_description: EntityDescription,

View File

@ -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)

View File

@ -23,7 +23,8 @@ from homeassistant.util.percentage import (
percentage_to_ordered_list_item, 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"] ORDERED_NAMED_FAN_SPEEDS = ["1", "2", "3", "4", "5", "6", "7", "8"]
@ -54,13 +55,13 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up sensors dynamically through discovery.""" """Set up sensors dynamically through discovery."""
def _constructor(coordinator: Coordinator): def _constructor(coordinator: FjaraskupanCoordinator):
return [Fan(coordinator, coordinator.device_info)] return [Fan(coordinator, coordinator.device_info)]
async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor)
class Fan(CoordinatorEntity[Coordinator], FanEntity): class Fan(CoordinatorEntity[FjaraskupanCoordinator], FanEntity):
"""Fan entity.""" """Fan entity."""
_attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE _attr_supported_features = FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE
@ -69,7 +70,7 @@ class Fan(CoordinatorEntity[Coordinator], FanEntity):
def __init__( def __init__(
self, self,
coordinator: Coordinator, coordinator: FjaraskupanCoordinator,
device_info: DeviceInfo, device_info: DeviceInfo,
) -> None: ) -> None:
"""Init fan entity.""" """Init fan entity."""

View File

@ -12,7 +12,8 @@ from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity 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( async def async_setup_entry(
@ -22,13 +23,13 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up tuya sensors dynamically through tuya discovery.""" """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)] return [Light(coordinator, coordinator.device_info)]
async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor)
class Light(CoordinatorEntity[Coordinator], LightEntity): class Light(CoordinatorEntity[FjaraskupanCoordinator], LightEntity):
"""Light device.""" """Light device."""
_attr_has_entity_name = True _attr_has_entity_name = True
@ -36,7 +37,7 @@ class Light(CoordinatorEntity[Coordinator], LightEntity):
def __init__( def __init__(
self, self,
coordinator: Coordinator, coordinator: FjaraskupanCoordinator,
device_info: DeviceInfo, device_info: DeviceInfo,
) -> None: ) -> None:
"""Init light entity.""" """Init light entity."""

View File

@ -9,7 +9,8 @@ from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity 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( async def async_setup_entry(
@ -19,7 +20,7 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up number entities dynamically through discovery.""" """Set up number entities dynamically through discovery."""
def _constructor(coordinator: Coordinator) -> list[Entity]: def _constructor(coordinator: FjaraskupanCoordinator) -> list[Entity]:
return [ return [
PeriodicVentingTime(coordinator, coordinator.device_info), 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) async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor)
class PeriodicVentingTime(CoordinatorEntity[Coordinator], NumberEntity): class PeriodicVentingTime(CoordinatorEntity[FjaraskupanCoordinator], NumberEntity):
"""Periodic Venting.""" """Periodic Venting."""
_attr_has_entity_name = True _attr_has_entity_name = True
@ -41,7 +42,7 @@ class PeriodicVentingTime(CoordinatorEntity[Coordinator], NumberEntity):
def __init__( def __init__(
self, self,
coordinator: Coordinator, coordinator: FjaraskupanCoordinator,
device_info: DeviceInfo, device_info: DeviceInfo,
) -> None: ) -> None:
"""Init number entities.""" """Init number entities."""

View File

@ -16,7 +16,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity 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( async def async_setup_entry(
@ -26,13 +27,13 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up sensors dynamically through discovery.""" """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)] return [RssiSensor(coordinator, coordinator.device, coordinator.device_info)]
async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor) async_setup_entry_platform(hass, config_entry, async_add_entities, _constructor)
class RssiSensor(CoordinatorEntity[Coordinator], SensorEntity): class RssiSensor(CoordinatorEntity[FjaraskupanCoordinator], SensorEntity):
"""Sensor device.""" """Sensor device."""
_attr_has_entity_name = True _attr_has_entity_name = True
@ -44,7 +45,7 @@ class RssiSensor(CoordinatorEntity[Coordinator], SensorEntity):
def __init__( def __init__(
self, self,
coordinator: Coordinator, coordinator: FjaraskupanCoordinator,
device: Device, device: Device,
device_info: DeviceInfo, device_info: DeviceInfo,
) -> None: ) -> None: