Skip to content

Add Home Assistant add-on UI configuration generator#414

Merged
tomquist merged 8 commits into
developfrom
claude/keen-dirac-76XCJ
Jun 2, 2026
Merged

Add Home Assistant add-on UI configuration generator#414
tomquist merged 8 commits into
developfrom
claude/keen-dirac-76XCJ

Conversation

@tomquist

@tomquist tomquist commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a new "Home Assistant add-on (UI options)" target to the web config generator that produces YAML configuration options for the Home Assistant add-on's Configuration tab. This complements the existing config.ini and ESPHome targets, making it easier for users to configure the add-on through the UI without manually editing YAML.

Key Changes

  • New generator function (generateHomeAssistant) that emits YAML key-value pairs for the add-on's Configuration schema, including:

    • Grid power source (single sensor or calculated from import/export entities)
    • Device types and throttling/wait options
    • CT identity and efficiency settings
    • Signal-conditioning filters (smoothing, deadband, Hampel outlier filter, PID controller)
    • Marstek and MQTT Insights configuration
    • Proper YAML scalar escaping for strings, numbers, and booleans
  • Updated state model to support "homeassistant" as a valid target alongside "python" and "esphome"

  • UI improvements:

    • New target choice button for "Home Assistant add-on (UI options)"
    • Automatic meter coercion to a single Home Assistant sensor when this target is selected
    • Contextual help text explaining the add-on's single-meter limitation
    • Custom config fallback note directing users to config.ini for advanced setups
    • Static "Power source" label (instead of meter type picker) when Home Assistant target is active
    • Dynamic output filename (astrameter-options.yaml for this target)
  • Add-on configuration schema (config.yaml) and translations (en.yaml) extended with new filter options:

    • hampel_window, hampel_n_sigma, hampel_min_threshold (Hampel outlier filter)
    • pid_kp, pid_ki, pid_kd, pid_output_max, pid_mode (PID controller)
  • Add-on runtime (run.sh) updated to pass these new filter options from the Configuration tab to the generated config

  • Tests added to verify correct YAML generation for full filter sets, minimal configs, and calculated power scenarios

  • Documentation updated (README, CHANGELOG) to reflect the new target and explain the add-on's single-meter constraint

Implementation Details

The generator intelligently handles:

  • Blank/empty values (omitted from output)
  • Type-aware YAML scalars (bare numbers/booleans, quoted strings with proper escaping)
  • Fallback logic (global throttle/wait options preferred over per-meter tuning)
  • Power calculation mode (separate import/export entities vs. single signed sensor)
  • Conditional sections (Marstek, MQTT only included if enabled and required fields present)

The UI enforces the add-on's architectural constraint (single Home Assistant sensor) by automatically collapsing the meter list when this target is selected, while still allowing users to switch back to config.ini for multi-meter or advanced scenarios.

https://claude.ai/code/session_01QFtgjskrRiwoJ96kstNyX2

Summary by CodeRabbit

  • New Features

    • Added Home Assistant add-on target to the web config generator, producing ready-to-paste configuration blocks.
    • Introduced optional signal-conditioning filters: Hampel outlier filter and PID controller for Home Assistant configuration.
  • Documentation

    • Updated installation guides with filter configuration instructions and guided setup references.
    • Added CHANGELOG entries documenting new optional filter parameters.

Surface the Hampel outlier filter and PID controller through the Home
Assistant add-on as optional Configuration fields (no defaults, so they
stay hidden/disabled unless set), wiring them through config.yaml schema,
translations, and run.sh.

Add a third "Home Assistant add-on" target to the web config generator
that emits a ready-to-paste add-on options YAML (including the newly
exposed filters), restricted to a single Home Assistant power source with
a pointer to custom config.ini for advanced setups.

Update README and CHANGELOG.
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tomquist, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 2 minutes and 10 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7b651465-e078-4cf2-97f1-09c8cfa12f5e

📥 Commits

Reviewing files that changed from the base of the PR and between c512c5f and 6fea00c.

📒 Files selected for processing (4)
  • web/ts/generate.test.ts
  • web/ts/generate.ts
  • web/ts/state.test.ts
  • web/ts/state.ts

Walkthrough

This pull request adds support for a new "homeassistant" configuration target in the web-based config generator. It includes Home Assistant add-on schema updates for Hampel filtering and PID controller parameters, runtime integration to pass these settings into the core config.ini, a new YAML-based options generator, UI field filtering by target, and updated documentation.

Changes

Home Assistant Add-on Target Configuration and Generation

Layer / File(s) Summary
State and target type definitions
web/ts/state.ts
The State.target union now includes "homeassistant". Default device types switch from shellypro3em to ct002. Marstek and MQTT Insights are enabled by default. Target migration normalizes s.target to "esphome" | "homeassistant" or falls back to "python".
Home Assistant configuration schema and UI labels
ha_addon/config.yaml, ha_addon/translations/en.yaml
New optional configuration fields added to the HA add-on schema for Hampel filtering (hampel_window, hampel_n_sigma, hampel_min_threshold) and PID control (pid_kp, pid_ki, pid_kd, pid_output_max, pid_mode). Corresponding UI labels and descriptions provided in translations.
Home Assistant runtime configuration emission
ha_addon/run.sh
The add-on startup script conditionally emits Hampel and PID parameter values from the HA configuration input into the generated config.ini file's [HOMEASSISTANT] section.
Configuration generation for Home Assistant target
web/ts/generate.ts
Incomplete marstek and mqtt_insights sections are now skipped when enabled but missing required credentials. New generateHomeAssistant() function produces YAML options blocks containing device types, power input aliasing, global/per-meter throttle settings, CT fields, filter parameters, and conditional Marstek/MQTT settings. The generate() dispatcher routes homeassistant target to the new generator.
Test coverage for new configuration generation
web/ts/generate.test.ts
Added test cases verifying that generateConfigIni omits incomplete marstek/mqtt_insights sections, and that generateHomeAssistant correctly emits filter fields, omits unset parameters, and derives calculated aliases from power mode settings.
Web UI filtering and target-aware field rendering
web/ts/app.ts
Introduces Home Assistant-specific field allowlists and coerceHaMeter() to enforce single-meter configuration for the HA target. CT membership toggling now edge-triggers updates to marstek.enabled and mqttInsights.enabled defaults. Meter editor filters fields to HA grid-power entity subset and removes per-meter throttle/wait-for-push fields. Power meter type picker is replaced with static "Power source" label for HA. Power meter, CT steering, and optional extras cards render different content by target. Output filename logic is centralized via outputFilename() helper used in preview/download operations.
Device type description updates
web/ts/schema.ts
Help text for ct002 and ct003 Marstek CT emulation options updated to clarify that ct003 is emulated identically to ct002.
Documentation and metadata updates
CHANGELOG.md, README.md, web/generator.html
CHANGELOG documents new Hampel/PID fields and homeassistant target. README adds guidance on optional signal-conditioning filters and directs users to the config generator for a copy-pastable add-on options block. Web generator meta description updated to mention Home Assistant add-on options output.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WebUI as Web Config Generator
  participant AppState as app.ts State
  participant Generator as generate.ts
  participant Output as Output File
  
  User->>WebUI: Select "Home Assistant add-on" target
  WebUI->>AppState: Update state.target = "homeassistant"
  AppState->>AppState: coerceHaMeter() enforces single meter
  AppState->>WebUI: Re-render UI with HA field filters
  
  User->>WebUI: Configure filters & options
  WebUI->>AppState: Update state with user settings
  
  User->>WebUI: Click preview/download
  WebUI->>Generator: Call generate(state)
  Generator->>Generator: Detect homeassistant target
  Generator->>Generator: Call generateHomeAssistant(state)
  Generator->>Output: Emit YAML options block
  Output-->>User: Display/download add-on options
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Add Home Assistant add-on UI configuration generator' accurately describes the main change: introducing a new Home Assistant add-on target to the web config generator that produces YAML for the add-on's Configuration tab.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/keen-dirac-76XCJ

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-02 12:20 UTC

claude added 6 commits June 2, 2026 07:50
The add-on sets the Home Assistant host, port, token and API path
automatically, so the HA target now only surfaces the grid-power entity
fields instead of the full homeassistant powermeter form.
For the Home Assistant target, hide settings the add-on configures
automatically or doesn't expose: general device IDs / skip-test / web
editor, CT active-control / balancer / saturation fields, Marstek base
URL / timezone, and the MQTT discovery/topic fields. The MQTT section is
reframed as an optional custom broker, and throttle / wait-for-push are
shown only once under General.
CT003 isn't just an older CT002 variant: CT002 clamps around the phases
while CT003 reads via the electricity meter's interface. They share the
same protocol.
Drop the hardware-matching framing: AstraMeter emulates both and the
protocol is identical, so there's no difference from the battery's
perspective and the choice doesn't depend on physical hardware.
CT002 is the recommended emulation for the common multi-battery case, so
make it the default device type instead of Shelly Pro 3EM.
These are CT002/CT003 features, so enable them by default (the default
device type is now CT002) and keep them in sync when CT emulation is
toggled in the device card — edge-triggered so a manual override sticks.

Guard the config.ini generators to omit an enabled-but-unconfigured
[MARSTEK] / [MQTT_INSIGHTS] section so the default-on toggles never
produce broken output before credentials/broker are entered.
@tomquist tomquist marked this pull request as ready for review June 2, 2026 09:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/ts/state.ts (1)

121-145: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize meters when restoring the Home Assistant target.

coerceHaMeter() only runs on the target button click. A loaded share link/project can already have target: "homeassistant" with multiple meters or a non-homeassistant first meter, and generateHomeAssistant() in web/ts/generate.ts always reads state.meters[0]. That leaves restore paths generating blank or wrong YAML until the user toggles targets manually. Migration should enforce the same single-meter HA contract as the UI.

Proposed fix
 export function migrate(s: any): State {
   const d = defaultState();
   s = s && typeof s === "object" ? s : {};
-  const meters = Array.isArray(s.meters) && s.meters.length ? s.meters : d.meters;
+  const target = s.target === "esphome" || s.target === "homeassistant" ? s.target : "python";
+  const restoredMeters = Array.isArray(s.meters) && s.meters.length ? s.meters.map(cleanMeter) : d.meters;
+  const meters =
+    target === "homeassistant"
+      ? [restoredMeters.find((m) => m.type === "homeassistant") ?? newMeter("homeassistant")]
+      : restoredMeters;
   return {
     ...d,
     ...s,
-    target: s.target === "esphome" || s.target === "homeassistant" ? s.target : "python",
+    target,
     general: ((): State["general"] => {
       const sg = s.general && typeof s.general === "object" ? s.general : {};
       const dg = d.general;
       return {
         deviceTypes: Array.isArray(sg.deviceTypes) ? sg.deviceTypes.map((t: unknown) => String(t)) : dg.deviceTypes,
@@
     esphome: { ...d.esphome, ...(s.esphome || {}) },
     ct: { fields: asObject(s.ct && s.ct.fields) },
     marstek: { enabled: !!(s.marstek && s.marstek.enabled), fields: asObject(s.marstek && s.marstek.fields) },
     mqttInsights: { enabled: !!(s.mqttInsights && s.mqttInsights.enabled), fields: asObject(s.mqttInsights && s.mqttInsights.fields) },
-    meters: meters.map(cleanMeter),
+    meters,
   };
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/ts/state.ts` around lines 121 - 145, When restoring state in the returned
object (where meters is computed and mapped with cleanMeter), ensure the same
single-meter Home Assistant contract used by the UI is enforced: if the resolved
target is "homeassistant" then coerce and keep only a single HA meter (apply
coerceHaMeter to meters[0] or to the first valid meter) and drop any additional
meters so generateHomeAssistant() will read a correct first meter; update the
meters assignment/normalization (the meters constant and the meters:
meters.map(cleanMeter) output) to perform this normalization before mapping with
cleanMeter and reference coerceHaMeter and generateHomeAssistant in your change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/ts/generate.ts`:
- Around line 568-581: yamlScalar currently returns unquoted digit-only scalars
which can lose leading zeros; update generation so that when calling yamlScalar
from generateHomeAssistant (via the add function) you pass the option key (or
otherwise detect field type) and force quoting for known string-typed fields
(e.g., "ct_mac", "mac", "mac_address", "password", "entity_id", "id", "serial")
and for any all-digit values for those keys; keep yamlScalar’s numeric and
boolean detection for other keys but ensure add calls yamlScalar with the key so
it can decide to quote, and add a regression for ct_mac values like
"001122334455". Ensure references: yamlScalar, generateHomeAssistant, add, opts,
isBlank.
- Around line 632-640: The mqtt URI builder incorrectly treats mi.TLS as truthy
for non-boolean strings and doesn't percent-encode credentials; update the block
that reads state.mqttInsights / const mi to normalize TLS (e.g., treat mi.TLS as
true only when the value explicitly equals boolean true or the string
"true"/"1") and percent-encode USER and PASSWORD before composing the credential
portion used by add("mqtt_uri", ...); ensure you build cred using the encoded
username and encoded password (and still omit the ":" or "@" when empty) so
parse_mqtt_uri can safely unquote them.

---

Outside diff comments:
In `@web/ts/state.ts`:
- Around line 121-145: When restoring state in the returned object (where meters
is computed and mapped with cleanMeter), ensure the same single-meter Home
Assistant contract used by the UI is enforced: if the resolved target is
"homeassistant" then coerce and keep only a single HA meter (apply coerceHaMeter
to meters[0] or to the first valid meter) and drop any additional meters so
generateHomeAssistant() will read a correct first meter; update the meters
assignment/normalization (the meters constant and the meters:
meters.map(cleanMeter) output) to perform this normalization before mapping with
cleanMeter and reference coerceHaMeter and generateHomeAssistant in your change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e1283e9f-4a59-45ad-9d8c-4c419fdf489c

📥 Commits

Reviewing files that changed from the base of the PR and between 2b8b38d and c512c5f.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • README.md
  • ha_addon/config.yaml
  • ha_addon/run.sh
  • ha_addon/translations/en.yaml
  • web/generator.html
  • web/ts/app.ts
  • web/ts/generate.test.ts
  • web/ts/generate.ts
  • web/ts/schema.ts
  • web/ts/state.ts

Comment thread web/ts/generate.ts Outdated
Comment thread web/ts/generate.ts
- Quote string-typed add-on options (ct_mac, entity aliases, transforms,
  credentials, mqtt_uri) so all-digit values like a MAC keep leading zeros
  and aren't coerced to numbers.
- Normalize mqtt_uri building: treat TLS as on only for explicit true/'true'/'1'
  (restored state may carry a string), and percent-encode the username/password.
- Enforce the single-Home-Assistant-meter contract on state restore so
  generateHomeAssistant() always reads a correct first meter.

Adds regressions for the MAC, TLS/credential, and restore cases.
@tomquist tomquist merged commit ac88216 into develop Jun 2, 2026
27 checks passed
@tomquist tomquist deleted the claude/keen-dirac-76XCJ branch June 2, 2026 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants