diff --git a/homeassistant/components/zha/binary_sensor.py b/homeassistant/components/zha/binary_sensor.py
index 4e3c7166bf04bfb6be4dd60bc0ed4feab911e97c..696216e3e814d833f7884e1a91bad82ec39acc4f 100644
--- a/homeassistant/components/zha/binary_sensor.py
+++ b/homeassistant/components/zha/binary_sensor.py
@@ -4,6 +4,8 @@ from __future__ import annotations
 import functools
 from typing import Any
 
+import zigpy.types as t
+from zigpy.zcl.clusters.general import OnOff
 from zigpy.zcl.clusters.security import IasZone
 
 from homeassistant.components.binary_sensor import (
@@ -119,11 +121,21 @@ class Occupancy(BinarySensor):
 
 @STRICT_MATCH(channel_names=CHANNEL_ON_OFF)
 class Opening(BinarySensor):
-    """ZHA BinarySensor."""
+    """ZHA OnOff BinarySensor."""
 
     SENSOR_ATTR = "on_off"
     _attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.OPENING
 
+    # Client/out cluster attributes aren't stored in the zigpy database, but are properly stored in the runtime cache.
+    # We need to manually restore the last state from the sensor state to the runtime cache for now.
+    @callback
+    def async_restore_last_state(self, last_state):
+        """Restore previous state to zigpy cache."""
+        self._channel.cluster.update_attribute(
+            OnOff.attributes_by_name[self.SENSOR_ATTR].id,
+            t.Bool.true if last_state.state == STATE_ON else t.Bool.false,
+        )
+
 
 @MULTI_MATCH(channel_names=CHANNEL_BINARY_INPUT)
 class BinaryInput(BinarySensor):
@@ -144,10 +156,9 @@ class BinaryInput(BinarySensor):
     manufacturers="Philips",
     models={"SML001", "SML002"},
 )
-class Motion(BinarySensor):
-    """ZHA BinarySensor."""
+class Motion(Opening):
+    """ZHA OnOff BinarySensor with motion device class."""
 
-    SENSOR_ATTR = "on_off"
     _attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.MOTION
 
 
diff --git a/tests/components/zha/test_binary_sensor.py b/tests/components/zha/test_binary_sensor.py
index ec25295ed5a5d333cb2595571beaee1d83fcecb3..2c0461a3c7c11f096f62574c48660295abc7cbac 100644
--- a/tests/components/zha/test_binary_sensor.py
+++ b/tests/components/zha/test_binary_sensor.py
@@ -3,6 +3,7 @@ from unittest.mock import patch
 
 import pytest
 import zigpy.profiles.zha
+import zigpy.zcl.clusters.general as general
 import zigpy.zcl.clusters.measurement as measurement
 import zigpy.zcl.clusters.security as security
 
@@ -40,6 +41,16 @@ DEVICE_OCCUPANCY = {
 }
 
 
+DEVICE_ONOFF = {
+    1: {
+        SIG_EP_PROFILE: zigpy.profiles.zha.PROFILE_ID,
+        SIG_EP_TYPE: zigpy.profiles.zha.DeviceType.ON_OFF_SENSOR,
+        SIG_EP_INPUT: [],
+        SIG_EP_OUTPUT: [general.OnOff.cluster_id],
+    }
+}
+
+
 @pytest.fixture(autouse=True)
 def binary_sensor_platform_only():
     """Only set up the binary_sensor and required base platforms to speed up tests."""
@@ -212,3 +223,30 @@ async def test_binary_sensor_migration_already_migrated(
     assert entity_id is not None
     assert hass.states.get(entity_id).state == STATE_ON  # matches attribute cache
     assert hass.states.get(entity_id).attributes["migrated_to_cache"]
+
+
+@pytest.mark.parametrize(
+    "restored_state",
+    [
+        STATE_ON,
+        STATE_OFF,
+    ],
+)
+async def test_onoff_binary_sensor_restore_state(
+    hass: HomeAssistant,
+    zigpy_device_mock,
+    core_rs,
+    zha_device_restored,
+    restored_state,
+) -> None:
+    """Test ZHA OnOff binary_sensor restores last state from HA."""
+
+    entity_id = "binary_sensor.fakemanufacturer_fakemodel_opening"
+    core_rs(entity_id, state=restored_state, attributes={})
+
+    zigpy_device = zigpy_device_mock(DEVICE_ONOFF)
+    zha_device = await zha_device_restored(zigpy_device)
+    entity_id = await find_entity_id(Platform.BINARY_SENSOR, zha_device, hass)
+
+    assert entity_id is not None
+    assert hass.states.get(entity_id).state == restored_state