
Все написанное относится к термостату с сигнатурой `_TZE204_u9bfwha0`. Если у вас другая, не нужно экспериментов, сперва убедитесь, что datapoint'ы соответсвуют приведенным в статье. Если нет. То или логанализатор, или сниффер и смотреть. Автор не несет ответственности, если данная статья сделает из вашего умного термостата полоумный.
Для пользователей умных домов, которые строят свои системы на протоколе Zigbee, название Tuya знакомо, как никогда. Причем в последнее время название Tuya не всегда ассоциируют именно с компанией Tuya. Очень много китайских компаний выпускают свои изделия, в сердце которых лежит платформа Tuya. Вот весь этот "зоопарк" и принято называть Tuya.
Но не будем тут рассматривать другие варианты устройств, остановимся только на термостате. Субъективно рассмотрим его плюсы и минусы.
Итак, плюсы.
-
Достаточно стильно и красиво выглядит, особенно в черном варианте.
-
Относительно недорогой (был приобретен на Озоне под брендом Fujihome за 3300 р.).
-
Устанавливается в обычный подрозетник.
-
Достаточно просто подключается (два контакта - вход 220, два - выход на нагрузку и еще два слаботочных на внешний датчик температуры, который идет в комплекте).
-
Достаточно контрастные изображения на экране.
-
Есть вариант считывать температуру по внутреннему датчику температуры, внешнему или сдвоенный режим, когда термостат контролирует оба датчика.
-
Достаточно много настроек по инструкции.
И минусы.
-
Инструкция видимо общая, частично не совпадает с реальной работой термостата.
-
Обязательно нужен Хаб Zigbee именно фирмы Tuya.
-
Нужно приложение на телефон, причем его просто так не установить (не для вашего региона).
-
Не совсем стандарная работа устройства в сети.
-
Очень большой поток сообщений от устройства в сеть.
-
Невозможность установить период отправки сообщений.
С плюсами все понятно, остановимся на минусах.
Инструкция содержит в себе какие-то части от Zigbee, какие-то от WiFi версий изделия. В некоторых моментах она не соответсвует заявленым действиям. Например, настроить расписание - в инструкции написано, при включенном термостате, в режиме программирования, нажать и удерживать значок часы. На самом деле это не так. Нужно его просто коснуться, причем не важно в каком режиме находится термостат (работа по датчику температуры или по расписанию).
Далее, в инструкции сказано, что расписание имеет 6 вхождений для каждого дня недели. На самом деле и это не так. Расписание имеет по 4 вхождения на 3 периода. 1 период - это рабочая неделя, т.е. с понедельника по пятницу. 2 период - суббота. 3 период - воскресенье. Что это значит? То, что отдельно понедельник от среды не настроить.
Теперь по настройкам. Все настройки локально регулируются. Удаленно не все.
-
Калибровка температы от -9°С до +9°С. Настраивается удаленно.
-
Гистерезис от 1°С до 5°С. Настраивается удаленно.
-
Блокировка кнопок (так называемый child lock). Настраивается удаленно.
-
Типы используемых для измерений датчиков температуры (три варианта IN, AL, OU). Настраивается удаленно.
-
Минимальная уставновка температуры от +5°С до +15°С. Не настраивается удаленно.
-
Максимальная устновка температуры от +15°С до 45°С. Не настраивается удаленно.
-
Отображать локальную и установленную температуры или только установленную. Не настраивается удаленно.
-
Защита замерзания от 0°С до 10°С. Не настраивается удаленно.
-
Защита от высокой температуры от 25°С до 70°С. Настраивается удаленно.
-
Яркость экрана (активный режим). Никакого изменения не происходит. Не настраивается удаленно.
-
Яркость экрана (пассивный режим). Не настраивается удаленно.
Собственно какие настройки регулируются удаленно можно посмотреть в родном приложении Tuya.

Ладно, это все присказка. Собственно для чего все это затевалось - попытаться сделать из этого термостата более-менее стандартное устройство с точки зрения Zigbee. Ну и чтобы убрать этот нескончаемый поток сообщений. Вот конкретно это устройство присылало 25 пакетов каждые 8 секунд. Несколько таких устройств в доме (квартире) способны нарушить нормальное функционирование сети Zigbee.
Итак, разбираем термостат, смотрим, что там внутри. Слаботочная часть выполнена в виде отдельного блока в отдельном корпусе. Передняя часть сдвигается вдоль термостата и силовая часть отделяется от слаботочной.

Отсоединяем разъем и разбираем корпус слаботочной части. Она скреплена 4 небольшими саморезами.


Видим, что производитель упростил схему, вместо транзисторных ключей он установил перемычки.
Черный большой чип, со стертой маркировкой, это как раз центральный процессор, на котором основана вся логика работы устройства. А вот белый модуль ZT3L - это как раз радиомодуль производства компании Tuya, который собственно и поддерживает связь в сети Zigbee.
Схемотехника всех таких устройств примерно одинакова. Есть центральный MCU, который отвечает за работу устройства и есть радиомодуль, который поддерживает связь с внешним миром. Причем модуль может быть Zigbee, Wi-Fi, Bluetooth и т.д. Общаюся MCU и модуль между собой, как правило, по UART.
Идем на сайт Tuya за документацией на модуль ZT3L. Документация достаточно подробная. Но нигде не написано, что за чип там применен. Но поиск и наличие контакта SWS привели к выводу, что там установлен чип компании Telink.
Смотрим, какие "ножки" припаяны и проверяем, что это такое.

В результате осмотра платы и чтения документации получаем, что модуль использует всего 6 контактов, 2 из которых это +3.3в и GND. Остаются RXD, TXD, PC4 и PC0. Посдедние два предназначены для пробуждения (предположительно, потому что в документации указаны другие GPIO). Один вход. Второй выход. Но ни модуль, ни MCU в данном устройстве не спят, так как устройство является роуером, и получить какую-либо активность на этих пинах у меня не получилось. Потому я их наличие просто проигнорировал. Для полноценной разведки, что там прячется в протоколе от Tuya, нам будет достаточно только RX и TX. Но для дальнейших манипуляций с термостатом понадобятся еще несколько лишних проводников - это RST, SWS и какой-нибудь выход для организации вывода отладочной информации в терминал.
Итак, схема подключения к модулю будет такая.

3V3 |
Питание 3.3в |
GND |
Земля |
RST |
Reset |
PC4 |
Не используется |
PC0 |
Не используется |
TXD |
TXD |
RXD |
RXD |
SWS |
Для считывания или загрузки прошивки |
PB5 |
Вывод отладочной информации |
Припаиваем к выбранным контактам проводники, подписываем, выводим их наружу и собраем термостат.



Берем простой недорогой логанализатор, подключаем в нему RXD, TXD, PC0, PC4. Запускаем программу PulseView. Ну и начинаем пользоваться термостатом, как локально, так и удаленно. И смотрим, что он там посылает в UART.
В принципе, протокол коммуникации MCU и модуля есть все там же, на сайте Tuya. Но самое трудное кроется в их так называемых datapoint'ах. Нет, все расписано и даже стандартизировано. У datapoint'а есть уникальный ID, есть тип этого datapoint'а (raw, bool, integer, string, enum, bitmap), его длина, ну и собственно значение. Но! Никаких привязок по типу деятельности, за что он отвечает, нет. Один и тот же datapoint на разных устройствах может иметь одинковый ID, но выполнять совершенно разные функции. И в этом вся проблема - что заложил в эти datapoint'ы производитель можно только угадать. Чем мы собственно и займемся.
Начнем с простого. Включение-выключение.

Остановимся более подробно, чтобы было понятней в дальнейшем.
-
0x55 0xAA - пакет всегда начинается с этих двух значений.
-
0x02 - версия протокола - 2.
-
0x1100 - Sequence number - что-то вроде ID пакета. Ответ на запрос должен содержать именно такой же номер. Он увеличивается от запроса к запросу на единицу. По достижении значения 0xFFF0 должен сбрасываться на 0.
-
0x04 - команда.
-
0x0005 - длина полезной нагрузки
-
0x01 - ID datapoint'а
-
0x01 - тип datapoint'а - bool
-
0x0001 - длина datapoint'а
-
0x01 - значение datapoint'а
-
0x1F - контрольная сумма
2-4 байтные числа передается в формате Big-endian.
Ну и вот так включая тот или иной режим можно посмотреть, что присылает MCU модулю, что модуль отвечает и наоборот. В итоге можно подвести вот такой итог.
DataPoint |
Назначение |
0x01 |
Включение и выключение термостата |
0x02 |
Режим работы термостата - от датчика температуры или по расписанию |
0x03 |
Не известно за что отвечает |
0x10 |
Установка температуры нагрева |
0x13 |
Ограничение по максимальному нагреву |
0x18 |
Содержит данные по локальной температуре |
0x1A |
Гистерезис |
0x1B |
Калибровка температуры |
0x24 |
Включена или выключена нагрузка |
0x28 |
Блокировка кнопок |
0x2B |
Выбор датчика температуры |
0x65 |
Расписание |
Это все, что удалось вытащить из термостата. Может я что-то и проглядел, но это вряд ли.
Далее, для того, чтобы не потерять родную прошивку, взять то ее будет негде, считываем ее из модуля и сохраняем на всякий случай.
Теперь нужно немного ознакомиться с кластером HVAC и начать писать свой Zigbee термостат.
Очень большая часть пользователей сетей Zigbee использует в качестве шлюза между Zigbee и умным домом zigbee2mqtt. А умным домом выступает Home Assistant. Автор тоже все это использует и потому все буде настраиваться и проверяться в zigbee2mqtt.
Для более менее стандартного устройства мы будем использовать следующие атрибуты.
-
localTemperature
-
absMinHeatSetpointLimit
-
absMaxHeatSetpointLimit
-
minHeatSetpointLimit
-
maxHeatSetpointLimit
-
localTemperatureCalibration
-
occupiedHeatingSetpoint
-
controlSequenceOfOperation
-
systemMode
-
runningState
-
startOfWeek
-
weeklyTransNum
-
dailyTransNum
-
ProgrammingOperationMode
-
MinSetpointDeadBand
-
temperatureDisplayMode
-
keypadLockout
Некоторая часть не будет использоваться в этой моделе термостата и сделана с запасом на будущее.
ZCL не предоставляет в кластере HVAC выбор датчика температуры. Поэтому придется сделать свой собственный атрибут в этом кластере (ZCL это не противоречит).
Основная логика прошивки модуля простая. Принимать данные из сети, преобразовывать их в понятный MCU вид и передавать ему. Получать от MCU данные, конвертировать их в формат ZCL и отправлять в сеть.
Также хотелось бы менять прошивку не с помощью проводов и программатора, а воспользоваться технологие OTA. На этом остановимся больее подробно, так как эта часть не стандартна и получилось все не с первого раза.
Стандартная прошивка с bootloader'ом от Telink имеет следующее распределение флеш.
-
0x0000 - 0x8000 - bootloader
-
0x8000 - 77000 - исполняемая программа
-
0x77000 - 0xE6000 - область для загрузки образа OTA
-
0xE6000 - 0x100000 - служебная часть
При обновлении OTA, образ грузится по адресу 0x77000. Далее, если образ отвечает всем правилам, он копируется на место исполняемой программы - 0x8000. Система перегружается, сперва стартует bootloader, он, в свою очередь, запускает исполняемую программу.
Прошивка Tuya делает практически тоже самое, только образ OTA грузит не по адресу 0x77000, а по 0x70000. Уж не знаю зачем, но вот так. Чем это грозит. В ближайшей перспективе - ничем. Мы обновим прошивку Tuya на кастомную (свою). И все будет работать. Все, да не все. Потеряется возможность обновления своей прошивки по воздуху. Потому что стандартный SDK будет грузить образ OTA по адресу 0x77000, а bootloader от Tuya будет его копировать с адреса 0x70000. Понятно, что ничего не заработает.
Поэтому нужно после обновления подменить bootloader на оригинальный из SDK. В общем все получилось. После обновления прошивки Tuya на кастомную, поледняя стартует, проверяет загрузчик, и если он Tuya, то переписывает его, если нет, оставляет все как есть. Для этого используется скрипт на Питоне, который берет прошивку, "приклеивает" к ней сзади bootloadr SDK, затем формирует образ с нужными crc, manufacturer code и image type.
Теперь у нас есть файл образа своей прошивки, которая притворяется прошивкой от Tuya. Как же это все обновить? На самом деле zigbee2mqtt очень мощный инструмент. И он дает нам такую возможность.
Для этого нужно создать простенький внешний конвертор, где мы укажем, что OTA разрешен. Например вот такой.
const {identify, reporting, ota} = require('zigbee-herdsman-converters/lib/modernExtend');
const definition = {
fingerprint: [{modelID: 'TS0601', manufacturerName: '_TZE204_u9bfwha0'}],
zigbeeModel: ['TS0601'],
model: 'TS0601',
vendor: '_TZE204_u9bfwha0',
description: 'Automatically generated definition',
extend: [
identify(),
ota(),
],
meta: {},
};
module.exports = definition;
Потом перегрузить z2m и увидеть свое устройство.

Далее идем в раздел OTA. И видим там свое устройство. Жмем проверить обновления.


Жмем на красную кнопку. И обновляемся.

Далее ждем окончания. После этого мы в OTA видим наш термостат со старым именем, но уже с новыми Firmware build date
и Firmware version
.

Далее идем в радел Devices
и жмем на нашем термостате "Корзинку", т.е. удаляем его из сети.


Потом прописываем в z2m новый конвертор для кастомной прошивки. И перегружаем z2m. Далее ждем, пока он загрузится и спариваем термостат по-новой - при выключенном устройстве, зажимаем стрелочку вниз до появления мигающего значка сети.

С обновлением мы закочили. Все работает. Смотрим, что у нас получилось.



По последней. Все репорты настраиваются. Вы можете сами выбрать период. По умолчанию каждый репорт приходит по изменению значения или один раз в час. Я не совтую менять минимальный период с 0 на какое-то число. Иначе не будет оперативно отображаться изменение параметров в интерфейсе умного дома.
Ну и на последок остановимся на расписании. Из интерфейса z2m можно настроить расписание. Оно устроено таким образом, что настраивать за один раз лучше один период. Напомню, у термостата 3 периода по 4 вхождения. К примеру, берем первый период, т.е. рабочую неделю. Нужно выбрать понедельник (другие дни, кроме понедельника, субботы и воскресенья, термостат проигнорирует). Расписание на понедельник будет распространяться на весь 1-й период, т.е. на рабочую неделю. Затем назначить время и температуру нагрева. Время и температуру можно указать до 4 раз. И отправить на термостат. Дальше очистить форму и сделать тоже самое для субботы и потом отдельно для воскресенья. Почему нельзя выбрать сразу все три дня (пн,сб.вс)? Можно, но нельзя настроить отдельно время и температуру для каждого дня. Вы получите одинаковое расписание на все 3 периода.
Что такое 4 вложения. Все просто.
-
Время 06:00, температура 25°С
-
Время 09:00, температура 20°С
-
Время 18:00, температура 25°С
-
Время 22:00, температура 23°С
Получается так. В 06:00 утра температура будет поддерживаться в районе 25°С и это будет продолжаться до 09:00 утра. В 09:00 утра температура включения поменяется на 20°С. И будет поддерживаться на этом уровне до 18:00. В 18:00 температура включения станет опять 25°С. С 22:00 температура включения поменяется на 23°С. И так по кругу. Надеюсь понятно объяснил.

Жмем Apply
. На всякий случай проверим сниффером.

Если нажать на синий квадратик, то координатор запросит расписание у термостата. На каждое нажатие приходит расписание на один период, т.е. по кругу. Нажали - пришло на понедельник, еще раз нажали - на субботу, еще раз - на воскресенье. К сожалению z2m такую информацию не выводит, но на будущее будет полезно.

Дальше останется только добавлять в проект похожие термостаты, чтобы они правильно работали с кастомной прошивкой.
Надеюсь был полезен.
Хочу выразить благодарность следующим людям. Без их участия написание прошивки затянулось на очень продолжительное время.
-
@ruslikx3mза предоставленный термостат на растерзание.
-
https://github.com/doctor64 @doctor_64- за желание всегда помочь и направить в правильное русло.
-
https://github.com/devbis@Belokobylskiy - за идеи и написание скриптов на Питоне.
-
@goofyk- за терпение и за хорошее знание z2m.
-
https://github.com/pvvx - за замечательный программматор для чипов Telink.
Ссылки.
Автор: Slacky1965