From 462512182980cf8a4f3c2a34ad5b2c443f0f2d15 Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Fri, 5 Dec 2025 21:57:24 -0700 Subject: [PATCH 01/11] initial adding of inovelli vzm32 entities --- zha/application/platforms/number/__init__.py | 169 ++++++++++++++++++ zha/zigbee/cluster_handlers/const.py | 2 + .../cluster_handlers/manufacturerspecific.py | 74 ++++++++ 3 files changed, 245 insertions(+) diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index fed5c8d95..f770c2e68 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -25,6 +25,7 @@ CLUSTER_HANDLER_BASIC, CLUSTER_HANDLER_COLOR, CLUSTER_HANDLER_INOVELLI, + CLUSTER_HANDLER_INOVELLI_MMWAVE, CLUSTER_HANDLER_LEVEL, CLUSTER_HANDLER_OCCUPANCY, CLUSTER_HANDLER_THERMOSTAT, @@ -724,6 +725,174 @@ class InovelliDoubleTapDownLevel(NumberConfigurationEntity): _attr_translation_key: str = "double_tap_down_level" +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI, models={"VZM32-SN"} +) +class InovelliLightOnPresenceBehavior(NumberConfigurationEntity): + """Inovelli light on presence behavior configuration entity.""" + + _unique_id_suffix = "light_on_presence_behavior" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "light_on_presence_behavior" + _attr_translation_key: str = "light_on_presence_behavior" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveZMin(NumberConfigurationEntity): + """Inovelli mmwave Z minimum configuration entity.""" + + _unique_id_suffix = "mmwave_z_min" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_z_min" + _attr_translation_key: str = "mmwave_z_min" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveZMax(NumberConfigurationEntity): + """Inovelli mmwave Z maximum configuration entity.""" + + _unique_id_suffix = "mmwave_z_max" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_z_max" + _attr_translation_key: str = "mmwave_z_max" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveXMin(NumberConfigurationEntity): + """Inovelli mmwave X minimum configuration entity.""" + + _unique_id_suffix = "mmwave_x_min" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_x_min" + _attr_translation_key: str = "mmwave_x_min" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveXMax(NumberConfigurationEntity): + """Inovelli mmwave X maximum configuration entity.""" + + _unique_id_suffix = "mmwave_x_max" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_x_max" + _attr_translation_key: str = "mmwave_x_max" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveYMin(NumberConfigurationEntity): + """Inovelli mmwave Y minimum configuration entity.""" + + _unique_id_suffix = "mmwave_y_min" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_y_min" + _attr_translation_key: str = "mmwave_y_min" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveYMax(NumberConfigurationEntity): + """Inovelli mmwave Y maximum configuration entity.""" + + _unique_id_suffix = "mmwave_y_max" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_y_max" + _attr_translation_key: str = "mmwave_y_max" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveTargetInfoReport(NumberConfigurationEntity): + """Inovelli mmwave target info report configuration entity.""" + + _unique_id_suffix = "mmwave_target_info_report" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_target_info_report" + _attr_translation_key: str = "mmwave_target_info_report" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveStayLife(NumberConfigurationEntity): + """Inovelli mmwave stay life configuration entity.""" + + _unique_id_suffix = "mmwave_stay_life" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_stay_life" + _attr_translation_key: str = "mmwave_stay_life" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveDetectSensitivity(NumberConfigurationEntity): + """Inovelli mmwave detect sensitivity configuration entity.""" + + _unique_id_suffix = "mmwave_detect_sensitivity" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_detect_sensitivity" + _attr_translation_key: str = "mmwave_detect_sensitivity" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveDetectTrigger(NumberConfigurationEntity): + """Inovelli mmwave detect trigger configuration entity.""" + + _unique_id_suffix = "mmwave_detect_trigger" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_detect_trigger" + _attr_translation_key: str = "mmwave_detect_trigger" + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveHoldTime(NumberConfigurationEntity): + """Inovelli mmwave hold time configuration entity.""" + + _unique_id_suffix = "mmwave_hold_time" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: float = 0 + _attr_native_max_value: float = 255 + _attribute_name = "mmwave_hold_time" + _attr_translation_key: str = "mmwave_hold_time" + + @CONFIG_DIAGNOSTIC_MATCH( cluster_handler_names="opple_cluster", models={"aqara.feeder.acn001"} ) diff --git a/zha/zigbee/cluster_handlers/const.py b/zha/zigbee/cluster_handlers/const.py index 5cdae12c7..2cd01d94f 100644 --- a/zha/zigbee/cluster_handlers/const.py +++ b/zha/zigbee/cluster_handlers/const.py @@ -80,12 +80,14 @@ CLUSTER_HANDLER_ZONE: Final[str] = "ias_zone" ZONE: Final[str] = CLUSTER_HANDLER_ZONE CLUSTER_HANDLER_INOVELLI = "inovelli_vzm31sn_cluster" +CLUSTER_HANDLER_INOVELLI_MMWAVE: Final[str] = "cluster_handler_0xfc32" AQARA_OPPLE_CLUSTER: Final[int] = 0xFCC0 IKEA_AIR_PURIFIER_CLUSTER: Final[int] = 0xFC7D IKEA_REMOTE_CLUSTER: Final[int] = 0xFC80 IKEA_SHORTCUT_V1_CLUSTER: Final[int] = 0xFC7F INOVELLI_CLUSTER: Final[int] = 0xFC31 +INOVELLI_MMWAVE_CLUSTER: Final[int] = 0xFC32 OSRAM_BUTTON_CLUSTER: Final[int] = 0xFD00 PHILIPS_CONTACT_CLUSTER: Final[int] = 0xFC06 PHILLIPS_REMOTE_CLUSTER: Final[int] = 0xFC00 diff --git a/zha/zigbee/cluster_handlers/manufacturerspecific.py b/zha/zigbee/cluster_handlers/manufacturerspecific.py index 89ef94e8d..8fd28bd17 100644 --- a/zha/zigbee/cluster_handlers/manufacturerspecific.py +++ b/zha/zigbee/cluster_handlers/manufacturerspecific.py @@ -32,6 +32,7 @@ IKEA_REMOTE_CLUSTER, IKEA_SHORTCUT_V1_CLUSTER, INOVELLI_CLUSTER, + INOVELLI_MMWAVE_CLUSTER, LEGRAND_CABLE_OUTLET_CLUSTER, OSRAM_BUTTON_CLUSTER, PHILIPS_CONTACT_CLUSTER, @@ -358,6 +359,53 @@ def __init__(self, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> None: "relay_click_in_on_off_mode": True, "disable_clear_notifications_double_tap": True, } + elif self.cluster.endpoint.model == "VZM32-SN": + self.ZCL_INIT_ATTRS = { + "dimming_speed_up_remote": True, + "dimming_speed_up_local": True, + "ramp_rate_off_to_on_remote": True, + "ramp_rate_off_to_on_local": True, + "dimming_speed_down_remote": True, + "dimming_speed_down_local": True, + "ramp_rate_on_to_off_remote": True, + "ramp_rate_on_to_off_local": True, + "minimum_level": True, + "maximum_level": True, + "invert_switch": True, + "auto_off_timer": True, + "default_level_local": True, + "default_level_remote": True, + "state_after_power_restored": True, + "load_level_indicator_timeout": True, + "active_power_reports": True, + "periodic_power_and_energy_reports": True, + "active_energy_reports": True, + "power_type": False, + "switch_type": False, + "increased_non_neutral_output": True, + "leading_or_trailing_edge": True, + "internal_temp_monitor": True, + "overheated": True, + "button_delay": False, + "smart_bulb_mode": False, + "double_tap_up_enabled": True, + "double_tap_down_enabled": True, + "double_tap_up_level": True, + "double_tap_down_level": True, + "led_color_when_on": True, + "led_color_when_off": True, + "led_intensity_when_on": True, + "led_intensity_when_off": True, + "led_scaling_mode": True, + "aux_switch_scenes": True, + "binding_off_to_on_sync_level": True, + "local_protection": False, + "output_mode": False, + "on_off_led_mode": True, + "firmware_progress_led": True, + "disable_clear_notifications_double_tap": True, + "light_on_presence_behavior": True, + } elif self.cluster.endpoint.model == "VZM35-SN": self.ZCL_INIT_ATTRS = { "dimming_speed_up_remote": True, @@ -433,6 +481,32 @@ async def issue_individual_led_effect( # pylint: disable=too-many-arguments,unu ) +@registries.CLUSTER_HANDLER_ONLY_CLUSTERS.register(INOVELLI_MMWAVE_CLUSTER) +@registries.CLUSTER_HANDLER_REGISTRY.register(INOVELLI_MMWAVE_CLUSTER) +class InovelliMmwaveClusterHandler(ClusterHandler): + """Inovelli mmwave cluster handler.""" + + REPORT_CONFIG = () + + def __init__(self, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> None: + """Initialize Inovelli mmwave cluster handler.""" + super().__init__(cluster, endpoint) + if self.cluster.endpoint.model == "VZM32-SN": + self.ZCL_INIT_ATTRS = { + "mmwave_z_min": True, + "mmwave_z_max": True, + "mmwave_x_min": True, + "mmwave_x_max": True, + "mmwave_y_min": True, + "mmwave_y_max": True, + "mmwave_target_info_report": True, + "mmwave_stay_life": True, + "mmwave_detect_sensitivity": True, + "mmwave_detect_trigger": True, + "mmwave_hold_time": True, + } + + @registries.CLUSTER_HANDLER_ONLY_CLUSTERS.register(IKEA_AIR_PURIFIER_CLUSTER) @registries.CLUSTER_HANDLER_REGISTRY.register(IKEA_AIR_PURIFIER_CLUSTER) class IkeaAirPurifierClusterHandler(ClusterHandler): From 433237f14cb824138ed9ccb86d34e0dab1596379 Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Fri, 5 Dec 2025 23:31:16 -0700 Subject: [PATCH 02/11] override name proprety for mmwave cluster --- zha/zigbee/cluster_handlers/manufacturerspecific.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zha/zigbee/cluster_handlers/manufacturerspecific.py b/zha/zigbee/cluster_handlers/manufacturerspecific.py index 8fd28bd17..568ac22b9 100644 --- a/zha/zigbee/cluster_handlers/manufacturerspecific.py +++ b/zha/zigbee/cluster_handlers/manufacturerspecific.py @@ -3,6 +3,7 @@ from __future__ import annotations from datetime import datetime +import functools import logging from typing import TYPE_CHECKING, Any @@ -28,6 +29,7 @@ ATTRIBUTE_ID, ATTRIBUTE_NAME, ATTRIBUTE_VALUE, + CLUSTER_HANDLER_INOVELLI_MMWAVE, IKEA_AIR_PURIFIER_CLUSTER, IKEA_REMOTE_CLUSTER, IKEA_SHORTCUT_V1_CLUSTER, @@ -506,6 +508,11 @@ def __init__(self, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> None: "mmwave_hold_time": True, } + @functools.cached_property + def name(self) -> str: + """Return friendly name.""" + return CLUSTER_HANDLER_INOVELLI_MMWAVE + @registries.CLUSTER_HANDLER_ONLY_CLUSTERS.register(IKEA_AIR_PURIFIER_CLUSTER) @registries.CLUSTER_HANDLER_REGISTRY.register(IKEA_AIR_PURIFIER_CLUSTER) From 97130f98ef93172223c5156929955a85dd749fbe Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Fri, 5 Dec 2025 23:41:43 -0700 Subject: [PATCH 03/11] fix labels for entities --- zha/application/platforms/number/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index f770c2e68..6d09c9f31 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -751,6 +751,7 @@ class InovelliMmwaveZMin(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_z_min" _attr_translation_key: str = "mmwave_z_min" + _attr_fallback_name = "mmWave Height Minimum (Floor)" @CONFIG_DIAGNOSTIC_MATCH( @@ -765,6 +766,7 @@ class InovelliMmwaveZMax(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_z_max" _attr_translation_key: str = "mmwave_z_max" + _attr_fallback_name = "mmWave Height Maximum (Ceiling)" @CONFIG_DIAGNOSTIC_MATCH( @@ -779,6 +781,7 @@ class InovelliMmwaveXMin(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_x_min" _attr_translation_key: str = "mmwave_x_min" + _attr_fallback_name = "mmWave Width Minimum (Left)" @CONFIG_DIAGNOSTIC_MATCH( @@ -793,6 +796,7 @@ class InovelliMmwaveXMax(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_x_max" _attr_translation_key: str = "mmwave_x_max" + _attr_fallback_name = "mmWave Width Maximum (Right)" @CONFIG_DIAGNOSTIC_MATCH( @@ -807,6 +811,7 @@ class InovelliMmwaveYMin(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_y_min" _attr_translation_key: str = "mmwave_y_min" + _attr_fallback_name = "mmWave Depth Minimum (Near)" @CONFIG_DIAGNOSTIC_MATCH( @@ -821,6 +826,7 @@ class InovelliMmwaveYMax(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_y_max" _attr_translation_key: str = "mmwave_y_max" + _attr_fallback_name = "mmWave Depth Maximum (Far)" @CONFIG_DIAGNOSTIC_MATCH( @@ -835,6 +841,7 @@ class InovelliMmwaveTargetInfoReport(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_target_info_report" _attr_translation_key: str = "mmwave_target_info_report" + _attr_fallback_name = "mmWave Target Info Report" @CONFIG_DIAGNOSTIC_MATCH( @@ -849,6 +856,7 @@ class InovelliMmwaveStayLife(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_stay_life" _attr_translation_key: str = "mmwave_stay_life" + _attr_fallback_name = "mmWave Stay Life" @CONFIG_DIAGNOSTIC_MATCH( @@ -863,6 +871,7 @@ class InovelliMmwaveDetectSensitivity(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_detect_sensitivity" _attr_translation_key: str = "mmwave_detect_sensitivity" + _attr_fallback_name = "mmWave Sensitivity" @CONFIG_DIAGNOSTIC_MATCH( @@ -877,6 +886,7 @@ class InovelliMmwaveDetectTrigger(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_detect_trigger" _attr_translation_key: str = "mmwave_detect_trigger" + _attr_fallback_name = "mmWave Target Speed" @CONFIG_DIAGNOSTIC_MATCH( @@ -891,6 +901,7 @@ class InovelliMmwaveHoldTime(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "mmwave_hold_time" _attr_translation_key: str = "mmwave_hold_time" + _attr_fallback_name = "mmWave Detection Timeout" @CONFIG_DIAGNOSTIC_MATCH( From 3ebdf9d2492699798ee79be7de38701f1953d99c Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Fri, 5 Dec 2025 23:56:20 -0700 Subject: [PATCH 04/11] modify min and max for mmwave config --- zha/application/platforms/number/__init__.py | 38 ++++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index 6d09c9f31..1ef7fe267 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -747,11 +747,12 @@ class InovelliMmwaveZMin(NumberConfigurationEntity): _unique_id_suffix = "mmwave_z_min" _attr_entity_category = EntityCategory.CONFIG - _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_min_value: float = -600 + _attr_native_max_value: float = 600 _attribute_name = "mmwave_z_min" _attr_translation_key: str = "mmwave_z_min" _attr_fallback_name = "mmWave Height Minimum (Floor)" + _attr_mode: NumberMode = NumberMode.BOX @CONFIG_DIAGNOSTIC_MATCH( @@ -762,11 +763,12 @@ class InovelliMmwaveZMax(NumberConfigurationEntity): _unique_id_suffix = "mmwave_z_max" _attr_entity_category = EntityCategory.CONFIG - _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_min_value: float = -600 + _attr_native_max_value: float = 600 _attribute_name = "mmwave_z_max" _attr_translation_key: str = "mmwave_z_max" _attr_fallback_name = "mmWave Height Maximum (Ceiling)" + _attr_mode: NumberMode = NumberMode.BOX @CONFIG_DIAGNOSTIC_MATCH( @@ -777,11 +779,12 @@ class InovelliMmwaveXMin(NumberConfigurationEntity): _unique_id_suffix = "mmwave_x_min" _attr_entity_category = EntityCategory.CONFIG - _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_min_value: float = -600 + _attr_native_max_value: float = 600 _attribute_name = "mmwave_x_min" _attr_translation_key: str = "mmwave_x_min" _attr_fallback_name = "mmWave Width Minimum (Left)" + _attr_mode: NumberMode = NumberMode.BOX @CONFIG_DIAGNOSTIC_MATCH( @@ -792,11 +795,12 @@ class InovelliMmwaveXMax(NumberConfigurationEntity): _unique_id_suffix = "mmwave_x_max" _attr_entity_category = EntityCategory.CONFIG - _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_min_value: float = -600 + _attr_native_max_value: float = 600 _attribute_name = "mmwave_x_max" _attr_translation_key: str = "mmwave_x_max" _attr_fallback_name = "mmWave Width Maximum (Right)" + _attr_mode: NumberMode = NumberMode.BOX @CONFIG_DIAGNOSTIC_MATCH( @@ -808,10 +812,11 @@ class InovelliMmwaveYMin(NumberConfigurationEntity): _unique_id_suffix = "mmwave_y_min" _attr_entity_category = EntityCategory.CONFIG _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_max_value: float = 600 _attribute_name = "mmwave_y_min" _attr_translation_key: str = "mmwave_y_min" _attr_fallback_name = "mmWave Depth Minimum (Near)" + _attr_mode: NumberMode = NumberMode.BOX @CONFIG_DIAGNOSTIC_MATCH( @@ -823,10 +828,11 @@ class InovelliMmwaveYMax(NumberConfigurationEntity): _unique_id_suffix = "mmwave_y_max" _attr_entity_category = EntityCategory.CONFIG _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_max_value: float = 600 _attribute_name = "mmwave_y_max" _attr_translation_key: str = "mmwave_y_max" _attr_fallback_name = "mmWave Depth Maximum (Far)" + _attr_mode: NumberMode = NumberMode.BOX @CONFIG_DIAGNOSTIC_MATCH( @@ -838,7 +844,7 @@ class InovelliMmwaveTargetInfoReport(NumberConfigurationEntity): _unique_id_suffix = "mmwave_target_info_report" _attr_entity_category = EntityCategory.CONFIG _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_max_value: float = 1 _attribute_name = "mmwave_target_info_report" _attr_translation_key: str = "mmwave_target_info_report" _attr_fallback_name = "mmWave Target Info Report" @@ -853,10 +859,11 @@ class InovelliMmwaveStayLife(NumberConfigurationEntity): _unique_id_suffix = "mmwave_stay_life" _attr_entity_category = EntityCategory.CONFIG _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_max_value: float = 4294967295 _attribute_name = "mmwave_stay_life" _attr_translation_key: str = "mmwave_stay_life" _attr_fallback_name = "mmWave Stay Life" + _attr_mode: NumberMode = NumberMode.BOX @CONFIG_DIAGNOSTIC_MATCH( @@ -868,7 +875,7 @@ class InovelliMmwaveDetectSensitivity(NumberConfigurationEntity): _unique_id_suffix = "mmwave_detect_sensitivity" _attr_entity_category = EntityCategory.CONFIG _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_max_value: float = 2 _attribute_name = "mmwave_detect_sensitivity" _attr_translation_key: str = "mmwave_detect_sensitivity" _attr_fallback_name = "mmWave Sensitivity" @@ -883,7 +890,7 @@ class InovelliMmwaveDetectTrigger(NumberConfigurationEntity): _unique_id_suffix = "mmwave_detect_trigger" _attr_entity_category = EntityCategory.CONFIG _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_max_value: float = 2 _attribute_name = "mmwave_detect_trigger" _attr_translation_key: str = "mmwave_detect_trigger" _attr_fallback_name = "mmWave Target Speed" @@ -898,10 +905,11 @@ class InovelliMmwaveHoldTime(NumberConfigurationEntity): _unique_id_suffix = "mmwave_hold_time" _attr_entity_category = EntityCategory.CONFIG _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 + _attr_native_max_value: float = 4294967296 _attribute_name = "mmwave_hold_time" _attr_translation_key: str = "mmwave_hold_time" _attr_fallback_name = "mmWave Detection Timeout" + _attr_mode: NumberMode = NumberMode.BOX @CONFIG_DIAGNOSTIC_MATCH( From 432513760189021c98acf3697e293a4749d09cc5 Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Sat, 6 Dec 2025 00:09:38 -0700 Subject: [PATCH 05/11] change some entity types --- zha/application/platforms/number/__init__.py | 61 -------------------- zha/application/platforms/select.py | 43 ++++++++++++++ 2 files changed, 43 insertions(+), 61 deletions(-) diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index 1ef7fe267..eaa5c4824 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -835,67 +835,6 @@ class InovelliMmwaveYMax(NumberConfigurationEntity): _attr_mode: NumberMode = NumberMode.BOX -@CONFIG_DIAGNOSTIC_MATCH( - cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} -) -class InovelliMmwaveTargetInfoReport(NumberConfigurationEntity): - """Inovelli mmwave target info report configuration entity.""" - - _unique_id_suffix = "mmwave_target_info_report" - _attr_entity_category = EntityCategory.CONFIG - _attr_native_min_value: float = 0 - _attr_native_max_value: float = 1 - _attribute_name = "mmwave_target_info_report" - _attr_translation_key: str = "mmwave_target_info_report" - _attr_fallback_name = "mmWave Target Info Report" - - -@CONFIG_DIAGNOSTIC_MATCH( - cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} -) -class InovelliMmwaveStayLife(NumberConfigurationEntity): - """Inovelli mmwave stay life configuration entity.""" - - _unique_id_suffix = "mmwave_stay_life" - _attr_entity_category = EntityCategory.CONFIG - _attr_native_min_value: float = 0 - _attr_native_max_value: float = 4294967295 - _attribute_name = "mmwave_stay_life" - _attr_translation_key: str = "mmwave_stay_life" - _attr_fallback_name = "mmWave Stay Life" - _attr_mode: NumberMode = NumberMode.BOX - - -@CONFIG_DIAGNOSTIC_MATCH( - cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} -) -class InovelliMmwaveDetectSensitivity(NumberConfigurationEntity): - """Inovelli mmwave detect sensitivity configuration entity.""" - - _unique_id_suffix = "mmwave_detect_sensitivity" - _attr_entity_category = EntityCategory.CONFIG - _attr_native_min_value: float = 0 - _attr_native_max_value: float = 2 - _attribute_name = "mmwave_detect_sensitivity" - _attr_translation_key: str = "mmwave_detect_sensitivity" - _attr_fallback_name = "mmWave Sensitivity" - - -@CONFIG_DIAGNOSTIC_MATCH( - cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} -) -class InovelliMmwaveDetectTrigger(NumberConfigurationEntity): - """Inovelli mmwave detect trigger configuration entity.""" - - _unique_id_suffix = "mmwave_detect_trigger" - _attr_entity_category = EntityCategory.CONFIG - _attr_native_min_value: float = 0 - _attr_native_max_value: float = 2 - _attribute_name = "mmwave_detect_trigger" - _attr_translation_key: str = "mmwave_detect_trigger" - _attr_fallback_name = "mmWave Target Speed" - - @CONFIG_DIAGNOSTIC_MATCH( cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} ) diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index a943d5f82..457dce591 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -31,6 +31,7 @@ CLUSTER_HANDLER_HUE_OCCUPANCY, CLUSTER_HANDLER_IAS_WD, CLUSTER_HANDLER_INOVELLI, + CLUSTER_HANDLER_INOVELLI_MMWAVE, CLUSTER_HANDLER_OCCUPANCY, CLUSTER_HANDLER_ON_OFF, CLUSTER_HANDLER_THERMOSTAT, @@ -582,6 +583,48 @@ class InovelliLedScalingModeEntity(ZCLEnumSelectEntity): _attr_translation_key: str = "led_scaling_mode" +class InovelliMmwaveSensitivity(types.enum8): + """Inovelli mmwave sensitivity.""" + + Low = 0x00 + Medium = 0x01 + High = 0x02 + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveSensitivityEntity(ZCLEnumSelectEntity): + """Inovelli mmwave sensitivity control.""" + + _unique_id_suffix = "mmwave_detect_sensitivity" + _attribute_name = "mmwave_detect_sensitivity" + _enum = InovelliMmwaveSensitivity + _attr_translation_key: str = "mmwave_detect_sensitivity" + _attr_fallback_name = "mmWave Sensitivity" + + +class InovelliMmwaveTargetSpeed(types.enum8): + """Inovelli mmwave target speed.""" + + Low = 0x00 # 5s + Medium = 0x01 # 1s + Fast = 0x02 # 0.2s + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} +) +class InovelliMmwaveTargetSpeedEntity(ZCLEnumSelectEntity): + """Inovelli mmwave target speed control.""" + + _unique_id_suffix = "mmwave_detect_trigger" + _attribute_name = "mmwave_detect_trigger" + _enum = InovelliMmwaveTargetSpeed + _attr_translation_key: str = "mmwave_detect_trigger" + _attr_fallback_name = "mmWave Target Speed" + + class InovelliFanLedScalingMode(types.enum8): """Inovelli fan led mode.""" From 0c804f78e067f2af5111cfc42287aabd3394ab2b Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Sat, 6 Dec 2025 00:29:36 -0700 Subject: [PATCH 06/11] adding light_on_presence and preset_room_size --- zha/application/platforms/select.py | 22 +++++++++++++++++++ .../cluster_handlers/manufacturerspecific.py | 7 ++++++ 2 files changed, 29 insertions(+) diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index 457dce591..ccf928d7e 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -625,6 +625,28 @@ class InovelliMmwaveTargetSpeedEntity(ZCLEnumSelectEntity): _attr_fallback_name = "mmWave Target Speed" +class InovelliMmwaveRoomSizePreset(types.enum8): + """Inovelli mmwave room size preset.""" + + Custom = 0x00 # User-defined + Small = 0x01 # X: -100 to 100, Y: 0 to 200, Z: -100 to 100 + Medium = 0x02 # X: -160 to 160, Y: 0 to 280, Z: -100 to 100 + Large = 0x03 # X: -210 to 210, Y: 0 to 360, Z: -100 to 100 + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI, models={"VZM32-SN"} +) +class InovelliMmwaveRoomSizePresetEntity(ZCLEnumSelectEntity): + """Inovelli mmwave room size preset control.""" + + _unique_id_suffix = "mmwave_room_size_preset" + _attribute_name = "mmwave_room_size_preset" + _enum = InovelliMmwaveRoomSizePreset + _attr_translation_key: str = "mmwave_room_size_preset" + _attr_fallback_name = "Room Size Preset" + + class InovelliFanLedScalingMode(types.enum8): """Inovelli fan led mode.""" diff --git a/zha/zigbee/cluster_handlers/manufacturerspecific.py b/zha/zigbee/cluster_handlers/manufacturerspecific.py index 568ac22b9..d04a8caea 100644 --- a/zha/zigbee/cluster_handlers/manufacturerspecific.py +++ b/zha/zigbee/cluster_handlers/manufacturerspecific.py @@ -29,6 +29,7 @@ ATTRIBUTE_ID, ATTRIBUTE_NAME, ATTRIBUTE_VALUE, + CLUSTER_HANDLER_INOVELLI, CLUSTER_HANDLER_INOVELLI_MMWAVE, IKEA_AIR_PURIFIER_CLUSTER, IKEA_REMOTE_CLUSTER, @@ -407,6 +408,7 @@ def __init__(self, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> None: "firmware_progress_led": True, "disable_clear_notifications_double_tap": True, "light_on_presence_behavior": True, + "mmwave_room_size_preset": True, } elif self.cluster.endpoint.model == "VZM35-SN": self.ZCL_INIT_ATTRS = { @@ -482,6 +484,11 @@ async def issue_individual_led_effect( # pylint: disable=too-many-arguments,unu led_number, effect_type, color, level, duration, expect_reply=False ) + @functools.cached_property + def name(self) -> str: + """Return friendly name.""" + return CLUSTER_HANDLER_INOVELLI + @registries.CLUSTER_HANDLER_ONLY_CLUSTERS.register(INOVELLI_MMWAVE_CLUSTER) @registries.CLUSTER_HANDLER_REGISTRY.register(INOVELLI_MMWAVE_CLUSTER) From c28a53b5f84c8715d492d2ed06d842bb7385faf4 Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Sat, 6 Dec 2025 00:38:22 -0700 Subject: [PATCH 07/11] fix light on presence behavior entity --- zha/application/platforms/number/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index eaa5c4824..530781ab9 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -737,6 +737,7 @@ class InovelliLightOnPresenceBehavior(NumberConfigurationEntity): _attr_native_max_value: float = 255 _attribute_name = "light_on_presence_behavior" _attr_translation_key: str = "light_on_presence_behavior" + _attr_fallback_name = "Light On Presence Behavior" @CONFIG_DIAGNOSTIC_MATCH( From 5121af10d683b39d86dcf4a718c740245bdbbc61 Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Sat, 6 Dec 2025 00:46:48 -0700 Subject: [PATCH 08/11] fix select options for two entities --- zha/application/platforms/number/__init__.py | 13 -------- zha/application/platforms/select.py | 33 ++++++++++++++++++-- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index 530781ab9..1bd0c7c39 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -725,19 +725,6 @@ class InovelliDoubleTapDownLevel(NumberConfigurationEntity): _attr_translation_key: str = "double_tap_down_level" -@CONFIG_DIAGNOSTIC_MATCH( - cluster_handler_names=CLUSTER_HANDLER_INOVELLI, models={"VZM32-SN"} -) -class InovelliLightOnPresenceBehavior(NumberConfigurationEntity): - """Inovelli light on presence behavior configuration entity.""" - - _unique_id_suffix = "light_on_presence_behavior" - _attr_entity_category = EntityCategory.CONFIG - _attr_native_min_value: float = 0 - _attr_native_max_value: float = 255 - _attribute_name = "light_on_presence_behavior" - _attr_translation_key: str = "light_on_presence_behavior" - _attr_fallback_name = "Light On Presence Behavior" @CONFIG_DIAGNOSTIC_MATCH( diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index ccf928d7e..dcce15dc6 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -629,9 +629,36 @@ class InovelliMmwaveRoomSizePreset(types.enum8): """Inovelli mmwave room size preset.""" Custom = 0x00 # User-defined - Small = 0x01 # X: -100 to 100, Y: 0 to 200, Z: -100 to 100 - Medium = 0x02 # X: -160 to 160, Y: 0 to 280, Z: -100 to 100 - Large = 0x03 # X: -210 to 210, Y: 0 to 360, Z: -100 to 100 + X_Small = 0x01 # X: -100 to 100, Y: 0 to 200, Z: -100 to 100 + Small = 0x02 # X: -160 to 160, Y: 0 to 280, Z: -100 to 100 + Medium = 0x03 # X: -210 to 210, Y: 0 to 360, Z: -100 to 100 + Large = 0x04 # X: -260 to 260, Y: 0 to 400, Z: -100 to 100 + X_Large = 0x05 # X: -310 to 310, Y: 0 to 460, Z: -100 to 100 + + +class InovelliLightOnPresenceBehavior(types.enum8): + """Inovelli light on presence behavior.""" + + Disabled = 0x00 + On_When_Occupied_Off_When_Unoccupied = 0x01 # Auto On/Off when occupied (default) + Off_When_Vacant = 0x02 # Auto Off when vacant + On_When_Occupied = 0x03 # Auto On when occupied + On_When_Vacant_Off_When_Occupied = 0x04 # Auto On/Off when Vacant + On_When_Vacant = 0x05 # Auto On when Vacant + Off_When_Occupied = 0x06 # Auto Off when Occupied + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI, models={"VZM32-SN"} +) +class InovelliLightOnPresenceBehaviorEntity(ZCLEnumSelectEntity): + """Inovelli light on presence behavior control.""" + + _unique_id_suffix = "light_on_presence_behavior" + _attribute_name = "light_on_presence_behavior" + _enum = InovelliLightOnPresenceBehavior + _attr_translation_key: str = "light_on_presence_behavior" + _attr_fallback_name = "Light On Presence Behavior" @CONFIG_DIAGNOSTIC_MATCH( From 3e16c9fccb7d5096af61a72b593bdc5f10c17701 Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Sat, 6 Dec 2025 20:40:05 -0700 Subject: [PATCH 09/11] changing constant value for consistency. Removing some unused attributes --- zha/zigbee/cluster_handlers/const.py | 2 +- zha/zigbee/cluster_handlers/manufacturerspecific.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/zha/zigbee/cluster_handlers/const.py b/zha/zigbee/cluster_handlers/const.py index 2cd01d94f..f3237735f 100644 --- a/zha/zigbee/cluster_handlers/const.py +++ b/zha/zigbee/cluster_handlers/const.py @@ -80,7 +80,7 @@ CLUSTER_HANDLER_ZONE: Final[str] = "ias_zone" ZONE: Final[str] = CLUSTER_HANDLER_ZONE CLUSTER_HANDLER_INOVELLI = "inovelli_vzm31sn_cluster" -CLUSTER_HANDLER_INOVELLI_MMWAVE: Final[str] = "cluster_handler_0xfc32" +CLUSTER_HANDLER_INOVELLI_MMWAVE: Final[str] = "inovelli_vzm32sn_cluster" AQARA_OPPLE_CLUSTER: Final[int] = 0xFCC0 IKEA_AIR_PURIFIER_CLUSTER: Final[int] = 0xFC7D diff --git a/zha/zigbee/cluster_handlers/manufacturerspecific.py b/zha/zigbee/cluster_handlers/manufacturerspecific.py index d04a8caea..92563a56e 100644 --- a/zha/zigbee/cluster_handlers/manufacturerspecific.py +++ b/zha/zigbee/cluster_handlers/manufacturerspecific.py @@ -508,8 +508,6 @@ def __init__(self, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> None: "mmwave_x_max": True, "mmwave_y_min": True, "mmwave_y_max": True, - "mmwave_target_info_report": True, - "mmwave_stay_life": True, "mmwave_detect_sensitivity": True, "mmwave_detect_trigger": True, "mmwave_hold_time": True, From 30cb5dfabcf262769efe733cb28c63c1cc85e94c Mon Sep 17 00:00:00 2001 From: InovelliUSA Date: Sat, 6 Dec 2025 20:56:19 -0700 Subject: [PATCH 10/11] adding switch type param --- zha/application/platforms/select.py | 20 +++++++++++++++++++ .../cluster_handlers/manufacturerspecific.py | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/zha/application/platforms/select.py b/zha/application/platforms/select.py index dcce15dc6..99bd74021 100644 --- a/zha/application/platforms/select.py +++ b/zha/application/platforms/select.py @@ -545,6 +545,26 @@ class InovelliSwitchTypeEntity(ZCLEnumSelectEntity): _attr_translation_key: str = "switch_type" +class InovelliVZM32SwitchType(types.enum8): + """Inovelli VZM32-SN switch type.""" + + Single_Pole = 0x00 + Three_Way_AUX = 0x01 + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_INOVELLI, models={"VZM32-SN"} +) +class InovelliVZM32SwitchTypeEntity(ZCLEnumSelectEntity): + """Inovelli VZM32-SN switch type control.""" + + _unique_id_suffix = "switch_type" + _attribute_name = "switch_type" + _enum = InovelliVZM32SwitchType + _attr_translation_key: str = "switch_type" + _attr_fallback_name = "Switch Type" + + class InovelliFanSwitchType(types.enum1): """Inovelli fan switch mode.""" diff --git a/zha/zigbee/cluster_handlers/manufacturerspecific.py b/zha/zigbee/cluster_handlers/manufacturerspecific.py index 92563a56e..8bbcf3db4 100644 --- a/zha/zigbee/cluster_handlers/manufacturerspecific.py +++ b/zha/zigbee/cluster_handlers/manufacturerspecific.py @@ -384,7 +384,7 @@ def __init__(self, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> None: "periodic_power_and_energy_reports": True, "active_energy_reports": True, "power_type": False, - "switch_type": False, + "switch_type": True, "increased_non_neutral_output": True, "leading_or_trailing_edge": True, "internal_temp_monitor": True, From 5d76c901664b38ba359ece425ce44853522cf9af Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 00:31:41 +0000 Subject: [PATCH 11/11] Apply pre-commit auto fixes --- zha/application/platforms/number/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/zha/application/platforms/number/__init__.py b/zha/application/platforms/number/__init__.py index 1bd0c7c39..8cbe64cce 100644 --- a/zha/application/platforms/number/__init__.py +++ b/zha/application/platforms/number/__init__.py @@ -725,8 +725,6 @@ class InovelliDoubleTapDownLevel(NumberConfigurationEntity): _attr_translation_key: str = "double_tap_down_level" - - @CONFIG_DIAGNOSTIC_MATCH( cluster_handler_names=CLUSTER_HANDLER_INOVELLI_MMWAVE, models={"VZM32-SN"} )