# Helper

> Use when creating, updating, deleting, or listing Home Assistant helpers (storage-based helpers plus the supported config-entry helper family) through HA NOVA Relay.

- **Type:** Skill
- **Install:** `agentstack add skill-markusleben-ha-nova-helper`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [markusleben](https://agentstack.voostack.com/s/markusleben)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [markusleben](https://github.com/markusleben)
- **Source:** https://github.com/markusleben/ha-nova/tree/main/skills/helper
- **Website:** https://github.com/markusleben/ha-nova

## Install

```sh
agentstack add skill-markusleben-ha-nova-helper
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# HA NOVA Helper

## Scope

`ha-nova:helper` has two helper families:

- **Storage-based family** — full CRUD for:
  - `input_boolean`, `input_number`, `input_text`, `input_select`, `input_datetime`, `input_button`, `counter`, `timer`, `schedule`
- **Config-entry family** — CRUD support for:
  - `utility_meter`, `derivative`, `integration`, `min_max`, `threshold`, `tod`, `statistics`, `history_stats`
  - `group` through the live menu-driven flow; end-to-end support is verified for the `sensor` subtype, and other subtypes must stay anchored to the live step schema instead of guessed fields

Not handled here:

- other config-entry helper families:
  - `template`, `trend`, `random`, `filter`, `generic_thermostat`, `switch_as_x`, `generic_hygrostat`
- automations/scripts config mutations (use `ha-nova:write`)

## Bootstrap (once per session)

Verify relay CLI: `ha-nova relay health`
If this fails: `ha-nova setup`

## Relay Contract

Write payloads with the client's native file-writing tool, then use:

- `ha-nova relay ws --data-file `
- `ha-nova relay core --method  --path  --body-file `
- `ha-nova relay ... --out ` for larger read/verify output
- `--jq-file ` for non-trivial filters; keep inline `--jq` for short selectors only

Family-specific transport:

- **Storage-based family:** WS CRUD + WS list
- **Config-entry family:** WS `config_entries/get` + WS entity-registry joins for list/read; relay `/core` config-entry flow, options-flow, and delete writes

## Flow

### Family 1: Storage-based helpers

#### Listing helpers

Use the compact entity registry (abbreviated keys: `ei` = entity_id, `en` = name, `ai` = area_id).

Create `` with `{"type":"config/entity_registry/list_for_display"}`, then run:

```text
ha-nova relay ws --data-file  --jq-file 
```

Write `` with:

```jq
[.data.entities[] | (.ei | split(".")[0]) as $domain | select(["input_boolean","input_number","input_text","input_select","input_datetime","input_button","counter","timer","schedule"] | index($domain)) | {entity_id: .ei, name: .en, area_id: .ai}] | .[0:30]
```

If user filters by type, narrow the domain filter to that single storage-based domain.

#### Keyword search

```text
ha-nova relay ws --data-file  --jq-file 
```

Write `` with:

```jq
[.data.entities[] | (.ei | split(".")[0]) as $domain | select(["input_boolean","input_number","input_text","input_select","input_datetime","input_button","counter","timer","schedule"] | index($domain)) | select((.ei + " " + (.en // "")) | test("KEYWORD";"i")) | {entity_id: .ei, name: .en, area_id: .ai}] | .[0:20]
```

If 0 results: try synonyms or shorter stems. Never dump entire domains.

#### Reading a single helper

1. Determine type from entity_id domain prefix.
2. Fetch full config via type-specific list:
   ```text
   ha-nova relay ws --data-file  --jq-file 
   ```
   Write `` with:
   ```jq
   [.data[] | select(.name | test("KEYWORD";"i"))]
   ```
3. No single-item read endpoint — always `{type}/list` + filter.

#### Creating a helper

1. Validate intent against `skills/ha-nova/helper-schemas.md` for required/optional fields.
2. Use-case defaults (create only, skip on update/delete):
   - Infer use-case from helper name + type using general HA knowledge.
   - Consult `skills/ha-nova/helper-schemas.md` → Suggested Defaults for principles and field name reminders.
   - If sensible defaults can be inferred: show max 4 as numbered list. Group related fields into one item.
     ```
     Suggested defaults for "{name}" ({type}):
     1. min: 16, max: 30, step: 0.5
     2. unit_of_measurement: "°C"
     3. mode: slider
     4. icon: mdi:thermometer
     Accept all, pick by number (e.g. "1 and 3"), or "skip".
     ```
   - User accepts all, picks by number, or says "skip".
   - Accepted → merge into payload BEFORE preview.
   - No useful defaults inferable → silently skip.
3. Preview the payload.
4. Ask for natural confirmation.
   - for unobserved `group` subtypes, this first confirmation authorizes only the non-persisting menu-step submit, not the final subtype-specific payload
5. Execute:
   ```text
   ha-nova relay ws --data-file 
   ```
6. Verify — list back and confirm new item exists.
7. No domain reload needed — immediate effect.
8. Run storage-family post-write review (see below).

#### Updating a helper

1. Resolve target from `{type}/list` by `name` or internal `id`.
2. Extract `id` from the list response (this is the `{type}_id` for the update command).
3. Preview current vs proposed as a `## Changes` diff (see `skills/ha-nova/write-safety.md` → Pre-Write Diff). Then run a pre-write impact check — `search/related` on this helper entity (see `skills/review/SKILL.md` Step 2) — and surface affected automations/scripts as an advisory. Advisory only; never block.
4. Ask for natural confirmation.
5. Execute:
   ```text
   ha-nova relay ws --data-file 
   ```
6. Verify by re-reading the same list item.
7. Run storage-family post-write review (see below).
8. Update-Revert: after the verified update, capture the snapshot and offer `revert` — see `skills/ha-nova/write-safety.md` → Update-Revert. Storage-family restore rebuilds a schema-valid `{type}/update` from `before_config` (typed `{type}_id` from its `id` + writable fields only — never the raw list item, which lacks `{type}_id` and carries read-only `id`/`entity_id`); `expected_after` is the post-update read-back.

#### Deleting a helper

1. Resolve target from `{type}/list`; extract its `id` (the `{type}_id` used for the delete call — internal, not shown to the user).
2. Preview (see `skills/ha-nova/write-safety.md` → Output hygiene — no raw internal id):
   - name
   - type
   - entity_id
3. Token confirmation: `confirm:` (strict: only exact token accepted; see context skill → Safety Baseline).
4. Execute:
   ```text
   ha-nova relay ws --data-file 
   ```
5. Verify absence from `{type}/list`.

### Family 2: Config-entry helpers

Canonical config-entry helper item:

- `entry_id`
- `domain`
- `title`
- `state`
- `linked_entities[]`
- `supports_options`

`entry_id` is the canonical identity for config-entry helper writes.
If the user gives only a linked `entity_id`, resolve it back to `config_entry_id` through the full entity registry before continuing.

#### Supported domains

- `utility_meter`
- `derivative`
- `integration`
- `min_max`
- `threshold`
- `tod`
- `statistics`
- `group`
- `history_stats`

#### Listing helpers

1. Read all config entries:
   ```text
   ha-nova relay ws --data-file  --out 
   ```
   with `{"type":"config_entries/get"}`.
2. Read full entity registry:
   ```text
   ha-nova relay ws --data-file  --out 
   ```
   with `{"type":"config/entity_registry/list"}`.
3. Filter config entries to the nine supported domains.
4. Join linked entities by matching `config_entry_id`.
5. Present a compact table with:
   - title
   - domain
   - `entry_id`
   - state
   - `supports_options`
   - linked entities (compact comma-separated summary)

#### Keyword search

Search against:

- config-entry `title`
- `domain`
- linked `entity_id`
- linked original/display names when available

If multiple matches remain, present max 5 candidates and ask one blocking question.

#### Reading a single helper

1. Resolve by one of:
   - `entry_id`
   - config-entry title
   - linked `entity_id`
   - if multiple candidates remain after resolution, stop and ask one blocking question
   - never guess between duplicate titles or ambiguous linked-entity matches
2. Re-read `config_entries/get`.
3. Re-read full entity registry and attach `linked_entities[]`.
4. If `supports_options: true`, start an options flow:
   ```text
   ha-nova relay core --method POST --path /api/config/config_entries/options/flow --body-file 
   ```
   with `` containing `{"handler":"","show_advanced_options":false}`.
5. Treat the returned current step as the current editable options snapshot:
   - record `step_id`
   - summarize each exposed field from `description.suggested_value` when present
   - if an exposed field has no `description.suggested_value`, mark its value as unavailable instead of guessing
   - ignore hidden fields that are not exposed in the current step
6. If the options flow is unavailable even though the domain is supported:
   - still show canonical metadata
   - mark update as unsupported on this HA version
7. Present:

```text
**Helper: {title}** (config-entry `{domain}`)
- **Entry ID:** {entry_id}
- **Config-entry state:** {state}
- **Linked entities:** {linked_entities summary}
- **Supports options-flow editing:** {yes/no}
- **Current flow step:** {step_id or "metadata-only fallback"}
- **Current editable fields:** {field summary from the current options step when available}
```

#### Creating a helper

1. Confirm the requested domain is supported in `skills/ha-nova/helper-flow-schemas.md`.
   - treat that file as observed field inventory, not a full validation schema
   - if required field semantics remain uncertain, fail loud and ask one blocking question
2. Prepare the full create plan using `skills/ha-nova/helper-flow-schemas.md`:
   - for one-step domains, the plan is one submit body
   - for `group` with subtype `sensor`, include the required `next_step_id` menu choice and the observed final form
   - for any other `group` subtype, plan only the menu choice before the flow starts; inspect the live subtype form before promising the final field set
   - for `statistics` and `history_stats`, prepare every later step body before preview
3. Preview:
   - title/name
   - domain
   - known step plan
   - all fields already known at this point
   - for unobserved `group` subtypes, say that the final subtype form will be previewed after the menu step returns live fields
4. Ask for natural confirmation.
5. Capture a pre-create baseline:
   ```text
   ha-nova relay ws --data-file  --out 
   ```
   with `` containing `{"type":"config_entries/get"}`.
6. Start the flow:
   ```text
   ha-nova relay core --method POST --path /api/config/config_entries/flow --body-file 
   ```
   `` must contain the handler-start body only.
7. Read the start response and extract `flow_id` before continuing.
   - persist it in a variable or note file
   - fail loud if the start response did not return `flow_id`
8. Iterate the flow until terminal success:
   - if the current response is a menu step, submit only the selected `next_step_id`
   - if that menu step leads to an unobserved `group` subtype form, stop and preview the live subtype fields before the terminal submit
   - after that live subtype preview, ask for a second natural confirmation before sending the terminal subtype-specific payload
   - if the current response is a form step, submit only the fields exposed for that step
   - the submit body for a form step must contain form fields only
   - if a required field is still unresolved and there is no safe value, stop and ask one blocking question
   - if HA returns a form with validation errors, fail loud instead of guessing
9. Verify success at the config-entry layer first:
   - re-read `config_entries/get` into ``
   - if the terminal flow result includes `entry_id`, `passed=true` only when that same `entry_id` is present in ``
   - if the terminal flow result omits `entry_id`, diff `config_entries/get` before vs after by `entry_id`
   - in the diff fallback, collect the new `entry_id` values that were absent before and present after
   - in the diff fallback, `passed=true` only when exactly one new `entry_id` appeared and its metadata is consistent with the requested create
   - if the diff fallback yields zero or multiple new `entry_id` values, or the new entry metadata is inconsistent with the request, fail loud as ambiguous create verification
   - `domain`/`title` are fallback tie-breakers only; they never override a terminal-flow `entry_id`
10. Resolve `linked_entities[]` through the entity registry as secondary evidence only.
11. If the created entry exposes `supports_options: true`, reopen the options flow and store the current editable options snapshot for the post-write response.
12. Run config-entry-family post-write review (see below).

#### Updating a helper

1. Resolve the canonical config-entry helper item:
   - `entry_id`
   - `domain`
   - `title`
   - `linked_entities[]`
   - `supports_options`
   - if multiple candidates remain after resolution, stop and ask one blocking question
   - never guess between duplicate titles or ambiguous linked-entity matches
2. If `supports_options` is false for this entry:
   - fail loud with `update unsupported for this helper on this HA version`
   - do not recreate
   - do not guess a direct patch body
3. Start the options flow:
   ```text
   ha-nova relay core --method POST --path /api/config/config_entries/options/flow --body-file 
   ```
   with `` containing `{"handler":"","show_advanced_options":false}`.
4. Read the start response and extract `flow_id` before continuing.
   - if the start response explicitly shows that options editing is unsupported for this entry on this HA version, fail loud with `update unsupported for this helper on this HA version`
   - if the start call fails for another reason, surface that relay/HA error directly instead of relabeling it as unsupported
   - persist it in a variable or note file
   - fail loud if the start response did not return `flow_id`
5. Capture the current editable options snapshot from the returned form:
   - use `description.suggested_value` as the current value source
   - if a requested field is exposed but lacks `description.suggested_value`, fail loud instead of guessing its current value
   - do not submit read-only fields
   - treat the current step as the authoritative mutable field set
6. Build the update body by merging requested changes over the current options snapshot:
   - carry forward unchanged required fields
   - if the user requests a field the current step does not expose, fail loud as unsupported update for that field on this HA version
   - do not silently ignore non-exposed requested fields
   - do not invent values for fields the current step does not expose
   - for `history_stats`, preserve HA's two-key window invariant across `start`, `end`, and `duration`
   - for `history_stats`, if the requested change switches to a different valid window pair, drop the old third key explicitly so the submit body still contains exactly two of `start`, `end`, and `duration`
7. Preview current vs proposed as a `## Changes` diff (see `skills/ha-nova/write-safety.md` → Pre-Write Diff).
8. Ask for natural confirmation.
9. Submit the current step:
   ```text
   ha-nova relay core --method POST --path /api/config/config_entries/options/flow/{flow_id} --body-file 
   ```
10. If HA returns another form step, repeat the same merge-and-submit rule until terminal `create_entry` or explicit failure.
11. Verify success:
   - re-read `config_entries/get`
   - `passed=true` only when the same `entry_id` still exists
   - reopen the options flow
   - `passed=true` only when the changed fields now appear in `description.suggested_value` as requested
   - if a requested changed field is exposed in the verification step but lacks `description.suggested_value`, fail loud as unverifiable update on this HA version
12. Resolve `linked_entities[]` again as secondary evidence only.
13. Run config-entry-family post-write review (see below).

#### Deleting a helper

1. Resolve target to the canonical config-entry helper item:
   - `entry_id`
   - `domain`
   - `title`
   - `linked_entities[]` when available
   - if multiple candidates remain after resolution, stop and ask one blocking question
   - never guess between duplicate titles or ambiguous linked-entity matches
2. Enforce the helper-domain allowlist before any delete:
   - allowed here: `utility_meter`, `derivative`, `integration`, `min_max`, `threshold`, `tod`, `statistics`, `group`, `history_stats`
   - if the resolved `domain` is outside that allowlist, stop
   - do not call `DELETE /api/config/config_entries

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [markusleben](https://github.com/markusleben)
- **Source:** [markusleben/ha-nova](https://github.com/markusleben/ha-nova)
- **License:** MIT
- **Homepage:** https://github.com/markusleben/ha-nova

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-markusleben-ha-nova-helper
- Seller: https://agentstack.voostack.com/s/markusleben
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
