    def _refresh_scanners(self):
        """
        Refresh our local (and saved) list of scanners (BLE Proxies).

        We refresh all scanners each time we are called, but only save out the config entry
        if it has changed *AND* we haven't tried to do so in the last SAVEOUT_COOLDOWN
        seconds (10 seems to be enough, we only do it when the proxies config has *actually*
        changed).
        """
        _previous_scannerlist = [device.address for device in self.devices.values() if device.is_scanner]
        _purge_scanners = _previous_scannerlist.copy()
        _scanners_without_areas = []

        # _LOGGER.error("Preserving %d current scanner entries", len(_previous_scannerlist))

        # Find active HaBaseScanners in the backend, and only pay attention to those
        # instead of trawling through the device registry first.
        #
        # scanner_ha: BaseHaScanner from HA's bluetooth backend
        # scanner_devreg_bt: DeviceEntry from HA's device_registry from Bluetooth integration
        # scanner_devreg_mac: DeviceEntry from HA's *other* integrations, like ESPHome, Shelly.
        # scanner_b: BermudaDevice entry

        # TODO: Eventually replace this with a minver requirement in hacs.json.
        if self.hass_version_min_2025_2:
            # New api
            self._hascanners = set(self._manager.async_current_scanners())
        else:
            # Evil: We're acessing private members of bt manager to do it since there's no API call for it.
            self._hascanners = self._manager._connectable_scanners | self._manager._non_connectable_scanners  # noqa: SLF001

        for hascanner in self._hascanners:
            scanner_address = format_mac(hascanner.source).lower()
            # As of 2025.2.0 The bluetooth integration creates its own device entries
            # for all HaScanners, not just local adaptors. So since there are two integration
            # pages where a user might apply an area setting (eg, the bluetooth page or the shelly or esphome page)
            # we should check both to see if the user has applied an area anywhere, and prefer the bluetooth one
            # if both are set.
            scanner_devreg_bt = self._device_registry.async_get_device(
                connections={
                    ("bluetooth", scanner_address.upper()),  # bluetooth, uppercase: matches bluetooth integration
                }
            )
            scanner_devreg_mac = self._device_registry.async_get_device(
                connections={
                    ("mac", scanner_address),  # mac, lowercase: matches ESPHome, Shellys integrations etc
                }
            )

            if scanner_devreg_bt is None and scanner_devreg_mac is None:
                _LOGGER_SPAM_LESS.error(
                    f"scanner_not_in_devreg_{scanner_address:s}",
                    "Failed to find scanner %s (%s) in Device Registry",
                    hascanner.name,
                    hascanner.source,
                )
                continue
            # _LOGGER.info("Great! Found scanner: %s (%s)", scanner_ha.name, scanner_ha.source)
            # Since this scanner still exists, we won't purge it
            if scanner_address in _purge_scanners:
                _purge_scanners.remove(scanner_address)

            # Populate the local copy of timestamps, if applicable
            if isinstance(hascanner, BaseHaRemoteScanner):
                self._hascanner_timestamps[hascanner.source.lower()] = hascanner._discovered_device_timestamps  # noqa: SLF001

            scanner_b = self._get_device(scanner_address)
            if scanner_b is None:
                # It's a new scanner, we will need to update our saved config.
                # _LOGGER.debug("New Scanner: %s", scanner_ha.name)
                scanner_b = self._get_or_create_device(scanner_address)

            # We found the device entry and have created our scannerdevice,
            # now update any fields that might be new from the device reg.
            # First clear the existing to make prioritising the bt/mac matches
            # easier (feel free to refactor, bear in mind we prefer bt first)
            scanner_b.area_id = None

            _bt_name = None
            _mac_name = None
            _bt_name_by_user = None
            _mac_name_by_user = None

            if scanner_devreg_bt is not None:
                scanner_b.area_id = scanner_devreg_bt.area_id
                scanner_b.entry_id = scanner_devreg_bt.id
                _bt_name_by_user = scanner_devreg_bt.name_by_user
                _bt_name = scanner_devreg_bt.name
            if scanner_devreg_mac is not None:
                # Only apply if the bt device entry hasn't been applied:
                scanner_b.area_id = scanner_b.area_id or scanner_devreg_mac.area_id
                scanner_b.entry_id = scanner_b.entry_id or scanner_devreg_mac.id
                _mac_name = scanner_devreg_mac.name
                _mac_name_by_user = scanner_devreg_mac.name_by_user

            # Bluetooth integ names scanners by address, so prefer the source integration's
            # autogenerated name over that.
            scanner_b.name_devreg = _mac_name or _bt_name
            # Bluetooth device reg is newer, so use the user-given name there if it exists.
            scanner_b.name_by_user = _bt_name_by_user or _mac_name_by_user

            areas = self.area_reg.async_get_area(scanner_b.area_id) if scanner_b.area_id else None
            if areas is not None and hasattr(areas, "name") and areas.name is not None:
                scanner_b.area_name = areas.name
            else:
                _LOGGER_SPAM_LESS.warning(
                    f"no_area_on_update{scanner_b.name}",
                    "No area name or no area id updating scanner %s, area_id %s",
                    scanner_b.name,
                    areas,
                )
                _scanners_without_areas.append(scanner_b.name)
                scanner_b.area_name = f"Invalid Area for {scanner_b.name}"
            scanner_b.is_scanner = True

        # Now un-tag any devices that are no longer scanners
        for address in _purge_scanners:
            self.devices[address].is_scanner = False
            update_scannerlist = True

        if _scanners_without_areas != self._scanners_without_areas:
            # the set has changed, or we have just started (since the one in self is defaulted to None)

            # Clear any existing repair, because it's either resolved now (empty list) or we need to re-issue
            # the repair in order to update the scanner list (re-calling doesn't update it).
            ir.async_delete_issue(self.hass, DOMAIN, REPAIR_SCANNER_WITHOUT_AREA)

            if len(_scanners_without_areas) != 0:
                ir.async_create_issue(
                    self.hass,
                    DOMAIN,
                    REPAIR_SCANNER_WITHOUT_AREA,
                    translation_key=REPAIR_SCANNER_WITHOUT_AREA,
                    translation_placeholders={
                        "scannerlist": "".join(f"- {name}\n" for name in _scanners_without_areas),
                    },
                    severity=ir.IssueSeverity.ERROR,
                    is_fixable=False,
                )

            # copy to self so we don't re-raise unless something changes in future.
            self._scanners_without_areas = _scanners_without_areas

        # Because of the quick check-time and the checks we have on saving the config_entry,
        # we'll update on every call:
        update_scannerlist = True
        if update_scannerlist:
            # bail out if the config entry isn't ready yet.
            if self.config_entry is None or self.config_entry.state != ConfigEntryState.LOADED:
                # _LOGGER.debug("Aborting refresh scanners due to config entry not being ready")
                self._do_full_scanner_init = True
                return False

            # Build the config_data and self.scanner_list structs fresh
            # ready to update our config entry if needed.
            self.scanner_list.clear()
            confdata_scanners: dict[str, dict] = {}
            for device in self.devices.values():
                if device.is_scanner:
                    self.scanner_list.append(device.address)
                    # Only add the necessary fields to confdata
                    confdata_scanners[device.address] = {
                        key: getattr(device, key)
                        for key in [
                            "name",
                            "name_devreg",
                            "name_by_user",
                            "address",
                            "ref_power",
                            "unique_id",
                            "address_type",
                            "area_id",
                            "area_name",
                            "is_scanner",
                            "entry_id",
                        ]
                    }

            if self.config_entry.data.get(CONFDATA_SCANNERS, {}) == confdata_scanners:
                # **** BAIL OUT, CONFIG HAS NOT CHANGED ****
                # _LOGGER.debug("Scanner configs are identical, not doing update.")
                self._do_full_scanner_init = False
                return True

            # We will arrive here every second for as long as the saved config is
            # different from our running config. But we don't want to save immediately,
            # since there is a lot of bouncing that happens during setup.

            # Make sure we haven't requested recently...
            if self.last_config_entry_update_request < MONOTONIC_TIME() - SAVEOUT_COOLDOWN:
                # OK, we're good to go.
                self.last_config_entry_update_request = MONOTONIC_TIME()
                _LOGGER.debug("Requesting save-out of scanner configs")
                self.hass.add_job(self.async_call_update_entry, confdata_scanners)

        return True
Refresh our local (and saved) list of scanners (BLE Proxies).

We refresh all scanners each time we are called, but only save out the config entry
if it has changed *AND* we haven't tried to do so in the last SAVEOUT_COOLDOWN
seconds (10 seems to be enough, we only do it when the proxies config has *actually*
changed).
custom_components/bermuda/coordinator.py