Koenkk / Koenkk/zigbee2mqtt

[External Converter]: Moes ZM7LT3 3-Phase Energy Power Meter (_TZE284_cf4b5ktf)

Open
#32,948 12 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

external converter
Dominant language
TypeScript
Stars
15.7k
Forks
2k
Avg merge
18h 55m
Merged PRs (30d)
35

Description

Link

https://aliexpress.com/item/1005012523225629.html

Database entry

{"id":29,"type":"Router","ieeeAddr":"0xa4c1384da9d1fa61","nwkAddr":24043,"manufId":4417,"manufName":"_TZE284_cf4b5ktf","powerSource":"Mains (single phase)","modelId":"TS0601","epList":[1,242],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[4,5,61184,0,60672],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65503":"��"2i"�"2i'�"2i,�"2i1�"2i6�"2i;�"2i@�"2i","65506":56,"65508":0,"65534":0,"modelId":"TS0601","manufacturerName":"_TZE284_cf4b5ktf","powerSource":1,"zclVersion":3,"appVersion":78,"stackVersion":0,"hwVersion":1,"dateCode":""}}},"binds":[],"configuredReportings":[],"meta":{}},"242":{"profId":41440,"epId":242,"devId":97,"inClusterList":[],"outClusterList":[33],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":78,"stackVersion":0,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"interviewState":"SUCCESSFUL","meta":{"configured":"0.0.0"},"lastSeen":1787828109955}

Zigbee2MQTT version

2.13.0 (fcbb7ff44bdc05a16e95d3472a81d110646cc17b
)

External converter
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const e = exposes.presets;
const ea = exposes.access;

// Decodifica um bloco de 18 bytes de uma fase
const decodePhase = (v) => ({
    voltage: ((v[2] << 8) | v[3]) / 10,
    current: ((v[4] << 16) | (v[5] << 8) | v[6]) / 1000,
    power: (v[7] << 16) | (v[8] << 8) | v[9],
    power_reactive: (v[10] << 16) | (v[11] << 8) | v[12],
    power_apparent: (v[13] << 16) | (v[14] << 8) | v[15],
    power_factor: v[16] / 100,
});

// DP 17 - limites de alarme: 3 blocos de 4 bytes [tipo, reservado, valorAlto, valorBaixo]
const decodeAlarmSettings = (v) => ({
    over_current_threshold: (v[2] << 8) | v[3],
    over_voltage_threshold: (v[6] << 8) | v[7],
    under_voltage_threshold: (v[10] << 8) | v[11],
});

const alarmFields = {
    over_current_threshold: { offset: 0 },
    over_voltage_threshold: { offset: 4 },
    under_voltage_threshold: { offset: 8 },
};

const getStoredDp17 = (meta) => {
    const stored = globalStore.getValue(meta.device, 'dp17raw');
    if (stored) return [...stored];
    return [1, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0]; // fallback com os tipos corretos
};

// Cria uma entrada de ESCRITA para um campo específico do DP 17
const makeAlarmWriteEntry = (key) => {
    const { offset } = alarmFields[key];
    return [17, key, {
        from: (v, meta) => {
            globalStore.putValue(meta.device, 'dp17raw', v);
            return decodeAlarmSettings(v);
        },
        to: async (value, meta) => {
            const buf = getStoredDp17(meta);
            buf[offset + 2] = (value >> 8) & 0xFF;
            buf[offset + 3] = value & 0xFF;
            globalStore.putValue(meta.device, 'dp17raw', buf);
            return buf; // array puro, NÃO Buffer
        },
    }];
};

const definition = {
    fingerprint: [{ modelID: 'TS0601', manufacturerName: '_TZE284_cf4b5ktf' }],
    model: 'ZM7LT3',
    vendor: 'Moes',
    description: 'Smart 3-Phase Energy Power Meter',
    extend: [
        tuya.modernExtend.tuyaBase({
            dp: true,
            queryOnConfigure: true,
            queryIntervalSeconds: 10, // atualização a cada 5 segundos
        }),
    ],
    exposes: [
        // Fase A
        e.numeric('voltage_l1', ea.STATE).withUnit('V').withDescription('Voltage phase A'),
        e.numeric('current_l1', ea.STATE).withUnit('A').withDescription('Current phase A'),
        e.numeric('power_l1', ea.STATE).withUnit('W').withDescription('Active power phase A'),
        e.numeric('power_reactive_l1', ea.STATE).withUnit('VAR').withDescription('Reactive power phase A'),
        e.numeric('power_apparent_l1', ea.STATE).withUnit('VA').withDescription('Apparent power phase A'),
        e.numeric('power_factor_l1', ea.STATE).withDescription('Power factor phase A'),

        // Fase B
        e.numeric('voltage_l2', ea.STATE).withUnit('V').withDescription('Voltage phase B'),
        e.numeric('current_l2', ea.STATE).withUnit('A').withDescription('Current phase B'),
        e.numeric('power_l2', ea.STATE).withUnit('W').withDescription('Active power phase B'),
        e.numeric('power_reactive_l2', ea.STATE).withUnit('VAR').withDescription('Reactive power phase B'),
        e.numeric('power_apparent_l2', ea.STATE).withUnit('VA').withDescription('Apparent power phase B'),
        e.numeric('power_factor_l2', ea.STATE).withDescription('Power factor phase B'),

        // Fase C
        e.numeric('voltage_l3', ea.STATE).withUnit('V').withDescription('Voltage phase C'),
        e.numeric('current_l3', ea.STATE).withUnit('A').withDescription('Current phase C'),
        e.numeric('power_l3', ea.STATE).withUnit('W').withDescription('Active power phase C'),
        e.numeric('power_reactive_l3', ea.STATE).withUnit('VAR').withDescription('Reactive power phase C'),
        e.numeric('power_apparent_l3', ea.STATE).withUnit('VA').withDescription('Apparent power phase C'),
        e.numeric('power_factor_l3', ea.STATE).withDescription('Power factor phase C'),

        // Limites de alarme (DP 17) - editáveis
        e.numeric('over_current_threshold', ea.ALL).withUnit('A')
            .withDescription('Overcurrent alarm threshold (any phase)').withCategory('config'),
        e.numeric('over_voltage_threshold', ea.ALL).withUnit('V')
            .withDescription('Overvoltage alarm threshold').withCategory('config'),
        e.numeric('under_voltage_threshold', ea.ALL).withUnit('V')
            .withDescription('Undervoltage alarm threshold').withCategory('config'),
    ],
    meta: {
        tuyaDatapoints: [
            [6, null, { from: (v) => {
                const d = decodePhase(v);
                return {
                    voltage_l1: d.voltage, current_l1: d.current, power_l1: d.power,
                    power_reactive_l1: d.power_reactive, power_apparent_l1: d.power_apparent,
                    power_factor_l1: d.power_factor,
                };
            }}],
            [7, null, { from: (v) => {
                const d = decodePhase(v);
                return {
                    voltage_l2: d.voltage, current_l2: d.current, power_l2: d.power,
                    power_reactive_l2: d.power_reactive, power_apparent_l2: d.power_apparent,
                    power_factor_l2: d.power_factor,
                };
            }}],
            [8, null, { from: (v) => {
                const d = decodePhase(v);
                return {
                    voltage_l3: d.voltage, current_l3: d.current, power_l3: d.power,
                    power_reactive_l3: d.power_reactive, power_apparent_l3: d.power_apparent,
                    power_factor_l3: d.power_factor,
                };
            }}],

            // Entrada "mestra" - roda na LEITURA e popula os 3 campos de uma vez
            [17, null, { from: (v, meta) => {
                globalStore.putValue(meta.device, 'dp17raw', v);
                return decodeAlarmSettings(v);
            }}],

            // Entradas específicas - usadas só na ESCRITA
            makeAlarmWriteEntry('over_current_threshold'),
            makeAlarmWriteEntry('over_voltage_threshold'),
            makeAlarmWriteEntry('under_voltage_threshold'),
        ],
    },
};

module.exports = definition;
What does/doesn't work with the external definition?

Recently, I purchased a Moes energy measurement module, model ZM7LT3, and was unpleasantly surprised to discover that it is not compatible with Z2M. I then mapped the DataPoints (DPs) with the help of a similar energy measurement module for which someone had posted the converter here (thanks, guys!).

The module returns 3 DPs: DP 6 (Phase A), DP 7 (Phase B), DP 8 (Phase C), and DP 17 (Alarm threshold values). If anyone feels that any information is missing and can help add to it, I would really appreciate it.

Image Image Image Image Image
Notes

software_build_id: undefined
date_code: ``
endpoints:

{"1":{"clusters":{"input":["genGroups","genScenes","manuSpecificTuya","genBasic","60672"],"output":["genOta","genTime"]}},"242":{"clusters":{"input":[],"output":["greenPower"]}}}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied external converter, especially tuya.modernExtend.tuyaBase and meta.tuyaDatapoints, and compare the DP 6, 7, 8, and 17 mappings with the linked ZHA handler. Confirm the device is supported, phase measurements are exposed, and alarm thresholds can be read and written correctly.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
embedded-iot
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.