Skip to content

Commit d41b641

Browse files
fix: support history_stats and mold_indicator helpers in ha_config_set_helper (#2206)
HA's Helpers UI has offered config flows for history_stats (since 2024.7) and mold_indicator, but the flow-helper enum was never re-audited after #324, so ha_config_set_helper rejected both at schema validation. Adds both to the flow-helper lists and mirrored literals, documents the deliberate otp exclusion (TOTP confirm step) with routing to ha_set_integration, and adds e2e lifecycle tests. Fixes #2187
1 parent 7549ea2 commit d41b641

10 files changed

Lines changed: 228 additions & 108 deletions

File tree

src/ha_mcp/server.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,14 +603,15 @@ def _apply_settings_visibility(self) -> None:
603603
"conditions actions get show detail"
604604
),
605605
# s09: "create helper" → ha_config_set_helper should outrank remove_helper
606-
# Covers all 27 helper types (12 simple + 15 flow-based, unified in #967).
606+
# Covers all 29 helper types (12 simple + 17 flow-based, unified in #967).
607607
"ha_config_set_helper": (
608608
"create update new add helper "
609609
"input_boolean input_button input_number input_text input_datetime "
610610
"input_select counter timer schedule zone person tag "
611611
"template group utility_meter derivative min_max threshold "
612612
"integration statistics trend random filter tod "
613-
"generic_thermostat switch_as_x generic_hygrostat"
613+
"generic_thermostat switch_as_x generic_hygrostat "
614+
"history_stats mold_indicator"
614615
),
615616
# Boost tools that compete with ha_search for common queries
616617
"ha_config_get_script": (

src/ha_mcp/settings_ui/locales/zh-Hans.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@
608608
},
609609
"ha_config_set_helper": {
610610
"title": "创建或更新辅助实体",
611-
"description": "创建或更新 Home Assistant 辅助实体与配置子条目(28 种类型,统一接口)。"
611+
"description": "创建或更新 Home Assistant 辅助实体与配置子条目(30 种类型,统一接口)。"
612612
},
613613
"ha_config_set_label": {
614614
"title": "创建或更新标签",

src/ha_mcp/tools/config_entry_flow.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
The create/update entry point is the unified ha_config_set_helper tool in
99
tools_config_helpers.py, which routes to create_flow_helper / update_flow_helper
10-
for the 15 helper types listed in FLOW_HELPER_TYPES.
10+
for the 17 helper types listed in FLOW_HELPER_TYPES.
1111
1212
The same flow walkers drive every other config-entry surface, not just
1313
helpers: ``ha_set_integration`` creates entries for arbitrary domains through
@@ -65,7 +65,10 @@ def _reject_redaction_sentinels(config_dict: dict[str, Any]) -> None:
6565
)
6666

6767

68-
# 15 helpers that use Config Entry Flow API (Issue #324).
68+
# 17 helpers that use Config Entry Flow API (Issue #324, #2187).
69+
# `otp` is the one helper-typed config flow deliberately left out: its confirm
70+
# step demands a live TOTP code derived from the secret, which no flow walker
71+
# can supply. It stays reachable through ha_set_integration(domain="otp").
6972
SUPPORTED_HELPERS = Literal[
7073
"template",
7174
"group",
@@ -82,6 +85,8 @@ def _reject_redaction_sentinels(config_dict: dict[str, Any]) -> None:
8285
"generic_thermostat",
8386
"switch_as_x",
8487
"generic_hygrostat",
88+
"history_stats",
89+
"mold_indicator",
8590
]
8691

8792
# Value-set form of SUPPORTED_HELPERS for runtime routing checks.
@@ -103,6 +108,8 @@ def _reject_redaction_sentinels(config_dict: dict[str, Any]) -> None:
103108
"generic_thermostat",
104109
"switch_as_x",
105110
"generic_hygrostat",
111+
"history_stats",
112+
"mold_indicator",
106113
}
107114
)
108115

src/ha_mcp/tools/tools_config_helpers.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4398,6 +4398,7 @@ async def ha_config_set_helper(
43984398
"generic_hygrostat",
43994399
"generic_thermostat",
44004400
"group",
4401+
"history_stats",
44014402
"input_boolean",
44024403
"input_button",
44034404
"input_datetime",
@@ -4406,6 +4407,7 @@ async def ha_config_set_helper(
44064407
"input_text",
44074408
"integration",
44084409
"min_max",
4410+
"mold_indicator",
44094411
"person",
44104412
"random",
44114413
"schedule",
@@ -4702,7 +4704,8 @@ async def ha_config_set_helper(
47024704
"helper_type='config_subentry' "
47034705
"(template, group, utility_meter, derivative, min_max, threshold, "
47044706
"integration, statistics, trend, random, filter, tod, "
4705-
"generic_thermostat, switch_as_x, generic_hygrostat). "
4707+
"generic_thermostat, switch_as_x, generic_hygrostat, "
4708+
"history_stats, mold_indicator). "
47064709
"Ignored for simple helper types. "
47074710
"Field set is delivered as data_schema on the first validation error."
47084711
),
@@ -4739,7 +4742,7 @@ async def ha_config_set_helper(
47394742
) -> dict[str, Any]:
47404743
"""
47414744
Create or update Home Assistant helper entities and config subentries
4742-
(28 types, unified interface).
4745+
(30 types, unified interface).
47434746
47444747
MUST call ha_get_skill_guide OR refer to your locally installed skills first.
47454748
@@ -4753,9 +4756,13 @@ async def ha_config_set_helper(
47534756
47544757
FLOW types (pass `config` dict, Config Entry Flow API): template, group,
47554758
utility_meter, derivative, min_max, threshold, integration, statistics, trend,
4756-
random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat.
4759+
random, filter, tod, generic_thermostat, switch_as_x, generic_hygrostat,
4760+
history_stats, mold_indicator.
47574761
Note: `tod` is the purpose-built "is-current-time-in-range" indicator
47584762
(supports cross-midnight ranges, unlike `schedule`).
4763+
Note: `otp` is a helper in the HA UI but is not offered here — its flow
4764+
requires a live TOTP code. Create it with ha_set_integration(domain="otp"),
4765+
as with any other helper-domain flow outside this list.
47594766
47604767
CONFIG_SUBENTRY type (Config Subentry Flow API): config_subentry.
47614768
Pass `entry_id`, `subentry_type`, and `config`. Pass `subentry_id` to

src/ha_mcp/tools/tools_integrations.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
"tag",
105105
# config-entry subentries
106106
"config_subentry",
107-
# 15 FLOW
107+
# 17 FLOW
108108
"template",
109109
"group",
110110
"utility_meter",
@@ -120,6 +120,8 @@
120120
"generic_thermostat",
121121
"switch_as_x",
122122
"generic_hygrostat",
123+
"history_stats",
124+
"mold_indicator",
123125
]
124126
assert set(get_args(HelperTypeLiteral)) == (
125127
SIMPLE_HELPER_TYPES | FLOW_HELPER_TYPES | {"config_subentry"}
@@ -1846,7 +1848,9 @@ async def ha_set_integration(
18461848
18471849
WHEN NOT TO USE:
18481850
- Helpers (template, group, utility_meter, ...): use
1849-
ha_config_set_helper.
1851+
ha_config_set_helper. The exception is `otp`, which is a helper in
1852+
the HA UI but is created HERE via domain="otp" — its flow needs a
1853+
live TOTP code, so ha_config_set_helper deliberately omits it.
18501854
- Config subentries: use
18511855
ha_config_set_helper(helper_type='config_subentry').
18521856
- Removing an entry: use ha_remove_helpers_integrations.
@@ -2110,10 +2114,10 @@ async def ha_remove_helpers_integrations(
21102114
- SIMPLE (12, websocket-delete): input_button, input_boolean,
21112115
input_select, input_number, input_text, input_datetime, counter,
21122116
timer, schedule, zone, person, tag.
2113-
- FLOW (15, config-entry-delete via entity lookup): template, group,
2117+
- FLOW (17, config-entry-delete via entity lookup): template, group,
21142118
utility_meter, derivative, min_max, threshold, integration,
21152119
statistics, trend, random, filter, tod, generic_thermostat,
2116-
switch_as_x, generic_hygrostat.
2120+
switch_as_x, generic_hygrostat, history_stats, mold_indicator.
21172121
21182122
ROUTING:
21192123
- SIMPLE helper_type + bare helper_id or entity_id → websocket delete.

src/ha_mcp/tools/tools_yaml_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ async def ha_config_set_yaml(
403403
- Automations (storage-mode) -> ha_config_set_automation
404404
- Scripts (storage-mode) -> ha_config_set_script
405405
- Scenes (storage-mode) -> ha_config_set_scene
406-
- All 28 helper types (input_*, counter, timer, schedule, zone, person,
406+
- All 30 helper types (input_*, counter, timer, schedule, zone, person,
407407
tag, group, min_max, threshold, derivative, statistics, utility_meter,
408408
trend, filter, switch_as_x, etc.) -> ha_config_set_helper
409409

tests/initial_test_state/configuration.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,14 @@ template:
7474
state: "{{ 10.0 }}"
7575
device_class: temperature
7676
state_class: measurement
77-
unit_of_measurement: "°C"
77+
unit_of_measurement: "°C"
78+
# mold_indicator's flow requires a humidity source alongside the two
79+
# temperature ones. HA's EntitySelector validates only the entity_id
80+
# shape, so a temperature sensor would be accepted here and then log
81+
# "unsupported unit" and hold the helper at unknown forever.
82+
- name: demo Humidity
83+
unique_id: e2e_demo_humidity
84+
state: "{{ 45.0 }}"
85+
device_class: humidity
86+
state_class: measurement
87+
unit_of_measurement: "%"

0 commit comments

Comments
 (0)