mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 10:17:09 +00:00
Move BluetoothServiceInfoBleak to home_assistant_bluetooth (#82064)
This commit is contained in:
parent
c940ad9920
commit
682187541f
@ -3,7 +3,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Callable, Iterable
|
from collections.abc import Callable, Iterable
|
||||||
from dataclasses import replace
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
@ -442,7 +441,19 @@ class BluetoothManager:
|
|||||||
# route any connection attempts to the connectable path, we
|
# route any connection attempts to the connectable path, we
|
||||||
# mark the service_info as connectable so that the callbacks
|
# mark the service_info as connectable so that the callbacks
|
||||||
# will be called and the device can be discovered.
|
# will be called and the device can be discovered.
|
||||||
service_info = replace(service_info, connectable=True)
|
service_info = BluetoothServiceInfoBleak(
|
||||||
|
name=service_info.name,
|
||||||
|
address=service_info.address,
|
||||||
|
rssi=service_info.rssi,
|
||||||
|
manufacturer_data=service_info.manufacturer_data,
|
||||||
|
service_data=service_info.service_data,
|
||||||
|
service_uuids=service_info.service_uuids,
|
||||||
|
source=service_info.source,
|
||||||
|
device=service_info.device,
|
||||||
|
advertisement=service_info.advertisement,
|
||||||
|
connectable=True,
|
||||||
|
time=service_info.time,
|
||||||
|
)
|
||||||
|
|
||||||
matched_domains = self._integration_matcher.match_domains(service_info)
|
matched_domains = self._integration_matcher.match_domains(service_info)
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
|
@ -21,11 +21,14 @@ from bleak.backends.scanner import (
|
|||||||
BaseBleakScanner,
|
BaseBleakScanner,
|
||||||
)
|
)
|
||||||
from bleak_retry_connector import NO_RSSI_VALUE, freshen_ble_device
|
from bleak_retry_connector import NO_RSSI_VALUE, freshen_ble_device
|
||||||
|
from home_assistant_bluetooth import BluetoothServiceInfoBleak
|
||||||
|
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
from homeassistant.helpers.frame import report
|
from homeassistant.helpers.frame import report
|
||||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
from homeassistant.helpers.service_info.bluetooth import ( # noqa: F401 # pylint: disable=unused-import
|
||||||
|
BluetoothServiceInfo,
|
||||||
|
)
|
||||||
from homeassistant.util.dt import monotonic_time_coarse
|
from homeassistant.util.dt import monotonic_time_coarse
|
||||||
|
|
||||||
from .const import FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS
|
from .const import FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS
|
||||||
@ -53,41 +56,6 @@ MANAGER: BluetoothManager | None = None
|
|||||||
MONOTONIC_TIME: Final = monotonic_time_coarse
|
MONOTONIC_TIME: Final = monotonic_time_coarse
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class BluetoothServiceInfoBleak(BluetoothServiceInfo):
|
|
||||||
"""BluetoothServiceInfo with bleak data.
|
|
||||||
|
|
||||||
Integrations may need BLEDevice and AdvertisementData
|
|
||||||
to connect to the device without having bleak trigger
|
|
||||||
another scan to translate the address to the system's
|
|
||||||
internal details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
device: BLEDevice
|
|
||||||
advertisement: AdvertisementData
|
|
||||||
connectable: bool
|
|
||||||
time: float
|
|
||||||
|
|
||||||
def as_dict(self) -> dict[str, Any]:
|
|
||||||
"""Return as dict.
|
|
||||||
|
|
||||||
The dataclass asdict method is not used because
|
|
||||||
it will try to deepcopy pyobjc data which will fail.
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"name": self.name,
|
|
||||||
"address": self.address,
|
|
||||||
"rssi": self.rssi,
|
|
||||||
"manufacturer_data": self.manufacturer_data,
|
|
||||||
"service_data": self.service_data,
|
|
||||||
"service_uuids": self.service_uuids,
|
|
||||||
"source": self.source,
|
|
||||||
"advertisement": self.advertisement,
|
|
||||||
"connectable": self.connectable,
|
|
||||||
"time": self.time,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class BluetoothScanningMode(Enum):
|
class BluetoothScanningMode(Enum):
|
||||||
"""The mode of scanning for bluetooth devices."""
|
"""The mode of scanning for bluetooth devices."""
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
import logging
|
import logging
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ class BasePassiveBluetoothCoordinator:
|
|||||||
if service_info := async_last_service_info(
|
if service_info := async_last_service_info(
|
||||||
self.hass, self.address, self.connectable
|
self.hass, self.address, self.connectable
|
||||||
):
|
):
|
||||||
return service_info.name
|
return cast(str, service_info.name) # for compat this can be a pyobjc
|
||||||
return self._last_name
|
return self._last_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -62,7 +62,7 @@ def async_name(
|
|||||||
"""Return a name for the device."""
|
"""Return a name for the device."""
|
||||||
if service_info.address in (
|
if service_info.address in (
|
||||||
service_info.name,
|
service_info.name,
|
||||||
service_info.name.replace("_", ":"),
|
service_info.name.replace("-", ":"),
|
||||||
):
|
):
|
||||||
base_name = f"{ibeacon_advertisement.uuid}_{ibeacon_advertisement.major}_{ibeacon_advertisement.minor}"
|
base_name = f"{ibeacon_advertisement.uuid}_{ibeacon_advertisement.major}_{ibeacon_advertisement.minor}"
|
||||||
else:
|
else:
|
||||||
|
@ -21,7 +21,7 @@ cryptography==38.0.3
|
|||||||
dbus-fast==1.74.0
|
dbus-fast==1.74.0
|
||||||
fnvhash==0.1.0
|
fnvhash==0.1.0
|
||||||
hass-nabucasa==0.56.0
|
hass-nabucasa==0.56.0
|
||||||
home-assistant-bluetooth==1.6.0
|
home-assistant-bluetooth==1.8.0
|
||||||
home-assistant-frontend==20221108.0
|
home-assistant-frontend==20221108.0
|
||||||
httpx==0.23.0
|
httpx==0.23.0
|
||||||
ifaddr==0.1.7
|
ifaddr==0.1.7
|
||||||
|
@ -36,7 +36,7 @@ dependencies = [
|
|||||||
# When bumping httpx, please check the version pins of
|
# When bumping httpx, please check the version pins of
|
||||||
# httpcore, anyio, and h11 in gen_requirements_all
|
# httpcore, anyio, and h11 in gen_requirements_all
|
||||||
"httpx==0.23.0",
|
"httpx==0.23.0",
|
||||||
"home-assistant-bluetooth==1.6.0",
|
"home-assistant-bluetooth==1.8.0",
|
||||||
"ifaddr==0.1.7",
|
"ifaddr==0.1.7",
|
||||||
"jinja2==3.1.2",
|
"jinja2==3.1.2",
|
||||||
"lru-dict==1.1.8",
|
"lru-dict==1.1.8",
|
||||||
|
@ -11,7 +11,7 @@ bcrypt==3.1.7
|
|||||||
certifi>=2021.5.30
|
certifi>=2021.5.30
|
||||||
ciso8601==2.2.0
|
ciso8601==2.2.0
|
||||||
httpx==0.23.0
|
httpx==0.23.0
|
||||||
home-assistant-bluetooth==1.6.0
|
home-assistant-bluetooth==1.8.0
|
||||||
ifaddr==0.1.7
|
ifaddr==0.1.7
|
||||||
jinja2==3.1.2
|
jinja2==3.1.2
|
||||||
lru-dict==1.1.8
|
lru-dict==1.1.8
|
||||||
|
@ -227,6 +227,10 @@ async def test_diagnostics_macos(
|
|||||||
-127,
|
-127,
|
||||||
[[]],
|
[[]],
|
||||||
],
|
],
|
||||||
|
"device": {
|
||||||
|
"__type": "<class " "'bleak.backends.device.BLEDevice'>",
|
||||||
|
"repr": "BLEDevice(44:44:33:11:23:45, " "wohand)",
|
||||||
|
},
|
||||||
"connectable": True,
|
"connectable": True,
|
||||||
"manufacturer_data": {
|
"manufacturer_data": {
|
||||||
"1": {"__type": "<class " "'bytes'>", "repr": "b'\\x01'"}
|
"1": {"__type": "<class " "'bytes'>", "repr": "b'\\x01'"}
|
||||||
@ -251,6 +255,10 @@ async def test_diagnostics_macos(
|
|||||||
-127,
|
-127,
|
||||||
[[]],
|
[[]],
|
||||||
],
|
],
|
||||||
|
"device": {
|
||||||
|
"__type": "<class " "'bleak.backends.device.BLEDevice'>",
|
||||||
|
"repr": "BLEDevice(44:44:33:11:23:45, " "wohand)",
|
||||||
|
},
|
||||||
"connectable": True,
|
"connectable": True,
|
||||||
"manufacturer_data": {
|
"manufacturer_data": {
|
||||||
"1": {"__type": "<class " "'bytes'>", "repr": "b'\\x01'"}
|
"1": {"__type": "<class " "'bytes'>", "repr": "b'\\x01'"}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
"""Tests for the ibeacon integration."""
|
"""Tests for the ibeacon integration."""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from bleak.backends.device import BLEDevice
|
from bleak.backends.device import BLEDevice
|
||||||
|
|
||||||
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
from homeassistant.helpers.service_info.bluetooth import BluetoothServiceInfo
|
||||||
@ -115,3 +117,18 @@ FEASY_BEACON_SERVICE_INFO_2 = BluetoothServiceInfo(
|
|||||||
],
|
],
|
||||||
source="local",
|
source="local",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def bluetooth_service_info_replace(
|
||||||
|
info: BluetoothServiceInfo, **kwargs: Any
|
||||||
|
) -> BluetoothServiceInfo:
|
||||||
|
"""Replace attributes of a BluetoothServiceInfoBleak."""
|
||||||
|
return BluetoothServiceInfo(
|
||||||
|
address=kwargs.get("address", info.address),
|
||||||
|
name=kwargs.get("name", info.name),
|
||||||
|
rssi=kwargs.get("rssi", info.rssi),
|
||||||
|
manufacturer_data=kwargs.get("manufacturer_data", info.manufacturer_data),
|
||||||
|
service_data=kwargs.get("service_data", info.service_data),
|
||||||
|
service_uuids=kwargs.get("service_uuids", info.service_uuids),
|
||||||
|
source=kwargs.get("source", info.source),
|
||||||
|
)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Test the ibeacon sensors."""
|
"""Test the ibeacon sensors."""
|
||||||
|
|
||||||
|
|
||||||
from dataclasses import replace
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -19,6 +18,7 @@ from . import (
|
|||||||
BLUECHARM_BEACON_SERVICE_INFO_DBUS,
|
BLUECHARM_BEACON_SERVICE_INFO_DBUS,
|
||||||
TESLA_TRANSIENT,
|
TESLA_TRANSIENT,
|
||||||
TESLA_TRANSIENT_BLE_DEVICE,
|
TESLA_TRANSIENT_BLE_DEVICE,
|
||||||
|
bluetooth_service_info_replace as replace,
|
||||||
)
|
)
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
@ -145,16 +145,17 @@ async def test_ignore_default_name(hass):
|
|||||||
assert len(hass.states.async_entity_ids()) == before_entity_count
|
assert len(hass.states.async_entity_ids()) == before_entity_count
|
||||||
|
|
||||||
|
|
||||||
async def test_rotating_major_minor_and_mac(hass):
|
async def test_rotating_major_minor_and_mac_with_name(hass):
|
||||||
"""Test the different uuid, major, minor from many addresses removes all associated entities."""
|
"""Test the different uuid, major, minor from many addresses removes all associated entities."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
before_entity_count = len(hass.states.async_entity_ids("device_tracker"))
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
before_entity_count = len(hass.states.async_entity_ids("device_tracker"))
|
||||||
|
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
service_info = BluetoothServiceInfo(
|
service_info = BluetoothServiceInfo(
|
||||||
name="BlueCharm_177999",
|
name="BlueCharm_177999",
|
||||||
@ -186,9 +187,10 @@ async def test_rotating_major_minor_and_mac_no_name(hass):
|
|||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
before_entity_count = len(hass.states.async_entity_ids("device_tracker"))
|
|
||||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
before_entity_count = len(hass.states.async_entity_ids("device_tracker"))
|
||||||
|
|
||||||
for i in range(51):
|
for i in range(51):
|
||||||
service_info = BluetoothServiceInfo(
|
service_info = BluetoothServiceInfo(
|
||||||
name=f"AA:BB:CC:DD:EE:{i:02X}",
|
name=f"AA:BB:CC:DD:EE:{i:02X}",
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Test the ibeacon device trackers."""
|
"""Test the ibeacon device trackers."""
|
||||||
|
|
||||||
|
|
||||||
from dataclasses import replace
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import time
|
import time
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
@ -22,6 +21,7 @@ from . import (
|
|||||||
BEACON_RANDOM_ADDRESS_SERVICE_INFO,
|
BEACON_RANDOM_ADDRESS_SERVICE_INFO,
|
||||||
BLUECHARM_BEACON_SERVICE_INFO,
|
BLUECHARM_BEACON_SERVICE_INFO,
|
||||||
BLUECHARM_BLE_DEVICE,
|
BLUECHARM_BLE_DEVICE,
|
||||||
|
bluetooth_service_info_replace as replace,
|
||||||
)
|
)
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Test the ibeacon sensors."""
|
"""Test the ibeacon sensors."""
|
||||||
|
|
||||||
|
|
||||||
from dataclasses import replace
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -24,6 +23,7 @@ from . import (
|
|||||||
FEASY_BEACON_SERVICE_INFO_1,
|
FEASY_BEACON_SERVICE_INFO_1,
|
||||||
FEASY_BEACON_SERVICE_INFO_2,
|
FEASY_BEACON_SERVICE_INFO_2,
|
||||||
NO_NAME_BEACON_SERVICE_INFO,
|
NO_NAME_BEACON_SERVICE_INFO,
|
||||||
|
bluetooth_service_info_replace as replace,
|
||||||
)
|
)
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
Loading…
x
Reference in New Issue
Block a user