Skip to content

Port beta's ENTSO-e pricing refinements to main + fix two related bugs #218

Description

@johanzander

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bot-analyzedTriage bot has processed this issueenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions