From 77f2e9b3f7a5012ddb3c78e3529a7f01146f74b6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 10 Dec 2022 12:21:16 -1000 Subject: [PATCH] fix: handle data corruption (#34) --- src/bluetooth_adapters/storage.py | 30 +++++++++++++++++++--------- tests/test_init.py | 33 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/bluetooth_adapters/storage.py b/src/bluetooth_adapters/storage.py index 9ae9592..8899f8d 100644 --- a/src/bluetooth_adapters/storage.py +++ b/src/bluetooth_adapters/storage.py @@ -2,6 +2,7 @@ from __future__ import annotations +import logging import time from dataclasses import dataclass from typing import Any, Final, TypedDict @@ -9,6 +10,8 @@ from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData +_LOGGER = logging.getLogger(__name__) + @dataclass class DiscoveredDeviceAdvertisementData: @@ -107,16 +110,25 @@ def expire_stale_scanner_discovered_device_advertisement_data( def discovered_device_advertisement_data_from_dict( data: DiscoveredDeviceAdvertisementDataDict, -) -> DiscoveredDeviceAdvertisementData: +) -> DiscoveredDeviceAdvertisementData | None: """Build discovered_device_advertisement_data dict.""" - return DiscoveredDeviceAdvertisementData( - data[CONNECTABLE], - data[EXPIRE_SECONDS], - _deserialize_discovered_device_advertisement_datas( - data[DISCOVERED_DEVICE_ADVERTISEMENT_DATAS] - ), - _deserialize_discovered_device_timestamps(data[DISCOVERED_DEVICE_TIMESTAMPS]), - ) + try: + return DiscoveredDeviceAdvertisementData( + data[CONNECTABLE], + data[EXPIRE_SECONDS], + _deserialize_discovered_device_advertisement_datas( + data[DISCOVERED_DEVICE_ADVERTISEMENT_DATAS] + ), + _deserialize_discovered_device_timestamps( + data[DISCOVERED_DEVICE_TIMESTAMPS] + ), + ) + except Exception as err: # pylint: disable=broad-except + _LOGGER.exception( + "Error deserializing discovered_device_advertisement_data, adapter startup will be slow: %s", + err, + ) + return None def discovered_device_advertisement_data_to_dict( diff --git a/tests/test_init.py b/tests/test_init.py index 869c112..4bc2bfc 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -896,6 +896,7 @@ def test_discovered_device_advertisement_data_from_dict(): platform_data=("Test Device", ""), rssi=-50, ) + assert result is not None out_ble_device = result.discovered_device_advertisement_datas["AA:BB:CC:DD:EE:FF"][ 0 ] @@ -1012,3 +1013,35 @@ def test_expire_stale_scanner_discovered_device_advertisement_data(): not in data["myscanner"]["discovered_device_advertisement_datas"] ) assert "all_expired" not in data + + +def test_discovered_device_advertisement_data_from_dict_corrupt(caplog): + """Test discovered_device_advertisement_data_from_dict with corrupt data.""" + now = time.time() + result = discovered_device_advertisement_data_from_dict( + { + "connectable": True, + "discovered_device_advertisement_datas": { + "AA:BB:CC:DD:EE:FF": { + "advertisement_data": { # type: ignore[typeddict-item] + "local_name": "Test " "Device", + "manufacturer_data": {"76": "0215aabbccddeeff"}, + "rssi": -50, + "service_data": { + "0000180d-0000-1000-8000-00805f9b34fb": "00000000" + }, + "service_uuids": ["0000180d-0000-1000-8000-00805f9b34fb"], + }, + "device": { # type: ignore[typeddict-item] + "address": "AA:BB:CC:DD:EE:FF", + "details": {"details": "test"}, + "rssi": -50, + }, + } + }, + "discovered_device_timestamps": {"AA:BB:CC:DD:EE:FF": now}, + "expire_seconds": 100, + } + ) + assert result is None + assert "Error deserializing discovered_device_advertisement_data" in caplog.text