Koenkk / zigbee-herdsman-converters

Collection of device converters to be used with zigbee-herdsman
MIT License
921 stars 3.06k forks source link

Issue adding new Thaleos radiator valve TS0601 vendor: '_TZE200_rv6iuyxb' #8062

Open kostya-fr opened 1 month ago

kostya-fr commented 1 month ago

Hello, I got same French valves by Thaleos-Connect, I have following definition :

const definition = {
    zigbeeModel: ['TS0601'],
    model: 'TS0601',
    vendor: '_TZE200_rv6iuyxb',
    description: 'Automatically generated definition',
    extend: [],
    meta: {},
};
I added following code (from #7998) into /config/zigbee2mqtt/TS0601.js : ```js zigbeeModel: ['TS0601'], const legacy = require('zigbee-herdsman-converters/lib/legacy'); const fz = {...require('zigbee-herdsman-converters/converters/fromZigbee'), legacy: require('zigbee-herdsman-converters/lib/legacy').fromZigbee}; const tz = {...require('zigbee-herdsman-converters/converters/toZigbee'), legacy: require('zigbee-herdsman-converters/lib/legacy').toZigbee}; const exposes = require('zigbee-herdsman-converters/lib/exposes'); const reporting = require('zigbee-herdsman-converters/lib/reporting'); const ota = require('zigbee-herdsman-converters/lib/ota'); const utils = require('zigbee-herdsman-converters/lib/utils'); const globalStore = require('zigbee-herdsman-converters/lib/store'); const tuya = require('zigbee-herdsman-converters/lib/tuya'); const e = exposes.presets; const ea = exposes.access; const definition = { zigbeeModel: ['TS0601'], fingerprint: [ {modelID: 'TS0601', manufacturerName: '_TZE200_rv6iuyxb'}, //Thaleos Connect ], model: 'TS0601_thermostat', vendor: 'Tuya', description: 'Radiator valve with thermostat', meta: {tuyaThermostatPreset: legacy.thermostatPresets, tuyaThermostatSystemMode: legacy.thermostatSystemModes3}, ota: ota.zigbeeOTA, onEvent: tuya.onEventSetLocalTime, fromZigbee: [legacy.fromZigbee.tuya_thermostat, fz.ignore_basic_report, fz.ignore_tuya_set_time], toZigbee: [ legacy.toZigbee.tuya_thermostat_child_lock, legacy.toZigbee.tuya_thermostat_window_detection, legacy.toZigbee.tuya_thermostat_valve_detection, legacy.toZigbee.tuya_thermostat_current_heating_setpoint, legacy.toZigbee.tuya_thermostat_auto_lock, legacy.toZigbee.tuya_thermostat_calibration, legacy.toZigbee.tuya_thermostat_min_temp, legacy.toZigbee.tuya_thermostat_max_temp, legacy.toZigbee.tuya_thermostat_boost_time, legacy.toZigbee.tuya_thermostat_comfort_temp, legacy.toZigbee.tuya_thermostat_eco_temp, legacy.toZigbee.tuya_thermostat_force_to_mode, legacy.toZigbee.tuya_thermostat_force, legacy.toZigbee.tuya_thermostat_preset, legacy.toZigbee.tuya_thermostat_window_detect, legacy.toZigbee.tuya_thermostat_schedule, legacy.toZigbee.tuya_thermostat_week, legacy.toZigbee.tuya_thermostat_schedule_programming_mode, legacy.toZigbee.tuya_thermostat_away_mode, legacy.toZigbee.tuya_thermostat_away_preset, ], exposes: [ e.child_lock(), e.window_detection(), e.binary('window_open', ea.STATE, true, false).withDescription('Window open?'), e.battery_low(), e.valve_detection(), e.position(), e .climate() .withSetpoint('current_heating_setpoint', 5, 35, 0.5, ea.STATE_SET) .withLocalTemperature(ea.STATE) .withSystemMode( ['heat', 'auto', 'off'], ea.STATE_SET, 'Mode of this device, in the `heat` mode the TS0601 will remain continuously heating, i.e. it does not regulate ' + 'to the desired temperature. If you want TRV to properly regulate the temperature you need to use mode `auto` ' + 'instead setting the desired temperature.', ) .withLocalTemperatureCalibration(-9, 9, 0.5, ea.STATE_SET) .withPreset(['schedule', 'manual', 'boost', 'complex', 'comfort', 'eco', 'away']) .withRunningState(['idle', 'heat'], ea.STATE), e.auto_lock(), e.away_mode(), e.away_preset_days(), e.boost_time(), e.comfort_temperature(), e.eco_temperature(), e.force(), e.max_temperature().withValueMin(16).withValueMax(70), e.min_temperature(), e.away_preset_temperature(), e.week(), e .text('workdays_schedule', ea.STATE_SET) .withDescription('Workdays schedule, 6 entries max, example: "00:20/5°C 01:20/5°C 6:59/15°C 18:00/5°C 20:00/5°C 23:30/5°C"'), e .text('holidays_schedule', ea.STATE_SET) .withDescription('Holidays schedule, 6 entries max, example: "00:20/5°C 01:20/5°C 6:59/15°C 18:00/5°C 20:00/5°C 23:30/5°C"'), ], }; module.exports = definition; ```

But it doesn't work for me. I added to zigbee2mqtt config :

external_converters:
  - /config/zigbee2mqtt/TS0601.js

after restarting zigbee2mqtt and adding device I keep message :


info 2024-10-02 08:16:06z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/0xa4cxxxxxxxxxxxxxxx/availability', payload '{"state":"online"}'
info 2024-10-02 08:16:06zh:controller: Interview for '0xa4cxxxxxxxxxxxxxxx' started
info 2024-10-02 08:16:06z2m: Device '0xa4cxxxxxxxxxxxxxxx' joined
info 2024-10-02 08:16:06z2m: Starting interview of '0xa4cxxxxxxxxxxxxxxx'
info 2024-10-02 08:16:07z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/bridge/event', payload '{"data":{"friendly_name":"0xa4cxxxxxxxxxxxxxxx","ieee_address":"0xa4cxxxxxxxxxxxxxxx"},"type":"device_joined"}'
info 2024-10-02 08:16:07z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/bridge/event', payload '{"data":{"friendly_name":"0xa4cxxxxxxxxxxxxxxx","ieee_address":"0xa4cxxxxxxxxxxxxxxx","status":"started"},"type":"device_interview"}'
info 2024-10-02 08:16:07zh:controller:device: Device '0xa4cxxxxxxxxxxxxxxx' is only compliant to revision '21' of the ZigBee specification (current revision: 23).
info 2024-10-02 08:16:10zh:controller: Succesfully interviewed '0xa4cxxxxxxxxxxxxxxx'
info 2024-10-02 08:16:10z2m: Successfully interviewed '0xa4cxxxxxxxxxxxxxxx', device has successfully been paired
warning 2024-10-02 08:16:10z2m: Device '0xa4cxxxxxxxxxxxxxxx' with Zigbee model 'TS0601' and manufacturer name '_TZE200_rv6iuyxb' is NOT supported, please follow https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html
info 2024-10-02 08:16:10z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/bridge/event', payload '{"data":{"friendly_name":"0xa4cxxxxxxxxxxxxxxx","ieee_address":"0xa4cxxxxxxxxxxxxxxx","status":"successful","supported":false},"type":"device_interview"}'

Originally posted by @kostya-fr in https://github.com/Koenkk/zigbee-herdsman-converters/issues/7998#issuecomment-2387298683

kostya-fr commented 1 month ago

I checked my model is TRV06 from this doc https://thaleos.com/wp-content/uploads/2024/06/Catalogue-Thaleos-Thermostat-Connecte-20.pdf.

So I updated script to similaire data from tuya.js, but still not working : ```js const legacy = require('zigbee-herdsman-converters/lib/legacy'); const fz = {...require('zigbee-herdsman-converters/converters/fromZigbee'), legacy: require('zigbee-herdsman-converters/lib/legacy').fromZigbee}; const tz = {...require('zigbee-herdsman-converters/converters/toZigbee'), legacy: require('zigbee-herdsman-converters/lib/legacy').toZigbee}; const exposes = require('zigbee-herdsman-converters/lib/exposes'); const reporting = require('zigbee-herdsman-converters/lib/reporting'); const ota = require('zigbee-herdsman-converters/lib/ota'); const utils = require('zigbee-herdsman-converters/lib/utils'); const globalStore = require('zigbee-herdsman-converters/lib/store'); const tuya = require('zigbee-herdsman-converters/lib/tuya'); const e = exposes.presets; const ea = exposes.access; const definition = { zigbeeModel: ['TS0601'], fingerprint: [ {modelID: 'TS0601', manufacturerName: '_TZE200_rv6iuyxb'}, //Thaleos Connect ], model: 'TS0601_thermostat_3', vendor: 'Tuya', description: 'Thermostatic radiator valve', fromZigbee: [tuya.fz.datapoints], toZigbee: [tuya.tz.datapoints], whiteLabel: [ tuya.whitelabel('AVATTO', 'ME167', 'Thermostatic radiator valve', ['_TZE200_bvu2wnxz', '_TZE200_6rdj8dzm']), tuya.whitelabel('AVATTO', 'ME168', 'Thermostatic radiator valve', ['_TZE200_p3dbf6qs', '_TZE200_rxntag7i']), tuya.whitelabel('EARU', 'TRV06', 'Smart thermostat module', ['_TZE200_yqgbrdyo', '_TZE200_rxq4iti9']), tuya.whitelabel('AVATTO', 'TRV06', 'Smart thermostat module', ['_TZE200_rv6iuyxb']), ], 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; ```
kostya-fr commented 1 month ago

OK, I got a external_converters path issues, IDK why it doesn't work I specified directly in /config/zigbee2mqtt/configuration.yaml and it worked perfectly for me. Here is the code :


const legacy = require('zigbee-herdsman-converters/lib/legacy');
const fz = {...require('zigbee-herdsman-converters/converters/fromZigbee'), legacy: require('zigbee-herdsman-converters/lib/legacy').fromZigbee};
const tz = {...require('zigbee-herdsman-converters/converters/toZigbee'), legacy: require('zigbee-herdsman-converters/lib/legacy').toZigbee};
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const ota = require('zigbee-herdsman-converters/lib/ota');
const utils = require('zigbee-herdsman-converters/lib/utils');
const globalStore = require('zigbee-herdsman-converters/lib/store');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const e = exposes.presets;
const ea = exposes.access;

const definition = {
    zigbeeModel: ['v6iuyxb'],
    fingerprint: [
        {modelID: 'TS0601', manufacturerName: '_TZE200_rv6iuyxb'}, //Thaleos Connect
    ],
    model: 'TS0601_thermostat_3',
    vendor: 'Tuya',
    description: 'Thermostatic radiator valve',
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    whiteLabel: [        
        tuya.whitelabel('AVATTO', 'TRV06', 'Smart thermostat module', ['_TZE200_rv6iuyxb']),
    ],
    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;

image

State :

{
    "battery_low": false,
    "child_lock": "UNLOCK",
    "current_heating_setpoint": 19,
    "frost_protection": "ON",
    "linkquality": 180,
    "local_temperature": 21,
    "local_temperature_calibration": 3,
    "running_state": "idle",
    "scale_protection": "OFF",
    "schedule_friday": "06:00/21.0 08:00/16.0 12:00/21.0 14:00/16.0",
    "schedule_monday": "06:00/21.0 08:00/16.0 12:00/21.0 14:00/16.0",
    "schedule_saturday": "06:00/21.0 08:00/16.0 12:00/21.0 14:00/16.0",
    "schedule_sunday": "06:00/21.0 08:00/16.0 12:00/21.0 14:00/16.0",
    "schedule_thursday": "06:00/21.0 08:00/16.0 12:00/21.0 14:00/16.0",
    "schedule_tuesday": "06:00/21.0 08:00/16.0 12:00/21.0 14:00/16.0",
    "schedule_wednesday": "06:00/21.0 08:00/16.0 12:00/21.0 14:00/16.0",
    "system_mode": "auto"
}

But documentation says it also could detect windows open/close state. I think it would be nice to add too, so I will work on later this week.

Can somebody help me to understand : tuyaDatapoints and tuya.valueConverters ... ?

martinbonneau commented 1 month ago

Hi @kostya-fr , thanks for the configuration you provided, i can confirm that's working well on my instance too. However, i needed to adapt the zigbeeModel & fingerprint for Zigbee2Mqtt to see my device as supported. My définition from auto_generated_definition :

    zigbeeModel: ['TS0601'],
    model: 'TS0601',
    vendor: '_TZE200_ow09xlxm',

Probably the vendor suffix is kinda random. Idk if it's possible to support _TZE200_*

Anyway, thanks :)

wrauner commented 1 month ago

I just got AVATTO TRV06 radiator valves, they reported as "unsupported" in my z2m (1.40.2) and managed to get them working thanks to @kostya-fr external converter.

Zigbee Model
    TS0601
Zigbee Manufacturer
    _TZE200_hvaxb2tc

The only change I made was the manufacturerName in the fingerprint and whiteLabel keys.

@martinbonneau it looks like the vendor suffix is somewhat random.