So, you find yourself here because you purchased a Tuya device, it may have been sold under another brand name, but if the manufacturer name looks like _TZE284_7ytb3h8u
, it’s still Tuya. You proceeded to pair your new device to ZHA only to find that it has no entities, the entities it has aren’t functioning, or it’s missing entities that it should have. Congratulations, you have found yourself in need of a quirk.
Before we continue, this guide only covers building quirks using TuyaQuirkBuilder based on the new V2 quirks. V1 quirks are not covered here and are in the process of being depreciated. To help you identify a V2 vs a V1 quirk examples are listed below, both for the same device.
V1 Quirk Example
quirk_id = TUYA_PLUG_MANUFACTURER
signature = {
MODELS_INFO: [
("_TZE200_laqjm8qd", "TS0601"),
],
ENDPOINTS: {
# <SimpleDescriptor endpoint=1 profile=260 device_type=81
# input_clusters=[0x0000,0x0004,0x0005,0x0006,0xEF00]
# output_clusters=[0x000A,0x0019]>
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaOnOffManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
# <SimpleDescriptor endpoint=242 profile=41440 device_type=97
# input_clusters=[]
# output_clusters=[33]
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaOnOffManufCluster,
TuyaOnOffNM,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
TuyaOnOffNM,
],
OUTPUT_CLUSTERS: [],
},
3: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
TuyaOnOffNM,
],
OUTPUT_CLUSTERS: [],
},
4: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
TuyaOnOffNM,
],
OUTPUT_CLUSTERS: [],
},
5: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
TuyaOnOffNM,
],
OUTPUT_CLUSTERS: [],
},
242: {
PROFILE_ID: zgp.PROFILE_ID,
DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
}
}
V2 (TuyaQuirkBuilder) Quirk Example
from zigpy.quirks.v2 import EntityType
import zigpy.types as t
from zhaquirks.tuya.builder import TuyaQuirkBuilder
(
TuyaQuirkBuilder("_TZE200_laqjm8qd", "TS0601")
.tuya_switch(
dp_id=1,
attribute_name="on_off_1",
entity_type=EntityType.STANDARD,
translation_key="on_off_1",
fallback_name="Switch 1",
)
.tuya_switch(
dp_id=2,
attribute_name="on_off_2",
entity_type=EntityType.STANDARD,
translation_key="on_off_2",
fallback_name="Switch 2",
)
.tuya_switch(
dp_id=3,
attribute_name="on_off_3",
entity_type=EntityType.STANDARD,
translation_key="on_off_3",
fallback_name="Switch 3",
)
.tuya_switch(
dp_id=4,
attribute_name="on_off_4",
entity_type=EntityType.STANDARD,
translation_key="on_off_4",
fallback_name="Switch 4",
)
.tuya_switch(
dp_id=5,
attribute_name="on_off_5",
entity_type=EntityType.STANDARD,
translation_key="on_off_5",
fallback_name="Switch 5",
)
.skip_configuration()
.add_to_registry()
)
Tuya Warning:
Before proceeding to the next steps, you should know that Tuya devices are some of the worst Zigbee devices you can purchase, as of today, almost all of the device support request in zigpy/zha-device-handlers are for Tuya devices. If you have purchased one or are planning to, you should know the following.
- Tuya devices do not follow the Zigbee Cluster Library (ZCL), so any new device has to be supported manually with a quirk. This isn’t a defect in ZHA, it’s just that your specific device hasn’t had a quirk added to HA yet. If Tuya followed the ZCL, we wouldn’t need a quirk.
- Tuya Devices are widely known to be junk, you should expect any of the following.
– Early device failure.
– Failing to report values.
– Reporting incorrect values.
– Devices acting as routers but failing to route traffic.
– Devices bought at the same time and reporting the same model and manufacturer having differing functionality.
– Devices flooding the Zigbee network with traffic causing network failure.
– Devices destructively failing and risking fire or electrical hazard.
In summary, don’t buy Tuya, now, that said, I do have a few Tuya devices on my network, and there are many others successfully using them. So, it can be done, and there are reasons to do it, but know the risks before you do.
Prerequisites:
- Ensure you have custom quirks enabled, see ZHA Documentation.
- Know how to enable ZHA debug logging.
- This is an iterative process, be prepared for that.
Adding a Tuya device:
As outlined above, Tuya doesn’t use the ZCL, so going to zigpy/zha-device-handlers and requesting support with the device signature is unlikely to result in a quirk. Tuya transmits everything as a Tuya Datapoint (DP) to a Tuya cluster (0xEF00
) and these won’t be listed in the device signature. So, the first step in supporting a new device is to identify the Tuya Datapoints and the datatype for each one.
Finding Tuya Datapoints:
There are three main methods for finding the Tuya DPs, listed in order of difficulty.- Find the Tuya DPs from the Tuya developer console. See the z2m guide.
- Find an existing Z2M converter.
- Find the DPs from the diagnostic output of Local Tuya.
You should end up with a list of DPs such as this example from a real device and pulled from Tuya developer console.
Example Tuya DPs
"1":"Switch"
"2":"Start"
"101":"Last irrigation time."
"102":"Next irrigation time."
"103":"Real-time irrigation method (frequency)"
"104":"Duration"
"105":"Real-time Irrigation Interval (Seconds)"
"106":"Current temperature (Celsius)"
"107":"Smart Weather"
"108":"Current battery level (%)"
"109":"Circular irrigation parameters."
"110":"Real-time cumulative duration (seconds)"
"111":"Real-time accumulated water volume (liters)"
"112":"Other extensions."
"113":"Timing function."
"114":"统计功能"
"115":"时区"
Now that we have a list of DPs, the next step is to check and see if a similar quirk already exists and just needs the signature updated. Taking the list of DPs you gathered earlier, check the source code for ZHA device handlers for a similar quirk. In the example above we would look in tuya/ts0601_valve.py and see that there is a matching quirk already, we just need to add an additional applies_to
to cover this device.
Building a Quirk:
This should be an iterative process, it's best to add a DP, test the quirk, then add the next DP. Adding them all at once is likely to make troubleshooting much harder.Assuming that we were unable to find a similar quirk, we would then need to create one. At this point we need to identify the DPs we care about and then identify the correct datatypes. Continuing the irrigation controller example above, we would pick DPs 1,2,101,102,104,105,107,108,111,114.
Now that we have the DPs, we need identify the datatype for each one. What entity should represent it and if the device reports it normally or if we need to do a conversion.
First, let’s build out a barebones V2 quirk.
from zigpy.quirks.v2 import EntityType
import zigpy.types as t
from zhaquirks.tuya.builder import TuyaQuirkBuilder
(
TuyaQuirkBuilder("_TZE200_laqjm8qd", "TS0601")
.skip_configuration()
.add_to_registry()
)
Now let’s add a battery sensor for DP 108
.tuya_battery(dp_id=108, power_cfg=TuyaPowerConfigurationCluster4AA)
DP 1 is a switch that controls the valve mode, we could use a regular switch via .tuya_switch
but let’s set this up as an enum. Here we add an enum, then we add the dp to attribute converter using tuya_dp_attribute
then finally add the enum entity with the appropriate attribute_name
and enum_class
.
class IrrigationMode(t.enum8):
"""Irrigation Mode Enum."""
Duration = 0x00
Capacity = 0x01
.tuya_dp_attribute(
dp_id=1,
attribute_name="irrigation_mode",
type=t.Bool,
)
.enum(
attribute_name="irrigation_mode",
cluster_id=TUYA_CLUSTER_ID,
enum_class=IrrigationMode,
translation_key="irrigation_mode",
fallback_name="Irrigation mode",
)
Next, let’s grab the valve or DP 2, since there is only one on off switch, we can simply just add.
.tuya_onoff(dp_id=2)
If we had more than one switch, we would need to add a tuya_switch
for each one.
This valve has metering, so that’s simply.
.tuya_metering(dp_id=111)
Note, this works since the default metering class is water metering, if we were metering something else, we would need to provide our own metering_cfg
. See TuyaValveWaterConsumed for the default metering class.
DP 103 is a used to set the number of irrigation cycles, a number entity is appropriate. Min and max values may not be shown in the Tuya dev console, so we can just pick sane ones.
.tuya_number(
dp_id=103,
attribute_name="irrigation_cycles",
type=t.uint8_t,
min_value=0,
max_value=100,
step=1,
translation_key="irrigation_cycles",
fallback_name="Irrigation cycles",
)
DP 114 represent the duration of the last irrigation in seconds, in typical Tuya fashion, this isn’t reported as integer and instead is reported as a string, so we need to convert it.
def tuya_string_to_td(v: str) -> int:
"""Convert String Duration to seconds."""
dt = datetime.strptime(v, "%H:%M:%S,%f")
return timedelta(hours=dt.hour, minutes=dt.minute, seconds=dt.second).seconds
Then with the conversion function, the sensor becomes. If this was a simpler conversion, a lambda would be acceptable.
.tuya_sensor(
dp_id=114,
attribute_name="irrigation_duration",
type=t.uint32_t,
converter=lambda x: tuya_string_to_td(x),
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DURATION,
unit=UnitOfTime.SECONDS,
translation_key="irrigation_duration",
fallback_name="Last irrigation duration",
)
The rest of the Datapoints are similar to the above and are omitted.
For further TuyaQuirkBuilder documentation see the draft documentation.
Next Steps
If you have a completed quirk that works, please submit a PR to zigpy/zha-device-handlers. There are way too many custom quirks floating around that were never submitted, help your fellow users out and submit yours.
If you need help or have issues, create an issue in the quirk repo or post here, remember, we can’t help you if you don’t have the Datapoints. We don’t have the device and can’t get the DPs, nor can we test, so you need to be willing to do the work to find them, create a quirk and test changes.
Some Tuya devices are harder to work with, some devices require sniffing traffic via Wireshark and reverse engineering to function. Not super common, but it’s possible that your device may not be supportable.
Good Luck!