Несколько лет у меня лежал недорогой китайский видеодомофон Doorbell M8, работающий через приложение Ulooka. Покупал я его на одном из маркетплейсов примерно за 400 рублей — скорее из любопытства, чем из реальной необходимости.

Устройство оказалось вполне типичным представителем дешёвого китайского IoT:
включается только после нажатия кнопки;
подключается к Wi-Fi;
отправляет уведомление через китайские серверы;
показывает изображение в приложении Ulooka;
почти не предоставляет возможностей для локальной интеграции.
В целом свою задачу домофон выполнял, но всё работало как-то коряво. Уведомления приходили с задержкой, приложение выглядело сомнительно, а зависимость от неизвестных облачных серверов энтузиазма не добавляла.
В какой-то момент я просто перестал им пользоваться. Причём не только как видеодомофоном, но даже как обычным звонком.
Недавно устройство снова попалось мне на глаза, и я решил разобраться: можно ли получить от него изображение напрямую, минуя Ulooka?
Попытка найти RTSP и локальный API
Первой мыслью было поискать у Doorbell M8 стандартный RTSP-поток.
Для IP-камер это обычная история:
rtsp://192.168.0.100/stream
или что-нибудь похожее:
rtsp://user:password@192.168.0.100/live/ch00_0
Я перебрал несколько типичных путей, посмотрел открытые порты, изучил сетевую активность устройства. Но RTSP так и не нашёл.
Следующая идея — перехватить протокол между домофоном и приложением Ulooka. Возможно, устройство поднимает локальный TCP- или UDP-сервис, через который можно получить кадр или видеопоток.
Однако быстро выяснилось, что всё завязано на собственный закрытый протокол и китайскую облачную инфраструктуру. Домофон выходит в интернет, устанавливает соединения с внешними серверами, а приложение уже общается с устройством через них.
Теоретически можно было продолжить реверс-инжиниринг:
анализировать пакеты;
разбирать бинарный протокол;
исследовать APK приложения;
подменять DNS;
пытаться воспроизвести авторизацию.
Но для домофона стоимостью 400 рублей это выглядело неоправданно сложно.
Тогда я решил зайти с другой стороны.
Не обязательно получать изображение именно с домофона
Мне не нужен был полный контроль над устройством. На самом деле требовалось решить две простые задачи:
понять, что кто-то нажал кнопку;
получить изображение человека перед дверью.
Первую задачу мог решить сам домофон. Вторую — любая другая камера.
И тут я вспомнил, что почти над домофоном у меня уже висит обычная IP-камера с поддержкой RTSP.
Камера достаточно старая. Когда-то я устанавливал её для наблюдения за садом, но нормальную детекцию движения так и не настроил. Она просто постоянно пишет изображение, зато предоставляет обычный RTSP-поток.
Получилась простая схема:
Нажатие кнопки Doorbell M8 ↓ MikroTik замечает сетевую активность домофона ↓ MikroTik отправляет HTTP POST на локальный сервер ↓ Python получает свежий кадр с RTSP-камеры ↓ Сохраняет фото и несколько секунд видео ↓ Отправляет всё в Telegram
То есть сам Doorbell M8 используется только как беспроводная кнопка с Wi-Fi.
Изображение берётся уже с нормальной камеры.
Как определить нажатие кнопки через MikroTik
Самой интересной частью проекта оказалось не получение изображения, а определение самого факта нажатия кнопки.
Doorbell M8 большую часть времени спит. В таком состоянии он почти не создаёт сетевого трафика. После нажатия кнопки устройство просыпается, подключается к Wi-Fi и начинает обмениваться пакетами как с локальной сетью, так и с внешними серверами Ulooka.
Получается, что нажатие можно определить, даже ничего не зная о внутреннем протоколе домофона. Достаточно найти характерный пакет, который появляется сразу после пробуждения устройства.
Закрепляем IP-адрес домофона
Сначала нужно определить IP- и MAC-адрес устройства.
В WinBox это можно посмотреть здесь:
IP → DHCP Server → Leases
В моём случае домофон получил адрес:
192.168.0.15
Чтобы адрес не изменился после перезагрузки роутера или повторного подключения устройства, lease необходимо сделать статическим:
IP → DHCP Server → Leases → Make Static
После этого правила MikroTik можно привязать к конкретному IP.
При желании правило можно строить по MAC-адресу, но для обычного домашнего сценария статического DHCP lease вполне достаточно.
Смотрим, что происходит после нажатия
Далее я запустил встроенный Packet Sniffer MikroTik и несколько раз нажал кнопку домофона.
В WinBox это находится здесь:
Tools → Packet Sniffer
Для начала достаточно указать:
Interface: интерфейс или bridge, к которому подключён домофон IP Address: 192.168.0.15
Полезно сделать два захвата:
несколько минут, пока домофон просто лежит и его никто не трогает;
отдельный захват непосредственно после нажатия кнопки.
Так проще отделить регулярные фоновые пакеты от трафика, который действительно связан с вызовом.
В моём дампе после нажатия домофон начал отправлять широковещательные UDP-пакеты на адрес 255.255.255.255 по портам 10104 и 17900. Позже появился короткий UDP-пакет с локального адреса 192.168.0.15 на внешний адрес 47.105.85.47, порт 10102. Размер этого пакета составлял всего 82 байта, то есть видеоданных в нём явно не было — это был только служебный обмен.
Внутри полезной нагрузки встречалась последовательность:
31 54 45 47
В ASCII это выглядит как:
1TEG
Но по одному короткому пакету нельзя надёжно определить название или устройство закрытого протокола. Для моей задачи это было и не нужно: важен не смысл содержимого, а сам факт появления пакета после нажатия.
Какой пакет использовать в качестве триггера
Сначала я попробовал ловить исходящий пакет на внешний порт 10102:
/ip firewall filter add \ chain=forward \ src-address=192.168.0.15 \ protocol=udp \ dst-port=10102 \ action=add-src-to-address-list \ address-list=doorbell_pressed \ address-list-timeout=5s \ comment="Doorbell cloud trigger"
Такой вариант работал, но пакет появлялся не сразу. Домофон сначала выполнял локальный обмен, и только потом соединялся с внешним сервером.
Для уведомления о звонке лишняя задержка не нужна.
Поэтому более удобным триггером оказался первый широковещательный UDP-пакет на порт 10104. Он появляется почти сразу после пробуждения устройства.
Итоговое правило:
/ip firewall filter add \ chain=input \ src-address=192.168.0.15 \ protocol=udp \ dst-address=255.255.255.255 \ dst-port=10104 \ src-address-list=!doorbell_cooldown \ action=add-src-to-address-list \ address-list=doorbell_pressed \ address-list-timeout=5s \ comment="Doorbell M8 press trigger"
В ходе настройки использовался аналогичный вариант правила для chain=input и UDP-порта 10104.
Параметры здесь означают следующее:
chain=input
Пакет направлен на широковещательный адрес локальной сети и попадает в IP-стек самого MikroTik.
src-address=192.168.0.15
Учитываем только пакеты от домофона.
dst-address=255.255.255.255 dst-port=10104
Ловим конкретный широковещательный UDP-пакет, обнаруженный в дампе.
src-address-list=!doorbell_cooldown
Не создаём новое событие, пока действует защита от повторного срабатывания.
action=add-src-to-address-list
MikroTik не блокирует пакет, а просто добавляет IP устройства во временный список.
address-list=doorbell_pressed
Название списка, который будет означать: «обнаружено нажатие кнопки».
Проверяем правило
После добавления правила нужно открыть:
IP → Firewall → Filter Rules
и посмотреть на счётчики:
Packets Bytes
После нажатия кнопки они должны увеличиться.
Затем открываем:
IP → Firewall → Address Lists
Там должен появиться адрес:
192.168.0.15
в списке:
doorbell_pressed
Если счётчики не меняются, стоит проверить несколько вещей.
Во-первых, правило должно находиться выше общих запрещающих правил. Иначе пакет может быть отброшен раньше, чем попадёт в наше правило.
Во-вторых, для конкретной конфигурации MikroTik широковещательный пакет может обрабатываться не в input, а в forward. В таком случае достаточно изменить:
chain=input
на:
chain=forward
В-третьих, нужно проверить, что домофон действительно имеет адрес 192.168.0.15. После переподключения устройства адрес мог измениться, если DHCP lease не был сделан статическим.
Ещё одна распространённая ошибка при вводе команды:
/ip firewall filteradd ...
Правильно:
/ip firewall filter add ...
Между filter и add обязательно должен быть пробел.
Передаём событие на Python-сервер
Само правило firewall не умеет отправлять HTTP-запрос. Оно только создаёт запись в address list.
Для передачи события я добавил скрипт MikroTik:
/system script add name=doorbell-webhook source={ :local isPressed [/ip firewall address-list find where list="doorbell_pressed"] :if ([:len $isPressed] > 0) do={ /ip firewall address-list add \ list=doorbell_cooldown \ address=192.168.0.15 \ timeout=10s \ comment="Doorbell repeat protection" /ip firewall address-list remove $isPressed /tool fetch \ url="http://192.168.0.16:5000/doorbell" \ http-method=post \ keep-result=no } }
Скрипт выполняет три действия.
Сначала он добавляет домофон в список:
doorbell_cooldown
на 10 секунд. Пока устройство находится в этом списке, firewall-правило игнорирует следующие пакеты от него.
Это важно, потому что после пробуждения Doorbell M8 отправляет не один пакет, а целую серию. Без защиты одно физическое нажатие могло бы превратиться в несколько HTTP-запросов.
Затем скрипт удаляет запись из:
doorbell_pressed
и вызывает локальное Python-приложение:
POST http://192.168.0.16:5000/doorbell
В RouterOS для POST-запроса используется параметр:
http-method=post
а не просто:
method=POST
Запускаем проверку раз в секунду
Остаётся добавить скрипт в Scheduler:
/system scheduler add \ name="Check Doorbell" \ interval=1s \ start-time=startup \ on-event=doorbell-webhook
Теперь MikroTik раз в секунду проверяет наличие записи в списке doorbell_pressed.
Полная последовательность выглядит так:
1. Гость нажимает кнопку. 2. Doorbell M8 просыпается. 3. Домофон отправляет UDP broadcast на порт 10104. 4. Firewall MikroTik добавляет 192.168.0.15 в doorbell_pressed. 5. Scheduler запускает скрипт doorbell-webhook. 6. Скрипт включает десятисекундную защиту от повторов. 7. MikroTik отправляет POST на Python-сервер. 8. Python-приложение получает кадр и записывает видео с RTSP-камеры. 9. Фото и ролик отправляются в Telegram.
Почему не стал привязываться к внешнему IP
Можно было бы ловить только пакет на конкретный внешний адрес:
47.105.85.47:10102
Но у такого решения есть недостаток: адрес облачного сервера может измениться.
Сегодня устройство связывается с одним сервером, а после обновления прошивки, изменения DNS или переключения облачной инфраструктуры может начать использовать другой.
Поэтому в основном правиле я использую:
постоянный локальный IP домофона;
UDP;
широковещательный адрес;
характерный порт
10104.
Такой триггер не зависит от текущего адреса китайского сервера.
При этом порт тоже не стоит считать универсальным для всех устройств Ulooka. У другой ревизии Doorbell M8 или другой версии прошивки сетевое поведение может отличаться. Перед созданием правила лучше выполнить собственный захват и сравнить трафик до и после нажатия.
Проверяем отсутствие ложных срабатываний
После настройки я рекомендую провести простой тест:
не трогать домофон 10–15 минут;
убедиться, что список
doorbell_pressedсам по себе не появляется;нажать кнопку 10 раз с интервалом около минуты;
проверить, что на каждое нажатие приходится ровно одно событие;
несколько раз быстро нажать кнопку подряд;
убедиться, что защита
doorbell_cooldownне допускает спама.
Если домофон периодически просыпается самостоятельно и отправляет те же broadcast-пакеты, одного порта 10104 может оказаться недостаточно. Тогда правило можно сделать строже:
учитывать направление и интерфейс;
выбрать другой пакет из дампа;
использовать комбинацию адреса и порта;
ловить исходящий пакет на
10102, пожертвовав небольшой задержкой;увеличить cooldown;
добавить дополнительную проверку на Python-сервере.
В моём приложении также есть программная защита от повторных событий. Поэтому даже при нескольких последовательных запросах сервер не начинает одновременно записывать десяток одинаковых роликов. На этом задача обнаружения нажатия была решена. MikroTik уже умел превращать сетевую активность домофона в обычный HTTP-запрос. Оставалось обработать этот запрос на локальном сервере, получить свежий кадр с расположенной рядом RTSP-камеры и отправить результат в Telegram.
Получение кадра из RTSP
Для работы с камерой я использовал OpenCV:
import cv2 RTSP_URL = "rtsp://192.168.0.199/stream.sdp1" cap = cv2.VideoCapture(RTSP_URL) ret, frame = cap.read() if ret: cv2.imwrite("snapshot.jpg", frame) cap.release()
Но открывать RTSP-соединение только после звонка оказалось не лучшей идеей.
Камера может отвечать несколько секунд, первый кадр иногда приходит старым, а часть дешёвых камер вообще нестабильно переносит частые переподключения.
Поэтому в итоговой версии приложения работает отдельный фоновый поток, который постоянно читает RTSP и хранит последний свежий кадр в памяти.
Условно это выглядит так:
latest_frame = None while True: ret, frame = cap.read() if ret: latest_frame = frame
Когда приходит сигнал от MikroTik, приложению не нужно заново подключаться к камере. Оно сразу берёт уже готовый свежий кадр.
Это заметно уменьшает задержку.
Запись короткого видео
Одного скриншота иногда недостаточно. Человек может отвернуться, наклониться или оказаться между кадрами.
Поэтому я добавил запись короткого ролика продолжительностью от 5 до 10 секунд.
Упрощённо запись выглядит так:
writer = cv2.VideoWriter( "doorbell.mp4", cv2.VideoWriter_fourcc(*"mp4v"), 10, (width, height), ) for _ in range(70): frame = get_latest_frame() if frame is not None: writer.write(frame) time.sleep(0.1) writer.release()
В реальном приложении длительность и FPS задаются через настройки.
Также видео при наличии FFmpeg перекодируется в H.264, чтобы Telegram и браузеры нормально его воспроизводили.
Отправка в Telegram
Для Telegram используется обычный Bot API.
Фотография отправляется методом sendPhoto:
url = f"https://api.telegram.org/bot{TOKEN}/sendPhoto" with open("snapshot.jpg", "rb") as photo: requests.post( url, data={ "chat_id": CHAT_ID, "caption": "? Кто-то звонит в дверь!", }, files={ "photo": photo, }, timeout=30, )
Видео отправляется через sendVideo:
url = f"https://api.telegram.org/bot{TOKEN}/sendVideo" with open("doorbell.mp4", "rb") as video: requests.post( url, data={ "chat_id": CHAT_ID, "caption": "? Видео после звонка", "supports_streaming": "true", }, files={ "video": video, }, timeout=90, )
В итоговом варианте я добавил три режима:
только фотография;
только видео;
фотография и видео.
Последний режим оказался наиболее удобным.
Сразу после звонка приложение фиксирует стоп-кадр и начинает отправлять его в Telegram. Одновременно продолжается запись ролика. После завершения видео отправляется вторым сообщением.
Таким образом, фотография приходит быстро, а через несколько секунд появляется и полный фрагмент события.
Веб-интерфейс
Сначала скрипт представлял собой небольшой Flask-обработчик с одним endpoint:
@app.post("/doorbell") def doorbell(): capture_and_send() return "", 202
Но довольно быстро стало понятно, что управлять настройками через исходный код неудобно.
Поэтому я добавил простой локальный веб-интерфейс.

Через него можно:
указать RTSP URL;
ввести токен Telegram-бота;
указать Chat ID;
выбрать режим фото, видео или фото плюс видео;
задать длительность ролика;
проверить подключение к камере;
отправить тестовое сообщение в Telegram;
посмотреть живой предпросмотр;
вручную сделать снимок;
вручную записать видео;
посмотреть историю звонков;
открыть сохранённые фотографии и ролики.
Настройки сохраняются в обычный файл:
settings.json
Никаких переменных окружения или сложной конфигурации для домашнего использования не требуется.
Главная страница доступна по адресу:
http://192.168.0.16:5000/
Настройки:
http://192.168.0.16:5000/settings

История событий
Каждый звонок записывается в SQLite.
Для события сохраняются:
время;
источник;
выбранный режим;
статус обработки;
имя фотографии или видео;
результат отправки в Telegram;
текст ошибки, если что-то пошло не так.
Файлы хранятся в каталоге:
media
Логи приложения:
logs
Также есть автоматическое удаление старой истории через заданное количество дней.
Проблемы с китайским H.264
Во время работы в консоли иногда появляются сообщения вроде:
error while decoding MB non-existing SPS referenced decode_slice_header error missing picture in access unit
Это не ошибки Telegram или Flask.
Их выводит декодер H.264 при чтении RTSP-потока. Старая камера иногда присылает повреждённые или неполные кадры.
В моём случае поток продолжает работать: отдельные кадры могут повреждаться, но на следующем ключевом кадре изображение восстанавливается.
Для уменьшения количества ошибок можно:
использовать RTSP поверх TCP;
отключить в камере H.264+ или Smart Codec;
снизить битрейт;
уменьшить FPS;
использовать дополнительный поток меньшего разрешения;
уменьшить интервал между ключевыми кадрами.
В приложении RTSP принудительно переводится на TCP:
os.environ.setdefault( "OPENCV_FFMPEG_CAPTURE_OPTIONS", "rtsp_transport;tcp", )
Что получилось
В результате старый китайский домофон получил вторую жизнь.
Я не стал ломать закрытый протокол Ulooka, подменять китайские серверы или пытаться вытащить видео из самого устройства.
Вместо этого разделил задачу на две части:
Doorbell M8 определяет сам факт нажатия кнопки;
обычная RTSP-камера предоставляет изображение.
MikroTik связывает эти два события, а небольшой Python-сервис отправляет результат в Telegram.
Теперь после нажатия кнопки я получаю:
уведомление в Telegram;
фотографию человека у двери;
короткий видеоролик;
локальную запись в истории.
И всё это работает внутри моей сети, за исключением самой отправки сообщения в Telegram.
Итоговая архитектура
┌──────────────────┐ │ Doorbell M8 │ │ кнопка + Wi-Fi │ └────────┬─────────┘ │ сетевая активность ▼ ┌──────────────────┐ │ MikroTik │ │ firewall + script│ └────────┬─────────┘ │ HTTP POST /doorbell ▼ ┌──────────────────┐ │ Python + Flask │ │ очередь событий │ └───────┬──────────┘ │ ├──────────────► RTSP-камера │ фото + видео │ ├──────────────► SQLite │ история │ └──────────────► Telegram Bot API уведомления
Иногда для полезной автоматизации не обязательно полностью взламывать или переписывать существующее устройство, достаточно определить, какую часть задачи оно уже умеет выполнять, и дополнить её теми компонентами, которые есть под рукой.
В моём случае дешёвый домофон стал кнопкой, старая камера — источником изображения, MikroTik — детектором события, а Telegram — удобным интерфейсом уведомлений.
kolabaister
Спасибо за статью, интересный опыт, но почему было нельзя за сопоставимые деньги взять нормальную кнопку, прямой функцией которой является подача сигнала о своем нажатии?
vaalimusic Автор
Спасибо за комментарий! Вы правы. Но в этом и фишка: круто взять ненужный хлам и превратить его во что-то полезное, вместо того чтобы он просто пылился.
kolabaister
У меня была такая гипотеза) Но мне кажется система не слишком надежна, а в задаче не желательны пропуски срабатывания.
У меня была похожая задача - уведомлять сотрудников о посетителе, нажавшем кнопку вызова. Я ее решил зигби кнопкой, home assistant и уже висящей камерой видеонаблюдения. HA получает нажатие кнопки, скачивает кадр по rtsp камере и отправляет его с текстом в общий чат
vaalimusic Автор
Конечно) У меня ведь очень не надежная схема для серьезных решений, тут больше костыли, просто сам факт того, что можно отследить что-то, повесить ярлык и получить триггер. У Вас все-таки по серьезней задача, да и Вам не пришлось бы использовать домофон за 400 рублей.
Я даже больше думал статью совсем камнями закидают но вроде терпимо, мы же тут что-бы делится своим diy.
kolabaister
Итоговый бюджет решения был 300 р)
а на хеллоуин сделал скрипт, который случайным образом слал уведомления с кадрами с той же камеры, но с прифотошопленными призраками, монстрами и тому подобной публикой.
vaalimusic Автор
Да но у меня ведь был "ЛЮКС" видео, аудио, кнопка звонка. А на деле тыква)
Расскажите как-нибудь о таком опыте почитаю с удовольствием! Надеюсь будем делиться опытом!
kolabaister
Все предельно просто. Автоматизация:
Скрытый текст
И пара команд в конфиге:
получение кадра и обрезка до нужной зоны
отправка сообщения и кадра
vaalimusic Автор
Схема рабочая, но она завязана все таки на Home Assistant если я правильно понял
kolabaister
Конечно. Я его большой поклонник и активно внедряю везде где прикладываю руки)
vaalimusic Автор
Мне кажется очень громоздкий. В наше время, нейросети так круто скрипты пишут. Можно под себя адаптировать все очень быстро, у меня стоял Home Assistant несколько лет назад, но у меня 1 датчик-Алиса-1камера.
В итоге потерял сервер и как-то не вернулся.