Koenkk / zigbee2mqtt

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

TRV602 (OLED) (vendor eweLink) is not working #22748

Open michapr opened 5 months ago

michapr commented 5 months ago

What happened?

Have included device and see in about screen:

grafik

But got message "Device '0x90ab96fffe721a13' with Zigbee model 'CK-MG22-TRVZB-01(7017)' and manufacturer name 'eWeLink' is NOT supported, please follow https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html"

Device does not report values and setpoint etc. cannot be set. Battery value will be reported.

(made wrong topic before - it is not "new device", but TRV602 is supported - in general...

What did you expect to happen?

Work like other TRV602 ;)

How to reproduce it (minimal and precise)

include device only.

Zigbee2MQTT version

1.37.1 commit: cda867a3

Adapter firmware version

7.4.1.0 build 0

Adapter

Z-Wave.Me Z-Station dual Zigbee & Z-Wave

Setup

Z2M plain, Pi3, bookworm

Debug log

as sample:

2024-05-23T16:23:02.130462+02:00 pi90-200 node[112070]: [2024-05-23 16:23:02] #033[34mdebug#033[39m: #011z2m: Received Zigbee message from '0x90ab96fffe721a13', type 'attributeReport', cluster '64529', data '{"24585":1700}' from endpoint 1 with groupID 0
2024-05-23T16:23:02.131389+02:00 pi90-200 node[112070]: [2024-05-23 16:23:02] #033[34mdebug#033[39m: #011z2m: No converter available for 'CK-MG22-TRVZB-01(7017)' with cluster '64529' and type 'attributeReport' and data '{"24585":1700}'

TRV602_converter_errors.log

TRV602_device.log

michapr commented 5 months ago

database entry:

{
    "id": 9,
    "type": "EndDevice",
    "ieeeAddr": "0x90ab96fffe721a13",
    "nwkAddr": 40761,
    "manufId": 4742,
    "manufName": "eWeLink",
    "powerSource": "Battery",
    "modelId": "CK-MG22-TRVZB-01(7017)",
    "epList": [1],
    "endpoints": {
        "1": {
            "profId": 260,
            "epId": 1,
            "devId": 769,
            "inClusterList": [0, 1, 3, 6, 32, 513, 64599, 64529],
            "outClusterList": [10, 25],
            "clusters": {
                "64529": {
                    "attributes": {
                        "0": 0,
                        "16": 513,
                        "24576": 0,
                        "24577": 1500,
                        "24578": 700,
                        "24579": 0,
                        "24580": 308,
                        "24581": 0,
                        "24582": 2604,
                        "24583": 1356,
                        "24584": 127,
                        "24585": 1250
                    }
                },
                "genBasic": {
                    "attributes": {
                        "appVersion": 0
                    }
                },
                "hvacThermostat": {
                    "attributes": {
                        "systemMode": 4,
                        "localTemp": 2160,
                        "occupiedHeatingSetpoint": 1250,
                        "runningState": 0,
                        "localTemperatureCalibration": 0
                    }
                },
                "genPollCtrl": {
                    "attributes": {
                        "checkinInterval": 14400
                    }
                },
                "genOnOff": {
                    "attributes": {
                        "onOff": 1
                    }
                },
                "genPowerCfg": {
                    "attributes": {
                        "batteryPercentageRemaining": 198
                    }
                }
            },
            "binds": [{
                    "cluster": 32,
                    "type": "endpoint",
                    "deviceIeeeAddress": "0x943469fffe0d65a9",
                    "endpointID": 1
                }, {
                    "cluster": 1,
                    "type": "endpoint",
                    "deviceIeeeAddress": "0x943469fffe0d65a9",
                    "endpointID": 1
                }, {
                    "cluster": 6,
                    "type": "endpoint",
                    "deviceIeeeAddress": "0x943469fffe0d65a9",
                    "endpointID": 1
                }
            ],
            "configuredReportings": [{
                    "cluster": 1,
                    "attrId": 33,
                    "minRepIntval": 3600,
                    "maxRepIntval": 65000,
                    "repChange": 10,
                    "manufacturerCode": null
                }, {
                    "cluster": 6,
                    "attrId": 0,
                    "minRepIntval": 0,
                    "maxRepIntval": 65000,
                    "repChange": 1,
                    "manufacturerCode": null
                }
            ],
            "meta": {}
        }
    },
    "appVersion": 0,
    "dateCode": "20230811",
    "swBuildId": "1.1.1",
    "zclVersion": 8,
    "interviewCompleted": true,
    "meta": {
        "configured": 332242049
    },
    "lastSeen": 1717654460858,
    "checkinInterval": 3600
}

@Koenkk : is there a chance to make an external definition? I have tried, but no success. Or better to forget about this device? ;)

railin commented 2 months ago

Hallo Michael, konntest Du Deinen TRV CK-MG22-TRVZB-01 von eWeLink irgendwie einbinden, oder hast Du das Gerät in die Ecke gelegt? Stehe vor dem gleichen Problem, da ich es als TRV602 gekauft habe, nun aber feststellen musste, dass es das gar nicht ist.

Danke für die Info, Rainer

michapr commented 2 months ago

Hallo, liegt noch in der Ecke - wartet darauf, dass ich nochmal versuche einen entsprechenden Konverter zu schreiben... Kannst ja nochmal einen request für ein neues Device aufmachen, vielleicht erhört man uns diesmal oder es findet sich jemand, der das schon gemacht hat... ;)

railin commented 2 months ago

Hallo Michael, danke für die Info. Werde das Teil auch erst einmal weg legen :-).

VG, Rainer

Am 13. September 2024 08:22:10 MESZ schrieb "Michael P." @.***>:

Hallo, liegt noch in der Ecke - wartet darauf, dass ich nochmal versuche einen entsprechenden Konverter zu schreiben... Kannst ja nochmal einen request für ein neues Device aufmachen, vielleicht erhört man uns diesmal oder es findet sich jemand, der das schon gemacht hat... ;)

-- Reply to this email directly or view it on GitHub: https://github.com/Koenkk/zigbee2mqtt/issues/22748#issuecomment-2348125454 You are receiving this because you commented.

Message ID: @.> -- Dr. Rainer Lindigkeit Wallmerkamp 47 D-38228 Salzgitter Phone: 0176 96044073 Email: @.

genmllc commented 2 weeks ago

Hello, I got it mostly working using the Sonoff TRVZB as a model. Here's my external converter :

const {
    Zcl
} = require('zigbee-herdsman');

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const constants = require('zigbee-herdsman-converters/lib/constants');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const {
    battery,
    binary,
    bindCluster,
    commandsOnOff,
    customTimeResponse,
    deviceAddCustomCluster,
    enumLookup,
    forcePowerSource,
    humidity,
    iasZoneAlarm,
    numeric,
    occupancy,
    onOff,
    ota,
    temperature,
} = require('zigbee-herdsman-converters/lib/modernExtend');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const {
    DefinitionWithExtend,
    Fz,
    KeyValue,
    KeyValueAny,
    ModernExtend,
    Tz
} = require('zigbee-herdsman-converters/lib/types');
const utils = require('zigbee-herdsman-converters/lib/utils');

const e = exposes.presets;
const ea = exposes.access;

// const sonoffExtend = {
    // weeklySchedule: (): ModernExtend => {
        // const exposes = e
            // .composite('schedule', 'weekly_schedule', ea.STATE_SET)
            // .withDescription(
                // 'The preset heating schedule to use when the system mode is set to "auto" (indicated with ⏲ on the TRV). ' +
                // "Up to 6 transitions can be defined per day, where a transition is expressed in the format 'HH:mm/temperature', each " +
                // 'separated by a space. The first transition for each day must start at 00:00 and the valid temperature range is 4-35°C ' +
                // '(in 0.5°C steps). The temperature will be set at the time of the first transition until the time of the next transition, ' +
                // "e.g. '04:00/20 10:00/25' will result in the temperature being set to 20°C at 04:00 until 10:00, when it will change to 25°C.",
            // )
            // .withFeature(e.text('sunday', ea.STATE_SET))
            // .withFeature(e.text('monday', ea.STATE_SET))
            // .withFeature(e.text('tuesday', ea.STATE_SET))
            // .withFeature(e.text('wednesday', ea.STATE_SET))
            // .withFeature(e.text('thursday', ea.STATE_SET))
            // .withFeature(e.text('friday', ea.STATE_SET))
            // .withFeature(e.text('saturday', ea.STATE_SET));

        // const fromZigbee: Fz.Converter[] = [{
            // cluster: 'hvacThermostat',
            // type: ['commandGetWeeklyScheduleRsp'],
            // convert: (model, msg, publish, options, meta) => {
                // const day = Object.entries(constants.thermostatDayOfWeek).find((d) => msg.data.dayofweek & (1 << +d[0]))[1];

                // const transitions = msg.data.transitions
                    // .map((t: {
                        // heatSetpoint: number;transitionTime: number
                    // }) => {
                        // const totalMinutes = t.transitionTime;
                        // const hours = totalMinutes / 60;
                        // const rHours = Math.floor(hours);
                        // const minutes = (hours - rHours) * 60;
                        // const rMinutes = Math.round(minutes);
                        // const strHours = rHours.toString().padStart(2, '0');
                        // const strMinutes = rMinutes.toString().padStart(2, '0');

                        // return `${strHours}:${strMinutes}/${t.heatSetpoint / 100}`;
                    // })
                    // .sort()
                    // .join(' ');

                // return {
                    // weekly_schedule: {
                        // ...(meta.state.weekly_schedule as Record < string, string > []),
                        // [day]: transitions,
                    // },
                // };
            // },
        // }, ];

        // const toZigbee: Tz.Converter[] = [{
            // key: ['weekly_schedule'],
            // convertSet: async (entity, key, value, meta) => {
                ////Transition format: HH:mm/temperature
                // const transitionRegex = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])\/(\d+(\.5)?)$/;

                // utils.assertObject(value, key);

                // for (const dayOfWeekName of Object.keys(value)) {
                    // const dayKey = utils.getKey(constants.thermostatDayOfWeek, dayOfWeekName.toLowerCase(), null);

                    // if (dayKey === null) {
                        // throw new Error(`Invalid schedule: invalid day name, found: ${dayOfWeekName}`);
                    // }

                    // const dayOfWeekBit = Number(dayKey);

                    // const transitions = value[dayOfWeekName].split(' ').sort();

                    // if (transitions.length > 6) {
                        // throw new Error('Invalid schedule: days must have no more than 6 transitions');
                    // }

                    // const payload: KeyValueAny = {
                        // dayofweek: 1 << Number(dayOfWeekBit),
                        // numoftrans: transitions.length,
                        // mode: 1 << 0, // heat
                        // transitions: [],
                    // };

                    // for (const transition of transitions) {
                        // const matches = transition.match(transitionRegex);

                        // if (!matches) {
                            // throw new Error(
                                // 'Invalid schedule: transitions must be in format HH:mm/temperature (e.g. 12:00/15.5), found: ' + transition,
                            // );
                        // }

                        // const hour = parseInt(matches[1]);
                        // const mins = parseInt(matches[2]);
                        // const temp = parseFloat(matches[3]);

                        // if (temp < 4 || temp > 35) {
                            // throw new Error(`Invalid schedule: temperature value must be between 4-35 (inclusive), found: ${temp}`);
                        // }

                        // payload.transitions.push({
                            // transitionTime: hour * 60 + mins,
                            // heatSetpoint: Math.round(temp * 100),
                        // });
                    // }

                    // if (payload.transitions[0].transitionTime !== 0) {
                        // throw new Error('Invalid schedule: the first transition of each day should start at 00:00');
                    // }

                    // await entity.command('hvacThermostat', 'setWeeklySchedule', payload, utils.getOptions(meta.mapped, entity));
                // }
            // },
        // }, ];

        // return {
            // exposes: [exposes],
            // fromZigbee,
            // toZigbee,
            // isModernExtend: true,
        // };
    // }
// };

const definition = {
    fingerprint: [{
        modelID: 'CK-MG22-TRVZB-01(7017)',
        manufacturerName: 'eWeLink'
    }],
    model: 'CK-MG22-TRVZB-01(7017)',
    vendor: 'eWeLink',
    description: 'Zigbee thermostatic radiator valve',
    exposes: [
        e
        .climate()
        .withSetpoint('occupied_heating_setpoint', 4, 35, 0.5)
        .withLocalTemperature()
        .withLocalTemperatureCalibration(-7.0, 7.0, 0.2)
        .withSystemMode(['off', 'auto', 'heat'], ea.ALL, 'Mode of the thermostat')
        .withRunningState(['idle', 'heat'], ea.STATE_GET),
        e.battery(),
    ],
    fromZigbee: [fz.thermostat, fz.battery],
    toZigbee: [
        tz.thermostat_local_temperature,
        tz.thermostat_local_temperature_calibration,
        tz.thermostat_occupied_heating_setpoint,
        tz.thermostat_system_mode,
        tz.thermostat_running_state,
    ],
    extend: [
        deviceAddCustomCluster('customSonoffTrvzb', {
            ID: 0xfc11,
            attributes: {
                childLock: {
                    ID: 0x0000,
                    type: Zcl.DataType.BOOLEAN
                },
                tamper: {
                    ID: 0x2000,
                    type: Zcl.DataType.UINT8
                },
                illumination: {
                    ID: 0x2001,
                    type: Zcl.DataType.UINT8
                },
                openWindow: {
                    ID: 0x6000,
                    type: Zcl.DataType.BOOLEAN
                },
                frostProtectionTemperature: {
                    ID: 0x6002,
                    type: Zcl.DataType.INT16
                },
                idleSteps: {
                    ID: 0x6003,
                    type: Zcl.DataType.UINT16
                },
                closingSteps: {
                    ID: 0x6004,
                    type: Zcl.DataType.UINT16
                },
                valveOpeningLimitVoltage: {
                    ID: 0x6005,
                    type: Zcl.DataType.UINT16
                },
                valveClosingLimitVoltage: {
                    ID: 0x6006,
                    type: Zcl.DataType.UINT16
                },
                valveMotorRunningVoltage: {
                    ID: 0x6007,
                    type: Zcl.DataType.UINT16
                },
                valveOpeningDegree: {
                    ID: 0x600b,
                    type: Zcl.DataType.UINT8
                },
                valveClosingDegree: {
                    ID: 0x600c,
                    type: Zcl.DataType.UINT8
                },
            },
            commands: {},
            commandsResponse: {},
        }),
        binary({
            name: 'child_lock',
            cluster: 'customSonoffTrvzb',
            attribute: 'childLock',
            entityCategory: 'config',
            description: 'Enables/disables physical input on the device',
            valueOn: ['LOCK', 0x01],
            valueOff: ['UNLOCK', 0x00],
        }),
        binary({
            name: 'open_window',
            cluster: 'customSonoffTrvzb',
            attribute: 'openWindow',
            entityCategory: 'config',
            description: 'Automatically turns off the radiator when local temperature drops by more than 1.5°C in 4.5 minutes.',
            valueOn: ['ON', 0x01],
            valueOff: ['OFF', 0x00],
        }),
        numeric({
            name: 'frost_protection_temperature',
            cluster: 'customSonoffTrvzb',
            attribute: 'frostProtectionTemperature',
            entityCategory: 'config',
            description: 'Minimum temperature at which to automatically turn on the radiator, if system mode is off, to prevent pipes freezing.',
            valueMin: 4.0,
            valueMax: 35.0,
            valueStep: 0.5,
            unit: '°C',
            scale: 100,
        }),
        numeric({
            name: 'idle_steps',
            cluster: 'customSonoffTrvzb',
            attribute: 'idleSteps',
            entityCategory: 'diagnostic',
            description: 'Number of steps used for calibration (no-load steps)',
            access: 'STATE_GET',
        }),
        numeric({
            name: 'closing_steps',
            cluster: 'customSonoffTrvzb',
            attribute: 'closingSteps',
            entityCategory: 'diagnostic',
            description: 'Number of steps it takes to close the valve',
            access: 'STATE_GET',
        }),
        numeric({
            name: 'valve_opening_limit_voltage',
            cluster: 'customSonoffTrvzb',
            attribute: 'valveOpeningLimitVoltage',
            entityCategory: 'diagnostic',
            description: 'Valve opening limit voltage',
            unit: 'mV',
            access: 'STATE_GET',
        }),
        numeric({
            name: 'valve_closing_limit_voltage',
            cluster: 'customSonoffTrvzb',
            attribute: 'valveClosingLimitVoltage',
            entityCategory: 'diagnostic',
            description: 'Valve closing limit voltage',
            unit: 'mV',
            access: 'STATE_GET',
        }),
        numeric({
            name: 'valve_motor_running_voltage',
            cluster: 'customSonoffTrvzb',
            attribute: 'valveMotorRunningVoltage',
            entityCategory: 'diagnostic',
            description: 'Valve motor running voltage',
            unit: 'mV',
            access: 'STATE_GET',
        }),
        numeric({
            name: 'valve_opening_degree',
            cluster: 'customSonoffTrvzb',
            attribute: 'valveOpeningDegree',
            entityCategory: 'config',
            description: 'Valve open position (percentage) control. ' +
                'If the opening degree is set to 100%, the valve is fully open when it is opened. ' +
                'If the opening degree is set to 0%, the valve is fully closed when it is opened, ' +
                'and the default value is 100%. ' +
                'Note: only version v1.1.4 or higher is supported.',
            valueMin: 0.0,
            valueMax: 100.0,
            valueStep: 1.0,
            unit: '%',
        }),
        numeric({
            name: 'valve_closing_degree',
            cluster: 'customSonoffTrvzb',
            attribute: 'valveClosingDegree',
            entityCategory: 'config',
            description: 'Valve closed position (percentage) control. ' +
                'If the closing degree is set to 100%, the valve is fully closed when it is closed. ' +
                'If the closing degree is set to 0%, the valve is fully opened when it is closed, ' +
                'and the default value is 100%. ' +
                'Note: Only version v1.1.4 or higher is supported.',
            valueMin: 0.0,
            valueMax: 100.0,
            valueStep: 1.0,
            unit: '%',
        }),
        //sonoffExtend.weeklySchedule(),
        customTimeResponse('1970_UTC'),
        ota(),
    ],
    configure: async (device, coordinatorEndpoint) => {
        const endpoint = device.getEndpoint(1);
        await reporting.bind(endpoint, coordinatorEndpoint, ['hvacThermostat']);
        await reporting.thermostatTemperature(endpoint);
        await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
        await reporting.thermostatSystemMode(endpoint);
        await endpoint.read('hvacThermostat', ['localTemperatureCalibration']);
        await endpoint.read(0xfc11, [0x0000, 0x6000, 0x6002, 0x6003, 0x6004, 0x6005, 0x6006, 0x6007]);
    },
};

module.exports = definition;

I was not able to make the schedule feature work (this part is commented in the converter). Otherwise, most features seem to work fine.