Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions zha/application/platforms/number/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -724,6 +725,118 @@ class InovelliDoubleTapDownLevel(NumberConfigurationEntity):
_attr_translation_key: str = "double_tap_down_level"


@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_height_minimum_floor"
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = -600
_attr_native_max_value: float = 600
_attribute_name = "mmwave_height_minimum_floor"
_attr_translation_key: str = "mmwave_height_minimum_floor"
_attr_fallback_name = "mmWave Height Minimum (Floor)"
_attr_mode: NumberMode = NumberMode.BOX


@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_height_maximum_ceiling"
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = -600
_attr_native_max_value: float = 600
_attribute_name = "mmwave_height_maximum_ceiling"
_attr_translation_key: str = "mmwave_height_maximum_ceiling"
_attr_fallback_name = "mmWave Height Maximum (Ceiling)"
_attr_mode: NumberMode = NumberMode.BOX


@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_width_minimum_left"
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = -600
_attr_native_max_value: float = 600
_attribute_name = "mmwave_width_minimum_left"
_attr_translation_key: str = "mmwave_width_minimum_left"
_attr_fallback_name = "mmWave Width Minimum (Left)"
_attr_mode: NumberMode = NumberMode.BOX


@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_width_maximum_right"
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = -600
_attr_native_max_value: float = 600
_attribute_name = "mmwave_width_maximum_right"
_attr_translation_key: str = "mmwave_width_maximum_right"
_attr_fallback_name = "mmWave Width Maximum (Right)"
_attr_mode: NumberMode = NumberMode.BOX


@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_depth_minimum_near"
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = 0
_attr_native_max_value: float = 600
_attribute_name = "mmwave_depth_minimum_near"
_attr_translation_key: str = "mmwave_depth_minimum_near"
_attr_fallback_name = "mmWave Depth Minimum (Near)"
_attr_mode: NumberMode = NumberMode.BOX


@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_depth_maximum_far"
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = 0
_attr_native_max_value: float = 600
_attribute_name = "mmwave_depth_maximum_far"
_attr_translation_key: str = "mmwave_depth_maximum_far"
_attr_fallback_name = "mmWave Depth Maximum (Far)"
_attr_mode: NumberMode = NumberMode.BOX


@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 = 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(
cluster_handler_names="opple_cluster", models={"aqara.feeder.acn001"}
)
Expand Down
112 changes: 112 additions & 0 deletions zha/application/platforms/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -544,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."""

Expand Down Expand Up @@ -582,6 +603,97 @@ 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 InovelliMmwaveRoomSizePreset(types.enum8):
"""Inovelli mmwave room size preset."""

Custom = 0x00 # User-defined
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(
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."""

Expand Down
2 changes: 2 additions & 0 deletions zha/zigbee/cluster_handlers/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -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] = "inovelli_vzm32sn_cluster"

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
Expand Down
86 changes: 86 additions & 0 deletions zha/zigbee/cluster_handlers/manufacturerspecific.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

from datetime import datetime
import functools
import logging
from typing import TYPE_CHECKING, Any

Expand All @@ -28,10 +29,13 @@
ATTRIBUTE_ID,
ATTRIBUTE_NAME,
ATTRIBUTE_VALUE,
CLUSTER_HANDLER_INOVELLI,
CLUSTER_HANDLER_INOVELLI_MMWAVE,
IKEA_AIR_PURIFIER_CLUSTER,
IKEA_REMOTE_CLUSTER,
IKEA_SHORTCUT_V1_CLUSTER,
INOVELLI_CLUSTER,
INOVELLI_MMWAVE_CLUSTER,
LEGRAND_CABLE_OUTLET_CLUSTER,
OSRAM_BUTTON_CLUSTER,
PHILIPS_CONTACT_CLUSTER,
Expand Down Expand Up @@ -358,6 +362,54 @@ 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": True,
"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,
"mmwave_room_size_preset": True,
}
elif self.cluster.endpoint.model == "VZM35-SN":
self.ZCL_INIT_ATTRS = {
"dimming_speed_up_remote": True,
Expand Down Expand Up @@ -432,6 +484,40 @@ 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)
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_height_minimum_floor": True,
"mmwave_height_maximum_ceiling": True,
"mmwave_width_minimum_left": True,
"mmwave_width_maximum_right": True,
"mmwave_depth_minimum_near": True,
"mmwave_depth_maximum_far": True,
"mmwave_detect_sensitivity": True,
"mmwave_detect_trigger": True,
"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)
Expand Down
Loading