Model windows, ventilation, wind exposure and recovery
Opening a window adds heat exchange that the envelope model mistook for
poor insulation or a constant cooling bias.
windows.py adds windows as config subentries ("Add window"): a contact
sensor (optionally inverted; unknown/unavailable stays unknown, never
"closed"), its zone, the direction its outside faces (default 345°,
editable) and an opening factor. Each poll computes the share of the
interval each window was open (one change located by its timestamp, a
round trip is unknown), the wind exposure with proper circular angles
(meteorological "from" direction vs the window's facing) and a recovery
weight after closing that decays towards 0, pulling the air back
towards its pre-ventilation temperature. A bounded event log records
opening/closing, duration, temperatures before/lowest/at close/30 min
after, weather and other open windows.
While a zone's window is open, unknown or recovering, the base and
extended models skip it; a separate ventilation learner fits the
closed-window model's residual with natural exchange, wind speed, a
directional term bounded by the wind term (a leeward window still
ventilates), recovery, and optional gust excess and cross-ventilation
terms. Forecasts assume an open window closes after a configurable time
(default 15 min), then recovers. New poll outcome "ventilation".
Weather gains optional wind direction (degrees, 360 -> 0) and wind gust
sources with the same priorities and fallback; knots and ft/s accepted.
On a synthetic week with ventilation events every four hours and a
daily outdoor swing, the configured zone keeps loss/heat/bias close to
the truth and recovers the combined natural+wind effect within 15 %,
recovery within 15 % and direction within 30 %. The same data without
the window configured produces a -0.013 °C/5 min cooling bias and an
11 % inflated boiler effect.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
00e70c612b
commit
ed8f9fe2e2
13 files changed
+1157
-39
No files matched your search
@@ -23,7 +23,9 @@
|
||||
"data": {
|
||||
"outside_temperature_sources": "Outdoor temperature sources (ordered: preferred first)",
|
||||
"outside_wind_sources": "Wind speed sources (optional)",
|
||||
"outside_clouds_sources": "Cloud coverage sources (optional)"
|
||||
"outside_clouds_sources": "Cloud coverage sources (optional)",
|
||||
"outside_wind_direction_sources": "Wind direction sources (optional; degrees the wind comes from)",
|
||||
"outside_wind_gust_sources": "Wind gust sources (optional)"
|
||||
}
|
||||
},
|
||||
"weather_extra": {
|
||||
@@ -68,11 +70,21 @@
|
||||
"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",
|
||||
"internal_heat_half_life_minutes": "Extra electrical heat: forecast half-life (minutes)"
|
||||
"internal_heat_half_life_minutes": "Extra electrical heat: forecast half-life (minutes)",
|
||||
"window_open_minutes": "Open window: assumed remaining open time (minutes)",
|
||||
"recovery_minutes": "Recovery after closing a window (minutes)",
|
||||
"ventilation_wind": "Ventilation: use wind speed and direction",
|
||||
"ventilation_gust": "Ventilation: use wind gusts",
|
||||
"ventilation_cross": "Ventilation: cross-ventilation between zones"
|
||||
},
|
||||
"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.",
|
||||
"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."
|
||||
"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.",
|
||||
"window_open_minutes": "Forecasts assume an open window closes after this many more minutes, then the room recovers.",
|
||||
"recovery_minutes": "Time constant of the air warming back towards the walls' temperature.",
|
||||
"ventilation_wind": "Needs wind sources; direction also needs wind direction sources.",
|
||||
"ventilation_gust": "Adds a term for gusts above the mean wind. Keep it only if Forecast Error improves.",
|
||||
"ventilation_cross": "Adds a term for windows open in several zones at once. Needs windows in two or more zones."
|
||||
}
|
||||
},
|
||||
"weather": {
|
||||
@@ -81,7 +93,9 @@
|
||||
"data": {
|
||||
"outside_temperature_sources": "Outdoor temperature sources (ordered: preferred first)",
|
||||
"outside_wind_sources": "Wind speed sources (optional)",
|
||||
"outside_clouds_sources": "Cloud coverage sources (optional)"
|
||||
"outside_clouds_sources": "Cloud coverage sources (optional)",
|
||||
"outside_wind_direction_sources": "Wind direction sources (optional; degrees the wind comes from)",
|
||||
"outside_wind_gust_sources": "Wind gust sources (optional)"
|
||||
}
|
||||
},
|
||||
"weather_extra": {
|
||||
@@ -109,6 +123,7 @@
|
||||
"first_sample": "First sample",
|
||||
"sensors_changed": "Zone sensors changed",
|
||||
"learned": "Learned",
|
||||
"ventilation": "Window open or recovering",
|
||||
"partly_rejected": "Partly rejected (outlier)",
|
||||
"interval_out_of_range": "Irregular interval",
|
||||
"relay_changed": "Relay switched",
|
||||
@@ -236,6 +251,60 @@
|
||||
"reconfigure_successful": "The heat source was updated.",
|
||||
"no_zones": "Add a zone first."
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"entry_type": "Window",
|
||||
"initiate_flow": {
|
||||
"user": "Add window",
|
||||
"reconfigure": "Edit window"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Window",
|
||||
"description": "A window contact sensor. Open-window periods are kept out of the zone's normal heat-loss learning and modeled as ventilation instead. Unknown or unavailable states count as unknown, never as closed.",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"sensor": "Contact sensor",
|
||||
"zone": "Zone",
|
||||
"inverted": "Sensor reports on when closed",
|
||||
"azimuth": "Facing direction (°)",
|
||||
"opening_factor": "Opening factor",
|
||||
"enabled": "Enabled"
|
||||
},
|
||||
"data_description": {
|
||||
"inverted": "Turn on for sensors whose polarity is the reverse of a standard window sensor.",
|
||||
"azimuth": "Direction the outside of the window faces, clockwise from north (0 = N, 90 = E, 180 = S, 270 = W). Used with the wind direction.",
|
||||
"opening_factor": "Relative ventilation when open: 1.0 for a normal window, lower for tilted, higher for a large or double window."
|
||||
}
|
||||
},
|
||||
"reconfigure": {
|
||||
"title": "Window",
|
||||
"description": "A window contact sensor. Open-window periods are kept out of the zone's normal heat-loss learning and modeled as ventilation instead. Unknown or unavailable states count as unknown, never as closed.",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"sensor": "Contact sensor",
|
||||
"zone": "Zone",
|
||||
"inverted": "Sensor reports on when closed",
|
||||
"azimuth": "Facing direction (°)",
|
||||
"opening_factor": "Opening factor",
|
||||
"enabled": "Enabled"
|
||||
},
|
||||
"data_description": {
|
||||
"inverted": "Turn on for sensors whose polarity is the reverse of a standard window sensor.",
|
||||
"azimuth": "Direction the outside of the window faces, clockwise from north (0 = N, 90 = E, 180 = S, 270 = W). Used with the wind direction.",
|
||||
"opening_factor": "Relative ventilation when open: 1.0 for a normal window, lower for tilted, higher for a large or double window."
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"window_name_required": "Enter a name.",
|
||||
"sensor_in_other_window": "This sensor is already used by another window.",
|
||||
"too_many_windows": "At most 24 windows are supported."
|
||||
},
|
||||
"abort": {
|
||||
"reconfigure_successful": "The window was updated.",
|
||||
"no_zones": "Add a zone first."
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
"data": {
|
||||
"outside_temperature_sources": "Senzori temperatură exterioară (prioritatea: primul selectat)",
|
||||
"outside_wind_sources": "Senzori viteză vânt (opțional)",
|
||||
"outside_clouds_sources": "Senzori nebulozitate (opțional)"
|
||||
"outside_clouds_sources": "Senzori nebulozitate (opțional)",
|
||||
"outside_wind_direction_sources": "Surse pentru direcția vântului (opțional; de unde bate, în grade)",
|
||||
"outside_wind_gust_sources": "Surse pentru rafale de vânt (opțional)"
|
||||
}
|
||||
},
|
||||
"weather_extra": {
|
||||
@@ -68,11 +70,21 @@
|
||||
"room_max_age_minutes": "Vârsta maximă a raportării senzorilor interiori (minute)",
|
||||
"weather_max_age_minutes": "Vârsta maximă a raportării senzorilor meteo (minute)",
|
||||
"room_stale_use_last_value": "Folosește în continuare ultima valoare a unui senzor de cameră care nu mai raportează",
|
||||
"internal_heat_half_life_minutes": "Căldură electrică suplimentară: timp de înjumătățire în prognoză (minute)"
|
||||
"internal_heat_half_life_minutes": "Căldură electrică suplimentară: timp de înjumătățire în prognoză (minute)",
|
||||
"window_open_minutes": "Fereastră deschisă: timp rămas presupus (minute)",
|
||||
"recovery_minutes": "Revenire după închiderea ferestrei (minute)",
|
||||
"ventilation_wind": "Aerisire: folosește viteza și direcția vântului",
|
||||
"ventilation_gust": "Aerisire: folosește rafalele de vânt",
|
||||
"ventilation_cross": "Aerisire: curent între zone"
|
||||
},
|
||||
"data_description": {
|
||||
"room_stale_use_last_value": "Dezactivat: un senzor de cameră mai vechi decât limita de raportare este ignorat, iar ponderile celorlalte camere sunt recalculate. Activat: ultima lui valoare este folosită în continuare, cel mult 24 de ore fără raportare; pentru senzorii care raportează doar la schimbarea valorii. Senzorii de cameră indisponibili sau invalizi sunt mereu ignorați. Nimic nu se oprește decât dacă nicio cameră nu poate fi folosită.",
|
||||
"internal_heat_half_life_minutes": "Prognozele presupun că puterea peste consumul de bază scade cu acest timp de înjumătățire, de exemplu la finalul unei sesiuni de jocuri. 0 presupune că se oprește acum."
|
||||
"internal_heat_half_life_minutes": "Prognozele presupun că puterea peste consumul de bază scade cu acest timp de înjumătățire, de exemplu la finalul unei sesiuni de jocuri. 0 presupune că se oprește acum.",
|
||||
"window_open_minutes": "Prognozele presupun că o fereastră deschisă se închide după atâtea minute, apoi camera revine.",
|
||||
"recovery_minutes": "Constanta de timp a aerului care se încălzește din nou spre temperatura pereților.",
|
||||
"ventilation_wind": "Necesită surse de vânt; direcția necesită și surse pentru direcția vântului.",
|
||||
"ventilation_gust": "Adaugă un termen pentru rafalele peste vântul mediu. Păstrează-l doar dacă eroarea de prognoză scade.",
|
||||
"ventilation_cross": "Adaugă un termen pentru ferestre deschise în mai multe zone deodată. Necesită ferestre în cel puțin două zone."
|
||||
}
|
||||
},
|
||||
"weather": {
|
||||
@@ -81,7 +93,9 @@
|
||||
"data": {
|
||||
"outside_temperature_sources": "Senzori temperatură exterioară (prioritatea: primul selectat)",
|
||||
"outside_wind_sources": "Senzori viteză vânt (opțional)",
|
||||
"outside_clouds_sources": "Senzori nebulozitate (opțional)"
|
||||
"outside_clouds_sources": "Senzori nebulozitate (opțional)",
|
||||
"outside_wind_direction_sources": "Surse pentru direcția vântului (opțional; de unde bate, în grade)",
|
||||
"outside_wind_gust_sources": "Surse pentru rafale de vânt (opțional)"
|
||||
}
|
||||
},
|
||||
"weather_extra": {
|
||||
@@ -109,6 +123,7 @@
|
||||
"first_sample": "Prima măsurătoare",
|
||||
"sensors_changed": "Senzorii zonei s-au schimbat",
|
||||
"learned": "Învățat",
|
||||
"ventilation": "Fereastră deschisă sau revenire după aerisire",
|
||||
"partly_rejected": "Respins parțial (valoare aberantă)",
|
||||
"interval_out_of_range": "Interval neregulat",
|
||||
"relay_changed": "Releul a comutat",
|
||||
@@ -236,6 +251,60 @@
|
||||
"reconfigure_successful": "Sursa de căldură a fost actualizată.",
|
||||
"no_zones": "Adaugă mai întâi o zonă."
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"entry_type": "Fereastră",
|
||||
"initiate_flow": {
|
||||
"user": "Adaugă fereastră",
|
||||
"reconfigure": "Modifică fereastra"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Fereastră",
|
||||
"description": "Un senzor de contact pentru fereastră. Perioadele cu fereastra deschisă nu intră în învățarea pierderilor normale de căldură ale zonei și sunt modelate separat, ca aerisire. Stările necunoscute sau indisponibile sunt tratate ca necunoscute, niciodată ca închise.",
|
||||
"data": {
|
||||
"name": "Nume",
|
||||
"sensor": "Senzor de contact",
|
||||
"zone": "Zonă",
|
||||
"inverted": "Senzorul raportează pornit când e închis",
|
||||
"azimuth": "Orientare (°)",
|
||||
"opening_factor": "Factor de deschidere",
|
||||
"enabled": "Activă"
|
||||
},
|
||||
"data_description": {
|
||||
"inverted": "Activează pentru senzorii cu polaritate inversă față de un senzor de fereastră obișnuit.",
|
||||
"azimuth": "Direcția spre care privește exteriorul ferestrei, în sensul acelor de ceasornic față de nord (0 = N, 90 = E, 180 = S, 270 = V). Se folosește împreună cu direcția vântului.",
|
||||
"opening_factor": "Aerisirea relativă când e deschisă: 1,0 pentru o fereastră obișnuită, mai puțin pentru rabatată, mai mult pentru una mare sau dublă."
|
||||
}
|
||||
},
|
||||
"reconfigure": {
|
||||
"title": "Fereastră",
|
||||
"description": "Un senzor de contact pentru fereastră. Perioadele cu fereastra deschisă nu intră în învățarea pierderilor normale de căldură ale zonei și sunt modelate separat, ca aerisire. Stările necunoscute sau indisponibile sunt tratate ca necunoscute, niciodată ca închise.",
|
||||
"data": {
|
||||
"name": "Nume",
|
||||
"sensor": "Senzor de contact",
|
||||
"zone": "Zonă",
|
||||
"inverted": "Senzorul raportează pornit când e închis",
|
||||
"azimuth": "Orientare (°)",
|
||||
"opening_factor": "Factor de deschidere",
|
||||
"enabled": "Activă"
|
||||
},
|
||||
"data_description": {
|
||||
"inverted": "Activează pentru senzorii cu polaritate inversă față de un senzor de fereastră obișnuit.",
|
||||
"azimuth": "Direcția spre care privește exteriorul ferestrei, în sensul acelor de ceasornic față de nord (0 = N, 90 = E, 180 = S, 270 = V). Se folosește împreună cu direcția vântului.",
|
||||
"opening_factor": "Aerisirea relativă când e deschisă: 1,0 pentru o fereastră obișnuită, mai puțin pentru rabatată, mai mult pentru una mare sau dublă."
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"window_name_required": "Introdu un nume.",
|
||||
"sensor_in_other_window": "Acest senzor este folosit deja de altă fereastră.",
|
||||
"too_many_windows": "Sunt permise cel mult 24 de ferestre."
|
||||
},
|
||||
"abort": {
|
||||
"reconfigure_successful": "Fereastra a fost actualizată.",
|
||||
"no_zones": "Adaugă mai întâi o zonă."
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
|
||||
Reference in new issue
Block a user