Merge pull request #1429 from Hestia-Homes/fix/property-overlay-storage-category

Set electric-storage category (7) on storage property overlays
This commit is contained in:
Jun-te Kim 2026-07-02 12:51:58 +01:00 committed by GitHub
commit e08048244a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 22 additions and 2 deletions

View file

@ -71,6 +71,13 @@ _ASSUMED_DUAL_METER_CODES = OFF_PEAK_IMPLYING_HEATING_CODES | _ROOM_HEATER_CODES
# systems that take a charge control.
_MANUAL_CHARGE_CONTROL = 2401
_STORAGE_HEATER_CODES = frozenset(range(401, 410))
# SAP Table 4a "electric storage heaters" category. Set on a storage-system
# override so the overlaid cert is internally consistent (code 401-409 ↔ category
# 7) — mirroring the control/fuel/meter companions the overlay already drags. A
# storage code with a stale non-storage category is a latent trap: the SAP 10.2
# Table 12a resolver classifies by category, so a leftover room-heater category
# 10 would price the off-peak storage heating at the peak rate.
_STORAGE_HEATER_CATEGORY = 7
# High heat retention storage heaters (SAP Table 4a 409) take a SINGLE intrinsic
# charge control — Table 4e 2404 (HHR, 0 C adjustment). It is named by the
# archetype, not unobserved, so 409 drags 2404 rather than the manual default
@ -266,10 +273,17 @@ def _control_for(code: int) -> Optional[int]:
def _category_for(code: int) -> Optional[int]:
"""The SAP Table 4a heating category a code implies, where the archetype
fixes it. Room heaters are category 10 set so a system-only override reads
as a room-heater system, not whatever category the replaced system carried."""
fixes it. Set so a system-only override reads as the new system's category,
not whatever category the replaced system carried room heaters are 10,
electric storage heaters (incl. HHR 409) are 7, heat pumps 4, heat networks
6. Setting storage 7 keeps the overlaid cert consistent (code 401-409
category 7); without it a storage override left a stale category behind,
which the SAP 10.2 Table 12a resolver (keyed on category) could misread as a
non-storage system and price the off-peak storage heating at the peak rate."""
if code in _CATEGORY_10_ROOM_HEATER_CODES:
return _ROOM_HEATER_CATEGORY
if code in _STORAGE_HEATER_CODES:
return _STORAGE_HEATER_CATEGORY
if code in _HEAT_PUMP_CODES:
return _HEAT_PUMP_CATEGORY
if code in _HEAT_NETWORK_CODES:

View file

@ -229,6 +229,9 @@ def test_high_heat_retention_storage_drags_its_intrinsic_companions() -> None:
assert simulation.heating.main_heating_control == 2404
assert simulation.heating.main_fuel_type == 29
assert simulation.heating.meter_type == "Dual"
# Storage category (7) — consistent with storage code 409, so the SAP 10.2
# Table 12a resolver prices the off-peak storage heating off-peak, not peak.
assert simulation.heating.main_heating_category == 7
@pytest.mark.parametrize(
@ -288,6 +291,9 @@ def test_storage_heaters_drag_along_conservative_manual_charge_control(
assert simulation is not None
assert simulation.heating is not None
assert simulation.heating.main_heating_control == 2401
# ...and the storage category (7), consistent with the storage code, so the
# SAP 10.2 Table 12a resolver prices these off-peak, not at the peak rate.
assert simulation.heating.main_heating_category == 7
def test_direct_acting_electric_does_not_drag_along_a_control() -> None: