Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sleepy BTHome devices #92991

Merged
merged 5 commits into from
May 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 13 additions & 15 deletions homeassistant/components/bthome/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
BluetoothScanningMode,
BluetoothServiceInfoBleak,
)
from homeassistant.components.bluetooth.passive_update_processor import (
PassiveBluetoothProcessorCoordinator,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
Expand All @@ -26,7 +23,7 @@
DOMAIN,
BTHomeBleEvent,
)
from .models import BTHomeData
from .coordinator import BTHomePassiveBluetoothProcessorCoordinator

PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]

Expand All @@ -42,7 +39,10 @@ def process_service_info(
) -> SensorUpdate:
"""Process a BluetoothServiceInfoBleak, running side effects and returning sensor data."""
update = data.update(service_info)
domain_data: BTHomeData = hass.data[DOMAIN][entry.entry_id]
coordinator: BTHomePassiveBluetoothProcessorCoordinator = hass.data[DOMAIN][
entry.entry_id
]
discovered_device_classes = coordinator.discovered_device_classes
if update.events:
address = service_info.device.address
for device_key, event in update.events.items():
Expand All @@ -59,16 +59,12 @@ def process_service_info(
event_class = event.device_key.key
event_type = event.event_type

if event_class not in domain_data.discovered_event_classes:
domain_data.discovered_event_classes.add(event_class)
if event_class not in discovered_device_classes:
discovered_device_classes.add(event_class)
hass.config_entries.async_update_entry(
entry,
data=entry.data
| {
CONF_DISCOVERED_EVENT_CLASSES: list(
domain_data.discovered_event_classes
)
},
| {CONF_DISCOVERED_EVENT_CLASSES: list(discovered_device_classes)},
)

hass.bus.async_fire(
Expand Down Expand Up @@ -104,19 +100,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
device_registry = async_get(hass)
coordinator = hass.data.setdefault(DOMAIN, {})[
entry.entry_id
] = PassiveBluetoothProcessorCoordinator(
] = BTHomePassiveBluetoothProcessorCoordinator(
hass,
_LOGGER,
address=address,
mode=BluetoothScanningMode.PASSIVE,
update_method=lambda service_info: process_service_info(
hass, entry, data, service_info, device_registry
),
device_data=data,
discovered_device_classes=set(
entry.data.get(CONF_DISCOVERED_EVENT_CLASSES, [])
),
connectable=False,
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
domain_data = BTHomeData(set(entry.data.get(CONF_DISCOVERED_EVENT_CLASSES, [])))
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = domain_data

entry.async_on_unload(
coordinator.async_start()
Expand Down
22 changes: 17 additions & 5 deletions homeassistant/components/bthome/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@
BinarySensorEntityDescription,
)
from homeassistant.components.bluetooth.passive_update_processor import (
PassiveBluetoothDataProcessor,
PassiveBluetoothDataUpdate,
PassiveBluetoothProcessorCoordinator,
PassiveBluetoothProcessorEntity,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info

from .const import DOMAIN
from .coordinator import (
BTHomePassiveBluetoothDataProcessor,
BTHomePassiveBluetoothProcessorCoordinator,
)
from .device import device_key_to_bluetooth_entity_key

BINARY_SENSOR_DESCRIPTIONS = {
Expand Down Expand Up @@ -173,10 +175,12 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the BTHome BLE binary sensors."""
coordinator: PassiveBluetoothProcessorCoordinator = hass.data[DOMAIN][
coordinator: BTHomePassiveBluetoothProcessorCoordinator = hass.data[DOMAIN][
entry.entry_id
]
processor = PassiveBluetoothDataProcessor(sensor_update_to_bluetooth_data_update)
processor = BTHomePassiveBluetoothDataProcessor(
sensor_update_to_bluetooth_data_update
)
entry.async_on_unload(
processor.async_add_entities_listener(
BTHomeBluetoothBinarySensorEntity, async_add_entities
Expand All @@ -186,7 +190,7 @@ async def async_setup_entry(


class BTHomeBluetoothBinarySensorEntity(
PassiveBluetoothProcessorEntity[PassiveBluetoothDataProcessor[bool | None]],
PassiveBluetoothProcessorEntity[BTHomePassiveBluetoothDataProcessor],
BinarySensorEntity,
):
"""Representation of a BTHome binary sensor."""
Expand All @@ -195,3 +199,11 @@ class BTHomeBluetoothBinarySensorEntity(
def is_on(self) -> bool | None:
"""Return the native value."""
return self.processor.entity_data.get(self.entity_key)

@property
def available(self) -> bool:
"""Return True if entity is available."""
coordinator: BTHomePassiveBluetoothProcessorCoordinator = (
self.processor.coordinator
)
return coordinator.device_data.sleepy_device or super().available
42 changes: 42 additions & 0 deletions homeassistant/components/bthome/coordinator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""The BTHome Bluetooth integration."""
from collections.abc import Callable
from logging import Logger
from typing import Any

from bthome_ble import BTHomeBluetoothDeviceData

from homeassistant.components.bluetooth import (
BluetoothScanningMode,
BluetoothServiceInfoBleak,
)
from homeassistant.components.bluetooth.passive_update_processor import (
PassiveBluetoothDataProcessor,
PassiveBluetoothProcessorCoordinator,
)
from homeassistant.core import HomeAssistant


class BTHomePassiveBluetoothProcessorCoordinator(PassiveBluetoothProcessorCoordinator):
"""Define a BTHome Bluetooth Passive Update Processor Coordinator."""

def __init__(
self,
hass: HomeAssistant,
logger: Logger,
address: str,
mode: BluetoothScanningMode,
update_method: Callable[[BluetoothServiceInfoBleak], Any],
device_data: BTHomeBluetoothDeviceData,
discovered_device_classes: set[str],
connectable: bool = False,
) -> None:
"""Initialize the BTHome Bluetooth Passive Update Processor Coordinator."""
super().__init__(hass, logger, address, mode, update_method, connectable)
self.discovered_device_classes = discovered_device_classes
self.device_data = device_data


class BTHomePassiveBluetoothDataProcessor(PassiveBluetoothDataProcessor):
"""Define a BTHome Bluetooth Passive Update Data Processor."""

coordinator: BTHomePassiveBluetoothProcessorCoordinator
2 changes: 1 addition & 1 deletion homeassistant/components/bthome/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/bthome",
"iot_class": "local_push",
"requirements": ["bthome-ble==2.9.0"]
"requirements": ["bthome-ble==2.11.3"]
}
11 changes: 0 additions & 11 deletions homeassistant/components/bthome/models.py

This file was deleted.

22 changes: 17 additions & 5 deletions homeassistant/components/bthome/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@

from homeassistant import config_entries
from homeassistant.components.bluetooth.passive_update_processor import (
PassiveBluetoothDataProcessor,
PassiveBluetoothDataUpdate,
PassiveBluetoothProcessorCoordinator,
PassiveBluetoothProcessorEntity,
)
from homeassistant.components.sensor import (
Expand Down Expand Up @@ -42,6 +40,10 @@
from homeassistant.helpers.sensor import sensor_device_info_to_hass_device_info

from .const import DOMAIN
from .coordinator import (
BTHomePassiveBluetoothDataProcessor,
BTHomePassiveBluetoothProcessorCoordinator,
)
from .device import device_key_to_bluetooth_entity_key

SENSOR_DESCRIPTIONS = {
Expand Down Expand Up @@ -343,10 +345,12 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the BTHome BLE sensors."""
coordinator: PassiveBluetoothProcessorCoordinator = hass.data[DOMAIN][
coordinator: BTHomePassiveBluetoothProcessorCoordinator = hass.data[DOMAIN][
entry.entry_id
]
processor = PassiveBluetoothDataProcessor(sensor_update_to_bluetooth_data_update)
processor = BTHomePassiveBluetoothDataProcessor(
sensor_update_to_bluetooth_data_update
)
entry.async_on_unload(
processor.async_add_entities_listener(
BTHomeBluetoothSensorEntity, async_add_entities
Expand All @@ -356,7 +360,7 @@ async def async_setup_entry(


class BTHomeBluetoothSensorEntity(
PassiveBluetoothProcessorEntity[PassiveBluetoothDataProcessor[float | int | None]],
PassiveBluetoothProcessorEntity[BTHomePassiveBluetoothDataProcessor],
SensorEntity,
):
"""Representation of a BTHome BLE sensor."""
Expand All @@ -365,3 +369,11 @@ class BTHomeBluetoothSensorEntity(
def native_value(self) -> int | float | None:
"""Return the native value."""
return self.processor.entity_data.get(self.entity_key)

@property
def available(self) -> bool:
"""Return True if entity is available."""
coordinator: BTHomePassiveBluetoothProcessorCoordinator = (
self.processor.coordinator
)
return coordinator.device_data.sleepy_device or super().available
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ brunt==1.2.0
bt_proximity==0.2.1

# homeassistant.components.bthome
bthome-ble==2.9.0
bthome-ble==2.11.3

# homeassistant.components.bt_home_hub_5
bthomehub5-devicelist==0.1.1
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ brottsplatskartan==0.0.1
brunt==1.2.0

# homeassistant.components.bthome
bthome-ble==2.9.0
bthome-ble==2.11.3

# homeassistant.components.buienradar
buienradar==1.0.5
Expand Down