Koenkk / zigbee2mqtt

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

TuYa - Zigbee Radiator Valve - TZE200_zr9c0day #9721

Closed keroseneman closed 2 years ago

keroseneman commented 2 years ago

I tried creating the entry myself but was unable to determine each of the values, was unable to find a similar device that I would be able to use. Only 1 DP was being picked up "1" with value "0"

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 extend = require('zigbee-herdsman-converters/lib/extend'); 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 data points // it's usually 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: 'TS0601', // The manufacturer name from: Device with modelID 'TS0601' is not supported. manufacturerName: '_TZE200_zr9c0day' }, ], model: 'SEA802-Zigbee', vendor: 'Saswell', description: 'Thermostatic radiator valve', supports: 'thermostat, temperature', fromZigbee: [ fz.ignore_basic_report, // Add this if you are getting no converter for 'genBasic' fz.tuya_data_point_dump, // This is a debug converter, it will be described in the next part ], toZigbee: [ tz.tuya_data_point_test, // Another debug converter ], onEvent: tuya.setTime, // Add this if you are getting no converter for 'commandSetTimeRequest' configure: async (device, coordinatorEndpoint, logger) => { const endpoint = device.getEndpoint(1); await reporting.bind(endpoint, coordinatorEndpoint, ['genBasic']); }, exposes: [ // Here you should put all functionality that your device exposes ], };

module.exports = definition;

Information about the device + link

TS0601 by _TZE200_zr9c0day

I believe it to be this device but it registers as something completely different.

https://www.saswell.com/etrv-smart-radiator-thermostat-tuya-zigbee-3-0-thermostatic-radiator-valve-sea802_p107.html

data/database.db entry of the device

{"id":1,"type":"Coordinator","ieeeAddr":"0x00212effff053c68","nwkAddr":0,"manufId":4405,"epList":[1,242],"endpoints":{"1":{"profId":260,"epId":1,"devId":5,"inClusterList":[0,10,25],"outClusterList":[1,32,1280],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}},"242":{"profId":41440,"epId":242,"devId":100,"inClusterList":[],"outClusterList":[33],"clusters":{},"binds":[],"configuredReportings":[],"meta":{}}},"interviewCompleted":true,"meta":{},"lastSeen":null} {"id":4,"type":"EndDevice","ieeeAddr":"0x2c1165fffe5b4127","nwkAddr":38529,"manufId":4098,"manufName":"_TZE200_zr9c0day","powerSource":"Battery","modelId":"TS0601","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[0,4,5,61184],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"modelId":"TS0601","manufacturerName":"_TZE200_zr9c0day","powerSource":3,"zclVersion":3,"appVersion":85,"stackVersion":0,"hwVersion":1,"dateCode":""}}},"binds":[{"cluster":0,"type":"endpoint","deviceIeeeAddress":"0x00212effff053c68","endpointID":1}],"configuredReportings":[],"meta":{}}},"appVersion":85,"stackVersion":0,"hwVersion":1,"dateCode":"","zclVersion":3,"interviewCompleted":true,"meta":{"configured":821693351},"lastSeen":1637181636270,"useImplicitCheckin":true}

borsuk85 commented 2 years ago

Hi, my almost working solution (child_lock not working):


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

const test = {
    cluster: 'manuSpecificTuya',
    type: ['commandGetData', 'commandSetDataResponse'],
    convert: (model, msg, publish, options, meta) => {
        const dp = msg.data.dp;
        const value = tuya.getDataValue(msg.data.datatype, msg.data.data);
        let temperature;
        meta.logger.info(`Debug data: DP #${dp} with data ${JSON.stringify(msg.data)}`);
        switch(dp) {
            case tuya.dataPoints.saswellHeatingSetpoint:
                return {current_heating_setpoint: value / 10};
            case 3:
                return {heating: tuya.getDataValue(1, msg.data.data)};
            case tuya.dataPoints.saswellWindowDetection:
                return {window_detection: value ? 'ON' : 'OFF'};
            case tuya.dataPoints.saswellChildLock: 
                return {child_lock: value ? 'LOCK' : 'UNLOCK'};
            case tuya.dataPoints.saswellLocalTemp:
                return {local_temperature: value / 10}
            case tuya.dataPoints.saswellState:
                return {system_mode: value ? 'heat' : 'off'};
            case tuya.dataPoints.saswellBatteryLow:
                return {battery_low: value ? true : false};
            case tuya.dataPoints.saswellAntiScaling:
                return {anti_scaling: value ? 'ON' : 'OFF'}
            case tuya.dataPoints.saswellTempCalibration:
                return {local_temperature_calibration: value > 6 ? 0xFFFFFFFF - value : value};
            case tuya.dataPoints.saswellAwayMode:
                if (value) {
                    return {away_mode: 'ON', preset_mode: 'away'};
                } else {
                    return {away_mode: 'OFF', preset_mode: 'none'};
                }
            default: 
                meta.logger.warn(`No to dupa: Unrecognized DP #${
                    dp} with data ${JSON.stringify(msg.data)}`);
        }
    }
};

const definition = {
    zigbeeModel: ['TS0601'], // The model ID from: Device with modelID 'lumi.sens' is not supported.
    model: '_TZE200_zr9c0day', // Vendor model number, look on the device for a model number
    vendor: 'Tuya', // Vendor of the device (only used for documentation and startup logging)
    description: 'Thermostatic radiator valve', // Description of the device, copy from vendor site. (only used for documentation and startup logging)
    fromZigbee: [fz.ignore_basic_report, fz.ignore_tuya_set_time, test], // We will add this later
    toZigbee: [
        tz.saswell_thermostat_current_heating_setpoint, tz.saswell_thermostat_mode, tz.saswell_thermostat_away,
            tz.saswell_thermostat_child_lock, tz.saswell_thermostat_window_detection, tz.saswell_thermostat_frost_detection,
            tz.saswell_thermostat_calibration, tz.saswell_thermostat_anti_scaling, tz.tuya_thermostat_weekly_schedule
    ],
    exposes: [
        e.battery_low(),
        e.window_detection(),
        e.child_lock(),
        exposes.binary('window', ea.STATE, 'CLOSED', 'OPEN').withDescription('Window status closed or open '),
        exposes.binary('heating', ea.STATE, 'ON', 'OFF').withDescription('Device valve is open or closed (heating or not)'),
        exposes.climate()
                .withSetpoint('current_heating_setpoint', 5, 30, 0.5, ea.STATE_SET)
                .withLocalTemperature(ea.STATE)
                .withSystemMode(['heat', 'auto', 'off'], ea.STATE_SET)
                .withLocalTemperatureCalibration(-6, 6, 1, ea.STATE_SET)
                .withAwayMode()
        ], // Defines what this device exposes, used for e.g. Home Assistant discovery and in the frontend
};

module.exports = definition;`
keroseneman commented 2 years ago

Widze, ze borsuk tez ma nowy termostat. "meta.logger.warn(No to dupa: " no to dupa

Thanks! I will test it out, I'll be honest that I'm pretty lost with it.

Did a test, when importing the external converters receive the following error.

zigbee2mqtt@1.22.0-dev start node index.js (node:7425) UnhandledPromiseRejectionWarning: SyntaxError: missing ) after argument list (Use node --trace-warnings ... to show where the warning was created) (node:7425) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1) (node:7425) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. [10:40:07] INFO: Handing over control to Zigbee2mqtt Core ...

Alan

valve.js and configuration.yaml in same location, external converter added and when I do it crashes zigbee2mqtt

image image

Alan

borsuk85 commented 2 years ago

Zapomniałem o oddupianiu kodu ;) Try this:


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

const test = {
    cluster: 'manuSpecificTuya',
    type: ['commandGetData', 'commandSetDataResponse'],
    convert: (model, msg, publish, options, meta) => {
        const dp = msg.data.dp;
        const value = tuya.getDataValue(msg.data.datatype, msg.data.data);
        let temperature;
        meta.logger.info(`Debug data: DP #${dp} with data ${JSON.stringify(msg.data)}`);
        switch(dp) {
            case tuya.dataPoints.saswellHeatingSetpoint:
                return {current_heating_setpoint: value / 10};
            case 3:
                return {heating: tuya.getDataValue(1, msg.data.data)};
            case tuya.dataPoints.saswellWindowDetection:
                return {window_detection: value ? 'ON' : 'OFF'};
            case tuya.dataPoints.saswellChildLock: 
                return {child_lock: value ? 'LOCK' : 'UNLOCK'};
            case tuya.dataPoints.saswellLocalTemp:
                return {local_temperature: value / 10}
            case tuya.dataPoints.saswellState:
                return {system_mode: value ? 'heat' : 'off'};
            case tuya.dataPoints.saswellBatteryLow:
                return {battery_low: value ? true : false};
            case tuya.dataPoints.saswellAntiScaling:
                return {anti_scaling: value ? 'ON' : 'OFF'}
            case tuya.dataPoints.saswellTempCalibration:
                return {local_temperature_calibration: value > 6 ? 0xFFFFFFFF - value : value};
            case tuya.dataPoints.saswellAwayMode:
                if (value) {
                    return {away_mode: 'ON', preset_mode: 'away'};
                } else {
                    return {away_mode: 'OFF', preset_mode: 'none'};
                }
            default: 
                meta.logger.warn(`Something went wrong: Unrecognized DP #${dp} with data ${JSON.stringify(msg.data)}`);
        }
    }
};

const definition = {
    // zigbeeModel: ['TS0601'], // The model ID from: Device with modelID 'lumi.sens' is not supported.
    fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_zr9c0day'}],
    model: 'SEA801-Zigbee/SEA802-Zigbee', // Vendor model number, look on the device for a model number
    vendor: 'Tuya', // Vendor of the device (only used for documentation and startup logging)
    description: 'Thermostatic radiator valve', // Description of the device, copy from vendor site. (only used for documentation and startup logging)
    fromZigbee: [fz.ignore_basic_report, fz.ignore_tuya_set_time, test], // We will add this later
    toZigbee: [
        tz.saswell_thermostat_current_heating_setpoint, tz.saswell_thermostat_mode, tz.saswell_thermostat_away,
            tz.saswell_thermostat_child_lock, tz.saswell_thermostat_window_detection, tz.saswell_thermostat_frost_detection,
            tz.saswell_thermostat_calibration, tz.saswell_thermostat_anti_scaling, tz.tuya_thermostat_weekly_schedule
    ],
    exposes: [
        e.battery_low(),
        e.window_detection(),
        e.child_lock(),
        exposes.binary('window', ea.STATE, 'CLOSED', 'OPEN').withDescription('Window status closed or open '),
        exposes.binary('heating', ea.STATE, 'ON', 'OFF').withDescription('Device valve is open or closed (heating or not)'),
        exposes.climate()
                .withSetpoint('current_heating_setpoint', 5, 30, 0.5, ea.STATE_SET)
                .withLocalTemperature(ea.STATE)
                .withSystemMode(['heat', 'auto', 'off'], ea.STATE_SET)
                .withLocalTemperatureCalibration(-6, 6, 1, ea.STATE_SET)
                .withAwayMode()
        ], // Defines what this device exposes, used for e.g. Home Assistant discovery and in the frontend
};

module.exports = definition;
keroseneman commented 2 years ago

Something still isn't right, I'm unable to use the js... I wonder if it's something on m side though.

Figured it out, was right in front of me... blind FZ was missing, added it and it loaded up

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

const test = { cluster: 'manuSpecificTuya', type: ['commandGetData', 'commandSetDataResponse'], convert: (model, msg, publish, options, meta) => { const dp = msg.data.dp; const value = tuya.getDataValue(msg.data.datatype, msg.data.data); let temperature; meta.logger.info(Debug data: DP #${dp} with data ${JSON.stringify(msg.data)}); switch(dp) { case tuya.dataPoints.saswellHeatingSetpoint: return {current_heating_setpoint: value / 10}; case 3: return {heating: tuya.getDataValue(1, msg.data.data)}; case tuya.dataPoints.saswellWindowDetection: return {window_detection: value ? 'ON' : 'OFF'}; case tuya.dataPoints.saswellChildLock: return {child_lock: value ? 'LOCK' : 'UNLOCK'}; case tuya.dataPoints.saswellLocalTemp: return {local_temperature: value / 10} case tuya.dataPoints.saswellState: return {system_mode: value ? 'heat' : 'off'}; case tuya.dataPoints.saswellBatteryLow: return {battery_low: value ? true : false}; case tuya.dataPoints.saswellAntiScaling: return {anti_scaling: value ? 'ON' : 'OFF'} case tuya.dataPoints.saswellTempCalibration: return {local_temperature_calibration: value > 6 ? 0xFFFFFFFF - value : value}; case tuya.dataPoints.saswellAwayMode: if (value) { return {away_mode: 'ON', preset_mode: 'away'}; } else { return {away_mode: 'OFF', preset_mode: 'none'}; } default: meta.logger.warn(Something went wrong: Unrecognized DP #${dp} with data ${JSON.stringify(msg.data)}); } } };

const definition = { // zigbeeModel: ['TS0601'], // The model ID from: Device with modelID 'lumi.sens' is not supported. fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE200_zr9c0day'}], model: 'SEA801-Zigbee/SEA802-Zigbee', // Vendor model number, look on the device for a model number vendor: 'Tuya', // Vendor of the device (only used for documentation and startup logging) description: 'Thermostatic radiator valve', // Description of the device, copy from vendor site. (only used for documentation and startup logging) fromZigbee: [fz.ignore_basic_report, fz.ignore_tuya_set_time, test], // We will add this later toZigbee: [ tz.saswell_thermostat_current_heating_setpoint, tz.saswell_thermostat_mode, tz.saswell_thermostat_away, tz.saswell_thermostat_child_lock, tz.saswell_thermostat_window_detection, tz.saswell_thermostat_frost_detection, tz.saswell_thermostat_calibration, tz.saswell_thermostat_anti_scaling, tz.tuya_thermostat_weekly_schedule ], exposes: [ e.battery_low(), e.window_detection(), e.child_lock(), exposes.binary('window', ea.STATE, 'CLOSED', 'OPEN').withDescription('Window status closed or open '), exposes.binary('heating', ea.STATE, 'ON', 'OFF').withDescription('Device valve is open or closed (heating or not)'), exposes.climate() .withSetpoint('current_heating_setpoint', 5, 30, 0.5, ea.STATE_SET) .withLocalTemperature(ea.STATE) .withSystemMode(['heat', 'auto', 'off'], ea.STATE_SET) .withLocalTemperatureCalibration(-6, 6, 1, ea.STATE_SET) .withAwayMode() ], // Defines what this device exposes, used for e.g. Home Assistant discovery and in the frontend };

module.exports = definition;`

borsuk85 commented 2 years ago

@keroseneman Did you copy whole js content from my comment? There was a syntax error near: meta.logger.info.

keroseneman commented 2 years ago

@keroseneman Did you copy whole js content from my comment? There was a syntax error near: meta.logger.info.

Yea I copied it, can you edit your post and add the line "const fz = require('zigbee-herdsman-converters/converters/fromZigbee');"

borsuk85 commented 2 years ago

Ok done.

keroseneman commented 2 years ago

Hey @borsuk85 -

How do you configure the open window temperature? Or is it just off when the window is open?

I have a feeling it's not recognising the the window is open.

What about "Away Mode"?

I do see a long of "something went wrong" messages in logs.

Alan

borsuk85 commented 2 years ago

Hey @borsuk85 -

How do you configure the open window temperature? Or is it just off when the window is open?

I have a feeling it's not recognising the the window is open.

What about "Away Mode"?

I do see a long of "something went wrong" messages in logs.

Alan

I think its not configurable. Just when temperature drops rapidly, valve sets state window_detection = true. I'll try to put the valve outside the window.

I have no errors when i switch the away_mode switch. It just change current_heating_setpoint to 16C.

keroseneman commented 2 years ago

So if it's away it sets it to 16? I see, that's a decent temp.

Does it state that the window is open?

image

borsuk85 commented 2 years ago

Ok, looks like the window state is not exposed by the valve or uses different data point. I'll test it later.

github-actions[bot] commented 2 years ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days

mgwoj commented 2 years ago

I have added support for this valve - https://github.com/Koenkk/zigbee-herdsman-converters/pull/3594

Only data point #3 is not supported - now it is https://github.com/Koenkk/zigbee-herdsman-converters/pull/3596

warn  2021-12-31 17:07:18: zigbee-herdsman-converters:SaswellThermostat: Unrecognized DP #3 with data {"status":0,"transid":27,"dp":3,"datatype":4,"fn":0,"data":{"type":"Buffer","data":[0]}}
warn  2021-12-31 17:08:55: zigbee-herdsman-converters:SaswellThermostat: Unrecognized DP #3 with data {"status":0,"transid":27,"dp":3,"datatype":4,"fn":0,"data":{"type":"Buffer","data":[1]}}
warn  2021-12-31 17:14:38: zigbee-herdsman-converters:SaswellThermostat: Unrecognized DP #3 with data {"status":0,"transid":27,"dp":3,"datatype":4,"fn":0,"data":{"type":"Buffer","data":[0]}}
warn  2021-12-31 17:27:11: zigbee-herdsman-converters:SaswellThermostat: Unrecognized DP #3 with data {"status":0,"transid":27,"dp":3,"datatype":4,"fn":0,"data":{"type":"Buffer","data":[1]}}
warn  2021-12-31 18:01:23: zigbee-herdsman-converters:SaswellThermostat: Unrecognized DP #3 with data {"status":0,"transid":27,"dp":3,"datatype":4,"fn":0,"data":{"type":"Buffer","data":[0]}}

It seems it is information if valve is opened or not (true == open)

github-actions[bot] commented 2 years ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days