Skip to content

Hanchu: battery_rate_max / inverter_limit depend on held power limits, not the unit's rating #5664

Closed

Description

@upton68

Thanks @springfall2008 for the follow-up work and for merging #5305, and @mgazza for the initial component.

ranges_for raises the power ceiling to whatever the device currently holds, and battery_rate_max / inverter_limit are derived from that. On the tested 6.0K this gives the right figures (5000 / 6000) only because its limits happen to be set that way. The result depends on what the limits were last set to, not on the unit:

Situation Result Manual
6.0K with discharge limit set to e.g. 4000 inverter_limit 5000 (menu max) 6000
6.0K with charge limit set to 6000 battery_rate_max 6000 5000
3.0 / 3.68 / 4.0K at defaults inverter_limit 5000 3000 / 3680 / 4000
7 / 8 / 10K holding 5000 or less 5000 for both 7000 / 8000 / 10000

The model is known (e.g. HESS-HY-S-6.0K), so a lookup table from the manuals would be reliable, with held values used only for models not in the table and the user overrides winning as now. Figures from the Hanchu user manuals (full per-model detail in my comment on #5305):

  • Single phase 3.0–6.0K: battery 5000 W for all; AC 3000 / 3680 / 4000 / 5000 / 6000 W.
  • Single phase 7 / 8 / 10K: battery = AC = 7000 / 8000 / 10000 W.
  • Three phase (T-05K…12K, T-4K-M…12K-M, T-10K-M4…20K-M4, CESS 25–50K): battery charge power = rated AC output in every case.

"6 kW" covers three different limits (S-6.0K 5000 W, T-06K 6000 W, T-6K-M 6 kW), so matching needs the full model string. The _warn_if_default_power docstring ("3.68, 5, 6 and 10 kW, so the default is wrong for most") also needs updating: the 5000 W default is correct for the 3.68 and 6 kW battery limit.

Evidence: this is a code reading against the manufacturer's specifications, not a runtime fault on my unit (HESS-HY-S-5.0K, where everything is 5000 W). The manual pages are summarised on #5305; I can attach screenshots of the spec tables if useful.

For information (not separate problems): slots crossing midnight work on my unit for charge, but keeping the split is the safe choice; and DTU_AC_CHG_SOC_LMT is confirmed as the right key for the charge target. My BLE setup uses it daily to stop grid charging, and CHG_BAT_SOC_LMT caps solar too, so never writing it is correct.

Activity

  1. springfall2008 commented on Oct 20, 2026

    @springfall2008
    Owner

    🤖 This is an automated first-pass triage — a maintainer will review before any action is taken.

    This ticket reports several distinct problems in one place, so detailed triage is on hold until it is split — the convention here is one problem per ticket, because each problem gets its own investigation and (if it goes ahead) its own fix PR. Reading your list, the separate problems are:

    • Power limits taken from held values — ranges_for raises the power ceiling to whatever the device currently holds, so battery_rate_max / inverter_limit end up depending on the last-set limits rather than on the unit; a lookup table from the manuals, with held values used only for models not in it, would be reliable. ← trim this ticket down to this one
    • Freeze export will not freeze — on your unit CHG_PWR_LMT only governs charging the battery from the grid, so surplus solar charges the battery regardless and freeze export does nothing; the proposed CHG_BAT_SOC_LMT fallback also couldn't go below 50% SoC.
    • Hold: backstop on grid charging — during a hold, also write DTU_AC_CHG_SOC_LMT at the current SoC (minimum 20) in the same iotSet, so grid charging stops even if the 0 W write did not take.
    • Minor: docs — Predbat overwrites all three charge and all three discharge slots, so anything set in the Hanchu app is cleared when Predbat takes control; worth a line in the docs for users moving from the app.

    Your midnight-slots note and the confirmed charge-target key are information rather than problems, so they don't need tickets of their own; the hold-slot rewrite question fits naturally alongside the hold issue you open for the backstop.

    Please:

    1. Trim this issue down to the power-limits problem above (adjust the title/body to match), and
    2. open one new issue for each of the others — freeze export, the hold backstop, and the docs line — carrying over the relevant section from here.

    In each new issue, please add the evidence that fits that problem: a predbat_debug.yaml and predbat.log captured while the behaviour is happening, both from the Debug panel on Predbat's web interface (the debug file downloads as predbat_debug.yaml.txt and can be attached as-is), or if an incident is over the debug snapshot for that time from the Debug column of the plan's History view — see the debug history docs. For these Hanchu items also useful: any Hanchu-side or component logs of the calls being made, screenshots of the relevant limits in the Hanchu app, your apps.yaml renamed to apps.txt, and when it happened (date, time, timezone).

    Detailed triage of each part will follow once they are separate.

  2. changed the title [-]Hanchu cloud component: follow-up from #5305 (power limits, freeze export, midnight slots, hold)[/-] [+]Hanchu: battery_rate_max / inverter_limit depend on held power limits, not the unit's rating[/+] on Oct 20, 2026
  3. upton68 commented on Oct 21, 2026

    @upton68
    ContributorAuthor

    Evidence from the harness (python3 hanchu.py --dump-menu) on my HESS-HY-S-5.0K. The menu's power limit items list the models they apply to:

    CHG_PWR_LMT    type 1   0-5000   models HESS-HY-S-3.0K,HESS-HY-S-3.68K,HESS-HY-S-4.0K,HESS-HY-S-5.0K,HESS-HY-S-6.0K,HESS-HY-S-7.0K,HESS-HY-S-8.0K,HESS-HY-S-10.0K,HESS-HY-T-05K,HC-HY-SL-2K-G2,HC-HY-SL-2.5K-G2,HC-HY-SL-3K-G2,HC-HY-SL-3.68K-G2,HC-HY-SL-4K-G2,HC-HY-SL-5K-G2,HC-HY-SL-6K-G2  when work_mode=3
    DSCHG_PWR_LMT  type 1   0-5000   (same model list)
    

    So the menu itself gives 7.0K, 8.0K and 10.0K units a 5000 W ceiling, while their manual (HESS-HY-S-7K-10K, section 10.1) gives 7000 / 8000 / 10000 W charge and discharge. Those units will be capped at 5000 W unless they happen to hold a higher value. The other three-phase models (T-06K and up) are not in this item at all, so if their menu matches, they fall back to the 5000 W default.

    Everything else read correctly on my unit: AC coupling detected and pv_power correct, status online, TCT_/TDT_ slots in work mode 3, all ranges from the menu, battery_rate_max and inverter_limit both 5000 W.

  4. springfall2008 commented on Oct 21, 2026

    @springfall2008
    Owner

    🤖 This is an automated follow-up triage review — a maintainer will review before any action is taken.

    What's new since the last review: the ticket is now trimmed to the power-limits problem, with the other parts split out into #5665, #5667 and #5668 — the split is complete, thank you. The new comment also supplied harness evidence (--dump-menu on a HESS-HY-S-5.0K): the menu's CHG_PWR_LMT/DSCHG_PWR_LMT items are 0–5000 W items whose model list covers HESS-HY-S-3.0K–10.0K plus the HC-HY-SL-2K–6K-G2 range. The previous bot comment was a split request, so there was no assessment to revise — this is the first full triage of the trimmed ticket.

    Verified against current main (b743b50 / v9.4.2-13, the commit that merged the component — nothing has changed since):

    • ranges_for (hanchu.py:751-774) applies the menu's bounds over built-in (0, 5000) defaults and raises both power-key ceilings to any higher value the device holds; battery_rate_max reads the charge-power ceiling (hanchu.py:829-840) and inverter_limit the discharge-power ceiling (hanchu.py:842-853). All four rows of the ticket's situation table reproduce from the code.
    • The new menu evidence strengthens the report: the menu's only power item is one 0–5000 W entry shared across models, so 7/8/10K units are capped at 5000 W unless holding a higher value, and models outside the entry's model list fall back to the 5000 W default. That menu behaviour on 7–10K and three-phase units is inferred from one device's menu, not observed on them. Note also _parse_menu_ranges (hanchu.py:663-684) ignores each item's productModel list — last match wins, and one key can appear several times, each for a different model set — so the menu could not discriminate by model today even where it names them.
    • _warn_if_default_power (hanchu.py:776-791) warns only when the power keys are absent entirely — a menu that answers with the shared 5000 W entry never warns, even where 5000 W is wrong for the model. Agreed its docstring is inaccurate as written: per the manuals the 5000 W default matches the battery limit on all single-phase 3.0–6.0K, and is wrong only for their AC ratings and the ≥7 kW models.
    • Not fixed by PR fix(hanchu): address code review findings from #5672 #5674: that PR addresses the distinct ceiling-undo finding (hanchu code review findings #5672 finding 1) by raising the ceiling to the highest value ever seen and caching it. The derivation stays as is, and a wrong high held value would even be locked in — this ticket remains open regardless of fix(hanchu): address code review findings from #5672 #5674.
    • Mitigation exists today: the hanchu_battery_rate_max and hanchu_inverter_limit overrides win over the derived values (hanchu.py:838-839, 851-852).

    Classification changed to bug, priority_low (the split gate had left it unclear with no assessment). The derived limits are the planner's only model of what the hardware can do, and they are wrong for nearly every Hanchu model; but the user overrides mitigate today, the component is a day old and flagged draft/unverified against hardware in its own merge commit, and the consequence is plan-quality bias rather than wrong control or hardware risk — the same treatment as the sibling ticket #5665. I've replaced this bot's own split-gate label unclear with bug + priority_low; I couldn't read the label-event history to double-check who applied unclear (the API call is unavailable in this session), so if that removal is wrong the maintainer can restore it. (There is no Hanchu-specific label in the repo to add.)

    One-line bug statement: battery_rate_max and inverter_limit are derived from a menu range the API shares across models plus whatever the device currently holds, so they don't match the unit's rating for most Hanchu models; a table of manual ratings should take precedence, with held values only for models the table doesn't cover.

    One question that decides the fix's shape: does a device-list entry carry a model string? The component reads only sn/devType from each entry (hanchu.py:498-521), the catalogue deliberately reports no model name (hanchu.py:1086-1088, where the device-list entry "may well have more fields"), and device_detail stores capacity/station/status only (hanchu.py:597-607). With tracing on (the harness default) your run has already printed the raw responses: the fields one device-list entry carries — from the Hanchu API response device_list trace line, with personal details removed — would settle whether the lookup table can key on the model as proposed, or needs the user to declare the model instead. (Screenshots of the manual spec tables aren't needed for this triage; they'd be welcome alongside a fix PR.)

    Applying waiting_for_user for that answer.

  5. upton68 commented on Oct 21, 2026

    @upton68
    ContributorAuthor

    Answering the question: the device-list entry carries no model string ({"devType": "2", "sn": "<serial>"} only), but getDeviceStatus already carries devModel, and the Hanchu web console maps it to a model name. From the console's JavaScript bundle (pcsEnMap):

    1 HESS-HY-S-3.0K      9 HESS-HY-T-10K      17 HESS-HY-T-10K-M4   25 CESS-HY-50K
    2 HESS-HY-S-3.68K    10 HESS-HY-T-12K      18 HESS-HY-T-12K-M4   26 CESS-HY-40K-P
    3 HESS-HY-S-4.0K     11 HESS-HY-T-4K-M     19 HESS-HY-T-15K-M4   27 CESS-HY-50K-P
    4 HESS-HY-S-5.0K     12 HESS-HY-T-5K-M     20 HESS-HY-T-20K-M4   28 HESS-HY-S-6K-S
    5 HESS-HY-S-6.0K     13 HESS-HY-T-6K-M     21 CESS-HY-25K        29 HESS-HY-S-12K-S
    6 HESS-HY-T-05K      14 HESS-HY-T-8K-M     22 CESS-HY-30K        30 HESS-HY-S-7.0K
    7 HESS-HY-T-06K      15 HESS-HY-T-10K-M    23 CESS-HY-36K        31 HESS-HY-S-8.0K
    8 HESS-HY-T-08K      16 HESS-HY-T-12K-M    24 CESS-HY-40K        32 HESS-HY-S-10.0K
    

    Confirmed on my unit: devModel 4, and the console's Inverter Information panel shows "HESS-HY-S-5.0K". (The console's pcs/detail call also returns ratedPower: 5000.0, but that is a /gateway/platform/ endpoint, so devModel from getDeviceStatus looks the better source.)

    With the manuals, that gives a complete table keyed on devModel:

    devModel Battery max (W) AC rating (W)
    1–5 (S-3.0K…6.0K) 5000 3000 / 3680 / 4000 / 5000 / 6000
    6–10 (T-05K…12K) = AC 5000 / 6000 / 8000 / 10000 / 12000
    11–16 (T-4K-M…12K-M) = AC 4000 / 5000 / 6000 / 8000 / 10000 / 12000
    17–20 (T-10K-M4…20K-M4) = AC 10000 / 12000 / 15000 / 20000
    21–27 (CESS 25K…50K-P) = AC 25000 / 30000 / 36000 / 40000 / 50000 / 40000 / 50000
    28, 29 (S-6K-S, S-12K-S) not stated (48 V, current-limited) 6000 / 12000
    30–32 (S-7.0K…10.0K) = AC 7000 / 8000 / 10000

    Notes: codes are not in size order (7/8/10K were added as 30–32), so a lookup is needed rather than an ordinal. The menu also lists HC-HY-SL-*-G2 models, which are not in pcsEnMap (possibly a different brandCode); those, 28/29 and any unknown code would fall back to the current derivation, with the user overrides winning throughout.

  6. added a commit that references this issue on Oct 22, 2026
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

    BOT_TRIAGEDHas been through the triage botbugSomething isn't workingpriority_low

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions