commit 64f12e6a38c559c84e35b66b13f5a54a95d1f7d4 Author: rafal Date: Sun Jan 18 23:47:47 2026 +0000 Upload files to "/" diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..7e3d194 --- /dev/null +++ b/README.en.md @@ -0,0 +1,118 @@ +[🇵🇱 Polish version](README.md) + + +# TS0601_BY_RK.js +Minimalist Zigbee2MQTT converter for Tuya TS0601 TRV heads (ON / OFF) + +## Why this converter exists + +This converter was created to solve a real-world problem: +some Tuya TS0601 thermostatic radiator valves generate a huge amount of Zigbee reports, which: + +- spam MQTT +- cause constant state changes in Home Assistant +- increase CPU usage and database load +- destabilise automations +- provide no real value in everyday use + +In installations with many TRVs (a dozen or more), this resulted in: +- Home Assistant slowdowns +- difficult troubleshooting +- unpredictable automation behaviour + +## Conscious design decision + +Instead of fighting every reported variable, a different approach was chosen: + +minimalism over completeness + +This converter: +- removes all unnecessary exposes +- ignores temperature, mode, and calibration reports +- reduces control to a single simple action: ON / OFF + +Result: +- minimal Zigbee traffic +- no MQTT spam +- stable Home Assistant +- predictable automations + +## How the converter works + +Tuya TS0601 TRVs are controlled using datapoint DP=2 (setpoint). + +Mapping: +- ON -> sends 45°C (valve forced fully open) +- OFF -> sends 0°C (valve fully closed) + +Any other value: +- is treated as ON +- is not corrected back to avoid generating additional Zigbee traffic + +The state is published immediately after sending the command, without waiting for further device reports. + +## What is NOT exposed (by design) + +This converter deliberately does NOT expose: +- target temperature +- operating modes +- calibration or offsets +- diagnostic data + +This is not a bug or missing feature — it is a deliberate limitation to ensure system stability. + +## Exposed features + +- Switch (ON / OFF) – main valve control +- Battery (%) – if reported by the device +- Local temperature (°C) – passive only + +## How to add the converter to Zigbee2MQTT + +1. Copy the file TS0601_BY_RK.js to the Zigbee2MQTT directory, for example: + /zigbee2mqtt/external_converters/TS0601_BY_RK.js + + If the external_converters directory does not exist, create it. + +2. In /zigbee2mqtt/configuration.yaml add: + ``` + external_converters: + - TS0601_BY_RK.js + ``` +![conf](https://github.com/user-attachments/assets/a7057153-2b3a-4db5-b2d1-ac4f3d0719c9) + +3. Restart Zigbee2MQTT + +## How to add a new TRV (fingerprint) + +If your TS0601 TRV is not supported, you need to add its identifier. + +1. In Zigbee2MQTT, check: + - modelID + - manufacturerName + + Example: + modelID: TS0601 + manufacturerName: _TZE200_xxxxxxxx + +2. In TS0601_BY_RK.js add a new fingerprint entry: + ``` + { modelID: 'TS0601', manufacturerName: '_TZE200_NOWYID' } + ``` +add new trv + +3. Restart Zigbee2MQTT + +## Who this converter is for + +- installations with many TRVs +- systems sensitive to MQTT spam +- simple open / close valve control +- stable and predictable automations + +It is NOT intended for users who expect full temperature control from Home Assistant. + +## Summary + +This converter does not try to be perfect. +Its goal is stability, silence, and predictable behaviour. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1dec001 --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ +[🇬🇧 English version](README.en.md) + + +# TS0601_BY_RK.js +Minimalistyczny konwerter Zigbee2MQTT dla głowic Tuya TS0601 (ON / OFF) + +## Po co ten konwerter istnieje + +Ten konwerter powstał jako rozwiązanie realnego problemu: +niektóre głowice termostatyczne Tuya TS0601 generują ogromne ilości raportów Zigbee, które: + +- spamują MQTT +- powodują ciągłe zmiany stanów w Home Assistant +- zwiększają zużycie CPU i bazy danych +- destabilizują automatyzacje +- nie wnoszą realnej wartości w codziennym użytkowaniu + +W instalacjach z wieloma głowicami (kilkanaście lub więcej) prowadziło to do: +- spowolnienia Home Assistant +- trudnej diagnostyki +- nieprzewidywalnego działania automatyzacji + +## Świadoma decyzja projektowa + +Zamiast walczyć z każdą raportowaną zmienną, przyjęto podejście: + +minimalizm zamiast kompletności + +Konwerter: +- usuwa wszystkie zbędne expose’y +- ignoruje raporty temperatur, trybów i kalibracji +- redukuje sterowanie do jednej prostej akcji: ON / OFF + +Efekt: +- minimalny ruch Zigbee +- brak spamu MQTT +- stabilny Home Assistant +- przewidywalne automatyzacje + +## Jak działa konwerter + +Głowice Tuya TS0601 sterowane są poprzez datapoint DP=2 (setpoint). + +Mapowanie: +- ON -> wysyłane 45°C (zawór wymuszony maksymalnie otwarty) +- OFF -> wysyłane 0°C (zawór zamknięty) + +Każda inna wartość: +- traktowana jest jako ON +- nie jest korygowana zwrotnie, aby nie generować dodatkowego ruchu Zigbee + +Stan publikowany jest natychmiast po wysłaniu komendy, bez oczekiwania na kolejne raporty z urządzenia. + +## Co NIE jest udostępniane (celowo) + +Ten konwerter świadomie nie udostępnia: +- temperatury zadanej +- trybów pracy +- kalibracji i offsetów +- danych diagnostycznych + +Nie jest to błąd ani brak funkcji — to celowe ograniczenie, mające na celu stabilność systemu. + +## Udostępnione expose + +- Switch (ON / OFF) – główne sterowanie zaworem +- Battery (%) – jeśli urządzenie raportuje +- Local temperature (°C) – tylko pasywnie + +## Jak dodać konwerter do Zigbee2MQTT + +1. Skopiuj plik TS0601_BY_RK.js do katalogu Zigbee2MQTT, .: + /zigbee2mqtt/external_converters/TS0601_BY_RK.js + jesli katalog external_converters nie istnieje dodaj go + +2. W pliku /zigbee2mqtt/configuration.yaml dodaj: + ``` + external_converters: + - TS0601_BY_RK.js + ``` +![conf](https://github.com/user-attachments/assets/a7057153-2b3a-4db5-b2d1-ac4f3d0719c9) + +3. Zrestartuj Zigbee2MQTT + +## Jak dodać nową głowicę (fingerprint) + +Jeśli Twoja głowica TS0601 nie jest obsługiwana, należy dodać jej identyfikator. + +1. W Zigbee2MQTT sprawdź: + - modelID + - manufacturerName + + Przykład: + modelID: TS0601 + manufacturerName: _TZE200_xxxxxxxx + +2. W pliku TS0601_BY_RK.js dodaj nowy fingerprint do listy: + ``` + { modelID: 'TS0601', manufacturerName: '_TZE200_NOWYID' } + ``` +add new trv + +4. Zrestartuj Zigbee2MQTT + +## Dla kogo ten konwerter jest przeznaczony + +- instalacje z wieloma głowicami TRV +- systemy wrażliwe na spam MQTT +- proste sterowanie otwórz / zamknij +- stabilne automatyzacje + +Nie jest przeznaczony dla osób oczekujących pełnej kontroli temperatury z Home Assistant. + +## Podsumowanie + +Ten konwerter nie próbuje być idealny. +Jego celem jest stabilność, cisza i przewidywalność działania. diff --git a/TS0601_BY_RK.js b/TS0601_BY_RK.js new file mode 100644 index 0000000..41f9ab5 --- /dev/null +++ b/TS0601_BY_RK.js @@ -0,0 +1,73 @@ +const exposes = require('zigbee-herdsman-converters/lib/exposes'); +const ea = exposes.access; + +function intTo4BytesBE(value) { + return [(value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]; +} + +async function sendSetpoint(entity, setpoint) { + const payload = { + seq: 0, + dpValues: [{ + dp: 2, + datatype: 0x02, + data: intTo4BytesBE(setpoint), + }], + }; + await entity.command('manuSpecificTuya', 'dataRequest', payload, {disableDefaultResponse: true}); +} + +module.exports = [ + { + fingerprint: [ + { modelID: 'TS0601', manufacturerName: '_TZE200_b6wax7g0' }, + { modelID: 'TS0601', manufacturerName: '_TZE204_pcdmj88b' } + ], + model: 'TS0601-TRV-MANUAL-RK', + vendor: 'Moes', + description: 'TS0601 TRV manual ON/OFF (ON=45, OFF=0, auto-sync)', + fromZigbee: [ + { + cluster: 'manuSpecificTuya', + type: ['commandDataResponse', 'commandDataReport'], + convert: (model, msg, publish, options, meta) => { + const dp = msg.data.dpValues[0].dp; + const raw = msg.data.dpValues[0].data; + const value = raw[3]; // ostatni bajt wystarcza dla setpoint + + if (dp === 2) { + if (value === 0) { + return {state: 'OFF', current_heating_setpoint: 0}; + } else if (value >= 45) { + return {state: 'ON', current_heating_setpoint: 45}; + } else { + // jeżeli przyjdzie np. 22 → traktujemy jako ON, ale poprawki nie wysyłamy z fromZigbee + return {state: 'ON', current_heating_setpoint: 45}; + } + } + }, + }, + ], + toZigbee: [ + { + key: ['state'], + convertSet: async (entity, key, value) => { + let setpoint = 0; + if (value.toLowerCase() === 'on') setpoint = 45; + if (value.toLowerCase() === 'off') setpoint = 0; + + await sendSetpoint(entity, setpoint); + + // natychmiast publikujemy nowy stan + return {state: {state: value.toUpperCase(), current_heating_setpoint: setpoint}}; + }, + }, + ], + exposes: [ + exposes.switch().withState('state', ea.ALL) + .withDescription('ON = valve forced open (45°C), OFF = valve closed (0°C)'), + exposes.numeric('battery', ea.STATE).withUnit('%').withDescription('Battery level'), + exposes.numeric('local_temperature', ea.STATE).withUnit('°C').withDescription('Measured local temperature'), + ], + }, +];