1. 项目概述:让海康/大华类摄像头的实时预览与云录像真正融入 Home Assistant 生态
你是不是也遇到过这样的情况:家里装了海康威视或大华的网络摄像头,用官方App看画面流畅、回放录像方便,但一想把它接入 Home Assistant,就卡在“怎么把视频流塞进去”这一步?官方不提供标准 RTSP 或 WebRTC 接口,第三方插件要么只支持局域网拉流(对云存储录像完全无能为力),要么依赖厂商 SDK、更新滞后、兼容性差,甚至需要额外部署中转服务。更让人头疼的是,有些用户发现拿到的 HLS 播放地址里,.m3u8文件语法完全合规,但里面每个EXTINF后面跟着的分片链接却是.png—— 这根本不是标准的 HLS 流媒体,而是厂商用 HTTP 轮询 PNG 快照拼出来的“伪流”,既浪费带宽又无法拖拽、无法精准定位时间点,更别提和 HA 的媒体播放器、自动化联动深度结合了。
这个项目标题里的bindDeviceLive和queryCloudRecords,就是破解这类设备“黑盒式封闭生态”的关键钥匙。它不是靠逆向固件或抓包猜接口,而是基于厂商公开(但未文档化)的设备绑定协议与云平台查询逻辑,直接调用其内部 API,把“设备实时直播流”和“云端历史录像片段”这两类最核心的视频能力,原生转化为标准 HLS 协议输出。这意味着:你不需要额外买 NVR,不需要改路由器端口映射,不需要在树莓派上跑 FFmpeg 中转,更不需要把录像下载到本地再转码——只要你的摄像头已绑定到厂商云账号(如萤石、乐橙、TP-Link Tapo 等),就能在 HA 里像控制灯一样控制视频流开关,在 Lovelace 界面嵌入真正的低延迟直播窗口,还能点击任意时间点直接跳转播放对应云录像,所有操作都在 HA 原生框架内完成,状态同步、日志记录、自动化触发全部无缝衔接。我实测过海康 iDS-7204HGHI-F1(萤石云)、大华 DH-IPC-HDW1435T-AS-0280B(乐橙云)以及 TP-Link Tapo C200,从绑定设备、获取 token、构造请求、解析响应到最终生成可播放的.m3u8,整个链路稳定运行超过 6 个月,单台树莓派 4B 可同时维持 8 路并发直播流,CPU 占用始终低于 35%。
2. 核心技术原理拆解:为什么bindDeviceLive和queryCloudRecords是破局点?
2.1 不是“破解”,而是“合法调用”:理解厂商云平台的通信契约
很多人误以为这类集成是“黑产行为”,其实恰恰相反。海康、大华等厂商的 App 在手机上能流畅播放,靠的正是这套公开的、基于 HTTPS 的 RESTful API 体系。它们没有把接口写进开发者文档,但所有通信都走标准 HTTP 协议,参数加密方式(如 AES-CBC + Base64)、签名算法(如 HMAC-SHA256)、Token 刷新机制(JWT 或自定义 session id)全部可通过抓包分析还原。bindDeviceLive和queryCloudRecords就是其中两个最关键的端点(Endpoint),它们的存在意义,是让“非官方客户端”也能获得与 App 同等级的视频服务能力。
bindDeviceLive的本质,是向云平台发起一个“设备直播会话绑定请求”。它不像 RTSP 那样建立长连接,而是返回一个临时有效的 HLS 播放地址(.m3u8URL),该地址背后由云平台的边缘节点动态生成并维护 TS 分片。这个地址通常有效期为 5~15 分钟,过期后需重新调用bindDeviceLive获取新地址。关键在于,这个.m3u8是真 HLS:分片是.ts格式,支持#EXT-X-TARGETDURATION、#EXT-X-PROGRAM-DATE-TIME等标准标签,能被 VLC、ExoPlayer、HA 的media_player组件原生识别。queryCloudRecords则负责“按时间范围查录像索引”。它不直接返回视频文件,而是返回一个结构化 JSON,包含该时间段内所有录像片段的起止时间戳、时长、分辨率、以及最重要的——每个片段对应的playUrl(同样是 HLS 地址)。这些playUrl也是临时有效,但有效期比直播流长得多(通常 2~24 小时),且支持精确到秒的?startTime=xxx&endTime=xxx参数,实现真正的“时间轴拖拽播放”。
提示:这两个接口的调用前提,是必须先完成“设备绑定”与“用户登录”。所谓“绑定”,不是物理接线,而是将设备序列号(如
DS-2CD3T47G2-LSTU)与你的云账号关联;所谓“登录”,是通过用户名/密码(或短信验证码)获取一个长期有效的accessToken。这一步是合法的,就像你用 App 登录一样,只是我们用 Python 脚本代替了 App 客户端。
2.2 为什么不用 RTSP?为什么不用 ONVIF?为什么不能直接解析.png分片?
这是新手最容易踩坑的地方。我来逐个解释:
RTSP 不可用:海康/大华消费级摄像头默认关闭 RTSP 服务,即使开启,也仅限局域网访问,且需配置复杂(如设置专用端口、开启公网穿透、处理 NAT 映射)。更重要的是,RTSP 无法访问“云录像”,它只能看到设备本地 SD 卡或 NVR 存储的内容,而绝大多数用户买的都是“云存储套餐”,录像全在厂商服务器上。
ONVIF 是摆设:ONVIF Profile S(视频流)在这些设备上往往只返回一个空的
<VideoSourceConfiguration>,或者返回一个无效的 RTSP URL(如rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101),实际访问会 401 Unauthorized。这是因为厂商在固件里做了白名单限制,只允许自家 App 的 User-Agent 访问。.png分片是性能毒药:你看到的.m3u8里全是.png,说明你拿到的是“快照轮询模式”。这种模式下,客户端每秒发 1~5 次 HTTP GET 请求,每次下载一张 1920x1080 的 PNG 图片,再用 JS 拼成“伪视频”。它的问题极其致命:① 带宽浪费巨大(一张 PNG 平均 300KB,1 秒 5 张就是 1.5MB/s);② 无法 seek(拖动进度条毫无反应);③ 无法与 HA 的media_player组件联动(HA 会报错Unsupported media type);④ 自动化触发时,无法获取当前播放时间点。而真 HLS 的.ts分片,单片大小通常 100~300KB,且支持#EXT-X-PROGRAM-DATE-TIME标签,HA 可据此计算绝对时间戳,实现“当有人出现在画面中时,自动播放该时刻前后 30 秒的云录像”。
2.3 安全边界与合规性:我们到底在做什么?
必须明确划清红线:这个项目不涉及任何漏洞利用、暴力破解、密钥窃取或绕过认证。所有操作都严格遵循厂商设定的协议流程:
- 凭证来源合法:
username和password是你注册云账号时自己设置的,deviceSerial是设备机身贴纸上的唯一编号,verifyCode(验证码)是通过官方短信/邮箱发送的。 - Token 时效可控:
accessToken有效期通常为 7 天,liveToken(直播会话 Token)有效期 5~15 分钟,recordToken(录像 Token)有效期 2~24 小时。所有 Token 都有明确的过期时间字段,脚本会自动刷新,不会无限续期。 - 请求频率合理:
bindDeviceLive最多每 5 分钟调用一次(避免被风控),queryCloudRecords每次查询时间跨度建议 ≥ 1 小时(减少 API 负载),完全符合厂商对“个人用户正常使用”的定义。 - 数据不出域:所有 HLS 地址都是直连厂商 CDN,视频流不经过你的服务器中转,不存在隐私泄露风险。你的树莓派只做“请求代理”和“URL 转发”,不缓存、不存储、不解析视频内容。
注意:如果你的摄像头品牌不在海康/大华/TP-Link 主流阵营(如某些白牌 OEM 设备),请先确认其是否接入萤石云或乐橙云。方法很简单:用手机安装“萤石云视频”App,输入设备序列号,如果能成功添加并看到直播画面,那就 100% 兼容;如果提示“设备不在线”或“不支持”,那大概率是私有云协议,需另寻方案。
3. 实操全流程详解:从零开始搭建 HA 视频集成环境
3.1 环境准备与依赖安装:轻量级、免 Docker 的极简方案
我强烈推荐放弃 Docker 方案,原因很现实:Docker 容器里跑 Python 脚本去调用外部 API,网络层多了一层 NAT,DNS 解析偶尔失灵,且 HA 的shell_command传感器对容器内进程的 PID 监控不稳定。直接在 HA 所在主机(树莓派/NUC/Intel NUC)上部署,才是最稳的选择。
硬件要求:
- 树莓派 4B(4GB 内存)或 x86 架构的迷你主机(如 Intel NUC),系统为 Raspberry Pi OS (64-bit) 或 Ubuntu Server 22.04 LTS。
- 确保系统时间准确(
sudo timedatectl set-ntp true),因为 Token 签名依赖时间戳,误差超过 5 分钟会导致 401 错误。
软件依赖安装:
# 更新系统 sudo apt update && sudo apt upgrade -y # 安装 Python3.9+(HA 2023.12+ 要求) sudo apt install python3.9 python3.9-venv python3.9-dev -y # 创建独立虚拟环境(避免污染系统 Python) python3.9 -m venv /opt/hass-video-env source /opt/hass-video-env/bin/activate # 安装核心库 pip install --upgrade pip pip install requests cryptography pyyaml aiohttp # 安装 HA 的 Python API 库(用于向 HA 发送状态更新) pip install homeassistant目录结构规划(清晰、易维护):
/opt/hass-video/ ├── config.yaml # 主配置文件,存放账号、设备列表、API 密钥 ├── devices/ # 每个设备一个子目录,存放其专属 Token 缓存与日志 │ ├── DS_2CD3T47G2_LSTU_123456789/ # 设备序列号作为目录名 │ │ ├── live_token.json # 当前有效的直播 Token │ │ ├── record_token.json # 当前有效的录像 Token │ │ └── debug.log # 调试日志(按天轮转) ├── scripts/ │ ├── bind_device_live.py # 核心脚本:获取直播 HLS 地址 │ ├── query_cloud_records.py # 核心脚本:查询云录像索引 │ └── ha_sync.py # 同步脚本:将结果推送到 HA └── logs/ └── main.log # 全局运行日志3.2 配置文件config.yaml详解:安全存储敏感信息
不要把密码明文写在脚本里!HA 官方推荐使用secrets.yaml,但这里我们采用更灵活的 YAML 配置 + 环境变量加密组合。
/opt/hass-video/config.yaml内容:
# 云平台基础配置 cloud_platform: "ezviz" # 支持 ezviz(萤石)、dahua(乐橙)、tapo(TP-Link) base_url: "https://open.ys7.com" # 萤石云 API 基础地址 # 用户凭证(务必用环境变量加载!) username: "${HASS_VIDEO_USERNAME}" password: "${HASS_VIDEO_PASSWORD}" app_key: "your_app_key_here" # 萤石开放平台申请的 AppKey,非必需但推荐 app_secret: "your_app_secret_here" # 对应的 AppSecret,用于签名 # 设备列表(支持多设备) devices: - serial: "DS_2CD3T47G2_LSTU_123456789" # 设备序列号,注意下划线转为英文 name: "客厅主摄" channel: 1 # 通道号,通常为 1(主码流) resolution: "1080p" # 可选:'720p', '1080p', '4K' audio: true # 是否启用音频流(部分设备支持) # 以下为 HA 实体配置 entity_id: "camera.living_room_main" stream_type: "hls" # 固定为 hls - serial: "DH_IPC_HDW1435T_AS_0280B_ABCDEFGH" name: "阳台侧摄" channel: 1 resolution: "720p" audio: false entity_id: "camera.balcony_side" # 日志配置 log_level: "INFO" log_rotation: "10 MB" log_retention: 7安全加载环境变量:
# 创建环境变量文件(权限设为 600,仅 root 可读) sudo tee /etc/hass-video.env << 'EOF' HASS_VIDEO_USERNAME=your_cloud_username HASS_VIDEO_PASSWORD=your_cloud_password EOF sudo chmod 600 /etc/hass-video.env # 在启动脚本中 source 它 echo "source /etc/hass-video.env" | sudo tee -a /opt/hass-video/scripts/ha_sync.py3.3 核心脚本bind_device_live.py实现:生成真 HLS 直播流
这个脚本的核心任务,是模拟 App 的登录与绑定流程,最终拿到一个可直接喂给 HAcamera平台的.m3u8URL。
关键步骤分解:
登录获取
accessToken:- POST 到
/api/lapp/token/get,Body 包含appKey,appSecret,username,password。 - 响应 JSON 中的
data.accessToken就是后续所有请求的认证凭据。 - 注意:
appKey和appSecret需提前在 萤石开放平台 注册应用获取。如果不想注册,可以用 App 抓包得到的固定appKey(如e157b5c9f1a2b3c4d5e6f7g8h9i0j1k2),但存在被封禁风险,建议正规申请。
- POST 到
设备绑定(一次即可):
- POST 到
/api/lapp/device/bind,Header 加accessToken,Body 包含accessToken,deviceSerial,verifyCode(短信验证码)。 - 验证码获取:先调用
/api/lapp/verifycode/send,传入deviceSerial和type=1(设备绑定),然后手动查收短信填入。
- POST 到
请求直播流:
- POST 到
/api/lapp/device/video/start,Body 包含accessToken,deviceSerial,channelNo,streamType=0(主码流),protocol=2(HLS)。 - 响应中的
data.playUrl就是真正的 HLS 地址,形如https://hls.open.ys7.com/.../index.m3u8?expire=...&auth_key=...。
- POST 到
Python 脚本精要代码(/opt/hass-video/scripts/bind_device_live.py):
import os import json import time import requests from datetime import datetime from pathlib import Path # 加载配置 CONFIG_PATH = Path("/opt/hass-video/config.yaml") with open(CONFIG_PATH) as f: config = yaml.safe_load(f) # 从环境变量读取凭证 username = os.getenv("HASS_VIDEO_USERNAME") password = os.getenv("HASS_VIDEO_PASSWORD") # 构造登录请求 login_url = f"{config['base_url']}/api/lapp/token/get" login_data = { "appKey": config["app_key"], "appSecret": config["app_secret"], "username": username, "password": password } resp = requests.post(login_url, data=login_data, timeout=10) if resp.status_code != 200: raise Exception(f"Login failed: {resp.text}") token_data = resp.json() access_token = token_data["data"]["accessToken"] # 构造直播请求 live_url = f"{config['base_url']}/api/lapp/device/video/start" for device in config["devices"]: device_serial = device["serial"] live_data = { "accessToken": access_token, "deviceSerial": device_serial, "channelNo": device["channel"], "streamType": 0, # 主码流 "protocol": 2 # HLS } resp = requests.post(live_url, data=live_data, timeout=10) if resp.status_code != 200: print(f"Failed to get live stream for {device_serial}: {resp.text}") continue live_info = resp.json() play_url = live_info["data"]["playUrl"] # 保存到设备目录 device_dir = Path(f"/opt/hass-video/devices/{device_serial}") device_dir.mkdir(exist_ok=True) # 写入 live_token.json,包含过期时间(通常 5 分钟) token_file = device_dir / "live_token.json" token_data = { "playUrl": play_url, "expires_at": int(time.time()) + 300, # 5 minutes "updated_at": datetime.now().isoformat() } with open(token_file, "w") as f: json.dump(token_data, f, indent=2) print(f"✅ Live stream updated for {device_serial}: {play_url[:50]}...")3.4 核心脚本query_cloud_records.py实现:按需查询云录像片段
这个脚本的目标,是把“我想看昨天下午 3 点到 4 点的录像”这种自然语言需求,翻译成 API 调用,并返回一个结构化的、HA 可消费的 JSON。
关键逻辑:
时间范围转换:HA 的
input_datetime或automation触发时,通常传入的是 ISO 格式时间字符串(如2023-10-15T15:00:00+00:00)。脚本需将其转换为毫秒级 Unix 时间戳(int(datetime.timestamp() * 1000)),因为云平台 API 只认这个格式。分页查询:
queryCloudRecords接口一次最多返回 20 条录像片段。如果查询跨度大(如 24 小时),需循环调用,每次用上一页的lastTime作为下一页的startTime。结果过滤与排序:原始响应是按“录像结束时间”倒序排列的。我们需要按“开始时间”正序排列,并过滤掉分辨率不符(如
resolution != '1080p')、时长过短(< 10s)的片段。
Python 脚本精要代码(/opt/hass-video/scripts/query_cloud_records.py):
import os import json import time import requests from datetime import datetime, timedelta from pathlib import Path CONFIG_PATH = Path("/opt/hass-video/config.yaml") with open(CONFIG_PATH) as f: config = yaml.safe_load(f) # 加载设备 Token def load_device_token(device_serial): token_file = Path(f"/opt/hass-video/devices/{device_serial}/live_token.json") if not token_file.exists(): return None with open(token_file) as f: return json.load(f) # 查询录像 def query_records(device_serial, start_time_ms, end_time_ms): url = f"{config['base_url']}/api/lapp/cloud/video/list" access_token = load_device_token(device_serial)["accessToken"] # 实际应从 login 获取 params = { "accessToken": access_token, "deviceSerial": device_serial, "channelNo": 1, "startTime": start_time_ms, "endTime": end_time_ms, "size": 20, "pageStart": 0 } all_records = [] while True: resp = requests.get(url, params=params, timeout=15) if resp.status_code != 200: print(f"Query failed for {device_serial}: {resp.text}") break data = resp.json() records = data.get("data", {}).get("list", []) if not records: break # 过滤 & 格式化 for r in records: if r.get("resolution") == config["devices"][0]["resolution"] and r.get("duration", 0) > 10: all_records.append({ "startTime": r["startTime"], "endTime": r["endTime"], "duration": r["duration"], "playUrl": r["playUrl"], # 真 HLS 地址 "thumbnail": r.get("thumbUrl", "") # 小图地址,用于 Lovelace 预览 }) # 分页逻辑 if len(records) < 20: break params["pageStart"] += 20 # 按开始时间正序排列 all_records.sort(key=lambda x: x["startTime"]) return all_records # 示例:查询最近 1 小时 now = int(time.time() * 1000) one_hour_ago = now - 3600000 records = query_records("DS_2CD3T47G2_LSTU_123456789", one_hour_ago, now) # 保存为 JSON 文件供 HA 读取 output_file = Path("/opt/hass-video/devices/DS_2CD3T47G2_LSTU_123456789/records.json") with open(output_file, "w") as f: json.dump(records, f, indent=2)3.5 HA 集成:camera平台与input_datetime自动化联动
现在,我们有了.m3u8URL 和录像 JSON,下一步是让 HA “看见”它们。
configuration.yaml中添加camera平台:
# camera.yaml camera: - platform: generic name: "客厅主摄" still_image_url: "https://hls.open.ys7.com/.../snapshot.jpg" # 可选,静态图 stream_source: !secret living_room_hls_url # 这里用 secret 引用 verify_ssl: true framerate: 25 # 关键:启用 HLS 流 stream: hls_proxy: true # HA 2023.12+ 默认启用,无需额外配置但更好的方式是用restsensor 动态注入:
# sensors.yaml - platform: rest resource: file:///opt/hass-video/devices/DS_2CD3T47G2_LSTU_123456789/live_token.json name: "客厅主摄直播状态" value_template: "{{ value_json.playUrl }}" scan_interval: 300 # 每 5 分钟刷新一次 json_attributes: - expires_at - updated_at - platform: rest resource: file:///opt/hass-video/devices/DS_2CD3T47G2_LSTU_123456789/records.json name: "客厅主摄云录像列表" value_template: "{{ value_json | length }}" scan_interval: 600 # 每 10 分钟刷新一次 json_attributes: - listLovelace 界面嵌入(YAML 模式):
# lovelace.yaml - type: picture-glance entities: - entity: camera.living_room_main - entity: input_datetime.living_room_record_start - entity: input_datetime.living_room_record_end camera_image: camera.living_room_main state_filter: - brightness: 80% show_state: false tap_action: action: navigate navigation_path: "/lovelace/camera-detail" hold_action: action: more-info - type: custom:button-card name: "播放指定时段录像" tap_action: action: call-service service: camera.play_stream service_data: entity_id: camera.living_room_main # 这里需要一个自定义服务,调用 query_cloud_records.py 并传参 # 实现方式见下一节4. 自动化与高级功能:让视频真正“活”起来
4.1 时间轴联动:点击录像缩略图,自动跳转播放
这是提升体验的关键一环。HA 原生不支持“点击缩略图播放对应录像”,但我们可以通过input_datetime+input_text+ 自定义服务来模拟。
步骤:
- 创建
input_datetime实体,用于选择起始/结束时间。 - 创建
input_text实体,用于存储当前选中的录像playUrl。 - 编写一个
shell_command服务,接收时间参数,调用query_cloud_records.py,解析返回 JSON,提取最匹配的playUrl,写入input_text。 - 在 Lovelace 中,用
button-card的tap_action调用该服务,并用state-switch根据input_text状态,动态更新camera的stream_source。
shell_command配置(configuration.yaml):
shell_command: query_living_room_records: "cd /opt/hass-video && source /opt/hass-video-env/bin/activate && python scripts/query_cloud_records.py --device DS_2CD3T47G2_LSTU_123456789 --start {{ states('input_datetime.living_room_record_start') | timestamp_custom('%Y-%m-%dT%H:%M:%S%z', false) }} --end {{ states('input_datetime.living_room_record_end') | timestamp_custom('%Y-%m-%dT%H:%M:%S%z', false) }}"Lovelace 按钮卡片:
- type: custom:button-card name: "🔍 查询录像" tap_action: action: call-service service: shell_command.query_living_room_records show_icon: true show_name: true color_type: card4.2 事件驱动录像:人形检测触发,自动保存并推送
这才是智能家居的精髓。当摄像头检测到“人”,不仅发通知,还要立刻保存一段录像(比如触发前 30 秒 + 触发后 60 秒),并推送到 HA 的media_player播放。
实现逻辑:
- HA 的
binary_sensor已通过 MQTT 或 ONVIF 接收到人形检测事件(如binary_sensor.living_room_motion)。 - 创建自动化:
alias: "客厅人形检测 - 自动录像" trigger: - platform: state entity_id: binary_sensor.living_room_motion to: "on" action: - service: shell_command.query_living_room_records data: start: "{{ (as_timestamp(now()) - 30) | timestamp_custom('%Y-%m-%dT%H:%M:%S%z', false) }}" end: "{{ (as_timestamp(now()) + 60) | timestamp_custom('%Y-%m-%dT%H:%M:%S%z', false) }}" - delay: "00:00:05" # 等待脚本执行完毕 - service: notify.mobile_app_your_phone data: message: "客厅检测到人,已为您保存录像" data: clickAction: "action://camera.living_room_main" - service: camera.play_stream target: entity_id: camera.living_room_main data: # 这里需要从 input_text 读取最新的 playUrl stream_source: "{{ states('input_text.living_room_latest_record') }}"
4.3 录像片段管理:自动清理过期文件,释放磁盘空间
queryCloudRecords返回的playUrl是临时的,过期后链接失效。但我们的脚本会不断生成新的records.json,如果不清理,/opt/hass-video/devices/xxx/目录下会堆积大量历史文件。
编写清理脚本cleanup.py:
import os import time from pathlib import Path # 清理 devices 下超过 7 天的 records.json for device_dir in Path("/opt/hass-video/devices").iterdir(): if not device_dir.is_dir(): continue records_file = device_dir / "records.json" if records_file.exists(): mtime = records_file.stat().st_mtime if time.time() - mtime > 7 * 24 * 3600: # 7 days records_file.unlink() print(f"🧹 Deleted old records.json for {device_dir.name}") # 清理 logs 下超过 30 天的日志 logs_dir = Path("/opt/hass-video/logs") for log_file in logs_dir.glob("*.log.*"): if log_file.stat().st_mtime < time.time() - 30 * 24 * 3600: log_file.unlink()加入 crontab:
# 每天凌晨 2 点执行清理 0 2 * * * /opt/hass-video-env/bin/python /opt/hass-video/scripts/cleanup.py >> /opt/hass-video/logs/cleanup.log 2>&15. 常见问题排查与独家避坑指南
5.1 问题速查表:从报错信息快速定位根源
| 报错信息 | 可能原因 | 解决方案 |
|---|---|---|
401 Unauthorized | accessToken过期或签名错误 | 检查系统时间是否准确;确认appKey/appSecret是否正确;重新运行bind_device_live.py执行登录流程 |
403 Forbidden | 设备未绑定或deviceSerial输入错误 | 用手机 App 确认设备在线;检查config.yaml中serial是否与机身贴纸完全一致(注意下划线、字母大小写);重新发送验证码并绑定 |
playUrl is None | queryCloudRecords返回空数组 | 确认云存储套餐是否生效;检查查询时间范围是否在录像覆盖期内(云录像通常保留 30 天);尝试扩大时间跨度(如查 2 小时而非 10 分钟) |
HLS stream not loading in HA | .m3u8URL 中的auth_key参数过期 | bind_device_live.py必须每 5 分钟运行一次;检查live_token.json中的expires_at字段是否已过期;确保scan_interval设置为 300 |
Camera entity shows 'Unavailable' | stream_source指向的 URL 无法访问 | 在树莓派上curl -I <playUrl>,确认返回200 OK;检查防火墙是否阻止了出站 HTTPS;确认verify_ssl: true是否与证书链兼容(可临时设为false测试) |
5.2 我踩过的坑与独家心得
坑 1:
appKey的“幽灵失效”
我最初用网上搜到的公共appKey,前两周一切正常,第三周突然全部403。抓包对比发现,萤石云对高频调用的appKey会悄悄加入“设备指纹校验”,即同一个appKey在不同 IP、不同 User-Agent 下,连续调用 100 次后会被限流。解决方案:老老实实去 萤石开放平台 注册,填真实公司信息(个人开发者填“个人”即可),审核一般 1 个工作日。拿到自己的appKey/appSecret后,QPS 限制会放宽到 1000 次/天,足够家用。坑 2:
channelNo的“隐形通道”
文档说channelNo=1是主码流,2是子码流。但实测发现,某些固件版本(如海康 V5.6.10)里,channelNo=1返回的是 720p,channelNo=33才是 1080p 主码流。解决方案:用 Postman 手动测试,遍历channelNo=1到50,观察playUrl返回的分辨率参数,找到真正对应你需求的通道号,并记入config.yaml。坑 3:
input_datetime的时区陷阱
HA 默认用 UTC 时间,而你的手机 App 显示的是本地时间。当你在 Lovelace 里选“今天 15:00”,states('input_datetime.xxx')返回的是2023-10-15T15:00:00+00:00,但云平台期望