Raspberry Pi стоит где-то в шкафу и следит за температурой, дверью, датчиком воды. Как узнать, что что-то случилось, если вы не дома? Самый простой и бесплатный способ: чтобы плата сама написала вам в Telegram. А если научить её отвечать на команды, то из чата можно будет узнавать температуру, просить фотографию с камеры или включать устройство.
В статье сделаем Telegram-бота на Raspberry Pi 5 на Python. Сначала простое: функция, которая отправляет вам сообщение. Затем полноценный бот с командами /status и /photo, защитой от посторонних и запуском при включении платы. Эти приёмы подойдут для любого проекта: метеостанции, датчика движения или реле.
Доступность Telegram. Бот ходит на серверы api.telegram.org. Если из вашей сети этот адрес недоступен, бот работать не будет. Проверьте на плате: curl -s -o /dev/null -w "%{http_code}\n" https://api.telegram.org. Ответ 302 или другой HTTP-код означает, что связь есть, а таймаут — что нет. Если выход к Telegram у вас организован через прокси, библиотека python-telegram-bot умеет их использовать (параметр proxy у построителя приложения).
Шаг 1. Создаём бота
- В Telegram найдите бота @BotFather (официальный, с галочкой).
- Отправьте команду
/newbot. - Введите имя бота (любое, например «Мой Raspberry Pi») и уникальное имя пользователя, оканчивающееся на
bot(например,my_rpi5_home_bot). - BotFather выдаст токен (строка вида
123456789:ABC...). Это пароль к боту: любой, у кого он есть, сможет управлять ботом. Никому не показывайте его и не публикуйте в интернете.
В поиске по имени @BotFather ниже официального бота появляются подделки с похожими названиями. Настоящий бот первый в списке и с синей галочкой у названия:
Диалог создания бота: команда /newbot, имя, username и в последнем сообщении токен (на снимке он закрашен):
Если токен утёк, в BotFather команда /revoke выпускает новый.
Шаг 2. Узнаём свой chat_id
Боту нужно знать, кому писать. Для этого нужен идентификатор вашего чата.
- Найдите своего бота по имени пользователя и отправьте ему любое сообщение (например, «привет»).
- Откройте в браузере (подставьте свой токен):
https://api.telegram.org/botВАШ_ТОКЕН/getUpdates
- В ответе найдите блок
"chat":{"id":123456789,...}. Число послеid— вашchat_id.
(Если ответ пустой [], отправьте боту сообщение ещё раз и обновите страницу.)
В Telegram найдите бота по username и откройте чат. Кнопка Start отправляет команду /start, после неё напишите любое сообщение (здесь «Hi!»):
Пока боту никто не писал, getUpdates возвращает пустой список ("result": []):
После сообщения в result появляется блок message. Число id в блоке chat и есть ваш chat_id (для личной переписки оно совпадает с id в блоке from). На снимке вместо настоящих имени, username и номера стоят условные значения:
Если токен вставлен неверно, вместо этого придёт ошибка 401 Unauthorized (см. таблицу в конце статьи).
Шаг 3. Установка библиотек
Библиотеки Python ставятся в виртуальное окружение (об окружениях):
mkdir -p ~/tgbot
cd ~/tgbot
python3 -m venv env
source env/bin/activate
pip install requests python-telegram-bot
requests — для простой отправки сообщений, python-telegram-bot — для полноценного бота с командами (нужен Python 3.10 или новее: в Raspberry Pi OS на базе Debian 12 и 13 он есть).
Токен и chat_id храните в отдельном файле, а не в тексте программы:
nano ~/tgbot/tg.env
TELEGRAM_TOKEN=123456789:ABC...
TELEGRAM_CHAT_ID=123456789
chmod 600 ~/tgbot/tg.env
Вариант 1. Просто отправить сообщение
Самый простой способ: обычный HTTPS-запрос без «ботов» и библиотек для работы с Telegram. Создайте notify.py:
import os
import requests
TOKEN = os.environ["TELEGRAM_TOKEN"]
CHAT_ID = os.environ["TELEGRAM_CHAT_ID"]
def send(text):
"""Отправляет текстовое сообщение владельцу."""
r = requests.post(
f"https://api.telegram.org/bot{TOKEN}/sendMessage",
data={"chat_id": CHAT_ID, "text": text},
timeout=10,
)
r.raise_for_status()
if __name__ == "__main__":
send("Raspberry Pi 5 на связи!")
Запуск (значения берутся из файла окружения):
set -a; source tg.env; set +a
python3 notify.py
В Telegram придёт сообщение. Команда set -a экспортирует переменные из файла в окружение.
Функцию send() можно подключить к любому скрипту: импортируйте её (from notify import send) и вызывайте, когда нужно.
Пример: уведомление о перегреве
Скрипт alert.py сообщает о высокой температуре процессора, и о том, что она вернулась в норму. Чтобы не получать десятки сообщений, используется гистерезис: сообщение приходит один раз при превышении порога и один раз при возвращении:
import time
from notify import send
LIMIT = 70.0 # порог тревоги, °C
RESET = 60.0 # температура, когда считаем, что всё в порядке
def cpu_temp():
with open("/sys/class/thermal/thermal_zone0/temp") as f:
return int(f.read()) / 1000
alarm = False
while True:
t = cpu_temp()
try:
if not alarm and t >= LIMIT:
alarm = True
send(f"⚠️ Перегрев: процессор {t:.1f} °C")
elif alarm and t <= RESET:
alarm = False
send(f"✅ Температура в норме: {t:.1f} °C")
except Exception as error: # нет сети или Telegram недоступен: попробуем позже
print("Не удалось отправить:", error, flush=True)
time.sleep(30)
Чтобы проверить оповещение, не дожидаясь настоящего перегрева, временно поставьте LIMIT чуть выше текущей температуры (она видна в vcgencmd measure_temp) и нагрузите процессор: stress-ng --cpu 4 --timeout 120s (пакет stress-ng). Так выглядит чат: сначала сообщение от notify.py, затем уведомление от alert.py:
Тот же шаблон работает для любого датчика: замените cpu_temp() на чтение DHT22 или BME280 (метеостанция), а порог на нужный. Для событий (сработал датчик движения) отправляйте сообщение при срабатывании, но не чаще раза в минуту.
Вариант 2. Бот с командами
Теперь научим плату отвечать на команды. Создайте bot.py:
import asyncio
import os
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
TOKEN = os.environ["TELEGRAM_TOKEN"]
OWNER = int(os.environ["TELEGRAM_CHAT_ID"])
def allowed(update: Update) -> bool:
"""Отвечаем только владельцу."""
return update.effective_chat is not None and update.effective_chat.id == OWNER
def cpu_temp() -> float:
with open("/sys/class/thermal/thermal_zone0/temp") as f:
return int(f.read()) / 1000
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not allowed(update):
return
await update.message.reply_text("Привет! Команды: /status, /photo")
async def status(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not allowed(update):
return
load1, _, _ = os.getloadavg()
await update.message.reply_text(
f"Температура процессора: {cpu_temp():.1f} °C\nНагрузка (1 мин): {load1:.2f}"
)
async def photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not allowed(update):
return
await update.message.reply_text("Снимаю…")
proc = await asyncio.create_subprocess_exec(
"rpicam-still", "-n", "-t", "1000", "--width", "1280", "--height", "720", "-o", "/tmp/photo.jpg"
)
await proc.wait()
with open("/tmp/photo.jpg", "rb") as f:
await update.message.reply_photo(f)
def main():
app = Application.builder().token(TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("status", status))
app.add_handler(CommandHandler("photo", photo))
app.run_polling()
if __name__ == "__main__":
main()
Запустите: python3 bot.py (с переменными из tg.env, как выше). Напишите боту /status: он ответит температурой и нагрузкой. Если подключена камера (подключение камеры), команда /photo пришлёт снимок.
Как это работает:
Application— основа бота.run_polling()сам опрашивает серверы Telegram: входящие соединения и открытые порты на роутере не нужны, работает и за NAT.- Функции
start,status,photo— обработчики команд. Они асинхронные (async def,await): пока одна ждёт ответ сети, остальные не блокируются. - Функция
allowed()проверяет номер чата: бот отвечает только вам. Это важно: имя бота публичное, и без проверки им сможет пользоваться любой. rpicam-stillзапускается отдельным процессом, чтобы не «замораживать» бота.
На снимке: ответ на /start, на /status и начало ответа на /photo. Сначала бот пишет «Снимаю…», а снимок приходит после него. Если камера не подключена или не включена, после «Снимаю…» ничего не придёт, а в терминале появится ошибка (см. таблицу в конце статьи).
Меню команд
Чтобы список команд появлялся в подсказке чата, откройте BotFather и выполните /setcommands, затем выберите бота и отправьте:
status - Состояние платы
photo - Фото с камеры
Управление устройством
Добавьте команду, которая включает и выключает, например, светодиод или реле (реле на Raspberry Pi): с gpiozero это пара строк.
Сначала установите библиотеки в то же виртуальное окружение. На Raspberry Pi 5 gpiozero работает через драйвер lgpio, а системный пакет python3-lgpio обычное окружение не видит, поэтому ставим его отдельно. Для pip нужны инструменты сборки: у lgpio нет готового пакета под эту систему, и он собирается на плате.
sudo apt install -y swig python3-dev liblgpio-dev
source ~/tgbot/env/bin/activate
pip install gpiozero lgpio
Если пропустить apt, сборка остановится с ошибкой error: command 'swig' failed: No such file or directory и сообщением Failed to build lgpio. Если не хватает заголовка (lgpio.h), помогает пакет liblgpio-dev.
Без lgpio программа упадёт с ошибкой BadPinFactory: Unable to load any default pin factory! и предупреждениями PinFactoryFallback: Falling back from lgpio: No module named 'lgpio'. Другой способ: создать окружение с доступом к системным пакетам (python3 -m venv --system-site-packages env), тогда lgpio берётся из системы и собирать ничего не нужно.
Дальше правим bot.py в три приёма. Первый: в начало файла, к остальным import, добавьте импорт и создайте светодиод (GPIO17 — вывод 11 на колодке):
from gpiozero import LED
lamp = LED(17)
Второй: добавьте две функции-обработчика, например после функции photo, перед main():
async def lamp_on(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not allowed(update):
return
lamp.on()
await update.message.reply_text("Включено")
async def lamp_off(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not allowed(update):
return
lamp.off()
await update.message.reply_text("Выключено")
async def lamp_state(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not allowed(update):
return
await update.message.reply_text("Светодиод: " + ("включён" if lamp.value else "выключен"))
Функция lamp_state отвечает по состоянию, которое «помнит» программа: lamp.value равно 1, когда устройство включено. Для реле с active_high=False это тоже «включено», хотя на выводе при этом низкий уровень.
Третий: подключите их в функции main(), добавив строки add_handler к уже имеющимся. Функция целиком выглядит так:
def main():
app = Application.builder().token(TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("status", status))
app.add_handler(CommandHandler("photo", photo))
app.add_handler(CommandHandler("on", lamp_on))
app.add_handler(CommandHandler("off", lamp_off))
app.add_handler(CommandHandler("state", lamp_state))
app.run_polling()
Перезапустите bot.py и напишите боту /on и /off: светодиод включится и выключится. Команда /state скажет, включён ли он.
Чтобы команды появились в меню чата, добавьте в /setcommands строки on - Включить, off - Выключить и state - Состояние светодиода. Реле с низким уровнем включения (как на модуле из статьи про реле) управляется так же, но вместо LED(17) нужно написать OutputDevice(17, active_high=False) (импорт: from gpiozero import OutputDevice): тогда on() подаёт на вход реле низкий уровень.
Подробности про GPIO: GPIO в Python. Для управления опасными нагрузками соблюдайте правила безопасности.
Запуск при включении платы
Оформите бота как службу systemd (статья про автозапуск). Файл /etc/systemd/system/tgbot.service:
[Unit]
Description=Telegram bot
After=network-online.target
Wants=network-online.target
[Service]
User=pi
WorkingDirectory=/home/pi/tgbot
EnvironmentFile=/home/pi/tgbot/tg.env
ExecStart=/home/pi/tgbot/env/bin/python3 /home/pi/tgbot/bot.py
Restart=on-failure
RestartSec=10
Environment=PYTHONUNBUFFERED=1
[Install]
WantedBy=multi-user.target
Замените pi на своё имя пользователя. Включите:
sudo systemctl daemon-reload
sudo systemctl enable --now tgbot
journalctl -u tgbot -f
Если бот использует камеру или GPIO, пользователь службы должен входить в группы video и gpio (у обычного пользователя Raspberry Pi OS они уже есть).
Советы
- Не спамьте. Telegram ограничивает частоту сообщений: в один чат не чаще примерно раза в секунду, а при массовых отправках возможны ошибки
429. Для частых событий накапливайте и отправляйте сводку. - Сообщение об ошибке отправки. Если сети нет,
send()выдаст ошибку: ловите исключение (как в примереalert.py), иначе программа упадёт. - Группы и каналы. Бота можно добавить в семейную группу: тогда
chat_idбудет отрицательным числом. Не забудьте разрешить боту читать сообщения группы. - Другая библиотека. Помимо
python-telegram-bot, популярнаaiogram: подход похож. - Безопасность. Токен храните в файле с правами
600, в программе проверяйтеchat_id, не давайте боту команд, которые выполняют произвольный код на плате.
Если что-то не работает
| Проблема | Что проверить |
|---|---|
KeyError: 'TELEGRAM_TOKEN' | Не загружены переменные окружения: set -a; source tg.env; set +a или EnvironmentFile в службе |
401 Unauthorized | Неверный токен. Проверьте, нет ли лишних символов и пробелов |
400 Bad Request: chat not found | Неверный chat_id или вы не писали боту первым |
getUpdates возвращает пустой список [] | Отправьте боту сообщение ещё раз и обновите страницу |
| Таймаут при подключении | Нет доступа к api.telegram.org. Проверьте сеть и настройки прокси |
| Бот не отвечает на команды | Проверьте, что bot.py запущен, нет ли ошибок (journalctl -u tgbot) и что вы пишете с того аккаунта, чей chat_id указан |
BadPinFactory: Unable to load any default pin factory!, No module named 'lgpio' | В виртуальном окружении нет драйвера GPIO: sudo apt install -y swig python3-dev liblgpio-dev, затем pip install lgpio (см. раздел «Управление устройством») |
Failed to build lgpio, command 'swig' failed | Не установлены инструменты сборки: sudo apt install -y swig python3-dev liblgpio-dev |
/photo не присылает снимок | Камера не обнаружена (rpicam-hello --list-cameras). Проверьте путь к файлу и права |
| Ошибка при запуске нескольких копий | Один токен можно использовать только в одной копии бота (Conflict: terminated by other getUpdates request) |
Что дальше
- Датчик движения PIR на Raspberry Pi 5: сообщение о движении.
- Метеостанция на Raspberry Pi: тревога по температуре и давлению.
- Автозапуск программ.
- Камера Raspberry Pi.
Материалы
- Telegram Bot API — справочник по методам, включая
sendMessageиgetUpdates. - BotFather и создание ботов — описание команд BotFather.
- python-telegram-bot — документация библиотеки.
- requests — библиотека HTTP-запросов.













