diff --git a/homeassistant/components/bluetooth/manager.py b/homeassistant/components/bluetooth/manager.py index 37c24423231..f0152f5ae5e 100644 --- a/homeassistant/components/bluetooth/manager.py +++ b/homeassistant/components/bluetooth/manager.py @@ -3,7 +3,6 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Iterable -from dataclasses import asdict from datetime import datetime, timedelta import itertools import logging @@ -185,11 +184,11 @@ class BluetoothManager: "adapters": self._adapters, "scanners": scanner_diagnostics, "connectable_history": [ - asdict(service_info) + service_info.as_dict() for service_info in self._connectable_history.values() ], "history": [ - asdict(service_info) for service_info in self._history.values() + service_info.as_dict() for service_info in self._history.values() ], } diff --git a/homeassistant/components/bluetooth/models.py b/homeassistant/components/bluetooth/models.py index d93f8efc1e2..9e93ea4d142 100644 --- a/homeassistant/components/bluetooth/models.py +++ b/homeassistant/components/bluetooth/models.py @@ -53,6 +53,25 @@ class BluetoothServiceInfoBleak(BluetoothServiceInfo): 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): """The mode of scanning for bluetooth devices.""" diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index d641cae9c7c..1da071a76ab 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -3,11 +3,13 @@ from unittest.mock import ANY, patch -from bleak.backends.scanner import BLEDevice +from bleak.backends.scanner import AdvertisementData, BLEDevice from homeassistant.components import bluetooth from homeassistant.components.bluetooth.const import DEFAULT_ADDRESS +from . import inject_advertisement + from tests.common import MockConfigEntry from tests.components.diagnostics import get_diagnostics_for_config_entry @@ -158,6 +160,10 @@ async def test_diagnostics_macos( # because we cannot import the scanner class directly without it throwing an # error if the test is not running on linux since we won't have the correct # deps installed when testing on MacOS. + switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand") + switchbot_adv = AdvertisementData( + local_name="wohand", service_uuids=[], manufacturer_data={1: b"\x01"} + ) with patch( "homeassistant.components.bluetooth.scanner.HaScanner.discovered_devices", @@ -180,6 +186,8 @@ async def test_diagnostics_macos( assert await hass.config_entries.async_setup(entry1.entry_id) await hass.async_block_till_done() + inject_advertisement(hass, switchbot_device, switchbot_adv) + diag = await get_diagnostics_for_config_entry(hass, hass_client, entry1) assert diag == { "adapters": { @@ -197,8 +205,34 @@ async def test_diagnostics_macos( "sw_version": ANY, } }, - "connectable_history": [], - "history": [], + "connectable_history": [ + { + "address": "44:44:33:11:23:45", + "advertisement": ANY, + "connectable": True, + "manufacturer_data": ANY, + "name": "wohand", + "rssi": 0, + "service_data": {}, + "service_uuids": [], + "source": "local", + "time": ANY, + } + ], + "history": [ + { + "address": "44:44:33:11:23:45", + "advertisement": ANY, + "connectable": True, + "manufacturer_data": ANY, + "name": "wohand", + "rssi": 0, + "service_data": {}, + "service_uuids": [], + "source": "local", + "time": ANY, + } + ], "scanners": [ { "adapter": "Core Bluetooth",