HA实体没有唯一标识符?ESP32传感器接入方法
ESP32 自制传感器推进 Home Assistant 后提示“没有唯一标识符”?用输入数值和模板传感器两层实体解决,不写 YAML,附界面步骤和自动创建的 API。

自己做的 ESP32 温湿度、人体感应,数据推进 Home Assistant 之后,点开实体只看到一行灰字:
此实体(xiaozhi.xiaozhi_a1b2c3_humidity)没有唯一标识符,因此无法在用户界面管理其设置。
改不了名字,分不了区域,HA 一重启实体还会消失,要等设备下次上报才回来。网上的答案大多是"去写 YAML"。其实不用碰配置文件:靠 HA 自带的两种辅助元素,再加几个 API 调用,就能把 ESP32 的数据变成有唯一 ID 的正式实体。下面是我给书房那块 ESP32-S3(接了 DHT11 和 HC-SR501)实际跑通的做法。

为什么会提示“没有唯一标识符”
最省事的推数据方法,是直接调用 REST 接口:
POST /api/states/sensor.study_temperature
{"state": 23.1, "attributes": {"unit_of_measurement": "°C"}}
这样写进去的只是一条临时状态。它不进实体注册表(entity registry),没有 unique_id,所以:
- 界面里不能改名、改图标、分配区域;
- 每次打开都弹出上面那句提示;
- HA 重启后状态清空,要等下一次上报才出现。
想在界面里管理,实体就得由某个集成正式登记。不装 MQTT、不写 YAML 的话,最顺手的是 HA 自带的两种辅助元素:输入数值 / 输入布尔值,和模板传感器。
思路:两层实体,各管一件事
ESP32 ──定时上报──▶ 你的中转服务(或脚本)
│ 调用服务 input_number.set_value / input_boolean.turn_on|off
▼
第一层:原始值(隐藏) input_number.xxx_temperature_raw
input_number.xxx_humidity_raw
input_boolean.xxx_motion_raw
│ 模板读取
▼
第二层:对外实体 sensor.xxx_temperature 书房 温度
sensor.xxx_humidity 书房 湿度
binary_sensor.xxx_motion 书房 有人
- 第一层只负责存数。输入数值、输入布尔值本身就有唯一 ID,可以通过服务调用写入,写完把它们隐藏起来。
- 第二层才是给仪表盘和自动化用的。模板传感器带上设备类别(温度、湿度、运动)和单位,HA 的历史曲线、统计、语音助手都会把它当成真传感器。
- 传感器拔掉时,往第一层写一个哨兵值(温度写 -100、湿度写 -1),第二层的可用性模板据此显示“不可用”,不会留着一个过期的旧数。
做完之后,打开对外实体的设置,名称、图标、区域都能改,实体标识符那一栏也能编辑:

在界面里手动配置(以温度为例)
湿度、人体感应同理,换一下名称和类型即可。
第 1 步:建原始值辅助元素
设置 → 设备与服务 → 辅助元素 → 创建辅助元素 → 数字:
- 名称:
xxx_temperature_raw(实体 ID 会按名称生成,先用英文,之后再改中文显示名) - 最小值 -100,最大值 100,步长 0.1,显示模式选“输入框”,单位 °C
人体感应选 开关(input_boolean)。
第 2 步:建对外的模板传感器
创建辅助元素 → 模板 → 模板传感器:
- 名称:
xxx_temperature - 状态模板:
{{ states('input_number.xxx_temperature_raw') }} - 度量单位 °C,设备类别“温度”,状态类“测量值”
- 高级选项 → 可用性模板:
{{ states('input_number.xxx_temperature_raw') | float(-100) > -50 }}

人体感应用模板二元传感器,状态模板写 {{ is_state('input_boolean.xxx_motion_raw', 'on') }},设备类别选“运动”。
第 3 步:整理
把三个原始值实体设成隐藏,把三个对外实体改成中文名(比如“书房 温度”),再分配到“书房”区域。
写入数据
ESP32 本身,或者它上报到的中转服务,按下面的方式调用服务就行。令牌在 HA 个人资料页底部的“长期访问令牌”里创建:
# 温度
curl -X POST "$HA_URL/api/services/input_number/set_value" \
-H "Authorization: Bearer $HA_TOKEN" -H "Content-Type: application/json" \
-d '{"entity_id": "input_number.xxx_temperature_raw", "value": 23.1}'
# 有人 / 没人
curl -X POST "$HA_URL/api/services/input_boolean/turn_on" \
-H "Authorization: Bearer $HA_TOKEN" -H "Content-Type: application/json" \
-d '{"entity_id": "input_boolean.xxx_motion_raw"}'
传感器读不到时,温度写 -100、湿度写 -1,对外实体会自动变成“不可用”。
设备多了,用 API 自动建实体
一台设备手动点一遍还行,设备多了就该交给程序。上面两层实体都能通过接口创建,我的中转服务在每台设备第一次上报时自动检查,缺什么补什么:
- 建原始值:走 WebSocket(
/api/websocket)认证后发{"type": "input_number/create", "name": "xxx_temperature_raw", "min": -100, "max": 100, "step": 0.1, "mode": "box", "unit_of_measurement": "°C"},输入布尔值用input_boolean/create。 - 建模板实体:走配置流程,依次 POST 三次:
/api/config/config_entries/flow,body{"handler": "template"}/api/config/config_entries/flow/<flow_id>,body{"next_step_id": "sensor"}(二元传感器填binary_sensor)- 同一地址,body
{"name": "xxx_temperature", "state": "{{ ... }}", "unit_of_measurement": "°C", "device_class": "temperature", "state_class": "measurement", "additional_options": {"availability": "{{ ... }}"}}
- 改显示名、隐藏原始值:WebSocket 发
config/entity_registry/update,带上name和"hidden_by": "user"。 - 已经存在就跳过:先用
config/entity_registry/get查一下实体是否已登记。模板公式要升级时,走选项流程/api/config/config_entries/options/flow更新,不用删了重建。
令牌需要管理员账号的长期访问令牌,普通用户调不了这些创建接口。
效果
对外实体就是普通传感器,历史曲线照常记录。下图是书房一个下午的温度,中间那段降温和回升都完整记了下来,右侧的小缺口是从临时状态迁移到正式实体时留下的。

踩过的坑
- DHT11 读数会跳:标称误差 ±2°C,每分钟读一次,会在 ±0.5°C 范围里来回变,屏幕显示和 HA 记录差 1 度很正常。想要更准,换 SHT30 或 AHT20。
- HA 和设备屏幕对不上:设备屏幕是实时值,HA 里的值最多晚一个上报周期(我设的 60 秒)。
- 旧的临时状态要清掉:从“直接写状态”迁移过来时,先
DELETE /api/states/<entity_id>删掉同名临时状态,再建模板实体,不然新实体会拿到带_2后缀的 ID。 - 还是看到“没有唯一标识符”:说明还有程序在用
POST /api/states写状态,把它改成调用input_number.set_value就行。