Model electrical heat sources (UPS, gaming PC) per zone

Electrical equipment heats its room, and a gaming session can add
hundreds of watts, which the 3-parameter model attributed to the boiler
or the bias term.

heat_sources.py adds heat sources as config subentries ("Add heat
source"): an active-power sensor (W/kW; VA and % rejected), the zone,
the share that becomes heat, an automatic baseline (20th percentile of
24 h, so the persistent UPS load is not double counted with the bias
term) or a manual one, an optional "included in" parent (the UPS total
minus a separately measured PC, one level deep) and a first-order
thermal response time. The heat during each interval is the power held
from its start, like the radiator lag. After startup or an outage a
source's response must run two time constants before it counts as
settled.

zone_model.py layers each zone: the base model is kept unchanged as the
fallback and comparison, and an extended FeatureLearner adds "internal"
(kW above baseline after the response) and neighbour coupling for
adjacent zones. It learns only intervals where all its inputs are
known, and is used for forecasts once as trained as the base. The
optimizer accepts a multi-zone step model; forecasts let extra power
fade to baseline with a configurable half-life (default 30 min).

On a synthetic office with thermostat cycling and gaming sessions the
extended model recovers the electrical, boiler and loss coefficients
within 10 %. Status lists each source's power, effective power,
baseline, variable and responding heat; models show the extended
coefficients with confidence. Saved models gain optional zone_models
and heat_sources keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
buzeraandClaude Opus 5.5 committed 2026-10-27 02:39:18 +02:00
1 parent 46587af8a3
commit 00e70c612b
12 files changed
+1083 -15

No files matched your search

+75 -2
View File
@@ -67,10 +67,12 @@
"comfort_band": "Comfort tolerance (°C)",
"room_max_age_minutes": "Maximum room sensor report age (minutes)",
"weather_max_age_minutes": "Maximum weather sensor report age (minutes)",
"room_stale_use_last_value": "Keep using the last value of a room sensor that stopped reporting"
"room_stale_use_last_value": "Keep using the last value of a room sensor that stopped reporting",
"internal_heat_half_life_minutes": "Extra electrical heat: forecast half-life (minutes)"
},
"data_description": {
"room_stale_use_last_value": "Off: a room sensor older than its report-age limit is left out and the other rooms are re-weighted. On: its last value is still used, for up to 24 hours without a report; for sensors that report only when the value changes. Unavailable or invalid room sensors are always left out. Nothing pauses unless no room is usable."
"room_stale_use_last_value": "Off: a room sensor older than its report-age limit is left out and the other rooms are re-weighted. On: its last value is still used, for up to 24 hours without a report; for sensors that report only when the value changes. Unavailable or invalid room sensors are always left out. Nothing pauses unless no room is usable.",
"internal_heat_half_life_minutes": "Forecasts assume heat above the baseline fades with this half-life, for example when a gaming session ends. 0 assumes it stops now."
}
},
"weather": {
@@ -171,6 +173,77 @@
"abort": {
"reconfigure_successful": "The zone was updated."
}
},
"heat_source": {
"entry_type": "Heat source",
"initiate_flow": {
"user": "Add heat source",
"reconfigure": "Edit heat source"
},
"step": {
"user": {
"title": "Electrical heat source",
"description": "Equipment whose electrical power ends up as heat in a zone, such as a UPS with computers. Only the variation above a persistent baseline is modeled: the constant part is already part of the zone's learned behavior. Use active power (W or kW), not VA or load %.",
"data": {
"name": "Name",
"sensor": "Active power sensor",
"zone": "Zone",
"heat_fraction": "Share of power that becomes heat in the zone",
"baseline_mode": "Baseline",
"baseline_w": "Manual baseline (W)",
"included_in": "Already included in the measurement of",
"response_minutes": "Thermal response time (minutes)",
"enabled": "Enabled"
},
"data_description": {
"heat_fraction": "1.0 when the equipment is in this zone. Lower it if part of the heat goes elsewhere, for example out of a vent.",
"baseline_mode": "Automatic uses the low end of the last 24 hours (the persistent load).",
"included_in": "Choose the UPS or meter whose reading already contains this device, so its power is not counted twice.",
"response_minutes": "How quickly the zone feels a change in power: room air, furniture and equipment take time to warm."
}
},
"reconfigure": {
"title": "Electrical heat source",
"description": "Equipment whose electrical power ends up as heat in a zone, such as a UPS with computers. Only the variation above a persistent baseline is modeled: the constant part is already part of the zone's learned behavior. Use active power (W or kW), not VA or load %.",
"data": {
"name": "Name",
"sensor": "Active power sensor",
"zone": "Zone",
"heat_fraction": "Share of power that becomes heat in the zone",
"baseline_mode": "Baseline",
"baseline_w": "Manual baseline (W)",
"included_in": "Already included in the measurement of",
"response_minutes": "Thermal response time (minutes)",
"enabled": "Enabled"
},
"data_description": {
"heat_fraction": "1.0 when the equipment is in this zone. Lower it if part of the heat goes elsewhere, for example out of a vent.",
"baseline_mode": "Automatic uses the low end of the last 24 hours (the persistent load).",
"included_in": "Choose the UPS or meter whose reading already contains this device, so its power is not counted twice.",
"response_minutes": "How quickly the zone feels a change in power: room air, furniture and equipment take time to warm."
}
}
},
"error": {
"source_name_required": "Enter a name.",
"sensor_in_other_source": "This power sensor is already used by another heat source.",
"invalid_parent_source": "The parent must be an existing source that is not itself included in another.",
"source_has_children": "Other sources are included in this one, so it cannot be included in another.",
"baseline_required": "Enter the manual baseline in W.",
"too_many_sources": "At most 16 heat sources are supported."
},
"abort": {
"reconfigure_successful": "The heat source was updated.",
"no_zones": "Add a zone first."
}
}
},
"selector": {
"baseline_mode": {
"options": {
"auto": "Automatic (24-hour low)",
"manual": "Manual value"
}
}
}
}