Skip to content

Add Marstek MQTT responder to answer CT002/CT003 polls locally#328

Draft
tomquist wants to merge 2 commits intodevelopfrom
claude/ct002-mqtt-support-BjFKJ
Draft

Add Marstek MQTT responder to answer CT002/CT003 polls locally#328
tomquist wants to merge 2 commits intodevelopfrom
claude/ct002-mqtt-support-BjFKJ

Conversation

@tomquist
Copy link
Copy Markdown
Owner

Summary

Adds a Marstek MQTT responder feature to the MQTT Insights service that allows AstraMeter to answer Marstek CT002/CT003 poll requests on the local MQTT broker. When combined with hame-relay bridging to the Marstek cloud, this surfaces the emulator in the Marstek mobile app as if it were a real device.

Key Changes

  • New marstek_mqtt.py module: Pure helper functions and dataclass for the Marstek MQTT protocol

    • MarstekMqttBinding: Dataclass holding per-device registration (device_id, ct_type, MAC, get_values callback, wifi_rssi, ver_v)
    • Topic helpers: app_topics_for(), device_topics_for(), parse_app_topic()
    • Payload helpers: is_poll_payload(), build_response(), normalize_mac()
    • Comprehensive unit tests in marstek_mqtt_test.py
  • Extended MqttInsightsService:

    • Added marstek_mqtt_enabled config option (default: true)
    • New public API: register_marstek() and unregister_marstek() for device lifecycle management
    • Message handler _handle_marstek_message() that dispatches polls quickly
    • Poll responder _serve_marstek_poll() that offloads to async tasks to prevent slow powermeters from blocking the listener loop
    • Tracks in-flight tasks and failed get_values calls per device with rate-limited logging
  • Integration in main.py:

    • Wires up Marstek MQTT responder for CT002 devices when Marstek credentials yield a managed MAC
    • Passes marstek_mac through device lifecycle
    • Registers/unregisters bindings on device start/stop
  • Configuration:

    • Added MARSTEK_MQTT_ENABLED config option to [MQTT_INSIGHTS] section
    • Updated config loader to parse the new boolean setting
    • Updated example config and README

Implementation Details

  • Non-blocking poll handling: Poll requests are dispatched to background tasks so a slow powermeter can't stall the MQTT listener loop
  • Dual-topic support: Responds on both hame_energy/ and marstek_energy/ topic prefixes for compatibility
  • Graceful failure handling: Suppresses repeated get_values errors per device with recovery logging
  • Live subscription: Supports registering bindings before or after service start; live-subscribes if already connected
  • Comprehensive test coverage: 10+ integration tests with real MQTT broker, covering poll dispatch, error handling, unregistration, and concurrent handler scenarios

https://claude.ai/code/session_01K5ypPxYASWXJewf7Lk9a1e

claude added 2 commits April 17, 2026 08:34
When [MARSTEK] credentials are configured, the managed fake CT MAC
returned by ensure_managed_fake_device() is now used to answer the
Marstek CT002/CT003 MQTT poll protocol (cd=1 on hame_energy/marstek_energy
topics) on the same broker as MQTT Insights. Combined with hame-relay,
this makes the emulator's readings visible as a CT in the Marstek app.

Enabled by default via MARSTEK_MQTT_ENABLED in [MQTT_INSIGHTS]; opt out
by setting it to false. Without Marstek credentials the responder stays
silent (one info log per CT device).

https://claude.ai/code/session_01K5ypPxYASWXJewf7Lk9a1e
The MQTT Insights listener is a single async-for loop. When a Marstek
poll handler awaited binding.get_values() inline it could block every
subsequent message (other CT polls, Insights commands) for as long as
the powermeter took to yield a reading. Spawn each response in its own
task instead, and track/cancel those tasks on disconnect/shutdown.

Also snapshot _marstek_bindings under the lock before scanning in
_find_marstek_binding, and drop the type: ignore on the topic helpers
by returning an explicit 2-tuple.

Adds two integration tests: register-before-start populates subscriptions
on first connect, and a slow handler on one binding doesn't block a
concurrent fast poll on another binding.

https://claude.ai/code/session_01K5ypPxYASWXJewf7Lk9a1e
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 19, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bba97618-6a69-4c7a-bfae-881eba17f7fc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/ct002-mqtt-support-BjFKJ

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.

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