ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

海康大华摄像头接入Home Assistant:真HLS直播与云录像方案

海康大华摄像头接入Home Assistant:真HLS直播与云录像方案 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 小时且支持精确到秒的?startTimexxxendTimexxx参数实现真正的“时间轴拖拽播放”。提示这两个接口的调用前提是必须先完成“设备绑定”与“用户登录”。所谓“绑定”不是物理接线而是将设备序列号如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:password192.168.1.100:554/Streaming/Channels/101实际访问会 401 Unauthorized。这是因为厂商在固件里做了白名单限制只允许自家 App 的 User-Agent 访问。.png分片是性能毒药你看到的.m3u8里全是.png说明你拿到的是“快照轮询模式”。这种模式下客户端每秒发 1~5 次 HTTP GET 请求每次下载一张 1920x1080 的 PNG 图片再用 JS 拼成“伪视频”。它的问题极其致命① 带宽浪费巨大一张 PNG 平均 300KB1 秒 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网络层多了一层 NATDNS 解析偶尔失灵且 HA 的shell_command传感器对容器内进程的 PID 监控不稳定。直接在 HA 所在主机树莓派/NUC/Intel NUC上部署才是最稳的选择。硬件要求树莓派 4B4GB 内存或 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.9HA 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乐橙、tapoTP-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_USERNAMEyour_cloud_username HASS_VIDEO_PASSWORDyour_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。关键步骤分解登录获取accessTokenPOST 到/api/lapp/token/getBody 包含appKey,appSecret,username,password。响应 JSON 中的data.accessToken就是后续所有请求的认证凭据。注意appKey和appSecret需提前在 萤石开放平台 注册应用获取。如果不想注册可以用 App 抓包得到的固定appKey如e157b5c9f1a2b3c4d5e6f7g8h9i0j1k2但存在被封禁风险建议正规申请。设备绑定一次即可POST 到/api/lapp/device/bindHeader 加accessTokenBody 包含accessToken,deviceSerial,verifyCode短信验证码。验证码获取先调用/api/lapp/verifycode/send传入deviceSerial和type1设备绑定然后手动查收短信填入。请求直播流POST 到/api/lapp/device/video/startBody 包含accessToken,deviceSerial,channelNo,streamType0主码流,protocol2HLS。响应中的data.playUrl就是真正的 HLS 地址形如https://hls.open.ys7.com/.../index.m3u8?expire...auth_key...。Python 脚本精要代码/opt/hass-video/scripts/bind_device_live.pyimport 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, datalogin_data, timeout10) if resp.status_code ! 200: raise Exception(fLogin 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, datalive_data, timeout10) if resp.status_code ! 200: print(fFailed 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_okTrue) # 写入 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, indent2) 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:0000:00。脚本需将其转换为毫秒级 Unix 时间戳int(datetime.timestamp() * 1000)因为云平台 API 只认这个格式。分页查询queryCloudRecords接口一次最多返回 20 条录像片段。如果查询跨度大如 24 小时需循环调用每次用上一页的lastTime作为下一页的startTime。结果过滤与排序原始响应是按“录像结束时间”倒序排列的。我们需要按“开始时间”正序排列并过滤掉分辨率不符如resolution ! 1080p、时长过短 10s的片段。Python 脚本精要代码/opt/hass-video/scripts/query_cloud_records.pyimport 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, paramsparams, timeout15) if resp.status_code ! 200: print(fQuery 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(keylambda 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, indent2)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_datetimeinput_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.yamlshell_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.pyimport 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 215. 常见问题排查与独家避坑指南5.1 问题速查表从报错信息快速定位根源报错信息可能原因解决方案401 UnauthorizedaccessToken过期或签名错误检查系统时间是否准确确认appKey/appSecret是否正确重新运行bind_device_live.py执行登录流程403 Forbidden设备未绑定或deviceSerial输入错误用手机 App 确认设备在线检查config.yaml中serial是否与机身贴纸完全一致注意下划线、字母大小写重新发送验证码并绑定playUrl is NonequeryCloudRecords返回空数组确认云存储套餐是否生效检查查询时间范围是否在录像覆盖期内云录像通常保留 30 天尝试扩大时间跨度如查 2 小时而非 10 分钟HLS stream not loading in HA.m3u8URL 中的auth_key参数过期bind_device_live.py必须每 5 分钟运行一次检查live_token.json中的expires_at字段是否已过期确保scan_interval设置为 300Camera entity shows Unavailablestream_source指向的 URL 无法访问在树莓派上curl -I playUrl确认返回200 OK检查防火墙是否阻止了出站 HTTPS确认verify_ssl: true是否与证书链兼容可临时设为false测试5.2 我踩过的坑与独家心得坑 1appKey的“幽灵失效”我最初用网上搜到的公共appKey前两周一切正常第三周突然全部403。抓包对比发现萤石云对高频调用的appKey会悄悄加入“设备指纹校验”即同一个appKey在不同 IP、不同 User-Agent 下连续调用 100 次后会被限流。解决方案老老实实去 萤石开放平台 注册填真实公司信息个人开发者填“个人”即可审核一般 1 个工作日。拿到自己的appKey/appSecret后QPS 限制会放宽到 1000 次/天足够家用。坑 2channelNo的“隐形通道”文档说channelNo1是主码流2是子码流。但实测发现某些固件版本如海康 V5.6.10里channelNo1返回的是 720pchannelNo33才是 1080p 主码流。解决方案用 Postman 手动测试遍历channelNo1到50观察playUrl返回的分辨率参数找到真正对应你需求的通道号并记入config.yaml。坑 3input_datetime的时区陷阱HA 默认用 UTC 时间而你的手机 App 显示的是本地时间。当你在 Lovelace 里选“今天 15:00”states(input_datetime.xxx)返回的是2023-10-15T15:00:0000:00但云平台期望
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进