Quirk zha pour détecteur millimetrique zigbee mmwave _TZE204_dapwryy7

Je suis tombé sur un quirk (zha quirk v2) qui avait l'air de fonctionner pour quelques personnes pour un module mmwave.
J'ai donné le code source à Claude (AI) je lui ai demander d'adapter le code avec la configuration de zigbee2mqtt qui lui prenait déjà en compte ce capteur. Claude m'a renvoyé le résultat qui fonctionnait out of the box pour la moitié des capteurs/configs ! J'ai rajouté le reste.
Donc merci à
- Claude AI https://claude.ai/new
- Vinzent https://gist.github.com/vinzent/2cd645b848fd3b6a0c3e5762956ec89f
- Et au projet Koenkk/zigbee-herdsman-converters https://github.com/Koenkk/zigbee-herdsman-converters/blob/9ede78a950d703dac4e60271e08dabf7808661e6/src/devices/tuya.ts#L10736

Je vous le partage ici (lire ici pour ajouter un quirk qui est un script python pour supporter un capteur https://www.home-assistant.io/integrations/zha#how-to-add-support-for-new-and-unsupported-devices) :
# rivsc https://blog.rivsc.ovh
import logging
from typing import Final

from zigpy.quirks.v2 import QuirkBuilder, BinarySensorDeviceClass

import zigpy.types as t
from zigpy.zcl.foundation import ZCLAttributeDef
from zigpy.zcl.clusters.measurement import (
    IlluminanceMeasurement,
    OccupancySensing,
)
from zigpy.zcl.clusters.security import IasZone
from zigpy.quirks.v2.homeassistant import EntityPlatform, EntityType

from zhaquirks.tuya import (
    TuyaLocalCluster,
    TuyaPowerConfigurationCluster2AAA,
)
from zhaquirks.tuya.mcu import TuyaMCUCluster, DPToAttributeMapping

class PresenceState(t.enum8):
    """Presence State values"""
    none = 0x00
    presence = 0x01
    peaceful = 0x02
    small_movement = 0x03
    large_movement = 0x04

class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster):
    """Tuya local OccupancySensing cluster."""

class TuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster):
    """Tuya local IlluminanceMeasurement cluster."""

class HumanPresenceSensorManufCluster(TuyaMCUCluster):
    """Human Presence Sensor ZG-205Z (5.8GHz)"""

    class AttributeDefs(TuyaMCUCluster.AttributeDefs):
        """Tuya DataPoints attributes"""

        # Presence state
        presence_state: Final = ZCLAttributeDef(
            id=0x0001, # DP 1
            type=t.uint16_t,
            access="rp",
            is_manufacturer_specific=True,
        )
        # Target distance
        target_distance: Final = ZCLAttributeDef(
            id=0x0101, # DP 101
            type=t.uint16_t,
            access="rp",
            is_manufacturer_specific=True,
        )
        # Illuminance value
        illuminance_lux: Final = ZCLAttributeDef(
            id=0x0102, # DP 102
            type=t.uint16_t,
            access="rp",
            is_manufacturer_specific=True,
        )
        # None delay time (presence keep time)
        none_delay_time: Final = ZCLAttributeDef(
            id=0x0103, # DP 103
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Indicator
        indicator: Final = ZCLAttributeDef(
            id=0x0104, # DP 104
            type=t.Bool,
            is_manufacturer_specific=True,
        )
        # Move detection max distance
        move_detection_max: Final = ZCLAttributeDef(
            id=0x0107, # DP 107
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Move detection min distance
        move_detection_min: Final = ZCLAttributeDef(
            id=0x0108, # DP 108
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Breath detection max distance
        breath_detection_max: Final = ZCLAttributeDef(
            id=0x0109, # DP 109
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Breath detection min distance
        breath_detection_min: Final = ZCLAttributeDef(
            id=0x0110, # DP 110
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Small move detection max distance
        small_move_detection_max: Final = ZCLAttributeDef(
            id=0x0114, # DP 114
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Small move detection min distance
        small_move_detection_min: Final = ZCLAttributeDef(
            id=0x0115, # DP 115
            type=t.uint16_t,
            is_manufacturer_specific=True,
        )
        # Move sensitivity
        move_sensitivity: Final = ZCLAttributeDef(
            id=0x0116, # DP 116
            type=t.uint8_t,
            is_manufacturer_specific=True,
        )
        # Small move sensitivity
        small_move_sensitivity: Final = ZCLAttributeDef(
            id=0x0117, # DP 117
            type=t.uint8_t,
            is_manufacturer_specific=True,
        )
        # Breath sensitivity
        breath_sensitivity: Final = ZCLAttributeDef(
            id=0x0118, # DP 118
            type=t.uint8_t,
            is_manufacturer_specific=True,
        )

    dp_to_attribute: dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "presence_state",
            converter=PresenceState
        ),
        101: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "target_distance",
            converter=lambda x: x / 100 if x is not None else 0
        ),
        102: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "illuminance_lux",
        ),
        103: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "none_delay_time",
            converter=lambda x: x if x is not None else 30
        ),
        104: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "indicator",
        ),
        107: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "move_detection_max",
            converter=lambda x: x / 100 if x is not None else 10
        ),
        108: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "move_detection_min",
            converter=lambda x: x / 100 if x is not None else 0
        ),
        109: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "breath_detection_max",
            converter=lambda x: x / 100 if x is not None else 6
        ),
        110: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "breath_detection_min",
            converter=lambda x: x / 100 if x is not None else 0
        ),
        114: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "small_move_detection_max",
            converter=lambda x: x / 100 if x is not None else 6
        ),
        115: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "small_move_detection_min",
            converter=lambda x: x / 100 if x is not None else 0
        ),
        116: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "move_sensitivity",
            converter=lambda x: x if x is not None else 5
        ),
        117: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "small_move_sensitivity",
            converter=lambda x: x if x is not None else 5
        ),
        118: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "breath_sensitivity",
            converter=lambda x: x if x is not None else 5
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        101: "_dp_2_attr_update",
        102: "_dp_2_attr_update",
        103: "_dp_2_attr_update",
        104: "_dp_2_attr_update",
        107: "_dp_2_attr_update",
        108: "_dp_2_attr_update",
        109: "_dp_2_attr_update",
        110: "_dp_2_attr_update",
        114: "_dp_2_attr_update",
        115: "_dp_2_attr_update",
        116: "_dp_2_attr_update",
        117: "_dp_2_attr_update",
        118: "_dp_2_attr_update",
    }

(
    QuirkBuilder("_TZE204_dapwryy7", "TS0601")
    .skip_configuration()
    .removes(IasZone.cluster_id)
    .adds(HumanPresenceSensorManufCluster)
    #.adds(TuyaOccupancySensing)
    .replaces(TuyaPowerConfigurationCluster2AAA)
    .replaces(TuyaIlluminanceMeasurement)
    .binary_sensor(
        "presence_state",
        HumanPresenceSensorManufCluster.cluster_id,
        endpoint_id=1,
        #entity_type=EntityType.STANDARD, # very soon (zigpy channel #dev on github
        device_class=BinarySensorDeviceClass.OCCUPANCY,
        fallback_name="Presence"
    )
    .enum(
        HumanPresenceSensorManufCluster.AttributeDefs.presence_state.name,
        PresenceState,
        HumanPresenceSensorManufCluster.cluster_id,
        entity_platform=EntityPlatform.SENSOR,
        entity_type=EntityType.STANDARD,
        fallback_name="Presence State",
        translation_key="presence_state"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.target_distance.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=10,
        #unit="m", # fail :/
        fallback_name="Target Distance (m)",
        translation_key="target_distance",
        #entity_type=EntityType.STANDARD # not yet :/
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.none_delay_time.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=1,
        min_value=0,
        max_value=28800,
        unit="s",
        fallback_name="Hold Delay Time",
        translation_key="none_delay_time"
    )
    .switch(
        HumanPresenceSensorManufCluster.AttributeDefs.indicator.name,
        HumanPresenceSensorManufCluster.cluster_id,
        fallback_name="LED Indicator",
        translation_key="indicator"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.move_detection_max.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=10,
        #unit="m",
        fallback_name="Move Detection Max Distance (m)",
        translation_key="move_detection_max"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.move_detection_min.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=10,
        #unit="m",
        fallback_name="Move Detection Min Distance (m)",
        translation_key="move_detection_min"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.small_move_detection_max.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=6,
        #unit="m",
        fallback_name="Small Move Detection Max Distance (m)",
        translation_key="small_move_detection_max"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.small_move_detection_min.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=6,
        #unit="m",
        fallback_name="Small Move Detection Min Distance (m)",
        translation_key="small_move_detection_min"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.breath_detection_max.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=6,
        #unit="m",
        fallback_name="Breath Detection Max Distance (m)",
        translation_key="breath_detection_max"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.breath_detection_min.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=0.01,
        min_value=0,
        max_value=6,
        #unit="m",
        fallback_name="Breath Detection Min Distance (m)",
        translation_key="breath_detection_min"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.move_sensitivity.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=1,
        min_value=0,
        max_value=10,
        fallback_name="Move Sensitivity",
        translation_key="move_sensitivity"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.small_move_sensitivity.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=1,
        min_value=0,
        max_value=10,
        fallback_name="Small Move Sensitivity",
        translation_key="small_move_sensitivity"
    )
    .number(
        HumanPresenceSensorManufCluster.AttributeDefs.breath_sensitivity.name,
        HumanPresenceSensorManufCluster.cluster_id,
        step=1,
        min_value=0,
        max_value=10,
        fallback_name="Breath Sensitivity", 
        translation_key="breath_sensitivity"
    )
    .add_to_registry()
)