Статья

Node Red + YeeLight = HomeKit

В прошлых статьях (раз два) мы подключили DeCONZ к Node Red и сделали первые автоматизации. Но у меня в хозяйстве оказались пара умных ламп линейки Yeelight от компании Xiaomi. Одна из них - Yeelight 650, имеет два режим работы: Солнце(яркий) и Луна (ночной) плюс и имеет встроенную RGB подсветку.

Стандартная платформа yeelight для Homeassistant не позволяла управлять подсветкой, по этому я решил посмотреть в сторону Node Red. Попробовав несколько сторонних модулей для yeelight, я обнаружил, что все они имеют различные ограничения и ни один корректно с этой лампой так и не заработал. К счастью, линейка yeelight имеет открытый протокол управления по локальной сети и он описан в документе: 

Поэтому было решено использовать стандартные методы Node Red. Я не имею опыта программирования в NodeJS, по этому ниже буду стараться максимально использовать простые блоки Node Red и избегать использования сложных функций.

«Слушаем» лампу. 

Одна из особенностей протокола состоит в том, что для прослушивания лампы нам надо отправить ей tcp команду, после получения ответа не разрывать соединение, и тогда ответом будут приходить сообщения о всех изменениях, происходящих в лампе.

Это инициирующее сообщение должно быть таким, чтобы его поняла лампа и прислала на него соответствующий ответ. 

Отправлять это сообщение мы будем нодой inject и будем запрашивать сразу все необходимые нам параметры. Первый раз отправим сообщение при старте Node Red, в дальнейшем будем повторять его каждую минуту, на случай если у лампы пропадёт питание и она на время «отвалится» от сети.

Эти параметры задаются в настройках ноды inject

Нажимаем три точки напротив «payload» и в открывшемся поле вставляем следующий код:

{ "id": 1, "method": "get_prop", "params": [ "power", "bright", "ct", "bg_power", "bg_ct", "bg_bright", "bg_hue", "bg_sat", "active_mode" ] }

Здесь через запятую перечисляем все необходимые нам функции. Полный перечень возможных запрашиваемых параметров есть в таблице на странице 21 протокола yeelight (по ссылке в начале статьи)

Далее нам необходимо сконвертировать это сообщение в формат, понятный лампе. Делаем это с помощью ноды function и вписываем в нее следующий код:

return { payload: JSON.stringify(msg.payload) '\r\n' };

На выход из функции цепляем ноду Debug, если в дебаг окне появились сообщения с периодичностью в минуту как на скриншоте ниже, то мы все сделали правильно(не забываем нажимать Deploy)

Далее добавляем ноду tcp request и заполняем как на скрине, подставляем ip адрес своей лампы.

На выходе из ноды добавляем еще один блок функции, в котором конвертируем сообщения от лампы в понятный нам формат:

Сама строка:

return { payload: JSON.parse(msg.payload)};

На выход снова ставим Debug и, если все правильно, на видим подобное сообщение:

Как видим, в ответном сообщении перечислены значения всех необходимых нам параметров, правда без наименования параметра и в текстовом виде. (Если цифры в дебаг окне красного цвета и в кавычках, то это для Node Red не число а текст) Значения выдаются в том порядке, в котором мы их задали в ноде  inject.

Нам необходимо задать параметр Питание для основного света и подсветки в формате boolean (правда/ложь), остальные параметры надо сконвертировать в числовой формат. Но, как я писал выше, в ответ лампа присылает не только эти параметры, но и все дальнейшие изменения этих параметров, с которыми мы вдальнейшем так-же будем работать. Так что для удобства ставим ноду Switch, в которой отфильтруем сообщения, содержащие исходные параметры, а остальные сообщения пустим по другой ветке:

Далее еще одной нодой switch разделяем сообщения, в котрых значение параметра «power» равно «on» и «off»

Далее, нодой Change задаем значение параметра msg.payload.On соответственно true или false для каждой из веток. Если значение этого параметра предполагается использовать в этом или других флоу, но в других цепочках, то имеет смысл сразу создать переменные global.payload или flow.payload

Далее объединяем эти цепочки в одну и аналогичным образом меняем значения параметра подсветки:

Следующим блоком Change конвертируем остальные, полученные нами значения, в числовой формат. Используем при этом исходящий формат данных Expression и вписываем формулу:

$number(payload.result[1])

где payload.result[1] - это путь к необходимому параметру в сообщении от лампы.

Должно получиться примерно так:

Вешаем на выход Debug, нажимаем Deploy и видим приблизительно такую картину:

Почти все параметры соответствуют тем, что нам необходимо передать в ноду HomeKit, кроме параметра цветовой температуры для основного света и подсветки,  которые поступают в Кельвинах, в диапазоне 1700-6500К (стр 10 протокола), а нода Homekit принимает в диапазоне 500-140. Как правильно конвертировать тепловую температуру на момент написания статьи я не знаю, по этому сконвертируем линейно, используем ноду function: В поле функции вставляем код: 

msg.payload.ColorTemperature = 640 - Math.round(msg.payload.ColorTemperature/13); return msg;

В крайних значениях цветовая температура на выходе будет получаться 510-140, что немного выходит за диапазон ноды HomeKit в верхних значениях (500-140). Отфильтруем нодой свитч значения, выходящие за диапазон:

И заменим их нодой Change на значение 500

Аналогично поступаем и с цветовой температурой подсветки. Смотрим дебаг - значения цветовой температуры должны всегда находиться в диапазоне 140-500

Полученные значения мы можем передавать в ноду Homekit для задания начальных значений лампы, подсветки и цветового режима. Прицепим пока на все выходы ноду link, чтобы дальнейшем связать их с нодой Homekit

2. «Слушаем» текущие значения лампы

Цепляем Debug на второй выход из ноды "Начальные Параметры", включаем/выключаем/меняем параметры лампы с пульта или приложения Yeelight и в дебаг панели видим сообщения с измененными параметрами:

Так как значение питания основного света и подсветки могут поступать в одном сообщении, то соединяем линии таким образом, чтобы изменение основного питания и питания подсветки выполнялись параллельно.

Приведем значение параметра «Питание» к виду правда/ложь, для этого свитчами отфильтруем сообщения содержащие параметр «Питание» и «Питание подсветки»

Для дальнейшей связи с нодами homekit основного света и подсветки поставим на соответствующие выходы блоки Link

Яркость основного света и режим работы лампы также передаются в одной команде, эти два параметра тоже обрабатываем параллельно:

 Аналогичным образом поступаем с параметрами: Яркость подсветки, цветовая температура основного света и подсветки (попутно конвертируем по аналогии с начальными значениями), Hue (цвет) и Saturation (Насыщенность). На выходы так-же цепляем Link для дальнейшей передачи значения в хоумкит. В итоге должно получиться примерно так:

3. Пробрасываем в хоумкит

На этом этап «Прослушивания» лампы считаем законченным и переходим непосредственно к проброске устройств в хоумкит. Начнем с самого простого: выбора режима. Устанавливаем , если не установлен, модуль node-red-contrib-homekit-bridged (меню - Manage Palette - Install и поиск по названию), в нем нажимаем на карандашик и создаем мост, затем выбираем этот мост для нашей службы, выбираем саму службу, нам больше всего подходит «switch»

Создаем ноду Link, связываем с выходами сообщений о режиме в начальных параметрах и в текущих, ноды с соответствующими линками мы сделали ранее, на выход ставим свитч, в котором отфильтровываем сообщения, содержащие информацию о режиме работы лампы. Ноль соответствует Солнце, единица - Луна

На выходы вешаем ноды для режима Солнце с функцией:

var characteristic = {}; characteristic.On = false; msg.payload = characteristic; return msg;

Для режима луны значение characteristic.On = true

Выходы функций соединяем с входом ноды хоумкит нашего переключателя. Должно получиться так:

 Следующим шагом ставим на выход из  ноды HomeKit такой switch:

Он пропустит через себя только сообщения, сгенерированные непосредственно нодой HomeKit и отсечет «ложные» сообщения.

На следующем этапе разделяем значения msg.payload.On на правду и ложь:

И на выходы из свитча ставим блоки функций, которые конвертируют значения в формат, который понимает лампа, и дальше нода Link для передачи сообщения непосредственно в лампу. В списке параметров функции самый последний отвечает за режим, в который включится лампа, на скрине цифра 5 - для режима Луны. Для Солнца - цифра 1 (стр 12 протокола)

Код для вставки выглядит так:

var payload = { "id":1, "method":"set_power", "params":[ "on", "smooth", 500, 5 ] }; return { payload: JSON.stringify(payload) '\r\n' };

Далее создаем аналогичную конструкцию для основного света лампы, только службу выбираем LightBulb, Должно получиться примерно так:

Входящие функции заполняем по образцу:

var characteristic = {}; characteristic.Brightness = msg.payload.Brightness; msg.payload = characteristic; return msg;

Исходящие функции по образцу:

var payload = { "id":1, "method":"set_bright", "params":[ msg.payload.Brightness, "smooth", 500 ] }; return { payload: JSON.stringify(payload) '\r\n' };

Температуру света на выходе конвертируем обратно в Кельвины, код для вставки в функцию:

var payload = { "id":1, "method":"set_ct_abx", "params":[ (8200-msg.payload.ColorTemperature*13), "smooth", 500 ] }; return { payload: JSON.stringify(payload) '\r\n' };

Для конструкции хоумкит для подсветки дополнительно передаем в китовую ноду и из нее в лампу параметры Hue и Saturation:

Из дополнительных хитростей: 

При включении питания подсветки, она включается с нулевой яркостью, пришлось с задержкой в пол секунды после включения (нода trigger) посылать в лампу текущее значение яркости подсветки (из переменной flow.payload.bg_Brightness)

2. Цвет и насыщенность надо передавать в лампу в одном сообщении, для этого ставим каскад из двух нод Change на выходе из китовой ноды, первая нода запоминает текущее значение цвета:

Вторая нода Change добавляет в сообщение сохраненное значение насыщенности:

И уже объединенное сообщение передаем в функцию:

Код:

var payload = { "id":1, "method":"bg_set_hsv", "params":[ msg.payload.Hue, msg.payload.Saturation, "smooth", 500 ] }; return { payload: JSON.stringify(payload) '\r\n' };

Параллельно с этой функцией, запускаем вторую, включающую питание подсветки, если она включена:

Аналогичную операцию проводим и с насыщенностью, сохраняем текущее значение и добавляем в сообщение цвет.

4. Управляем состоянием лампы из Node Red

Добавляем ноду Link, ставим ее на вход ноды tcp-out и прописываем параметры ноды по образцу: ip - вашей лампы, порт 55443

Если вы что-то упустили, тут приведено полное флоу, описанное в статье. Нажимаем на страничке кнопку raw, потом"выделить все" - скопировать, в Node Red: Меню - import - from clipboard, вставляем текст из буфера обмена, нажимаем import.

Добавляем бридж в хоумкит, прописываем в двух местах ip адрес лампы и пользуемся)


Спасибо, за титаническую работу!!!!

Вообще отлично описано и можно применить ко многим другим похожим задачам. Еще раз убедился что Node Red серьезная штука с огромным потенциалом.



"Как правильно конвертировать тепловую температуру на момент написания статьи я не знаю, по этому сконвертируем линейно, используем ноду function:"

Можно немного сократить эту часть, и последующие действия с CT (colour temperature), применив нод Function->Range. Не забудьте поставить галку "Round result to the nearest integer":

1000x_image.png?1552740849

Осталось лишь добавить заголовок, используя Function, чтоб Хом апп понимал, что за цифры вы ему пытаетесь скормить:

1000x_image.png?1552740991

таким же способом "превращаем" CT в обратную сторону после Хом апп

Спасибо автору за статью. Кто еще пробовал подключить потолочную лампу Yeelight этим способом. У меня не получается пробросить в Homekit.

Отличная статья!

Может кто подскажет, побывал прокинуть светильник Yeelight из HA в Node-red, но столкнулся с проблемой. Состояние атрибутов не меняется. То есть если я первый раз включил светильник из HA то атрибуты прилетели в Node-red но если я далее меняю допустим яркость, то состояние в Node-red не меняется. Как мне получить обновленное состояние в Node-red?

Спасибо за статью. На ее основе я хочу сделать просто включение Yeelight 650 по таймеру. Для этого отправляю на нее запрос. Все почти получилось, но как я понимаю метод  "set_power" включает лампу в заданном режиме (солнце/луна, плавно или сразу), но яркость при включении не регулируется - лампа включается с той яркостью, с которой она выключалась. А метод "set_btight" может установить яркость на нужную величину, но работает только с уже включенной лампой. Подскажите как включить лампу с заданной яркостью независимо от состояния при выключении?  

Тоже интересно, если в HA выбрать любой уровень яркости когда люстра выключена (ползунок который), то включается именно с выбранной яркостью.

Адаптировал данный флоу для 480ой модели люстры. Она без подсветки. https://sprut.ai/client/paste/wQpRz2Kh">https://sprut.ai/client/paste/wQpRz2Kh

спасибо огромное, у меня 480 лампа. очень помог. 

представляю сколько сил вложено. огромная работа = огромное спасибо Х2

Добрый день,


тут ошибка в JS.  уъно добавить `+` перед `'\r\n'`
return { payload: JSON.stringify(msg.payload) +'\r\n' };


Спасибо за статью. Очень помогла.
У меня есть в наличии пара настольных ламп, которые теперь сложно найти в продаже. Гуглятся по словам "xiaomi desk lamp 1s". На самом деле производитель тоже YeeLight.
Чтобы не плодить тем, добавлю здесь, что общение с ними возможно и почти так же, как описано выше автором. Я это делал немного отлично. Inject это простая строка
{ "id": 1, "method": "get_prop", "params": [ "power", "bright" ] }
А потом ставим функцию с кодом:
msg.payload = msg.payload + '\r\n';
return msg;

Включение/выключение через функцию:
var message = msg.payload;
msg.payload = '{ "id": 1, "method": "set_power", "params": ["' + message + '", "smooth", 500] }' + '\r\n';
return msg;
куда засылаются "on" или "off".
Добрый день. А как должна выглядеть команда для включения функции bg_flowing?
Добрый день, а не у кого кода не осталось? По ссылке из статьи не доступен

Вернуться назад

Устройства


Phoscon

deCONZ Conbee 2

(5 отзывов)

Вернуться назад