From 7cf19260814fa20a913a09e7cdcd558e9aa9df02 Mon Sep 17 00:00:00 2001
From: "J. Nick Koston" <nick@koston.org>
Date: Wed, 5 Apr 2023 00:19:37 -1000
Subject: [PATCH] Fix BLEDevice not getting updated when details change for
 remote scanners (#90815)

---
 .../components/bluetooth/base_scanner.py      | 27 ++++++++++++++-----
 .../components/bluetooth/test_base_scanner.py | 20 +++++++++++++-
 2 files changed, 40 insertions(+), 7 deletions(-)

diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py
index f1ffd6ecf58..97b2aebba7a 100644
--- a/homeassistant/components/bluetooth/base_scanner.py
+++ b/homeassistant/components/bluetooth/base_scanner.py
@@ -345,12 +345,27 @@ class BaseHaRemoteScanner(BaseHaScanner):
             tx_power=NO_RSSI_VALUE if tx_power is None else tx_power,
             platform_data=(),
         )
-        device = BLEDevice(
-            address=address,
-            name=local_name,
-            details=self._details | details,
-            rssi=rssi,  # deprecated, will be removed in newer bleak
-        )
+        if prev_discovery:
+            #
+            # Bleak updates the BLEDevice via create_or_update_device.
+            # We need to do the same to ensure integrations that already
+            # have the BLEDevice object get the updated details when they
+            # change.
+            #
+            # https://github.com/hbldh/bleak/blob/222618b7747f0467dbb32bd3679f8cfaa19b1668/bleak/backends/scanner.py#L203
+            #
+            device = prev_device
+            device.name = local_name
+            device.details = self._details | details
+            # pylint: disable-next=protected-access
+            device._rssi = rssi  # deprecated, will be removed in newer bleak
+        else:
+            device = BLEDevice(
+                address=address,
+                name=local_name,
+                details=self._details | details,
+                rssi=rssi,  # deprecated, will be removed in newer bleak
+            )
         self._discovered_device_advertisement_datas[address] = (
             device,
             advertisement_data,
diff --git a/tests/components/bluetooth/test_base_scanner.py b/tests/components/bluetooth/test_base_scanner.py
index 79a36630df2..8817acad468 100644
--- a/tests/components/bluetooth/test_base_scanner.py
+++ b/tests/components/bluetooth/test_base_scanner.py
@@ -481,7 +481,18 @@ async def test_device_with_ten_minute_advertising_interval(
         connectable=False,
     )
 
-    for _ in range(0, 20):
+    with patch(
+        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        return_value=new_time,
+    ):
+        scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
+
+    original_device = scanner.discovered_devices_and_advertisement_data[
+        bparasite_device.address
+    ][0]
+    assert original_device is not bparasite_device
+
+    for _ in range(1, 20):
         new_time += advertising_interval
         with patch(
             "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
@@ -489,6 +500,13 @@ async def test_device_with_ten_minute_advertising_interval(
         ):
             scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
 
+    # Make sure the BLEDevice object gets updated
+    # and not replaced
+    assert (
+        scanner.discovered_devices_and_advertisement_data[bparasite_device.address][0]
+        is original_device
+    )
+
     future_time = new_time
     assert (
         bluetooth.async_address_present(hass, bparasite_device.address, False) is True
-- 
GitLab