core/homeassistant/components/switchbot/coordinator.py

81 lines
2.5 KiB
Python

"""Provides the switchbot DataUpdateCoordinator."""
from __future__ import annotations
import asyncio
import contextlib
import logging
from typing import TYPE_CHECKING, Any
import async_timeout
import switchbot
from homeassistant.components import bluetooth
from homeassistant.components.bluetooth.passive_update_coordinator import (
PassiveBluetoothDataUpdateCoordinator,
)
from homeassistant.core import HomeAssistant, callback
if TYPE_CHECKING:
from bleak.backends.device import BLEDevice
_LOGGER = logging.getLogger(__name__)
def flatten_sensors_data(sensor):
"""Deconstruct SwitchBot library temp object C/Fº readings from dictionary."""
if "temp" in sensor["data"]:
sensor["data"]["temperature"] = sensor["data"]["temp"]["c"]
return sensor
class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator):
"""Class to manage fetching switchbot data."""
def __init__(
self,
hass: HomeAssistant,
logger: logging.Logger,
ble_device: BLEDevice,
device: switchbot.SwitchbotDevice,
base_unique_id: str,
device_name: str,
) -> None:
"""Initialize global switchbot data updater."""
super().__init__(
hass, logger, ble_device.address, bluetooth.BluetoothScanningMode.ACTIVE
)
self.ble_device = ble_device
self.device = device
self.data: dict[str, Any] = {}
self.device_name = device_name
self.base_unique_id = base_unique_id
self._ready_event = asyncio.Event()
@callback
def _async_handle_bluetooth_event(
self,
service_info: bluetooth.BluetoothServiceInfoBleak,
change: bluetooth.BluetoothChange,
) -> None:
"""Handle a Bluetooth event."""
super()._async_handle_bluetooth_event(service_info, change)
if adv := switchbot.parse_advertisement_data(
service_info.device, service_info.advertisement
):
self.data = flatten_sensors_data(adv.data)
if "modelName" in self.data:
self._ready_event.set()
_LOGGER.debug("%s: Switchbot data: %s", self.ble_device.address, self.data)
self.device.update_from_advertisement(adv)
self.async_update_listeners()
async def async_wait_ready(self) -> bool:
"""Wait for the device to be ready."""
with contextlib.suppress(asyncio.TimeoutError):
async with async_timeout.timeout(55):
await self._ready_event.wait()
return True
return False