Skip to content

Instantly share code, notes, and snippets.

@vinzent
Last active January 29, 2025 13:08
Show Gist options
  • Save vinzent/2194a8ef531d8e8ec3bc17ea17eab0e9 to your computer and use it in GitHub Desktop.
Save vinzent/2194a8ef531d8e8ec3bc17ea17eab0e9 to your computer and use it in GitHub Desktop.
zg-204zs.py
"""Luminance motion sensor ZG-204ZS"""
from typing import Dict, Final
from zigpy.quirks.v2 import QuirkBuilder
import zigpy.types as t
from zigpy.zcl.clusters.measurement import IlluminanceMeasurement, OccupancySensing
from zigpy.zcl.clusters.security import IasZone
from zigpy.zcl.clusters.general import Identify
from zigpy.zcl.foundation import ZCLAttributeDef
from zhaquirks.tuya import NoManufacturerCluster, TuyaLocalCluster, TuyaPowerConfigurationCluster2AAA
from zhaquirks.tuya.mcu import (
DPToAttributeMapping,
TuyaMCUCluster,
)
class TuyaOccupancySensing(OccupancySensing, TuyaLocalCluster):
"""Tuya local OccupancySensing cluster."""
class TuyaIlluminanceMeasurement(IlluminanceMeasurement, TuyaLocalCluster):
"""Tuya local IlluminanceMeasurement cluster."""
class PirState(t.enum8):
"""Pir state"""
pir = 0x00
none = 0x01
@staticmethod
def to_occupancy(x):
""" Map PIR state to occupancy enum. Actually the PirState is Occupancy inverted."""
if x == PirState.pir:
return OccupancySensing.Occupancy.Occupied
return OccupancySensing.Occupancy.Unoccupied
class PirSensitivity(t.enum8):
"""Sensitivity level enum."""
LOW = 0x00
MEDIUM = 0x01
HIGH = 0x02
class PirTime(t.enum8):
"""Keep time"""
_10_SEC = 0x00
_30_SEC = 0x01
_60_SEC = 0x02
_120_SEC = 0x03
class LuminanceMotionManufCluster(NoManufacturerCluster, TuyaMCUCluster):
"""Tuya Manufacturer cluster"""
# 1 "PIR state" pir_state Enum pir|none
# 4 "Battery level" battery_percentage Integer unit=%,min=0,max=100,scale=0,step=1
# 9 "PIR Sensitivity" pir_sensitivity Enum low|middle|high
# 10 "Keep time" pir_time Enum 10s|30s|60s|120s
# 12 "Illuminance value" illuminance_value Integer unit=lux,min=0,max=10000,scale=0,step=1
# 102 "Light Sense Interval Time(Version 2.0.1 and above)" interval_time Integer unit="Min",min=1,max=720,scale=0,step=1
class AttributeDefs(TuyaMCUCluster.AttributeDefs):
"""Manufacturer specific attribute definitions"""
# Mapped to OccupancySensing cluster
# pir_state: Final = ZCLAttributeDef(
# id=0x0001, # DP 9
# type=PirState,
# is_manufacturer_specific=True,
# )
battery_percentage: Final = ZCLAttributeDef(
id=0x0004, # DP 4
type=t.uint16_t,
is_manufacturer_specific=True,
)
pir_sensitivity: Final = ZCLAttributeDef(
id=0x0009, # DP 9
type=PirSensitivity,
is_manufacturer_specific=True,
)
pir_time: Final = ZCLAttributeDef(
id=0x0010, # DP 10
type=PirTime,
is_manufacturer_specific=True,
)
illuminance_value: Final = ZCLAttributeDef(
id=0x0012, # DP 12
type=t.uint16_t,
is_manufacturer_specific=True,
)
interval_time: Final = ZCLAttributeDef(
id=0x0102, # DP 102
type=t.uint16_t,
is_manufacturer_specific=True,
)
dp_to_attribute: Dict[int, DPToAttributeMapping] = {
1: DPToAttributeMapping(
TuyaOccupancySensing.ep_attribute,
"occupancy",
converter=PirState.to_occupancy
),
4: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"battery_percentage",
),
9: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"pir_sensitivity",
converter=PirSensitivity
),
10: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"pir_time",
converter=PirTime
),
12: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"illuminance_value",
),
102: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"interval_time",
),
}
data_point_handlers = {
1: "_dp_2_attr_update",
4: "_dp_2_attr_update",
9: "_dp_2_attr_update",
10: "_dp_2_attr_update",
12: "_dp_2_attr_update",
102: "_dp_2_attr_update",
}
(
QuirkBuilder("_TZE200_3towulqd", "TS0601")
.skip_configuration()
.removes(IasZone.cluster_id)
.removes(Identify.cluster_id)
.adds(LuminanceMotionManufCluster)
.adds(TuyaOccupancySensing)
.replaces(TuyaPowerConfigurationCluster2AAA)
.replaces(TuyaIlluminanceMeasurement)
.enum(
LuminanceMotionManufCluster.AttributeDefs.pir_time.name,
PirTime,
LuminanceMotionManufCluster.cluster_id,
fallback_name="PIR time",
translation_key="pir_time"
)
.enum(
LuminanceMotionManufCluster.AttributeDefs.pir_sensitivity.name,
PirSensitivity,
LuminanceMotionManufCluster.cluster_id,
fallback_name="PIR sensitivity",
translation_key="pir_sensitivity"
)
# Exception with unit="m" seen with HA 2024.6.0 + 2024.5.x
.number(
LuminanceMotionManufCluster.AttributeDefs.interval_time.name,
LuminanceMotionManufCluster.cluster_id,
step=1,
min_value=1,
max_value=720,
fallback_name="Interval time",
translation_key="interval_time"
)
.add_to_registry()
)
@vinzent
Copy link
Author

vinzent commented Aug 13, 2024

@b3nni93 will update it by wednesday.

@b3nni93
Copy link

b3nni93 commented Aug 13, 2024

Very nice.. thanks :)
Maybe it‘s an idea to commit it to the official branch, so they can include it natively into ZHA without the need of a quirk?

@XxmArKxX94
Copy link

XxmArKxX94 commented Nov 17, 2024

today i updated home assitant to version 2024.11.2 and the quirk stopped working with the following error code. (2024.10.4 is working well, all the 2024.11.x versions have this error)

Unexpected exception importing custom quirk 'zg-204zs' Traceback (most recent call last): File "/usr/local/lib/python3.12/site-packages/zhaquirks/__init__.py", line 479, in setup spec.loader.exec_module(module) File "<frozen importlib._bootstrap_external>", line 995, in exec_module File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed File "/config/custom_zha_quirks/zg-204zs.py", line 154, in <module> .enum(LuminanceMotionManufCluster.AttributeDefs.pir_time.name, PirTime, LuminanceMotionManufCluster.cluster_id) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/zigpy/quirks/v2/__init__.py", line 666, in enum ZCLEnumMetadata( File "<attrs generated init zigpy.quirks.v2.ZCLEnumMetadata>", line 16, in __init__ __attr_validator_fallback_name(self, __attr_fallback_name, self.fallback_name) File "/usr/local/lib/python3.12/site-packages/attr/validators.py", line 101, in __call__ raise TypeError( TypeError: ("'fallback_name' must be <class 'str'> (got None that is a <class 'NoneType'>).", Attribute(name='fallback_name', default=NOTHING, validator=<instance_of validator for type <class 'str'>>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type='str', converter=None, kw_only=True, inherited=True, on_setattr=None, alias='fallback_name'), <class 'str'>, None)

@vinzent
Copy link
Author

vinzent commented Nov 19, 2024

added fallback_name and translation_key keys that seem to be required wit >=2024.11.

@jdm09
Copy link

jdm09 commented Nov 20, 2024

@vinzent
really nice and "mostly" working quirk - thanks a lot for your work!

Just realized, that this quirk was dedicated for a different model as i bought.
Is there a chance, to get this quirk working for the round model with identically signature?

threat
quirk

The quirk "ts0601_pirmotion.py" from Link: quirk is working without writing the values for sensitivity and pir time, but reports occupancy and illuminance correctly.
Your custom quirk works perfect, but is not reporting illuminance and battery level any more.

Could you please help me to find the part to change to get this quirk working for the different model also?

@XxmArKxX94
Copy link

added fallback_name and translation_key keys that seem to be required wit >=2024.11.

thanks for the update :)

@jdm09
Copy link

jdm09 commented Nov 24, 2024

Hi,

i managed it to get back working for me. If someone does have the same issue as me, here is the working quirk v2 for the Device with Signature: _TZE200_3towulqd but the round tiny version

PIR round with Lux

Modified Quirk for round PIR
"""
* Luminance motion sensor ZG-204ZS
* TS0601 ZG-204ZS
* https://de.aliexpress.com/item/1005006896353345.html (Color: with Lux)
* Gitlist: https://gist.github.com/vinzent/2194a8ef531d8e8ec3bc17ea17eab0e9
"""

import math
from typing import Dict, Final


from zigpy.quirks.v2 import QuirkBuilder
import zigpy.types as t
from zigpy.zcl.clusters.measurement import IlluminanceMeasurement, OccupancySensing
from zigpy.zcl.clusters.security import IasZone
from zigpy.zcl.clusters.general import Identify

from zigpy.zcl.foundation import ZCLAttributeDef

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

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


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


class PirState(t.enum8):
   """Pir state"""

   pir = 0x00
   none = 0x01


   @staticmethod
   def to_occupancy(x):
       """ Map PIR state to occupancy enum. Actually the PirState is Occupancy inverted."""

       if x == PirState.pir:
           return OccupancySensing.Occupancy.Occupied
       return OccupancySensing.Occupancy.Unoccupied

class PirSensitivity(t.enum8):
   """Sensitivity level enum."""

   LOW = 0x00
   MEDIUM = 0x01
   HIGH = 0x02

class PirTime(t.enum8):
   """Keep time"""

   _10_SEC = 0x00
   _30_SEC = 0x01
   _60_SEC = 0x02
   _120_SEC = 0x03

def illuminance_converter(x):
   """Converter-Methode für die mathematische Berechnung"""
   return 10000 * math.log10(x) + 1 if x != 0 else 0

class LuminanceMotionManufCluster(NoManufacturerCluster, TuyaMCUCluster):
   """Tuya Manufacturer cluster"""

   # 1 "PIR state" pir_state Enum pir|none
   # 4 "Battery level" battery_percentage Integer unit=%,min=0,max=100,scale=0,step=1
   # 9 "PIR Sensitivity" pir_sensitivity Enum low|middle|high
   # 10 "Keep time" pir_time Enum 10s|30s|60s|120s
   # 12 "Illuminance value" illuminance_value Integer unit=lux,min=0,max=10000,scale=0,step=1
   # 102 "Light Sense Interval Time(Version 2.0.1 and above)" interval_time Integer unit="Min",min=1,max=720,scale=0,step=1

   class AttributeDefs(TuyaMCUCluster.AttributeDefs):
       """Manufacturer specific attribute definitions"""

       # Mapped to OccupancySensing cluster
       # pir_state: Final = ZCLAttributeDef(
       #     id=0x0001, # DP 9
       #     type=PirState,
       #     is_manufacturer_specific=True,
       # )
       battery_percentage_remaining: Final = ZCLAttributeDef(
           id=0x0004, # DP 4
           type=t.uint16_t,
           is_manufacturer_specific=True,
       )
       pir_sensitivity: Final = ZCLAttributeDef(
           id=0x0009, # DP 9
           type=PirSensitivity,
           is_manufacturer_specific=True,
       )
       pir_time: Final = ZCLAttributeDef(
           id=0x0010, # DP 10
           type=PirTime,
           is_manufacturer_specific=True,
       )
       measured_value: Final = ZCLAttributeDef(
           id=0x0012, # DP 12
           type=t.uint16_t,
           is_manufacturer_specific=True,
       )
       interval_time: Final = ZCLAttributeDef(
           id=0x0102, # DP 102
           type=t.uint16_t,
           is_manufacturer_specific=True,
       )

   dp_to_attribute: Dict[int, DPToAttributeMapping] = {
       1: DPToAttributeMapping(
           TuyaOccupancySensing.ep_attribute,
           "occupancy",
           converter=PirState.to_occupancy
       ),
       4: DPToAttributeMapping(
           TuyaPowerConfigurationCluster.ep_attribute,
           "battery_percentage_remaining",
       ),
       9: DPToAttributeMapping(
           TuyaMCUCluster.ep_attribute,
           "pir_sensitivity",
           converter=PirSensitivity
       ),
       10: DPToAttributeMapping(
           TuyaMCUCluster.ep_attribute,
           "pir_time",
           converter=PirTime
       ),
       12: DPToAttributeMapping(
           TuyaIlluminanceMeasurement.ep_attribute,
           "measured_value",
           converter=illuminance_converter,
       ),
       102: DPToAttributeMapping(
           TuyaMCUCluster.ep_attribute,
           "interval_time",
       ),
   }

   data_point_handlers = {
       1: "_dp_2_attr_update",
       4: "_dp_2_attr_update",
       9: "_dp_2_attr_update",
       10: "_dp_2_attr_update",
       12: "_dp_2_attr_update",
       102: "_dp_2_attr_update",
   }

(
   QuirkBuilder("_TZE200_3towulqd", "TS0601")
   .skip_configuration()
   .removes(IasZone.cluster_id)
   .removes(Identify.cluster_id)
   .adds(LuminanceMotionManufCluster)
   .adds(TuyaOccupancySensing)
   .replaces(TuyaPowerConfigurationCluster)
   .replaces(TuyaPowerConfigurationCluster2AAA)
   .replaces(TuyaIlluminanceMeasurement)
   .enum(
       LuminanceMotionManufCluster.AttributeDefs.pir_time.name, 
       PirTime, 
       LuminanceMotionManufCluster.cluster_id,
       fallback_name="PIR time",
       translation_key="pir_time"
   )
   .enum(
       LuminanceMotionManufCluster.AttributeDefs.pir_sensitivity.name,
       PirSensitivity,
       LuminanceMotionManufCluster.cluster_id,
       fallback_name="PIR sensitivity",
       translation_key="pir_sensitivity"
   )
   # Exception with unit="m" seen with HA 2024.6.0 + 2024.5.x
   .number(
       LuminanceMotionManufCluster.AttributeDefs.interval_time.name,
       LuminanceMotionManufCluster.cluster_id,
       step=1,
       min_value=1,
       max_value=720,
       fallback_name="Interval time",
       translation_key="interval_time"
   )
   .add_to_registry()
)

Thanks again for your very good quirk v2!!

@wkrzyzanowski
Copy link

@jdm09 can you paste your quirk correctly formatted? I’m trying to make it work with the same motion sensor as yours.

@jdm09
Copy link

jdm09 commented Jan 10, 2025

@jdm09 can you paste your quirk correctly formatted? I’m trying to make it work with the same motion sensor as yours.

@wkrzyzanowski Sure, would really appreciate this!
here is the Version, which is working actually, but this one does not update the illuminance and battery percentage.
I can not find the wrong part.

Thanks for your support!

Modified Quirk for round PIR
#import math
from typing import Dict, Final


from zigpy.quirks.v2 import QuirkBuilder
import zigpy.types as t
from zigpy.zcl.clusters.measurement import IlluminanceMeasurement, OccupancySensing
from zigpy.zcl.clusters.security import IasZone
from zigpy.zcl.clusters.general import Identify

from zigpy.zcl.foundation import ZCLAttributeDef

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

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


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


class PirState(t.enum8):
   """Pir state"""

   pir = 0x00
   none = 0x01


   @staticmethod
   def to_occupancy(x):
       """ Map PIR state to occupancy enum. Actually the PirState is Occupancy inverted."""

       if x == PirState.pir:
           return OccupancySensing.Occupancy.Occupied
       return OccupancySensing.Occupancy.Unoccupied

class PirSensitivity(t.enum8):
   """Sensitivity level enum."""

   LOW = 0x00
   MEDIUM = 0x01
   HIGH = 0x02

class PirTime(t.enum8):
   """Keep time"""

   _10_SEC = 0x00
   _30_SEC = 0x01
   _60_SEC = 0x02
   _120_SEC = 0x03

#def illuminance_converter(x):
#    """Converter-Methode für die mathematische Berechnung"""
#    if x != 0:
#    	return 10000 * math.log10(x) + 1
#    return 0

class LuminanceMotionManufCluster(NoManufacturerCluster, TuyaMCUCluster):
   """Tuya Manufacturer cluster"""

   # 1 "PIR state" pir_state Enum pir|none
   # 4 "Battery level" battery_percentage Integer unit=%,min=0,max=100,scale=0,step=1
   # 9 "PIR Sensitivity" pir_sensitivity Enum low|middle|high
   # 10 "Keep time" pir_time Enum 10s|30s|60s|120s
   # 12 "Illuminance value" illuminance_value Integer unit=lux,min=0,max=10000,scale=0,step=1
   # 102 "Light Sense Interval Time(Version 2.0.1 and above)" interval_time Integer unit="Min",min=1,max=720,scale=0,step=1

   class AttributeDefs(TuyaMCUCluster.AttributeDefs):
       """Manufacturer specific attribute definitions"""

       # Mapped to OccupancySensing cluster
       # pir_state: Final = ZCLAttributeDef(
       #     id=0x0001, # DP 9
       #     type=PirState,
       #     is_manufacturer_specific=True,
       # )
       battery_percentage_remaining: Final = ZCLAttributeDef(
           id=0x0004, # DP 4
           type=t.uint16_t,
           is_manufacturer_specific=True,
       )
       pir_sensitivity: Final = ZCLAttributeDef(
           id=0x0009, # DP 9
           type=PirSensitivity,
           is_manufacturer_specific=True,
       )
       pir_time: Final = ZCLAttributeDef(
           id=0x0010, # DP 10
           type=PirTime,
           is_manufacturer_specific=True,
       )
       illuminance_value: Final = ZCLAttributeDef(
           id=0x0012, # DP 12
           type=t.uint16_t,
           is_manufacturer_specific=True,
       )
       interval_time: Final = ZCLAttributeDef(
           id=0x0102, # DP 102
           type=t.uint16_t,
           is_manufacturer_specific=True,
       )

   dp_to_attribute: Dict[int, DPToAttributeMapping] = {
       1: DPToAttributeMapping(
           TuyaOccupancySensing.ep_attribute,
           "occupancy",
           converter=PirState.to_occupancy
       ),
       4: DPToAttributeMapping(
           TuyaPowerConfigurationCluster.ep_attribute,
           "battery_percentage_remaining"
       ),
       9: DPToAttributeMapping(
           TuyaMCUCluster.ep_attribute,
           "pir_sensitivity",
           converter=PirSensitivity
       ),
       10: DPToAttributeMapping(
           TuyaMCUCluster.ep_attribute,
           "pir_time",
           converter=PirTime
       ),
       12: DPToAttributeMapping(
           TuyaIlluminanceMeasurement.ep_attribute,
           "illuminance_value",
           #converter=illuminance_converter
       ),
       102: DPToAttributeMapping(
           TuyaMCUCluster.ep_attribute,
           "interval_time"
       ),
   }

   data_point_handlers = {
       1: "_dp_2_attr_update",
       4: "_dp_2_attr_update",
       9: "_dp_2_attr_update",
       10: "_dp_2_attr_update",
       12: "_dp_2_attr_update",
       102: "_dp_2_attr_update",
   }

(
   QuirkBuilder("_TZE200_3towulqd", "TS0601")
   .skip_configuration()
   .removes(IasZone.cluster_id)
   .removes(Identify.cluster_id)
   .adds(LuminanceMotionManufCluster)
   .adds(TuyaOccupancySensing)
   .replaces(TuyaPowerConfigurationCluster)
   .replaces(TuyaPowerConfigurationCluster2AAA)
   .replaces(TuyaIlluminanceMeasurement)
   .enum(
       LuminanceMotionManufCluster.AttributeDefs.pir_time.name, 
       PirTime, 
       LuminanceMotionManufCluster.cluster_id,
       fallback_name="PIR time",
       translation_key="pir_time"
   )
   .enum(
       LuminanceMotionManufCluster.AttributeDefs.pir_sensitivity.name,
       PirSensitivity,
       LuminanceMotionManufCluster.cluster_id,
       fallback_name="PIR sensitivity",
       translation_key="pir_sensitivity"
   )
   # Exception with unit="m" seen with HA 2024.6.0 + 2024.5.x
   .number(
       LuminanceMotionManufCluster.AttributeDefs.interval_time.name,
       LuminanceMotionManufCluster.cluster_id,
       step=1,
       min_value=1,
       max_value=720,
       fallback_name="Interval time",
       translation_key="interval_time"
   )
   .add_to_registry()
)

@wkrzyzanowski
Copy link

wkrzyzanowski commented Jan 10, 2025

Thank you! Will take a look 👌

I’m trying to make this to work:

Tuya Zigbee Human Motion Sensor
https://a.aliexpress.com/_EGQq8gk

This is motion + illuminace sensor and I need to make configurable detection intervals.

@jdm09
Copy link

jdm09 commented Jan 11, 2025

@wkrzyzanowski
What i recently noticed is a small difference between two sorts of this ZB PIR you mentioned above:

1. this Signature seems to work with this quirk
{
 "node_descriptor": {
   "logical_type": 2,
   "complex_descriptor_available": 0,
   "user_descriptor_available": 0,
   "reserved": 0,
   "aps_flags": 0,
   "frequency_band": 8,
   "mac_capability_flags": 128,
   "manufacturer_code": 4417,
   "maximum_buffer_size": 66,
   "maximum_incoming_transfer_size": 66,
   "server_mask": 10752,
   "maximum_outgoing_transfer_size": 66,
   "descriptor_capability_field": 0
 },
 "endpoints": {
   "1": {
     "profile_id": "0x0104",
     "device_type": "0x0402",
     "input_clusters": [
       "0x0000",
       "0x0001",
       "0x0400",
       "0x0406",
       "0xe000",
       "0xe002",
       "0xee00",
       "0xef00"
     ],
     "output_clusters": []
   }
 },
 "manufacturer": "_TZE200_3towulqd",
 "model": "TS0601",
 "class": "zigpy.quirks.v2.CustomDeviceV2"
}
2. this slightly different Signature is also from the same Model but is not working:
{
 "node_descriptor": {
   "logical_type": 2,
   "complex_descriptor_available": 0,
   "user_descriptor_available": 0,
   "reserved": 0,
   "aps_flags": 0,
   "frequency_band": 8,
   "mac_capability_flags": 128,
   "manufacturer_code": 4417,
   "maximum_buffer_size": 66,
   "maximum_incoming_transfer_size": 66,
   "server_mask": 10752,
   "maximum_outgoing_transfer_size": 66,
   "descriptor_capability_field": 0
 },
 "endpoints": {
   "1": {
     "profile_id": "0x0104",
     "device_type": "0x0402",
     "input_clusters": [
       "0x0000",
       "0x0001",
       "0x0400",
       "0x0406",
       "0xef00"
     ],
     "output_clusters": [
       "0x000a",
       "0x0019"
     ]
   }
 },
 "manufacturer": "_TZE200_3towulqd",
 "model": "TS0601",
 "class": "zigpy.quirks.v2.CustomDeviceV2"
}

Actually i have both of these PIRs in my ZHA Network.

Seems that there are slightly different Models with the same Manufacturer and Model Signature.
But sorry, i have no idea on how to identify and treat these differences with the quirk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment