Пробрасываем свет из HomeAssistant в HomeKit через Node-Red

09 мая 2020, 16:03

Преамбула: по неким необъяснимым причинам возникло желание пробрасывать свет из HomeAssistant в HomeKit без участия HA. Потому что родная интеграция НА для HomeKit хоть и хорошая, но почему-то хромает, например, может пересоздать устройство или задублировать его. И если со всеми выключателями, которые живут своей жизнью, через MQTT все было просто, то вот со светом (а у меня большинство ламп и лент от Yeelight) - пришлось посидеть, подумать. Зачем пробрасывать лампы не напрямую, а из НА? Ну потому что они все равно там есть, и потому что в результате получается некий универсальный интерфейс к лампам разных производителей. Не претендую на идеал, но, по крайней мере, flow не занимает всю страницу, и при дублировании нужно поправить всего две ноды для тех, кто дочитает до середины, или одну - для тех, кто дочитает до конца :)

В общем, поехали.

Вот так вот выглядит целиком (так и тянет сказать flow, но flow - это вроде как целиком лист) мой набор нод для проброса лампочек из НА в HomeKit и обратно. Думаю, тут большинству все понятно даже по названиям, но пройдемся по каждой, для начинающих.

Самая первая нода - это Events: state, в свойствах которой мы указываем, события от какого устройства мы хотим получать. В данном случае, это light.bed_strip. Обратите внимание, если галка Output only... установлена, ее нужно снять, иначе вы будете получать события только при включении/выключении лампы, а нам нужны все.

Вторая нода - это function. В ней я подготавливаю сообщение для передачи в НК и провожу дополнительные манипуляции со значениями яркости и цвета. Чтобы не срисовывать с картинки, ниже привожу ее код целиком. Обратите внимание, привожу значения яркости к целому числу, предварительно разделив на 2.55. Если получившееся значение 0 - ставлю значение по умолчанию в 10%. В этой же ноде я записываю все значения во flow переменные, они нам пригодятся потом.

var characteristic = {};

if(msg.data.new_state.state === 'on') {
    characteristic.On = true;

    if(msg.data.new_state.attributes.hasOwnProperty("brightness") && msg.data.new_state.attributes.brightness !== undefined) {
        characteristic.Brightness = parseInt((msg.data.new_state.attributes.brightness / 2.55).toFixed()) || 10;
    } else {
        characteristic.Brightness = 100;
    }
    
    if(msg.data.new_state.attributes.hasOwnProperty("hs_color") && msg.data.new_state.attributes.hs_color[0] !== undefined) {
        characteristic.Hue = msg.data.new_state.attributes.hs_color[0];
    } else {
        characteristic.Hue = 0;
    }
    
    if(msg.data.new_state.attributes.hasOwnProperty("hs_color") && msg.data.new_state.attributes.hs_color[1] !== undefined) {
        characteristic.Saturation = msg.data.new_state.attributes.hs_color[1];
    } else {
        characteristic.Saturation = 0;
    }
    

    var brg_name = msg.topic   '.brightness';
    var sat_name = msg.topic   '.saturation';
    var hue_name = msg.topic   '.hue'
    
    flow.set(brg_name, characteristic.Brightness);
    flow.set(sat_name, characteristic.Saturation);
    flow.set(hue_name, characteristic.Hue);

} else {
    characteristic.On = false;
}


msg.payload = characteristic;
return msg;

Кстати, я очень не люблю использовать "постоянные" переменные, например, если скопировать ноду, в которой пишется что-то в переменную test, у нас будет две ноды, работающие с одной переменной. А значит, в какой-то момент результаты могут быть неожиданными. Поскольку я понял, что без flow переменных все-таки не обойтись, то я решил сделать так, чтобы имена переменных были сами переменными, в результате, для каждой лампы имена будут уникальными. 

Имя переменной, в которой хранится значение яркости в данном случае, например, будет light.bed_strip.brightness. При другом имени лампочки в НА - будет другое имя. Но этим можно голову не забивать, к ним обращаться не требуется.

Дальше идет стандартная нода НК, в которой нужно прописать имя устройства, под которым лампочка появится в НК и какие характеристики она поддерживает. Для лампочек из Икея, например, которые только белые, стоит убрать Hue и Saturation, в этом случае нода не будет посылать эти параметры, хоть обтыкайся в цветовой круг в HK App.

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

Теперь мы подходим еще к одному свичу, который называется On/Off, но при этом имеет три выхода. Даже более, в свойствах у него идет булева проверка ложь/истина и все остальное. Такая вот неформальная логика получается :) Обратите внимание, что проверяю не просто msg.payload, а его свойство On - msg.payload.On

Логика его работы проста: если msg.payload.On установлен в false - то пойдем в первый выход, который приведет нас к выключению света, если true - то идем к включению света, а если что-то еще или не установлен - то пойдем в третий выход, предполагая, что данное сообщение предназначено для регулировки яркости или цвета.

Ноды выключения и включения света - это ноды call service и выглядят идентично, за исключеним вызываемого сервиса. На всякий случай все равно их здесь приведу, хотя думаю, они всем известны.

Основной интерес представляют оставшиеся две с половиной ноды, а именно, function, join и call service. 

Нода function выглядит вот так и с ней пришлось повозиться. Ее задача - сформировать полное сообщение для отправки в ноду сервиса, а именно, установить параметры яркости, цвета и насыщенности. Берет она один из сообщения (а нода НК шлет по одному параметру в сообщении, что для меня было весьма огорчительно), а два остальных - из записанных ранее во flow переменных. В случае, если лампа включается впервые (например, только добавлена), то параметров hue и saturation не будет, НА еще не записал ничего в переменные flow, читать нам нечего. Но при этом, iOS при включении новой лампы, в некоторые моменты, шлет два сообщения подряд, On=true (что логично) и второе brightness=100 (что лишнее, из-за чего мы в свиче On/Off уходим по третьему пути и как раз попадаем в ситуацию отсутствия значений Hue и Saturation). Чтобы не возникала ошибка в такой ситуации, добавлена проверка наличия этих значений и если их нет, то мы в НА отправим только параметр яркости. Читабельный код ниже.

var brg_name = msg.topic   '.brightness';
var sat_name = msg.topic   '.saturation';
var hue_name = msg.topic   '.hue'

var new_val = {};

if(msg.payload.Brightness >= 0) {
    new_val.brightness = msg.payload.Brightness;
    flow.set(brg_name, msg.payload.Brightness);
    new_val.saturation = flow.get(sat_name);
    new_val.hue = flow.get(hue_name);
}

if(msg.payload.Hue >= 0) {
    new_val.brightness = flow.get(brg_name);
    new_val.saturation = flow.get(sat_name);
    new_val.hue = msg.payload.Hue;
    flow.set(hue_name, msg.payload.Hue);
}

if(msg.payload.Saturation >= 0) {
    new_val.brightness = flow.get(brg_name);
    new_val.saturation = msg.payload.Saturation;
    flow.set(sat_name, msg.payload.Saturation);
    new_val.hue = flow.get(hue_name);  
}

new_val.data = {
   "brightness_pct" : new_val.brightness
}

if(new_val.hue !== undefined && new_val.saturation !== undefined) {
        new_val.data.hs_color = [new_val.hue, new_val.saturation];
}

msg.payload = new_val;
return msg;

Нода join. Спорная нода и ее присутствие обусловлено лишь тем, что yeelight очень не любит спам на порту и если послать ему некое количество сообщений за единицу времени, он временно отрубит возможность управлять лампой по сети. А при регулировке яркости или задумчивом выборе цвета на цветовой палитре, нода НК просто сыпет сообщениями. Чтобы их чуть-чуть замедлить, я и добавил эту ноду. Ее негативный импакт - некоторые операции начинают отрабатывать с задержкой в 1 секунду, лампа становится слегка задумчивой, например, если менять только цвет и не менять его насыщенность. В общем ее можно смело убрать, но помнить про реакции yeelightов на спам.

Ну и последняя нода - тоже call service с типом сервиса turn_on. Обратите внимание, что поле Data у этой ноды не заполнено никак, необходимые данные мы передаем напрямую в msg.payload.data. Нода получается полностью идентичной ноде включения и выделена отдельно только для наглядности.

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

Но если у вас еще есть время и желание, давайте порассуждаем над такой чисто гипотетической ситуацией, когда у кого-то лампочек не одна и даже не две? Я понимаю, конечно, что это ситуация чисто гипотетическая, ну у кого может быть три лампы, тем более умных, не говоря уже о большем количестве? Но вдруг? 

В таком случае, flow у этого человека может выглядеть как-то вот так, например.

Ну а что тут такого? Предполагаю, что у кого-то это и правда выглядит вот так и не вызывает никаких вопросов. Но мою тонкую душевную организацию от этого просто рвет на тысячу маленьких медвежат. Но даже если забить на тонкую душевную организацию, то если вдруг по прошествии времени где-то найдется ошибка в функции? Придется править n x [количество ламп] раз. Ну прям совсем не вариант, согласны? А значит, приступаем к операции "оптимизация"

Надеюсь, никто не будет спорить, что вся та портянка, приведенная выше, после оптимизации ее до такого вида, выглядит куда лучше? Если согласны - поехали смотреть, как тут все устроено.

На самом деле, отличий от первого варианта тут минимум, именно поэтому он и был так подробно расписан. 

Что изменилось? Первая нода теперь state changes с фильтром light., чтобы не загромождать flow соединениями, используются ноды link in и link out и немного поменялись ноды НК и вызовов сервиса. А теперь  подробнее.

Первая нода - events state с фильтром по подстроке, получает все сообщения от НА для домена light.

Нода функции "To HomeKit" не изменилась совсем.

Как пользоваться коннекторами, даже если не знаете, разобраться очень просто. Давайте им понятные имена и помните, что out - это исходящее сообщение, in - входящее. Но неправильно вы их даже прицепить не сможете, так что с ними все.

Ноды homekit. Единственное, что требует правки при добавлении нового устройства. Теперь тут нужно править два поля: имя устройства, под которым оно будет видно в НК, и Topic - то, сообщения с каким топиком (заголовком) будут приниматься этой нодой. Не забудьте только поставить галку Filter on Topic. Для примера на рисунке - эта нода будет ловить и обрабатывать сообщения только в том случае, если его заголовок содержит light.wall_strip_1 - это entity_id из НА.

У ноды свича On/Off пропал третий выход и ее выходы теперь настроены на false и остальное. Под остальным у нас проходит включение света или регулировка яркости/цвета.

Полностью пропала спорная нода join, потому что в этом случае она начинает мешать прохождению сообщений, когда вы просите Сири включить или выключить все лампы сразу. Ну да и не беда, просто помним про возможное поведение yeelight. Кстати, если в самом НА дергать ползунки яркости или цветности - лампы точно также благополучно отключают на какое-то время возможность управления по сети, так что ничего мы не теряем.

Ноды call service on и off - меняем Entity Id на {{ topic }}.

Поле Data по-прежнему оставляем пустым, вся нужная информация идет в payload.data.

И вот на этом уже действительно все. Теперь, когда нужно будет что-то поправить, достаточно будет открыть одну ноду, а добавление новой лампы сводится к копированию ноды НК с обоими линками и внесением правок в два поля внутри нее.

Спасибо что дочитали, надеюсь все у вас получится.


Все новости мира умных домов - t.me/SprutAI_News или Instagram
Остались вопросы? Мы в Telegram - @Soprut

Хочешь умный дом но нет времени разбираться?
Посмотри примеры работ и выбери себе интегратора.
  1. Ошев Андрей (andrey_oshev)
    Ошев Андрей (andrey_oshev) 19 дней назад

    Не ясно зачем нужен NodeRed, если ХА сам умеет в HomeKit, либо зачем нужен ХА, если можно сразу заводить в NodeRed

    • Владимир iStitch07 (iStitch07)

      Там прям в самом первом абзаце на оба вопроса даны ответы :)

        • Владимир iStitch07 (iStitch07)
          Владимир iStitch07 (iStitch07) отредактировано 19 дней назад

          Хорошо, мне букв не жалко, попробую объяснить, что мною двигало, пойдем по порядку:
          1 зачем нужен NodeRed, если ХА сам умеет в HomeKit
          Умеет, и для быстрого старта этого вполне достаточно. Но у него есть минусы - из самых неприятных - это когда он после обновления на очередную версию вдруг берет и пересоздает тебе устройства, иногда дублируя их, иногда просто создавая заново. Например один из моих датчиков движения в НК представлял из себя сгруппированные 8 (!!!) датчиков, 7 из которых были в статусе "нет ответа". Не знаю почему этот датчик ему так не понравился, но пересоздавал он его регулярно. Поэтому у кого-то, как например и у меня, может возникнуть желание написать в конфигурации # homekit: и пробрасывать туда устройства с более предсказуемыми результатами.

          2 зачем нужен ХА, если можно сразу заводить в NodeRed
          Можно. Но если есть лампы от Yeelight, IKEA, OSRAM, например? Для каждой лампы придется писать свой коннектор, а в таком варианте я использую уже написанные и проверенные тысячами людей коннекторы, превращая ХА в универсальный интерфейс ко всем лампам. Мне не нужно каждый раз думать, какой должен быть payload для того что бы включить конкретно эту модель лампочки или какие она поддерживает параметры. В моем случае я абсолютно свободно управляю как раз лампами от yeelight (wi-fi), IKEA (Zigbee) и Koogeek (homekit)
          Да и в любом случае в ХА лампы присутствуют, если вообще ХА есть. И самому иногда на картинках потыкать удобнее, да и гостям похвастаться хорошо сделанным Lovelace UI лучше, чем скриншотом из НК :)

  2. (nikita.yuryev)
    (nikita.yuryev) 17 дней назад

    А если установлен hass.io в Docker, вНутри HA стоит NodeRed. Можно ли так же устройства пробросить в home kit или создать виртуальные в среде nodered? Какой нужен пакет для homekit в Nodered, на сколько я понял это homekit-bridged?

    • Владимир iStitch07 (iStitch07)
      Да, модуль node-red-contrib-homekit-bridged - единственный живой проект, который я нашел, и максимально документированный.
      По поводу hass.io не знаю, не пробовал. Проще всего попробовать - это создать в node-red какой-нибудь выключатель и пощелкать им, привязав ноды дебага к выходам. Если сообщения из НК будут приходить - то значит все ок.
      Только следует учитывать, что если в самом НА включен homekit то может не получиться, они конфликтуют "по портам", устройство то создается, а вот информация о действиях с ним в node-red не поступает.

      • (nikita.yuryev)
        (nikita.yuryev) 17 дней назад

        попробую все что можно))

      • (nikita.yuryev)
        (nikita.yuryev) отредактировано 17 дней назад

        в Hass.io (в Docker) заработало, конфликтов с интеграцие homekit hass нет, все что нужно пробрасывается нормально)

        Был мой косяк: не дал имя мосту (в настройках nodered) 

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

Устройства в материале

Smart Light Strip

Производитель: Yeelight

Yeelight Wi-Fi Bulb

Производитель: Yeelight

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

Интернет-магазин yourhomekit.ru

+7 914 550-51-11
Промокод:
SPRUT-BLG
Размер скидки:
8%
Cамый большой ассортимент в России аксессуаров Apple HomeKit

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

09 ноября 2018, 20:54
Кейс создания умного дома без каких либо прокладок в виде Raspberry pi
04 сентября 2018, 12:14
Интеграция RGB ленты на ESP8266 с прошивкой tasmota в систему HomeBridge (HomeKit)
17 декабря 2019, 17:49
Универсальный привод для автоматического удаленного открытия окон с простой интеграцией в умные дома.
15 октября 2018, 09:05
Прошивка для Sonoff c нативным HomeKit
15 ноября 2018, 09:42
Способы автоматизации механических ворот
27 октября 2018, 12:20
Нативный Термостат для котла на ESP8266 с поддержкой Apple HomeKit
15 ноября 2018, 13:11
Xiaomi Mi Remote 360 добавляем Apple HomeKit
01 октября 2018, 07:43
Нативный HomeKit на ESP8266
24 августа 2018, 12:18
Пошаговая установка HomeAssistant
20 октября 2018, 22:57
Теоретические основы протокола MQTT и описание того, как он работает и для чего используется