Skip to content

Huawei driver publishes the inverter's invalid-value markers as telemetry (SOC 6553.5 %, power 2.1 GW) and suspends fleet control #494

Description

@sphings79

Reported by @maic-de in #464 (SUN2000-17K-MB0 + LUNA2000, v1.4.0). The code below is from release/v1.5.0, so this is not fixed in 1.5.0.

What happened

At 04:15 the LUNA2000 sat at its discharge cutoff. Shortly after, the integration published a battery power above 2,100,000 W, and by 07:11 a battery SOC above 6,500 %. The Marstek batteries in the same fleet then did not discharge for about two hours and the house imported from the grid instead.

The values are Huawei's "value not available" markers

The live block is decoded as:

# drivers/huawei.py
_BLOCK_LIVE = (37000, 5, "high", {
    "inverter_state":  (0, "u16", 1),
    "battery_power":   (1, "i32", 1),
    "battery_voltage": (3, "u16", 0.1),
    "battery_soc":     (4, "u16", 0.1),
})
  • battery_soc is u16 × 0.1. Huawei's invalid marker for u16 is 0xFFFF = 65535 → 6553.5 %, which matches the reported "more than 6,500 %" exactly.
  • battery_power is i32, where the marker is 0x7FFFFFFF = 2,147,483,647. Consistent with the reported magnitude, though not derivable to the digit from the screenshot.

Why they reach the control layer

The decode loop discards a block that fails to read, which is correct:

# drivers/huawei.py
regs = await self._client.async_read_holding_block(start, count)
if regs is None:
    # A failed block omits its keys rather than publishing zeros; the
    # coordinator treats missing keys as stale, which is correct.
    continue

But a block that reads successfully and contains a marker is decoded and published verbatim. Downstream, the only plausibility guard in infra/coordinator.py is is_untrusted_energy_reading, gated on state_class == "total_increasing". battery_soc and battery_power are measurement, so nothing inspects them. There is no upper bound on SOC between the register and the allocator.

Consequence

A SOC of 6553 % places the battery far above every floor, cutoff and reserve, and a battery power of 2.1 GW corrupts the derived house-consumption balance (battery + grid + solar). The fleet allocator then has no usable picture, which is what left the Marstek batteries idle.

So this is not cosmetic: an invalid reading on one driver suspends control of the whole fleet.

The guard already exists, on the other path

control/pack_soc.py filters per-pack SOC on the Marstek side:

and isinstance(value, (int, float))
and 0 <= value <= 100

with the comment "Values are bounded on read: … an out-of-range reading here would move a charge or discharge limit." That is precisely the failure above, one driver over.

Suggested direction

Treat a sentinel like a failed read — omit the key — so the existing "missing keys are stale" behaviour applies and no new policy is needed:

  • reject 0xFFFF / 0xFFFFFFFF for unsigned and 0x7FFF / 0x7FFFFFFF for signed reads before scaling;
  • additionally bound the physical ranges (battery_soc 0–100 %, power against the inverter's rated maximum), since a marker is not the only way a nonsense value can arrive;
  • log at WARNING when a value is dropped, so the condition is visible without debug logging.

Worth considering whether the bound belongs in the coordinator merge path rather than per driver, so every brand gets it.

Open question (does not block a fix)

The likely trigger is the battery being asleep at its cutoff overnight, with the inverter answering the battery registers with markers while it sleeps. That is a hypothesis. It would determine whether dropping the value is sufficient or whether the driver should also report the battery as temporarily unavailable.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    implementedBug fixed or feature implemented

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions