Skip to content

Add AstraMeter device connections and improve device naming#311

Merged
tomquist merged 5 commits into
developfrom
claude/add-astrameter-discovery-5ScEV
Apr 7, 2026
Merged

Add AstraMeter device connections and improve device naming#311
tomquist merged 5 commits into
developfrom
claude/add-astrameter-discovery-5ScEV

Conversation

@tomquist
Copy link
Copy Markdown
Owner

@tomquist tomquist commented Apr 6, 2026

Summary

This PR enhances MQTT device discovery by adding AstraMeter-specific device connections and improving device naming consistency across all device types.

Key Changes

  • Device Connections: Added astrameter connection type to all device discovery payloads (CT002 consumer, CT002 device, Shelly battery, and Shelly device) to establish proper device correlation within the AstraMeter ecosystem
  • CT002 Consumer: Now always includes an astrameter connection ["astrameter", "ct002_{device_id}"], even when consumer_id is not a MAC address
  • Device Naming: Updated device names to include "AstraMeter" prefix for consistency:
    • CT002 device: "AstraMeter CT002 {device_id}"
    • Shelly battery: "AstraMeter Shelly Battery {battery_ip}"
    • Shelly device: "AstraMeter Shelly {device_id}"
  • Conditional Logic: Simplified CT002 consumer discovery by removing the conditional check before assigning connections (connections list is now always populated)

Implementation Details

  • The astrameter connection uses the format ["astrameter", "{device_type}_{device_id}"] to uniquely identify devices within the system
  • CT002 consumer connections now always include the astrameter connection as the first entry, followed by any additional connections (Bluetooth MAC, network MAC, or IP)
  • Updated test cases to verify the presence of astrameter connections and improved device naming

https://claude.ai/code/session_013FZJBrgH4U9vA8eqUzXYZP

Summary by CodeRabbit

  • New Features

    • Devices published via MQTT discovery now include add-on linkage so they appear under the AstraMeter add-on in Home Assistant and show AstraMeter-branded names for CT002, Shelly, and battery devices.
    • The add-on now resolves and emits its slug for discovery when available.
  • Tests

    • Unit tests expanded to cover addon-slug handling and branded device discovery behavior.

claude added 4 commits April 6, 2026 23:08
…er connection

- CT002 / Shelly meter device names now include "AstraMeter".
- Meter and consumer/battery devices share an `astrameter` connection
  tuple so Home Assistant can correlate consumers with their meter.
Resolve the add-on slug from the supervisor (`/store/addons/self`) in
run.sh and forward it as `ADDON_SLUG` in the `[MQTT_INSIGHTS]` section.
The MQTT Insights service then sets `via_device: <addon_slug>` on the
CT002 and Shelly meter discovery payloads so Home Assistant nests them
under the AstraMeter add-on device.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7dd635c-e28f-4364-9c19-6c915018ff9c

📥 Commits

Reviewing files that changed from the base of the PR and between 281d652 and 7b040f3.

📒 Files selected for processing (3)
  • ha_addon/run.sh
  • src/astrameter/config/config_loader.py
  • src/astrameter/mqtt_insights/mqtt_insights_test.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • ha_addon/run.sh
  • src/astrameter/mqtt_insights/mqtt_insights_test.py

Walkthrough

The add-on runtime now queries Supervisor for its slug and emits ADDON_SLUG in the generated MQTT discovery config when available; the app reads ADDON_SLUG into its config and discovery builders optionally include that slug as device.via_device and add "AstraMeter" branding to device names.

Changes

Cohort / File(s) Summary
Add-on runtime & config loader
ha_addon/run.sh, src/astrameter/config/config_loader.py
run.sh queries Supervisor /addons/self/info for .slug and emits ADDON_SLUG into the generated MQTT discovery config.ini when discovery is enabled and slug resolved. Config loader reads optional ADDON_SLUG into MqttInsightsConfig.addon_slug (normalizes empty/whitespace to None).
Discovery builders & service
src/astrameter/mqtt_insights/discovery.py, src/astrameter/mqtt_insights/service.py
Device discovery builders for CT002 and Shelly gain an optional `addon_slug: str
Tests
src/astrameter/mqtt_insights/mqtt_insights_test.py
Unit tests updated to assert device["via_device"] presence when addon_slug provided and absence when omitted; tests also validate ADDON_SLUG parsing (present, absent, whitespace-only).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Supervisor
    participant AddonRun as "ha_addon/run.sh"
    participant AstraService as "AstraMeter Service"
    participant MQTT as "MQTT Broker"
    participant HomeAssistant as "Home Assistant"

    Supervisor->>AddonRun: GET /addons/self/info (retrieve .slug)
    AddonRun-->>AddonRun: parse .slug (store ADDON_SLUG)
    AddonRun->>AstraService: start process (env/config includes ADDON_SLUG)
    AstraService->>AstraService: read config (ADDON_SLUG -> cfg.addon_slug)
    AstraService->>MQTT: publish discovery payloads (include via_device=ADDON_SLUG when set)
    MQTT->>HomeAssistant: Home Assistant consumes discovery topics
    HomeAssistant-->>HomeAssistant: create devices/entities linked via device.via_device
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes in the PR: adding device connections (via_device linking for AstraMeter ecosystem) and improving device naming with AstraMeter branding.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/add-astrameter-discovery-5ScEV

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
src/astrameter/mqtt_insights/mqtt_insights_test.py (1)

309-323: Consider asserting addon_slug is None in default/empty config tests.

You already assert the populated case; adding explicit None assertions in the default/empty tests would close the regression loop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/astrameter/mqtt_insights/mqtt_insights_test.py` around lines 309 - 323,
Add an assertion that addon_slug is None in the default/empty config tests to
ensure regressions are caught: update the tests that call
read_mqtt_insights_config for default/empty configs (the same test functions
asserting broker/port/tls etc.) to include assert result.addon_slug is None so
the code path handling addon_slug is validated alongside the populated case
already covered in the existing test that asserts addon_slug ==
"34dea19a_astrameter".
src/astrameter/config/config_loader.py (1)

573-573: Trim ADDON_SLUG before coercing to None.

Small hardening: whitespace-only values should not propagate as a slug.

Suggested diff
-                addon_slug=config.get(section, "ADDON_SLUG", fallback="") or None,
+                addon_slug=(
+                    config.get(section, "ADDON_SLUG", fallback="").strip() or None
+                ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/astrameter/config/config_loader.py` at line 573, The ADDON_SLUG value is
being coerced to None without trimming, so whitespace-only values become a
non-empty slug; fetch the raw value via config.get(section, "ADDON_SLUG",
fallback=""), strip it (e.g. .strip()) and then coerce to None if the stripped
string is empty—i.e., set addon_slug based on the stripped result; update the
assignment that currently uses addon_slug=config.get(...) or None to perform the
strip-before-none logic so whitespace-only values become None.
ha_addon/run.sh (1)

149-153: Use more reliable endpoint to resolve add-on slug.

The Supervisor API contract guarantees that /store/addons/self returns slug as a non-null field, so a literal "null" string guard is unnecessary. However, relying on the store layer is indirect—use /addons/self/info instead, which is the direct endpoint for the current add-on's metadata:

addon_slug="$(bashio::api.supervisor GET '/addons/self/info' false | jq -r '.slug')"

Alternatively, if available in this bashio version, use the bashio::addons helper which already calls this endpoint with proper defaults.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ha_addon/run.sh` around lines 149 - 153, Replace the indirect Supervisor API
call that reads '/store/addons/self' when resolving addon_slug in run.sh: use
the direct add-on metadata endpoint '/addons/self/info' (or the bashio::addons
helper if available) to reliably extract the slug into the addon_slug variable;
update the bashio::api.supervisor invocation used where addon_slug is set and
ensure the command parses the JSON .slug field (e.g., via jq -r '.slug' or the
helper's output) so the check for non-empty addon_slug and the subsequent log
messages behave correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ha_addon/run.sh`:
- Around line 149-153: Replace the indirect Supervisor API call that reads
'/store/addons/self' when resolving addon_slug in run.sh: use the direct add-on
metadata endpoint '/addons/self/info' (or the bashio::addons helper if
available) to reliably extract the slug into the addon_slug variable; update the
bashio::api.supervisor invocation used where addon_slug is set and ensure the
command parses the JSON .slug field (e.g., via jq -r '.slug' or the helper's
output) so the check for non-empty addon_slug and the subsequent log messages
behave correctly.

In `@src/astrameter/config/config_loader.py`:
- Line 573: The ADDON_SLUG value is being coerced to None without trimming, so
whitespace-only values become a non-empty slug; fetch the raw value via
config.get(section, "ADDON_SLUG", fallback=""), strip it (e.g. .strip()) and
then coerce to None if the stripped string is empty—i.e., set addon_slug based
on the stripped result; update the assignment that currently uses
addon_slug=config.get(...) or None to perform the strip-before-none logic so
whitespace-only values become None.

In `@src/astrameter/mqtt_insights/mqtt_insights_test.py`:
- Around line 309-323: Add an assertion that addon_slug is None in the
default/empty config tests to ensure regressions are caught: update the tests
that call read_mqtt_insights_config for default/empty configs (the same test
functions asserting broker/port/tls etc.) to include assert result.addon_slug is
None so the code path handling addon_slug is validated alongside the populated
case already covered in the existing test that asserts addon_slug ==
"34dea19a_astrameter".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a242aa47-eea8-4d08-bcfb-8b2575603768

📥 Commits

Reviewing files that changed from the base of the PR and between af3334a and 281d652.

📒 Files selected for processing (5)
  • ha_addon/run.sh
  • src/astrameter/config/config_loader.py
  • src/astrameter/mqtt_insights/discovery.py
  • src/astrameter/mqtt_insights/mqtt_insights_test.py
  • src/astrameter/mqtt_insights/service.py

- run.sh: query the canonical /addons/self/info endpoint and parse the
  slug with jq instead of relying on the /store/addons/self route.
- config_loader: strip ADDON_SLUG before falling back to None so
  whitespace-only values do not become a bogus slug.
- tests: assert addon_slug is None for default/empty configs and add a
  whitespace-only regression test.
@tomquist tomquist merged commit 8d62753 into develop Apr 7, 2026
13 checks passed
@tomquist tomquist deleted the claude/add-astrameter-discovery-5ScEV branch April 7, 2026 08:41
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