Лирическое отступление: когда-то при переходе с 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 — ну так у меня его и нет, не проблема.
Комментарии (7)
 - xSVPx25.01.2025 06:22- Можно группировать по типу, как делаете вы, а можно по источнику, как делают многие. - Т.е. ветки имеют вид типа /место/устройство/данные или команды. - И совершенно не факт, что ваш подход лучше. Вы группируете значения по типу, но обычно надо знать какие значения есть для конкретного датчика.  - JBFW Автор25.01.2025 06:22- Ну я-то под себя делаю ) - Как раз для того чтобы логика управления не смешивалась с конкретной реализацией. - Управление даёт команду "включи" - а кто конкретно и как будет это выполнять решает "драйвер", который как раз и формирует специальную команду под конкретный девайс. - Например, вебинтерфейс может вообще не знать как сегодня реализовано выполнение команды, или откуда берутся данные "температура на улице", ему это без разницы. За технические подробности отвечает скрипт-драйвер, который принимает девайс-специфические параметры и выдает уже какой-то результат. - Что-то типа HAL - абстрагируемся от железа. В случае замены - просто меняется что-то в драйвере, не затрагивая остального. - Ещё пример: включение ночного света: драйвер знает, что на вот такие и такие реле надо отправить такую команду, на такой диммер - другую, на ещё один - третью. А вот саму команду включить или выключить - можно отправить хоть кнопкой, хоть автоматически по реле или по таймеру, разницы нет. 
 
 - past25.01.2025 06:22- Есть вот такая конвенция на этот счёт. Там не только про mqtt - https://homieiot.github.io/ 
 
           
 
olku
Не подскажете что почитать про протоколы умных счётчиков потребления газа, электроэнергии, тепла, и организации связи с ними?
JBFW Автор
Поискать в документации производителя?
С электричеством проще всего, у большинства счётчиков есть выход-контакт, замыкается скажем 1 раз на каждый 1 Вт*ч - ставим счётчик импульсов, считаем, передаем по mqtt.
Есть водяные счётчики по такой же схеме, наверно газовые тоже, этого не знаю
Telmah
В газовых геркончики вешают, а в нутре магнитик крутится, принцип тоже - считаем замыкания
olku
Беглый поиск выдает IEC 62056, ANSI C12.18, OSGP, DLMS, COSEM, IDIS. Складывается впечатление что вендоры в погоне за вендор-локами смешивают сетевые и прикладные протоколы с интерфейсами. Хотелось бы систематизировать многообразие smart meters.