From f7475a5bdb9ad505a9edeccc3ed2928526450dee Mon Sep 17 00:00:00 2001
From: Martin Hjelmare <marhje52@gmail.com>
Date: Tue, 24 May 2022 23:52:07 +0200
Subject: [PATCH] Clean zwave_js entity driver access (#72427)

---
 homeassistant/components/zwave_js/__init__.py | 28 ++++++-------
 .../components/zwave_js/binary_sensor.py      | 21 ++++++----
 homeassistant/components/zwave_js/button.py   | 11 +++--
 homeassistant/components/zwave_js/climate.py  | 15 ++++---
 homeassistant/components/zwave_js/cover.py    | 21 ++++++----
 homeassistant/components/zwave_js/entity.py   | 19 +++++----
 homeassistant/components/zwave_js/fan.py      | 21 ++++++----
 homeassistant/components/zwave_js/helpers.py  | 16 +++----
 .../components/zwave_js/humidifier.py         | 11 +++--
 homeassistant/components/zwave_js/light.py    | 15 ++++---
 homeassistant/components/zwave_js/lock.py     |  4 +-
 homeassistant/components/zwave_js/migrate.py  |  8 ++--
 homeassistant/components/zwave_js/number.py   | 15 ++++---
 homeassistant/components/zwave_js/select.py   | 21 ++++++----
 homeassistant/components/zwave_js/sensor.py   | 36 +++++++++-------
 homeassistant/components/zwave_js/siren.py    |  9 ++--
 homeassistant/components/zwave_js/switch.py   | 15 ++++---
 .../components/zwave_js/triggers/event.py     |  4 +-
 .../zwave_js/triggers/value_updated.py        |  4 +-
 tests/components/zwave_js/test_api.py         |  7 ++--
 tests/components/zwave_js/test_button.py      |  5 ++-
 .../components/zwave_js/test_device_action.py | 32 ++++++++++----
 tests/components/zwave_js/test_diagnostics.py |  2 +-
 tests/components/zwave_js/test_init.py        |  8 ++--
 tests/components/zwave_js/test_migrate.py     | 42 ++++++++++++-------
 tests/components/zwave_js/test_sensor.py      |  5 ++-
 tests/components/zwave_js/test_services.py    | 20 ++++++---
 27 files changed, 250 insertions(+), 165 deletions(-)

diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py
index 9f928eb7d3d..5c583d8321f 100644
--- a/homeassistant/components/zwave_js/__init__.py
+++ b/homeassistant/components/zwave_js/__init__.py
@@ -124,13 +124,13 @@ def register_node_in_dev_reg(
     hass: HomeAssistant,
     entry: ConfigEntry,
     dev_reg: device_registry.DeviceRegistry,
-    client: ZwaveClient,
+    driver: Driver,
     node: ZwaveNode,
     remove_device_func: Callable[[device_registry.DeviceEntry], None],
 ) -> device_registry.DeviceEntry:
     """Register node in dev reg."""
-    device_id = get_device_id(client, node)
-    device_id_ext = get_device_id_ext(client, node)
+    device_id = get_device_id(driver, node)
+    device_id_ext = get_device_id_ext(driver, node)
     device = dev_reg.async_get_device({device_id})
 
     # Replace the device if it can be determined that this node is not the
@@ -281,7 +281,7 @@ async def setup_driver(  # noqa: C901
             ent_reg,
             registered_unique_ids[device.id][disc_info.platform],
             device,
-            client,
+            driver,
             disc_info,
         )
 
@@ -316,7 +316,7 @@ async def setup_driver(  # noqa: C901
         LOGGER.debug("Processing node %s", node)
         # register (or update) node in device registry
         device = register_node_in_dev_reg(
-            hass, entry, dev_reg, client, node, remove_device
+            hass, entry, dev_reg, driver, node, remove_device
         )
         # We only want to create the defaultdict once, even on reinterviews
         if device.id not in registered_unique_ids:
@@ -384,7 +384,7 @@ async def setup_driver(  # noqa: C901
         )
         # we do submit the node to device registry so user has
         # some visual feedback that something is (in the process of) being added
-        register_node_in_dev_reg(hass, entry, dev_reg, client, node, remove_device)
+        register_node_in_dev_reg(hass, entry, dev_reg, driver, node, remove_device)
 
     async def async_on_value_added(
         value_updates_disc_info: dict[str, ZwaveDiscoveryInfo], value: Value
@@ -393,7 +393,7 @@ async def setup_driver(  # noqa: C901
         # If node isn't ready or a device for this node doesn't already exist, we can
         # let the node ready event handler perform discovery. If a value has already
         # been processed, we don't need to do it again
-        device_id = get_device_id(client, value.node)
+        device_id = get_device_id(driver, value.node)
         if (
             not value.node.ready
             or not (device := dev_reg.async_get_device({device_id}))
@@ -417,7 +417,7 @@ async def setup_driver(  # noqa: C901
         node: ZwaveNode = event["node"]
         replaced: bool = event.get("replaced", False)
         # grab device in device registry attached to this node
-        dev_id = get_device_id(client, node)
+        dev_id = get_device_id(driver, node)
         device = dev_reg.async_get_device({dev_id})
         # We assert because we know the device exists
         assert device
@@ -426,7 +426,7 @@ async def setup_driver(  # noqa: C901
 
             async_dispatcher_send(
                 hass,
-                f"{DOMAIN}_{get_valueless_base_unique_id(client, node)}_remove_entity",
+                f"{DOMAIN}_{get_valueless_base_unique_id(driver, node)}_remove_entity",
             )
         else:
             remove_device(device)
@@ -434,7 +434,7 @@ async def setup_driver(  # noqa: C901
     @callback
     def async_on_value_notification(notification: ValueNotification) -> None:
         """Relay stateless value notification events from Z-Wave nodes to hass."""
-        device = dev_reg.async_get_device({get_device_id(client, notification.node)})
+        device = dev_reg.async_get_device({get_device_id(driver, notification.node)})
         # We assert because we know the device exists
         assert device
         raw_value = value = notification.value
@@ -469,7 +469,7 @@ async def setup_driver(  # noqa: C901
         notification: EntryControlNotification | NotificationNotification | PowerLevelNotification | MultilevelSwitchNotification = event[
             "notification"
         ]
-        device = dev_reg.async_get_device({get_device_id(client, notification.node)})
+        device = dev_reg.async_get_device({get_device_id(driver, notification.node)})
         # We assert because we know the device exists
         assert device
         event_data = {
@@ -533,11 +533,11 @@ async def setup_driver(  # noqa: C901
             return
         disc_info = value_updates_disc_info[value.value_id]
 
-        device = dev_reg.async_get_device({get_device_id(client, value.node)})
+        device = dev_reg.async_get_device({get_device_id(driver, value.node)})
         # We assert because we know the device exists
         assert device
 
-        unique_id = get_unique_id(client, disc_info.primary_value.value_id)
+        unique_id = get_unique_id(driver, disc_info.primary_value.value_id)
         entity_id = ent_reg.async_get_entity_id(disc_info.platform, DOMAIN, unique_id)
 
         raw_value = value_ = value.value
@@ -575,7 +575,7 @@ async def setup_driver(  # noqa: C901
         dev_reg, entry.entry_id
     )
     known_devices = [
-        dev_reg.async_get_device({get_device_id(client, node)})
+        dev_reg.async_get_device({get_device_id(driver, node)})
         for node in driver.controller.nodes.values()
     ]
 
diff --git a/homeassistant/components/zwave_js/binary_sensor.py b/homeassistant/components/zwave_js/binary_sensor.py
index 8a86898239a..7e9882377bd 100644
--- a/homeassistant/components/zwave_js/binary_sensor.py
+++ b/homeassistant/components/zwave_js/binary_sensor.py
@@ -10,6 +10,7 @@ from zwave_js_server.const.command_class.lock import DOOR_STATUS_PROPERTY
 from zwave_js_server.const.command_class.notification import (
     CC_SPECIFIC_NOTIFICATION_TYPE,
 )
+from zwave_js_server.model.driver import Driver
 
 from homeassistant.components.binary_sensor import (
     DOMAIN as BINARY_SENSOR_DOMAIN,
@@ -267,6 +268,8 @@ async def async_setup_entry(
     @callback
     def async_add_binary_sensor(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave Binary Sensor."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[BinarySensorEntity] = []
 
         if info.platform_hint == "notification":
@@ -298,7 +301,7 @@ async def async_setup_entry(
 
                 entities.append(
                     ZWaveNotificationBinarySensor(
-                        config_entry, client, info, state_key, notification_description
+                        config_entry, driver, info, state_key, notification_description
                     )
                 )
         elif info.platform_hint == "property" and (
@@ -308,12 +311,12 @@ async def async_setup_entry(
         ):
             entities.append(
                 ZWavePropertyBinarySensor(
-                    config_entry, client, info, property_description
+                    config_entry, driver, info, property_description
                 )
             )
         else:
             # boolean sensor
-            entities.append(ZWaveBooleanBinarySensor(config_entry, client, info))
+            entities.append(ZWaveBooleanBinarySensor(config_entry, driver, info))
 
         async_add_entities(entities)
 
@@ -332,11 +335,11 @@ class ZWaveBooleanBinarySensor(ZWaveBaseEntity, BinarySensorEntity):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
     ) -> None:
         """Initialize a ZWaveBooleanBinarySensor entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
 
         # Entity class attributes
         self._attr_name = self.generate_name(include_value_name=True)
@@ -359,13 +362,13 @@ class ZWaveNotificationBinarySensor(ZWaveBaseEntity, BinarySensorEntity):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
         state_key: str,
         description: NotificationZWaveJSEntityDescription | None = None,
     ) -> None:
         """Initialize a ZWaveNotificationBinarySensor entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self.state_key = state_key
         if description:
             self.entity_description = description
@@ -394,12 +397,12 @@ class ZWavePropertyBinarySensor(ZWaveBaseEntity, BinarySensorEntity):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
         description: PropertyZWaveJSEntityDescription,
     ) -> None:
         """Initialize a ZWavePropertyBinarySensor entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self.entity_description = description
         self._attr_name = self.generate_name(include_value_name=True)
 
diff --git a/homeassistant/components/zwave_js/button.py b/homeassistant/components/zwave_js/button.py
index 49c2a76cccf..cef64f1724a 100644
--- a/homeassistant/components/zwave_js/button.py
+++ b/homeassistant/components/zwave_js/button.py
@@ -2,6 +2,7 @@
 from __future__ import annotations
 
 from zwave_js_server.client import Client as ZwaveClient
+from zwave_js_server.model.driver import Driver
 from zwave_js_server.model.node import Node as ZwaveNode
 
 from homeassistant.components.button import ButtonEntity
@@ -28,7 +29,9 @@ async def async_setup_entry(
     @callback
     def async_add_ping_button_entity(node: ZwaveNode) -> None:
         """Add ping button entity."""
-        async_add_entities([ZWaveNodePingButton(client, node)])
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
+        async_add_entities([ZWaveNodePingButton(driver, node)])
 
     config_entry.async_on_unload(
         async_dispatcher_connect(
@@ -45,7 +48,7 @@ class ZWaveNodePingButton(ButtonEntity):
     _attr_should_poll = False
     _attr_entity_category = EntityCategory.CONFIG
 
-    def __init__(self, client: ZwaveClient, node: ZwaveNode) -> None:
+    def __init__(self, driver: Driver, node: ZwaveNode) -> None:
         """Initialize a ping Z-Wave device button entity."""
         self.node = node
         name: str = (
@@ -53,11 +56,11 @@ class ZWaveNodePingButton(ButtonEntity):
         )
         # Entity class attributes
         self._attr_name = f"{name}: Ping"
-        self._base_unique_id = get_valueless_base_unique_id(client, node)
+        self._base_unique_id = get_valueless_base_unique_id(driver, node)
         self._attr_unique_id = f"{self._base_unique_id}.ping"
         # device is precreated in main handler
         self._attr_device_info = DeviceInfo(
-            identifiers={get_device_id(client, node)},
+            identifiers={get_device_id(driver, node)},
         )
 
     async def async_poll_value(self, _: bool) -> None:
diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py
index 67def698fc2..f721db41d9f 100644
--- a/homeassistant/components/zwave_js/climate.py
+++ b/homeassistant/components/zwave_js/climate.py
@@ -17,6 +17,7 @@ from zwave_js_server.const.command_class.thermostat import (
     ThermostatOperatingState,
     ThermostatSetpointType,
 )
+from zwave_js_server.model.driver import Driver
 from zwave_js_server.model.value import Value as ZwaveValue
 
 from homeassistant.components.climate import (
@@ -103,11 +104,13 @@ async def async_setup_entry(
     @callback
     def async_add_climate(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave Climate."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
         if info.platform_hint == "dynamic_current_temp":
-            entities.append(DynamicCurrentTempClimate(config_entry, client, info))
+            entities.append(DynamicCurrentTempClimate(config_entry, driver, info))
         else:
-            entities.append(ZWaveClimate(config_entry, client, info))
+            entities.append(ZWaveClimate(config_entry, driver, info))
 
         async_add_entities(entities)
 
@@ -124,10 +127,10 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
     """Representation of a Z-Wave climate."""
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize thermostat."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self._hvac_modes: dict[HVACMode, int | None] = {}
         self._hvac_presets: dict[str, int | None] = {}
         self._unit_value: ZwaveValue | None = None
@@ -479,10 +482,10 @@ class DynamicCurrentTempClimate(ZWaveClimate):
     """Representation of a thermostat that can dynamically use a different Zwave Value for current temp."""
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize thermostat."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self.data_template = cast(
             DynamicCurrentTempClimateDataTemplate, self.info.platform_data_template
         )
diff --git a/homeassistant/components/zwave_js/cover.py b/homeassistant/components/zwave_js/cover.py
index c7ba50ee7e7..f9a9990ed77 100644
--- a/homeassistant/components/zwave_js/cover.py
+++ b/homeassistant/components/zwave_js/cover.py
@@ -15,6 +15,7 @@ from zwave_js_server.const.command_class.multilevel_switch import (
     COVER_OPEN_PROPERTY,
     COVER_UP_PROPERTY,
 )
+from zwave_js_server.model.driver import Driver
 from zwave_js_server.model.value import Value as ZwaveValue
 
 from homeassistant.components.cover import (
@@ -51,13 +52,15 @@ async def async_setup_entry(
     @callback
     def async_add_cover(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave cover."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
         if info.platform_hint == "motorized_barrier":
-            entities.append(ZwaveMotorizedBarrier(config_entry, client, info))
+            entities.append(ZwaveMotorizedBarrier(config_entry, driver, info))
         elif info.platform_hint == "window_shutter_tilt":
-            entities.append(ZWaveTiltCover(config_entry, client, info))
+            entities.append(ZWaveTiltCover(config_entry, driver, info))
         else:
-            entities.append(ZWaveCover(config_entry, client, info))
+            entities.append(ZWaveCover(config_entry, driver, info))
         async_add_entities(entities)
 
     config_entry.async_on_unload(
@@ -105,11 +108,11 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
     ) -> None:
         """Initialize a ZWaveCover entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
 
         # Entity class attributes
         self._attr_device_class = CoverDeviceClass.WINDOW
@@ -188,11 +191,11 @@ class ZWaveTiltCover(ZWaveCover):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
     ) -> None:
         """Initialize a ZWaveCover entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self.data_template = cast(
             CoverTiltDataTemplate, self.info.platform_data_template
         )
@@ -233,11 +236,11 @@ class ZwaveMotorizedBarrier(ZWaveBaseEntity, CoverEntity):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
     ) -> None:
         """Initialize a ZwaveMotorizedBarrier entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self._target_state: ZwaveValue = self.get_zwave_value(
             TARGET_STATE_PROPERTY, add_to_watched_value_ids=False
         )
diff --git a/homeassistant/components/zwave_js/entity.py b/homeassistant/components/zwave_js/entity.py
index b89b4c9c9de..a4271ac1c02 100644
--- a/homeassistant/components/zwave_js/entity.py
+++ b/homeassistant/components/zwave_js/entity.py
@@ -3,8 +3,8 @@ from __future__ import annotations
 
 import logging
 
-from zwave_js_server.client import Client as ZwaveClient
 from zwave_js_server.const import NodeStatus
+from zwave_js_server.model.driver import Driver
 from zwave_js_server.model.value import Value as ZwaveValue, get_value_id
 
 from homeassistant.config_entries import ConfigEntry
@@ -30,11 +30,11 @@ class ZWaveBaseEntity(Entity):
     _attr_should_poll = False
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize a generic Z-Wave device entity."""
         self.config_entry = config_entry
-        self.client = client
+        self.driver = driver
         self.info = info
         # entities requiring additional values, can add extra ids to this list
         self.watched_value_ids = {self.info.primary_value.value_id}
@@ -46,16 +46,14 @@ class ZWaveBaseEntity(Entity):
 
         # Entity class attributes
         self._attr_name = self.generate_name()
-        self._attr_unique_id = get_unique_id(
-            self.client, self.info.primary_value.value_id
-        )
+        self._attr_unique_id = get_unique_id(driver, self.info.primary_value.value_id)
         self._attr_entity_registry_enabled_default = (
             self.info.entity_registry_enabled_default
         )
         self._attr_assumed_state = self.info.assumed_state
         # device is precreated in main handler
         self._attr_device_info = DeviceInfo(
-            identifiers={get_device_id(self.client, self.info.node)},
+            identifiers={get_device_id(driver, self.info.node)},
         )
 
     @callback
@@ -145,7 +143,10 @@ class ZWaveBaseEntity(Entity):
             if item:
                 name += f" - {item}"
         # append endpoint if > 1
-        if self.info.primary_value.endpoint > 1:
+        if (
+            self.info.primary_value.endpoint is not None
+            and self.info.primary_value.endpoint > 1
+        ):
             name += f" ({self.info.primary_value.endpoint})"
 
         return name
@@ -154,7 +155,7 @@ class ZWaveBaseEntity(Entity):
     def available(self) -> bool:
         """Return entity availability."""
         return (
-            self.client.connected
+            self.driver.client.connected
             and bool(self.info.node.ready)
             and self.info.node.status != NodeStatus.DEAD
         )
diff --git a/homeassistant/components/zwave_js/fan.py b/homeassistant/components/zwave_js/fan.py
index f571884ac80..eb6d053f958 100644
--- a/homeassistant/components/zwave_js/fan.py
+++ b/homeassistant/components/zwave_js/fan.py
@@ -10,6 +10,7 @@ from zwave_js_server.const.command_class.thermostat import (
     THERMOSTAT_FAN_OFF_PROPERTY,
     THERMOSTAT_FAN_STATE_PROPERTY,
 )
+from zwave_js_server.model.driver import Driver
 from zwave_js_server.model.value import Value as ZwaveValue
 
 from homeassistant.components.fan import (
@@ -53,13 +54,15 @@ async def async_setup_entry(
     @callback
     def async_add_fan(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave fan."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
         if info.platform_hint == "has_fan_value_mapping":
-            entities.append(ValueMappingZwaveFan(config_entry, client, info))
+            entities.append(ValueMappingZwaveFan(config_entry, driver, info))
         elif info.platform_hint == "thermostat_fan":
-            entities.append(ZwaveThermostatFan(config_entry, client, info))
+            entities.append(ZwaveThermostatFan(config_entry, driver, info))
         else:
-            entities.append(ZwaveFan(config_entry, client, info))
+            entities.append(ZwaveFan(config_entry, driver, info))
 
         async_add_entities(entities)
 
@@ -78,10 +81,10 @@ class ZwaveFan(ZWaveBaseEntity, FanEntity):
     _attr_supported_features = FanEntityFeature.SET_SPEED
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize the fan."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self._target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY)
 
     async def async_set_percentage(self, percentage: int) -> None:
@@ -147,10 +150,10 @@ class ValueMappingZwaveFan(ZwaveFan):
     """A Zwave fan with a value mapping data (e.g., 1-24 is low)."""
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize the fan."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self.data_template = cast(
             FanValueMappingDataTemplate, self.info.platform_data_template
         )
@@ -300,10 +303,10 @@ class ZwaveThermostatFan(ZWaveBaseEntity, FanEntity):
     _fan_state: ZwaveValue | None = None
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize the thermostat fan."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
 
         self._fan_mode = self.info.primary_value
 
diff --git a/homeassistant/components/zwave_js/helpers.py b/homeassistant/components/zwave_js/helpers.py
index 0657d8531f3..807ae0287eb 100644
--- a/homeassistant/components/zwave_js/helpers.py
+++ b/homeassistant/components/zwave_js/helpers.py
@@ -110,29 +110,29 @@ def update_data_collection_preference(
 
 
 @callback
-def get_valueless_base_unique_id(client: ZwaveClient, node: ZwaveNode) -> str:
+def get_valueless_base_unique_id(driver: Driver, node: ZwaveNode) -> str:
     """Return the base unique ID for an entity that is not based on a value."""
-    return f"{client.driver.controller.home_id}.{node.node_id}"
+    return f"{driver.controller.home_id}.{node.node_id}"
 
 
-def get_unique_id(client: ZwaveClient, value_id: str) -> str:
+def get_unique_id(driver: Driver, value_id: str) -> str:
     """Get unique ID from client and value ID."""
-    return f"{client.driver.controller.home_id}.{value_id}"
+    return f"{driver.controller.home_id}.{value_id}"
 
 
 @callback
-def get_device_id(client: ZwaveClient, node: ZwaveNode) -> tuple[str, str]:
+def get_device_id(driver: Driver, node: ZwaveNode) -> tuple[str, str]:
     """Get device registry identifier for Z-Wave node."""
-    return (DOMAIN, f"{client.driver.controller.home_id}-{node.node_id}")
+    return (DOMAIN, f"{driver.controller.home_id}-{node.node_id}")
 
 
 @callback
-def get_device_id_ext(client: ZwaveClient, node: ZwaveNode) -> tuple[str, str] | None:
+def get_device_id_ext(driver: Driver, node: ZwaveNode) -> tuple[str, str] | None:
     """Get extended device registry identifier for Z-Wave node."""
     if None in (node.manufacturer_id, node.product_type, node.product_id):
         return None
 
-    domain, dev_id = get_device_id(client, node)
+    domain, dev_id = get_device_id(driver, node)
     return (
         domain,
         f"{dev_id}-{node.manufacturer_id}:{node.product_type}:{node.product_id}",
diff --git a/homeassistant/components/zwave_js/humidifier.py b/homeassistant/components/zwave_js/humidifier.py
index 8cf0b6aec7c..5aeb6d0272f 100644
--- a/homeassistant/components/zwave_js/humidifier.py
+++ b/homeassistant/components/zwave_js/humidifier.py
@@ -11,6 +11,7 @@ from zwave_js_server.const.command_class.humidity_control import (
     HumidityControlMode,
     HumidityControlSetpointType,
 )
+from zwave_js_server.model.driver import Driver
 from zwave_js_server.model.value import Value as ZwaveValue
 
 from homeassistant.components.humidifier import (
@@ -85,6 +86,8 @@ async def async_setup_entry(
     @callback
     def async_add_humidifier(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave Humidifier."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
 
         if (
@@ -93,7 +96,7 @@ async def async_setup_entry(
         ):
             entities.append(
                 ZWaveHumidifier(
-                    config_entry, client, info, HUMIDIFIER_ENTITY_DESCRIPTION
+                    config_entry, driver, info, HUMIDIFIER_ENTITY_DESCRIPTION
                 )
             )
 
@@ -103,7 +106,7 @@ async def async_setup_entry(
         ):
             entities.append(
                 ZWaveHumidifier(
-                    config_entry, client, info, DEHUMIDIFIER_ENTITY_DESCRIPTION
+                    config_entry, driver, info, DEHUMIDIFIER_ENTITY_DESCRIPTION
                 )
             )
 
@@ -128,12 +131,12 @@ class ZWaveHumidifier(ZWaveBaseEntity, HumidifierEntity):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
         description: ZwaveHumidifierEntityDescription,
     ) -> None:
         """Initialize humidifier."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
 
         self.entity_description = description
 
diff --git a/homeassistant/components/zwave_js/light.py b/homeassistant/components/zwave_js/light.py
index 534a86f1c86..d026868b418 100644
--- a/homeassistant/components/zwave_js/light.py
+++ b/homeassistant/components/zwave_js/light.py
@@ -23,6 +23,7 @@ from zwave_js_server.const.command_class.color_switch import (
     TARGET_COLOR_PROPERTY,
     ColorComponent,
 )
+from zwave_js_server.model.driver import Driver
 
 from homeassistant.components.light import (
     ATTR_BRIGHTNESS,
@@ -72,11 +73,13 @@ async def async_setup_entry(
     @callback
     def async_add_light(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave Light."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
 
         if info.platform_hint == "black_is_off":
-            async_add_entities([ZwaveBlackIsOffLight(config_entry, client, info)])
+            async_add_entities([ZwaveBlackIsOffLight(config_entry, driver, info)])
         else:
-            async_add_entities([ZwaveLight(config_entry, client, info)])
+            async_add_entities([ZwaveLight(config_entry, driver, info)])
 
     config_entry.async_on_unload(
         async_dispatcher_connect(
@@ -101,10 +104,10 @@ class ZwaveLight(ZWaveBaseEntity, LightEntity):
     """Representation of a Z-Wave light."""
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize the light."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self._supports_color = False
         self._supports_rgbw = False
         self._supports_color_temp = False
@@ -445,10 +448,10 @@ class ZwaveBlackIsOffLight(ZwaveLight):
     """
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize the light."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
 
         self._last_color: dict[str, int] | None = None
         self._supported_color_modes.discard(ColorMode.BRIGHTNESS)
diff --git a/homeassistant/components/zwave_js/lock.py b/homeassistant/components/zwave_js/lock.py
index 3781821e4c7..e7fbbeb3f99 100644
--- a/homeassistant/components/zwave_js/lock.py
+++ b/homeassistant/components/zwave_js/lock.py
@@ -61,8 +61,10 @@ async def async_setup_entry(
     @callback
     def async_add_lock(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave Lock."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
-        entities.append(ZWaveLock(config_entry, client, info))
+        entities.append(ZWaveLock(config_entry, driver, info))
 
         async_add_entities(entities)
 
diff --git a/homeassistant/components/zwave_js/migrate.py b/homeassistant/components/zwave_js/migrate.py
index 1413bf8e5b4..400c2b3cffe 100644
--- a/homeassistant/components/zwave_js/migrate.py
+++ b/homeassistant/components/zwave_js/migrate.py
@@ -4,7 +4,7 @@ from __future__ import annotations
 from dataclasses import dataclass
 import logging
 
-from zwave_js_server.client import Client as ZwaveClient
+from zwave_js_server.model.driver import Driver
 from zwave_js_server.model.value import Value as ZwaveValue
 
 from homeassistant.const import STATE_UNAVAILABLE
@@ -138,12 +138,12 @@ def async_migrate_discovered_value(
     ent_reg: EntityRegistry,
     registered_unique_ids: set[str],
     device: DeviceEntry,
-    client: ZwaveClient,
+    driver: Driver,
     disc_info: ZwaveDiscoveryInfo,
 ) -> None:
     """Migrate unique ID for entity/entities tied to discovered value."""
 
-    new_unique_id = get_unique_id(client, disc_info.primary_value.value_id)
+    new_unique_id = get_unique_id(driver, disc_info.primary_value.value_id)
 
     # On reinterviews, there is no point in going through this logic again for already
     # discovered values
@@ -155,7 +155,7 @@ def async_migrate_discovered_value(
 
     # 2021.2.*, 2021.3.0b0, and 2021.3.0 formats
     old_unique_ids = [
-        get_unique_id(client, value_id)
+        get_unique_id(driver, value_id)
         for value_id in get_old_value_ids(disc_info.primary_value)
     ]
 
diff --git a/homeassistant/components/zwave_js/number.py b/homeassistant/components/zwave_js/number.py
index fb9266ac071..56a7ce33be6 100644
--- a/homeassistant/components/zwave_js/number.py
+++ b/homeassistant/components/zwave_js/number.py
@@ -3,6 +3,7 @@ from __future__ import annotations
 
 from zwave_js_server.client import Client as ZwaveClient
 from zwave_js_server.const import TARGET_VALUE_PROPERTY
+from zwave_js_server.model.driver import Driver
 
 from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN, NumberEntity
 from homeassistant.config_entries import ConfigEntry
@@ -28,11 +29,13 @@ async def async_setup_entry(
     @callback
     def async_add_number(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave number entity."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
         if info.platform_hint == "volume":
-            entities.append(ZwaveVolumeNumberEntity(config_entry, client, info))
+            entities.append(ZwaveVolumeNumberEntity(config_entry, driver, info))
         else:
-            entities.append(ZwaveNumberEntity(config_entry, client, info))
+            entities.append(ZwaveNumberEntity(config_entry, driver, info))
         async_add_entities(entities)
 
     config_entry.async_on_unload(
@@ -48,10 +51,10 @@ class ZwaveNumberEntity(ZWaveBaseEntity, NumberEntity):
     """Representation of a Z-Wave number entity."""
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize a ZwaveNumberEntity entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         if self.info.primary_value.metadata.writeable:
             self._target_value = self.info.primary_value
         else:
@@ -99,10 +102,10 @@ class ZwaveVolumeNumberEntity(ZWaveBaseEntity, NumberEntity):
     """Representation of a volume number entity."""
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize a ZwaveVolumeNumberEntity entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self.correction_factor = int(
             self.info.primary_value.metadata.max - self.info.primary_value.metadata.min
         )
diff --git a/homeassistant/components/zwave_js/select.py b/homeassistant/components/zwave_js/select.py
index 085c694fc0e..f8cb294919e 100644
--- a/homeassistant/components/zwave_js/select.py
+++ b/homeassistant/components/zwave_js/select.py
@@ -6,6 +6,7 @@ from typing import cast
 from zwave_js_server.client import Client as ZwaveClient
 from zwave_js_server.const import TARGET_VALUE_PROPERTY, CommandClass
 from zwave_js_server.const.command_class.sound_switch import ToneID
+from zwave_js_server.model.driver import Driver
 
 from homeassistant.components.select import DOMAIN as SELECT_DOMAIN, SelectEntity
 from homeassistant.config_entries import ConfigEntry
@@ -32,15 +33,17 @@ async def async_setup_entry(
     @callback
     def async_add_select(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave select entity."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
         if info.platform_hint == "Default tone":
-            entities.append(ZwaveDefaultToneSelectEntity(config_entry, client, info))
+            entities.append(ZwaveDefaultToneSelectEntity(config_entry, driver, info))
         elif info.platform_hint == "multilevel_switch":
             entities.append(
-                ZwaveMultilevelSwitchSelectEntity(config_entry, client, info)
+                ZwaveMultilevelSwitchSelectEntity(config_entry, driver, info)
             )
         else:
-            entities.append(ZwaveSelectEntity(config_entry, client, info))
+            entities.append(ZwaveSelectEntity(config_entry, driver, info))
         async_add_entities(entities)
 
     config_entry.async_on_unload(
@@ -58,10 +61,10 @@ class ZwaveSelectEntity(ZWaveBaseEntity, SelectEntity):
     _attr_entity_category = EntityCategory.CONFIG
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize a ZwaveSelectEntity entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
 
         # Entity class attributes
         self._attr_name = self.generate_name(include_value_name=True)
@@ -94,10 +97,10 @@ class ZwaveDefaultToneSelectEntity(ZWaveBaseEntity, SelectEntity):
     _attr_entity_category = EntityCategory.CONFIG
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize a ZwaveDefaultToneSelectEntity entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self._tones_value = self.get_zwave_value(
             "toneId", command_class=CommandClass.SOUND_SWITCH
         )
@@ -145,10 +148,10 @@ class ZwaveMultilevelSwitchSelectEntity(ZWaveBaseEntity, SelectEntity):
     """Representation of a Z-Wave Multilevel Switch CC select entity."""
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize a ZwaveSelectEntity entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self._target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY)
         assert self.info.platform_data_template
         self._lookup_map = cast(
diff --git a/homeassistant/components/zwave_js/sensor.py b/homeassistant/components/zwave_js/sensor.py
index 71e6bc48952..eb5bf778cc0 100644
--- a/homeassistant/components/zwave_js/sensor.py
+++ b/homeassistant/components/zwave_js/sensor.py
@@ -12,6 +12,7 @@ from zwave_js_server.const.command_class.meter import (
     RESET_METER_OPTION_TARGET_VALUE,
     RESET_METER_OPTION_TYPE,
 )
+from zwave_js_server.model.driver import Driver
 from zwave_js_server.model.node import Node as ZwaveNode
 from zwave_js_server.model.value import ConfigurationValue
 from zwave_js_server.util.command_class.meter import get_meter_type
@@ -179,6 +180,8 @@ async def async_setup_entry(
     @callback
     def async_add_sensor(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave Sensor."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
 
         if info.platform_data:
@@ -191,13 +194,13 @@ async def async_setup_entry(
 
         if info.platform_hint == "string_sensor":
             entities.append(
-                ZWaveStringSensor(config_entry, client, info, entity_description)
+                ZWaveStringSensor(config_entry, driver, info, entity_description)
             )
         elif info.platform_hint == "numeric_sensor":
             entities.append(
                 ZWaveNumericSensor(
                     config_entry,
-                    client,
+                    driver,
                     info,
                     entity_description,
                     data.unit_of_measurement,
@@ -205,17 +208,17 @@ async def async_setup_entry(
             )
         elif info.platform_hint == "list_sensor":
             entities.append(
-                ZWaveListSensor(config_entry, client, info, entity_description)
+                ZWaveListSensor(config_entry, driver, info, entity_description)
             )
         elif info.platform_hint == "config_parameter":
             entities.append(
                 ZWaveConfigParameterSensor(
-                    config_entry, client, info, entity_description
+                    config_entry, driver, info, entity_description
                 )
             )
         elif info.platform_hint == "meter":
             entities.append(
-                ZWaveMeterSensor(config_entry, client, info, entity_description)
+                ZWaveMeterSensor(config_entry, driver, info, entity_description)
             )
         else:
             LOGGER.warning(
@@ -230,7 +233,9 @@ async def async_setup_entry(
     @callback
     def async_add_node_status_sensor(node: ZwaveNode) -> None:
         """Add node status sensor."""
-        async_add_entities([ZWaveNodeStatusSensor(config_entry, client, node)])
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
+        async_add_entities([ZWaveNodeStatusSensor(config_entry, driver, node)])
 
     config_entry.async_on_unload(
         async_dispatcher_connect(
@@ -265,13 +270,13 @@ class ZwaveSensorBase(ZWaveBaseEntity, SensorEntity):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
         entity_description: SensorEntityDescription,
         unit_of_measurement: str | None = None,
     ) -> None:
         """Initialize a ZWaveSensorBase entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self.entity_description = entity_description
         self._attr_native_unit_of_measurement = unit_of_measurement
 
@@ -370,14 +375,14 @@ class ZWaveListSensor(ZwaveSensorBase):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
         entity_description: SensorEntityDescription,
         unit_of_measurement: str | None = None,
     ) -> None:
         """Initialize a ZWaveListSensor entity."""
         super().__init__(
-            config_entry, client, info, entity_description, unit_of_measurement
+            config_entry, driver, info, entity_description, unit_of_measurement
         )
 
         # Entity class attributes
@@ -414,14 +419,14 @@ class ZWaveConfigParameterSensor(ZwaveSensorBase):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
         entity_description: SensorEntityDescription,
         unit_of_measurement: str | None = None,
     ) -> None:
         """Initialize a ZWaveConfigParameterSensor entity."""
         super().__init__(
-            config_entry, client, info, entity_description, unit_of_measurement
+            config_entry, driver, info, entity_description, unit_of_measurement
         )
         self._primary_value = cast(ConfigurationValue, self.info.primary_value)
 
@@ -466,11 +471,10 @@ class ZWaveNodeStatusSensor(SensorEntity):
     _attr_entity_category = EntityCategory.DIAGNOSTIC
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, node: ZwaveNode
+        self, config_entry: ConfigEntry, driver: Driver, node: ZwaveNode
     ) -> None:
         """Initialize a generic Z-Wave device entity."""
         self.config_entry = config_entry
-        self.client = client
         self.node = node
         name: str = (
             self.node.name
@@ -479,11 +483,11 @@ class ZWaveNodeStatusSensor(SensorEntity):
         )
         # Entity class attributes
         self._attr_name = f"{name}: Node Status"
-        self._base_unique_id = get_valueless_base_unique_id(client, node)
+        self._base_unique_id = get_valueless_base_unique_id(driver, node)
         self._attr_unique_id = f"{self._base_unique_id}.node_status"
         # device is precreated in main handler
         self._attr_device_info = DeviceInfo(
-            identifiers={get_device_id(self.client, self.node)},
+            identifiers={get_device_id(driver, self.node)},
         )
         self._attr_native_value: str = node.status.name.lower()
 
diff --git a/homeassistant/components/zwave_js/siren.py b/homeassistant/components/zwave_js/siren.py
index e686c5446ca..67e6aa4afb4 100644
--- a/homeassistant/components/zwave_js/siren.py
+++ b/homeassistant/components/zwave_js/siren.py
@@ -5,6 +5,7 @@ from typing import Any
 
 from zwave_js_server.client import Client as ZwaveClient
 from zwave_js_server.const.command_class.sound_switch import ToneID
+from zwave_js_server.model.driver import Driver
 
 from homeassistant.components.siren import (
     DOMAIN as SIREN_DOMAIN,
@@ -35,8 +36,10 @@ async def async_setup_entry(
     @callback
     def async_add_siren(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave siren entity."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
-        entities.append(ZwaveSirenEntity(config_entry, client, info))
+        entities.append(ZwaveSirenEntity(config_entry, driver, info))
         async_add_entities(entities)
 
     config_entry.async_on_unload(
@@ -52,10 +55,10 @@ class ZwaveSirenEntity(ZWaveBaseEntity, SirenEntity):
     """Representation of a Z-Wave siren entity."""
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize a ZwaveSirenEntity entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         # Entity class attributes
         self._attr_available_tones = {
             int(id): val for id, val in self.info.primary_value.metadata.states.items()
diff --git a/homeassistant/components/zwave_js/switch.py b/homeassistant/components/zwave_js/switch.py
index 115f90b8e11..52b8f813326 100644
--- a/homeassistant/components/zwave_js/switch.py
+++ b/homeassistant/components/zwave_js/switch.py
@@ -9,6 +9,7 @@ from zwave_js_server.const import TARGET_VALUE_PROPERTY
 from zwave_js_server.const.command_class.barrier_operator import (
     BarrierEventSignalingSubsystemState,
 )
+from zwave_js_server.model.driver import Driver
 
 from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SwitchEntity
 from homeassistant.config_entries import ConfigEntry
@@ -36,13 +37,15 @@ async def async_setup_entry(
     @callback
     def async_add_switch(info: ZwaveDiscoveryInfo) -> None:
         """Add Z-Wave Switch."""
+        driver = client.driver
+        assert driver is not None  # Driver is ready before platforms are loaded.
         entities: list[ZWaveBaseEntity] = []
         if info.platform_hint == "barrier_event_signaling_state":
             entities.append(
-                ZWaveBarrierEventSignalingSwitch(config_entry, client, info)
+                ZWaveBarrierEventSignalingSwitch(config_entry, driver, info)
             )
         else:
-            entities.append(ZWaveSwitch(config_entry, client, info))
+            entities.append(ZWaveSwitch(config_entry, driver, info))
 
         async_add_entities(entities)
 
@@ -59,10 +62,10 @@ class ZWaveSwitch(ZWaveBaseEntity, SwitchEntity):
     """Representation of a Z-Wave switch."""
 
     def __init__(
-        self, config_entry: ConfigEntry, client: ZwaveClient, info: ZwaveDiscoveryInfo
+        self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
     ) -> None:
         """Initialize the switch."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
 
         self._target_value = self.get_zwave_value(TARGET_VALUE_PROPERTY)
 
@@ -91,11 +94,11 @@ class ZWaveBarrierEventSignalingSwitch(ZWaveBaseEntity, SwitchEntity):
     def __init__(
         self,
         config_entry: ConfigEntry,
-        client: ZwaveClient,
+        driver: Driver,
         info: ZwaveDiscoveryInfo,
     ) -> None:
         """Initialize a ZWaveBarrierEventSignalingSwitch entity."""
-        super().__init__(config_entry, client, info)
+        super().__init__(config_entry, driver, info)
         self._state: bool | None = None
 
         self._update_state()
diff --git a/homeassistant/components/zwave_js/triggers/event.py b/homeassistant/components/zwave_js/triggers/event.py
index fd46c89832b..83fd7570ab9 100644
--- a/homeassistant/components/zwave_js/triggers/event.py
+++ b/homeassistant/components/zwave_js/triggers/event.py
@@ -207,7 +207,9 @@ async def async_attach_trigger(
         unsubs.append(source.on(event_name, async_on_event))
 
     for node in nodes:
-        device_identifier = get_device_id(node.client, node)
+        driver = node.client.driver
+        assert driver is not None  # The node comes from the driver.
+        device_identifier = get_device_id(driver, node)
         device = dev_reg.async_get_device({device_identifier})
         assert device
         # We need to store the device for the callback
diff --git a/homeassistant/components/zwave_js/triggers/value_updated.py b/homeassistant/components/zwave_js/triggers/value_updated.py
index 8a0b287c26b..38a19eaa377 100644
--- a/homeassistant/components/zwave_js/triggers/value_updated.py
+++ b/homeassistant/components/zwave_js/triggers/value_updated.py
@@ -162,7 +162,9 @@ async def async_attach_trigger(
 
     dev_reg = dr.async_get(hass)
     for node in nodes:
-        device_identifier = get_device_id(node.client, node)
+        driver = node.client.driver
+        assert driver is not None  # The node comes from the driver.
+        device_identifier = get_device_id(driver, node)
         device = dev_reg.async_get_device({device_identifier})
         assert device
         value_id = get_value_id(node, command_class, property_, endpoint, property_key)
diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py
index 3d491b98f93..58c26cd5797 100644
--- a/tests/components/zwave_js/test_api.py
+++ b/tests/components/zwave_js/test_api.py
@@ -78,7 +78,7 @@ from homeassistant.helpers import device_registry as dr
 def get_device(hass, node):
     """Get device ID for a node."""
     dev_reg = dr.async_get(hass)
-    device_id = get_device_id(node.client, node)
+    device_id = get_device_id(node.client.driver, node)
     return dev_reg.async_get_device({device_id})
 
 
@@ -124,11 +124,12 @@ async def test_node_ready(
     node_data = deepcopy(multisensor_6_state)  # Copy to allow modification in tests.
     node = Node(client, node_data)
     node.data["ready"] = False
-    client.driver.controller.nodes[node.node_id] = node
+    driver = client.driver
+    driver.controller.nodes[node.node_id] = node
 
     dev_reg = dr.async_get(hass)
     device = dev_reg.async_get_or_create(
-        config_entry_id=entry.entry_id, identifiers={get_device_id(client, node)}
+        config_entry_id=entry.entry_id, identifiers={get_device_id(driver, node)}
     )
 
     await ws_client.send_json(
diff --git a/tests/components/zwave_js/test_button.py b/tests/components/zwave_js/test_button.py
index 29858e0eb97..5ae5f8e7254 100644
--- a/tests/components/zwave_js/test_button.py
+++ b/tests/components/zwave_js/test_button.py
@@ -49,11 +49,12 @@ async def test_ping_entity(
     assert "There is no value to refresh for this entity" in caplog.text
 
     # Assert a node ping button entity is not created for the controller
-    node = client.driver.controller.nodes[1]
+    driver = client.driver
+    node = driver.controller.nodes[1]
     assert node.is_controller_node
     assert (
         async_get(hass).async_get_entity_id(
-            DOMAIN, "sensor", f"{get_valueless_base_unique_id(client, node)}.ping"
+            DOMAIN, "sensor", f"{get_valueless_base_unique_id(driver, node)}.ping"
         )
         is None
     )
diff --git a/tests/components/zwave_js/test_device_action.py b/tests/components/zwave_js/test_device_action.py
index b8fa43d9cec..ad9d61b3f33 100644
--- a/tests/components/zwave_js/test_device_action.py
+++ b/tests/components/zwave_js/test_device_action.py
@@ -30,7 +30,9 @@ async def test_get_actions(
     """Test we get the expected actions from a zwave_js node."""
     node = lock_schlage_be469
     dev_reg = device_registry.async_get(hass)
-    device = dev_reg.async_get_device({get_device_id(client, node)})
+    driver = client.driver
+    assert driver
+    device = dev_reg.async_get_device({get_device_id(driver, node)})
     assert device
     expected_actions = [
         {
@@ -99,7 +101,9 @@ async def test_get_actions_meter(
     """Test we get the expected meter actions from a zwave_js node."""
     node = aeon_smart_switch_6
     dev_reg = device_registry.async_get(hass)
-    device = dev_reg.async_get_device({get_device_id(client, node)})
+    driver = client.driver
+    assert driver
+    device = dev_reg.async_get_device({get_device_id(driver, node)})
     assert device
     actions = await async_get_device_automations(
         hass, DeviceAutomationType.ACTION, device.id
@@ -116,7 +120,9 @@ async def test_actions(
 ) -> None:
     """Test actions."""
     node = climate_radio_thermostat_ct100_plus
-    device_id = get_device_id(client, node)
+    driver = client.driver
+    assert driver
+    device_id = get_device_id(driver, node)
     dev_reg = device_registry.async_get(hass)
     device = dev_reg.async_get_device({device_id})
     assert device
@@ -236,7 +242,9 @@ async def test_actions_multiple_calls(
 ) -> None:
     """Test actions can be called multiple times and still work."""
     node = climate_radio_thermostat_ct100_plus
-    device_id = get_device_id(client, node)
+    driver = client.driver
+    assert driver
+    device_id = get_device_id(driver, node)
     dev_reg = device_registry.async_get(hass)
     device = dev_reg.async_get_device({device_id})
     assert device
@@ -281,7 +289,9 @@ async def test_lock_actions(
 ) -> None:
     """Test actions for locks."""
     node = lock_schlage_be469
-    device_id = get_device_id(client, node)
+    driver = client.driver
+    assert driver
+    device_id = get_device_id(driver, node)
     dev_reg = device_registry.async_get(hass)
     device = dev_reg.async_get_device({device_id})
     assert device
@@ -350,7 +360,9 @@ async def test_reset_meter_action(
 ) -> None:
     """Test reset_meter action."""
     node = aeon_smart_switch_6
-    device_id = get_device_id(client, node)
+    driver = client.driver
+    assert driver
+    device_id = get_device_id(driver, node)
     dev_reg = device_registry.async_get(hass)
     device = dev_reg.async_get_device({device_id})
     assert device
@@ -613,7 +625,9 @@ async def test_get_action_capabilities_meter_triggers(
     """Test we get the expected action capabilities for meter triggers."""
     node = aeon_smart_switch_6
     dev_reg = device_registry.async_get(hass)
-    device = dev_reg.async_get_device({get_device_id(client, node)})
+    driver = client.driver
+    assert driver
+    device = dev_reg.async_get_device({get_device_id(driver, node)})
     assert device
     capabilities = await device_action.async_get_action_capabilities(
         hass,
@@ -669,7 +683,9 @@ async def test_unavailable_entity_actions(
     await hass.async_block_till_done()
     node = lock_schlage_be469
     dev_reg = device_registry.async_get(hass)
-    device = dev_reg.async_get_device({get_device_id(client, node)})
+    driver = client.driver
+    assert driver
+    device = dev_reg.async_get_device({get_device_id(driver, node)})
     assert device
     actions = await async_get_device_automations(
         hass, DeviceAutomationType.ACTION, device.id
diff --git a/tests/components/zwave_js/test_diagnostics.py b/tests/components/zwave_js/test_diagnostics.py
index 99475cd63ec..8d00c9a2f64 100644
--- a/tests/components/zwave_js/test_diagnostics.py
+++ b/tests/components/zwave_js/test_diagnostics.py
@@ -51,7 +51,7 @@ async def test_device_diagnostics(
 ):
     """Test the device level diagnostics data dump."""
     dev_reg = async_get(hass)
-    device = dev_reg.async_get_device({get_device_id(client, multisensor_6)})
+    device = dev_reg.async_get_device({get_device_id(client.driver, multisensor_6)})
     assert device
 
     # Update a value and ensure it is reflected in the node state
diff --git a/tests/components/zwave_js/test_init.py b/tests/components/zwave_js/test_init.py
index 7b3fd773839..a2962261ac3 100644
--- a/tests/components/zwave_js/test_init.py
+++ b/tests/components/zwave_js/test_init.py
@@ -800,8 +800,10 @@ async def test_removed_device(
     hass, client, climate_radio_thermostat_ct100_plus, lock_schlage_be469, integration
 ):
     """Test that the device registry gets updated when a device gets removed."""
+    driver = client.driver
+    assert driver
     # Verify how many nodes are available
-    assert len(client.driver.controller.nodes) == 2
+    assert len(driver.controller.nodes) == 2
 
     # Make sure there are the same number of devices
     dev_reg = dr.async_get(hass)
@@ -814,7 +816,7 @@ async def test_removed_device(
     assert len(entity_entries) == 29
 
     # Remove a node and reload the entry
-    old_node = client.driver.controller.nodes.pop(13)
+    old_node = driver.controller.nodes.pop(13)
     await hass.config_entries.async_reload(integration.entry_id)
     await hass.async_block_till_done()
 
@@ -824,7 +826,7 @@ async def test_removed_device(
     assert len(device_entries) == 1
     entity_entries = er.async_entries_for_config_entry(ent_reg, integration.entry_id)
     assert len(entity_entries) == 17
-    assert dev_reg.async_get_device({get_device_id(client, old_node)}) is None
+    assert dev_reg.async_get_device({get_device_id(driver, old_node)}) is None
 
 
 async def test_suggested_area(hass, client, eaton_rf9640_dimmer):
diff --git a/tests/components/zwave_js/test_migrate.py b/tests/components/zwave_js/test_migrate.py
index 37c53700d95..cf1e3ee7eaa 100644
--- a/tests/components/zwave_js/test_migrate.py
+++ b/tests/components/zwave_js/test_migrate.py
@@ -190,12 +190,14 @@ async def test_old_entity_migration(
 ):
     """Test old entity on a different endpoint is migrated to a new one."""
     node = Node(client, copy.deepcopy(hank_binary_switch_state))
+    driver = client.driver
+    assert driver
 
     ent_reg = er.async_get(hass)
     dev_reg = dr.async_get(hass)
     device = dev_reg.async_get_or_create(
         config_entry_id=integration.entry_id,
-        identifiers={get_device_id(client, node)},
+        identifiers={get_device_id(driver, node)},
         manufacturer=hank_binary_switch_state["deviceConfig"]["manufacturer"],
         model=hank_binary_switch_state["deviceConfig"]["label"],
     )
@@ -204,7 +206,7 @@ async def test_old_entity_migration(
     entity_name = SENSOR_NAME.split(".")[1]
 
     # Create entity RegistryEntry using fake endpoint
-    old_unique_id = f"{client.driver.controller.home_id}.32-50-1-value-66049"
+    old_unique_id = f"{driver.controller.home_id}.32-50-1-value-66049"
     entity_entry = ent_reg.async_get_or_create(
         "sensor",
         DOMAIN,
@@ -221,7 +223,7 @@ async def test_old_entity_migration(
     for i in range(0, 2):
         # Add a ready node, unique ID should be migrated
         event = {"node": node}
-        client.driver.controller.emit("node added", event)
+        driver.controller.emit("node added", event)
         await hass.async_block_till_done()
 
         # Check that new RegistryEntry is using new unique ID format
@@ -236,12 +238,14 @@ async def test_different_endpoint_migration_status_sensor(
 ):
     """Test that the different endpoint migration logic skips over the status sensor."""
     node = Node(client, copy.deepcopy(hank_binary_switch_state))
+    driver = client.driver
+    assert driver
 
     ent_reg = er.async_get(hass)
     dev_reg = dr.async_get(hass)
     device = dev_reg.async_get_or_create(
         config_entry_id=integration.entry_id,
-        identifiers={get_device_id(client, node)},
+        identifiers={get_device_id(driver, node)},
         manufacturer=hank_binary_switch_state["deviceConfig"]["manufacturer"],
         model=hank_binary_switch_state["deviceConfig"]["label"],
     )
@@ -250,7 +254,7 @@ async def test_different_endpoint_migration_status_sensor(
     entity_name = SENSOR_NAME.split(".")[1]
 
     # Create entity RegistryEntry using fake endpoint
-    old_unique_id = f"{client.driver.controller.home_id}.32.node_status"
+    old_unique_id = f"{driver.controller.home_id}.32.node_status"
     entity_entry = ent_reg.async_get_or_create(
         "sensor",
         DOMAIN,
@@ -267,7 +271,7 @@ async def test_different_endpoint_migration_status_sensor(
     for i in range(0, 2):
         # Add a ready node, unique ID should be migrated
         event = {"node": node}
-        client.driver.controller.emit("node added", event)
+        driver.controller.emit("node added", event)
         await hass.async_block_till_done()
 
         # Check that the RegistryEntry is using the same unique ID
@@ -280,12 +284,14 @@ async def test_skip_old_entity_migration_for_multiple(
 ):
     """Test that multiple entities of the same value but on a different endpoint get skipped."""
     node = Node(client, copy.deepcopy(hank_binary_switch_state))
+    driver = client.driver
+    assert driver
 
     ent_reg = er.async_get(hass)
     dev_reg = dr.async_get(hass)
     device = dev_reg.async_get_or_create(
         config_entry_id=integration.entry_id,
-        identifiers={get_device_id(client, node)},
+        identifiers={get_device_id(driver, node)},
         manufacturer=hank_binary_switch_state["deviceConfig"]["manufacturer"],
         model=hank_binary_switch_state["deviceConfig"]["label"],
     )
@@ -294,7 +300,7 @@ async def test_skip_old_entity_migration_for_multiple(
     entity_name = SENSOR_NAME.split(".")[1]
 
     # Create two entity entrrys using different endpoints
-    old_unique_id_1 = f"{client.driver.controller.home_id}.32-50-1-value-66049"
+    old_unique_id_1 = f"{driver.controller.home_id}.32-50-1-value-66049"
     entity_entry = ent_reg.async_get_or_create(
         "sensor",
         DOMAIN,
@@ -308,7 +314,7 @@ async def test_skip_old_entity_migration_for_multiple(
     assert entity_entry.unique_id == old_unique_id_1
 
     # Create two entity entrrys using different endpoints
-    old_unique_id_2 = f"{client.driver.controller.home_id}.32-50-2-value-66049"
+    old_unique_id_2 = f"{driver.controller.home_id}.32-50-2-value-66049"
     entity_entry = ent_reg.async_get_or_create(
         "sensor",
         DOMAIN,
@@ -322,12 +328,12 @@ async def test_skip_old_entity_migration_for_multiple(
     assert entity_entry.unique_id == old_unique_id_2
     # Add a ready node, unique ID should be migrated
     event = {"node": node}
-    client.driver.controller.emit("node added", event)
+    driver.controller.emit("node added", event)
     await hass.async_block_till_done()
 
     # Check that new RegistryEntry is created using new unique ID format
     entity_entry = ent_reg.async_get(SENSOR_NAME)
-    new_unique_id = f"{client.driver.controller.home_id}.32-50-0-value-66049"
+    new_unique_id = f"{driver.controller.home_id}.32-50-0-value-66049"
     assert entity_entry.unique_id == new_unique_id
 
     # Check that the old entities stuck around because we skipped the migration step
@@ -340,12 +346,14 @@ async def test_old_entity_migration_notification_binary_sensor(
 ):
     """Test old entity on a different endpoint is migrated to a new one for a notification binary sensor."""
     node = Node(client, copy.deepcopy(multisensor_6_state))
+    driver = client.driver
+    assert driver
 
     ent_reg = er.async_get(hass)
     dev_reg = dr.async_get(hass)
     device = dev_reg.async_get_or_create(
         config_entry_id=integration.entry_id,
-        identifiers={get_device_id(client, node)},
+        identifiers={get_device_id(driver, node)},
         manufacturer=multisensor_6_state["deviceConfig"]["manufacturer"],
         model=multisensor_6_state["deviceConfig"]["label"],
     )
@@ -353,7 +361,9 @@ async def test_old_entity_migration_notification_binary_sensor(
     entity_name = NOTIFICATION_MOTION_BINARY_SENSOR.split(".")[1]
 
     # Create entity RegistryEntry using old unique ID format
-    old_unique_id = f"{client.driver.controller.home_id}.52-113-1-Home Security-Motion sensor status.8"
+    old_unique_id = (
+        f"{driver.controller.home_id}.52-113-1-Home Security-Motion sensor status.8"
+    )
     entity_entry = ent_reg.async_get_or_create(
         "binary_sensor",
         DOMAIN,
@@ -370,12 +380,14 @@ async def test_old_entity_migration_notification_binary_sensor(
     for _ in range(0, 2):
         # Add a ready node, unique ID should be migrated
         event = {"node": node}
-        client.driver.controller.emit("node added", event)
+        driver.controller.emit("node added", event)
         await hass.async_block_till_done()
 
         # Check that new RegistryEntry is using new unique ID format
         entity_entry = ent_reg.async_get(NOTIFICATION_MOTION_BINARY_SENSOR)
-        new_unique_id = f"{client.driver.controller.home_id}.52-113-0-Home Security-Motion sensor status.8"
+        new_unique_id = (
+            f"{driver.controller.home_id}.52-113-0-Home Security-Motion sensor status.8"
+        )
         assert entity_entry.unique_id == new_unique_id
         assert (
             ent_reg.async_get_entity_id("binary_sensor", DOMAIN, old_unique_id) is None
diff --git a/tests/components/zwave_js/test_sensor.py b/tests/components/zwave_js/test_sensor.py
index 1d41e145a95..848c4d7b0e5 100644
--- a/tests/components/zwave_js/test_sensor.py
+++ b/tests/components/zwave_js/test_sensor.py
@@ -205,13 +205,14 @@ async def test_node_status_sensor(
     assert hass.states.get(NODE_STATUS_ENTITY).state != STATE_UNAVAILABLE
 
     # Assert a node status sensor entity is not created for the controller
-    node = client.driver.controller.nodes[1]
+    driver = client.driver
+    node = driver.controller.nodes[1]
     assert node.is_controller_node
     assert (
         ent_reg.async_get_entity_id(
             DOMAIN,
             "sensor",
-            f"{get_valueless_base_unique_id(client, node)}.node_status",
+            f"{get_valueless_base_unique_id(driver, node)}.node_status",
         )
         is None
     )
diff --git a/tests/components/zwave_js/test_services.py b/tests/components/zwave_js/test_services.py
index e04ec569c9f..dec4c3c0a9d 100644
--- a/tests/components/zwave_js/test_services.py
+++ b/tests/components/zwave_js/test_services.py
@@ -1367,11 +1367,11 @@ async def test_multicast_set_value(
     # Test using area ID
     dev_reg = async_get_dev_reg(hass)
     device_eurotronic = dev_reg.async_get_device(
-        {get_device_id(client, climate_eurotronic_spirit_z)}
+        {get_device_id(client.driver, climate_eurotronic_spirit_z)}
     )
     assert device_eurotronic
     device_danfoss = dev_reg.async_get_device(
-        {get_device_id(client, climate_danfoss_lc_13)}
+        {get_device_id(client.driver, climate_danfoss_lc_13)}
     )
     assert device_danfoss
     area_reg = async_get_area_reg(hass)
@@ -1655,11 +1655,15 @@ async def test_ping(
     """Test ping service."""
     dev_reg = async_get_dev_reg(hass)
     device_radio_thermostat = dev_reg.async_get_device(
-        {get_device_id(client, climate_radio_thermostat_ct100_plus_different_endpoints)}
+        {
+            get_device_id(
+                client.driver, climate_radio_thermostat_ct100_plus_different_endpoints
+            )
+        }
     )
     assert device_radio_thermostat
     device_danfoss = dev_reg.async_get_device(
-        {get_device_id(client, climate_danfoss_lc_13)}
+        {get_device_id(client.driver, climate_danfoss_lc_13)}
     )
     assert device_danfoss
 
@@ -1789,11 +1793,15 @@ async def test_invoke_cc_api(
     """Test invoke_cc_api service."""
     dev_reg = async_get_dev_reg(hass)
     device_radio_thermostat = dev_reg.async_get_device(
-        {get_device_id(client, climate_radio_thermostat_ct100_plus_different_endpoints)}
+        {
+            get_device_id(
+                client.driver, climate_radio_thermostat_ct100_plus_different_endpoints
+            )
+        }
     )
     assert device_radio_thermostat
     device_danfoss = dev_reg.async_get_device(
-        {get_device_id(client, climate_danfoss_lc_13)}
+        {get_device_id(client.driver, climate_danfoss_lc_13)}
     )
     assert device_danfoss