diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py index 01f55048c6d..b6058767669 100644 --- a/homeassistant/components/bluetooth/__init__.py +++ b/homeassistant/components/bluetooth/__init__.py @@ -39,6 +39,8 @@ _LOGGER = logging.getLogger(__name__) MAX_REMEMBER_ADDRESSES: Final = 2048 +SOURCE_LOCAL: Final = "local" + class BluetoothCallbackMatcherOptional(TypedDict, total=False): """Matcher for the bluetooth integration for callback optional fields.""" @@ -266,7 +268,7 @@ class BluetoothManager: ): if service_info is None: service_info = BluetoothServiceInfo.from_advertisement( - device, advertisement_data + device, advertisement_data, SOURCE_LOCAL ) try: callback(service_info, BluetoothChange.ADVERTISEMENT) @@ -277,7 +279,7 @@ class BluetoothManager: return if service_info is None: service_info = BluetoothServiceInfo.from_advertisement( - device, advertisement_data + device, advertisement_data, SOURCE_LOCAL ) for domain in matched_domains: discovery_flow.async_create_flow( @@ -312,7 +314,9 @@ class BluetoothManager: ): try: callback( - BluetoothServiceInfo.from_advertisement(*device_adv_data), + BluetoothServiceInfo.from_advertisement( + *device_adv_data, SOURCE_LOCAL + ), BluetoothChange.ADVERTISEMENT, ) except Exception: # pylint: disable=broad-except @@ -338,7 +342,9 @@ class BluetoothManager: discovered = models.HA_BLEAK_SCANNER.discovered_devices history = models.HA_BLEAK_SCANNER.history return [ - BluetoothServiceInfo.from_advertisement(*history[device.address]) + BluetoothServiceInfo.from_advertisement( + *history[device.address], SOURCE_LOCAL + ) for device in discovered if device.address in history ] diff --git a/homeassistant/helpers/service_info/bluetooth.py b/homeassistant/helpers/service_info/bluetooth.py index 9dff2782da0..003a4228d80 100644 --- a/homeassistant/helpers/service_info/bluetooth.py +++ b/homeassistant/helpers/service_info/bluetooth.py @@ -22,10 +22,11 @@ class BluetoothServiceInfo(BaseServiceInfo): manufacturer_data: dict[int, bytes] service_data: dict[str, bytes] service_uuids: list[str] + source: str @classmethod def from_advertisement( - cls, device: BLEDevice, advertisement_data: AdvertisementData + cls, device: BLEDevice, advertisement_data: AdvertisementData, source: str ) -> BluetoothServiceInfo: """Create a BluetoothServiceInfo from an advertisement.""" return cls( @@ -35,6 +36,7 @@ class BluetoothServiceInfo(BaseServiceInfo): manufacturer_data=advertisement_data.manufacturer_data, service_data=advertisement_data.service_data, service_uuids=advertisement_data.service_uuids, + source=source, ) @cached_property diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py index 2bbe4ce7dcb..1d348f42f0e 100644 --- a/tests/components/bluetooth/test_init.py +++ b/tests/components/bluetooth/test_init.py @@ -6,6 +6,7 @@ from bleak.backends.scanner import AdvertisementData, BLEDevice from homeassistant.components import bluetooth from homeassistant.components.bluetooth import ( + SOURCE_LOCAL, BluetoothChange, BluetoothServiceInfo, models, @@ -244,6 +245,7 @@ async def test_async_discovered_device_api(hass, mock_bleak_scanner_start): assert len(service_infos) == 1 # wrong_name should not appear because bleak no longer sees it assert service_infos[0].name == "wohand" + assert service_infos[0].source == SOURCE_LOCAL assert bluetooth.async_address_present(hass, "44:44:33:11:23:42") is False assert bluetooth.async_address_present(hass, "44:44:33:11:23:45") is True @@ -255,7 +257,8 @@ async def test_register_callbacks(hass, mock_bleak_scanner_start): callbacks = [] def _fake_subscriber( - service_info: BluetoothServiceInfo, change: BluetoothChange + service_info: BluetoothServiceInfo, + change: BluetoothChange, ) -> None: """Fake subscriber for the BleakScanner.""" callbacks.append((service_info, change)) @@ -312,16 +315,19 @@ async def test_register_callbacks(hass, mock_bleak_scanner_start): service_info: BluetoothServiceInfo = callbacks[0][0] assert service_info.name == "wohand" + assert service_info.source == SOURCE_LOCAL assert service_info.manufacturer == "Nordic Semiconductor ASA" assert service_info.manufacturer_id == 89 service_info: BluetoothServiceInfo = callbacks[1][0] assert service_info.name == "empty" + assert service_info.source == SOURCE_LOCAL assert service_info.manufacturer is None assert service_info.manufacturer_id is None service_info: BluetoothServiceInfo = callbacks[2][0] assert service_info.name == "empty" + assert service_info.source == SOURCE_LOCAL assert service_info.manufacturer is None assert service_info.manufacturer_id is None