Node Red + YeeLight = HomeKit

14 мар. 2019 г.

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

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

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


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

Одна из особенностей протокола состоит в том, что для прослушивания лампы нам надо отправить ей 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:

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

  1. При включении питания подсветки, она включается с нулевой яркостью, пришлось с задержкой в пол секунды после включения (нода 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 адрес лампы и пользуемся)


Все новости мира умных домов - t.me/SprutAI_News

Остались вопросы? Мы в Telegram - t.me/soprut

  1. Олег Челбаев (sprut)
    Олег Челбаев (sprut) 2 месяца назад
    Спасибо, за титаническую работу!!!!

  2. Григорий Абрамов (grishmanCH)
    Вообще отлично описано и можно применить ко многим другим похожим задачам. Еще раз убедился что Node Red серьезная штука с огромным потенциалом.

  3. . . (Bonefolder)
    . . (Bonefolder) 2 месяца назад

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

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

    1000x_image.png?1552740849

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

    1000x_image.png?1552740991

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

К списку статей

Скидки для сообщества

Ноотехника

+375 17 233-25-45
Промокод:
Sprut
Размер скидки:
10%
Действует у всех официальных представителей. Для применение необходимо указать в поле комментария или при звонке менеджеру.

MI-DOM

+7 977 282-80-53
Промокод:
SPRUTAI
Размер скидки:
5%

Похожие статьи

15 нояб. 2018 г.
Xiaomi Mi Remote 360 добавляем Apple HomeKit
9 нояб. 2018 г.
Кейс создания умного дома без каких либо прокладок в виде Raspberry pi
19 мар. 2019 г.
Обзор долгожданного 2х канального zigbee реле от Aqara, сравнение с конкурентами и небольшой тест на Deconz, Mi Hub и Aqara Hub.
1 нояб. 2018 г.
Настройка Deconz USB стика ConBee от Dresden Elektronik в Hass.io и некоторые особенности эксплуатации
4 апр. 2019 г.
Расскажу о том за ~1 час перевезти УД с 2мя USB стиками с Raspberry PI на Synology NAS.
1 дек. 2018 г.
Получение токена устройств Xiaomi с помошью Windows или MacOs
3 апр. 2019 г.
Разбираем простейшую задачу по электрическому подключению светодиодной ленты к источнику питания и управлению через Умный дом.
11 мая 2019 г.
Как активировать русский язык в Google Assistant для Google Home
12 мая 2018 г.
Добавление выключателя Aqara zero line(LN) в HomeKit через homebridge
11 мар. 2019 г.
Пользование устройствами начального уровня, такие как лампы, датчики, мойка и пылесос. Опыт эксплуатации и мнение на счет необходимости покупки устройств.