Skip to content

feat(meter): add ecoflow-stream-mqtt with public-API MQTT and cascade discovery#29217

Draft
Metatron2k2 wants to merge 1 commit intoevcc-io:masterfrom
Metatron2k2:feat/ecoflow-stream-mqtt
Draft

feat(meter): add ecoflow-stream-mqtt with public-API MQTT and cascade discovery#29217
Metatron2k2 wants to merge 1 commit intoevcc-io:masterfrom
Metatron2k2:feat/ecoflow-stream-mqtt

Conversation

@Metatron2k2
Copy link
Copy Markdown

Summary

Adds a dedicated device template for the EcoFlow Stream family that uses EcoFlow's public-API MQTT broker for live data. The existing ecoflow-stream template (REST-only via the generic ecoflow meter) is kept untouched so no existing configuration changes behaviour.

Why

The REST quota/all endpoint returns a stale, largely aggregated snapshot for Stream devices. Notably:

  • individual PV string power (powGetPv..powGetPv4) is never populated, only powGetPvSum, which can include output from unrelated PV sources connected to the same cloud meter;
  • battery power on the CMS level (powGetBpCms) is 0 for Stream AC Hybrid installations;
  • cmsBattSoc is always 0; the real per-module SoC lives in f32ShowSoc;
  • in cascade/multi-unit setups only the master's view is reachable via REST, so the SoC shown by evcc differs from the app's cascade average.

Home Assistant's tolwi/hassio-ecoflow-cloud integration gets these values by subscribing to EcoFlow's public MQTT broker. evcc now does the same.

What

  • meter/ecoflow_mqtt.go — shared public-API MQTT client, one per (accessKey, region).

    • HMAC-SHA256 signed certification call to /iot-open/sign/certification, TLS connection, subscribe to /open/<certificateAccount>/<sn>/quota.
    • The client is reused across every ecoflow-stream-mqtt meter so only one of EcoFlow's 10 unique-client-ID/day slots is consumed per account.
    • Client ID is hashed from accessKey + hostname so dev and prod instances on the same account can coexist without disconnecting each other.
    • Parser handles both the flat top-level JSON payload Stream devices emit and the {"params": {...}} / {"param": {...}} envelopes used by other device families.
  • meter/ecoflow_stream_mqtt.go — new meter type ecoflow-stream-mqtt.

    • Maps evcc usage → EcoFlow param keys: gridpowGetSysGrid; pvpowGetPv..powGetPv4; batteryinputWatts - outputWatts with f32ShowSoc.
    • Optional sibling auto-discovery via /iot-open/sign/device/list, matching by the 2-char serial prefix so cascade systems (e.g. two Stream AC Hybrids linked as master + slave) appear as one aggregated meter.
    • Subscribes each discovered serial on the shared MQTT client and caches the latest values.
    • Reads prefer the MQTT cache, fall back to per-serial REST with util.Cached.
    • CurrentPower is the sum across devices; Soc is the plain average, matching the EcoFlow app's cascade view.
  • templates/definition/meter/ecoflow-stream-mqtt.yaml — new device entry listed as EcoFlow Stream (MQTT). Exposes the same access key / secret key / serial / usage inputs as the existing ecoflow-stream template, plus an advanced discover toggle (default true).

Backwards compatibility

Purely additive: no existing files are modified. The legacy ecoflow-stream template and the generic ecoflow meter remain byte-for-byte identical to master.

Verification

Live-tested against a cascade of two EcoFlow Stream AC Hybrid units on one account. With a single configured master serial and discover: true, evcc reports:

[ecoflow-stream-mqtt] INFO  discover: tracking 2 device(s): BK11...,BK31...
[ecoflow-mqtt]        DEBUG subscribe /open/<user>/BK11.../quota
[ecoflow-mqtt]        DEBUG subscribe /open/<user>/BK31.../quota
[site]                pv 1 power: 593W
[site]                battery 1 power: -1067W
[site]                battery 1 soc: 37%

Values match the EcoFlow mobile-app cascade view within the usual smoothing lag.

Test plan

  • make test — all Go tests pass, including the meter package.
  • make lint — no new lint findings in the added files (the handful of warnings on master pre-date this PR).
  • go vet ./meter/... — clean.
  • make docs — regenerates templates/docs/{de,en}/meter/ecoflow-stream-mqtt.yaml (gitignored path, matching repo convention).
  • go build ./... — binary built.
  • Live end-to-end test on a 2-unit Stream AC Hybrid cascade: PV sum, battery inputWatts - outputWatts (negated), and averaged SoC all match the app.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In CurrentPower, any error from a single device’s REST fallback aborts the whole reading; consider logging and skipping failing devices so transient REST issues for one unit don’t take down the aggregated meter.
  • The MQTT certification call in ecoflow_mqtt.go uses http.DefaultClient without an explicit timeout; aligning this with the rest of the codebase by using a client with request.Timeout (or similar) would avoid potential hangs on slow/blocked networks.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `CurrentPower`, any error from a single device’s REST fallback aborts the whole reading; consider logging and skipping failing devices so transient REST issues for one unit don’t take down the aggregated meter.
- The MQTT certification call in `ecoflow_mqtt.go` uses `http.DefaultClient` without an explicit timeout; aligning this with the rest of the codebase by using a client with `request.Timeout` (or similar) would avoid potential hangs on slow/blocked networks.

## Individual Comments

### Comment 1
<location path="templates/definition/meter/ecoflow-stream-mqtt.yaml" line_range="47-34" />
<code_context>
+    help:
+      en: Secret Key from EcoFlow Developer Console
+      de: Secret Key aus der EcoFlow Developer Console
+  - name: serial
+    required: true
+    help:
+      en: Serial number of the main (master) device of your Stream system
</code_context>
<issue_to_address>
**question:** Template requires a serial even though the implementation supports discovery-only setups.

The Go constructor only errors when `Serial == "" && len(Serials) == 0 && !Discover`, so discovery-only use is supported. Marking `serial` as `required: true` in the template blocks this mode. If discovery-only setups should be allowed, make `serial` optional here or document that it must still be set even when `discover` is enabled.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread templates/definition/meter/ecoflow-stream-mqtt.yaml
@Metatron2k2 Metatron2k2 force-pushed the feat/ecoflow-stream-mqtt branch 3 times, most recently from f033108 to 509fb9c Compare April 18, 2026 07:30
@andig
Copy link
Copy Markdown
Member

andig commented Apr 18, 2026

We should only have one implementation, not two. If we want this the other should be deprecated. Further, this needs be greatly simplified to use the evcc mqtt infrastructure, ideally reducing this to a template-only implementation.

@andig andig added the devices Specific device support label Apr 18, 2026
@andig andig marked this pull request as draft April 18, 2026 08:00
@Metatron2k2
Copy link
Copy Markdown
Author

@andig Thanks for the feedback. I'm looking into it.

@Metatron2k2 Metatron2k2 force-pushed the feat/ecoflow-stream-mqtt branch from 8349c92 to 3ff2348 Compare April 18, 2026 08:17
… discovery

Adds a dedicated device template for the EcoFlow Stream family that
uses EcoFlow's public-API MQTT broker for live data. The existing
`ecoflow-stream` template (REST-only via the generic `ecoflow` meter)
is kept untouched so no existing configuration changes behaviour.

Why
---
The REST `quota/all` endpoint returns a stale, largely aggregated
snapshot for Stream devices. Notably:

- individual PV string power (`powGetPv`..`powGetPv4`) is never
  populated, only `powGetPvSum`, which includes output from unrelated
  PV sources on the same cloud meter;
- battery power on the CMS level (`powGetBpCms`) is 0 for Stream AC
  Hybrid installations;
- `cmsBattSoc` is always 0; the real per-module SoC lives in
  `f32ShowSoc`;
- in cascade/multi-unit setups only the master's view is reachable
  via REST.

Home Assistant's `hassio-ecoflow-cloud` integration gets these values
by subscribing to EcoFlow's public MQTT broker. evcc now does the
same.

What
----
- `meter/ecoflow_mqtt.go`: shared MQTT client for the EcoFlow public
  API, one per (access key, region). HMAC-SHA256 signed certification
  call to `/iot-open/sign/certification` via `request.NewClient`
  (inherits `request.Timeout` and the instrumented transport), then
  transport is delegated to evcc's shared `plugin/mqtt.Client` (same
  path as `meter/zendure`). `plugin/mqtt` handles the Paho options,
  TLS, reconnect and automatic re-subscription of every `Listen`ed
  topic, so this file is limited to the EcoFlow-specific bits: cert
  fetch, per-host stable client ID (hash of access key + hostname) to
  respect EcoFlow's 10-unique-client-IDs/account/day quota while
  letting prod and dev instances on the same account coexist,
  per-serial `/open/<certificateAccount>/<sn>/quota` subscription,
  and a payload parser that accepts both flat Stream objects and the
  {"params":{...}} envelopes used by other device families.

- `meter/ecoflow_stream_mqtt.go`: new meter type
  `ecoflow-stream-mqtt`. Responsibilities:
    * map evcc usage (grid/pv/battery) to the right EcoFlow keys
      (powGetSysGrid / powGetPv..4 / inputWatts-outputWatts +
      f32ShowSoc);
    * optionally discover sibling devices on the same account via
      `/iot-open/sign/device/list`, matching by the 2-char serial
      prefix so cascade systems (e.g. two Stream AC Hybrids linked
      as master+slave) appear as one aggregated meter;
    * subscribe each discovered serial to MQTT and cache the latest
      values;
    * prefer the MQTT cache for reads, fall back to per-serial REST
      with `util.Cached`;
    * sum CurrentPower across devices and return a plain-average
      SoC, matching the cascade view shown in the EcoFlow app;
    * tolerate per-device REST errors: a transient failure on one
      cascade unit is logged and skipped so healthy devices keep
      reporting; only when every device fails is the last error
      surfaced.

- `templates/definition/meter/ecoflow-stream-mqtt.yaml`: new device
  entry listed as "EcoFlow Stream (MQTT)". Exposes the same
  access key / secret key / serial / usage inputs as `ecoflow-stream`
  plus an advanced `discover` toggle (default true).

Verification
------------
Live-tested with a cascade of two EcoFlow Stream AC Hybrid units.
With a single configured master serial and discover=true, evcc now
reports:

  [ecoflow-stream-mqtt] INFO discover: tracking 2 device(s): BK11...,BK31...
  [site] pv 1 power: 593W
  [site] battery 1 power: -1067W
  [site] battery 1 soc: 37%

matching the EcoFlow mobile-app cascade view within the usual smoothing
lag. `go test ./...` and `golangci-lint run` are clean.
@Metatron2k2 Metatron2k2 force-pushed the feat/ecoflow-stream-mqtt branch from 3ff2348 to 557e37d Compare April 18, 2026 08:18
@Metatron2k2
Copy link
Copy Markdown
Author

@andig I moved it to the builtin client. Sorry for not doing this in the first place.

Having it template only does not work, because of the HMAC Signing necessary for ecoflow.

@Kubiac
Copy link
Copy Markdown
Contributor

Kubiac commented Apr 21, 2026

Hi @Metatron2k2 , I looked a little bit into your branch. The go-ecoflow lib also has a mqtt-client, so I think it would be much better to use this instead of vibe-coding a new one. Then you can probably reduce the meter a little bit more that could be enough. Keep in mind that the current implementation supports power ocean and stream.

@Metatron2k2
Copy link
Copy Markdown
Author

Hi @Kubiac, thanks for the Input. I'm not sure if using go-ecoflow is panning out here. The MQTT api of go-ecoflow uses the private api, which requires username and password instead of app tokens, so it would it is not compatible with the current auth method on the ecoflow devices. Without analyzing to far, it also seems like the Ocean is not supported by the private api (https://github.com/tolwi/hassio-ecoflow-cloud) .

Also, you also noticed, that the go-ecoflow did not get any commits (besides adding a license file) in the last two years. With your PR over there not being looked at since mid march i would also assume the library has been abandoned.

So all in all, using mqtt with the go-ecoflow library would nee about the same implementation (HMAC, etc.) as what my current client does, with the difference, that it would not use evccs mqtt implementation anymore.

Looking at the Docu of the Ocean devices it should be pretty straight forward to port that to my client, or leave it as is.

Fact is, that currently the Stream devices report very wron gata, whan there are orhter devices on the home elcetrical network and we need to fix it, if we want evcc to report the corect values.

@github-actions github-actions Bot added the stale Outdated and ready to close label Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

devices Specific device support stale Outdated and ready to close

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants