Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New device support]: Smart home radiator valves TRV-705ZB (BEOK Controls) #24247

Open
mrumskiy opened this issue Oct 6, 2024 · 23 comments
Open
Labels
new device support New device support request

Comments

@mrumskiy
Copy link

mrumskiy commented Oct 6, 2024

Link

https://www.beok-controls.com/thermostatic-radiator-valve/zigbee-trv-actuator/zigbee-smart-home-radiator-valves.html

Database entry

{"id":19,"type":"EndDevice","ieeeAddr":"0xa4c138dd1abf3cbc","nwkAddr":26479,"manufId":4417,"manufName":"_TZE284_ltwbm23f","powerSource":"Battery","modelId":"TS0601","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[4,5,61184,0,60672],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65487":14400,"65503":"b��.\u0011","65506":56,"65508":0,"modelId":"TS0601","manufacturerName":"_TZE284_ltwbm23f","powerSource":3,"zclVersion":3,"appVersion":77,"stackVersion":0,"hwVersion":1,"dateCode":""}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":77,"stackVersion":0,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"meta":{},"lastSeen":1728223302713}

Zigbee2MQTT version

1.40.2

Comments

I tried to add this new device but appears in the list as not supported yet. Please include this device in the list of supported devices

External definition

const definition = {
    zigbeeModel: ['TS0601'],
    model: 'TS0601',
    vendor: '_TZE284_ltwbm23f',
    description: 'Automatically generated definition',
    extend: [],
    meta: {},
};

module.exports = definition;

What does/doesn't work with the external definition?

It shows that the device is Not supported

@mrumskiy mrumskiy added the new device support New device support request label Oct 6, 2024
@larryson309
Copy link

Hi, I have the same wish! Thanks for it!

@dobmeierr
Copy link

dobmeierr commented Oct 11, 2024

Hello, i have the same unsupperted device

#24167

@4ug3r
Copy link

4ug3r commented Oct 22, 2024

Link

https://www.beok-controls.com/thermostatic-radiator-valve/zigbee-trv-actuator/zigbee-smart-home-radiator-valves.html

Database entry

{"id":19,"type":"EndDevice","ieeeAddr":"0xa4c138dd1abf3cbc","nwkAddr":26479,"manufId":4417,"manufName":"_TZE284_ltwbm23f","powerSource":"Battery","modelId":"TS0601","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[4,5,61184,0,60672],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65487":14400,"65503":"b��.\u0011","65506":56,"65508":0,"modelId":"TS0601","manufacturerName":"_TZE284_ltwbm23f","powerSource":3,"zclVersion":3,"appVersion":77,"stackVersion":0,"hwVersion":1,"dateCode":""}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":77,"stackVersion":0,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"meta":{},"lastSeen":1728223302713}

Zigbee2MQTT version

1.40.2

Comments

I tried to add this new device but appears in the list as not supported yet. Please include this device in the list of supported devices

External definition

const definition = {
    zigbeeModel: ['TS0601'],
    model: 'TS0601',
    vendor: '_TZE284_ltwbm23f',
    description: 'Automatically generated definition',
    extend: [],
    meta: {},
};

module.exports = definition;

What does/doesn't work with the external definition?

It shows that the device is Not supported

Go for it (:

https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_2-creating-the-external-definition

@larryson309
Copy link

Because it is a tuya device, I created this beok.js - File - but it doesn't work. I don't see anything in the debug log file. I'm new in any programming, and my english isn't the best.

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 = {
    // Since a lot of Tuya devices use the same modelID, but use different datapoints
    // it's necessary to provide a fingerprint instead of a zigbeeModel
    fingerprint: [
        {
            // The model ID from: Device with modelID 'TS0601' is not supported
            // You may need to add \u0000 at the end of the name in some cases
            modelID: 'TRV-705ZB',
            // The manufacturer name from: Device with modelID 'TS0601' is not supported.
            manufacturerName: '_TZE284_ltwbm23f',
        },
    ],
    model: 'TRV-705ZB',
    vendor: '_TZE284_ltwbm23f',
    description: 'Fill in a description of the device here',
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    onEvent: tuya.onEventSetTime, // Add this if you are getting no converter for 'commandMcuSyncTime'
    configure: tuya.configureMagicPacket,
    exposes: [
        // Here you should put all functionality that your device exposes
    ],
    meta: {
        // All datapoints go in here
        tuyaDatapoints: [],
    },
    extend: [
        // A preferred new way of extending functionality.
    ],
};

module.exports = definition;

@adelaiglesia
Copy link

Hi, @larryson309, @mrumskiy, @dobmeierr, i have bought one of this valves. I have crafted a basic converter to use. This is not a complete converter which is ready to upload to zigbee2mqtt but it does the job. @4ug3r @Koenkk When i have a complete converter i will do a pull request.

For all the people that are searching 'TRV-705ZB zigbee2mqtt' or similar in Google i have to say that this is the best quality construcion tuya valve i have purchased, and i have 8 models. The battery cap doesn't drop, screen is well lit, buttons work...

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 e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');

const definition = {
    fingerprint: [
        {
            // The model ID from: Device with modelID 'TS0601' is not supported
            // You may need to add \u0000 at the end of the name in some cases
            modelID: 'TS0601',
            // The manufacturer name from: Device with modelID 'TS0601' is not supported.
            manufacturerName: '_TZE284_ltwbm23f',
        },
    ],
    model: 'TRV-705ZB',
    vendor: 'TuYa',
    description: 'Thermostat Radiator Valve',
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    onEvent: tuya.onEventSetTime, // Add this if you are getting no converter for 'commandMcuSyncTime'
    configure: tuya.configureMagicPacket,
    exposes: [
        e.climate()
            .withLocalTemperatureCalibration(-5, 5, 0.1, ea.STATE_SET)
            .withLocalTemperature(ea.STATE).withSetpoint('current_heating_setpoint', 5, 30, 0.5, ea.STATE_SET),
        e.enum('preset', ea.STATE_SET, ['off', 'antifrost', 'eco', 'comfort', 'program', 'valve_open']).withDescription('Off deactivates valve operation, '
            + 'the other modes presets when setting on 5º (antifrost), from 15º (eco) and from 20º (comfort). ' + 
            'It is better to set directly the temperature to deactivate off mode.'),
        e.enum('valve_status', ea.STATE_SET, ['valve_closed', 'valve_open']).withDescription('Off deactivates valve operation, '
            + 'the other modes presets when setting on 5º (antifrost), from 15º (eco) and from 20º (comfort). ' + 
            'It is better to set directly the temperature to deactivate off mode.'),
        e.child_lock(),
    ],
    
    meta: {
        tuyaDatapoints: [
            [2, 'preset', tuya.valueConverterBasic.lookup({
                'off': tuya.enum(0),
                'antifrost': tuya.enum(1),
                'eco': tuya.enum(2),
                'comfort': tuya.enum(3),
                'program': tuya.enum(4),
                'valve_open': tuya.enum(5),
            })],
            [2, 'valve_status', tuya.valueConverterBasic.lookup({
                'valve_closed': tuya.enum(0),
                'auto': tuya.enum(1),
                'auto': tuya.enum(2),
                'auto': tuya.enum(4),
                'auto': tuya.enum(3),
                'valve_open': tuya.enum(5),
            }, 'auto')],
            [4, 'current_heating_setpoint', tuya.valueConverter.divideBy10],
            [5, 'local_temperature', tuya.valueConverter.divideBy10],
            [7, 'child_lock', tuya.valueConverter.lockUnlock],
            [47, 'local_temperature_calibration', tuya.valueConverter.localTempCalibration1],
        ],
    },
};

module.exports = definition;

For zigbee2mqtt starting guys:

  1. Copy this in the zigbee2mqtt config folder (The same folder where configuration.yaml is) and save it as tuya_trv705.js
  2. Modify configuration.yaml adding
external_converters:
  - tuya_trv705.js

If you have in your file an 'external_converters' add bellow the file line.

Bye

@harryzz
Copy link

harryzz commented Dec 8, 2024

Some additional endpoint added:

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 e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');

const definition = {
    fingerprint: [
        {
            // The model ID from: Device with modelID 'TS0601' is not supported
            // You may need to add \u0000 at the end of the name in some cases
            modelID: 'TS0601',
            // The manufacturer name from: Device with modelID 'TS0601' is not supported.
            manufacturerName: '_TZE284_ltwbm23f',
        },
    ],
    model: 'TRV-705ZB',
    vendor: 'TuYa',
    description: 'Thermostat Radiator Valve',
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    onEvent: tuya.onEventSetTime, // Add this if you are getting no converter for 'commandMcuSyncTime'
    configure: tuya.configureMagicPacket,
    exposes: [
        e.battery(),
        e.child_lock(),
        e.position(),
        e.window_detection(),
        e.binary('window', ea.STATE, 'OPEN', 'CLOSE').withDescription('Window status closed or open '),        
        e.binary('valve_state', ea.STATE, 'OPEN', 'CLOSE').withDescription('Valve status closed or opened '),        
        e.climate()
            .withLocalTemperatureCalibration(-5, 5, 0.1, ea.STATE_SET)
            .withLocalTemperature(ea.STATE)
            .withSetpoint('current_heating_setpoint', 5, 30, 0.5, ea.STATE_SET)
            .withPreset( ['off', 'antifrost', 'eco', 'comfort', 'program', 'valve_open'],
                'Off deactivates valve operation, ' +
                'the other modes presets when setting on 5º (antifrost), from 15º (eco) and from 20º (comfort). ' + 
                'It is better to set directly the temperature to deactivate off mode.'),
        e.enum('valve_status', ea.STATE_SET, ['valve_closed', 'valve_open']).withDescription('Off deactivates valve operation, '
            + 'the other modes presets when setting on 5º (antifrost), from 15º (eco) and from 20º (comfort). ' + 
            'It is better to set directly the temperature to deactivate off mode.'),
        ...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.enum('display_brightness', ea.STATE_SET, ['high', 'medium', 'low']).withDescription('Display brightness'),
        e.enum('screen_orientation', ea.STATE_SET, ['up', 'down']).withDescription('Screen orientation'),
        e
        .enum('mode', ea.STATE_SET, ['comfort', 'eco'])
        .withDescription(
            'Hysteresis - comfort > switches off/on exactly at reached ' +
                'temperature with valve smooth from 0 to 100%, eco > 0.5 degrees above or below, valve either 0 or 100%',
        ),
    ],
    
    meta: {
        tuyaDatapoints: [
            [2, 'preset', tuya.valueConverterBasic.lookup({
                'off': tuya.enum(0),
                'antifrost': tuya.enum(1),
                'eco': tuya.enum(2),
                'comfort': tuya.enum(3),
                'program': tuya.enum(4),
                'valve_open': tuya.enum(5),
            })],
            [2, 'valve_status', tuya.valueConverterBasic.lookup({
                'valve_closed': tuya.enum(0),
                'auto': tuya.enum(1),
                'auto': tuya.enum(2),
                'auto': tuya.enum(4),
                'auto': tuya.enum(3),
                'valve_open': tuya.enum(5),
            }, 'auto')],
            [
                3,
                'valve_state',
                tuya.valueConverterBasic.lookup({
                    CLOSE: tuya.enum(0),
                    OPEN: tuya.enum(1),
                }),
            ],
            [4, 'current_heating_setpoint', tuya.valueConverter.divideBy10],
            [5, 'local_temperature', tuya.valueConverter.divideBy10],
            [6, 'battery', tuya.valueConverter.raw],
            [7, 'child_lock', tuya.valueConverter.lockUnlock],
            [14, 'window_detection', tuya.valueConverter.onOff],
            [
                15,
                'window',
                tuya.valueConverterBasic.lookup({
                    CLOSE: tuya.enum(0),
                    OPEN: tuya.enum(1),
                }),
            ],            
            [47, 'local_temperature_calibration', tuya.valueConverter.localTempCalibration1],
            [102, 'schedule_monday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV602Z_WithDayNumber(1)],
            [103, 'schedule_tuesday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV602Z_WithDayNumber(2)],
            [104, 'schedule_wednesday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV602Z_WithDayNumber(3)],
            [105, 'schedule_thursday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV602Z_WithDayNumber(4)],
            [106, 'schedule_friday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV602Z_WithDayNumber(5)],
            [107, 'schedule_saturday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV602Z_WithDayNumber(6)],
            [108, 'schedule_sunday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV602Z_WithDayNumber(7)],            
            [111, 'display_brightness', tuya.valueConverterBasic.lookup({high: tuya.enum(0), medium: tuya.enum(1), low: tuya.enum(2)})],
            [
                113,
                'screen_orientation',
                tuya.valueConverterBasic.lookup({
                    up: tuya.enum(0),
                    down: tuya.enum(1),
                }),
            ],            
            [114, 'position', tuya.valueConverter.divideBy10],
            [127, 'mode', tuya.valueConverterBasic.lookup({comfort: tuya.enum(0), eco: tuya.enum(1)})],

        ],
    },
};

module.exports = definition;

more

Temp UpperLimit
9 - temp*10 (20-35) step:1

antifrost protection
122 - true/false

Mode set temperature
comfort mode:
119 - temp*10 (15.5 - 30.0) step: 0.5
eco mode:
120 - temp*10  (5.5 - 19.5) step:0.5
antifrost mode:
121 - temp*10  (5 - 14.5) step: 0.5

regards,
Zahari

@tco99ttocs
Copy link

and how could this be converted to a ZHA python file?

@Lestat-GitHub
Copy link

Hi @harryzz,

thanks for all that new endpoints that make this device usable !

Can you provide the "thermostatScheduleDayMultiDP_TRV602Z_WithDayNumber" function in order to be able to use the schedule ?

Thanks.

@Lestat-GitHub
Copy link

I did some tests (got the function from current master branch) but it's not working... the function does not correctly handle the temperatures on schedule (30° is rounded to 4.4 => 300 written on 2 bytes).

I tried to adapt the function but without success...

Hope someone get mode luck on this as this TRV is exactly what I need in terms of features.

BTW: The battery does not report anything... maybe wrong DPID.

Thanks for any help on supporting this device.

@harryzz
Copy link

harryzz commented Dec 31, 2024

I did some tests (got the function from current master branch) but it's not working... the function does not correctly handle the temperatures on schedule (30° is rounded to 4.4 => 300 written on 2 bytes).

I tried to adapt the function but without success...

Hope someone get mode luck on this as this TRV is exactly what I need in terms of features.

BTW: The battery does not report anything... maybe wrong DPID.

Thanks for any help on supporting this device.

Hi

I use the converter as described by @adelaiglesia. Also, to find the additional endpoints I used "tuya-mqtt" modified by me to support the zigbee tuya coordinators ( https://github.com/harryzz/tuya-mqtt ) . For me battery and scheduler displayed correctly.

{
    "battery": 67,
    "child_lock": "LOCK",
    "current_heating_setpoint": 5,
    "display_brightness": "low",
    "last_seen": "2024-12-31T08:23:25+02:00",
    "linkquality": 138,
    "local_temperature": 18,
    "local_temperature_calibration": 0,
    "mode": "eco",
    "position": 0,
    "preset": "off",
    "schedule_friday": "06:00/20.0 08:00/15.0 12:00/20.0 14:00/15.0 18:00/20.0 22:00/15.0",
    "schedule_monday": "06:00/20.0 08:00/15.0 12:00/20.0 14:00/15.0 18:00/20.0 22:00/15.0",
    "schedule_saturday": "06:00/20.0 08:00/15.0 12:00/20.0 14:00/15.0 18:00/20.0 22:00/15.0",
    "schedule_sunday": "06:00/20.0 08:00/15.0 12:00/20.0 14:00/15.0 18:00/20.0 22:00/15.0",
    "schedule_thursday": "06:00/20.0 08:00/15.0 12:00/20.0 14:00/15.0 18:00/20.0 22:00/15.0",
    "schedule_tuesday": "06:00/20.0 08:00/15.0 12:00/20.0 14:00/15.0 18:00/20.0 22:00/15.0",
    "schedule_wednesday": "06:00/20.0 08:00/15.0 12:00/20.0 14:00/15.0 18:00/20.0 22:00/15.0",
    "screen_orientation": "up",
    "valve_state": "CLOSE",
    "valve_status": "valve_closed",
    "window": "CLOSE",
    "window_detection": "OFF"
}

I don't have in-depth knowledge of the zigbee2mqtt source so I can't help you more if some of the endpoints do not work correctly.

regards,
Zahari

@harryzz
Copy link

harryzz commented Dec 31, 2024

And about schedule - not exactly the same as this function: thermostatScheduleDayMultiDP_TRV602Z_WithDayNumber.
probably something like that:

02  c16830c8  c1e02096  c2d030c8  c3482096  c43830c8  c5282096 //08:00 - eco

02  c16830c8  a1e04096  c2d030c8  c3482096  c43830c8  c5282096 //08:00 - manual 15

02  c16830c8  a1e0412c  c2d030c8  c3482096  c43830c8  c5282096 //08:00 - manual 30

02  c16830c8  c1e0312c  c2d030c8  c3482096  c43830c8  c5282096 //08:00 - comfort

02  c16830c8  c1e0112c  c2d030c8  c3482096  c43830c8  c5282096 //08:00 - antifrost

02  c16830c8  c16830c8  c16830c8  c16830c8  c16830c8  c16830c8 // schedule only at 06:00
02  c16830c8  c1e02096  c3482096  c43830c8  c5282096  c5282096 // deleted schedule at 12:00


first byte: day of week - start from monday = 1

next: 6 times 4 bytes

a[index]&0xf0 = [0xa0=manual, 0xc0=programmed mode]?
((a[index]&0x0f) << 8) + a[index+1] = minutes after midnight
a[index+2]&0xf0 = mode [ 0x10=antifrost, 0x20=eco, 0x30=comfort, 0x40=manual ]
(((a[index+2]&0x0f) << 8 ) + a[index+3] ) / 10 = desired temp in manual mode

regards,
Zahari

@Lestat-GitHub
Copy link

Thanks a lot for your message that made me "insist" on the device, and after an update of zigbee2mqtt (to 1.42.0, I was not on the last 😞 ) + a reset on the device, the battery level started to report correctly...

The schedule was still not working because only the last byte of converted temperature was sent so for 30° you get 4.5° on schedule (300 - 255) with the TRV602Z function.

You last message is just gold !!!

Thanks a lot, I will try to send a raw payload with a desired value to check is that works correctly on the device, and if so I will have to improve my JS skills to create a converter

I try to explain your discovery in human readable format using ascii art 😄

<day> <program 1> <program 2> <program 3> <program 4> <program 5> <program 6>
 02   c1 68 30 c8
             ^ ^^-------> temperature * 10 => 0xc8 => 200 => 20.0
            ^-----------> 1 = Antifrost / 2 = Eco / 3 = Comfort / 4 = Manual
       ^ ^^-------------> minutes after midnight 0x168 => 360 => 6:00
      ^-----------------> a = manual / c = programmed
 ^^---------------------> day of week starting on Monday (here Tuesday)

@Lestat-GitHub
Copy link

Hi,

I've created a converter based on the 602Z. Now the schedule works but for an unknown reason, I have a shift on days. The schedule for Thursday is controlled by the Tuesday (day 2) line... I have to determine if there is a pattern or not...

In the meantime, please find attached my converter. Don't hesitate to make comments as the JS is not my native language 😄

tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB = {
    from: (v) => {
        const schedule = [];
        for (let index = 1; index < 24; index = index + 4) {
            const minutesSinceMidnight = parseInt((v[index] & 0x0f) << 8 | v[index + 1]);
            const temperature = parseFloat((v[index + 2] & 0x0f) << 8 | v[index + 3]) / 10.0;

            const hour = Math.floor(minutesSinceMidnight / 60);
            const minutes = minutesSinceMidnight % 60;

            schedule.push(
                String(hour).padStart(2, '0') + ':' + String(minutes).padStart(2, '0') + '/' + temperature.toFixed(1),
            );
        }
        return schedule.join(' ');
    },
    to: (v) => {
        const payload = [];
        const transitions = v.split(' ');
        if (transitions.length != 6) {
            throw new Error('Invalid schedule: there should be 6 transitions');
        }
        for (const transition of transitions) {
            const timeTemp = transition.split('/');
            if (timeTemp.length != 2) {
                throw new Error('Invalid schedule: wrong transition format: ' + transition);
            }
            const hourMin = timeTemp[0].split(':');
            const hour = parseInt(hourMin[0]);
            const min = parseInt(hourMin[1]);
            const temperature = Math.floor(parseFloat(timeTemp[1]) * 10);
            if (hour < 0 || hour > 24 || min < 0 || min > 60 || temperature < 50 || temperature > 300) {
                throw new Error('Invalid hour, minute or temperature of: ' + transition);
            }

            const minutesSinceMidnight = hour * 60 + min;

            const firstByte = 0xa0 | (minutesSinceMidnight >> 8);
            const secondByte = minutesSinceMidnight & 0xff;
            const thirdByte = 0x40 | (temperature >> 8);
            const fourthByte = temperature & 0xff;

            payload.push(firstByte, secondByte, thirdByte, fourthByte);
        }
        return payload;
    },
}

tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber = (dayNum) => {
    return {
        from: (v) => tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB.from(v),
        to: (v) => {
            const data = tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB.to(v);
            data.unshift(dayNum);
            return data;
        },
    };
}

And then my config is

...
[102, 'schedule_monday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(1)],
[103, 'schedule_tuesday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(2)],
[104, 'schedule_wednesday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(3)],
[105, 'schedule_thursday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(4)],
[106, 'schedule_friday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(5)],
[107, 'schedule_saturday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(6)],
[108, 'schedule_sunday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(7)],
...

@harryzz
Copy link

harryzz commented Jan 2, 2025

Hi
First thanks for convertor.
About shift days try to change in definition

onEvent: tuya.onEventSetTime to onEvent: tuya.onEventSetLocalTime

more endpoints:
switching hysteresis - 115 - x*10 ( 6 = 0.6 )
boost ( only in programming & vacation ) - 118 - 30,60,90,120 minutes
vacation mode - 117 - ( 1 - 60 days )?

regards,
Zahari

@harryzz
Copy link

harryzz commented Jan 2, 2025

There almost complete working convertor with fixed scheduler from @Lestat-GitHub (in manual mode for now ), some additional endpoints for setting different modes temperature.

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 e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const utils = require('zigbee-herdsman-converters/lib/utils');

tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB = {
    from: (v) => {
        const schedule = [];
        for (let index = 1; index < 24; index = index + 4) {
            const minutesSinceMidnight = parseInt((v[index] & 0x0f) << 8 | v[index + 1]);
            const temperature = parseFloat((v[index + 2] & 0x0f) << 8 | v[index + 3]) / 10.0;

            const hour = Math.floor(minutesSinceMidnight / 60);
            const minutes = minutesSinceMidnight % 60;

            schedule.push(
                String(hour).padStart(2, '0') + ':' + String(minutes).padStart(2, '0') + '/' + temperature.toFixed(1),
            );
        }
        return schedule.join(' ');
    },
    to: (v) => {
        const payload = [];
        const transitions = v.split(' ');
        if (transitions.length != 6) {
            throw new Error('Invalid schedule: there should be 6 transitions');
        }
        for (const transition of transitions) {
            const timeTemp = transition.split('/');
            if (timeTemp.length != 2) {
                throw new Error('Invalid schedule: wrong transition format: ' + transition);
            }
            const hourMin = timeTemp[0].split(':');
            const hour = parseInt(hourMin[0]);
            const min = parseInt(hourMin[1]);
            const temperature = Math.floor(parseFloat(timeTemp[1]) * 10);
            if (hour < 0 || hour > 24 || min < 0 || min > 60 || temperature < 50 || temperature > 300) {
                throw new Error('Invalid hour, minute or temperature of: ' + transition);
            }

            const minutesSinceMidnight = hour * 60 + min;

            const firstByte = 0xa0 | (minutesSinceMidnight >> 8);
            const secondByte = minutesSinceMidnight & 0xff;
            const thirdByte = 0x40 | (temperature >> 8);
            const fourthByte = temperature & 0xff;

            payload.push(firstByte, secondByte, thirdByte, fourthByte);
        }
        return payload;
    },
}

tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber = (dayNum) => {
    return {
        from: (v) => tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB.from(v),
        to: (v) => {
            const data = tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB.to(v);
            data.unshift(dayNum);
            return data;
        },
    };
}

const definition = {
    fingerprint: [
        {
            // The model ID from: Device with modelID 'TS0601' is not supported
            // You may need to add \u0000 at the end of the name in some cases
            modelID: 'TS0601',
            // The manufacturer name from: Device with modelID 'TS0601' is not supported.
            manufacturerName: '_TZE284_ltwbm23f',
        },
    ],
    model: 'TRV-705ZB',
    vendor: 'TuYa',
    description: 'Thermostat Radiator Valve',
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    onEvent: tuya.onEventSetLocalTime, // Add this if you are getting no converter for 'commandMcuSyncTime'
    configure: tuya.configureMagicPacket,
    exposes: [
        e.battery(),
        e.child_lock(),
        e.position(),
        e.window_detection(),
        e.binary('window', ea.STATE, 'OPEN', 'CLOSE').withDescription('Window status closed or open '),        
        e.binary('valve_state', ea.STATE, 'OPEN', 'CLOSE').withDescription('Valve status closed or opened '),        
        e.climate()
            .withLocalTemperatureCalibration(-5, 5, 0.1, ea.STATE_SET)
            .withLocalTemperature(ea.STATE)
            .withSetpoint('current_heating_setpoint', 5, 30, 0.5, ea.STATE_SET)
            .withPreset( ['off', 'antifrost', 'eco', 'comfort', 'program', 'valve_open'],
                'Off deactivates valve operation, ' +
                'the other modes presets when setting on 5º (antifrost), from 15º (eco) and from 20º (comfort). ' + 
                'It is better to set directly the temperature to deactivate off mode.'),
        e.binary('frost_protection', ea.STATE_SET, 'ON', 'OFF').withDescription('Antifreeze function'),        
        e.enum('valve_status', ea.STATE_SET, ['valve_closed', 'valve_open']).withDescription('Off deactivates valve operation, '
            + 'the other modes presets when setting on 5º (antifrost), from 15º (eco) and from 20º (comfort). ' + 
            'It is better to set directly the temperature to deactivate off mode.'),
        ...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.comfort_temperature().withValueMin(15.5).withValueMax(30.0).withValueStep(0.5).withDescription('Comfort mode temperature'),
        e.eco_temperature().withValueMin(5.5).withValueMax(20).withValueStep(0.5).withDescription('Eco mode temperature'),
        e.holiday_temperature().withValueMin(5).withValueMax(15).withValueStep(0.5).withDescription('Antifreeze mode temperature'),        
        e.enum('display_brightness', ea.STATE_SET, ['high', 'medium', 'low']).withDescription('Display brightness'),
        e.enum('screen_orientation', ea.STATE_SET, ['up', 'down']).withDescription('Screen orientation'),
        e
        .enum('mode', ea.STATE_SET, ['comfort', 'eco'])
        .withDescription(
            'Hysteresis - comfort > switches off/on exactly at reached ' +
                'temperature with valve smooth from 0 to 100%, eco > 0.5 degrees above or below, valve either 0 or 100%',
        ),
    ],
    
    meta: {
        tuyaDatapoints: [
            [2, 'preset', tuya.valueConverterBasic.lookup({
                'off': tuya.enum(0),
                'antifrost': tuya.enum(1),
                'eco': tuya.enum(2),
                'comfort': tuya.enum(3),
                'program': tuya.enum(4),
                'valve_open': tuya.enum(5),
            })],
            [2, 'valve_status', tuya.valueConverterBasic.lookup({
                'valve_closed': tuya.enum(0),
                'auto': tuya.enum(1),
                'auto': tuya.enum(2),
                'auto': tuya.enum(4),
                'auto': tuya.enum(3),
                'valve_open': tuya.enum(5),
            }, 'auto')],
            [
                3,
                'valve_state',
                tuya.valueConverterBasic.lookup({
                    CLOSE: tuya.enum(0),
                    OPEN: tuya.enum(1),
                }),
            ],
            [4, 'current_heating_setpoint', tuya.valueConverter.divideBy10],
            [5, 'local_temperature', tuya.valueConverter.divideBy10],
            [6, 'battery', tuya.valueConverter.raw],
            [7, 'child_lock', tuya.valueConverter.lockUnlock],
            [14, 'window_detection', tuya.valueConverter.onOff],
            [
                15,
                'window',
                tuya.valueConverterBasic.lookup({
                    CLOSE: tuya.enum(0),
                    OPEN: tuya.enum(1),
                }),
            ],            
            [47, 'local_temperature_calibration', tuya.valueConverter.localTempCalibration1],
            [102, 'schedule_monday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(1)],
            [103, 'schedule_tuesday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(2)],
            [104, 'schedule_wednesday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(3)],
            [105, 'schedule_thursday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(4)],
            [106, 'schedule_friday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(5)],
            [107, 'schedule_saturday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(6)],
            [108, 'schedule_sunday', tuya.valueConverter.thermostatScheduleDayMultiDP_TRV705ZB_WithDayNumber(7)],            
            [111, 'display_brightness', tuya.valueConverterBasic.lookup({high: tuya.enum(0), medium: tuya.enum(1), low: tuya.enum(2)})],
            [
                113,
                'screen_orientation',
                tuya.valueConverterBasic.lookup({
                    up: tuya.enum(0),
                    down: tuya.enum(1),
                }),
            ],            
            [114, 'position', tuya.valueConverter.divideBy10],
            [119, 'comfort_temperature', tuya.valueConverter.divideBy10], //--
            [120, 'eco_temperature', tuya.valueConverter.divideBy10], //--
            [121, 'holiday_temperature', tuya.valueConverter.divideBy10], //--
            [122, 'frost_protection', tuya.valueConverter.onOff], //--
            [127, 'mode', tuya.valueConverterBasic.lookup({comfort: tuya.enum(0), eco: tuya.enum(1)})],

        ],
    },
};

module.exports = definition;

regards,
Zahari

@John-Rookie
Copy link

John-Rookie commented Jan 8, 2025

Thanks ! During testing I discovered the following:
If I change the "child_lock" on the TVR, it is displayed correctly in the z2m.
However, a change in z2m is not adopted by the TVR.

Send Command to 0xXXXXXXXXXXXXXXXX failed with no error code (undefined)

J.R.

@Prickelkoenig
Copy link

Prickelkoenig commented Jan 9, 2025

After update to zigbee2mqtt 2.0.0 seems that the converter is unavailable/unknown to zigbee2mqtt. Where i have to store the converter now? Before that the converter works fine, also the battery sensor.

I found it look here...
[https://github.com//issues/25463]

@RobertRad
Copy link

The PR for this is here: Koenkk/zigbee-herdsman-converters#8581
I can coordinate the changes, but didn't write the code, so I need some help there :)

@Lestat-GitHub
Copy link

Lestat-GitHub commented Jan 11, 2025

Hi @RobertRad,

Thanks for coordinating the PR, but I don't totally agree the fact that you decided to silently change the name of the device to TRV-706Z (probably to match your own device model).

All this thread is about the TRV-705ZB and even if we know that many Tuya devices share the same hardware, I think that its a question of respect to keep other's contributions unchanged (converter name, device name, etc...) or at least to ask before pushing such a change.

Regards.

@RobertRad
Copy link

First of all, you're right. I should have mentioned the change and discuss it.

@RobertRad
Copy link

I thought, the 'B' in TRV-705ZB stands for 'Beok'.
So - if this assumption holds - this would make it a Whitelabel device of the Tuya device.

What is the full name of your device? 'Tuya TRV-705ZB' or 'Beok TRV-705ZB'?

@Lestat-GitHub
Copy link

The device is the one mentioned in this thread:

Seems like the ZB means "ZigBee" as there is a WiFi version named TRV-705WF
So if you want to stay generic, you can use TRV-705

@RobertRad
Copy link

Wow, I overlooked the first link in this thread.
I changed the accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new device support New device support request
Projects
None yet
Development

No branches or pull requests