Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
ia1gerebec opened this issue Aug 2, 2024 · 29 comments
Labels
new device support New device support request stale Stale issues

Comments

@ia1gerebec
Copy link

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;
@ia1gerebec ia1gerebec added the new device support New device support request label Aug 2, 2024
@SBozhyi
Copy link

SBozhyi commented Aug 3, 2024

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
Copy link

peekayn84 commented Aug 3, 2024

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
Copy link

Another one waiting too. Thanks.

@peekayn84
Copy link

peekayn84 commented Aug 5, 2024

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
Copy link

peekayn84 commented Aug 5, 2024

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
Copy link

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

@Koenkk
Copy link
Owner

Koenkk commented Aug 5, 2024

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

@peekayn84
Copy link

peekayn84 commented Aug 6, 2024

_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
Copy link

peekayn84 commented Aug 6, 2024

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
Copy link

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
Copy link

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
Copy link

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

@peekayn84
Copy link

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
Copy link

I talk about this paramater:

imagen

@peekayn84
Copy link

I will check it, i have same problem

@peekayn84
Copy link

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
Copy link

Excellent work, thank you very much.

@Koenkk
Copy link
Owner

Koenkk commented Aug 7, 2024

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

@peekayn84
Copy link

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

@peekayn84
Copy link

peekayn84 commented Aug 7, 2024

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
Copy link

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

@peekayn84
Copy link

So it's maybe final version of this

@Koenkk
Copy link
Owner

Koenkk commented Aug 8, 2024

@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 Koenkk/zigbee-herdsman-converters@36e7440 . Can you check with the latest dev?

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

@peekayn84
Copy link

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
Copy link

skinkie commented Aug 10, 2024

@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
Copy link
Owner

Koenkk commented Aug 12, 2024

@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
Copy link

DmytroKozlovskyi commented Sep 7, 2024

Seems fixed by TZE204_fhvdgeuh definition and released in 1.40.1-1

@ia1gerebec
Copy link
Author

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

Copy link
Contributor

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 30 days

@github-actions github-actions bot added the stale Stale issues label Dec 20, 2024
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Dec 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new device support New device support request stale Stale issues
Projects
None yet
Development

No branches or pull requests

7 participants