Упорядочиваем MQTT

от автора

Лирическое отступление: когда-то при переходе с Windows на UNIX для меня стал открытием тот факт, что файловая система в UNIX намного более стандартизирована и упорядочена, чем в Windows (особенно в Windows того времени).

В самом деле, если в Windows было в порядке вещей свалить в один каталог саму программу, ее настройки, вспомогательные файлы, и всё это положить куда угодно как попало — то в UNIX была определенная иерархия файлов, что где должно лежать, чтобы было по фен-шую: bin, lib, var, etc…

Казалось бы, при чем тут MQTT и наши дни? При том, что при попытке организовать взаимодействие между устройствами при помощи MQTT сообщений я увидел тот же самый бардак, какой был когда-то в Windows: одна система отправляет сообщения как zigbee/#, другая как mesh/#, третья еще как-то, все самодельные устройства могут отправлять вообще любые топики, и если потом требуется связать одно с другим — то нужно помнить что на что реагирует и как что рассылает.

И чтобы превратить бардак в порядок — придумал сам для себя некую стандартизацию сообщений.
Например, если происходит какое-то событие — это топик event/XXX, если событие требует реакции — это alarm/XXX, если нужно отправить команду — command/XXX.
Просто сообщения, например, текущий статус, etc/XXX.

И оказалось, что такая мелочь сильно упрощает жизнь: скажем, если в ответ на событие «пропало напряжение» нужно отправить в телеграм текст — можно, конечно, написать отдельный обработчик для этого, и еще для 100500 событий.
А можно написать обработчик, который при появлении etc-сообщения от устройства контроля напряжения просто сформирует alarm/message с нужным текстом, и уже другой обработчик, слушающий конкретно alarm/message — отправит текст куда надо.

При этом блок контроля напряжения не заботится о способе доставки, скрипт доставки доставит любой текст, не в телеграм так в СМС, остается сделать скрипт, который отслеживает жалобу блока контроля и отправляет alarm.
Для одной задачи это конечно избыточно, но если подключить к той же схеме «контроль температуры», «контроль протечки», «контроль открытия ворот», «контроль срабатывания замка» — мы просто отслеживаем разные etc-сообщения и формируем alarm с разным текстом, всё остальное происходит само.

Если надо включить свет — отправим не alarm, а command/XXX — и реле, которое слушает конкретно этот command, сработает, хоть одно, хоть 10.
Если надо именить настройки котла отопления — достаточно просто отправить соответствующий command, который слушает именно котел — и снова всё происходит как бы само.

Причем даже неважно, в какой сети работает котел: простой WiFi, Zigbee или PainlessMesh — достаточно лишь запустить скрипт-драйвер, который преобразует, скажем, command/heat1 в mesh/to/141455135 или в команды Zigbee-протокола.

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

Ну а реализация всего этого очень простая: любое самодельное устройство всегда можно заставить реагировать на свою команду и отправлять свое сообщение так как требуется, а интерфейс к «чужим» и скрипты автоматизации прекрасно пишутся всё на том же Perl:

#!/usr/bin/perl -w  $|=1;  use Net::MQTT::Simple; use JSON; use Data::Dumper;  $SIG{CHLD} = "IGNORE";  ########################################  my $mqtt = Net::MQTT::Simple->new("127.0.0.1");  # send MQTT message sub pub {  my ($topic, $message, $retain) = @_;  my $pid = fork();  if(defined $pid && $pid == 0){    if($retain){      $mqtt->retain($topic => $message);    }else{      $mqtt->publish($topic => $message);    }    sleep(2);    exit(0);  } }  ######################################## # MAC термодатчика на складе my $term = '287d92440600144a7'; # температура по умолчанию my $t0 = 7;  # processing $mqtt->run(   'etc/stock1/control' => sub {     my ($topic, $message) = @_;     my $data = undef;      if($message =~ /^{.*}$/){       $data = from_json($message);     }      if(defined $data && defined $data->{ $term }){        my $t = $data->{$term};        my $m = {         dtm         => $data->{dtm} || time,         temperature => $data->{ $term },       };       pub("state/temperature/stock1", to_json($m), 1);        if($t > $t0){         pub("command/stock1/control","off 1");       }       elsif($t < $t0){         pub("command/stock1/control","on 1" );       }      }   },   'command/stock1/settemp' => sub {     my ($topic, $message) = @_;     my $data = undef;      if($message =~ /^{.*}$/){       $data = from_json($message);     }      if(defined $data && defined $data->{ temperature }){       $t0 = $data->{ temperature };     }    },   #=============================== );  exit; 

Этот скрипт просто слушает сообщения от блока контроля на складе, и занимается тем что включает или выключает обогреватель, поддерживая температуру 7 градусов.
При этом текущая температура помещается в retain-топик state/temperature/stock1, откуда ее можно всегда узнать, а команда command/stock1/settemp позволяет установить температуру, которую будет поддерживать автоматика.

Сам блок контроля умеет только отправлять температуру и включать-выключать реле (ну есть еще встроенная защита, температура не может быть выше 40, но это тут не важно).
Его можно переделать на Zigbee, нужно будет только дописать пару правил в скриптах Tasmota, не меняя всего остального.

Для запуска этого и подобных скриптов прекрасно подходит коробочка-TV-box: много памяти не требуется, производительности тоже, питание 5В через адаптер от аккумулятора, управление удаленное…

В эту схему не вписывается HomeAssistant — ну так у меня его и нет, не проблема.


ссылка на оригинал статьи https://habr.com/ru/articles/876526/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *