OPT-11 P1: per-battery mode limits, on/off, solar order
A battery's mode is a soft unit limit (source `mode`, P_MODE 35) kept
apart from the hard limits in pre_resolve(). The allocator applies it
only while the site target is decided at or below 35 (new
Resolution.deciding_priority), so the peak floor releases it and a mode
`hi` never meets the site `lo`. At N=1 resolve() applies the same rule
to the site target after stage 2. A mode range without 0 W is dropped
as a software fault, so REFUSE_SIGN stays unreachable from a mode.
compute() gets the loop bound (sum over live units of hard limits with
modes applied), so the loop never asks for watts no battery may give
and the saturation freeze never parks on them. While charging, Leftover
solar weighs 0 so Solar first fills first. Store keys pack_use and
pack_mode per battery, set by two new /action values; Off stops and
skips maintenance with a log line. Minimal Batteries cards (EN/NL).
Off by default (battery_modes_enabled); off, commands are byte-identical
to a5dddad at N=1 and N=2 (test_modes.py part 1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,10 @@ not options is a Store key, `setup_done`, written when the wizard's save
|
||||
succeeds, so the page stops opening the wizard by itself (uipage.setup_needed).
|
||||
UX-15's setup-only mode serves these routes from `setuponly.SetupOnly`, which
|
||||
commands nothing; there `/action` answers 409.
|
||||
OPT-11 P1 adds two `/action` values, `pack_use` and `pack_mode`, which set one
|
||||
battery's on/off switch or mode in the store (Controller._pack_action). They
|
||||
are the only actions that name a battery; the configuration routes still reach
|
||||
none of them.
|
||||
"""
|
||||
|
||||
import io
|
||||
@@ -53,6 +57,9 @@ from .uimodel import DeriveError, options_to_ui
|
||||
|
||||
_LOG = logging.getLogger("goodwe.web")
|
||||
|
||||
# OPT-11: the per-battery actions, which name a battery and a value.
|
||||
PACK_ACTIONS = ("pack_use", "pack_mode")
|
||||
|
||||
# UX-13 P1: the page lives in app/static/ as three files - index.html,
|
||||
# app.css, app.js - plus the page words from app/ui_text/<lang>.json (English
|
||||
# and, since P5, Dutch; the page picks by the browser's language). They are
|
||||
@@ -146,7 +153,17 @@ def build_app(controller, selfcfg=None) -> web.Application:
|
||||
# UX-15: nothing to start or stop until setup is saved.
|
||||
return web.json_response(
|
||||
{"ok": False, "error": messages.row_setup_only()}, status=409)
|
||||
result = await controller.handle_action(what)
|
||||
if what in PACK_ACTIONS:
|
||||
# OPT-11: one battery's switch or mode. Store state for the named
|
||||
# battery, applied on the next cycle; nothing is written here.
|
||||
result = await controller.handle_action(
|
||||
what, unit=body.get("unit"), value=body.get("value"))
|
||||
if isinstance(result, dict) and "error" in result:
|
||||
return web.json_response({"ok": False,
|
||||
"error": result["error"]},
|
||||
status=400)
|
||||
else:
|
||||
result = await controller.handle_action(what)
|
||||
if result is None:
|
||||
raise web.HTTPBadRequest(text=f"unknown action {what!r}")
|
||||
return web.json_response({"ok": True, "result": result})
|
||||
|
||||
Reference in New Issue
Block a user