Skip to content

Huawei cutoff entities offer a range the driver rejects: Marstek's 12 % floor hardcoded, charge/discharge windows mismatched #495

Description

@sphings79

Reported by @maic-de in #464. Confirmed on a second installation (SUN2000-8K-MAP0 + LUNA2000). Code below is from release/v1.5.0.

Symptom

The Huawei battery's Discharging cutoff capacity cannot be set below 12 %. Huawei's own minimum is 5 %.

Cause

The entity comes from a class written for Marstek batteries without hardware cutoff registers, and the Huawei driver reuses it:

# number.py
class MarstekSoftSocLimitNumber(CoordinatorEntity, NumberEntity):
    """Software-enforced SOC limit for batteries that don't expose hardware
    cutoff registers (v3/vA/vD)."""
    ...
    else:
        self._attr_translation_key = "discharging_cutoff_capacity"
        self._attr_unique_id = f"{coordinator.device_key}_discharging_cutoff_capacity"
        self._attr_icon = "mdi:battery-arrow-down"
        self._attr_native_min_value = 12
        self._attr_native_max_value = 50

12 % is the Venus D's hardware floor. Nothing about it applies to a LUNA2000.

The mismatch is wider than the reported symptom

The Huawei driver declares its own register ranges, and they barely overlap with what the entity offers:

# drivers/huawei.py
_CHARGE_CUTOFF_RANGE    = (90.0, 100.0)   # register 47081
_DISCHARGE_CUTOFF_RANGE = (0.0, 20.0)     # register 47082
entity offers driver accepts usable
discharge cutoff 12 – 50 % 0 – 20 % 12 – 20 %
charge cutoff 50 – 100 % 90 – 100 % 90 – 100 %

Anything outside the driver's range is not clamped but skipped, and only at debug level:

if not low <= float(soc_pct) <= high:
    _LOGGER.debug(
        "Huawei driver: %s cutoff %.1f%% is outside the register range "
        "%.0f-%.0f%%; leaving the hardware backstop untouched", ...
    )
    return False

So a user who sets the discharge cutoff to 30 %, or the charge cutoff to 80 %, gets an entity that shows the new value while the hardware backstop never moves, with nothing in the log unless debug is on. Skipping rather than clamping is the right call — the comment explains why — but the control that produces the value should not offer a range the write path will reject.

Visible contradiction on the reported symptom

On my installation the entity reads 5.0, taken from the inverter, while its slider minimum is 12 — the displayed value sits below the minimum the control accepts:

number.omnibattery_huawei_luna2000_2_discharging_cutoff_capacity
  state: 5.0
  min: 12   max: 50   step: 1   unit: %

Any user who touches that control raises their floor from 5 % to 12 % and cannot undo it through the UI. On a 10 kWh LUNA2000 that is roughly 0.7 kWh removed from the usable range.

Suggested direction

Take the entity bounds from the driver instead of from constants in number.py. The correct values already exist on the driver side (_CHARGE_CUTOFF_RANGE / _DISCHARGE_CUTOFF_RANGE), and the capability set is the natural place to surface them, so every brand supplies its own window rather than inheriting Marstek's.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions