MAINT-20 part 1 review: restore three lost lines, rewrap ragged comments
Comment, docstring and DECISIONS only (same-code script OK; python -OO bytecode now identical for all six files). - peakguard.charge_room_w: one-line docstring + SAFETY-125 pointer (cut whole). - peakguard.shaping_suspended: keep 'the one place this conjunction is written; do not inline it'. - price_forecast module note: an hour with no price and no yesterday is left out, never filled (test_price.py 14d cites the module note for it). - Refill comment blocks the cut left ragged; DECISIONS pointers on one line. - FCST-04 note: _yesterday_of(), not _previous_day(); FCST-03 note: the 49 stronger cases are why the rule is a real change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6e2fde63ff
commit
63dda4b0d3
@@ -40,8 +40,8 @@ from .custom_formula import (
|
||||
_LOG = logging.getLogger("goodwe.price")
|
||||
|
||||
|
||||
# Brussels wall time, without a tz database (DECISIONS/TARIFF-02-price-
|
||||
# provider-plugins.md).
|
||||
# Brussels wall time, without a tz database.
|
||||
# DECISIONS/TARIFF-02-price-provider-plugins.md.
|
||||
|
||||
CET = timezone(timedelta(hours=1))
|
||||
CEST = timezone(timedelta(hours=2))
|
||||
@@ -176,9 +176,8 @@ class PriceProduct(Enum):
|
||||
|
||||
|
||||
# specs.md 8.3: real-time / imbalance products are high-volatility and widen
|
||||
# the
|
||||
# arbitrage thresholds. The ONLY place that says which products are volatile.
|
||||
# OPT-04 reads it; nothing checks plugin ids.
|
||||
# the arbitrage thresholds. The ONLY place that says which products are
|
||||
# volatile. OPT-04 reads it; nothing checks plugin ids.
|
||||
_WIDE = frozenset({PriceProduct.REALTIME})
|
||||
|
||||
|
||||
@@ -194,8 +193,8 @@ def volatility_regime(product: PriceProduct) -> str:
|
||||
# FCST-03: ONE widening path, driven by a SET of active uncertainty sources
|
||||
# (real-time product 8.3, no weather data 11.4). The combination rule is `max`,
|
||||
# not a product. The factors are policy defaults, not derived, and they differ
|
||||
# so the combination rules stay distinguishable (DECISIONS/TARIFF-02-price-
|
||||
# provider-plugins.md).
|
||||
# so the combination rules stay distinguishable.
|
||||
# DECISIONS/TARIFF-02-price-provider-plugins.md.
|
||||
class Uncertainty(Enum):
|
||||
"""Why an arbitrage threshold is currently wider than the tariff alone says."""
|
||||
REALTIME_PRODUCT = "realtime_product" # §9.2 row 2, §8.3
|
||||
@@ -460,9 +459,9 @@ def plan_is_provisional(read: PriceRead) -> bool:
|
||||
# Each row is one line of the Eneco residential card for Flanders, September
|
||||
# 2026, including 6 % VAT, in EUR/kWh. The default of `price_taxes_eur_kwh` is
|
||||
# their sum (test_price.py section 1b re-adds them). Per DSO, and set near the
|
||||
# low
|
||||
# end of the band on purpose; `energiebijdrage` is a zero row since 2026-08-01.
|
||||
# Why: DECISIONS/TARIFF-02-price-provider-plugins.md, "Taxes components".
|
||||
# low end of the band on purpose; `energiebijdrage` is a zero row since
|
||||
# 2026-08-01. Why: DECISIONS/TARIFF-02-price-provider-plugins.md, "Taxes
|
||||
# components".
|
||||
FLEMISH_TAXES_COMPONENTS_EUR_KWH = (
|
||||
("Fluvius distribution, Antwerpen", 0.0535),
|
||||
("Bijzondere accijns, 'Alle verbruik' - no volume bands", 0.04876),
|
||||
@@ -472,17 +471,14 @@ FLEMISH_TAXES_COMPONENTS_EUR_KWH = (
|
||||
|
||||
# TARIFF-18: moving this default changes which configs start. validate.py
|
||||
# (TARIFF-17 rung) compares `price_taxes_eur_kwh` to the default, so a
|
||||
# superseded
|
||||
# default (0.06) must still read as untouched. Do not change a default without
|
||||
# that check. DECISIONS/TARIFF-02-price-provider-plugins.md.
|
||||
# superseded default (0.06) must still read as untouched. Do not change a
|
||||
# default without that check. DECISIONS/TARIFF-02-price-provider-plugins.md.
|
||||
SUPERSEDED_TAXES_DEFAULTS_EUR_KWH = (0.06,) # TARIFF-02 .. TARIFF-17
|
||||
|
||||
|
||||
# CFG-03. The one in-code mirror of config.yaml for the price options:
|
||||
# validate.py
|
||||
# imports it, main.py has no price `.get()` fallbacks, and test_price.py
|
||||
# section 1
|
||||
# pins it to config.yaml key for key.
|
||||
# validate.py imports it, main.py has no price `.get()` fallbacks, and
|
||||
# test_price.py section 1 pins it to config.yaml key for key.
|
||||
PRICE_OPTION_DEFAULTS = {
|
||||
"price_provider": "off",
|
||||
"price_markup_eur_kwh": 0.03, # §14 tariffs.energy_price.markup_eur_kwh
|
||||
@@ -498,14 +494,13 @@ PRICE_OPTION_DEFAULTS = {
|
||||
"price_horizon_h": 48,
|
||||
"price_timeout_s": 10,
|
||||
# TARIFF-03. A JSON static day/night or fixed tariff; "" is unset. Parsed
|
||||
# by
|
||||
# app/static_tariff.py, refused at startup by validate.py. Here, not in
|
||||
# by app/static_tariff.py, refused at startup by validate.py. Here, not in
|
||||
# static_tariff.py, so there is one defaults mirror (CFG-03).
|
||||
"static_profile": "",
|
||||
# TARIFF-17. A period table in `static_profile`'s grammar whose
|
||||
# `eur_per_kwh`
|
||||
# is the TAXES component per band; "" is unset and the scalar decides. In
|
||||
# FRAMEWORK_KEYS, so a plugin cannot add taxes a second time per hour.
|
||||
# `eur_per_kwh` is the TAXES component per band; "" is unset and the scalar
|
||||
# decides. In FRAMEWORK_KEYS, so a plugin cannot add taxes a second time
|
||||
# per hour.
|
||||
"price_taxes_profile": "",
|
||||
# TARIFF-15. One contract's two coefficients, units, VAT rate and the card
|
||||
# figures they are graded against; "" is unset. Parsed by
|
||||
@@ -513,15 +508,13 @@ PRICE_OPTION_DEFAULTS = {
|
||||
"price_formula": "",
|
||||
# TARIFF-16. The INJECTION leg in `price_formula`'s own grammar, read by
|
||||
# `custom_formula.parse_export_formula()`; "" is unset and
|
||||
# `export_price_eur_kwh`
|
||||
# decides. In FRAMEWORK_KEYS, so a consumption plugin cannot fold it in.
|
||||
# `export_price_eur_kwh` decides. In FRAMEWORK_KEYS, so a consumption
|
||||
# plugin cannot fold it in.
|
||||
"price_export_formula": "",
|
||||
# OPT-04, 10.1 priority 3, and `optimizer.cycle_cost_threshold_eur`. The
|
||||
# two
|
||||
# SoC keys are not the maintenance keys. 0.10 is the 10.1 name, not a value
|
||||
# that
|
||||
# makes anything trade (static spread is about 0.08): an owner decision.
|
||||
# DECISIONS/TARIFF-02-price-provider-plugins.md.
|
||||
# two SoC keys are not the maintenance keys. 0.10 is the 10.1 name, not a
|
||||
# value that makes anything trade (static spread is about 0.08): an owner
|
||||
# decision. DECISIONS/TARIFF-02-price-provider-plugins.md.
|
||||
"arbitrage_enabled": False,
|
||||
"cycle_cost_threshold_eur": 0.10,
|
||||
"arbitrage_soc_floor": 20,
|
||||
@@ -658,8 +651,8 @@ class PriceProvider(ABC):
|
||||
id = "?"
|
||||
|
||||
# PRICE-01. Ids to try in order when this source cannot answer, and whether
|
||||
# the
|
||||
# last answer is kept for a day. Empty / False for every earlier plugin.
|
||||
# the last answer is kept for a day. Empty / False for every earlier
|
||||
# plugin.
|
||||
FALLBACKS: tuple = ()
|
||||
KEEPS_LAST_GOOD: bool = False
|
||||
|
||||
@@ -688,8 +681,7 @@ class PriceProvider(ABC):
|
||||
|
||||
# Names a plugin's own CODE may not mention: FRAMEWORK_KEYS plus `_o`,
|
||||
# `price_option` and `PRICE_OPTION_DEFAULTS`, the other routes PR #16 review
|
||||
# used
|
||||
# to double-count.
|
||||
# used to double-count.
|
||||
BANNED_IN_PLUGIN_CODE = tuple(sorted(FRAMEWORK_KEYS)) + (
|
||||
"PRICE_OPTION_DEFAULTS", "price_option", "_o")
|
||||
|
||||
@@ -717,8 +709,7 @@ def code_leaks(cls) -> list:
|
||||
# TARIFF-07: the ids that USED to resolve here. A retired id is not a typo and
|
||||
# has no silent alias (it published retail prices, `generic_dayahead` reads
|
||||
# wholesale). Each value is the id-specific half of the refusal; validate.py
|
||||
# owns
|
||||
# the framing. DECISIONS/TARIFF-07-withdrawing-three-plugins.md.
|
||||
# owns the framing. DECISIONS/TARIFF-07-withdrawing-three-plugins.md.
|
||||
RETIRED_PROVIDERS: dict = {
|
||||
"engie_dynamic": (
|
||||
"its endpoint https://api.engie.be/dynamic/be does not exist - the "
|
||||
@@ -751,8 +742,7 @@ def register(cls):
|
||||
raise ValueError(f"duplicate provider id {cls.id!r}")
|
||||
if cls.id in RETIRED_PROVIDERS:
|
||||
# At import: re-registering a retired id silences validate.py's
|
||||
# refusal. Take
|
||||
# the id out of RETIRED_PROVIDERS in the same commit.
|
||||
# refusal. Take the id out of RETIRED_PROVIDERS in the same commit.
|
||||
raise ValueError(
|
||||
f"{cls.id!r} is in RETIRED_PROVIDERS and cannot also be a shipped "
|
||||
f"plugin. A site configured on it is refused at startup with a "
|
||||
@@ -920,10 +910,8 @@ def check_egress(url: str, caps: ProviderCapabilities) -> str:
|
||||
|
||||
# TARIFF-08: the COLUMNAR document the public EPEX feed publishes (parallel
|
||||
# `unix_seconds` / `price` arrays plus `unit`). The only parser; the row parser
|
||||
# it
|
||||
# replaced read a shape no feed published. Two shapes, two functions, no
|
||||
# sniffing.
|
||||
# DECISIONS/TARIFF-02-price-provider-plugins.md.
|
||||
# it replaced read a shape no feed published. Two shapes, two functions, no
|
||||
# sniffing. DECISIONS/TARIFF-02-price-provider-plugins.md.
|
||||
|
||||
COLUMNAR_TIME_KEY = "unix_seconds"
|
||||
COLUMNAR_PRICE_KEY = "price"
|
||||
@@ -1247,8 +1235,7 @@ class CustomFormula(PriceProvider):
|
||||
body = await transport(url, float(opts.get("price_timeout_s")))
|
||||
# `scale=1.0`, so these RawPoints carry EUR/**MWh** in a field spelled
|
||||
# `eur_per_kwh`. Contained: destructured on the next line and never
|
||||
# leaves this
|
||||
# method (`_validate_raw()` would refuse an escape).
|
||||
# leaves this method (`_validate_raw()` would refuse an escape).
|
||||
index = _points_from_columnar_json(body, expect_unit="EUR / MWh",
|
||||
scale=1.0,
|
||||
provisional_default=False)
|
||||
|
||||
Reference in New Issue
Block a user