Skip to content

Commit 0b558f1

Browse files
authored
feat: include scanner type in details (#284)
1 parent eb6868e commit 0b558f1

File tree

8 files changed

+318
-6
lines changed

8 files changed

+318
-6
lines changed

poetry.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ python = ">=3.11,<3.14"
3838
bleak = ">=1.0.1"
3939
bleak-retry-connector = ">=4.2.0"
4040
bluetooth-data-tools = ">=1.28.0"
41-
bluetooth-adapters = ">=2"
41+
bluetooth-adapters = ">=2.1.0"
4242
bluetooth-auto-recovery = ">=1.5.1"
4343
async-interrupt = ">=1.1.1"
4444
dbus-fast = { version = ">=2.30.2", markers = "platform_system == 'Linux'" }

src/habluetooth/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
HaScannerModeChange,
2424
HaScannerRegistration,
2525
HaScannerRegistrationEvent,
26+
HaScannerType,
2627
)
2728
from .scanner import BluetoothScanningMode, HaScanner, ScannerStartError
2829
from .scanner_device import BluetoothScannerDevice
@@ -63,6 +64,7 @@
6364
"HaScannerModeChange",
6465
"HaScannerRegistration",
6566
"HaScannerRegistrationEvent",
67+
"HaScannerType",
6668
"ScannerStartError",
6769
"discovered_device_advertisement_data_from_dict",
6870
"discovered_device_advertisement_data_to_dict",

src/habluetooth/base_scanner.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
BluetoothServiceInfoBleak,
2828
HaBluetoothConnector,
2929
HaScannerDetails,
30+
HaScannerType,
3031
)
3132
from .scanner_device import BluetoothScannerDevice
3233
from .storage import DiscoveredDeviceAdvertisementData
@@ -92,11 +93,26 @@ def __init__(
9293
self._cancel_watchdog: asyncio.TimerHandle | None = None
9394
self._loop: asyncio.AbstractEventLoop | None = None
9495
self._manager = get_manager()
96+
# Determine scanner type based on class type
97+
scanner_type = HaScannerType.UNKNOWN
98+
if isinstance(self, BaseHaRemoteScanner):
99+
scanner_type = HaScannerType.REMOTE
100+
# Try to get adapter type from manager's cached adapters
101+
elif (
102+
(adapters := self._manager.get_cached_bluetooth_adapters())
103+
and (adapter_details := adapters.get(adapter))
104+
and (adapter_type := adapter_details.get("adapter_type"))
105+
):
106+
if adapter_type == "usb":
107+
scanner_type = HaScannerType.USB
108+
elif adapter_type == "uart":
109+
scanner_type = HaScannerType.UART
95110
self.details = HaScannerDetails(
96111
source=self.source,
97112
connectable=self.connectable,
98113
name=self.name,
99114
adapter=self.adapter,
115+
scanner_type=scanner_type,
100116
)
101117
self._previous_service_info: dict[str, BluetoothServiceInfoBleak] = {}
102118
# Scanners only care about connectable devices. The manager

src/habluetooth/manager.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ async def _async_refresh_adapters(self) -> None:
281281
self._adapter_refresh_future.set_result(None)
282282
self._adapter_refresh_future = None
283283

284+
def get_cached_bluetooth_adapters(self) -> dict[str, AdapterDetails] | None:
285+
"""Get cached bluetooth adapters synchronously."""
286+
return self._adapters
287+
284288
async def async_get_bluetooth_adapters(
285289
self, cached: bool = True
286290
) -> dict[str, AdapterDetails]:

src/habluetooth/models.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ class HaScannerDetails:
8282
connectable: bool
8383
name: str
8484
adapter: str
85+
scanner_type: HaScannerType
86+
87+
88+
class HaScannerType(Enum):
89+
"""The type of scanner."""
90+
91+
USB = "usb"
92+
UART = "uart"
93+
REMOTE = "remote"
94+
UNKNOWN = "unknown"
8595

8696

8797
class BluetoothScanningMode(Enum):

tests/test_base_scanner.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import asyncio
66
import time
77
from datetime import timedelta
8-
from unittest.mock import ANY
8+
from typing import Any
9+
from unittest.mock import ANY, MagicMock, patch
910

1011
import pytest
1112
from bleak.backends.device import BLEDevice
@@ -14,10 +15,12 @@
1415

1516
from habluetooth import (
1617
BaseHaRemoteScanner,
18+
BaseHaScanner,
1719
BluetoothScanningMode,
1820
HaBluetoothConnector,
1921
HaScannerDetails,
2022
HaScannerModeChange,
23+
HaScannerType,
2124
get_manager,
2225
)
2326
from habluetooth.const import (
@@ -150,6 +153,7 @@ async def test_remote_scanner(name_2: str | None) -> None:
150153
connectable=scanner.connectable,
151154
name=scanner.name,
152155
adapter=scanner.adapter,
156+
scanner_type=HaScannerType.REMOTE,
153157
)
154158
unsetup = scanner.async_setup()
155159
cancel = manager.async_register_scanner(scanner)
@@ -538,6 +542,7 @@ async def test_merge_manufacturer_data_history_existing() -> None:
538542
connectable=scanner.connectable,
539543
name=scanner.name,
540544
adapter=scanner.adapter,
545+
scanner_type=HaScannerType.REMOTE,
541546
)
542547
unsetup = scanner.async_setup()
543548
cancel = manager.async_register_scanner(scanner)
@@ -616,6 +621,7 @@ async def test_merge_manufacturer_data_history_new() -> None:
616621
connectable=scanner.connectable,
617622
name=scanner.name,
618623
adapter=scanner.adapter,
624+
scanner_type=HaScannerType.REMOTE,
619625
)
620626
unsetup = scanner.async_setup()
621627
cancel = manager.async_register_scanner(scanner)
@@ -683,6 +689,7 @@ async def test_filter_apple_data() -> None:
683689
connectable=scanner.connectable,
684690
name=scanner.name,
685691
adapter=scanner.adapter,
692+
scanner_type=HaScannerType.REMOTE,
686693
)
687694
unsetup = scanner.async_setup()
688695
cancel = manager.async_register_scanner(scanner)
@@ -807,3 +814,40 @@ def mode_callback(change: HaScannerModeChange) -> None:
807814
# Clean up
808815
unsetup()
809816
cancel()
817+
818+
819+
def test_remote_scanner_type() -> None:
820+
"""Test that remote scanners have REMOTE type."""
821+
822+
class TestRemoteScanner(BaseHaRemoteScanner):
823+
"""Test remote scanner implementation."""
824+
825+
pass
826+
827+
scanner = TestRemoteScanner("test_source", "test_adapter")
828+
assert scanner.details.scanner_type is HaScannerType.REMOTE
829+
830+
831+
def test_base_scanner_with_connector() -> None:
832+
"""Test BaseHaScanner with connector and adapter type."""
833+
manager = get_manager()
834+
835+
mock_adapters: dict[str, dict[str, Any]] = {
836+
"test_adapter": {
837+
"address": "00:1A:7D:DA:71:04",
838+
"adapter_type": "usb",
839+
}
840+
}
841+
842+
connector = HaBluetoothConnector(
843+
client=MagicMock, source="test_source", can_connect=lambda: True
844+
)
845+
846+
with patch.object(manager, "_adapters", mock_adapters):
847+
scanner = BaseHaScanner(
848+
source="test_source",
849+
adapter="test_adapter",
850+
connector=connector,
851+
connectable=True,
852+
)
853+
assert scanner.details.scanner_type is HaScannerType.USB

0 commit comments

Comments
 (0)