Koenkk / Koenkk/zigbee2mqtt

[External Converter]: TS0601 from _TZE284_y9nhbthz

Open
#33,073 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

external converter
Dominant language
TypeScript
Stars
15.7k
Forks
2k
Avg merge
18h 55m
Merged PRs (30d)
35

Description

Link

https://en.b2b.emos.cz/gosmart-digital-thermostatic-valve-p5631s-with-zigbee-3-0

Database entry

{"id":6,"type":"EndDevice","ieeeAddr":"0xa4c138f97bb941c5","nwkAddr":679,"manufId":4417,"manufName":"_TZE284_y9nhbthz","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":"K}22g��22i��22i��22iƊ22i��22f��22\u0012","65506":56,"65508":0,"65534":0,"modelId":"TS0601","manufacturerName":"_TZE284_y9nhbthz","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,"interviewState":"SUCCESSFUL","meta":{"configured":"0.0.0"},"lastSeen":1788847967585}

Zigbee2MQTT version

2.14.1 (unknown)

External converter
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const tuya = require('zigbee-herdsman-converters/lib/tuya');

const e = exposes.presets;
const ea = exposes.access;

const definition = {
    fingerprint: [
        {
            modelID: 'TS0601',
            manufacturerName: '_TZE284_y9nhbthz',
        },
    ],

    model: 'P5631S',
    vendor: 'EMOS',
    description: 'EMOS P5631S thermostatic radiator valve',

    extend: [
        tuya.modernExtend.tuyaBase({
            dp: true,
            forceTimeUpdates: true,
            timeStart: '1970',
        }),
    ],

    exposes: [
        e.climate()
            .withLocalTemperature(ea.STATE)
            .withSetpoint(
                'current_heating_setpoint',
                5,
                30,
                0.5,
                ea.STATE_SET
            ),

        e.enum(
            'preset',
            ea.STATE_SET,
            ['auto', 'manual', 'antifrost']
        ).withDescription('Thermostat operating mode'),

        e.child_lock(),
        e.battery(),

        e.numeric(
            'local_temperature_calibration',
            ea.STATE_SET
        )
            .withUnit('°C')
            .withValueMin(-10)
            .withValueMax(50)
            .withValueStep(0.5)
            .withDescription('Local temperature calibration'),

        e.binary(
            'window_detection',
            ea.STATE_SET,
            'ON',
            'OFF'
        ).withDescription(
            'Enable or disable built-in Open Window detection'
        ),

        e.binary(
            'scale_switch',
            ea.STATE_SET,
            'ON',
            'OFF'
        ).withDescription(
            'Anti-scale / anti-sticking valve function'
        ),

        e.binary(
            'valve_calibration',
            ea.STATE_SET,
            'ON',
            'OFF'
        ).withDescription(
            'Start thermostatic valve calibration'
        ),

        e.binary(
            'find_device',
            ea.SET,
            'ON',
            'OFF'
        ).withDescription(
            'Find device command'
        ),

        e.numeric(
            'external_ambient_temperature',
            ea.SET
        )
            .withUnit('°C')
            .withValueMin(0)
            .withValueMax(50)
            .withValueStep(0.1)
            .withDescription(
                'External ambient temperature input'
            ),

        e.binary(
            'external_door_magnet',
            ea.SET,
            'ON',
            'OFF'
        ).withDescription(
            'External door/window magnetic sensor input'
        ),

        e.binary(
            'external_pir',
            ea.SET,
            'ON',
            'OFF'
        ).withDescription(
            'External PIR sensor input'
        ),

        e.numeric(
            'fault',
            ea.STATE
        )
            .withValueMin(0)
            .withValueMax(7)
            .withValueStep(1)
            .withDescription(
                'Fault bitmap: bit0 sensor fault, bit1 valve failure, bit2 button malfunction'
            ),

        e.text('schedule_monday', ea.STATE_SET)
            .withDescription('Monday automatic schedule - 6 periods'),

        e.text('schedule_tuesday', ea.STATE_SET)
            .withDescription('Tuesday automatic schedule - 6 periods'),

        e.text('schedule_wednesday', ea.STATE_SET)
            .withDescription('Wednesday automatic schedule - 6 periods'),

        e.text('schedule_thursday', ea.STATE_SET)
            .withDescription('Thursday automatic schedule - 6 periods'),

        e.text('schedule_friday', ea.STATE_SET)
            .withDescription('Friday automatic schedule - 6 periods'),

        e.text('schedule_saturday', ea.STATE_SET)
            .withDescription('Saturday automatic schedule - 6 periods'),

        e.text('schedule_sunday', ea.STATE_SET)
            .withDescription('Sunday automatic schedule - 6 periods'),
    ],

    meta: {
        tuyaDatapoints: [
            [
                2,
                'preset',
                tuya.valueConverterBasic.lookup({
                    auto: tuya.enum(0),
                    manual: tuya.enum(3),
                    antifrost: tuya.enum(5),
                }),
            ],

            [
                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,
            ],

            [
                28,
                'schedule_monday',
                tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(
                    1,
                    6
                ),
            ],

            [
                29,
                'schedule_tuesday',
                tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(
                    2,
                    6
                ),
            ],

            [
                30,
                'schedule_wednesday',
                tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(
                    3,
                    6
                ),
            ],

            [
                31,
                'schedule_thursday',
                tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(
                    4,
                    6
                ),
            ],

            [
                32,
                'schedule_friday',
                tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(
                    5,
                    6
                ),
            ],

            [
                33,
                'schedule_saturday',
                tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(
                    6,
                    6
                ),
            ],

            [
                34,
                'schedule_sunday',
                tuya.valueConverter.thermostatScheduleDayMultiDPWithDayNumber(
                    7,
                    6
                ),
            ],

            [
                35,
                'fault',
                tuya.valueConverter.raw,
            ],

            [
                39,
                'scale_switch',
                tuya.valueConverter.onOff,
            ],

            [
                47,
                'local_temperature_calibration',
                tuya.valueConverter.divideBy10,
            ],

            [
                48,
                'valve_calibration',
                tuya.valueConverter.onOff,
            ],

            [
                101,
                'find_device',
                tuya.valueConverter.onOff,
            ],

            [
                102,
                'external_ambient_temperature',
                tuya.valueConverter.divideBy10,
            ],

            [
                103,
                'external_door_magnet',
                tuya.valueConverter.onOff,
            ],

            [
                104,
                'external_pir',
                tuya.valueConverter.onOff,
            ],
        ],
    },
};

module.exports = definition;
What does/doesn't work with the external definition?

Pairing / interview | — | ✅ WORKS | Pairs correctly as battery EndDevice
Time synchronization | — | ✅ WORKS | timeStart: '1970'
Current temperature | 5 | ✅ WORKS | Confirmed, e.g. 21.3 °C
Target temperature | 4 | ✅ WORKS | Read/write; 5–30 °C
Battery percentage | 6 | ✅ WORKS | Confirmed 0–100%
Child lock | 7 | ✅ WORKS | LOCK/UNLOCK confirmed
Auto mode | 2 | ✅ WORKS | DP2 enum 0
Manual mode | 2 | ✅ WORKS | DP2 enum 3
Antifrost mode | 2 | ✅ WORKS | DP2 enum 5
Built-in Open Window Detection ON/OFF | 14 | ✅ WORKS | Confirmed
Open-window active state | 15 | ✅ DP KNOWN, intentionally removed | We found it, but you asked not to expose it
Temperature calibration | 47 | ✅ WORKS | Confirmed; e.g. raw −16 = −1.6 °C
Valve calibration | 48 | 🟡 PARTLY CONFIRMED | Command works, but ON/OFF state semantics aren't fully clear
Anti-scale / anti-sticking | 39 | 🟡 DP KNOWN, NOT TESTED IN Z2M | Tuya identifies it as scale_switch / 防水垢
Fault reporting | 35 | 🟡 DP KNOWN, NOT TESTED | Bitmap: sensor / valve / button faults
Monday schedule | 28 | 🟠 DP KNOWN, CONVERTER NOT WORKING CORRECTLY | Raw format known
Tuesday schedule | 29 | 🟠 same |  
Wednesday schedule | 30 | 🟠 same |  
Thursday schedule | 31 | 🟠 same |  
Friday schedule | 32 | 🟠 same |  
Saturday schedule | 33 | 🟠 same |  
Sunday schedule | 34 | 🟠 same |  
Find device | 101 | 🟡 DP KNOWN, NOT TESTED | Write-only Tuya command
External ambient temperature | 102 | 🟡 DP KNOWN, NOT TESTED | Write-only input, 0–50 °C
External door/window sensor | 103 | 🟡 DP KNOWN, NOT TESTED | Write-only input
External PIR | 104 | 🟡 DP KNOWN, NOT TESTED | Write-only input
Comfort mode | DP2 | 🟠 NOT MAPPED YET | Tuya says it exists; enum number not established
ECO mode | DP2 | 🟠 NOT MAPPED YET | Same
OFF mode | DP2 | 🟠 NOT MAPPED YET | Same
ON mode | DP2 | 🟠 NOT MAPPED YET | May correspond to our current manual, but not proven
Freeze mode | DP2 | 🟠 NOT FULLY MAPPED | Likely corresponds to our enum 5/antifrost, but Tuya terminology needs reconciliation
Actual valve position % | — | ❌ NOT AVAILABLE | No corresponding DP in the Tuya data model
Heating/running state | — | ❌ NOT AVAILABLE | No running_state DP exposed by this model
Valve open/closed state | — | ❌ NOT AVAILABLE | Cannot currently know actual valve movement/state
Heating demand | — | ⚠️ CAN ONLY BE INFERRED | Can estimate from target vs room temperature

Notes

software_build_id: undefined
date_code: ``
endpoints:

{"1":{"clusters":{"input":["genGroups","genScenes","manuSpecificTuya","genBasic","60672"],"output":["genOta","genTime"]}}}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the provided external converter definition and compare its Tuya datapoint mappings with existing converter patterns in the repository. Test the confirmed controls and schedule handling against the TS0601 device, then verify that any integrated mappings behave as described and that unsupported states remain unexposed.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
backend, embedded-iot
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.