Skip to content

About

Telegram-бот для контроллеров Wiren Board: команды из Telegram в MQTT, ответы и уведомления из wb-rules

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

Repository files navigation

wb-telegram

Telegram-бот для контроллеров Wiren Board: команды из Telegram публикуются в MQTT, правила wb-rules на них реагируют и отвечают. Пользователи и команды настраиваются в веб-интерфейсе контроллера.

Бот — тонкий транспорт Telegram ⇄ MQTT. Что делать по команде, решают правила wb-rules; бот только доставляет вызовы и ответы.

Установка на контроллер

Подходит для Wiren Board 6, 7 и 8. Модель знать не нужно: установщик сам определит архитектуру (WB8 — arm64, WB6/WB7 — armhf) и скачает нужный пакет из последнего релиза. Контроллеру нужен интернет.

  1. Зайти на контроллер по SSH под root — с компьютера в той же сети:

    ssh root@<адрес-контроллера>

    Адрес — тот же, по которому открывается веб-интерфейс контроллера в браузере (IP вида 192.168.1.50 или имя вида wirenboard-XXXXXXXX.local, где XXXXXXXX — серийный номер с наклейки).

  2. Установить бота одной командой:

    curl -fsSL https://github.com/Format-C-eft/wb-telegram/releases/latest/download/install.sh | sh

    После установки бот выключен — systemctl status wb-telegram покажет inactive, так и должно быть.

  3. Создать бота в Telegram: написать @BotFather команду /newbot, придумать имя — BotFather пришлёт токен вида 123456789:AAH....

  4. Настроить в веб-интерфейсе контроллера: Настройки → Конфигурационные файлы → «Telegram-бот»:

    • «Токен бота» — токен от BotFather;
    • «Бот включён» — поставить галочку;
    • «Записать». Бот запустится.
  5. Добавить людей. Каждый пишет боту в Telegram что угодно, бот отвечает «Нет доступа, ваш chat_id: N». Это число вместе с именем вписывается в таблицу «Пользователи» → «Записать». «Системные уведомления» — получать ли сообщения правил, адресованные всем.

  6. Добавить команды во вкладках «Команды» (например, gate — «Открыть ворота») и правило wb-rules, которое на них отвечает. Готовый пример:

    cp /usr/share/doc/wb-telegram/examples/telegram-example.js /etc/wb-rules/

    Подробности — в разделе Правила wb-rules.

Обновление — та же команда из шага 2, настройки и токен сохраняются (если в новой версии пакета изменился конфиг по умолчанию, он кладётся рядом как /etc/wb-telegram.conf.dpkg-dist; новые настройки бот подставляет сам). Конкретная версия:

curl -fsSL https://github.com/Format-C-eft/wb-telegram/releases/latest/download/install.sh | WB_TELEGRAM_VERSION=v0.1.12 sh

(список — на странице релизов).

Удаление: apt purge wb-telegram (вместе с токеном) или apt remove wb-telegram (настройки и токен остаются).

Если что-то не так — журнал journalctl -u wb-telegram -f и раздел Ошибки и диагностика.

Когда использовать

  • Нужно управлять домом на Wiren Board из Telegram нескольким людям, у каждой команды — свой список тех, кому она доступна.
  • Нужны уведомления из правил wb-rules (ошибки оборудования, события) в Telegram.
  • Нужно, чтобы новая команда добавлялась без пересборки бота: строка в настройках плюс правило.

Когда не использовать

  • Нужны роли, группы, права сложнее «кому доступна команда».
  • Нужна логика прямо в боте без правил wb-rules: без правила команда ничего не делает, пользователь получит «ответа от правил нет».
  • Нужны inline-кнопки, форматирование (Markdown/HTML), графики, webhook — в первой версии этого нет.

Как это работает

Telegram ──/gate 22──▶ wb-telegram ──JSON──▶ /devices/telegram_bot/controls/cmd_gate ──▶ правило wb-rules
Telegram ◀──текст──── wb-telegram ◀──────── /devices/telegram_bot/controls/send/on ◀── cmd.reply(...) / tg.send(...)
  1. Пользователь пишет боту /gate 22.
  2. Бот проверяет chat_id и доступ к команде и публикует вызов JSON-ом в контрол cmd_gate устройства telegram_bot.
  3. Правило wb-rules (через модуль telegram) получает вызов, делает своё дело и отвечает cmd.reply("...") — это запись в контрол send.
  4. Бот доставляет ответ автору вызова.

Настройки

Форма «Telegram-бот» в веб-интерфейсе:

Поле По умолчанию Описание
Бот включён выкл. Выключенный бот не запускается и убирает своё устройство из MQTT
Токен бота пусто Формат 123456:ABC.... При открытии формы всегда пустой; пусто при записи — токен не меняется
Токен — Только для чтения: «Задан» / «Не задан»
Удалить токен выкл. Удаляет сохранённый токен, бот останавливается. Одновременно с новым токеном — ошибка записи
Таймаут подключения к Telegram, с 30 5–120. Время на TCP-соединение и TLS-рукопожатие с api.telegram.org. Увеличьте, если в журнале TLS handshake timeout
Таймаут опроса Telegram, с 30 1–50. Сколько один запрос getUpdates ждёт новых сообщений (long polling)
Таймаут ответа правила, с 10 1–300. Если правило не ответило за это время, пользователь узнает. Команды старше этого времени не выполняются
Отладочный лог выкл. Подробный журнал (в том числе тексты сообщений). Токен не пишется никогда
Пользователи пусто Таблица: имя (уникальное), chat_id (уникальный), «Системные уведомления»
Команды пусто До 100 команд: имя (a-z, 0-9, _, до 32 символов, кроме start и help), описание (1–256 символов, видно в меню Telegram), «Кому доступно» (пусто — всем)

Проверки, которые не выражаются схемой формы (уникальность имён, ссылки команд на существующих пользователей, формат токена, «новый токен + удалить токен»), делает сам бот при записи. Если запись отклонена, веб-интерфейс показывает «Ошибка записи», файл не меняется, причина — в журнале journalctl -u wb-mqtt-confed.

Файлы на контроллере:

Файл Права Содержимое
/etc/wb-telegram.conf как пишет wb-mqtt-confed все настройки, кроме токена
/etc/wb-telegram/token 0600, каталог 0700 токен; удаляется при apt purge

Флаги сервиса можно задать переменной WB_TELEGRAM_OPTIONS в /etc/default/wb-telegram (файл необязателен):

Флаг По умолчанию Описание
-broker unix:///var/run/mosquitto/mosquitto.sock, если сокет есть, иначе tcp://localhost:1883 адрес MQTT-брокера
-config /etc/wb-telegram.conf путь к конфигу
-token-file /etc/wb-telegram/token путь к файлу токена
-debug выкл. отладочный журнал с самого старта (до чтения конфига)
-version — напечатать версию и выйти

-to-json и -from-json — служебные режимы для wb-mqtt-confed, вручную не нужны.

Протокол MQTT

Устройство telegram_bot по MQTT Conventions Wiren Board, все сообщения retained:

Топик Значение
/devices/telegram_bot/meta {"driver":"wb-telegram","title":{"en":"Telegram Bot","ru":"Telegram-бот"}}
/devices/telegram_bot/meta/error r — бот остановлен или потерял связь с брокером (LWT); пусто — бот готов
/devices/telegram_bot/controls/send text; последнее отправленное сообщение правил
/devices/telegram_bot/controls/send/on сюда правила пишут сообщения (см. ниже)
/devices/telegram_bot/controls/send/meta/error w, пока Telegram не принимает сообщения; снимается после первой успешной отправки
/devices/telegram_bot/controls/cmd_<имя> text, только чтение; вызов команды (см. ниже). Заголовок контрола — описание команды

Вызов команды /temp 22 от пользователя «Супруга» публикуется в cmd_temp:

{"id":"8812345","ts":1759312345,"user":"Супруга","args":"22"}
  • id — update_id Telegram, уникален для каждого вызова;
  • ts — момент публикации ботом (unix, секунды);
  • user — имя из настроек;
  • args — текст после команды; суффикс @имябота отрезается, имя команды приводится к нижнему регистру.

Контролы cmd_* одноразовые: при каждом (пере)подключении бот публикует в них заглушку {}, а не последний вызов, чтобы правило не выполнило его повторно. Не пустую строку: пустое retained-сообщение в MQTT удаляет значение, wb-rules считает контрол удалённым, и на первый вызов после рестарта бота whenChanged не срабатывает. Пустое значение send при старте не публикуется по той же причине. Контролы удалённых из настроек команд убираются при старте.

Сообщение правил — запись в send/on:

Payload Кому
строка (не JSON-объект) всем пользователям с «Системные уведомления»
{"text":"...","to":["Супруга"]} названным людям, независимо от флага уведомлений
{"text":"...","reply_to":"8812345"} автору вызова с этим id (reply_to важнее to)
поле id в объекте игнорируется ботом; нужно, чтобы одинаковые сообщения подряд не склеились

Текст отправляется как есть, без разметки. Длиннее 4096 символов — обрезается с «…». Неизвестные имена в to и reply_to на неизвестный или слишком старый (больше часа) вызов только пишутся в журнал.

Правила wb-rules

Модуль telegram ставится пакетом в /usr/share/wb-rules-modules/:

var tg = require("telegram");

// Команда /gate должна быть заведена в настройках бота.
tg.onCommand("gate", function (cmd) {      // cmd.id, cmd.user, cmd.args
  dev["wb-gpio/EXT1_R3A1"] = true;
  cmd.reply("Ворота открываются");         // ответ автору вызова
});

tg.send("Котёл: ошибка E04");              // всем с «Системные уведомления»
tg.sendTo(["Супруга"], "Курьер у ворот");  // адресно, по именам из настроек

Защита от повторного выполнения встроена в модуль: он молча игнорирует пустые значения и заглушку {} (вызов без id), вызовы старше 60 с, вызовы, появившиеся до загрузки модуля (после рестарта wb-rules), и уже обработанные id. Каждое сообщение модуль отправляет JSON-ом с уникальным id.

Особенности:

  • Если бот выключен или не запущен, устройства telegram_bot в MQTT нет, и запись в telegram_bot/send (tg.send, cmd.reply) бросает исключение в wb-rules. В правилах, которые должны работать и без бота, оборачивайте отправку в try { ... } catch (e) { ... }.
  • Возраст вызова модуль считает по часам контроллера. Если часы после загрузки wb-rules перевели назад больше чем на секунду, новые вызовы будут отбрасываться до перезагрузки правил (systemctl restart wb-rules).
  • Правило может ответить несколько раз и даже после «ответа от правил нет» — все ответы будут доставлены.
  • Эмодзи в тексте можно использовать любые: wb-rules отдаёт символы вне базовой плоскости Unicode в CESU-8, бот сам пересобирает их в UTF-8.

Ответы бота

Ситуация Ответ
Незнакомый chat_id «Нет доступа, ваш chat_id: N»
/start, /help, обычный текст «Доступные команды:» и список /имя — описание
Нет ни одной доступной команды «Для вас пока нет доступных команд.»
Команда недоступна или не существует «Неизвестная команда.» и список команд
Команда старше таймаута (по времени отправки в Telegram) «Команда /x устарела (отправлена ЧЧ:ММ), повторите» — не выполняется
Нет связи с MQTT «Контроллер недоступен, команда не выполнена» — не выполняется и не откладывается
Правило не ответило за таймаут «Команда /x отправлена, ответа от правил нет»
Стикер, фото и другие сообщения без текста без ответа

Меню команд в Telegram у каждого пользователя своё — только доступные ему команды.

Ошибки и диагностика

Журнал: journalctl -u wb-telegram -f (отладка — «Отладочный лог» в форме).

Состояние systemd Что делать
«Бот выключен в настройках» inactive включить в форме
«Токен не задан» inactive задать токен в форме
«Telegram отклонил токен» inactive задать новый токен
Конфиг на диске не читается или не проходит проверку failed после повторных попыток исправить в форме; причина в журнале
Работает active —

В трёх первых случаях бот убирает своё устройство из MQTT. Включение обратно — запись формы: веб-интерфейс перезапускает сервис.

Поведение при сбоях:

  • Нет связи с Telegram. Опрос и отправка повторяются с паузой от 1 до 60 с (на 429 — столько, сколько просит Telegram). В журнал пишутся первая ошибка и восстановление. send/meta/error = w, пока сообщения не уходят. Очередь исходящих — 100 сообщений, при переполнении выбрасываются самые старые (предупреждение в журнале). Сообщения, которые Telegram отверг окончательно (4xx кроме 429), не повторяются.
  • Нет связи с MQTT. Бот переподключается сам; на время обрыва meta/error = r, команды отвечают «Контроллер недоступен».
  • Старт. Примерно первую секунду после запуска бот убирает контролы удалённых команд и подписывается на send/on; команды, пришедшие в это окно, получают «Контроллер недоступен».
  • Остановка (SIGTERM/SIGINT): опрос прекращается, очередь досылается до 5 с, meta/error = r. Если Telegram недоступен, остановка занимает до ~6 с.
  • Сообщения, отправленные боту, пока он был выключен, Telegram отдаёт после запуска; те, что старше таймаута, получают ответ «устарела».

Если в журнале Telegram: ошибка связи … TLS handshake timeout, соединение с Telegram медленное (провайдер может замедлять его): увеличьте «Таймаут подключения к Telegram» в форме или пустите контроллер в Telegram через VPN. Проверить скорость рукопожатия с контроллера: curl -sS -o /dev/null -w 'tls=%{time_appconnect}s\n' https://api.telegram.org/.

Токен никогда не попадает в журнал, в том числе в отладочном режиме: адрес Bot API в ошибках HTTP-клиента маскируется.

Разработка

Нужен Go 1.26+.

make test        # go test -race ./... -count=1 -timeout=60s -v -short
make lint-full   # golangci-lint по всему коду (ставится в ./bin)
make lint        # golangci-lint только по изменениям относительно origin/master (как в CI для веток)
make build       # бинарь wb-telegram под linux/arm64 (CGO_ENABLED=0)
make deb         # deb-пакет в Docker (см. ниже)
make generate    # моки (go.uber.org/mock)
make tidy        # go mod tidy
make clean       # удалить бинарь
make deploy CONTROLLER=root@<ip-контроллера> SSH_KEY=~/.ssh/<ключ>   # быстрая выкладка бинаря поверх пакета

Архитектура бинаря: make build DEB_TARGET_ARCH=armhf (WB6/WB7). Тесты не ходят в сеть: Telegram подменяется httptest.Server, MQTT — встроенным брокером mochi-mqtt на 127.0.0.1:0.

Раскладка:

cmd/                 точка входа: флаги, режимы -to-json/-from-json, сборка компонентов
internal/app/        жизненный цикл компонентов, сигналы, остановка
internal/config/     конфиг, проверки, файл токена, крючки формы
internal/wbdevice/   MQTT Conventions: устройство, контролы, /on, LWT, уборка
internal/telegram/   Bot API: опрос, очередь с лимитами, повторы, маскирование токена
internal/retry/      паузы повторов 1 → 60 с
internal/bot/        ядро: доступ, команды → JSON, send → доставка, таймауты
wb/                  схема формы, модуль и пример wb-rules, конфиг по умолчанию
debian/              упаковка

Сборка пакета и релизы

CI — .github/workflows/ci.yml. Прямой push в master запрещён, всё через pull request со squash-слиянием.

  • Push в любую ветку — make test и make lint (lint только изменений относительно origin/master). Это обязательная проверка «Тесты и линт»: без неё PR не вливается.
  • Слияние в master — make test и make lint-full по всему коду, информационно: релиз не блокируют, итог виден в сводке прогона и предупреждением — что поправить в следующих версиях. Затем сборка пакетов arm64 (WB8) и armhf (WB6/WB7) и GitHub-релиз v<мажор>.<минор>.<номер сборки> (мажор и минор — из debian/changelog). Слияние, меняющее только документацию (*.md, docs/, LICENSE), релиз не выпускает.
  • В релиз попадают пакеты с версией в имени, они же без версии (wb-telegram_arm64.deb, wb-telegram_armhf.deb — на них ссылается установщик) и install.sh.

Локально пакет собирается в Docker (в том числе на macOS с Apple Silicon):

make deb                          # dist/wb-telegram_<версия>_arm64.deb (WB8)
make deb ARCH=armhf               # dist/wb-telegram_<версия>_armhf.deb (WB6/WB7)
make deb ARCH=armhf DEB_VERSION=0.1.999

Внутри — scripts/build-deb.sh: dpkg-buildpackage -b -a<арх> -us -uc -d в образе golang:1.26-trixie с кросс-binutils. -d пропускает проверку Build-Depends: Go берётся из образа, а не из пакета golang-1.26-go (штатный wbdev Wiren Board на macOS не работает). Пакет сжат gzip и содержит статический бинарь, поэтому ставится на контроллеры с Debian bullseye. Ставить собранный пакет вручную: dpkg -i wb-telegram_<версия>_<арх>.deb.

Почему сделано так

  • Логика в правилах, бот — транспорт. Новая команда — строка в настройках и правило; код бота не меняется. Правила wb-rules уже умеют всё про устройства дома.
  • MQTT Conventions напрямую. Go-сервисы Wiren Board работают через закрытый бинарный Go-плагин; бот реализует открытые MQTT Conventions сам, поверх paho.mqtt.golang, и собирается обычным статическим бинарём.
  • Токен отдельно от конфига. wb-mqtt-confed записывает конфиги с широкими правами и отдаёт их в браузер, поэтому токен хранится в отдельном файле 0600 и никогда не возвращается в форму.
  • Устаревшие команды не выполняются, чтобы ворота не открылись через час после нажатия, а повтор retained-значения после рестарта не выполнил команду второй раз.
  • Минимум зависимостей: только go-telegram/bot и paho.mqtt.golang (в тестах — mochi-mqtt и go.uber.org/mock).

Подробнее — docs/adr/0001-architecture.md.

Лицензия

MIT.

About

Telegram-бот для контроллеров Wiren Board: команды из Telegram в MQTT, ответы и уведомления из wb-rules

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages