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/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

View File

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

View File

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

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,
)
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."""

View File

@ -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."""

View File

@ -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."""

View File

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