Ich habe mich doch noch einmal hingesetzt und einen Konverter gebastelt.
Ich habe allerdings nur alles zusammenkopiert und mangels Zeit noch nichts wirklich getestet.
Auf den ersten Blick sieht es bei mir aber ganz gut aus.
Grundlegend habe ich mich an der üblichen Vorgehensweise orientiert:
https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html
Dabei hatte ich aber nicht alle Besonderheiten für Tuya-Geräte beachtet:
https://www.zigbee2mqtt.io/advanced/support-new-devices/02_support_new_tuya_devices.html
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 reporting = require('zigbee-herdsman-converters/lib/reporting');
const modernExtend = require('zigbee-herdsman-converters/lib/modernExtend');
const e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const definition = {
zigbeeModel: ['TS0601'],
fingerprint: tuya.fingerprint('TS0601', [
'_TZE200_9xfjixap' /* model: 'ME167', vendor: 'AVATTO' */,
]),
model: 'ME167',
vendor: 'Tuya',
description: 'Thermostatic radiator valve',
extend: [],
meta: {},
fromZigbee: [tuya.fz.datapoints],
toZigbee: [tuya.tz.datapoints],
whiteLabel: [
tuya.whitelabel('AVATTO', 'ME167', 'Thermostatic radiator valve', ['_TZE200_9xfjixap']),
],
onEvent: tuya.onEventSetTime,
configure: tuya.configureMagicPacket,
exposes: [
e.child_lock(),
e.battery_low(),
e
.climate()
.withSetpoint('current_heating_setpoint', 5, 35, 1, ea.STATE_SET)
.withLocalTemperature(ea.STATE)
.withSystemMode(['auto', 'heat', 'off'], ea.STATE_SET)
.withRunningState(['idle', 'heat'], ea.STATE)
.withLocalTemperatureCalibration(-9, 9, 1, ea.STATE_SET),
...tuya.exposes.scheduleAllDays(ea.STATE_SET, 'HH:MM/C HH:MM/C HH:MM/C HH:MM/C HH:MM/C HH:MM/C'),
e
.binary('scale_protection', ea.STATE_SET, 'ON', 'OFF')
.withDescription(
'If the heat sink is not fully opened within ' +
'two weeks or is not used for a long time, the valve will be blocked due to silting up and the heat sink will not be ' +
'able to be used. To ensure normal use of the heat sink, the controller will automatically open the valve fully every ' +
'two weeks. It will run for 30 seconds per time with the screen displaying "Ad", then return to its normal working state ' +
'again.',
),
e
.binary('frost_protection', ea.STATE_SET, 'ON', 'OFF')
.withDescription(
'When the room temperature is lower than 5 °C, the valve opens; when the temperature rises to 8 °C, the valve closes.',
),
e.numeric('error', ea.STATE).withDescription('If NTC is damaged, "Er" will be on the TRV display.'),
],
meta: {
tuyaDatapoints: [
[2, 'system_mode', tuya.valueConverterBasic.lookup({auto: tuya.enum(0), heat: tuya.enum(1), off: tuya.enum(2)})],
[3, 'running_state', tuya.valueConverterBasic.lookup({heat: tuya.enum(0), idle: tuya.enum(1)})],
[4, 'current_heating_setpoint', tuya.valueConverter.divideBy10],
[5, 'local_temperature', tuya.valueConverter.divideBy10],
[7, 'child_lock', tuya.valueConverter.lockUnlock],
[28, 'schedule_monday', tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(1)],
[29, 'schedule_tuesday', tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(2)],
[30, 'schedule_wednesday', tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(3)],
[31, 'schedule_thursday', tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(4)],
[32, 'schedule_friday', tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(5)],
[33, 'schedule_saturday', tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(6)],
[34, 'schedule_sunday', tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(7)],
[35, null, tuya.valueConverter.errorOrBatteryLow],
[36, 'frost_protection', tuya.valueConverter.onOff],
[39, 'scale_protection', tuya.valueConverter.onOff],
[47, 'local_temperature_calibration', tuya.valueConverter.localTempCalibration2],
],
},
};
module.exports = definition;