Koenkk / zigbee2mqtt

Zigbee 🐝 to MQTT bridge 🌉, get rid of your proprietary Zigbee bridges 🔨
https://www.zigbee2mqtt.io
GNU General Public License v3.0
11.97k stars 1.67k forks source link

[New device support]: Tuya TS0601 TZE204_dhotiauw #24259

Open thisIsLiving opened 1 week ago

thisIsLiving commented 1 week ago

Link

https://www.aliexpress.com/item/1005007499685162.html

Database entry

{"id":27,"type":"Router","ieeeAddr":"0xa4c138ebbf2af8ac","nwkAddr":6431,"manufId":4417,"manufName":"_TZE204_dhotiauw","powerSource":"Mains (single phase)","modelId":"TS0601","epList":[1,242],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[4,5,61184,0],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65503":"�\u0003�.i�\u0003�.i�\u0003�.i�\u0003�.i�\u0003�.i�\u0003�.i�\u0003�.i�\u0003�.i","65506":56,"65508":0,"65534":0,"stackVersion":0,"dateCode":"","appVersion":74,"modelId":"TS0601","manufacturerName":"_TZE204_dhotiauw","powerSource":1,"zclVersion":3,"hwVersion":1}}},"binds":[],"configuredReportings":[],"meta":{}},"242":{"profId":41440,"epId":242,"devId":97,"inClusterList":[],"outClusterList":[33],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":74,"stackVersion":0,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"meta":{"configured":332242049},"lastSeen":1728363652030}

Zigbee2MQTT version

1.40.1-dev commit: 2ae19a7

Comments

I've created an external definition for TZE204_dhotiauw based off TZE200_rks0sgb7 and it works fine. However I think it more aligns with TZE204_81yrt3lo (PJ-1203A). The problem is TZE204_81yrt3lo has some special configs to it, see #22248. I don't know how to convert it into a .js file to test unfortunately.

Here are the datapointids from Tuya:

19 | Power total 20 | Voltage 101 | Current 102 | Current 103 | Power 104 | Power 105 | Supply frequency 107 | Reverse energy total 110 | Forward energy Total 115 | Forward energy 116 | Reverse energy 117 | Forward energy 118 | Reverse energy 120 | Power factor 121 | Power factor 122 | Report frequency 123 | Voltage calibration 124 | AC frequency calibretion 125 | Current  calibretion 126 | Power  calibretion 127 | Forword energy  calibretion 128 | Reverse energy  calibretion 130 | Current calibration 131 | Power calibration 132 | Forword energy calibretion 133 | Reverse energy calibretion 140 | Chanel set 141 | Current A reverse 142 | Current B reverse 143 | Alarm overcurrent 144 | Alarm overcurrent 145 | Overcurrent alarm 146 | Overcurrent alarm 147 | A电流过流提醒 148 | B电流过流提醒

External definition

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const modernExtend = require('zigbee-herdsman-converters/lib/modernExtend');
const e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const utils = require('zigbee-herdsman-converters/lib/utils');
const logger = require('zigbee-herdsman-converters/lib/logger');

const definition = {
    // Since a lot of Tuya devices use the same modelID, but use different datapoints
    // it's necessary to provide a fingerprint instead of a zigbeeModel
    fingerprint: [
        {
            // The model ID from: Device with modelID 'TS0601' is not supported
            // You may need to add \u0000 at the end of the name in some cases
            modelID: 'TS0601',
            // The manufacturer name from: Device with modelID 'TS0601' is not supported.
            manufacturerName: '_TZE204_dhotiauw',
        },
    ],
    model: 'TS0601_new',
    vendor: 'Tuya',
    description: 'Bidirectional energy meter with 80A current clamp',
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    onEvent: tuya.onEventSetTime, // Add this if you are getting no converter for 'commandMcuSyncTime'
    configure: tuya.configureMagicPacket,
    exposes: [
        e.ac_frequency(),
        e.voltage(),
        tuya.exposes.powerWithPhase('a'),
        tuya.exposes.powerWithPhase('b'),
        tuya.exposes.powerWithPhase('ab'),
        tuya.exposes.currentWithPhase('a'),
        tuya.exposes.currentWithPhase('b'),
        tuya.exposes.powerFactorWithPhase('a'),
        tuya.exposes.powerFactorWithPhase('b'),
        tuya.exposes.energyFlowWithPhase('a', ['sign']),
        tuya.exposes.energyFlowWithPhase('b', ['sign']),
        tuya.exposes.energyWithPhase('a'),
        tuya.exposes.energyWithPhase('b'),
        tuya.exposes.energyProducedWithPhase('a'),
        tuya.exposes.energyProducedWithPhase('b'),
        e
            .numeric('update_frequency', ea.STATE_SET)
            .withUnit('s')
            .withDescription('Update frequency')
            .withValueMin(3)
            .withValueMax(60)
            .withPreset('default', 10, 'Default value'),
        // Timestamp a and b are basically equivalent to last_seen
        // but they indicate when the unsigned value of power_a and power_b
        // were received. They can be several seconds in the past if
        // the publication was delayed because of the late_energy_flow options.
        e.numeric('timestamp_a', ea.STATE).withDescription('Timestamp at power measure (phase a)'),
        e.numeric('timestamp_b', ea.STATE).withDescription('Timestamp at power measure (phase b)'),
    ],
    meta: {
        multiEndpointSkip: ['power_factor', 'power_factor_phase_b', 'power_factor_phase_c', 'energy'],
        tuyaDatapoints: [
            [105, 'ac_frequency', tuya.valueConverter.divideBy100],
            [103, 'power_a', tuya.valueConverter.divideBy10],
            [104, 'power_b', tuya.valueConverter.divideBy10],
            [19, 'power', tuya.valueConverter.divideBy10],
            [20, 'voltage', tuya.valueConverter.divideBy10],
            [101, 'current_a', tuya.valueConverter.divideBy1000],
            [102, 'current_b', tuya.valueConverter.divideBy1000],
            [120, 'power_factor_a', tuya.valueConverter.raw],
            [121, 'power_factor_b', tuya.valueConverter.raw],
            [110, 'energy', tuya.valueConverter.divideBy100],
            [115, 'energy_a', tuya.valueConverter.divideBy100],
            [117, 'energy_b', tuya.valueConverter.divideBy100],
            [116, 'energy_produced_a', tuya.valueConverter.divideBy100],
            [118, 'energy_produced_b', tuya.valueConverter.divideBy100],
            [107, 'energy_produced', tuya.valueConverter.divideBy100],
            [122, 'update_frequency', tuya.valueConverter.raw],
        ],
    },
};

module.exports = definition;

What does/doesn't work with the external definition?

Timestamping plus the issues under #22248

schauveau commented 1 week ago

If you thing that TZE204_dhotiauw is a variant of the PJ-1203A then the easiest way to proceed is probably to use my external converter from https://github.com/schauveau/z2m_PJ-1203A

I did not use those external converters since it was officially included 6 month ago so they may require some minor changes if there was some API changes in Z2M.

I am interested to see if some of the PJ-1203A bugs are solved in that version.

The one that is especially annoying is that energy_flow_a and energy_flow_b (the consuming/producing information) are sent with a delay. I do not see them in your list of datapoints. That could be "Current A|B reverse" 141 and 142.

The other features implemented in my driver are less critical and so could be ignored. (thought, the ability to used a signed power instead of a pair (value, direction) is nice)

Another annoying feature of the PJ-1203A is that all Tuya datapoints are sent one at a time. It would be nice to see if that device is sending them together. That would put less stress on the zigbee traffic.

That would be nice if you could send some debug logs showing all the zigbee messages.

schauveau commented 1 week ago

The datapoint 140 (Channel Set) appears to be new. Could that be a way to select which datapoints are reported by the device thus reducing the Zigbee traffic?

thisIsLiving commented 1 week ago

What's the best way to get the actual payload data that gets sent with each datapointid? The debug log just lines up with the converter so don't know if I've mapped them correctly.

That would be nice if you could send some debug logs showing all the zigbee messages.

I'm not so sure now it's a variant of PJ-1203A. I couldn't map 107 and 110 to an equivalent point on PJ-1203A. Also I don't think 141 and 142 is the sign direction. How do I check what values are being output on those datapoints?

EDIT: I had a look through the Tuya IOT portal and 141 and 142 just showed 0. I didn't get a chance to test it out by reversing the CT polarity while I had it connected to a Tuya Gateway

schauveau commented 1 week ago

I suspect that 'total' means 'AB' so the sum of the two channels with the proper signs applied. If so, dp110 could be something like dp115+dp117.

I did not update Z2M for months but in my version, the interesting logs for the DP look like that:

[2024-10-08 09:57:52] debug:    z2m: Received Zigbee message from 'energy_meter', type 'commandDataReport', cluster 'manuSpecificTuya', data '{"dpValues":[{"data":{"data":[0,0,2,204],"type":"Buffer"},"datatype":2,"dp":101}],"seq":52736}' from endpoint 1 with groupID 0

For Tuya devices, the relevant lines shall contain both commandDataReport and the name of your device (here that is energy_meter). This is a debug log so you need to set Z2M to debug mode in the LOG panel of the web interfacebut be aware that this is producing a LOT of output. Do not forget to restore it to a more sensible level. Also debug logs may not appear in the web interface so you may have to "grep" the relevant lines from the log files.

In that example, the line describes a single data datapoint 101. The data type 2 means a 32bit values the data is composed of 4 bytes 0,0,2,204 that shall be interpreted as 2*256+204 = 716

from 'src/lib/tuya.ts`:

export const dataTypes = {
    raw: 0, // [ bytes ]
    bool: 1, // [0/1]
    number: 2, // [ 4 byte value ]
    string: 3, // [ N byte string ]
    enum: 4, // [ 0-255 ]
    bitmap: 5, // [ 1,2,4 bytes ] as bits
};
schauveau commented 1 week ago

One more trick! in the manuSpecificTuya log, the whole data value is a valid JSON string so you can make it more readable using a command line tool such as jq

For example, using grep, sed and jq on Linux

grep commandDataReport log.txt | grep 'energy_meter' | sed "s/.*data '\(.*\)'.*/\1/" | jq .