Home Assistant Prometheus 集成:指标命名规范与 /api/prometheus 实现原理
本篇围绕 Home Assistant 的 Prometheus 集成展开:它以 README 中的三条指标命名准则 为核心骨架,结合 集成主源码 与 测试用例,讲清该集成如何把实体状态转换为 Prometheus 兼容格式、如何暴露 /api/prometheus 抓取端点,以及命名规则在代码中的落地点,帮助你在自有监控体系中正确配置、抓取并理解每一条指标的由来。
集成定位:把 Home Assistant 变成 Prometheus 指标源
集成文档 README.md 开篇即给出定位:该集成以 Prometheus 兼容格式对外暴露指标(exposes metrics in a Prometheus compatible format)。也就是说,Home Assistant 自身作为被抓取方(scrape target),把实体状态、设备注册表信息、房间/楼层拓扑等数据,实时转换为 Prometheus 文本 exposition 格式,供 Prometheus、Grafana、Alertmanager 等工具消费。
从 manifest.json 可以确认其工程特征:
- 依赖
http集成("dependencies": ["http"]),因为指标端点挂在 HTTP 服务上; - 通过固定依赖
prometheus-client==0.21.0提供指标序列化能力,并在loggers中把prometheus_client的日志纳入 Home Assistant 日志体系; iot_class为assumed_state,即集成不实时连接外部设备,仅基于 Home Assistant 内部状态推导。
抓取端点常量定义在源码第 91 行:API_ENDPOINT = "/api/prometheus",由 PrometheusView 类 注册为 HomeAssistantView。其 get 方法在请求到达时调用 prometheus_client.generate_latest(prometheus_client.REGISTRY)(放入执行器线程以避免阻塞事件循环),并以 text/plain 返回最新快照。由于指标注册在进程级全局 REGISTRY 上,抓取端点天然还包含 python_info、python_gc_* 等 Python 运行时指标——测试代码 中即断言了 python_info、python_gc_objects_collected_total 等行会出现在响应体中。
配置入口
该集成是纯 YAML 配置型组件,setup() 在 源码第 136-192 行 完成三件事:向 hass.http 注册视图、构建 PrometheusMetrics 实例、订阅五类总线事件(state_changed 以及实体/设备/房间/楼层四个注册表的 updated 事件),并在启动时遍历现存实体完成首次枚举。
指标命名准则:README 三条规则与源码落点
README 的“Metric naming guidelines”一节给出了三条准则。逐条对照源码,可以看到每一条都有明确的实现支撑。
准则一:指标名与标签名须符合 Prometheus 命名规范
原文要求 metric 和 label 名称遵循 Prometheus 官方命名实践。源码中的直接证据是指标名清洗函数 _sanitize_metric_name:
ALLOWED_METRIC_CHARS = set(string.ascii_letters + string.digits + "_:")
@staticmethod
def _sanitize_metric_name(metric: str) -> str:
metric = metric.replace("\u03bc", "\u00b5")
return "".join(
[c if c in ALLOWED_METRIC_CHARS else f"u{hex(ord(c))}" for c in metric]
)
它把允许字符集限定为字母、数字、下划线与冒号(冒号用于 :job 风格的记录规则指标),并把希腊字母 μ(U+03BC,常见于密度单位 “μg/m³”)替换为度量衡符号 μ(U+00B5)——这与 _unit_string 中对 UnitOfDensity.MICROGRAMS_PER_CUBIC_METER 的注释相互印证。任何不在允许集内的字符会被转写为 u<hex(ord(c))> 形式,保证指标名在 Prometheus 端永不非法。
准则二:领域特定指标以 domain 作为指标名前缀
原文要求:sensor、switch、climate 等特定领域(domain)的指标,必须以 domain 作为指标名前缀。这一规则体现在通用数值指标生成器 _numeric_metric 中:
metric = self._metric(
f"{domain}_state_{unit}", # 有单位时:如 binary_sensor_state_w
prometheus_client.Gauge,
f"State of the {title} measured in {unit}",
self._labels(state),
)
# 无单位时退化为 f"{domain}_state"
binary_sensor、input_boolean、number、lock、person、humidifier 等十余个领域共用这条路径(见 _handle_binary_sensor、_handle_lock 等处理函数,源码第 755-909 行),因此你会看到 binary_sensor_state_*、lock_state_*、input_number_state_* 这类带 domain 前缀的指标名。领域专属指标同样遵循该约定,例如 climate_target_temperature_celsius、cover_position、fan_speed_percent、water_heater_away_mode 等,前缀即其所属 domain。
准则三:枚举型状态导出为按标签拆分的 0/1 布尔指标
原文要求:类似枚举的值(实体状态、当前模式等)应导出为取值 0 或 1 的“布尔”指标,并按状态/模式值作为指标标签拆分。源码中对应的通用实现是 _enum_metric:
def _enum_metric(self, state, current_value, values, metric_name,
metric_description, enum_label_name):
if current_value is None or values is None:
return
for value in values:
self._metric(
metric_name,
prometheus_client.Gauge,
metric_description,
self._labels(state, {enum_label_name: value}),
).set(float(value == current_value))
它对候选值序列中的每一个取值都打一个标签组合,当前值对应 1、其余全为 0。典型例子:
cover实体导出cover_state{state="closed"}、{state="opening"}、{state="open"}、{state="closing"}四组标签(_handle_cover);climate实体导出climate_mode{mode="..."}、climate_action{action="..."}、climate_preset_mode、climate_fan_mode,候选值分别来自实体的hvac_modes、HVACAction枚举、preset_modes、fan_modes能力属性;alarm_control_panel实体按AlarmControlPanelState全枚举展开alarm_control_panel_state{state="..."}。
这种“one-hot 展开”正是 Prometheus 社区处理分类状态的惯用做法,便于在 PromQL 中用 climate_mode{mode="heat"} == 1 直接做判断。
配置参数全解(基于源码 Schema)
README 未列出配置项,但 CONFIG_SCHEMA 完整定义了七项参数,可据此写出可复制的配置:
prometheus:
filter:
include_domains: [] # 使用 entityfilter 过滤语法,精确控制哪些实体参与导出
namespace: "homeassistant" # 指标名前缀,默认 "homeassistant"
requires_auth: true # 抓取端点是否要求认证,默认 true
default_metric: "sensor_value" # 传感器默认指标名
override_metric: "my_sensor" # 所有传感器的统一覆盖指标名
component_config: # 按 entity_id 精确覆盖
sensor.living_room_temp:
override_metric: "lr_temperature"
component_config_glob: # 按 glob 通配覆盖
"sensor.living_room_*":
override_metric: "lr_metric"
component_config_domain: # 按 domain 覆盖
sensor:
override_metric: "all_sensors"
各参数语义与源码依据:
| 参数 | 默认值 | 作用 | 源码依据 |
|---|---|---|---|
filter |
{}(全量) |
采用 entityfilter.FILTER_SCHEMA,决定哪些实体进入指标导出 |
setup() L141-L142;被过滤实体在调试日志中打印 “Filtered out entity” |
namespace |
homeassistant |
生成 metrics_prefix,拼在每个指标名前;设为空字符串则不加前缀 |
L109、L243-L246;测试 test_view_empty_namespace / test_view_default_namespace 分别验证空/默认前缀行为 |
requires_auth |
true |
控制 PrometheusView 抓取是否需要认证 |
setup() L138、PrometheusView L1148-L1156 |
default_metric |
未设置 | 传感器命名回退链中的兜底指标名 | _sensor_default_metric L1087-L1089 |
override_metric |
未设置 | 全局统一覆盖所有传感器指标名 | _sensor_override_metric L1110-L1114 |
component_config |
{} |
以 entity_id 为键的单实体覆盖,值为 {override_metric: str} |
COMPONENT_CONFIG_SCHEMA_ENTRY,L104-L106 |
component_config_glob / component_config_domain |
{} |
以 glob / domain 为键的批量覆盖 | 三者经 EntityValues 组合查询,L146-L150 |
传感器指标名的决定链
传感器(sensor)是指标命名最复杂的领域。_handle_sensor 按一个有序处理链确定指标名,取第一个非空结果:
_sensor_override_component_metric—— 命中component_config/_glob/_domain中的override_metric;_sensor_override_metric—— 命中全局override_metric;_sensor_timestamp_metric——device_class: timestamp的传感器固定导出为sensor_timestamp_seconds(该 device class 没有单位属性,值经as_timestamp转成时间戳);_sensor_attribute_metric—— 有device_class时导出sensor_{device_class}_{unit},如sensor_temperature_celsius(测试中的断言示例);_sensor_default_metric—— 命中default_metric;_sensor_fallback_metric—— 兜底逻辑:有单位则sensor_unit_{unit},否则sensor_state,用于向后兼容(L1122-L1127)。
单位字符串本身也经过规范化(_unit_string):/ 转为 _per_、统一小写、% 映射为 percent、°F 映射为 celsius(因为华氏值会被统一换算,见下文)。
标签体系与基础指标
每条实体指标都携带固定三标签(_labels):
entity:实体 ID;domain:实体所属 domain;friendly_name:实体友好名称(重命名时旧标签组合会被移除,见handle_state_changed_event对 friendly name 变化的检测)。
在领域专属指标之外,每个实体状态变化还会更新一组“元指标”(handle_state L279-L326):
*_state_change(Counter):状态变更累计次数;*_entity_available(Gauge):实体是否可用(unavailable/unknown记 0);*_last_updated_time_seconds(Gauge):last_updated的 Unix 时间戳;*_entity_info(Gauge,值为 1):记录实体与房间(area标签)的归属关系,房间取自实体自身area_id,或回退到其所属设备的有效房间(_find_area_id L615-L631)。
注册表级指标 area_info、floor_info 则把房间/楼层拓扑本身导出为指标(handle_area / handle_floor L423-L481),标签含 area/area_name/floor 或 floor/floor_name/floor_level,便于在 Grafana 中做维度下钻。
标签集清理机制
Prometheus 客户端库不擅长“查询某指标已设置过哪些标签值”,而旧标签组合若不清理会永久残留在抓取输出中。为此源码定义了 MetricNameWithLabelValues 数据类,按实体记录其写过的 (metric_name, label_values) 组合;实体从注册表移除、被禁用、重命名或变为 unavailable 时,_remove_labelsets 会逐一调用 metric.remove(*label_values) 回收对应序列(L483-L508)。房间/设备归属变化也会触发 entity_info 的移除与重建(handle_entity_registry_updated、handle_device_registry_updated,含父设备房间变更时刷新子设备实体的逻辑)。
温度与距离的单位统一
从源码结构看,该集成强制把温度统一到摄氏度:_temperature_metric 检测到系统单位为 °F 时用 TemperatureConverter 先把属性值转成 °C 再导出(L649-L664);_numeric_metric 与 _handle_sensor 对 unit_of_measurement: °F 的状态值同样先换算(L745-L753、L1071-L1077)。geo_location 的距离则统一为米(DistanceConverter 转换后写入 geo_location_distance_meters,L773-L802)。这样跨实体、跨集成查询时,单位语义始终一致。
其他值得注意的领域导出:light 导出 0-100 的亮度百分比(on 时取 brightness/255,L829-L843);switch 除数值状态外还会导出全部可浮点化的属性(switch_attr_*,_handle_attributes L510-L522);带 battery_level 属性的实体(含 zwave)额外导出 battery_level_percent;automation 每次触发累加 automation_triggered_count(Counter)。
验证方式
集成自带完整测试 tests/components/prometheus/test_init.py(3400 余行),其核心模式与真实使用一致:
- fixture 中先重置
prometheus_client.REGISTRY并注册ProcessCollector、PlatformCollector、GCCollector(L485-L506); - 通过
async_setup_component(hass, "prometheus", {...})以 YAML 配置加载集成; client.get("/api/prometheus")断言 200、content-type: text/plain,并逐行比对指标文本(generate_latest_metrics L509-L519);EntityMetric辅助类强制校验每条断言必须包含domain、friendly_name、entity三个必备标签(L113-L150)。
实际部署时,可在 Home Assistant 所在主机验证端点(requires_auth: true 时需带认证):
curl http://<home-assistant-host>:8123/api/prometheus
随后在 Prometheus 侧添加对应 scrape job(路径 /api/prometheus)即可开始采集;命名是否符合上文三条准则、前缀是否为 homeassistant_(默认 namespace),都可以直接在返回的文本 exposition 中核对。
小结
- README 的三条命名准则并非口号,分别由
_sanitize_metric_name的字符集清洗、_numeric_metric等的 domain 前缀拼接、_enum_metric的 one-hot 标签展开在源码中逐条兑现; - 指标端点为
/api/prometheus,默认带homeassistant前缀、默认要求认证,均可通过 YAML 配置调整; - 传感器指标名有六级优先回退链,温度统一换算为摄氏度,标签固定含
entity/domain/friendly_name,并配有实体/注册表事件驱动的标签集清理机制; - 若你正在为该集成新增领域处理函数,应沿用
_float_metric、_bool_metric、_enum_metric等通用构建器并保持 domain 前缀命名,同时参照 test_init.py 的断言风格补充测试。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00