mirror of
https://github.com/home-assistant/core
synced 2024-07-30 21:18:57 +02:00
119 lines
3.9 KiB
Python
119 lines
3.9 KiB
Python
"""Bluetooth support for esphome."""
|
|
from __future__ import annotations
|
|
|
|
from functools import partial
|
|
import logging
|
|
|
|
from aioesphomeapi import APIClient, BluetoothProxyFeature
|
|
|
|
from homeassistant.components.bluetooth import (
|
|
HaBluetoothConnector,
|
|
async_get_advertisement_callback,
|
|
async_register_scanner,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntry
|
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
|
|
|
|
from ..entry_data import RuntimeEntryData
|
|
from .cache import ESPHomeBluetoothCache
|
|
from .client import ESPHomeClient, ESPHomeClientData
|
|
from .device import ESPHomeBluetoothDevice
|
|
from .scanner import ESPHomeScanner
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
@hass_callback
|
|
def _async_can_connect(
|
|
entry_data: RuntimeEntryData, bluetooth_device: ESPHomeBluetoothDevice, source: str
|
|
) -> bool:
|
|
"""Check if a given source can make another connection."""
|
|
can_connect = bool(entry_data.available and bluetooth_device.ble_connections_free)
|
|
_LOGGER.debug(
|
|
(
|
|
"%s [%s]: Checking can connect, available=%s, ble_connections_free=%s"
|
|
" result=%s"
|
|
),
|
|
entry_data.name,
|
|
source,
|
|
entry_data.available,
|
|
bluetooth_device.ble_connections_free,
|
|
can_connect,
|
|
)
|
|
return can_connect
|
|
|
|
|
|
async def async_connect_scanner(
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
cli: APIClient,
|
|
entry_data: RuntimeEntryData,
|
|
cache: ESPHomeBluetoothCache,
|
|
) -> CALLBACK_TYPE:
|
|
"""Connect scanner."""
|
|
assert entry.unique_id is not None
|
|
source = str(entry.unique_id)
|
|
new_info_callback = async_get_advertisement_callback(hass)
|
|
device_info = entry_data.device_info
|
|
assert device_info is not None
|
|
feature_flags = device_info.bluetooth_proxy_feature_flags_compat(
|
|
entry_data.api_version
|
|
)
|
|
connectable = bool(feature_flags & BluetoothProxyFeature.ACTIVE_CONNECTIONS)
|
|
bluetooth_device = ESPHomeBluetoothDevice(entry_data.name, device_info.mac_address)
|
|
entry_data.bluetooth_device = bluetooth_device
|
|
_LOGGER.debug(
|
|
"%s [%s]: Connecting scanner feature_flags=%s, connectable=%s",
|
|
entry.title,
|
|
source,
|
|
feature_flags,
|
|
connectable,
|
|
)
|
|
client_data = ESPHomeClientData(
|
|
bluetooth_device=bluetooth_device,
|
|
cache=cache,
|
|
client=cli,
|
|
device_info=device_info,
|
|
api_version=entry_data.api_version,
|
|
title=entry.title,
|
|
scanner=None,
|
|
disconnect_callbacks=entry_data.disconnect_callbacks,
|
|
)
|
|
connector = HaBluetoothConnector(
|
|
# MyPy doesn't like partials, but this is correct
|
|
# https://github.com/python/mypy/issues/1484
|
|
client=partial(ESPHomeClient, client_data=client_data), # type: ignore[arg-type]
|
|
source=source,
|
|
can_connect=hass_callback(
|
|
partial(_async_can_connect, entry_data, bluetooth_device, source)
|
|
),
|
|
)
|
|
scanner = ESPHomeScanner(
|
|
hass, source, entry.title, new_info_callback, connector, connectable
|
|
)
|
|
client_data.scanner = scanner
|
|
if connectable:
|
|
# If its connectable be sure not to register the scanner
|
|
# until we know the connection is fully setup since otherwise
|
|
# there is a race condition where the connection can fail
|
|
await cli.subscribe_bluetooth_connections_free(
|
|
bluetooth_device.async_update_ble_connection_limits
|
|
)
|
|
unload_callbacks = [
|
|
async_register_scanner(hass, scanner, connectable),
|
|
scanner.async_setup(),
|
|
]
|
|
if feature_flags & BluetoothProxyFeature.RAW_ADVERTISEMENTS:
|
|
await cli.subscribe_bluetooth_le_raw_advertisements(
|
|
scanner.async_on_raw_advertisements
|
|
)
|
|
else:
|
|
await cli.subscribe_bluetooth_le_advertisements(scanner.async_on_advertisement)
|
|
|
|
@hass_callback
|
|
def _async_unload() -> None:
|
|
for callback in unload_callbacks:
|
|
callback()
|
|
|
|
return _async_unload
|