Koenkk / zigbee2mqtt

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

TS0601 Производитель Zigbee _TZE204_fhvdgeuhonst Умный автоматический выключатель Tuya #23485

Open ia1gerebec opened 1 month ago

ia1gerebec commented 1 month ago

Link

https://aliexpress.ru/item/1005005984673866.html?spm=a2g2w.orderdetail.0.0.42154aa6JzNYVB&sku_id=12000038280422974

Database entry

{"id":12,"type":"Router","ieeeAddr":"0xa4c1382762a38dd1","nwkAddr":58462,"manufId":4417,"manufName":"_TZE204_fhvdgeuh","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":"&�?.i��?.i��?.i��?.i��?.i��?.i��?.i","65506":56,"65508":0,"modelId":"TS0601","manufacturerName":"_TZE204_fhvdgeuh","powerSource":1,"zclVersion":3,"appVersion":74,"stackVersion":0,"hwVersion":1,"dateCode":""}}},"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":{},"lastSeen":1722618429404}

Comments

Устройство -Умный автоматический выключатель Tuya https://aliexpress.ru/item/1005005984673866.html?spm=a2g2w.orderdetail.0.0.42154aa6JzNYVB&sku_id=12000038280422974

External definition

const definition = {
    zigbeeModel: ['TS0601'],
    model: 'TS0601',
    vendor: '_TZE204_fhvdgeuh',
    description: 'Automatically generated definition',
    extend: [],
    meta: {},
};

module.exports = definition;
SBozhyi commented 1 month ago

I'm waiting for support for this device too. There is Custom quirk for this device available for ZHA but I can't convert it to Z2M (lack of skills). But may be it will help for someone skilled _TZE204_fhvdgeuh

peekayn84 commented 1 month ago

Datapoint from tuya cloud, i am waiting too: {"1":"Total forward energy","6":"Phase A","9":"Fault","11":"Switch prepayment","12":"Clear energy","13":"Balance energy (kWh)","14":"charge energy","15":"Leakage current","16":"Switch","17":"Alarm set1","18":"Alarm set2","101":"合闸恢复时间(弃用)","102":"reclosing allowed times","103":"Temp Current","104":"reclosing enable","105":"Timer","106":"Cycle Schedule","107":"reclose recover seconds","108":"Random Timing","109":"Switch Inching","119":"Power-on delay power-on time","124":"Overcurrent Event Threshold Time","125":"Time Threshold of Lost Flow Event","126":"alarm set3","127":"status","134":"relay status for power on"}

francisPiragua commented 1 month ago

Another one waiting too. Thanks.

peekayn84 commented 1 month ago

I create this one, but how parse this data i don't know, can someone help, please: Alarm set1 {"0":4,"1":1,"2":0,"3":50,"4":5,"5":1,"6":0,"7":80} Alarm set2 {"0":1,"1":1,"2":2,"3":118,"4":3,"5":1,"6":0,"7":252,"8":4,"9":1,"10":0,"11":210}

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 tuya = require('zigbee-herdsman-converters/lib/tuya');
const e = exposes.presets;
const ea = exposes.access;

const tz_settings = {
    key: ['reclose_recover_seconds','clear_energy'],
    convertSet: async (entity, key, value, meta) => {
        const settingType = {'trip': 2, 'alarm': 1, 'closed': 0};
        // const typeswitch = {'ON':true, 'OFF':false}
        let dp_key = -1; // value if not found
        // identify dp.
        const tuyadatapoints_list = meta.mapped.meta.tuyaDatapoints
        for (let i = 0; i < tuyadatapoints_list.length; i++) {
            if (tuyadatapoints_list[i][1] === key) {
                dp_key = tuyadatapoints_list[i][0];
                break;
            }
        }
        switch (key) {
        case 'reclose_recover_seconds': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'clear_energy': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        default: // Unknown key
            meta.logger.warn(`Unhandled key ${key}`);
        }
    },
}

const customTrueFalseConverter = {
    key: ['state'],
    convertSet: (entity, key, value, meta) => {
      // Your conversion logic
      const presence = value === 1 ? true : false;
      return {state: {presence}};
    },
    convertGet: (entity, key, meta) => {
      // Convert the state value from Home Assistant to the appropriate Zigbee value
      const zigbeeValue = meta.state.presence ? 1 : 0;
      return {state: zigbeeValue};
    },
  };

const definition = {
    fingerprint: [
        {
            modelID: 'TS0601',
            manufacturerName: '_TZE204_fhvdgeuh',
        },
    ],
    model: 'TZE204_fhvdgeuh',
    vendor: 'Tuya',
    description: 'Tuya multi-function energy monitor',
    fromZigbee: [
        tuya.fz.datapoints,
        // Дополнительные преобразователи, если необходимо
    ],
    toZigbee: [tuya.tz.datapoints,tz_settings],
    onEvent: tuya.onEvent({respondToMcuVersionResponse:false, queryIntervalSeconds: 3 * 60}),
    configure: tuya.configureMagicPacket,
    exposes: [
        tuya.exposes.switch(),
        e.energy(),
        e.power(),
        e.voltage(),
        e.current(),
        e.temperature(),
        e.numeric('leakage_current', ea.STATE).withUnit('mA').withDescription('Leakage current'),
        e.text('fault', ea.STATE).withDescription('Fault indication'),
        e.numeric('balance_energy', ea.STATE).withUnit('kWh').withDescription('Balance energy (kWh)'),
        e.numeric('charge_energy', ea.STATE).withDescription('Charge energy'),
        e.binary('clear_energy', ea.STATE_SET, 'ON', 'OFF').withDescription('Clear energy data'),
        e.binary('switch_prepayment', ea.STATE_SET, 'ON', 'OFF').withDescription('Switch prepayment'),
        e.numeric('reclosing_allowed_times', ea.STATE).withDescription('Reclosing allowed times'),
        e.binary('reclosing_enable', ea.STATE_SET, 'ON', 'OFF').withDescription('Reclosing enable'),
        e.numeric('timer', ea.STATE).withDescription('Timer'),
        e.numeric('cycle_schedule', ea.STATE).withDescription('Cycle Schedule'),
        e.numeric('reclose_recover_seconds', ea.STATE_SET).withDescription('Reclose recover seconds').withValueMin(10).withValueMax(99).withValueStep(1),
        e.numeric('random_timing', ea.STATE).withDescription('Random Timing'),
        e.numeric('switch_inching', ea.STATE).withDescription('Switch Inching'),
        e.numeric('power_on_delay', ea.STATE).withDescription('Power-on delay power-on time'),
        e.numeric('overcurrent_event_threshold_time', ea.STATE).withDescription('Overcurrent Event Threshold Time'),
        e.numeric('time_threshold_lost_flow', ea.STATE).withDescription('Time Threshold of Lost Flow Event'),
        e.numeric('alarm_set1', ea.STATE).withDescription('Alarm set1'),
        e.numeric('alarm_set2', ea.STATE).withDescription('Alarm set2'),
        e.numeric('alarm_set3', ea.STATE).withDescription('Alarm set3'),
        //e.numeric('status', ea.STATE).withDescription('Device status'),
        e.binary('status', ea.STATE, 'ON', 'OFF').withDescription('Device status'),
        e.numeric('relay_status_power_on', ea.STATE).withDescription('Relay status for power on'),
        //e.binary('relay_status_power_on', ea.STATE, 'ON', 'OFF').withDescription('Relay status for power on'),
    ],
    meta: {
        tuyaDatapoints: [
            [1, 'total_forward_energy', tuya.valueConverter.divideBy100],
            [6, null, tuya.valueConverter.phaseVariant2],
            [9, 'fault', tuya.valueConverter.raw],
            [11, 'switch_prepayment', tuya.valueConverter.onOff],
            [12, 'clear_energy', tuya.valueConverter.onOff],
            [13, 'balance_energy', tuya.valueConverter.divideBy100],
            [14, 'charge_energy', tuya.valueConverter.divideBy100],
            [15, 'leakage_current', tuya.valueConverter.raw],
            [16, 'state', tuya.valueConverter.onOff],
            [17, 'alarm_set1', tuya.valueConverter.raw],
            [18, 'alarm_set2', tuya.valueConverter.raw],
            [101, 'restore_time', tuya.valueConverter.raw], // Deprecated
            [102, 'reclosing_allowed_times', tuya.valueConverter.raw],
            [103, 'temperature', tuya.valueConverter.raw],
            [104, 'reclosing_enable', tuya.valueConverter.onOff],
            [105, 'timer', tuya.valueConverter.raw],
            [106, 'cycle_schedule', tuya.valueConverter.raw],
            [107, 'reclose_recover_seconds', tuya.valueConverter.raw],
            [108, 'random_timing', tuya.valueConverter.raw],
            [109, 'switch_inching', tuya.valueConverter.raw],
            [119, 'power_on_delay', tuya.valueConverter.raw],
            [124, 'overcurrent_event_threshold_time', tuya.valueConverter.raw],
            [125, 'time_threshold_lost_flow', tuya.valueConverter.raw],
            [126, 'alarm_set3', tuya.valueConverter.raw],
            //[127, 'status', tuya.valueConverter.raw],
            [127, 'status', tuya.valueConverter.onOff],
            [134, 'relay_status_power_on', tuya.valueConverter.raw],
            //[134, 'relay_status_power_on', tuya.valueConverter.onOff],
        ],
    },
};

module.exports = definition;
peekayn84 commented 1 month ago

I find already supported _TZ3000_cayepv1a, and if i right understand it's same, but how connect it to this device i don't khow

peekayn84 commented 1 month ago

@Koenkk , hi can i ask you to add _TZE204_fhvdgeuh to already realized _TZ3000_cayepv1a or i can do it by myself?

Koenkk commented 1 month ago

If you stop z2m, in the data/database.db change _TZ3000_cayepv1a to _TZE204_fhvdgeuh and start z2m, does it work?

peekayn84 commented 1 month ago

_TZE204_fhvdgeuh Unfortunately no, but I almost got a normal converter


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 tuya = require('zigbee-herdsman-converters/lib/tuya');
const legacy = require('zigbee-herdsman-converters/lib/legacy');
const e = exposes.presets;
const ea = exposes.access;
const fzLocal = {
    value: {
        cluster: 'manuSpecificTuya',
        type: ['commandDataResponse', 'commandDataReport'],
        convert: (model, msg, publish, options, meta) => {
                payload = {};
                for (const dpValue of msg.data.dpValues) {
                        const dp = dpValue.dp;
                        switch (dp) {
                            case 18:
                                {
                                    payload['under_voltage'] = legacy.getDataValue(dpValue)[11];
                                    payload['over_voltage'] = legacy.getDataValue(dpValue)[7];
                                    return payload;
                                };
                        };
                };
        },
    },
};

const tz_settings = { key: ['reclose_recover_seconds','clear_energy','under_voltage','over_voltage'], convertSet: async (entity, key, value, meta) => { const settingType = {'trip': 2, 'alarm': 1, 'closed': 0}; // const typeswitch = {'ON':true, 'OFF':false} let dp_key = -1; // value if not found // identify dp. const tuyadatapoints_list = meta.mapped.meta.tuyaDatapoints for (let i = 0; i < tuyadatapoints_list.length; i++) { if (tuyadatapoints_list[i][1] === key) { dp_key = tuyadatapoints_list[i][0]; break; } } switch (key) { case 'reclose_recover_seconds': { await tuya.sendDataPointValue(entity, dp_key, value); break; } case 'clear_energy': { await tuya.sendDataPointValue(entity, dp_key, value); break; } case 'over_voltage': { //Data not updated const value_send = meta.state['alarm2']; value_send['7']=value; await tuya.sendDataPointValue(entity, 18, value_send); break; } case 'under_voltage': { //Data not updated const value_send = meta.state['alarm2']; value_send['11']=value; await tuya.sendDataPointValue(entity, 18, value_send); break; } default: // Unknown key meta.logger.warn(Unhandled key ${key}); } }, }

const customTrueFalseConverter = { key: ['state'], convertSet: (entity, key, value, meta) => { // Your conversion logic const presence = value === 1 ? true : false; return {state: {presence}}; }, convertGet: (entity, key, meta) => { // Convert the state value from Home Assistant to the appropriate Zigbee value const zigbeeValue = meta.state.presence ? 1 : 0; return {state: zigbeeValue}; }, };

const definition = { fingerprint: [ { modelID: 'TS0601', manufacturerName: '_TZE204_fhvdgeuh', }, ], model: 'TZE204_fhvdgeuh', vendor: 'Tuya', description: 'Tuya multi-function energy monitor', fromZigbee: [ tuya.fz.datapoints, fzLocal.value, // Дополнительные преобразователи, если необходимо ], toZigbee: [tuya.tz.datapoints,tz_settings], onEvent: tuya.onEvent({respondToMcuVersionResponse:false, queryIntervalSeconds: 3 * 60}), configure: tuya.configureMagicPacket, exposes: [ tuya.exposes.switch(), e.energy(), e.power(), e.voltage(), e.current(), e.temperature(), e.numeric('leakage_current', ea.STATE).withUnit('mA').withDescription('Leakage current'), e.text('fault', ea.STATE).withDescription('Fault indication'), e.numeric('balance_energy', ea.STATE).withUnit('kWh').withDescription('Balance energy (kWh)'), e.numeric('charge_energy', ea.STATE).withDescription('Charge energy'), e.binary('clear_energy', ea.STATE_SET, 'ON', 'OFF').withDescription('Clear energy data'), e.binary('switch_prepayment', ea.STATE_SET, 'ON', 'OFF').withDescription('Switch prepayment'), e.numeric('reclosing_allowed_times', ea.STATE).withDescription('Reclosing allowed times'), e.binary('reclosing_enable', ea.STATE_SET, 'ON', 'OFF').withDescription('Reclosing enable'), e.numeric('timer', ea.STATE).withDescription('Timer'), e.numeric('cycle_schedule', ea.STATE).withDescription('Cycle Schedule'), e.numeric('reclose_recover_seconds', ea.STATE_SET).withDescription('Reclose recover seconds').withValueMin(10).withValueMax(99).withValueStep(1), e.numeric('random_timing', ea.STATE).withDescription('Random Timing'), e.numeric('switch_inching', ea.STATE).withDescription('Switch Inching'), e.numeric('power_on_delay', ea.STATE).withDescription('Power-on delay power-on time'), e.numeric('overcurrent_event_threshold_time', ea.STATE).withDescription('Overcurrent Event Threshold Time'), e.numeric('time_threshold_lost_flow', ea.STATE).withDescription('Time Threshold of Lost Flow Event'), e.numeric('alarm_set1', ea.STATE).withDescription('Alarm set1'), e.numeric('alarm2', ea.STATE).withDescription('Alarm set2'), e.numeric('alarm_set3', ea.STATE).withDescription('Alarm set3'), e.numeric('status', ea.STATE).withDescription('Device status'), //e.binary('status', ea.STATE, 'ON', 'OFF').withDescription('Device status'), e.numeric('relay_status_power_on', ea.STATE).withDescription('Relay status for power on'), //e.binary('relay_status_power_on', ea.STATE, 'ON', 'OFF').withDescription('Relay status for power on'), exposes.numeric('over_voltage', ea.STATE_ALL).withDescription('Low Voltage Cut Off').withValueMin(120).withValueMax(300).withValueStep(1), exposes.numeric('under_voltage', ea.STATE_ALL).withDescription('Low Voltage Cut Off').withValueMin(80).withValueMax(210).withValueStep(1),

],
meta: {
    tuyaDatapoints: [
        [1, 'total_forward_energy', tuya.valueConverter.divideBy100],
        [6, null, tuya.valueConverter.phaseVariant2],
        [9, 'fault', tuya.valueConverter.raw],
        [11, 'switch_prepayment', tuya.valueConverter.onOff],
        [12, 'clear_energy', tuya.valueConverter.onOff],
        [13, 'balance_energy', tuya.valueConverter.divideBy100],
        [14, 'charge_energy', tuya.valueConverter.divideBy100],
        [15, 'leakage_current', tuya.valueConverter.raw],
        [16, 'state', tuya.valueConverter.onOff],
        [17, 'alarm_set1', tuya.valueConverter.raw],
        [18, 'alarm2', tuya.valueConverter.raw],
        [101, 'restore_time', tuya.valueConverter.raw], // Deprecated
        [102, 'reclosing_allowed_times', tuya.valueConverter.raw],
        [103, 'temperature', tuya.valueConverter.raw],
        [104, 'reclosing_enable', tuya.valueConverter.onOff],
        [105, 'timer', tuya.valueConverter.raw],
        [106, 'cycle_schedule', tuya.valueConverter.raw],
        [107, 'reclose_recover_seconds', tuya.valueConverter.raw],
        [108, 'random_timing', tuya.valueConverter.raw],
        [109, 'switch_inching', tuya.valueConverter.raw],
        [119, 'power_on_delay', tuya.valueConverter.raw],
        [124, 'overcurrent_event_threshold_time', tuya.valueConverter.raw],
        [125, 'time_threshold_lost_flow', tuya.valueConverter.raw],
        [126, 'alarm_set3', tuya.valueConverter.raw],
        [127, 'status', tuya.valueConverter.raw],
        //[127, 'status', customTrueFalseConverter],
        [134, 'relay_status_power_on', tuya.valueConverter.raw],
        //[134, 'relay_status_power_on', tuya.valueConverter.onOff],
    ],
},

};

module.exports = definition;

peekayn84 commented 1 month ago

I created something like this, not all function add, but almost needed Edit me if needed @Koenkk

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 tuya = require('zigbee-herdsman-converters/lib/tuya');
const legacy = require('zigbee-herdsman-converters/lib/legacy');
const e = exposes.presets;
const ea = exposes.access;

    const fzLocal = {
        value: {
            cluster: 'manuSpecificTuya',
            type: ['commandDataResponse', 'commandDataReport'],
            convert: (model, msg, publish, options, meta) => {
                    payload = {};
                    for (const dpValue of msg.data.dpValues) {
                            const dp = dpValue.dp;
                            switch (dp) {
                                case 17:
                                    {
                                        payload['over_leakage_current'] = legacy.getDataValue(dpValue)[3];
                                        payload['over_temperature'] = legacy.getDataValue(dpValue)[7];
                                        return payload;
                                    };
                                case 18:
                                    {
                                        payload['under_voltage'] = legacy.getDataValue(dpValue)[11];
                                        payload['over_voltage'] = legacy.getDataValue(dpValue)[7];
                                        overcurrent=legacy.getDataValue(dpValue)[2]*256+legacy.getDataValue(dpValue)[3];
                                        payload['over_current']=overcurrent/10.0;
                                        return payload;
                                    };
                            };
                    };
            },
        },
    };

const tz_settings = {
    key: ['reclose_recover_seconds','clear_energy','under_voltage','over_voltage','reclosing_allowed_times','over_leakage_current','over_temperature','over_current'],
    convertSet: async (entity, key, value, meta) => {
        const settingType = {'trip': 2, 'alarm': 1, 'closed': 0};
        // const typeswitch = {'ON':true, 'OFF':false}
        let dp_key = -1; // value if not found
        // identify dp.
        const tuyadatapoints_list = meta.mapped.meta.tuyaDatapoints
        for (let i = 0; i < tuyadatapoints_list.length; i++) {
            if (tuyadatapoints_list[i][1] === key) {
                dp_key = tuyadatapoints_list[i][0];
                break;
            }
        }
        switch (key) {
        case 'reclose_recover_seconds': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'reclosing_allowed_times': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'clear_energy': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'over_leakage_current': {
            const value_send = meta.state['alarm1'];
            value_send['3']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7']];
            await tuya.sendDataPointRaw(entity, 17, payload);
            break;
        }
        case 'over_temperature': {
            const value_send = meta.state['alarm1'];
            value_send['7']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7']];
            await tuya.sendDataPointRaw(entity, 17, payload);
            break;
        }
        case 'over_current': {
            const value_send = meta.state['alarm2'];
            value_send['2']=value*10/256;
            value_send['3']=value*10%256;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        case 'over_voltage': {
            const value_send = meta.state['alarm2'];
            value_send['7']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        case 'under_voltage': {
            const value_send = meta.state['alarm2'];
            value_send['11']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        default: // Unknown key
            meta.logger.warn(`Unhandled key ${key}`);
        }
    },
}

const customTrueFalseConverter = {
    key: ['state'],
    convertSet: (entity, key, value, meta) => {
      // Your conversion logic
      const presence = value === 1 ? true : false;
      return {state: {presence}};
    },
    convertGet: (entity, key, meta) => {
      // Convert the state value from Home Assistant to the appropriate Zigbee value
      const zigbeeValue = meta.state.presence ? 1 : 0;
      return {state: zigbeeValue};
    },
  };

const definition = {
    fingerprint: [
        {
            modelID: 'TS0601',
            manufacturerName: '_TZE204_fhvdgeuh',
        },
    ],
    model: 'TZE204_fhvdgeuh',
    vendor: 'Tuya',
    description: 'Tuya multi-function energy monitor',
    fromZigbee: [
        tuya.fz.datapoints,
        fzLocal.value,
        // Дополнительные преобразователи, если необходимо
    ],
    toZigbee: [tuya.tz.datapoints,tz_settings],
    onEvent: tuya.onEvent({respondToMcuVersionResponse:false, queryIntervalSeconds: 3 * 60}),
    configure: tuya.configureMagicPacket,
    exposes: [
        tuya.exposes.switch(),
        e.energy(),
        e.power(),
        e.voltage(),
        e.current(),
        e.temperature(),
        e.numeric('leakage_current', ea.STATE).withUnit('mA').withDescription('Leakage current'),
        e.text('fault', ea.STATE).withDescription('Fault indication'),
        e.numeric('balance_energy', ea.STATE).withUnit('kWh').withDescription('Balance energy (kWh)'),
        exposes.numeric('over_leakage_current', ea.STATE_ALL).withDescription('Over Leakage Current Cut Off').withValueMin(0).withValueMax(99).withValueStep(1),
        exposes.numeric('over_temperature', ea.STATE_ALL).withDescription('Over Temperature Cut Off').withValueMin(0).withValueMax(100).withValueStep(1),
        exposes.numeric('over_voltage', ea.STATE_ALL).withDescription('Over Voltage Cut Off').withValueMin(120).withValueMax(300).withValueStep(1),
        exposes.numeric('under_voltage', ea.STATE_ALL).withDescription('Under Voltage Cut Off').withValueMin(80).withValueMax(210).withValueStep(1),
        e.numeric('reclose_recover_seconds', ea.STATE_SET).withDescription('Reclose recover seconds').withValueMin(10).withValueMax(99).withValueStep(1),
        exposes.numeric('over_current', ea.STATE_ALL).withDescription('Over Current Cut Off').withValueMin(1).withValueMax(63).withValueStep(0.1),
        e.numeric('reclosing_allowed_times', ea.STATE_ALL).withDescription('Time Restore After Over Current').withValueMin(0).withValueMax(30).withValueStep(1),
        e.numeric('charge_energy', ea.STATE).withDescription('Charge energy'),
        e.binary('clear_energy', ea.STATE_SET, 'ON', 'OFF').withDescription('Clear energy data'),
        e.binary('switch_prepayment', ea.STATE_SET, 'ON', 'OFF').withDescription('Switch prepayment'),
        //e.numeric('reclosing_allowed_times', ea.STATE).withDescription('Reclosing allowed times'),
        e.binary('reclosing_enable', ea.STATE_SET, 'ON', 'OFF').withDescription('Reclosing enable'),
        e.numeric('timer', ea.STATE).withDescription('Timer'),
        e.numeric('cycle_schedule', ea.STATE).withDescription('Cycle Schedule'),
        e.numeric('random_timing', ea.STATE).withDescription('Random Timing'),
        e.numeric('switch_inching', ea.STATE).withDescription('Switch Inching'),
        e.numeric('power_on_delay', ea.STATE).withDescription('Power-on delay power-on time'),
        e.numeric('overcurrent_event_threshold_time', ea.STATE).withDescription('Overcurrent Event Threshold Time'),
        e.numeric('time_threshold_lost_flow', ea.STATE).withDescription('Time Threshold of Lost Flow Event'),
        e.numeric('alarm1', ea.STATE).withDescription('Alarm set1'),
        e.numeric('alarm2', ea.STATE).withDescription('Alarm set2'),
        e.numeric('alarm_set3', ea.STATE).withDescription('Alarm set3'),
        e.numeric('status', ea.STATE).withDescription('Device status'),
        //e.binary('status', ea.STATE, 'ON', 'OFF').withDescription('Device status'),
        e.numeric('relay_status_power_on', ea.STATE).withDescription('Relay status for power on'),
        //e.binary('relay_status_power_on', ea.STATE, 'ON', 'OFF').withDescription('Relay status for power on'),

    ],
    meta: {
        tuyaDatapoints: [
            [1, 'total_forward_energy', tuya.valueConverter.divideBy100],
            [6, null, tuya.valueConverter.phaseVariant2],
            [9, 'fault', tuya.valueConverter.raw],
            [11, 'switch_prepayment', tuya.valueConverter.onOff],
            [12, 'clear_energy', tuya.valueConverter.onOff],
            [13, 'balance_energy', tuya.valueConverter.divideBy100],
            [14, 'charge_energy', tuya.valueConverter.divideBy100],
            [15, 'leakage_current', tuya.valueConverter.raw],
            [16, 'state', tuya.valueConverter.onOff],
            [17, 'alarm1', tuya.valueConverter.raw],
            [18, 'alarm2', tuya.valueConverter.raw],
            [101, 'restore_time', tuya.valueConverter.raw], // Deprecated
            [102, 'reclosing_allowed_times', tuya.valueConverter.raw],
            [103, 'temperature', tuya.valueConverter.raw],
            [104, 'reclosing_enable', tuya.valueConverter.onOff],
            [105, 'timer', tuya.valueConverter.raw],
            [106, 'cycle_schedule', tuya.valueConverter.raw],
            [107, 'reclose_recover_seconds', tuya.valueConverter.raw],
            [108, 'random_timing', tuya.valueConverter.raw],
            [109, 'switch_inching', tuya.valueConverter.raw],
            [119, 'power_on_delay', tuya.valueConverter.raw],
            [124, 'overcurrent_event_threshold_time', tuya.valueConverter.raw],
            [125, 'time_threshold_lost_flow', tuya.valueConverter.raw],
            [126, 'alarm_set3', tuya.valueConverter.raw],
            [127, 'status', tuya.valueConverter.raw],
            //[127, 'status', customTrueFalseConverter],
            [134, 'relay_status_power_on', tuya.valueConverter.raw],
            //[134, 'relay_status_power_on', tuya.valueConverter.onOff],
        ],
    },
};

module.exports = definition;
peekayn84 commented 1 month ago

Bug Fix

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 tuya = require('zigbee-herdsman-converters/lib/tuya');
const legacy = require('zigbee-herdsman-converters/lib/legacy');
const e = exposes.presets;
const ea = exposes.access;

    const fzLocal = {
        value: {
            cluster: 'manuSpecificTuya',
            type: ['commandDataResponse', 'commandDataReport'],
            convert: (model, msg, publish, options, meta) => {
                    payload = {};
                    for (const dpValue of msg.data.dpValues) {
                            const dp = dpValue.dp;
                            switch (dp) {
                                case 17:
                                    {
                                        payload['over_leakage_current'] = legacy.getDataValue(dpValue)[3];
                                        payload['over_temperature'] = legacy.getDataValue(dpValue)[7];
                                        return payload;
                                    };
                                case 18:
                                    {
                                        payload['under_voltage'] = legacy.getDataValue(dpValue)[11];
                                        overvoltage=legacy.getDataValue(dpValue)[6]*256+legacy.getDataValue(dpValue)[7]
                                        payload['over_voltage'] = overvoltage;
                                        overcurrent=legacy.getDataValue(dpValue)[2]*256+legacy.getDataValue(dpValue)[3];
                                        payload['over_current']=overcurrent/10.0;
                                        return payload;
                                    };
                            };
                    };
            },
        },
    };

const tz_settings = {
    key: ['reclose_recover_seconds','clear_energy','under_voltage','over_voltage','reclosing_allowed_times','over_leakage_current','over_temperature','over_current'],
    convertSet: async (entity, key, value, meta) => {
        const settingType = {'trip': 2, 'alarm': 1, 'closed': 0};
        // const typeswitch = {'ON':true, 'OFF':false}
        let dp_key = -1; // value if not found
        // identify dp.
        const tuyadatapoints_list = meta.mapped.meta.tuyaDatapoints
        for (let i = 0; i < tuyadatapoints_list.length; i++) {
            if (tuyadatapoints_list[i][1] === key) {
                dp_key = tuyadatapoints_list[i][0];
                break;
            }
        }
        switch (key) {
        case 'reclose_recover_seconds': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'reclosing_allowed_times': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'clear_energy': {
            await tuya.sendDataPointBool(entity, dp_key, value === 'ON');
            break;
        }
        case 'over_leakage_current': {
            const value_send = meta.state['alarm1'];
            value_send['3']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7']];
            await tuya.sendDataPointRaw(entity, 17, payload);
            break;
        }
        case 'over_temperature': {
            const value_send = meta.state['alarm1'];
            value_send['7']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7']];
            await tuya.sendDataPointRaw(entity, 17, payload);
            break;
        }
        case 'over_current': {
            const value_send = meta.state['alarm2'];
            value_send['2']=value*10/256;
            value_send['3']=value*10%256;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        case 'over_voltage': {
            const value_send = meta.state['alarm2'];
            value_send['6']=value*10/256;
            value_send['7']=value*10%256;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        case 'under_voltage': {
            const value_send = meta.state['alarm2'];
            value_send['11']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        default: // Unknown key
            meta.logger.warn(`Unhandled key ${key}`);
        }
    },
}

const customTrueFalseConverter = {
    key: ['state'],
    convertSet: (entity, key, value, meta) => {
      // Your conversion logic
      const presence = value === 1 ? true : false;
      return {state: {presence}};
    },
    convertGet: (entity, key, meta) => {
      // Convert the state value from Home Assistant to the appropriate Zigbee value
      const zigbeeValue = meta.state.presence ? 1 : 0;
      return {state: zigbeeValue};
    },
  };

const definition = {
    fingerprint: [
        {
            modelID: 'TS0601',
            manufacturerName: '_TZE204_fhvdgeuh',
        },
    ],
    model: 'TZE204_fhvdgeuh',
    vendor: 'Tuya',
    description: 'Tuya multi-function energy monitor',
    fromZigbee: [
        tuya.fz.datapoints,
        fzLocal.value,
        // Дополнительные преобразователи, если необходимо
    ],
    toZigbee: [tuya.tz.datapoints,tz_settings],
    onEvent: tuya.onEvent({respondToMcuVersionResponse:false, queryIntervalSeconds: 3 * 60}),
    configure: tuya.configureMagicPacket,
    exposes: [
        tuya.exposes.switch(),
        e.energy(),
        e.power(),
        e.voltage(),
        e.current(),
        e.temperature(),
        e.numeric('leakage_current', ea.STATE).withUnit('mA').withDescription('Leakage current'),
        e.text('fault', ea.STATE).withDescription('Fault indication'),
        e.numeric('balance_energy', ea.STATE).withUnit('kWh').withDescription('Balance energy (kWh)'),
        e.numeric('over_leakage_current', ea.STATE_SET).withDescription('Over Leakage Current Cut Off').withValueMin(0).withValueMax(99).withValueStep(1).withUnit('mA'),
        e.numeric('over_temperature', ea.STATE_SET).withDescription('Over Temperature Cut Off').withValueMin(0).withValueMax(100).withValueStep(1).withUnit('°C'),
        e.numeric('over_voltage', ea.STATE_SET).withDescription('Over Voltage Cut Off').withValueMin(120).withValueMax(300).withValueStep(1).withUnit('V'),
        e.numeric('under_voltage', ea.STATE_SET).withDescription('Under Voltage Cut Off').withValueMin(80).withValueMax(210).withValueStep(1).withUnit('V'),
        e.numeric('reclose_recover_seconds', ea.STATE_SET).withDescription('Reclose recover seconds').withValueMin(10).withValueMax(99).withValueStep(1),
        e.numeric('over_current', ea.STATE_SET).withDescription('Over Current Cut Off').withValueMin(1).withValueMax(63).withValueStep(0.1).withUnit('A'),
        e.numeric('reclosing_allowed_times', ea.STATE_SET).withDescription('Time Restore After Over Current').withValueMin(0).withValueMax(30).withValueStep(1),
        e.numeric('charge_energy', ea.STATE).withDescription('Charge energy'),
        e.binary('clear_energy', ea.STATE_SET, 'ON', 'OFF').withDescription('Clear energy data'),
        e.binary('switch_prepayment', ea.STATE_SET, 'ON', 'OFF').withDescription('Switch prepayment'),
        //e.numeric('reclosing_allowed_times', ea.STATE).withDescription('Reclosing allowed times'),
        e.binary('reclosing_enable', ea.STATE_SET, 'ON', 'OFF').withDescription('Reclosing enable'),
        e.numeric('timer', ea.STATE).withDescription('Timer'),
        e.numeric('cycle_schedule', ea.STATE).withDescription('Cycle Schedule'),
        e.numeric('random_timing', ea.STATE).withDescription('Random Timing'),
        e.numeric('switch_inching', ea.STATE).withDescription('Switch Inching'),
        e.numeric('power_on_delay', ea.STATE).withDescription('Power-on delay power-on time'),
        e.numeric('overcurrent_event_threshold_time', ea.STATE).withDescription('Overcurrent Event Threshold Time'),
        e.numeric('time_threshold_lost_flow', ea.STATE).withDescription('Time Threshold of Lost Flow Event'),
        e.numeric('alarm1', ea.STATE).withDescription('Alarm set1'),
        e.numeric('alarm2', ea.STATE).withDescription('Alarm set2'),
        e.numeric('alarm_set3', ea.STATE).withDescription('Alarm set3'),
        e.numeric('status', ea.STATE).withDescription('Device status'),
        //e.binary('status', ea.STATE, 'ON', 'OFF').withDescription('Device status'),
        e.numeric('relay_status_power_on', ea.STATE).withDescription('Relay status for power on'),
        //e.binary('relay_status_power_on', ea.STATE, 'ON', 'OFF').withDescription('Relay status for power on'),

    ],
    meta: {
        tuyaDatapoints: [
            [1, 'total_forward_energy', tuya.valueConverter.divideBy100],
            [6, null, tuya.valueConverter.phaseVariant2],
            [9, 'fault', tuya.valueConverter.raw],
            [11, 'switch_prepayment', tuya.valueConverter.onOff],
            [12, 'clear_energy', tuya.valueConverter.onOff],
            [13, 'balance_energy', tuya.valueConverter.divideBy100],
            [14, 'charge_energy', tuya.valueConverter.divideBy100],
            [15, 'leakage_current', tuya.valueConverter.raw],
            [16, 'state', tuya.valueConverter.onOff],
            [17, 'alarm1', tuya.valueConverter.raw],
            [18, 'alarm2', tuya.valueConverter.raw],
            [101, 'restore_time', tuya.valueConverter.raw], // Deprecated
            [102, 'reclosing_allowed_times', tuya.valueConverter.raw],
            [103, 'temperature', tuya.valueConverter.raw],
            [104, 'reclosing_enable', tuya.valueConverter.onOff],
            [105, 'timer', tuya.valueConverter.raw],
            [106, 'cycle_schedule', tuya.valueConverter.raw],
            [107, 'reclose_recover_seconds', tuya.valueConverter.raw],
            [108, 'random_timing', tuya.valueConverter.raw],
            [109, 'switch_inching', tuya.valueConverter.raw],
            [119, 'power_on_delay', tuya.valueConverter.raw],
            [124, 'overcurrent_event_threshold_time', tuya.valueConverter.raw],
            [125, 'time_threshold_lost_flow', tuya.valueConverter.raw],
            [126, 'alarm_set3', tuya.valueConverter.raw],
            [127, 'status', tuya.valueConverter.raw],
            //[127, 'status', customTrueFalseConverter],
            [134, 'relay_status_power_on', tuya.valueConverter.raw],
            //[134, 'relay_status_power_on', tuya.valueConverter.onOff],
        ],
    },
};

module.exports = definition;
peekayn84 commented 1 month ago

Hot Fix

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 tuya = require('zigbee-herdsman-converters/lib/tuya');
const legacy = require('zigbee-herdsman-converters/lib/legacy');
const e = exposes.presets;
const ea = exposes.access;

    const fzLocal = {
        value: {
            cluster: 'manuSpecificTuya',
            type: ['commandDataResponse', 'commandDataReport'],
            convert: (model, msg, publish, options, meta) => {
                    payload = {};
                    for (const dpValue of msg.data.dpValues) {
                            const dp = dpValue.dp;
                            switch (dp) {
                                case 17:
                                    {
                                        payload['over_leakage_current'] = legacy.getDataValue(dpValue)[3];
                                        payload['over_temperature'] = legacy.getDataValue(dpValue)[7];
                                        return payload;
                                    };
                                case 18:
                                    {
                                        payload['under_voltage'] = legacy.getDataValue(dpValue)[11];
                                        overvoltage=legacy.getDataValue(dpValue)[6]*256+legacy.getDataValue(dpValue)[7]
                                        payload['over_voltage'] = overvoltage;
                                        overcurrent=legacy.getDataValue(dpValue)[2]*256+legacy.getDataValue(dpValue)[3];
                                        payload['over_current']=overcurrent/10.0;
                                        return payload;
                                    };
                            };
                    };
            },
        },
    };

const tz_settings = {
    key: ['reclose_recover_seconds','clear_energy','under_voltage','over_voltage','reclosing_allowed_times','over_leakage_current','over_temperature','over_current'],
    convertSet: async (entity, key, value, meta) => {
        const settingType = {'trip': 2, 'alarm': 1, 'closed': 0};
        // const typeswitch = {'ON':true, 'OFF':false}
        let dp_key = -1; // value if not found
        // identify dp.
        const tuyadatapoints_list = meta.mapped.meta.tuyaDatapoints
        for (let i = 0; i < tuyadatapoints_list.length; i++) {
            if (tuyadatapoints_list[i][1] === key) {
                dp_key = tuyadatapoints_list[i][0];
                break;
            }
        }
        switch (key) {
        case 'reclose_recover_seconds': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'reclosing_allowed_times': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'clear_energy': {
            await tuya.sendDataPointBool(entity, dp_key, value === 'ON');
            break;
        }
        case 'over_leakage_current': {
            const value_send = meta.state['alarm1'];
            value_send['3']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7']];
            await tuya.sendDataPointRaw(entity, 17, payload);
            break;
        }
        case 'over_temperature': {
            const value_send = meta.state['alarm1'];
            value_send['7']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7']];
            await tuya.sendDataPointRaw(entity, 17, payload);
            break;
        }
        case 'over_current': {
            const value_send = meta.state['alarm2'];
            value_send['2']=value*10/256;
            value_send['3']=value*10%256;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        case 'over_voltage': {
            const value_send = meta.state['alarm2'];
            value_send['6']=value/256;
            value_send['7']=value%256;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        case 'under_voltage': {
            const value_send = meta.state['alarm2'];
            value_send['11']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        default: // Unknown key
            meta.logger.warn(`Unhandled key ${key}`);
        }
    },
}

const customTrueFalseConverter = {
    key: ['state'],
    convertSet: (entity, key, value, meta) => {
      // Your conversion logic
      const presence = value === 1 ? true : false;
      return {state: {presence}};
    },
    convertGet: (entity, key, meta) => {
      // Convert the state value from Home Assistant to the appropriate Zigbee value
      const zigbeeValue = meta.state.presence ? 1 : 0;
      return {state: zigbeeValue};
    },
  };

const definition = {
    fingerprint: [
        {
            modelID: 'TS0601',
            manufacturerName: '_TZE204_fhvdgeuh',
        },
    ],
    model: 'TZE204_fhvdgeuh',
    vendor: 'Tuya',
    description: 'Tuya multi-function energy monitor',
    fromZigbee: [
        tuya.fz.datapoints,
        fzLocal.value,
        // Дополнительные преобразователи, если необходимо
    ],
    toZigbee: [tuya.tz.datapoints,tz_settings],
    onEvent: tuya.onEvent({respondToMcuVersionResponse:false, queryIntervalSeconds: 3 * 60}),
    configure: tuya.configureMagicPacket,
    exposes: [
        tuya.exposes.switch(),
        e.energy(),
        e.power(),
        e.voltage(),
        e.current(),
        e.temperature(),
        e.numeric('leakage_current', ea.STATE).withUnit('mA').withDescription('Leakage current'),
        e.text('fault', ea.STATE).withDescription('Fault indication'),
        e.numeric('balance_energy', ea.STATE).withUnit('kWh').withDescription('Balance energy (kWh)'),
        e.numeric('over_leakage_current', ea.STATE_SET).withDescription('Over Leakage Current Cut Off').withValueMin(0).withValueMax(99).withValueStep(1).withUnit('mA'),
        e.numeric('over_temperature', ea.STATE_SET).withDescription('Over Temperature Cut Off').withValueMin(0).withValueMax(100).withValueStep(1).withUnit('°C'),
        e.numeric('over_voltage', ea.STATE_SET).withDescription('Over Voltage Cut Off').withValueMin(120).withValueMax(300).withValueStep(1).withUnit('V'),
        e.numeric('under_voltage', ea.STATE_SET).withDescription('Under Voltage Cut Off').withValueMin(80).withValueMax(210).withValueStep(1).withUnit('V'),
        e.numeric('reclose_recover_seconds', ea.STATE_SET).withDescription('Reclose recover seconds').withValueMin(10).withValueMax(99).withValueStep(1),
        e.numeric('over_current', ea.STATE_SET).withDescription('Over Current Cut Off').withValueMin(1).withValueMax(63).withValueStep(0.1).withUnit('A'),
        e.numeric('reclosing_allowed_times', ea.STATE_SET).withDescription('Time Restore After Over Current').withValueMin(0).withValueMax(30).withValueStep(1),
        e.numeric('charge_energy', ea.STATE).withDescription('Charge energy'),
        e.binary('clear_energy', ea.STATE_SET, 'ON', 'OFF').withDescription('Clear energy data'),
        e.binary('switch_prepayment', ea.STATE_SET, 'ON', 'OFF').withDescription('Switch prepayment'),
        //e.numeric('reclosing_allowed_times', ea.STATE).withDescription('Reclosing allowed times'),
        e.binary('reclosing_enable', ea.STATE_SET, 'ON', 'OFF').withDescription('Reclosing enable'),
        e.numeric('timer', ea.STATE).withDescription('Timer'),
        e.numeric('cycle_schedule', ea.STATE).withDescription('Cycle Schedule'),
        e.numeric('random_timing', ea.STATE).withDescription('Random Timing'),
        e.numeric('switch_inching', ea.STATE).withDescription('Switch Inching'),
        e.numeric('power_on_delay', ea.STATE).withDescription('Power-on delay power-on time'),
        e.numeric('overcurrent_event_threshold_time', ea.STATE).withDescription('Overcurrent Event Threshold Time'),
        e.numeric('time_threshold_lost_flow', ea.STATE).withDescription('Time Threshold of Lost Flow Event'),
        e.numeric('alarm1', ea.STATE).withDescription('Alarm set1'),
        e.numeric('alarm2', ea.STATE).withDescription('Alarm set2'),
        e.numeric('alarm_set3', ea.STATE).withDescription('Alarm set3'),
        e.numeric('status', ea.STATE).withDescription('Device status'),
        //e.binary('status', ea.STATE, 'ON', 'OFF').withDescription('Device status'),
        e.numeric('relay_status_power_on', ea.STATE).withDescription('Relay status for power on'),
        //e.binary('relay_status_power_on', ea.STATE, 'ON', 'OFF').withDescription('Relay status for power on'),

    ],
    meta: {
        tuyaDatapoints: [
            [1, 'total_forward_energy', tuya.valueConverter.divideBy100],
            [6, null, tuya.valueConverter.phaseVariant2],
            [9, 'fault', tuya.valueConverter.raw],
            [11, 'switch_prepayment', tuya.valueConverter.onOff],
            [12, 'clear_energy', tuya.valueConverter.onOff],
            [13, 'balance_energy', tuya.valueConverter.divideBy100],
            [14, 'charge_energy', tuya.valueConverter.divideBy100],
            [15, 'leakage_current', tuya.valueConverter.raw],
            [16, 'state', tuya.valueConverter.onOff],
            [17, 'alarm1', tuya.valueConverter.raw],
            [18, 'alarm2', tuya.valueConverter.raw],
            [101, 'restore_time', tuya.valueConverter.raw], // Deprecated
            [102, 'reclosing_allowed_times', tuya.valueConverter.raw],
            [103, 'temperature', tuya.valueConverter.raw],
            [104, 'reclosing_enable', tuya.valueConverter.onOff],
            [105, 'timer', tuya.valueConverter.raw],
            [106, 'cycle_schedule', tuya.valueConverter.raw],
            [107, 'reclose_recover_seconds', tuya.valueConverter.raw],
            [108, 'random_timing', tuya.valueConverter.raw],
            [109, 'switch_inching', tuya.valueConverter.raw],
            [119, 'power_on_delay', tuya.valueConverter.raw],
            [124, 'overcurrent_event_threshold_time', tuya.valueConverter.raw],
            [125, 'time_threshold_lost_flow', tuya.valueConverter.raw],
            [126, 'alarm_set3', tuya.valueConverter.raw],
            [127, 'status', tuya.valueConverter.raw],
            //[127, 'status', customTrueFalseConverter],
            [134, 'relay_status_power_on', tuya.valueConverter.raw],
            //[134, 'relay_status_power_on', tuya.valueConverter.onOff],
        ],
    },
};

module.exports = definition;
francisPiragua commented 1 month ago

Thank you very much @peekayn84, I'm trying it and it works, but I don't receive data on the energy.

peekayn84 commented 1 month ago

I don't know what mean at energy, but interface realised it and i show it too, but parametr at down "Power" work correcty, if i understand you right this is what you need

Thank you very much @peekayn84, I'm trying it and it works, but I don't receive data on the energy.

francisPiragua commented 1 month ago

I talk about this paramater:

imagen

peekayn84 commented 1 month ago

I will check it, i have same problem

peekayn84 commented 1 month ago

I talk about this paramater:

imagen

Fixed

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 tuya = require('zigbee-herdsman-converters/lib/tuya');
const legacy = require('zigbee-herdsman-converters/lib/legacy');
const e = exposes.presets;
const ea = exposes.access;

    const fzLocal = {
        value: {
            cluster: 'manuSpecificTuya',
            type: ['commandDataResponse', 'commandDataReport'],
            convert: (model, msg, publish, options, meta) => {
                    payload = {};
                    for (const dpValue of msg.data.dpValues) {
                            const dp = dpValue.dp;
                            switch (dp) {
                                case 17:
                                    {
                                        payload['over_leakage_current'] = legacy.getDataValue(dpValue)[3];
                                        payload['over_temperature'] = legacy.getDataValue(dpValue)[7];
                                        return payload;
                                    };
                                case 18:
                                    {
                                        payload['under_voltage'] = legacy.getDataValue(dpValue)[11];
                                        overvoltage=legacy.getDataValue(dpValue)[6]*256+legacy.getDataValue(dpValue)[7]
                                        payload['over_voltage'] = overvoltage;
                                        overcurrent=legacy.getDataValue(dpValue)[2]*256+legacy.getDataValue(dpValue)[3];
                                        payload['over_current']=overcurrent/10.0;
                                        return payload;
                                    };
                            };
                    };
            },
        },
    };

const tz_settings = {
    key: ['reclose_recover_seconds','clear_energy','under_voltage','over_voltage','reclosing_allowed_times','over_leakage_current','over_temperature','over_current'],
    convertSet: async (entity, key, value, meta) => {
        const settingType = {'trip': 2, 'alarm': 1, 'closed': 0};
        // const typeswitch = {'ON':true, 'OFF':false}
        let dp_key = -1; // value if not found
        // identify dp.
        const tuyadatapoints_list = meta.mapped.meta.tuyaDatapoints
        for (let i = 0; i < tuyadatapoints_list.length; i++) {
            if (tuyadatapoints_list[i][1] === key) {
                dp_key = tuyadatapoints_list[i][0];
                break;
            }
        }
        switch (key) {
        case 'reclose_recover_seconds': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'reclosing_allowed_times': {
            await tuya.sendDataPointValue(entity, dp_key, value);
            break;
        }
        case 'clear_energy': {
            await tuya.sendDataPointBool(entity, dp_key, value === 'ON');
            break;
        }
        case 'over_leakage_current': {
            const value_send = meta.state['alarm1'];
            value_send['3']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7']];
            await tuya.sendDataPointRaw(entity, 17, payload);
            break;
        }
        case 'over_temperature': {
            const value_send = meta.state['alarm1'];
            value_send['7']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7']];
            await tuya.sendDataPointRaw(entity, 17, payload);
            break;
        }
        case 'over_current': {
            const value_send = meta.state['alarm2'];
            value_send['2']=value*10/256;
            value_send['3']=value*10%256;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        case 'over_voltage': {
            const value_send = meta.state['alarm2'];
            value_send['6']=value/256;
            value_send['7']=value%256;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        case 'under_voltage': {
            const value_send = meta.state['alarm2'];
            value_send['11']=value;
            const payload = [value_send['0'],value_send['1'],value_send['2'],value_send['3'],value_send['4'],value_send['5'],value_send['6'],value_send['7'],value_send['8'],value_send['9'],value_send['10'],value_send['11']];
            await tuya.sendDataPointRaw(entity, 18, payload);
            break;
        }
        default: // Unknown key
            meta.logger.warn(`Unhandled key ${key}`);
        }
    },
}

const customTrueFalseConverter = {
    key: ['state'],
    convertSet: (entity, key, value, meta) => {
      // Your conversion logic
      const presence = value === 1 ? true : false;
      return {state: {presence}};
    },
    convertGet: (entity, key, meta) => {
      // Convert the state value from Home Assistant to the appropriate Zigbee value
      const zigbeeValue = meta.state.presence ? 1 : 0;
      return {state: zigbeeValue};
    },
  };

const definition = {
    fingerprint: [
        {
            modelID: 'TS0601',
            manufacturerName: '_TZE204_fhvdgeuh',
        },
    ],
    model: 'TZE204_fhvdgeuh',
    vendor: 'Tuya',
    description: 'Tuya multi-function energy monitor',
    fromZigbee: [
        tuya.fz.datapoints,
        fzLocal.value,
        // Дополнительные преобразователи, если необходимо
    ],
    toZigbee: [tuya.tz.datapoints,tz_settings],
    onEvent: tuya.onEvent({respondToMcuVersionResponse:false, queryIntervalSeconds: 3 * 60}),
    configure: tuya.configureMagicPacket,
    exposes: [
        tuya.exposes.switch(),
        e.energy(),
        e.power(),
        e.voltage(),
        e.current(),
        e.temperature(),
        e.numeric('leakage_current', ea.STATE).withUnit('mA').withDescription('Leakage current'),
        e.text('fault', ea.STATE).withDescription('Fault indication'),
        e.numeric('balance_energy', ea.STATE).withUnit('kWh').withDescription('Balance energy (kWh)'),
        e.numeric('over_leakage_current', ea.STATE_SET).withDescription('Over Leakage Current Cut Off').withValueMin(0).withValueMax(99).withValueStep(1).withUnit('mA'),
        e.numeric('over_temperature', ea.STATE_SET).withDescription('Over Temperature Cut Off').withValueMin(0).withValueMax(100).withValueStep(1).withUnit('°C'),
        e.numeric('over_voltage', ea.STATE_SET).withDescription('Over Voltage Cut Off').withValueMin(120).withValueMax(300).withValueStep(1).withUnit('V'),
        e.numeric('under_voltage', ea.STATE_SET).withDescription('Under Voltage Cut Off').withValueMin(80).withValueMax(210).withValueStep(1).withUnit('V'),
        e.numeric('reclose_recover_seconds', ea.STATE_SET).withDescription('Reclose recover seconds').withValueMin(10).withValueMax(99).withValueStep(1),
        e.numeric('over_current', ea.STATE_SET).withDescription('Over Current Cut Off').withValueMin(1).withValueMax(63).withValueStep(0.1).withUnit('A'),
        e.numeric('reclosing_allowed_times', ea.STATE_SET).withDescription('Time Restore After Over Current').withValueMin(0).withValueMax(30).withValueStep(1),
        exposes.numeric('charge_energy', ea.STATE).withDescription('Charge energy'),
        exposes.binary('clear_energy', ea.STATE_SET, 'ON', 'OFF').withDescription('Clear energy data'),
        exposes.binary('switch_prepayment', ea.STATE_SET, 'ON', 'OFF').withDescription('Switch prepayment'),
        //e.numeric('reclosing_allowed_times', ea.STATE).withDescription('Reclosing allowed times'),
        exposes.binary('reclosing_enable', ea.STATE_SET, 'ON', 'OFF').withDescription('Reclosing enable'),
        exposes.numeric('timer', ea.STATE).withDescription('Timer'),
        exposes.numeric('cycle_schedule', ea.STATE).withDescription('Cycle Schedule'),
        exposes.numeric('random_timing', ea.STATE).withDescription('Random Timing'),
        exposes.numeric('switch_inching', ea.STATE).withDescription('Switch Inching'),
        exposes.numeric('power_on_delay', ea.STATE).withDescription('Power-on delay power-on time'),
        exposes.numeric('overcurrent_event_threshold_time', ea.STATE).withDescription('Overcurrent Event Threshold Time'),
        exposes.numeric('time_threshold_lost_flow', ea.STATE).withDescription('Time Threshold of Lost Flow Event'),
        exposes.numeric('alarm1', ea.STATE).withDescription('Alarm set1'),
        exposes.numeric('alarm2', ea.STATE).withDescription('Alarm set2'),
        exposes.numeric('alarm_set3', ea.STATE).withDescription('Alarm set3'),
        exposes.numeric('status', ea.STATE).withDescription('Device status'),
        //e.binary('status', ea.STATE, 'ON', 'OFF').withDescription('Device status'),
        exposes.numeric('relay_status_power_on', ea.STATE).withDescription('Relay status for power on'),
        //e.binary('relay_status_power_on', ea.STATE, 'ON', 'OFF').withDescription('Relay status for power on'),

    ],
    meta: {
        tuyaDatapoints: [
            [1, 'energy', tuya.valueConverter.raw],
            [6, null, tuya.valueConverter.phaseVariant2],
            [9, 'fault', tuya.valueConverter.raw],
            [11, 'switch_prepayment', tuya.valueConverter.onOff],
            [12, 'clear_energy', tuya.valueConverter.onOff],
            [13, 'balance_energy', tuya.valueConverter.divideBy100],
            [14, 'charge_energy', tuya.valueConverter.divideBy100],
            [15, 'leakage_current', tuya.valueConverter.raw],
            [16, 'state', tuya.valueConverter.onOff],
            [17, 'alarm1', tuya.valueConverter.raw],
            [18, 'alarm2', tuya.valueConverter.raw],
            [101, 'restore_time', tuya.valueConverter.raw], // Deprecated
            [102, 'reclosing_allowed_times', tuya.valueConverter.raw],
            [103, 'temperature', tuya.valueConverter.raw],
            [104, 'reclosing_enable', tuya.valueConverter.onOff],
            [105, 'timer', tuya.valueConverter.raw],
            [106, 'cycle_schedule', tuya.valueConverter.raw],
            [107, 'reclose_recover_seconds', tuya.valueConverter.raw],
            [108, 'random_timing', tuya.valueConverter.raw],
            [109, 'switch_inching', tuya.valueConverter.raw],
            [119, 'power_on_delay', tuya.valueConverter.raw],
            [124, 'overcurrent_event_threshold_time', tuya.valueConverter.raw],
            [125, 'time_threshold_lost_flow', tuya.valueConverter.raw],
            [126, 'alarm_set3', tuya.valueConverter.raw],
            [127, 'status', tuya.valueConverter.raw],
            //[127, 'status', customTrueFalseConverter],
            [134, 'relay_status_power_on', tuya.valueConverter.raw],
            //[134, 'relay_status_power_on', tuya.valueConverter.onOff],
        ],
    },
};

module.exports = definition;
francisPiragua commented 1 month ago

Excellent work, thank you very much.

Koenkk commented 1 month ago

Could someone submit a pull request to add of the box support for this device?

peekayn84 commented 1 month ago

I can, but i don't know what do you mean at "pull request". Where and how can i do it?

peekayn84 commented 1 month ago

And all function not realized in this version here. Must have as i think it's clear data energy button: image In tuya app need write pass, and in real life too. But i don't know where and how. Cause wipe "Clear energy" button not work and as i think it cause without pass. PS I applologize for my english. I am from Ukraine.

peekayn84 commented 1 month ago

Ok, i check it, and as i understand right, it can't reset Energy remotly, not in tuya app, not through zigbee2mqtt

peekayn84 commented 1 month ago

So it's maybe final version of this

Koenkk commented 1 month ago

@peekayn84 the converter is not yet in a state that I can integrate it. We should e.g. get rid of the tz_settings. I guess you redefined it because some keys are missing. I've fixed that now in https://github.com/Koenkk/zigbee-herdsman-converters/commit/36e7440a4fa34b6757ce534771408d5fa0360db5 . Can you check with the latest dev?

Changes will be available in the dev branch in a few hours from now.

peekayn84 commented 1 month ago

I use it cause i don't know how change complex value like over_voltage, it part of 18 datapoit, i delete tz_setting and get this error image

skinkie commented 1 month ago

@peekayn84 misunderstood @Koenkk, I tried your variant as external converter. Notice that all values remain 'Null'. I noticed the same "Cannot read properties..." error.

image

after a while:

image

...I am surprised about the 2kWh used, since the device isn't connected to anything.

Koenkk commented 1 month ago

@peekayn84 is this on the latest dev branch? For more complex value converters, see e.g. https://github.com/Koenkk/zigbee-herdsman-converters/blob/b3169739e07ee6ffa90da93d21eefe78bb046d07/src/lib/tuya.ts#L587

DmytroKozlovskyi commented 1 week ago

Seems fixed by TZE204_fhvdgeuh definition and released in 1.40.1-1

ia1gerebec commented 1 week ago

новое обновление1.40.1-1 решило но есть проблемы по отображению вольт и ампер в пользовательской карточке 2024-09-07_22-59-08