core/homeassistant/components/fjaraskupan/__init__.py

152 lines
4.6 KiB
Python
Raw Normal View History

"""The Fjäråskupan integration."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from datetime import timedelta
import logging
from bleak import BleakScanner
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
2021-10-18 20:23:06 +00:00
from fjaraskupan import UUID_SERVICE, Device, State, device_filter
from homeassistant.config_entries import ConfigEntry
2021-12-04 12:26:40 +00:00
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DISPATCH_DETECTION, DOMAIN
2021-12-04 12:26:40 +00:00
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.FAN,
Platform.LIGHT,
Platform.NUMBER,
Platform.SENSOR,
]
_LOGGER = logging.getLogger(__name__)
@dataclass
class DeviceState:
"""Store state of a device."""
device: Device
coordinator: DataUpdateCoordinator[State]
device_info: DeviceInfo
@dataclass
class EntryState:
"""Store state of config entry."""
scanner: BleakScanner
devices: dict[str, DeviceState]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Fjäråskupan from a config entry."""
2021-10-18 20:23:06 +00:00
scanner = BleakScanner(filters={"UUIDs": [str(UUID_SERVICE)]})
state = EntryState(scanner, {})
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = state
async def detection_callback(
ble_device: BLEDevice, advertisement_data: AdvertisementData
) -> None:
2021-10-18 12:01:23 +00:00
if data := state.devices.get(ble_device.address):
2021-10-18 20:23:06 +00:00
_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)
else:
2021-10-18 20:23:06 +00:00
if not device_filter(ble_device, advertisement_data):
return
_LOGGER.debug(
"Detected: %s %s - %s", ble_device.name, ble_device, advertisement_data
)
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)
2021-10-23 10:01:21 +00:00
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
async_dispatcher_send(
hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", device_state
)
scanner.register_detection_callback(detection_callback)
await scanner.start()
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True
@callback
def async_setup_entry_platform(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
constructor: Callable[[DeviceState], 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)
)
@callback
def _detection(device_state: DeviceState) -> None:
async_add_entities(constructor(device_state))
entry.async_on_unload(
async_dispatcher_connect(
hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", _detection
)
)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
entry_state: EntryState = hass.data[DOMAIN].pop(entry.entry_id)
await entry_state.scanner.stop()
return unload_ok