Благодарности: денежное общество и мойка авто

Domoticz c Node-Red на OpenWrt. Проброс mqtt из SLS и обратно.

Domoticz есть в стандартных пакетах OpenWrt, поскольку я буду в Domoticz заводить устройства через mqtt то на роутер надо поставить mosquitto, Node-Red в стандартной сборке я не нашел, есть 2 варианта установки:

1. Установка Node-Red по этой инструкции.

2. Сборка OpenWrt с пакетами Node.js версии 10, 12 или 14 и Node-red. а так же нам может понадобиться node-npm, собираем OpenWrt из исходников добавив пакеты node.js отсюда.

После установки Domoticz будет на порту 8080 по адресу роутера, Node-red по адресу роутера на порту 1880.

В Domoticz добавляем mqtt

и устройство Dummy

далее создаем виртуальные датчики, фактически на каждый топик в mqtt свой датчик, это может быть выключатель, температура, напряжение и т.п.

Чтобы смотреть куда Ваши устройства публикуют свои статусы рекомендую установить mqtt клиента, я использую кросс платформенный MQTT Explorer

Проблема в том, что Domoticz случает только топик domoticz/in и публикует в топик domoticz/out

Есть несколько вариантов решения данной проблемы, одна из которых это использование плагина для Domoticz, я лишь нашел Python плагины, но проблема в том, что видимо Domoticz для OpenWrt собран без поддержки Python и Python плагины не работают.

Есть вариант использовать оборудование с поддержкой Domoticz или возможностью настройки публикации, но это сильно сокращает список оборудования которое возможно использовать, да и например в прошивке Tasmota можно указать топики, но тогда кроме данных mqtt сервера надо настраивать топики на каждом конечном устройстве и обработчик всё равно надо писать, по этому на мой взгляд лучшее решение это Node-Red.

Настройка Node-Red

Вообще всю автоматизацию возможно использовать только с Node-Red, визуализация так же возможна с помощью Node-Red Dashboard, но мы будем использовать Node-Red только для конвертации сообщений в и из Domoticz.

Для этого надо создать 2 потока, фактически это потоки конвертирования из mqtt и mqtt. Поток mqtt to domoticz это конвертер из топиков # в топик domoticz/in, поток domoticz to mqtt это конвертер из потока domoticz/out в топики устройств.

Вот так выглядит часть кода конвертера из mqtt в domoticz/in (взято отсюда)

красным подчеркнуто последний уровень топика, синим idx устройства в Domoticz, при этом вызываемая функция зависит от типа датчика в Domoticz.

Для того чтобы каждый раз не ползать в Node-Red при добавлении нового устройства и не сопоставлять idx устройств с топиками и типом и не находить место куда надо добавить нужный код мы пойдем другим путем.

Кроме того данный метод не приемлем если использовать обратный поток из domoticz/out когда значение выключателя для переключения находиться в другом топике нежели значение для статуса, например у меня в шлюзе SLS реле xiaomi имеет статус в топике sls/rele1/state_l1 а чтобы его переключить надо опубликовать в топик sls/rele1/set/state_l1

При публикации в топике sls/rele1/set/state_l1 из потока domoticz to mqtt поток mqtt to domoticz запускается поскольку изменилось значение sls/rele1/state_l1 которое в потоке mqtt to domoticz изменяет статус выключателя в Dоmoticz, а поскольку статус изменился, то Domoticz публикует в топик sls/rele1/set/state_l1 и так по кругу обеспечивая такую ddos атаку собственного mqtt сервера. после 3 минут работы потоков и последующего отключения потоков в Node-Red в mqtt сыпались сообщения ещё 1 минуту. По этому нам надо как то решить эту проблему.

Метод такой: при получении сообщения из топика мы будем искать в свойствах устройств такой топик, при совпадении полученного топика и топика в устройстве и если значение топика отличается от значения в устройстве (чтобы не дергать лишний раз одинаковыми значениями) мы будем формировать json запрос в топик domoticz/in

Для получения параметров устройств мы воспользуемся Domoticz API/JSON URL’s

поток будет выглядеть следующим образом:

функция 1 получает от mqtt данные о топике и его значении, эти данные надо сохранить для того чтобы их использовать совместно с полученными данными от Domoticz о списке устройств и их параметрах, что при такой конструкции в одной функции невозможно. Тут нам на помощь приходят переменные, а именно переменные в пределах потока (flow.set), то есть при установке в функции 1 её можно получить в функции 2. Но этого не достаточно, поскольку существует проблема синхронизации запуска потока. Дело в том, что если устройства будет публиковать в несколько топиков данные с интервалом несколько милисекунд, а именно так происходит например когда нажимаешь клавишу выключателя xiaomi, публикуются данные в 6 топиков, то есть поток запускается 6 раз (6 экземпляров потока). Когда в первом экземпляре потока мы присвоим переменной какое то значение, начнется выполняться http запрос в этом экземпляре потока, в это время запуститься второй экземпляр потока который присвоит этой же переменной свое значение и когда первый экземпляре потока дойдет до функции 2 и там считается значение этой переменной, то значение будет уже из второго экземпляре потока. По наблюдениям когда второй экземпляре потока в функции 2 будет считывать переменную заданную в функции 1, то он получит значение которое присвоено в 18-м экземпляре потока, то есть на момент выполнения функции 2 в 17-и экземплярах потока в переменной будет значение из 17-го экземпляра потока и мы получим в функции 2 только значение 17 экземпляре потока во всех 17 потоках.

Чтобы этого избежать надо нумеровать переменные в зависимости от номера потока. Мы знаем, что  каждый топик запускает поток, в потоке один раз выполняется функция 1, потом один раз выполняется http запрос, потом один раз выполняется функция 2. То есть в 10-м потоке функция 1 будет выполнена 10-й раз, функция 2 будет выполнена тоже 10-й раз, в 18-м потоке обе функции будет выполнены 18 раз. Теперь в каждой функции нам надо сделать счетчик запуска функции, чтобы этот счетчик могла менять только функция, для этого мы будем использовать переменную в контексте (context.set).

В Node-Red имеются следующие периоды жизни переменных:

var myParam = «моя переменная»; — будет жить только внутри функции при выполнении, закончилась выполняться функция, переменная исчезла, при новом запуске функции значения этой переменной не будет пока его заново не присвоить.

context.set(‘myParam’,»моя переменная»);  — будет жить в пределах функции в которой присвоена, при новом запуске функции значение этой переменной сохраниться.

flow.set(‘myParam’, «моя переменная»); — будет жить в пределах всего потока, при завершении потока сохраняется и может быть получена при повторном запуске потока. Не видна в других потоках

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

Поток mqtt to domoticz будет выглядеть так

данные в узлах:

 

 

Полностью json потока для экспорта в Node Red

теперь создадим обратно из Domoticz в Node-red, поток будет выглядеть так

 

Полностью json потока для экспорта в Node Red

Теперь нам осталось в устройствах Domoticz в описаниях добавить пути к топикам, для статусов только путь к топику со статусом в формате: topic: путь/к/топику;, для тех кто публикует (выключатели) путь set в формате: topic_set: путь/к/топику;

для датчиков:

для выключателей:

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

Поделиться в соц. сетях

Опубликовать в Facebook
Опубликовать в Google Buzz
Опубликовать в Google Plus
Опубликовать в LiveJournal
Опубликовать в Мой Мир
Опубликовать в Одноклассники
Опубликовать в Яндекс