Skip to content
Browse files
Fix bluetooth diagnostics on macos (#79680)
* Fix bluetooth diagnostics on macos

The pyobjc objects cannot be pickled which cases dataclasses
asdict to raise an exception when trying to do the deepcopy

We now implement our own as_dict to avoid this problem

* add cover
  • Loading branch information
bdraco committed Oct 6, 2022
1 parent 558b327 commit c798723c2744a7b386c56de9627a31c349309bbf
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 6 deletions.
@@ -3,7 +3,6 @@

import asyncio
from import Callable, Iterable
from dataclasses import asdict
from datetime import datetime, timedelta
import itertools
import logging
@@ -185,11 +184,11 @@ async def async_diagnostics(self) -> dict[str, Any]:
"adapters": self._adapters,
"scanners": scanner_diagnostics,
"connectable_history": [
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()

@@ -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 {
"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."""
@@ -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(
@@ -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",

0 comments on commit c798723

Please sign in to comment.