From 701404fa0b765eb009fc5f7260f7201927477f01 Mon Sep 17 00:00:00 2001 From: Peter Hall Date: Wed, 24 Jan 2024 00:31:32 +1100 Subject: [PATCH] Add ZHA entities for snzb06p (#107379) * Updating zha component to add entities for snzb06p Sonoff snzb06p presence detector needs some custom entities. * Updating ZCL_INIT_ATTRS for sonoff specific attrs * updating cluster name due to change in quirk --- .../zha/core/cluster_handlers/helpers.py | 7 ++++++ .../cluster_handlers/manufacturerspecific.py | 8 +++++++ .../zha/core/cluster_handlers/measurement.py | 6 ++++- homeassistant/components/zha/number.py | 19 ++++++++++++++++ homeassistant/components/zha/select.py | 21 ++++++++++++++++++ homeassistant/components/zha/sensor.py | 22 +++++++++++++++++++ homeassistant/components/zha/strings.json | 9 ++++++++ 7 files changed, 91 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zha/core/cluster_handlers/helpers.py b/homeassistant/components/zha/core/cluster_handlers/helpers.py index 17bc5763977..f4444f3995c 100644 --- a/homeassistant/components/zha/core/cluster_handlers/helpers.py +++ b/homeassistant/components/zha/core/cluster_handlers/helpers.py @@ -13,3 +13,10 @@ def is_hue_motion_sensor(cluster_handler: ClusterHandler) -> bool: "SML003", "SML004", ) + + +def is_sonoff_presence_sensor(cluster_handler: ClusterHandler) -> bool: + """Return true if the manufacturer and model match known Sonoff sensor models.""" + return cluster_handler.cluster.endpoint.manufacturer in ( + "SONOFF", + ) and cluster_handler.cluster.endpoint.model in ("SNZB-06P",) diff --git a/homeassistant/components/zha/core/cluster_handlers/manufacturerspecific.py b/homeassistant/components/zha/core/cluster_handlers/manufacturerspecific.py index 57f1e2ee304..2acf6b7e5e4 100644 --- a/homeassistant/components/zha/core/cluster_handlers/manufacturerspecific.py +++ b/homeassistant/components/zha/core/cluster_handlers/manufacturerspecific.py @@ -423,3 +423,11 @@ class IkeaRemote(ClusterHandler): ) class XiaomiVibrationAQ1ClusterHandler(MultistateInput): """Xiaomi DoorLock Cluster is in fact a MultiStateInput Cluster.""" + + +@registries.CLUSTER_HANDLER_ONLY_CLUSTERS.register(0xFC11) +@registries.ZIGBEE_CLUSTER_HANDLER_REGISTRY.register(0xFC11) +class SonoffPresenceSenor(ClusterHandler): + """SonoffPresenceSensor cluster handler.""" + + ZCL_INIT_ATTRS = {"last_illumination_state": True} diff --git a/homeassistant/components/zha/core/cluster_handlers/measurement.py b/homeassistant/components/zha/core/cluster_handlers/measurement.py index bd483920842..5249c196864 100644 --- a/homeassistant/components/zha/core/cluster_handlers/measurement.py +++ b/homeassistant/components/zha/core/cluster_handlers/measurement.py @@ -14,7 +14,7 @@ from ..const import ( REPORT_CONFIG_MIN_INT, ) from . import AttrReportConfig, ClusterHandler -from .helpers import is_hue_motion_sensor +from .helpers import is_hue_motion_sensor, is_sonoff_presence_sensor if TYPE_CHECKING: from ..endpoint import Endpoint @@ -69,6 +69,10 @@ class OccupancySensing(ClusterHandler): if is_hue_motion_sensor(self): self.ZCL_INIT_ATTRS = self.ZCL_INIT_ATTRS.copy() self.ZCL_INIT_ATTRS["sensitivity"] = True + if is_sonoff_presence_sensor(self): + self.ZCL_INIT_ATTRS = self.ZCL_INIT_ATTRS.copy() + self.ZCL_INIT_ATTRS["ultrasonic_o_to_u_delay"] = True + self.ZCL_INIT_ATTRS["ultrasonic_u_to_o_threshold"] = True @registries.ZIGBEE_CLUSTER_HANDLER_REGISTRY.register( diff --git a/homeassistant/components/zha/number.py b/homeassistant/components/zha/number.py index 24964d7a154..c3c4c0b604a 100644 --- a/homeassistant/components/zha/number.py +++ b/homeassistant/components/zha/number.py @@ -20,6 +20,7 @@ from .core.const import ( CLUSTER_HANDLER_COLOR, CLUSTER_HANDLER_INOVELLI, CLUSTER_HANDLER_LEVEL, + CLUSTER_HANDLER_OCCUPANCY, CLUSTER_HANDLER_THERMOSTAT, SIGNAL_ADD_ENTITIES, SIGNAL_ATTR_UPDATED, @@ -966,3 +967,21 @@ class ThermostatLocalTempCalibration(ZHANumberConfigurationEntity): _attr_mode: NumberMode = NumberMode.SLIDER _attr_native_unit_of_measurement: str = UnitOfTemperature.CELSIUS _attr_icon: str = ICONS[0] + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_OCCUPANCY, models={"SNZB-06P"} +) +# pylint: disable-next=hass-invalid-inheritance # needs fixing +class SonoffPresenceSenorTimeout(ZHANumberConfigurationEntity): + """Configuration of Sonoff sensor presence detection timeout.""" + + _unique_id_suffix = "presence_detection_timeout" + _attr_entity_category = EntityCategory.CONFIG + _attr_native_min_value: int = 15 + _attr_native_max_value: int = 60 + _attribute_name = "ultrasonic_o_to_u_delay" + _attr_translation_key: str = "presence_detection_timeout" + + _attr_mode: NumberMode = NumberMode.BOX + _attr_icon: str = "mdi:timer-edit" diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index 1c13779209d..5c32ca44dee 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -25,6 +25,7 @@ from .core.const import ( CLUSTER_HANDLER_HUE_OCCUPANCY, CLUSTER_HANDLER_IAS_WD, CLUSTER_HANDLER_INOVELLI, + CLUSTER_HANDLER_OCCUPANCY, CLUSTER_HANDLER_ON_OFF, SIGNAL_ADD_ENTITIES, SIGNAL_ATTR_UPDATED, @@ -652,3 +653,23 @@ class AqaraThermostatPreset(ZCLEnumSelectEntity): _attribute_name = "preset" _enum = AqaraThermostatPresetMode _attr_translation_key: str = "preset" + + +class SonoffPresenceDetectionSensitivityEnum(types.enum8): + """Enum for detection sensitivity select entity.""" + + Low = 0x01 + Medium = 0x02 + High = 0x03 + + +@CONFIG_DIAGNOSTIC_MATCH( + cluster_handler_names=CLUSTER_HANDLER_OCCUPANCY, models={"SNZB-06P"} +) +class SonoffPresenceDetectionSensitivity(ZCLEnumSelectEntity): + """Entity to set the detection sensitivity of the Sonoff SNZB-06P.""" + + _unique_id_suffix = "detection_sensitivity" + _attribute_name = "ultrasonic_u_to_o_threshold" + _enum = SonoffPresenceDetectionSensitivityEnum + _attr_translation_key: str = "detection_sensitivity" diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index 9de4bcf75f5..b4531dc3f68 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -1214,3 +1214,25 @@ class AqaraSmokeDensityDbm(Sensor): _attr_state_class: SensorStateClass = SensorStateClass.MEASUREMENT _attr_icon: str = "mdi:google-circles-communities" _attr_suggested_display_precision: int = 3 + + +class SonoffIlluminationStates(types.enum8): + """Enum for displaying last Illumination state.""" + + Dark = 0x00 + Light = 0x01 + + +@MULTI_MATCH(cluster_handler_names="sonoff_manufacturer", models={"SNZB-06P"}) +# pylint: disable-next=hass-invalid-inheritance # needs fixing +class SonoffPresenceSenorIlluminationStatus(Sensor): + """Sensor that displays the illumination status the last time peresence was detected.""" + + _attribute_name = "last_illumination_state" + _unique_id_suffix = "last_illumination" + _attr_translation_key: str = "last_illumination_state" + _attr_icon: str = "mdi:theme-light-dark" + + def formatter(self, value: int) -> int | float | None: + """Numeric pass-through formatter.""" + return SonoffIlluminationStates(value).name diff --git a/homeassistant/components/zha/strings.json b/homeassistant/components/zha/strings.json index a4a53b3c1b4..a47e83fcf4b 100644 --- a/homeassistant/components/zha/strings.json +++ b/homeassistant/components/zha/strings.json @@ -727,6 +727,9 @@ }, "local_temperature_calibration": { "name": "Local temperature offset" + }, + "presence_detection_timeout": { + "name": "Presence detection timeout" } }, "select": { @@ -792,6 +795,9 @@ }, "decoupled_mode": { "name": "Decoupled mode" + }, + "detection_sensitivity": { + "name": "Detection Sensitivity" } }, "sensor": { @@ -869,6 +875,9 @@ }, "smoke_density": { "name": "Smoke density" + }, + "last_illumination_state": { + "name": "Last illumination state" } }, "switch": {