Статья

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

Преамбула: по неким необъяснимым причинам возникло желание пробрасывать свет из 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. При другом имени лампочки в НА - будет другое имя. Но этим можно голову не забивать, к ним обращаться не требуется.

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

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

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;

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

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

В таком случае, flow у этого человека может выглядеть как-то вот так, например. Ну а что тут такого? Предполагаю, что у кого-то это и правда выглядит вот так и не вызывает никаких вопросов. Но мою тонкую душевную организацию от этого просто рвет на тысячу маленьких медвежат. Но даже если забить на тонкую душевную организацию, то если вдруг по прошествии времени где-то найдется ошибка в функции? Придется править n x [количество ламп] раз. Ну прям совсем не вариант, согласны? А значит, приступаем к операции "оптимизация"
Надеюсь, никто не будет спорить, что вся та портянка, приведенная выше, после оптимизации ее до такого вида, выглядит куда лучше? Если согласны - поехали смотреть, как тут все устроено.На самом деле, отличий от первого варианта тут минимум, именно поэтому он и был так подробно расписан. Что изменилось? Первая нода теперь state changes с фильтром light., чтобы не загромождать flow соединениями, используются ноды link in и link out и немного поменялись ноды НК и вызовов сервиса. А теперь  подробнее.

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

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

6

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

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

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

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


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


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

Устройства


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