Raspberry Pi CCTV Discord bot
Sup
Сегодня мы будем строить систему видеонаблюдения, которая будет присылать в дискорд смешные видосы (иногда с нашим участием) при обнаружении движения в охраняемой области.
Я использовал Raspberry Pi 3B+ и веб-камеру Logitech C270. Дополнительно на камере закреплена небольшая широкоугольная линза.
Motion
Для обнаружения движения на изображении с камеры используется пакет motion
.
$ yay -Sy motion $ sudo systemctl enable motion.service
By the way I am using Archlinux. И обертку yay для pacman'а. В вашем любимом дистре команды установки пакетов могут быть другими. И я предполагаю, что первоначальная настройка RPi уже выполнена - вы установили и обновили ОС и подключились к сети.
Приведу полностью свой /etc/motion/motion.conf
:
# Motion будет запускаться как сервис systemd, # поэтому родная функциональность демона у него отключена daemon off setup_mode off log_level 6 # Директория, куда будут сохраняться видео target_dir /opt/CAM1 # Настройки камеры videodevice /dev/video0 v4l2_palette 8 width 1280 height 720 # Частота кадров снижена из соображений производительности framerate 2 text_left CAMERA1 text_right %Y-%m-%d\n%T-%q text_changes on emulate_motion off # Порог срабатывания в количестве изменившихся пикселей threshold 3500 noise_level 32 despeckle_filter EedDl minimum_motion_frames 1 # Минимальный интервал между срабатываниями обнаружения event_gap 30 pre_capture 2 post_capture 2 # Вывод картинок отключен picture_output off picture_output_motion off picture_filename %Y%m%d%H%M%S-%q # Настройки видео movie_output on movie_max_time 60 movie_quality 55 movie_codec mp4 movie_filename %t-%v-%Y%m%d%H%M%S movie_passthrough off # Веб-интерфейс отключен webcontrol_port 8080 webcontrol_localhost on webcontrol_parms 0 webcontrol_html_output off # Вывод видеопотока в реальном времени отключен stream_port 8081 stream_localhost on stream_motion off stream_maxrate 15 # Предотвращение срабатывания при резком включении света lightswitch_percent 65 lightswitch_frames 1 # Место, где обнаружено движение будет обведено красным прямоугольником locate_motion_mode on locate_motion_style redbox # По завершении создания видео запустить этот скрипт on_movie_end /opt/motion_scripts/mvfin.sh %f
Описание опций можно посмотреть в документации к motion.
Так как видеофайл создается сразу же когда обнаружится движение, то прежде чем отправлять его, нам нужно дождаться, пока он будет создан полностью.
Для этого motion вызывает простой скрипт, перемещающий готовый файл в другую директорию, которую периодически проверяет бот.
## /opt/motion_scripts/mvfin.sh #!/usr/bin/sh if [ -f $1 ]; then mv $1 /opt/FIN1/ fi
Чтобы всё работало, motion должен иметь права на запись в директории /opt/CAM1
и /opt/FIN1
. А так же иметь права выполнять скрипт.
$ cd /opt && sudo mkdir CAM1 FIN1 $ sudo chown -R motion:video CAM1 FIN1 motion_scripts $ sudo chmod -R o-rwx CAM1 FIN1 motion_scripts $ sudo chmod g+w FIN1 $ sudo usermod -a video YourUserName $ ls -l drwxr-x--- 2 motion video 3488 Oct 27 09:29 CAM1 drwxrwx--- 2 motion video 3488 Oct 27 09:29 FIN1 drwxr-x--- 2 motion video 3488 Oct 26 11:55 motion_scripts $ ls -l motion_scripts/mvfin.sh -rwxr-x--- 1 motion video 61 Oct 26 11:55 motion_scripts/mvfin.sh
Бот, который будет запущен от обычного пользователя должен иметь право удалять файлы из директории /opt/FIN1
, поэтому её права установлены в 770, а мой пользователь добавлен в группу video
.
Теперь motion можно запускать и проверить, что в /opt/FIN1
появляются файлы.$ sudo systemctl start motion.service
Python
Для работы бота потребуется установить пакет discord.py$ yay -S python-discord
Возможно, вы предпочтёте создать venv и ставить пакеты pip'ом.
Создаем директорию ~/cctv
, и в ней два файла:
## config.py # Токен бота TOKEN = 'Qwertyuiop' # Идентификатор канала для уведомлений CHANNEL = 123123123123 # Директория из которой будут отправляться файлы CAMDIR = '/opt/FIN1' # Интервал проверки новых файлов INTERVAL = 120
## bot.py import os import time import discord import asyncio import config class CCTV(discord.Client): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.bg_task = self.loop.create_task(self.check_vid_task()) async def on_ready(self): print('Bot login: {0.user}'.format(client)) async def on_message(self, message): if message.author == client.user: return if message.content.startswith('&ping'): await message.channel.send(f'pong {round (client.latency * 1000)}ms ') async def check_vid_task(self): await self.wait_until_ready() chan = self.get_channel(config.CHANNEL) while not self.is_closed(): for newfile in os.listdir(config.CAMDIR): await chan.send(content=time.ctime(), file=discord.File(config.CAMDIR + '/' + newfile)) os.remove(config.CAMDIR + '/' + newfile) await asyncio.sleep(config.INTERVAL) client = CCTV() client.run(config.TOKEN)
Следуя инструкции, создаем бота, и пишем его токен в конфиг.
Создаем в дискорде сервер, в котором не будет никаких лишних участников. И добавляем текстовый канал для уведомлений от бота. ID этого канала тоже пишем в конфиг.
ID копируется через контекстное меню канала
Добавляем бота на созданный сервер, по той же инструкции.
Стартуем бота$ python ~/cctv/bot.py
Если всё сделано правильно, бот напишет свой логин в терминал и начнет отправлять в канал сообщения с видео, которые успели накопиться с запуска motion'a.
Автозапуск
Чтобы бот стартовал сам после загрузки RPi создадим пользовательский юнит systemd.
## ~/.config/systemd/user/cctvbot.service [Unit] Description=CCTV Discord Bot [Service] ExecStart=python /home/YourUserName/cctv/bot.py [Install] WantedBy=default.target
Включаем его:
$ systemctl --user --now enable cctvbot.service $ sudo loginctl enable-linger YourUserName
Еще для автозапуска можно использовать supervisor
Примеры работы
Заключение
Я, всё-таки, оказался не готов получать уведомления от бота каждый раз, когда выхожу в коридор. И думаю пока каким образом лучше автоматизировать активацию оповещений.
Есть три варианта:
- Физический переключатель, подключенный к GPIO. А в коде скрипта считывать его состояние с помощью python-periphery.
- Подписать бот на специальный topic в моей MQTT сети и управлять видеонаблюдением со смартфона.
- Включать бота просто по расписанию. Например даже cron'ом.
UPD-0
Отправка оповещений теперь включается если мой смартфон не подключен к местной беспроводной сети.
А проверяется это банальным пингом:
... if subprocess.call(['ping', '-c', '1', '-w', '1', '192.168.0.10']) == 1: await chan.send(...) ...
Конечно, нужно еще импортировать модуль subprocess
в коде бота и подкрутить настройки DHCP на роутере, чтобы смартфон каждый раз получал один и тот же IP адрес.