Context
Discovered while resetting bess-manager-beta's history to a clean copy of main (all non-experimental beta work had already been merged upstream via #208). Auditing every file that differed between the two surfaced functionality that beta developed for #126 (Belpex/ENTSO-e) but that never made it back to main. None of this is on main today.
1. Missing feature: multiplicative spot-price adjustment
main's entsoe provider (#208) only supports the same additive markup model as Nordpool ((spot + markup) * VAT + fees). Beta built out spot_multiplier / export_spot_multiplier fields to support contracts that scale the raw spot price instead (e.g. Belgian Luminus Dynamic: spot * 1.0175 + fees) * VAT, export spot * 1.018 + compensation). Without this, Luminus-style contract users get systematically wrong prices.
Touches: core/bess/settings.py (PriceSettings.spot_multiplier/export_spot_multiplier), core/bess/price_manager.py (_calculate_buy_price/_calculate_sell_price), core/bess/battery_system_manager.py (wiring), backend/api_dataclasses.py, backend/api.py (_PROVIDER_PRICING_DEFAULTS / _pricing_defaults_for_discovery), frontend/src/components/settings/PricingFormSection.tsx (provider-aware form + help text), frontend/src/pages/SetupWizardPage.tsx, frontend/src/pages/SettingsPage.tsx.
This has already been re-ported onto beta's reset history (bess-manager-beta v9.10.0b1) — that diff is a ready-made reference for porting to main.
2. Bug: setup wizard silently drops spot_multiplier/export_spot_multiplier
Even on beta itself, backend/api.py's setup_complete() uses a hardcoded allow-list (_PRICE_MAP) to decide which payload fields get persisted:
_PRICE_MAP = {
"markupRate": "markup_rate",
"vatMultiplier": "vat_multiplier",
"additionalCosts": "additional_costs",
"taxReduction": "tax_reduction",
}
spotMultiplier/exportSpotMultiplier aren't in this map, so a user completing the initial setup wizard with a non-default multiplier (e.g. the Luminus 1.0175 pre-fill) never gets it saved — it silently reverts to the global default (1.0). The wizard UI shows the right number; it's just never persisted. This is separate from the already-fixed restart-persistence bug (#49 on beta) — that one covered values that were saved correctly but got reset on restart. This one means new installs never save the value in the first place.
The Settings page (PATCH /api/settings) is unaffected — it passes the section through generically rather than through a fixed field list.
Fix: add spotMultiplier/exportSpotMultiplier to _PRICE_MAP (or replace the allow-list with something that can't silently drop new PriceSettings fields — see the TestPriceModelAttrsConsistency contract test pattern already added for the startup-path map, in backend/tests/test_settings_contracts.py on beta).
3. Incomplete: Solcast entity-registry detection never wired up
Beta added entity-registry-based Solcast detection (HomeAssistantAPIController.SOLCAST_SUFFIX_MAP, discover_optional_sensors(states, entity_registry=...)) to fix detection failing on non-English HA installs (substring matching on entity_id breaks when the locale renames things). But the only call site, in backend/api.py's discovery endpoint, never passes the registry through:
optional_sensors = ha.discover_optional_sensors(states) # registry available in scope, just not passed
So this fix is dead code on beta today too — no regression from the beta reset, but also not actually helping any real user yet. Needs the call site fixed (registry is already fetched in the same function for discover_octopus_entities(registry)), then ported to main alongside item 1.
Suggested order
Item 2 and 3 are small, self-contained bug fixes — worth doing first/independently. Item 1 is the larger port and depends on nothing here; could be its own PR.
Related: #126
Context
Discovered while resetting
bess-manager-beta's history to a clean copy ofmain(all non-experimental beta work had already been merged upstream via #208). Auditing every file that differed between the two surfaced functionality that beta developed for #126 (Belpex/ENTSO-e) but that never made it back tomain. None of this is onmaintoday.1. Missing feature: multiplicative spot-price adjustment
main'sentsoeprovider (#208) only supports the same additive markup model as Nordpool ((spot + markup) * VAT + fees). Beta built outspot_multiplier/export_spot_multiplierfields to support contracts that scale the raw spot price instead (e.g. Belgian Luminus Dynamic:spot * 1.0175 + fees) * VAT, exportspot * 1.018 + compensation). Without this, Luminus-style contract users get systematically wrong prices.Touches:
core/bess/settings.py(PriceSettings.spot_multiplier/export_spot_multiplier),core/bess/price_manager.py(_calculate_buy_price/_calculate_sell_price),core/bess/battery_system_manager.py(wiring),backend/api_dataclasses.py,backend/api.py(_PROVIDER_PRICING_DEFAULTS/_pricing_defaults_for_discovery),frontend/src/components/settings/PricingFormSection.tsx(provider-aware form + help text),frontend/src/pages/SetupWizardPage.tsx,frontend/src/pages/SettingsPage.tsx.This has already been re-ported onto beta's reset history (
bess-manager-betav9.10.0b1) — that diff is a ready-made reference for porting tomain.2. Bug: setup wizard silently drops spot_multiplier/export_spot_multiplier
Even on beta itself,
backend/api.py'ssetup_complete()uses a hardcoded allow-list (_PRICE_MAP) to decide which payload fields get persisted:spotMultiplier/exportSpotMultiplieraren't in this map, so a user completing the initial setup wizard with a non-default multiplier (e.g. the Luminus 1.0175 pre-fill) never gets it saved — it silently reverts to the global default (1.0). The wizard UI shows the right number; it's just never persisted. This is separate from the already-fixed restart-persistence bug (#49 on beta) — that one covered values that were saved correctly but got reset on restart. This one means new installs never save the value in the first place.The Settings page (
PATCH /api/settings) is unaffected — it passes the section through generically rather than through a fixed field list.Fix: add
spotMultiplier/exportSpotMultiplierto_PRICE_MAP(or replace the allow-list with something that can't silently drop newPriceSettingsfields — see theTestPriceModelAttrsConsistencycontract test pattern already added for the startup-path map, inbackend/tests/test_settings_contracts.pyon beta).3. Incomplete: Solcast entity-registry detection never wired up
Beta added entity-registry-based Solcast detection (
HomeAssistantAPIController.SOLCAST_SUFFIX_MAP,discover_optional_sensors(states, entity_registry=...)) to fix detection failing on non-English HA installs (substring matching onentity_idbreaks when the locale renames things). But the only call site, inbackend/api.py's discovery endpoint, never passes the registry through:So this fix is dead code on beta today too — no regression from the beta reset, but also not actually helping any real user yet. Needs the call site fixed (registry is already fetched in the same function for
discover_octopus_entities(registry)), then ported tomainalongside item 1.Suggested order
Item 2 and 3 are small, self-contained bug fixes — worth doing first/independently. Item 1 is the larger port and depends on nothing here; could be its own PR.
Related: #126