pvvx / ZigbeeTLc

Custom firmware for Zigbee 3.0 IoT devices on the TLSR825x chip
Other
321 stars 15 forks source link

Updated ZigbeeTlc v0.1.2.1+ external Converter #87

Open robvanoostenrijk opened 2 months ago

robvanoostenrijk commented 2 months ago

@pvvx,

I've updated and consolidated the external converter code for Z2M and aligned it to v.0.1.2.1+ specifications:

image

This is tested against CGDK2 and TS0201_TZ3000. However, I already put in the definitions for devices with comfort display (but unable to test). Someone with such a device should be easy able to create the definition with the below code however.

Additionally it should support both '-z' and '-bz' devices.

pvvx-zigbeetlc.js.txt

// requires zigbee2mqtt v1.34+
// external converter for ZigbeeTLc v0.1.2.1+ by pvvx
// https://github.com/pvvx/ZigbeeTLc
// based on external converter for devbis-Firmware
// https://raw.githubusercontent.com/devbis/z03mmc/master/converters/lywsd03mmc.js

const fz = require("zigbee-herdsman-converters/converters/fromZigbee");
const tz = require("zigbee-herdsman-converters/converters/toZigbee");

const herdsman = require('zigbee-herdsman');
const { binary, numeric, quirkAddEndpointCluster } = require('zigbee-herdsman-converters/lib/modernExtend');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const constants = require('zigbee-herdsman-converters/lib/constants');
const ota = require('zigbee-herdsman-converters/lib/ota');
const e = exposes.presets;
const ea = exposes.access;

const pvvx = {
    comfortDisplay: binary({
        name: 'comfort_display',
        valueOn: ['show', 0],
        valueOff: ['hide', 1],
        cluster: 'hvacUserInterfaceCfg',
        attribute: {ID: 0x0002, type: herdsman.Zcl.DataType.enum8},
        description: 'Whether to show a comfort indicator on the device screen.',
    }),
    tempCalibration: numeric({
            name: 'temperature_calibration',
            unit: '°C',
            cluster: 'hvacUserInterfaceCfg',
            attribute: {ID: 0x0100, type: herdsman.Zcl.DataType.int16},
            valueMin: -327.67,
            valueMax: 327.67,
            valueStep: 0.01,
            scale: 10,
            description: 'Temperature calibration, in 0.01° steps, default 0%.',
        }),
    humidityCalibration: numeric({
            name: 'humidity_calibration',
            unit: '%',
            cluster: 'hvacUserInterfaceCfg',
            attribute: {ID: 0x0101, type: herdsman.Zcl.DataType.int16},
            valueMin: -327.67,
            valueMax: 327.67,
            valueStep: 0.01,
            scale: 10,
            description: 'Humidity calibration, in 0.01% steps, default 0%.',
        }),
    comfortTemperatureMin: numeric({
            name: 'comfort_temperature_min',
            unit: '°C',
            cluster: 'hvacUserInterfaceCfg',
            attribute: {ID: 0x0102, type: herdsman.Zcl.DataType.int16},
            valueMin: -327.67,
            valueMax: 327.67,
            valueStep: 0.01,
            scale: 10,
            description: 'Comfort parameters/Temperature minimum, in 0.01°C steps, default 20.00°C.',
        }),
    comfortTemperatureMax: numeric({
            name: 'comfort_temperature_max',
            unit: '°C',
            cluster: 'hvacUserInterfaceCfg',
            attribute: {ID: 0x0103, type: herdsman.Zcl.DataType.int16},
            valueMin: -327.67,
            valueMax: 327.67,
            valueStep: 0.01,
            scale: 10,
            description: 'Comfort parameters/Temperature maximum, in 0.01°C steps, default 25.00°C.',
        }),
    comfortHumidityMin: numeric({
            name: 'comfort_humidity_min',
            unit: '%',
            cluster: 'hvacUserInterfaceCfg',
            attribute: {ID: 0x0104, type: herdsman.Zcl.DataType.uint16},
            valueMin: 0,
            valueMax: 9999,
            scale: 10,
            description: 'Comfort parameters/Humidity minimum, in 1% steps, default 40.00%',
        }),
    comfortHumidityMax: numeric({
            name: 'comfort_humidity_max',
            unit: '%',
            cluster: 'hvacUserInterfaceCfg',
            attribute: {ID: 0x0105, type: herdsman.Zcl.DataType.uint16},
            valueMin: 0,
            valueMax: 9999,
            scale: 10,
            description: 'Comfort parameters/Humidity maximum, in 1% steps, default 60.00%.',
    }),
    display: binary({
        name: 'display',
        valueOn: ['on', 0],
        valueOff: ['off', 1],
        cluster: 'hvacUserInterfaceCfg',
        attribute: {ID: 0x0106, type: herdsman.Zcl.DataType.enum8},
        description: 'Whether to enable the device display.',
    }),
    measurementInterval: numeric({
        name: 'measurement_interval',
        unit: 's',
        cluster: 'hvacUserInterfaceCfg',
        attribute: {ID: 0x0107, type: herdsman.Zcl.DataType.uint8},
        valueMin: 3,
        valueMax: 255,
        description: 'Measurement interval, default 10 seconds.',
    }),
}

const definitions = [
    {
        // CGDK2 - https://pvvx.github.io/CGDK2/
        fingerprint: [
            {modelID: 'CGDK2-z', manufacturerName: 'Qingping', model: 'CGDK2'},
            {modelID: 'CGDK2-bz', manufacturerName: 'Qingping', model: 'CGDK2'}
        ],
        model: 'TS0201',
        vendor: 'Qingping',
        description: 'Temp & RH Monitor Lite (CGDK2 - pvxx/ZigbeeTLc)',
        fromZigbee: [fz.temperature, fz.humidity, fz.battery],
        toZigbee: [tz.thermostat_temperature_display_mode],
        exposes: [
            e.battery(),
            e.temperature(),
            e.humidity(),
            e.enum('temperature_display_mode', ea.ALL, ['celsius', 'fahrenheit'])
                .withDescription('The temperature unit displayed on the screen')
        ],
        extend: [
            quirkAddEndpointCluster({
                endpointID: 1,
                outputClusters: [
                    'genOta',
                ],
                inputClusters: [
                    'genBasic',
                    'genPowerCfg',
                    'genIdentify',
                    'genPollCtrl',
                    'hvacUserInterfaceCfg',
                    'msTemperatureMeasurement',
                    'msRelativeHumidity',
                ],
            }),
            pvvx.tempCalibration,
            pvvx.humidityCalibration,
            pvvx.display,
            pvvx.measurementInterval
        ],
        ota: ota.zigbeeOTA,
        configure: async (device, coordinatorEndpoint, logger) => {
            const endpoint = device.getEndpoint(1);
            const bindClusters = ['msTemperatureMeasurement', 'msRelativeHumidity', 'genPowerCfg', 'genPollCtrl'];
            await reporting.bind(endpoint, coordinatorEndpoint, bindClusters);
            await reporting.temperature(endpoint, {min: 10, max: constants.repInterval.MINUTES_5, change: 10});
            await reporting.humidity(endpoint, {min: 10, max: constants.repInterval.MINUTES_5, change: 50});
            await reporting.batteryPercentageRemaining(endpoint);
            device.powerSource = 'Battery';
            device.save();
        },
    },
    {
        // TS0201_TZ3000 - https://pvvx.github.io/TS0201_TZ3000/
        fingerprint: [
            {modelID: 'TS0201-z', manufacturerName: 'Tuya', model: 'TS0201'},
            {modelID: 'TS0201-bz', manufacturerName: 'Tuya', model: 'TS0201'}
        ],
        model: 'WSD500A',
        vendor: 'Tuya',
        description: 'Temperature & Humidity Sensor (TS0201 - pvxx/ZigbeeTLc)',
        fromZigbee: [fz.temperature, fz.humidity, fz.battery],
        toZigbee: [],
        exposes: [ e.battery(), e.temperature(), e.humidity()],
        extend: [
            quirkAddEndpointCluster({
                endpointID: 1,
                outputClusters: [
                    'genOta',
                ],
                inputClusters: [
                    'genBasic',
                    'genPowerCfg',
                    'genIdentify',
                    'genPollCtrl',
                    'hvacUserInterfaceCfg',
                    'msTemperatureMeasurement',
                    'msRelativeHumidity',
                ],
            }),
            pvvx.tempCalibration,
            pvvx.humidityCalibration,
            pvvx.measurementInterval
        ],
        ota: ota.zigbeeOTA,
        configure: async (device, coordinatorEndpoint, logger) => {
            const endpoint = device.getEndpoint(1);
            const bindClusters = ['msTemperatureMeasurement', 'msRelativeHumidity', 'genPowerCfg', 'genPollCtrl'];
            await reporting.bind(endpoint, coordinatorEndpoint, bindClusters);
            await reporting.temperature(endpoint, {min: 10, max: constants.repInterval.MINUTES_5, change: 10});
            await reporting.humidity(endpoint, {min: 10, max: constants.repInterval.MINUTES_5, change: 50});
            await reporting.batteryPercentageRemaining(endpoint);
            device.powerSource = 'Battery';
            device.save();
        },
    },
]

module.exports = definitions;
pvvx commented 2 months ago

Thank you!

'genPollCtrl' is disabled. Some coordinates set a constant "longPollInterval" period of 6 seconds or less, ignoring the "longPollIntervalMin" value (=10 sec). This greatly affects consumption. Current implementations of Zigbee programs are a complete mess. 'genPollCtrl' has been removed and a fixed value equal to "measurement Interval" has been applied. And this can lead to other consequences for some coordinators and routers... Z2M and ZHA do not fully support the Zigbee 3.0 specification.

In Z2M, settings are made by device name, and not by program version, manufacturer identifiers and device hardware version. Although OTA works based on the device's digital identifiers. This causes conflicts with different versions of Zigbee devices, including Tuya and devices from other manufacturers. There are no such problems in ZHA. And users do not need to be able to program special scripts for typical Zigbee 3.0 devices. Users need to beg to register a new Zigbee 3.0 device in Z2M :)