mirror of https://github.com/home-assistant/core
158 lines
5.2 KiB
Python
158 lines
5.2 KiB
Python
"""Thermopro button platform."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Coroutine
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from thermopro_ble import SensorUpdate, ThermoProBluetoothDeviceData, ThermoProDevice
|
|
|
|
from homeassistant.components.bluetooth import (
|
|
BluetoothServiceInfoBleak,
|
|
async_ble_device_from_address,
|
|
async_track_unavailable,
|
|
)
|
|
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.const import EntityCategory
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.helpers import device_registry as dr
|
|
from homeassistant.helpers.dispatcher import (
|
|
async_dispatcher_connect,
|
|
async_dispatcher_send,
|
|
)
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
from homeassistant.util.dt import now
|
|
|
|
from .const import DOMAIN, SIGNAL_AVAILABILITY_UPDATED, SIGNAL_DATA_UPDATED
|
|
|
|
PARALLEL_UPDATES = 1 # one connection at a time
|
|
|
|
|
|
@dataclass(kw_only=True, frozen=True)
|
|
class ThermoProButtonEntityDescription(ButtonEntityDescription):
|
|
"""Describe a ThermoPro button entity."""
|
|
|
|
press_action_fn: Callable[[HomeAssistant, str], Coroutine[None, Any, Any]]
|
|
|
|
|
|
async def _async_set_datetime(hass: HomeAssistant, address: str) -> None:
|
|
"""Set Date&Time for a given device."""
|
|
ble_device = async_ble_device_from_address(hass, address, connectable=True)
|
|
assert ble_device is not None
|
|
await ThermoProDevice(ble_device).set_datetime(now(), am_pm=False)
|
|
|
|
|
|
BUTTON_ENTITIES: tuple[ThermoProButtonEntityDescription, ...] = (
|
|
ThermoProButtonEntityDescription(
|
|
key="datetime",
|
|
translation_key="set_datetime",
|
|
icon="mdi:calendar-clock",
|
|
entity_category=EntityCategory.CONFIG,
|
|
press_action_fn=_async_set_datetime,
|
|
),
|
|
)
|
|
|
|
MODELS_THAT_SUPPORT_BUTTONS = {"TP358", "TP393"}
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the thermopro button platform."""
|
|
address = entry.unique_id
|
|
assert address is not None
|
|
availability_signal = f"{SIGNAL_AVAILABILITY_UPDATED}_{entry.entry_id}"
|
|
entity_added = False
|
|
|
|
@callback
|
|
def _async_on_data_updated(
|
|
data: ThermoProBluetoothDeviceData,
|
|
service_info: BluetoothServiceInfoBleak,
|
|
update: SensorUpdate,
|
|
) -> None:
|
|
nonlocal entity_added
|
|
sensor_device_info = update.devices[data.primary_device_id]
|
|
if sensor_device_info.model not in MODELS_THAT_SUPPORT_BUTTONS:
|
|
return
|
|
|
|
if not entity_added:
|
|
name = sensor_device_info.name
|
|
assert name is not None
|
|
entity_added = True
|
|
async_add_entities(
|
|
ThermoProButtonEntity(
|
|
description=description,
|
|
data=data,
|
|
availability_signal=availability_signal,
|
|
address=address,
|
|
)
|
|
for description in BUTTON_ENTITIES
|
|
)
|
|
|
|
if service_info.connectable:
|
|
async_dispatcher_send(hass, availability_signal, True)
|
|
|
|
entry.async_on_unload(
|
|
async_dispatcher_connect(
|
|
hass, f"{SIGNAL_DATA_UPDATED}_{entry.entry_id}", _async_on_data_updated
|
|
)
|
|
)
|
|
|
|
|
|
class ThermoProButtonEntity(ButtonEntity):
|
|
"""Representation of a ThermoPro button entity."""
|
|
|
|
_attr_has_entity_name = True
|
|
entity_description: ThermoProButtonEntityDescription
|
|
|
|
def __init__(
|
|
self,
|
|
description: ThermoProButtonEntityDescription,
|
|
data: ThermoProBluetoothDeviceData,
|
|
availability_signal: str,
|
|
address: str,
|
|
) -> None:
|
|
"""Initialize the thermopro button entity."""
|
|
self.entity_description = description
|
|
self._address = address
|
|
self._availability_signal = availability_signal
|
|
self._attr_unique_id = f"{address}-{description.key}"
|
|
self._attr_device_info = dr.DeviceInfo(
|
|
name=data.get_device_name(),
|
|
identifiers={(DOMAIN, address)},
|
|
connections={(dr.CONNECTION_BLUETOOTH, address)},
|
|
)
|
|
|
|
async def async_added_to_hass(self) -> None:
|
|
"""Connect availability dispatcher."""
|
|
await super().async_added_to_hass()
|
|
self.async_on_remove(
|
|
async_dispatcher_connect(
|
|
self.hass,
|
|
self._availability_signal,
|
|
self._async_on_availability_changed,
|
|
)
|
|
)
|
|
self.async_on_remove(
|
|
async_track_unavailable(
|
|
self.hass, self._async_on_unavailable, self._address, connectable=True
|
|
)
|
|
)
|
|
|
|
@callback
|
|
def _async_on_unavailable(self, _: BluetoothServiceInfoBleak) -> None:
|
|
self._async_on_availability_changed(False)
|
|
|
|
@callback
|
|
def _async_on_availability_changed(self, available: bool) -> None:
|
|
self._attr_available = available
|
|
self.async_write_ha_state()
|
|
|
|
async def async_press(self) -> None:
|
|
"""Execute the press action for the entity."""
|
|
await self.entity_description.press_action_fn(self.hass, self._address)
|