diff --git a/homeassistant/components/bluetooth/__init__.py b/homeassistant/components/bluetooth/__init__.py
index 5948d4d135817a249a167960f75753ab0967f8d6..a0eb263757a7ac5781497ee75c8ae652c479cb30 100644
--- a/homeassistant/components/bluetooth/__init__.py
+++ b/homeassistant/components/bluetooth/__init__.py
@@ -21,6 +21,7 @@ from bluetooth_adapters import (
     adapter_unique_name,
     get_adapters,
 )
+from habluetooth import HaBluetoothConnector
 from home_assistant_bluetooth import BluetoothServiceInfo, BluetoothServiceInfoBleak
 
 from homeassistant.components import usb
@@ -77,12 +78,7 @@ from .const import (
 )
 from .manager import BluetoothManager
 from .match import BluetoothCallbackMatcher, IntegrationMatcher
-from .models import (
-    BluetoothCallback,
-    BluetoothChange,
-    BluetoothScanningMode,
-    HaBluetoothConnector,
-)
+from .models import BluetoothCallback, BluetoothChange, BluetoothScanningMode
 from .scanner import MONOTONIC_TIME, HaScanner, ScannerStartError
 from .storage import BluetoothStorage
 
diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py
index f7696c2e90b3eb32eebdca286496bee769574f7e..8267a73fd71e3d04b85f80a304d54bb4d9784812 100644
--- a/homeassistant/components/bluetooth/base_scanner.py
+++ b/homeassistant/components/bluetooth/base_scanner.py
@@ -1,19 +1,14 @@
 """Base classes for HA Bluetooth scanners for bluetooth."""
 from __future__ import annotations
 
-from abc import abstractmethod
-import asyncio
-from collections.abc import Callable, Generator
-from contextlib import contextmanager
+from collections.abc import Callable
 from dataclasses import dataclass
-import logging
-from typing import Any, Final, final
+from typing import Any
 
 from bleak.backends.device import BLEDevice
 from bleak.backends.scanner import AdvertisementData
-from bleak_retry_connector import NO_RSSI_VALUE
-from bluetooth_adapters import DiscoveredDeviceAdvertisementData, adapter_human_name
-from bluetooth_data_tools import monotonic_time_coarse
+from bluetooth_adapters import DiscoveredDeviceAdvertisementData
+from habluetooth import BaseHaRemoteScanner, BaseHaScanner, HaBluetoothConnector
 from home_assistant_bluetooth import BluetoothServiceInfoBleak
 
 from homeassistant.const import EVENT_HOMEASSISTANT_STOP
@@ -25,16 +20,6 @@ from homeassistant.core import (
 )
 
 from . import models
-from .const import (
-    CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
-    SCANNER_WATCHDOG_INTERVAL,
-    SCANNER_WATCHDOG_TIMEOUT,
-)
-from .models import HaBluetoothConnector
-
-SCANNER_WATCHDOG_INTERVAL_SECONDS: Final = SCANNER_WATCHDOG_INTERVAL.total_seconds()
-MONOTONIC_TIME: Final = monotonic_time_coarse
-_LOGGER = logging.getLogger(__name__)
 
 
 @dataclass(slots=True)
@@ -46,363 +31,6 @@ class BluetoothScannerDevice:
     advertisement: AdvertisementData
 
 
-class BaseHaScanner:
-    """Base class for high availability BLE scanners."""
-
-    __slots__ = (
-        "adapter",
-        "connectable",
-        "source",
-        "connector",
-        "_connecting",
-        "name",
-        "scanning",
-        "_last_detection",
-        "_start_time",
-        "_cancel_watchdog",
-        "_loop",
-    )
-
-    def __init__(
-        self,
-        source: str,
-        adapter: str,
-        connector: HaBluetoothConnector | None = None,
-    ) -> None:
-        """Initialize the scanner."""
-        self.connectable = False
-        self.source = source
-        self.connector = connector
-        self._connecting = 0
-        self.adapter = adapter
-        self.name = adapter_human_name(adapter, source) if adapter != source else source
-        self.scanning = True
-        self._last_detection = 0.0
-        self._start_time = 0.0
-        self._cancel_watchdog: asyncio.TimerHandle | None = None
-        self._loop: asyncio.AbstractEventLoop | None = None
-
-    @hass_callback
-    def async_setup(self) -> CALLBACK_TYPE:
-        """Set up the scanner."""
-        self._loop = asyncio.get_running_loop()
-        return self._unsetup
-
-    @hass_callback
-    def _async_stop_scanner_watchdog(self) -> None:
-        """Stop the scanner watchdog."""
-        if self._cancel_watchdog:
-            self._cancel_watchdog.cancel()
-            self._cancel_watchdog = None
-
-    @hass_callback
-    def _async_setup_scanner_watchdog(self) -> None:
-        """If something has restarted or updated, we need to restart the scanner."""
-        self._start_time = self._last_detection = MONOTONIC_TIME()
-        if not self._cancel_watchdog:
-            self._schedule_watchdog()
-
-    def _schedule_watchdog(self) -> None:
-        """Schedule the watchdog."""
-        loop = self._loop
-        assert loop is not None
-        self._cancel_watchdog = loop.call_at(
-            loop.time() + SCANNER_WATCHDOG_INTERVAL_SECONDS,
-            self._async_call_scanner_watchdog,
-        )
-
-    @final
-    def _async_call_scanner_watchdog(self) -> None:
-        """Call the scanner watchdog and schedule the next one."""
-        self._async_scanner_watchdog()
-        self._schedule_watchdog()
-
-    @hass_callback
-    def _async_watchdog_triggered(self) -> bool:
-        """Check if the watchdog has been triggered."""
-        time_since_last_detection = MONOTONIC_TIME() - self._last_detection
-        _LOGGER.debug(
-            "%s: Scanner watchdog time_since_last_detection: %s",
-            self.name,
-            time_since_last_detection,
-        )
-        return time_since_last_detection > SCANNER_WATCHDOG_TIMEOUT
-
-    @hass_callback
-    def _async_scanner_watchdog(self) -> None:
-        """Check if the scanner is running.
-
-        Override this method if you need to do something else when the watchdog
-        is triggered.
-        """
-        if self._async_watchdog_triggered():
-            _LOGGER.info(
-                (
-                    "%s: Bluetooth scanner has gone quiet for %ss, check logs on the"
-                    " scanner device for more information"
-                ),
-                self.name,
-                SCANNER_WATCHDOG_TIMEOUT,
-            )
-            self.scanning = False
-            return
-        self.scanning = not self._connecting
-
-    @hass_callback
-    def _unsetup(self) -> None:
-        """Unset up the scanner."""
-
-    @contextmanager
-    def connecting(self) -> Generator[None, None, None]:
-        """Context manager to track connecting state."""
-        self._connecting += 1
-        self.scanning = not self._connecting
-        try:
-            yield
-        finally:
-            self._connecting -= 1
-            self.scanning = not self._connecting
-
-    @property
-    @abstractmethod
-    def discovered_devices(self) -> list[BLEDevice]:
-        """Return a list of discovered devices."""
-
-    @property
-    @abstractmethod
-    def discovered_devices_and_advertisement_data(
-        self,
-    ) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
-        """Return a list of discovered devices and their advertisement data."""
-
-    async def async_diagnostics(self) -> dict[str, Any]:
-        """Return diagnostic information about the scanner."""
-        device_adv_datas = self.discovered_devices_and_advertisement_data.values()
-        return {
-            "name": self.name,
-            "start_time": self._start_time,
-            "source": self.source,
-            "scanning": self.scanning,
-            "type": self.__class__.__name__,
-            "last_detection": self._last_detection,
-            "monotonic_time": MONOTONIC_TIME(),
-            "discovered_devices_and_advertisement_data": [
-                {
-                    "name": device.name,
-                    "address": device.address,
-                    "rssi": advertisement_data.rssi,
-                    "advertisement_data": advertisement_data,
-                    "details": device.details,
-                }
-                for device, advertisement_data in device_adv_datas
-            ],
-        }
-
-
-class BaseHaRemoteScanner(BaseHaScanner):
-    """Base class for a high availability remote BLE scanner."""
-
-    __slots__ = (
-        "_new_info_callback",
-        "_discovered_device_advertisement_datas",
-        "_discovered_device_timestamps",
-        "_details",
-        "_expire_seconds",
-        "_cancel_track",
-    )
-
-    def __init__(
-        self,
-        scanner_id: str,
-        name: str,
-        new_info_callback: Callable[[BluetoothServiceInfoBleak], None],
-        connector: HaBluetoothConnector | None,
-        connectable: bool,
-    ) -> None:
-        """Initialize the scanner."""
-        super().__init__(scanner_id, name, connector)
-        self._new_info_callback = new_info_callback
-        self._discovered_device_advertisement_datas: dict[
-            str, tuple[BLEDevice, AdvertisementData]
-        ] = {}
-        self._discovered_device_timestamps: dict[str, float] = {}
-        self.connectable = connectable
-        self._details: dict[str, str | HaBluetoothConnector] = {"source": scanner_id}
-        # Scanners only care about connectable devices. The manager
-        # will handle taking care of availability for non-connectable devices
-        self._expire_seconds = CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS
-        self._cancel_track: asyncio.TimerHandle | None = None
-
-    def _cancel_expire_devices(self) -> None:
-        """Cancel the expiration of old devices."""
-        if self._cancel_track:
-            self._cancel_track.cancel()
-            self._cancel_track = None
-
-    @hass_callback
-    def _unsetup(self) -> None:
-        """Unset up the scanner."""
-        self._async_stop_scanner_watchdog()
-        self._cancel_expire_devices()
-
-    @hass_callback
-    def async_setup(self) -> CALLBACK_TYPE:
-        """Set up the scanner."""
-        super().async_setup()
-        self._schedule_expire_devices()
-        self._async_setup_scanner_watchdog()
-        return self._unsetup
-
-    def _schedule_expire_devices(self) -> None:
-        """Schedule the expiration of old devices."""
-        loop = self._loop
-        assert loop is not None
-        self._cancel_expire_devices()
-        self._cancel_track = loop.call_at(loop.time() + 30, self._async_expire_devices)
-
-    @hass_callback
-    def _async_expire_devices(self) -> None:
-        """Expire old devices."""
-        now = MONOTONIC_TIME()
-        expired = [
-            address
-            for address, timestamp in self._discovered_device_timestamps.items()
-            if now - timestamp > self._expire_seconds
-        ]
-        for address in expired:
-            del self._discovered_device_advertisement_datas[address]
-            del self._discovered_device_timestamps[address]
-        self._schedule_expire_devices()
-
-    @property
-    def discovered_devices(self) -> list[BLEDevice]:
-        """Return a list of discovered devices."""
-        device_adv_datas = self._discovered_device_advertisement_datas.values()
-        return [
-            device_advertisement_data[0]
-            for device_advertisement_data in device_adv_datas
-        ]
-
-    @property
-    def discovered_devices_and_advertisement_data(
-        self,
-    ) -> dict[str, tuple[BLEDevice, AdvertisementData]]:
-        """Return a list of discovered devices and advertisement data."""
-        return self._discovered_device_advertisement_datas
-
-    @hass_callback
-    def _async_on_advertisement(
-        self,
-        address: str,
-        rssi: int,
-        local_name: str | None,
-        service_uuids: list[str],
-        service_data: dict[str, bytes],
-        manufacturer_data: dict[int, bytes],
-        tx_power: int | None,
-        details: dict[Any, Any],
-        advertisement_monotonic_time: float,
-    ) -> None:
-        """Call the registered callback."""
-        self.scanning = not self._connecting
-        self._last_detection = advertisement_monotonic_time
-        try:
-            prev_discovery = self._discovered_device_advertisement_datas[address]
-        except KeyError:
-            # We expect this is the rare case and since py3.11+ has
-            # near zero cost try on success, and we can avoid .get()
-            # which is slower than [] we use the try/except pattern.
-            device = BLEDevice(
-                address=address,
-                name=local_name,
-                details=self._details | details,
-                rssi=rssi,  # deprecated, will be removed in newer bleak
-            )
-        else:
-            # Merge the new data with the old data
-            # to function the same as BlueZ which
-            # merges the dicts on PropertiesChanged
-            prev_device = prev_discovery[0]
-            prev_advertisement = prev_discovery[1]
-            prev_service_uuids = prev_advertisement.service_uuids
-            prev_service_data = prev_advertisement.service_data
-            prev_manufacturer_data = prev_advertisement.manufacturer_data
-            prev_name = prev_device.name
-
-            if prev_name and (not local_name or len(prev_name) > len(local_name)):
-                local_name = prev_name
-
-            if service_uuids and service_uuids != prev_service_uuids:
-                service_uuids = list({*service_uuids, *prev_service_uuids})
-            elif not service_uuids:
-                service_uuids = prev_service_uuids
-
-            if service_data and service_data != prev_service_data:
-                service_data = prev_service_data | service_data
-            elif not service_data:
-                service_data = prev_service_data
-
-            if manufacturer_data and manufacturer_data != prev_manufacturer_data:
-                manufacturer_data = prev_manufacturer_data | manufacturer_data
-            elif not manufacturer_data:
-                manufacturer_data = prev_manufacturer_data
-            #
-            # 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
-
-        advertisement_data = AdvertisementData(
-            local_name=None if local_name == "" else local_name,
-            manufacturer_data=manufacturer_data,
-            service_data=service_data,
-            service_uuids=service_uuids,
-            tx_power=NO_RSSI_VALUE if tx_power is None else tx_power,
-            rssi=rssi,
-            platform_data=(),
-        )
-        self._discovered_device_advertisement_datas[address] = (
-            device,
-            advertisement_data,
-        )
-        self._discovered_device_timestamps[address] = advertisement_monotonic_time
-        self._new_info_callback(
-            BluetoothServiceInfoBleak(
-                name=local_name or address,
-                address=address,
-                rssi=rssi,
-                manufacturer_data=manufacturer_data,
-                service_data=service_data,
-                service_uuids=service_uuids,
-                source=self.source,
-                device=device,
-                advertisement=advertisement_data,
-                connectable=self.connectable,
-                time=advertisement_monotonic_time,
-            )
-        )
-
-    async def async_diagnostics(self) -> dict[str, Any]:
-        """Return diagnostic information about the scanner."""
-        now = MONOTONIC_TIME()
-        return await super().async_diagnostics() | {
-            "connectable": self.connectable,
-            "discovered_device_timestamps": self._discovered_device_timestamps,
-            "time_since_last_device_detection": {
-                address: now - timestamp
-                for address, timestamp in self._discovered_device_timestamps.items()
-            },
-        }
-
-
 class HomeAssistantRemoteScanner(BaseHaRemoteScanner):
     """Home Assistant remote BLE scanner.
 
diff --git a/homeassistant/components/bluetooth/const.py b/homeassistant/components/bluetooth/const.py
index 150239eec021eaab7b655d60aab119ab283a73ff..fa8efabcb1d7620680b3873170c165f9f5b833f9 100644
--- a/homeassistant/components/bluetooth/const.py
+++ b/homeassistant/components/bluetooth/const.py
@@ -1,9 +1,15 @@
 """Constants for the Bluetooth integration."""
 from __future__ import annotations
 
-from datetime import timedelta
 from typing import Final
 
+from habluetooth import (  # noqa: F401
+    CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
+    FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
+    SCANNER_WATCHDOG_INTERVAL,
+    SCANNER_WATCHDOG_TIMEOUT,
+)
+
 DOMAIN = "bluetooth"
 
 CONF_ADAPTER = "adapter"
@@ -19,42 +25,6 @@ UNAVAILABLE_TRACK_SECONDS: Final = 60 * 5
 
 START_TIMEOUT = 15
 
-# The maximum time between advertisements for a device to be considered
-# stale when the advertisement tracker cannot determine the interval.
-#
-# We have to set this quite high as we don't know
-# when devices fall out of the ESPHome device (and other non-local scanners)'s
-# stack like we do with BlueZ so its safer to assume its available
-# since if it does go out of range and it is in range
-# of another device the timeout is much shorter and it will
-# switch over to using that adapter anyways.
-#
-FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 60 * 15
-
-# The maximum time between advertisements for a device to be considered
-# stale when the advertisement tracker can determine the interval for
-# connectable devices.
-#
-# BlueZ uses 180 seconds by default but we give it a bit more time
-# to account for the esp32's bluetooth stack being a bit slower
-# than BlueZ's.
-CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS: Final = 195
-
-
-# We must recover before we hit the 180s mark
-# where the device is removed from the stack
-# or the devices will go unavailable. Since
-# we only check every 30s, we need this number
-# to be
-# 180s Time when device is removed from stack
-# - 30s check interval
-# - 30s scanner restart time * 2
-#
-SCANNER_WATCHDOG_TIMEOUT: Final = 90
-# How often to check if the scanner has reached
-# the SCANNER_WATCHDOG_TIMEOUT without seeing anything
-SCANNER_WATCHDOG_INTERVAL: Final = timedelta(seconds=30)
-
 
 # When the linux kernel is configured with
 # CONFIG_FW_LOADER_USER_HELPER_FALLBACK it
diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json
index bb052a3a042343b18eb4471e4a164a931b89f7f4..74cec5edfe20886129b6eb6a069f31642a9dc768 100644
--- a/homeassistant/components/bluetooth/manifest.json
+++ b/homeassistant/components/bluetooth/manifest.json
@@ -19,6 +19,7 @@
     "bluetooth-adapters==0.16.1",
     "bluetooth-auto-recovery==1.2.3",
     "bluetooth-data-tools==1.17.0",
-    "dbus-fast==2.14.0"
+    "dbus-fast==2.14.0",
+    "habluetooth==0.1.0"
   ]
 }
diff --git a/homeassistant/components/bluetooth/models.py b/homeassistant/components/bluetooth/models.py
index 1b8c12c6eb3f994f00546031c82bbf15d0fbd29c..48ba021cd6c2d559a0391d8a05f83258897d9be5 100644
--- a/homeassistant/components/bluetooth/models.py
+++ b/homeassistant/components/bluetooth/models.py
@@ -2,11 +2,9 @@
 from __future__ import annotations
 
 from collections.abc import Callable
-from dataclasses import dataclass
 from enum import Enum
 from typing import TYPE_CHECKING, Final
 
-from bleak import BaseBleakClient
 from bluetooth_data_tools import monotonic_time_coarse
 from home_assistant_bluetooth import BluetoothServiceInfoBleak
 
@@ -19,15 +17,6 @@ MANAGER: BluetoothManager | None = None
 MONOTONIC_TIME: Final = monotonic_time_coarse
 
 
-@dataclass(slots=True)
-class HaBluetoothConnector:
-    """Data for how to connect a BLEDevice from a given scanner."""
-
-    client: type[BaseBleakClient]
-    source: str
-    can_connect: Callable[[], bool]
-
-
 class BluetoothScanningMode(Enum):
     """The mode of scanning for bluetooth devices."""
 
diff --git a/homeassistant/components/bluetooth/scanner.py b/homeassistant/components/bluetooth/scanner.py
index 712fe1c0d9ab28b63b1e27a2697f232cb7566551..203bdac68a6c4f65b43c5fe4a35a24f53c01d3ff 100644
--- a/homeassistant/components/bluetooth/scanner.py
+++ b/homeassistant/components/bluetooth/scanner.py
@@ -16,13 +16,14 @@ from bleak.backends.device import BLEDevice
 from bleak.backends.scanner import AdvertisementData, AdvertisementDataCallback
 from bleak_retry_connector import restore_discoveries
 from bluetooth_adapters import DEFAULT_ADDRESS
+from bluetooth_data_tools import monotonic_time_coarse as MONOTONIC_TIME
 from dbus_fast import InvalidMessageError
 
 from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback as hass_callback
 from homeassistant.exceptions import HomeAssistantError
 from homeassistant.util.package import is_docker_env
 
-from .base_scanner import MONOTONIC_TIME, BaseHaScanner
+from .base_scanner import BaseHaScanner
 from .const import (
     SCANNER_WATCHDOG_INTERVAL,
     SCANNER_WATCHDOG_TIMEOUT,
diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt
index 06892125940ddd46daa9d6f2a1d8e2d7eecd2b22..f7f67dcbf7adfbcadde46b4e7d6d615754cc5c99 100644
--- a/homeassistant/package_constraints.txt
+++ b/homeassistant/package_constraints.txt
@@ -23,6 +23,7 @@ dbus-fast==2.14.0
 fnv-hash-fast==0.5.0
 ha-av==10.1.1
 ha-ffmpeg==3.1.0
+habluetooth==0.1.0
 hass-nabucasa==0.74.0
 hassil==1.5.1
 home-assistant-bluetooth==1.10.4
diff --git a/requirements_all.txt b/requirements_all.txt
index 94895a49444c26f70640fc06c4197ba62e63ef61..640c050e05fa9f60d47bd5221f8cdec323187c93 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -983,6 +983,9 @@ ha-philipsjs==3.1.1
 # homeassistant.components.habitica
 habitipy==0.2.0
 
+# homeassistant.components.bluetooth
+habluetooth==0.1.0
+
 # homeassistant.components.cloud
 hass-nabucasa==0.74.0
 
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index e3424447c2fec8be37654fa37735cf7a5a4aef83..608a53b46f66d3f7393465a122ee84dfa64b9a95 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -782,6 +782,9 @@ ha-philipsjs==3.1.1
 # homeassistant.components.habitica
 habitipy==0.2.0
 
+# homeassistant.components.bluetooth
+habluetooth==0.1.0
+
 # homeassistant.components.cloud
 hass-nabucasa==0.74.0
 
diff --git a/tests/components/bluetooth/test_advertisement_tracker.py b/tests/components/bluetooth/test_advertisement_tracker.py
index f04ea2873f01c0136477b4124f9a7e20a04d3f96..6ae847ba84a3133be7858a3390ffd3c2abf08ab0 100644
--- a/tests/components/bluetooth/test_advertisement_tracker.py
+++ b/tests/components/bluetooth/test_advertisement_tracker.py
@@ -352,7 +352,7 @@ async def test_advertisment_interval_longer_than_adapter_stack_timeout_adapter_c
     )
     switchbot_device_went_unavailable = False
 
-    scanner = FakeScanner(hass, "new", "fake_adapter")
+    scanner = FakeScanner("new", "fake_adapter")
     cancel_scanner = async_register_scanner(hass, scanner, False)
 
     @callback
diff --git a/tests/components/bluetooth/test_base_scanner.py b/tests/components/bluetooth/test_base_scanner.py
index a39f18e037ec047121da45e3534bfb786dc75ef8..5886cc10aac17aa805f00dff4f8ca31258573b81 100644
--- a/tests/components/bluetooth/test_base_scanner.py
+++ b/tests/components/bluetooth/test_base_scanner.py
@@ -215,7 +215,7 @@ async def test_remote_scanner_expires_connectable(
         seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
     )
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=expire_monotonic,
     ):
         async_fire_time_changed(hass, expire_utc)
@@ -298,7 +298,7 @@ async def test_remote_scanner_expires_non_connectable(
         seconds=CONNECTABLE_FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
     )
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=expire_monotonic,
     ):
         async_fire_time_changed(hass, expire_utc)
@@ -314,7 +314,7 @@ async def test_remote_scanner_expires_non_connectable(
         seconds=FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
     )
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=expire_monotonic,
     ):
         async_fire_time_changed(hass, expire_utc)
@@ -515,7 +515,7 @@ async def test_device_with_ten_minute_advertising_interval(
     )
 
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=new_time,
     ):
         scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
@@ -528,7 +528,7 @@ async def test_device_with_ten_minute_advertising_interval(
     for _ in range(1, 20):
         new_time += advertising_interval
         with patch(
-            "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+            "habluetooth.base_scanner.MONOTONIC_TIME",
             return_value=new_time,
         ):
             scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
@@ -562,7 +562,7 @@ async def test_device_with_ten_minute_advertising_interval(
         "homeassistant.components.bluetooth.manager.MONOTONIC_TIME",
         return_value=missed_advertisement_future_time,
     ), patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=missed_advertisement_future_time,
     ):
         # Fire once for the scanner to expire the device
@@ -629,7 +629,7 @@ async def test_scanner_stops_responding(
     )
     # We hit the timer with no detections, so we reset the adapter and restart the scanner
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=failure_reached_time,
     ):
         async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
@@ -653,7 +653,7 @@ async def test_scanner_stops_responding(
     failure_reached_time += 1
 
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=failure_reached_time,
     ):
         scanner.inject_advertisement(bparasite_device, bparasite_device_adv)
diff --git a/tests/components/bluetooth/test_init.py b/tests/components/bluetooth/test_init.py
index 21fade843f54c5bc35fa17f4b20adba62e0b504e..b24bb97e1e384a63f97cabb2a7086e0124fb067a 100644
--- a/tests/components/bluetooth/test_init.py
+++ b/tests/components/bluetooth/test_init.py
@@ -2815,7 +2815,7 @@ async def test_scanner_count_connectable(
     hass: HomeAssistant, enable_bluetooth: None
 ) -> None:
     """Test getting the connectable scanner count."""
-    scanner = FakeScanner(hass, "any", "any")
+    scanner = FakeScanner("any", "any")
     cancel = bluetooth.async_register_scanner(hass, scanner, False)
     assert bluetooth.async_scanner_count(hass, connectable=True) == 1
     cancel()
@@ -2823,7 +2823,7 @@ async def test_scanner_count_connectable(
 
 async def test_scanner_count(hass: HomeAssistant, enable_bluetooth: None) -> None:
     """Test getting the connectable and non-connectable scanner count."""
-    scanner = FakeScanner(hass, "any", "any")
+    scanner = FakeScanner("any", "any")
     cancel = bluetooth.async_register_scanner(hass, scanner, False)
     assert bluetooth.async_scanner_count(hass, connectable=False) == 2
     cancel()
diff --git a/tests/components/bluetooth/test_models.py b/tests/components/bluetooth/test_models.py
index 1d07ab75a485ca540733ffad569860e7e58c098f..8cffbe685b6590709b02c189b097704e5d2c8bcb 100644
--- a/tests/components/bluetooth/test_models.py
+++ b/tests/components/bluetooth/test_models.py
@@ -107,7 +107,6 @@ async def test_wrapped_bleak_client_local_adapter_only(
             return None
 
     scanner = FakeScanner(
-        hass,
         "00:00:00:00:00:01",
         "hci0",
     )
diff --git a/tests/components/bluetooth/test_scanner.py b/tests/components/bluetooth/test_scanner.py
index bc32a5b302d4831adc1014671921a78e67c05ad2..b660be74aa91fe0f6ac522eec01b893cf77ad7d9 100644
--- a/tests/components/bluetooth/test_scanner.py
+++ b/tests/components/bluetooth/test_scanner.py
@@ -228,7 +228,7 @@ async def test_recovery_from_dbus_restart(
 
     # Ensure we don't restart the scanner if we don't need to
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic + 10,
     ):
         async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
@@ -238,7 +238,7 @@ async def test_recovery_from_dbus_restart(
 
     # Fire a callback to reset the timer
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic,
     ):
         _callback(
@@ -248,7 +248,7 @@ async def test_recovery_from_dbus_restart(
 
     # Ensure we don't restart the scanner if we don't need to
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic + 20,
     ):
         async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
@@ -258,7 +258,7 @@ async def test_recovery_from_dbus_restart(
 
     # We hit the timer, so we restart the scanner
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic + SCANNER_WATCHDOG_TIMEOUT + 20,
     ):
         async_fire_time_changed(
@@ -303,7 +303,7 @@ async def test_adapter_recovery(hass: HomeAssistant, one_adapter: None) -> None:
     start_time_monotonic = time.monotonic()
 
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic,
     ), patch(
         "homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
@@ -318,7 +318,7 @@ async def test_adapter_recovery(hass: HomeAssistant, one_adapter: None) -> None:
 
     # Ensure we don't restart the scanner if we don't need to
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic + 10,
     ):
         async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
@@ -328,7 +328,7 @@ async def test_adapter_recovery(hass: HomeAssistant, one_adapter: None) -> None:
 
     # Ensure we don't restart the scanner if we don't need to
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic + 20,
     ):
         async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
@@ -338,7 +338,7 @@ async def test_adapter_recovery(hass: HomeAssistant, one_adapter: None) -> None:
 
     # We hit the timer with no detections, so we reset the adapter and restart the scanner
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic
         + SCANNER_WATCHDOG_TIMEOUT
         + SCANNER_WATCHDOG_INTERVAL.total_seconds(),
@@ -392,7 +392,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
     start_time_monotonic = time.monotonic()
 
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic,
     ), patch(
         "homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
@@ -407,7 +407,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
 
     # Ensure we don't restart the scanner if we don't need to
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic + 10,
     ):
         async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
@@ -417,7 +417,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
 
     # Ensure we don't restart the scanner if we don't need to
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic + 20,
     ):
         async_fire_time_changed(hass, dt_util.utcnow() + SCANNER_WATCHDOG_INTERVAL)
@@ -427,7 +427,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
 
     # We hit the timer with no detections, so we reset the adapter and restart the scanner
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic
         + SCANNER_WATCHDOG_TIMEOUT
         + SCANNER_WATCHDOG_INTERVAL.total_seconds(),
@@ -443,7 +443,7 @@ async def test_adapter_scanner_fails_to_start_first_time(
     # We hit the timer again the previous start call failed, make sure
     # we try again
     with patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic
         + SCANNER_WATCHDOG_TIMEOUT
         + SCANNER_WATCHDOG_INTERVAL.total_seconds(),
@@ -506,7 +506,7 @@ async def test_adapter_fails_to_start_and_takes_a_bit_to_init(
         "homeassistant.components.bluetooth.scanner.ADAPTER_INIT_TIME",
         0,
     ), patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic,
     ), patch(
         "homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
@@ -557,7 +557,7 @@ async def test_restart_takes_longer_than_watchdog_time(
         "homeassistant.components.bluetooth.scanner.ADAPTER_INIT_TIME",
         0,
     ), patch(
-        "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+        "habluetooth.base_scanner.MONOTONIC_TIME",
         return_value=start_time_monotonic,
     ), patch(
         "homeassistant.components.bluetooth.scanner.OriginalBleakScanner",
@@ -572,7 +572,7 @@ async def test_restart_takes_longer_than_watchdog_time(
         # Now force a recover adapter 2x
         for _ in range(2):
             with patch(
-                "homeassistant.components.bluetooth.base_scanner.MONOTONIC_TIME",
+                "habluetooth.base_scanner.MONOTONIC_TIME",
                 return_value=start_time_monotonic
                 + SCANNER_WATCHDOG_TIMEOUT
                 + SCANNER_WATCHDOG_INTERVAL.total_seconds(),