diff --git a/homeassistant/components/zha/icons.json b/homeassistant/components/zha/icons.json
index 65ad029a66d..9d5254fe237 100644
--- a/homeassistant/components/zha/icons.json
+++ b/homeassistant/components/zha/icons.json
@@ -86,6 +86,18 @@
       },
       "presence_detection_timeout": {
         "default": "mdi:timer-edit"
+      },
+      "exercise_trigger_time": {
+        "default": "mdi:clock"
+      },
+      "external_temperature_sensor": {
+        "default": "mdi:thermometer"
+      },
+      "load_room_mean": {
+        "default": "mdi:scale-balance"
+      },
+      "regulation_setpoint_offset": {
+        "default": "mdi:thermostat"
       }
     },
     "select": {
@@ -94,6 +106,9 @@
       },
       "keypad_lockout": {
         "default": "mdi:lock"
+      },
+      "exercise_day_of_week": {
+        "default": "mdi:wrench-clock"
       }
     },
     "sensor": {
@@ -132,6 +147,15 @@
       },
       "hooks_state": {
         "default": "mdi:hook"
+      },
+      "open_window_detected": {
+        "default": "mdi:window-open"
+      },
+      "load_estimate": {
+        "default": "mdi:scale-balance"
+      },
+      "preheat_time": {
+        "default": "mdi:radiator"
       }
     },
     "switch": {
@@ -158,6 +182,21 @@
       },
       "hooks_locked": {
         "default": "mdi:lock"
+      },
+      "external_window_sensor": {
+        "default": "mdi:window-open"
+      },
+      "use_internal_window_detection": {
+        "default": "mdi:window-open"
+      },
+      "prioritize_external_temperature_sensor": {
+        "default": "mdi:thermometer"
+      },
+      "heat_available": {
+        "default": "mdi:water-boiler"
+      },
+      "use_load_balancing": {
+        "default": "mdi:scale-balance"
       }
     }
   },
diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json
index a5e57fcb1ec..df60829a1e2 100644
--- a/homeassistant/components/zha/manifest.json
+++ b/homeassistant/components/zha/manifest.json
@@ -21,7 +21,7 @@
     "zha",
     "universal_silabs_flasher"
   ],
-  "requirements": ["universal-silabs-flasher==0.0.22", "zha==0.0.31"],
+  "requirements": ["universal-silabs-flasher==0.0.22", "zha==0.0.32"],
   "usb": [
     {
       "vid": "10C4",
diff --git a/homeassistant/components/zha/update.py b/homeassistant/components/zha/update.py
index e12d048b190..3a857f9d89b 100644
--- a/homeassistant/components/zha/update.py
+++ b/homeassistant/components/zha/update.py
@@ -95,6 +95,7 @@ class ZHAFirmwareUpdateEntity(
         UpdateEntityFeature.INSTALL
         | UpdateEntityFeature.PROGRESS
         | UpdateEntityFeature.SPECIFIC_VERSION
+        | UpdateEntityFeature.RELEASE_NOTES
     )
 
     def __init__(self, entity_data: EntityData, **kwargs: Any) -> None:
@@ -143,6 +144,14 @@ class ZHAFirmwareUpdateEntity(
         """
         return self.entity_data.entity.release_summary
 
+    async def async_release_notes(self) -> str | None:
+        """Return full release notes.
+
+        This is suitable for a long changelog that does not fit in the release_summary
+        property. The returned string can contain markdown.
+        """
+        return self.entity_data.entity.release_notes
+
     @property
     def release_url(self) -> str | None:
         """URL to the full release notes of the latest version available."""
@@ -155,7 +164,7 @@ class ZHAFirmwareUpdateEntity(
     ) -> None:
         """Install an update."""
         try:
-            await self.entity_data.entity.async_install(version=version, backup=backup)
+            await self.entity_data.entity.async_install(version=version)
         except ZHAException as exc:
             raise HomeAssistantError(exc) from exc
         finally:
diff --git a/requirements_all.txt b/requirements_all.txt
index 42962c759aa..1bfe1756173 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -3012,7 +3012,7 @@ zeroconf==0.133.0
 zeversolar==0.3.1
 
 # homeassistant.components.zha
-zha==0.0.31
+zha==0.0.32
 
 # homeassistant.components.zhong_hong
 zhong-hong-hvac==1.0.12
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 6ba5ecbea6c..4a63a54d0b8 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -2389,7 +2389,7 @@ zeroconf==0.133.0
 zeversolar==0.3.1
 
 # homeassistant.components.zha
-zha==0.0.31
+zha==0.0.32
 
 # homeassistant.components.zwave_js
 zwave-js-server-python==0.57.0
diff --git a/tests/components/zha/snapshots/test_diagnostics.ambr b/tests/components/zha/snapshots/test_diagnostics.ambr
index 67655aebc8c..e0da54e2492 100644
--- a/tests/components/zha/snapshots/test_diagnostics.ambr
+++ b/tests/components/zha/snapshots/test_diagnostics.ambr
@@ -162,19 +162,19 @@
           '0x0500': dict({
             'attributes': dict({
               '0x0000': dict({
-                'attribute': "ZCLAttributeDef(id=0x0000, name='zone_state', type=<enum 'ZoneState'>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
+                'attribute': "ZCLAttributeDef(id=0x0000, name='zone_state', type=<enum 'ZoneState'>, zcl_type=<DataTypeId.enum8: 48>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
                 'value': None,
               }),
               '0x0001': dict({
-                'attribute': "ZCLAttributeDef(id=0x0001, name='zone_type', type=<enum 'ZoneType'>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
+                'attribute': "ZCLAttributeDef(id=0x0001, name='zone_type', type=<enum 'ZoneType'>, zcl_type=<DataTypeId.uint16: 33>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
                 'value': None,
               }),
               '0x0002': dict({
-                'attribute': "ZCLAttributeDef(id=0x0002, name='zone_status', type=<flag 'ZoneStatus'>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
+                'attribute': "ZCLAttributeDef(id=0x0002, name='zone_status', type=<flag 'ZoneStatus'>, zcl_type=<DataTypeId.map16: 25>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
                 'value': None,
               }),
               '0x0010': dict({
-                'attribute': "ZCLAttributeDef(id=0x0010, name='cie_addr', type=<class 'zigpy.types.named.EUI64'>, access=<ZCLAttributeAccess.Read|Write: 3>, mandatory=True, is_manufacturer_specific=False)",
+                'attribute': "ZCLAttributeDef(id=0x0010, name='cie_addr', type=<class 'zigpy.types.named.EUI64'>, zcl_type=<DataTypeId.EUI64: 240>, access=<ZCLAttributeAccess.Read|Write: 3>, mandatory=True, is_manufacturer_specific=False)",
                 'value': list([
                   50,
                   79,
@@ -187,15 +187,15 @@
                 ]),
               }),
               '0x0011': dict({
-                'attribute': "ZCLAttributeDef(id=0x0011, name='zone_id', type=<class 'zigpy.types.basic.uint8_t'>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
+                'attribute': "ZCLAttributeDef(id=0x0011, name='zone_id', type=<class 'zigpy.types.basic.uint8_t'>, zcl_type=<DataTypeId.uint8: 32>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
                 'value': None,
               }),
               '0x0012': dict({
-                'attribute': "ZCLAttributeDef(id=0x0012, name='num_zone_sensitivity_levels_supported', type=<class 'zigpy.types.basic.uint8_t'>, access=<ZCLAttributeAccess.Read: 1>, mandatory=False, is_manufacturer_specific=False)",
+                'attribute': "ZCLAttributeDef(id=0x0012, name='num_zone_sensitivity_levels_supported', type=<class 'zigpy.types.basic.uint8_t'>, zcl_type=<DataTypeId.uint8: 32>, access=<ZCLAttributeAccess.Read: 1>, mandatory=False, is_manufacturer_specific=False)",
                 'value': None,
               }),
               '0x0013': dict({
-                'attribute': "ZCLAttributeDef(id=0x0013, name='current_zone_sensitivity_level', type=<class 'zigpy.types.basic.uint8_t'>, access=<ZCLAttributeAccess.Read|Write: 3>, mandatory=False, is_manufacturer_specific=False)",
+                'attribute': "ZCLAttributeDef(id=0x0013, name='current_zone_sensitivity_level', type=<class 'zigpy.types.basic.uint8_t'>, zcl_type=<DataTypeId.uint8: 32>, access=<ZCLAttributeAccess.Read|Write: 3>, mandatory=False, is_manufacturer_specific=False)",
                 'value': None,
               }),
             }),
@@ -208,11 +208,11 @@
           '0x0501': dict({
             'attributes': dict({
               '0xfffd': dict({
-                'attribute': "ZCLAttributeDef(id=0xFFFD, name='cluster_revision', type=<class 'zigpy.types.basic.uint16_t'>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
+                'attribute': "ZCLAttributeDef(id=0xFFFD, name='cluster_revision', type=<class 'zigpy.types.basic.uint16_t'>, zcl_type=<DataTypeId.uint16: 33>, access=<ZCLAttributeAccess.Read: 1>, mandatory=True, is_manufacturer_specific=False)",
                 'value': None,
               }),
               '0xfffe': dict({
-                'attribute': "ZCLAttributeDef(id=0xFFFE, name='reporting_status', type=<enum 'AttributeReportingStatus'>, access=<ZCLAttributeAccess.Read: 1>, mandatory=False, is_manufacturer_specific=False)",
+                'attribute': "ZCLAttributeDef(id=0xFFFE, name='reporting_status', type=<enum 'AttributeReportingStatus'>, zcl_type=<DataTypeId.enum8: 48>, access=<ZCLAttributeAccess.Read: 1>, mandatory=False, is_manufacturer_specific=False)",
                 'value': None,
               }),
             }),
diff --git a/tests/components/zha/test_helpers.py b/tests/components/zha/test_helpers.py
index 13c03c17cf7..d3392685437 100644
--- a/tests/components/zha/test_helpers.py
+++ b/tests/components/zha/test_helpers.py
@@ -60,16 +60,14 @@ async def test_zcl_schema_conversions(hass: HomeAssistant) -> None:
             "required": True,
         },
         {
-            "type": "integer",
-            "valueMin": 0,
-            "valueMax": 255,
+            "type": "multi_select",
+            "options": ["Execute if off present"],
             "name": "options_mask",
             "optional": True,
         },
         {
-            "type": "integer",
-            "valueMin": 0,
-            "valueMax": 255,
+            "type": "multi_select",
+            "options": ["Execute if off"],
             "name": "options_override",
             "optional": True,
         },
diff --git a/tests/components/zha/test_update.py b/tests/components/zha/test_update.py
index 6a1a19b407f..e2a614915f9 100644
--- a/tests/components/zha/test_update.py
+++ b/tests/components/zha/test_update.py
@@ -3,8 +3,11 @@
 from unittest.mock import AsyncMock, call, patch
 
 import pytest
+from zha.application.platforms.update import (
+    FirmwareUpdateEntity as ZhaFirmwareUpdateEntity,
+)
 from zigpy.exceptions import DeliveryError
-from zigpy.ota import OtaImageWithMetadata
+from zigpy.ota import OtaImagesResult, OtaImageWithMetadata
 import zigpy.ota.image as firmware
 from zigpy.ota.providers import BaseOtaImageMetadata
 from zigpy.profiles import zha
@@ -43,6 +46,8 @@ from homeassistant.setup import async_setup_component
 from .common import find_entity_id, update_attribute_cache
 from .conftest import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
 
+from tests.typing import WebSocketGenerator
+
 
 @pytest.fixture(autouse=True)
 def update_platform_only():
@@ -119,8 +124,11 @@ async def setup_test_data(
         ),
     )
 
-    cluster.endpoint.device.application.ota.get_ota_image = AsyncMock(
-        return_value=None if file_not_found else fw_image
+    cluster.endpoint.device.application.ota.get_ota_images = AsyncMock(
+        return_value=OtaImagesResult(
+            upgrades=() if file_not_found else (fw_image,),
+            downgrades=(),
+        )
     )
     zha_device_proxy: ZHADeviceProxy = gateway_proxy.get_device_proxy(zigpy_device.ieee)
     zha_device_proxy.device.async_update_sw_build_id(installed_fw_version)
@@ -544,81 +552,56 @@ async def test_firmware_update_raises(
         )
 
 
-async def test_firmware_update_no_longer_compatible(
+async def test_update_release_notes(
     hass: HomeAssistant,
+    hass_ws_client: WebSocketGenerator,
     setup_zha,
     zigpy_device_mock,
 ) -> None:
-    """Test ZHA update platform - firmware update is no longer valid."""
+    """Test ZHA update platform release notes."""
     await setup_zha()
-    zha_device, cluster, fw_image, installed_fw_version = await setup_test_data(
-        hass, zigpy_device_mock
+
+    gateway = get_zha_gateway(hass)
+    gateway_proxy: ZHAGatewayProxy = get_zha_gateway_proxy(hass)
+
+    zigpy_device = zigpy_device_mock(
+        {
+            1: {
+                SIG_EP_INPUT: [general.Basic.cluster_id, general.OnOff.cluster_id],
+                SIG_EP_OUTPUT: [general.Ota.cluster_id],
+                SIG_EP_TYPE: zha.DeviceType.ON_OFF_SWITCH,
+                SIG_EP_PROFILE: zha.PROFILE_ID,
+            }
+        },
+        node_descriptor=b"\x02@\x84_\x11\x7fd\x00\x00,d\x00\x00",
     )
 
+    gateway.get_or_create_device(zigpy_device)
+    await gateway.async_device_initialized(zigpy_device)
+    await hass.async_block_till_done(wait_background_tasks=True)
+
+    zha_device: ZHADeviceProxy = gateway_proxy.get_device_proxy(zigpy_device.ieee)
+    zha_lib_entity = next(
+        e
+        for e in zha_device.device.platform_entities.values()
+        if isinstance(e, ZhaFirmwareUpdateEntity)
+    )
+    zha_lib_entity._attr_release_notes = "Some lengthy release notes"
+    zha_lib_entity.maybe_emit_state_changed_event()
+    await hass.async_block_till_done()
+
     entity_id = find_entity_id(Platform.UPDATE, zha_device, hass)
     assert entity_id is not None
 
-    assert hass.states.get(entity_id).state == STATE_UNKNOWN
-
-    # simulate an image available notification
-    await cluster._handle_query_next_image(
-        foundation.ZCLHeader.cluster(
-            tsn=0x12, command_id=general.Ota.ServerCommandDefs.query_next_image.id
-        ),
-        general.QueryNextImageCommand(
-            fw_image.firmware.header.field_control,
-            zha_device.device.manufacturer_code,
-            fw_image.firmware.header.image_type,
-            installed_fw_version,
-            fw_image.firmware.header.header_version,
-        ),
+    ws_client = await hass_ws_client(hass)
+    await ws_client.send_json(
+        {
+            "id": 1,
+            "type": "update/release_notes",
+            "entity_id": entity_id,
+        }
     )
 
-    await hass.async_block_till_done()
-    state = hass.states.get(entity_id)
-    assert state.state == STATE_ON
-    attrs = state.attributes
-    assert attrs[ATTR_INSTALLED_VERSION] == f"0x{installed_fw_version:08x}"
-    assert not attrs[ATTR_IN_PROGRESS]
-    assert (
-        attrs[ATTR_LATEST_VERSION] == f"0x{fw_image.firmware.header.file_version:08x}"
-    )
-
-    new_version = 0x99999999
-
-    async def endpoint_reply(cluster_id, tsn, data, command_id):
-        if cluster_id == general.Ota.cluster_id:
-            hdr, cmd = cluster.deserialize(data)
-            if isinstance(cmd, general.Ota.ImageNotifyCommand):
-                zha_device.device.device.packet_received(
-                    make_packet(
-                        zha_device.device.device,
-                        cluster,
-                        general.Ota.ServerCommandDefs.query_next_image.name,
-                        field_control=general.Ota.QueryNextImageCommand.FieldControl.HardwareVersion,
-                        manufacturer_code=fw_image.firmware.header.manufacturer_id,
-                        image_type=fw_image.firmware.header.image_type,
-                        # The device reports that it is no longer compatible!
-                        current_file_version=new_version,
-                        hardware_version=1,
-                    )
-                )
-
-    cluster.endpoint.reply = AsyncMock(side_effect=endpoint_reply)
-    with pytest.raises(HomeAssistantError):
-        await hass.services.async_call(
-            UPDATE_DOMAIN,
-            SERVICE_INSTALL,
-            {
-                ATTR_ENTITY_ID: entity_id,
-            },
-            blocking=True,
-        )
-
-    # We updated the currently installed firmware version, as it is no longer valid
-    state = hass.states.get(entity_id)
-    assert state.state == STATE_OFF
-    attrs = state.attributes
-    assert attrs[ATTR_INSTALLED_VERSION] == f"0x{new_version:08x}"
-    assert not attrs[ATTR_IN_PROGRESS]
-    assert attrs[ATTR_LATEST_VERSION] == f"0x{new_version:08x}"
+    result = await ws_client.receive_json()
+    assert result["success"] is True
+    assert result["result"] == "Some lengthy release notes"