Prefilter noisy apple devices from bluetooth (#77808)
This commit is contained in:
parent
c5100d2895
commit
804e4ab989
2 changed files with 106 additions and 40 deletions
|
@ -54,6 +54,10 @@ if TYPE_CHECKING:
|
|||
|
||||
FILTER_UUIDS: Final = "UUIDs"
|
||||
|
||||
APPLE_MFR_ID: Final = 76
|
||||
APPLE_HOMEKIT_START_BYTE: Final = 0x06 # homekit_controller
|
||||
APPLE_DEVICE_ID_START_BYTE: Final = 0x10 # bluetooth_le_tracker
|
||||
APPLE_START_BYTES_WANTED: Final = {APPLE_DEVICE_ID_START_BYTE, APPLE_HOMEKIT_START_BYTE}
|
||||
|
||||
RSSI_SWITCH_THRESHOLD = 6
|
||||
|
||||
|
@ -290,6 +294,19 @@ class BluetoothManager:
|
|||
than the source from the history or the timestamp
|
||||
in the history is older than 180s
|
||||
"""
|
||||
|
||||
# Pre-filter noisy apple devices as they can account for 20-35% of the
|
||||
# traffic on a typical network.
|
||||
advertisement_data = service_info.advertisement
|
||||
manufacturer_data = advertisement_data.manufacturer_data
|
||||
if (
|
||||
len(manufacturer_data) == 1
|
||||
and (apple_data := manufacturer_data.get(APPLE_MFR_ID))
|
||||
and apple_data[0] not in APPLE_START_BYTES_WANTED
|
||||
and not advertisement_data.service_data
|
||||
):
|
||||
return
|
||||
|
||||
device = service_info.device
|
||||
connectable = service_info.connectable
|
||||
address = device.address
|
||||
|
@ -299,7 +316,6 @@ class BluetoothManager:
|
|||
return
|
||||
|
||||
self._history[address] = service_info
|
||||
advertisement_data = service_info.advertisement
|
||||
source = service_info.source
|
||||
|
||||
if connectable:
|
||||
|
|
|
@ -1291,16 +1291,16 @@ async def test_register_callback_by_manufacturer_id(
|
|||
cancel = bluetooth.async_register_callback(
|
||||
hass,
|
||||
_fake_subscriber,
|
||||
{MANUFACTURER_ID: 76},
|
||||
{MANUFACTURER_ID: 21},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
||||
apple_device = BLEDevice("44:44:33:11:23:45", "apple")
|
||||
apple_device = BLEDevice("44:44:33:11:23:45", "rtx")
|
||||
apple_adv = AdvertisementData(
|
||||
local_name="apple",
|
||||
manufacturer_data={76: b"\xd8.\xad\xcd\r\x85"},
|
||||
local_name="rtx",
|
||||
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
|
||||
)
|
||||
|
||||
inject_advertisement(hass, apple_device, apple_adv)
|
||||
|
@ -1316,9 +1316,59 @@ async def test_register_callback_by_manufacturer_id(
|
|||
assert len(callbacks) == 1
|
||||
|
||||
service_info: BluetoothServiceInfo = callbacks[0][0]
|
||||
assert service_info.name == "apple"
|
||||
assert service_info.manufacturer == "Apple, Inc."
|
||||
assert service_info.manufacturer_id == 76
|
||||
assert service_info.name == "rtx"
|
||||
assert service_info.manufacturer == "RTX Telecom A/S"
|
||||
assert service_info.manufacturer_id == 21
|
||||
|
||||
|
||||
async def test_filtering_noisy_apple_devices(
|
||||
hass, mock_bleak_scanner_start, enable_bluetooth
|
||||
):
|
||||
"""Test filtering noisy apple devices."""
|
||||
mock_bt = []
|
||||
callbacks = []
|
||||
|
||||
def _fake_subscriber(
|
||||
service_info: BluetoothServiceInfo, change: BluetoothChange
|
||||
) -> None:
|
||||
"""Fake subscriber for the BleakScanner."""
|
||||
callbacks.append((service_info, change))
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt
|
||||
):
|
||||
await async_setup_with_default_adapter(hass)
|
||||
|
||||
with patch.object(hass.config_entries.flow, "async_init"):
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
cancel = bluetooth.async_register_callback(
|
||||
hass,
|
||||
_fake_subscriber,
|
||||
{MANUFACTURER_ID: 21},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
||||
apple_device = BLEDevice("44:44:33:11:23:45", "rtx")
|
||||
apple_adv = AdvertisementData(
|
||||
local_name="noisy",
|
||||
manufacturer_data={76: b"\xd8.\xad\xcd\r\x85"},
|
||||
)
|
||||
|
||||
inject_advertisement(hass, apple_device, apple_adv)
|
||||
|
||||
empty_device = BLEDevice("11:22:33:44:55:66", "empty")
|
||||
empty_adv = AdvertisementData(local_name="empty")
|
||||
|
||||
inject_advertisement(hass, empty_device, empty_adv)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
cancel()
|
||||
|
||||
assert len(callbacks) == 0
|
||||
|
||||
|
||||
async def test_register_callback_by_address_connectable_manufacturer_id(
|
||||
|
@ -1346,21 +1396,21 @@ async def test_register_callback_by_address_connectable_manufacturer_id(
|
|||
cancel = bluetooth.async_register_callback(
|
||||
hass,
|
||||
_fake_subscriber,
|
||||
{MANUFACTURER_ID: 76, CONNECTABLE: False, ADDRESS: "44:44:33:11:23:45"},
|
||||
{MANUFACTURER_ID: 21, CONNECTABLE: False, ADDRESS: "44:44:33:11:23:45"},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
||||
apple_device = BLEDevice("44:44:33:11:23:45", "apple")
|
||||
apple_device = BLEDevice("44:44:33:11:23:45", "rtx")
|
||||
apple_adv = AdvertisementData(
|
||||
local_name="apple",
|
||||
manufacturer_data={76: b"\xd8.\xad\xcd\r\x85"},
|
||||
local_name="rtx",
|
||||
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
|
||||
)
|
||||
|
||||
inject_advertisement(hass, apple_device, apple_adv)
|
||||
|
||||
apple_device_wrong_address = BLEDevice("44:44:33:11:23:46", "apple")
|
||||
apple_device_wrong_address = BLEDevice("44:44:33:11:23:46", "rtx")
|
||||
|
||||
inject_advertisement(hass, apple_device_wrong_address, apple_adv)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -1370,9 +1420,9 @@ async def test_register_callback_by_address_connectable_manufacturer_id(
|
|||
assert len(callbacks) == 1
|
||||
|
||||
service_info: BluetoothServiceInfo = callbacks[0][0]
|
||||
assert service_info.name == "apple"
|
||||
assert service_info.manufacturer == "Apple, Inc."
|
||||
assert service_info.manufacturer_id == 76
|
||||
assert service_info.name == "rtx"
|
||||
assert service_info.manufacturer == "RTX Telecom A/S"
|
||||
assert service_info.manufacturer_id == 21
|
||||
|
||||
|
||||
async def test_register_callback_by_manufacturer_id_and_address(
|
||||
|
@ -1400,19 +1450,19 @@ async def test_register_callback_by_manufacturer_id_and_address(
|
|||
cancel = bluetooth.async_register_callback(
|
||||
hass,
|
||||
_fake_subscriber,
|
||||
{MANUFACTURER_ID: 76, ADDRESS: "44:44:33:11:23:45"},
|
||||
{MANUFACTURER_ID: 21, ADDRESS: "44:44:33:11:23:45"},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
||||
apple_device = BLEDevice("44:44:33:11:23:45", "apple")
|
||||
apple_adv = AdvertisementData(
|
||||
local_name="apple",
|
||||
manufacturer_data={76: b"\xd8.\xad\xcd\r\x85"},
|
||||
rtx_device = BLEDevice("44:44:33:11:23:45", "rtx")
|
||||
rtx_adv = AdvertisementData(
|
||||
local_name="rtx",
|
||||
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
|
||||
)
|
||||
|
||||
inject_advertisement(hass, apple_device, apple_adv)
|
||||
inject_advertisement(hass, rtx_device, rtx_adv)
|
||||
|
||||
yale_device = BLEDevice("44:44:33:11:23:45", "apple")
|
||||
yale_adv = AdvertisementData(
|
||||
|
@ -1426,7 +1476,7 @@ async def test_register_callback_by_manufacturer_id_and_address(
|
|||
other_apple_device = BLEDevice("44:44:33:11:23:22", "apple")
|
||||
other_apple_adv = AdvertisementData(
|
||||
local_name="apple",
|
||||
manufacturer_data={76: b"\xd8.\xad\xcd\r\x85"},
|
||||
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
|
||||
)
|
||||
inject_advertisement(hass, other_apple_device, other_apple_adv)
|
||||
|
||||
|
@ -1435,9 +1485,9 @@ async def test_register_callback_by_manufacturer_id_and_address(
|
|||
assert len(callbacks) == 1
|
||||
|
||||
service_info: BluetoothServiceInfo = callbacks[0][0]
|
||||
assert service_info.name == "apple"
|
||||
assert service_info.manufacturer == "Apple, Inc."
|
||||
assert service_info.manufacturer_id == 76
|
||||
assert service_info.name == "rtx"
|
||||
assert service_info.manufacturer == "RTX Telecom A/S"
|
||||
assert service_info.manufacturer_id == 21
|
||||
|
||||
|
||||
async def test_register_callback_by_service_uuid_and_address(
|
||||
|
@ -1603,31 +1653,31 @@ async def test_register_callback_by_local_name(
|
|||
cancel = bluetooth.async_register_callback(
|
||||
hass,
|
||||
_fake_subscriber,
|
||||
{LOCAL_NAME: "apple"},
|
||||
{LOCAL_NAME: "rtx"},
|
||||
BluetoothScanningMode.ACTIVE,
|
||||
)
|
||||
|
||||
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||
|
||||
apple_device = BLEDevice("44:44:33:11:23:45", "apple")
|
||||
apple_adv = AdvertisementData(
|
||||
local_name="apple",
|
||||
manufacturer_data={76: b"\xd8.\xad\xcd\r\x85"},
|
||||
rtx_device = BLEDevice("44:44:33:11:23:45", "rtx")
|
||||
rtx_adv = AdvertisementData(
|
||||
local_name="rtx",
|
||||
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
|
||||
)
|
||||
|
||||
inject_advertisement(hass, apple_device, apple_adv)
|
||||
inject_advertisement(hass, rtx_device, rtx_adv)
|
||||
|
||||
empty_device = BLEDevice("11:22:33:44:55:66", "empty")
|
||||
empty_adv = AdvertisementData(local_name="empty")
|
||||
|
||||
inject_advertisement(hass, empty_device, empty_adv)
|
||||
|
||||
apple_device_2 = BLEDevice("44:44:33:11:23:45", "apple")
|
||||
apple_adv_2 = AdvertisementData(
|
||||
local_name="apple2",
|
||||
manufacturer_data={76: b"\xd8.\xad\xcd\r\x85"},
|
||||
rtx_device_2 = BLEDevice("44:44:33:11:23:45", "rtx")
|
||||
rtx_adv_2 = AdvertisementData(
|
||||
local_name="rtx2",
|
||||
manufacturer_data={21: b"\xd8.\xad\xcd\r\x85"},
|
||||
)
|
||||
inject_advertisement(hass, apple_device_2, apple_adv_2)
|
||||
inject_advertisement(hass, rtx_device_2, rtx_adv_2)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -1636,9 +1686,9 @@ async def test_register_callback_by_local_name(
|
|||
assert len(callbacks) == 1
|
||||
|
||||
service_info: BluetoothServiceInfo = callbacks[0][0]
|
||||
assert service_info.name == "apple"
|
||||
assert service_info.manufacturer == "Apple, Inc."
|
||||
assert service_info.manufacturer_id == 76
|
||||
assert service_info.name == "rtx"
|
||||
assert service_info.manufacturer == "RTX Telecom A/S"
|
||||
assert service_info.manufacturer_id == 21
|
||||
|
||||
|
||||
async def test_register_callback_by_local_name_overly_broad(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue