Простой Telegram-бот для получения информации через MQTT

    Этот бот был разработан для просмотра информации, находящейся на mqtt сервере внутри локальной сети. Он может работать на одном компьютере с mqtt сервером (в том числе на Raspberry PI или подобном) или отдельно. Задача удалённого управления не ставилась, только предоставление доступа к данным.

    Протокол MQTT предназначен специально для использования в различных устройствах автоматики, на нём очень легко организовать телеметрию и сбор данных. Этот протокол поддерживают как "умные" бытовые устройства, так и многие промышленные контроллеры. Также есть множество проектов на ESP8266, ESP32 или подобных платформах.

    На mqtt сервере публикуются данные телеметрии с различных датчиков - допустим, это метеостанция и термометры в теплицах. Для их просмотра на десктопе я раньше делал виджет, веб-страницу, потом захотелось иметь эти данные всегда под рукой. Конечно, можно было пробросить доступ к серверу наружу или разместить его в облаке, но тут возникает ещё целый ряд проблем. Тема использования бота в мессенджере для меня не новая - ещё пятнадцать лет назад я использовал ICQ клиента на мобильном телефоне, чтобы с помощью ICQRemote и скрипта на AutoIt переключать треки в Winamp на десктопе. Сейчас средний телефон гораздо мощнее того десктопа и имеет почти столько же постоянной памяти, но проблема внешнего подключения к устройству в локальной сети по-прежнему существует. И боты всё так же отлично справляются с решением этой проблемы. В общем, было решено делать Телеграм-бота, который просто предоставляет по запросу необходимую информацию.

    Я не буду рассказывать про регистрацию имени бота и получение токена, так как это всё уже есть в каждой предыдущей статье про телеграм-ботов. Перейду сразу к программе на Python. Она разрабатывалась под Windows, но я не вижу препятствий для её запуска под другими системами - используемые библиотеки python-telegram-bot и paho-mqtt это позволяют.

    Настройки программы хранятся в ini файле. В секции TELEGRAM прописывается токен для бота, в секции MQTT адрес и логин/пароль mqtt сервера, а также топик для получения данных (если несколько - через запятую, без пробелов). При запуске бот подключается к mqtt серверу и подписывается на необходимые топики. Глубина вложенности уровней может быть любой. Поступающие данные попадают в словарь alldata, ключом является полный топик:

    {
    'greenhouse/1/temp': '24.76',
    'greenhouse/1/upd': '22.04 18:20:30',
    'greenhouse/2/temp': '22.95',
    'greenhouse/3/temp': '28.91',
    'air/outdoor/1/temp': '17.32',
    'air/outdoor/1/upd': '22.04 18:21:25',
    'air/outdoor/1/pressure': '739',
    'air/outdoor/1/humidity': '58.3'
    }

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

    def maketree(group, items, path):
        def sep(s):
            return s.split('/', 1)
    
        head = [i for i in items if len(sep(i)) == 2]
        tail = [i for i in items if len(sep(i)) == 1]
        if len(tail) == 1:
            return group, tail[0]
        gv = groupby(sorted(head), lambda i: sep(i)[0])
        return group, dict([(i, path) for i in tail] + [maketree(g, [sep(i)[1] for i in v], '') for g, v in gv])
    

    В результате получается такая структура:

    {
        "air": {
            "outdoor": {
                "1": {
                    "humidity": "58.3",
                    "pressure": "739",
                    "temp": "17.32",
                    "upd": "22.04 18:21:25"
                }
            }
        },
        "greenhouse": {
            "1": {
                "temp": "24.76",
                "upd": "22.04 18:20:30"
            },
            "2": {
                "temp": "22.95"
            },
            "3": {
                "temp": "28.91"
            }
        }
    }
    

    Из такого словаря очень легко получить нужные данные. Например, температура в 1 теплице находится по адресу tree[greenhouse][1][temp]. Так как данные обновляются намного чаще, чем запрашиваются, преобразование в момент запроса достаточно эффективно. При более частых запросах лучше будет формировать и обновлять такое дерево сразу при поступлении данных.

    Затем идёт подключение к серверу Телеграм. Бот предназначен для работы в локальной сети без возможности пробросить необходимый для веб-хука порт, поэтому для подключения он использует Long Polling запросы. Используется библиотека python-telegram-bot версии 12.8, так как в 13 версии разработчики что-то поломали. Установить её можно командой pip3 install python-telegram-bot==12.8

    Для упрощения работы с ботом не используются команды: в ответ на знакомое слово он отсылает информацию, на незнакомое выдаёт кнопки выбора. В моём случае кнопок две, они прописаны в функции get_keyb:

    def get_keyb():
        return [[InlineKeyboardButton('Погода', callback_data='1'),
                InlineKeyboardButton('Теплицы', callback_data='2')]]
     

    А также в словаре, строчными буквами для удобства сравнения:

    keys = {'погода': '1', 'теплицы': '2', 'приборы': '3'}

    Ключ "приборы" добавлен для отладки, на него ответ всегда "40"

    Вариант диалога
    Вариант диалога

    Кнопки можно многократно использовать для обновления информации.

    В принципе, подобного бота можно использовать и для управления чем-либо, например через публикацию команд в те же mqtt топики - это ограничивается только Вашей фантазией. Но тогда нужно будет добавить авторизацию и список контактов. Полный код бота на GitHub

    Комментарии 6

      +7
      — Приборы!
      — 120!
      — Что 120?
      — А что приборы?
        0
        Если управляемое устройство чуть мощнее то на него тоже можно ставить Телеграм и управлять напрямую без MQTT.
          0
          Тоже хороший вариант! Так можно собрать полностью автономное устройство со своей сим-картой.
          У меня данные собираются с нескольких устройств, поэтому объединил их на MQTT сервере. Планирую ещё статью про модульную систему сбора информации по различным каналам (http, modbus, Siemens S7, mqtt)
            +1

            Я тоже делал бота, только как самостоятельный канал для мониторинга и управления программируемым реле https://youtu.be/7l8woF2JhH8, все это живёт в esp32 и может соседствовать и с mqtt, пока проверил с HomeKit.

          0
          И Вы тоже что-то спрашиваете у бота :(
          А как-то можно реализовать общение двух ботов устройств через телеграм минуя человека?
          Одна esp'шка пишет в телеграм, а на большой земле «что-то» читает и обрабатывает. Или мешает пресловутое «бот боту не пишет»? Можно как-то обмануть?
          Хочу заменить внешний vpn-сервер, который используется только для MQTT.
            0
            Можно попробовать создать группу и добавить туда ботов админами, но это в порядке бреда — сам не пробовал.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое