November 21, 2025

Камера Уфанет в Home Assistant

UPDATE @AlexxIT добавил поддержку в go2rtс 🥳
https://t.me/homassistant/971504
как добавлять камеры описано тут, листаем до секции уфанет: https://github.com/AlexxIT/go2rtc/blob/master/internal/expr/README.md

устаревший вариант (более не нужен, т.к. появилась поддержка камер уфанет в go2rtc)

Благодарность Anton @simple1elf за метод!

Описание настроек go2rtc относятся к go2rtc установленному как аддон!

Если у вас другой тип установки go2rtc - расположение файлов будет другое, главное чтобы у go2rtc был доступ до файлов, ориентируйтесь на свой сервер.

Понадобится:
1. логин пароль от Уфанет с доступом к https://ucams.ufanet.ru/ (можно будет подтянуть камеры которые там отображаются)
2. go2rtc (например, аддоном)

Порядок

1. Ищем нужные нам данные по камере.
Заходим https://ucams.ufanet.ru/ в браузере, напримере Chromе
Видим наши камеры, нажимаем на просмотр, чтобы шел поток от камеры
Жмякаем F12 чтобы попасть в консоль разработчика
На вкладке сеть будет виден hls поток, копируем его URL

Получаем ссылку подобную этой:

https://manul-9.cams.ufanet.ru/16161614917OJQ678/0/16434534534517OJQ678-17632342344675-2615945.ts?token=eyJ0eXAiOiJKV1QiL(и далее длинный токен)

Нам понадобится номер сервера
manul-9.cams.ufanet.ru

И номер камеры

manul-9.cams.ufanet.ru/16161614917OJQ678/0/

В home assistant любым удобным способом попадаем в папку конфигурации и создаем папку ufanet_video
/config/ufanet_video Внутри папки создаем файл ufanet_video.py (полный путь будет /config/ufanet_video/ufanet_video.py)

import requests
import json
import os
import argparse
from datetime import datetime, timedelta

# Парсинг аргументов командной строки
parser = argparse.ArgumentParser(description="Получение RTSP-ссылки с токеном для камеры Уфанет")
parser.add_argument("--camera-id", required=True, help="Идентификатор камеры")
parser.add_argument("--username", required=True, help="Имя пользователя (логин)")
parser.add_argument("--password", required=True, help="Пароль")
parser.add_argument("--manul-id", default="21", help="Идентификатор manul-сервера (по умолчанию: 21)")
parser.add_argument("--token-dir", default="/config/ufanet_video", help="Директория для хранения token.json (по умолчанию: /config/ufanet_video)")

args = parser.parse_args()

camera_id = args.camera_id
credentials = {
    "username": args.username,
    "password": args.password
}
manul_id = args.manul_id
token_dir = args.token_dir

# Уникальное имя файла токена для каждой камеры + пользователя (защита от конфликтов)
safe_name = f"{args.username}_{camera_id}".replace("/", "_").replace("\\", "_")
token_file = os.path.join(token_dir, f"token_{safe_name}.json")

ttl_minutes = 30  # время жизни токена

def load_token_if_valid():
    if not os.path.exists(token_file):
        return None
    try:
        with open(token_file, "r", encoding="utf-8") as f:
            data = json.load(f)
        token = data.get("token")
        obtained_at_str = data.get("obtained_at")
        if not token or not obtained_at_str:
            return None
        obtained_at = datetime.fromisoformat(obtained_at_str)
        if datetime.now() - obtained_at < timedelta(minutes=ttl_minutes):
            return token
        else:
            return None
    except (json.JSONDecodeError, ValueError, KeyError):
        return None

def save_token(token):
    token_info = {
        "token": token,
        "obtained_at": datetime.now().isoformat()
    }
    os.makedirs(token_dir, exist_ok=True)
    with open(token_file, "w", encoding="utf-8") as f:
        json.dump(token_info, f, ensure_ascii=False, indent=4)

def get_new_token():
    session = requests.Session()
    response = session.post("https://ucams.ufanet.ru/api/internal/login/", data=credentials)
    if response.status_code != 200:
        raise Exception(f"Auth API error: {response.status_code} {response.text}")

    sessionid = session.cookies.get("sessionid")
    cookies = {"sessionid": sessionid}

    camera_payload = {
        "fields": ["token_l", "token_r"],
        "token_l_ttl": 3600,
        "token_r_ttl": 3600,
        "numbers": [camera_id]
    }

    cam_response = requests.post(
        "https://ucams.ufanet.ru/api/v0/cameras/this/?lang=ru",
        json=camera_payload,
        cookies=cookies
    )
    if cam_response.status_code != 200:
        raise Exception(f"Camera API error: {cam_response.status_code} {cam_response.text}")

    result = cam_response.json()["results"][0]
    return result["token_l"]

# Основной поток
token = load_token_if_valid()
if token is None:
    token = get_new_token()
    save_token(token)

rtsp_url = f"rtsp://manul-{manul_id}.cams.ufanet.ru/{camera_id}?token={token}#audio=pcmu#video=h265"
print(rtsp_url)


И сохраняем.

Сначала надо доустановить пакеты python, для этого добавляем в go2rtc stream

 streams:
  domofon3: | 
   echo: pip install requests -t /config/ufanet_video

Сохраняем и открываем полученный поток на просмотр. Скачаются пакеты.

Поток можно убрать, больше не понадобится.

Добавляем в go2rtc источники, столько сколько надо камер получить в ХА

streams:
  domofon1: |
   echo: python3 /config/ufanet_video/ufanet_video.py --camera-id "111111111" --username "123123123" --password "321321" --manul-id "666"
  domofon2: |
   echo: python3 /config/ufanet_video/ufanet_video.py --camera-id "222222222" --username "123123123" --password "321321" --manul-id "777"

Где:
--camera-id "111111111"
1111111 = номер камеры, полученный выше

--username "123123123" --password "321321"
логин пароль от сайта ucams.ufanet.ru

--manul-id "777"
777 = номер сервера, полученный выше

Сохраняем, рестартим go2rtc


И получаем потоки с камер уфанет

Например, rtsp://192.168.1.100:8554/domofon1
Не забудьте сменить ip на свой

Поток можно проверить в видеоплеере VLC

И всё это можно связать с открытием домофона из Home Assistant

https://teletype.in/@blacksli/domofon_ufanet_home_assistant