${esc(zone.name)}
${esc(device?.name || tr('common.noDevice'))} · ${esc(tr('groups.group'))}: ${esc(groupText)}
${esc(zone.name)}
${esc(device?.name || tr('common.noDevice'))} · ${esc(zonePresetLabel(displayPreset))}
diff --git a/Cargo.lock b/Cargo.lock
index 8aead0e..59aa397 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -719,7 +719,7 @@ dependencies = [
[[package]]
name = "gree-controller"
-version = "0.16.4"
+version = "0.16.5"
dependencies = [
"aes",
"aes-gcm",
diff --git a/Cargo.toml b/Cargo.toml
index 521fa3b..079cc09 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "gree-controller"
-version = "0.16.4"
+version = "0.16.5"
edition = "2021"
authors = ["GREE Controller contributors"]
description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support"
diff --git a/FILE_MANIFEST.sha256 b/FILE_MANIFEST.sha256
index 802b50e..4dc4afd 100644
--- a/FILE_MANIFEST.sha256
+++ b/FILE_MANIFEST.sha256
@@ -1,17 +1,17 @@
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
2fd3bf1fb7ef548c7abc9b26d1cc4ef470784b8637af65446b845b27a45b547b ./.gitignore
-147eed97b308e0c27d6a5ee738a0ee710fa6ba2c6bbf7503df0fe484e0c7f842 ./Cargo.lock
-c604af0060aef4e79d2e11fd1e1222a2444cebc835bc50629cff3d2373c5a05a ./Cargo.toml
+795c232a7845a217a5711da4d9547282ba7f42fd160a249df07d631fac83928b ./Cargo.lock
+2c57e2334ace9e7e320c97b14e1b0372c881717dad4bf3e6ef6abae252ed8056 ./Cargo.toml
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
-75a6652611273ae458cb3509401cdb196d1084db1e49496ecdce448f490e6249 ./README.md
-eedafc7faaa9eb95b564a249a4fef82d5f4cc32697eb0e370b96264f3c47b69c ./SOURCE_MANIFEST.sha256
+eb8dd4d456419762d78e8d426326ac33fa99dbf857ab0f34f52dadc30e8a62be ./README.md
+c0f61039799416d2d72d37be85beabd1a68fc3325563189732027df31e7efc2b ./SOURCE_MANIFEST.sha256
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
-bb9840a0dc2b89d057d1461ae581ee61ebf856e3b6d38bc7d1bdd111eef7c550 ./docs/API.md
+e961deaaa82be18f9e2933999dda33f55bfc648200e62043b5e8fffdfdd4464c ./docs/API.md
4f9826f1bf3b42ed2d62d8a8e9221b50175b3a9fdda7e9d219351f7a33039257 ./docs/FLOW.md
-52c8dc48aed987fd28b2b543af1e5a782efb2a44104ccb0fd6b1740255cf384e ./docs/openapi.json
-d8a1b3e34cf8a79ac3296bf600ad47826d639cdef4c1c2c099f8b6b21a391ebd ./flows/simple-heating/igor-simple-heat-v5.flow.json
-bd9a07b8ca4b090ad0089e9037492cc25c47f3456b97ec08152947314ea904a6 ./flows/simple-heating/jan-pieczara-simple-heat-v5.flow.json
-c4e6e3e8df5eb7f9730a79fb90edd155f0510f7bb4862a04b9f35ba649c22376 ./flows/simple-heating/sypialnia-simple-heat-v5.flow.json
+5c24d9f03e5025eda289e6862a7919fe3951992f7120647dc6e0cd0f1f9aa96f ./docs/openapi.json
+a5ad73f7933031dfaa99aa92e27f10223d53fb84b14ee0a378eb5f10eb80e8c8 ./flows/simple-heating/igor-simple-heat-v5.flow.json
+9743f4328a18bc9d89d10a9011d13ca201a4f352db2119335a66a2147a9a486d ./flows/simple-heating/jan-pieczara-simple-heat-v5.flow.json
+2d804831a72577e80dc1ec3cf4f86dc8bcf00b1d8c80929896f11da0230b2b07 ./flows/simple-heating/sypialnia-simple-heat-v5.flow.json
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
@@ -25,7 +25,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./ha-addon/hom
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
-e8d079ebe63cd2916bd219cd45f583940e3d7ba5769af8deb1038415084a1a35 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
+4ecd424fd1969a1bcd81eb85ea5ac50295a0d7691d50013027a7c2e400aec449 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.py
@@ -33,10 +33,10 @@ cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/hom
a745cc458c235158d5d70b6a33d8000fd8b40ff93bfe2f2796af0f4b3a9eef9b ./ha-addon/home-assistant/custom_components/gree_controller/translations/en.json
50b7d6fe6329ad4ea6bee9612ab141c2f73f8382fcc2af0bb076a8e8d4dd4465 ./ha-addon/home-assistant/custom_components/gree_controller/translations/pl.json
c5fc5c87273d82d2834a0a5a365c821a416413ed509613441e24e4dc3507c4d2 ./ha-addon/home-assistant/generated/gree_controller_entities.example.json
-904b21f54c7af0f59d91016d8513baaea5e927a349e18207f4290c51d7f2be8a ./ha-addon/repository/gree-controller/CHANGELOG.md
+a4ae5bfa385365ef5db5332f2635a77e212a82e00a3cf473c5617ad134d3133e ./ha-addon/repository/gree-controller/CHANGELOG.md
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
e3834bf712d4cd2acc715d8c85db96dd6e2fb3fbb758dac537f6034c337b3745 ./ha-addon/repository/gree-controller/README.md
-efe06cb35044b19474ae11e5ef42e3201787ff1d164087cdbdb845a5ad6a16d8 ./ha-addon/repository/gree-controller/config.yaml
+1fa36a0a0a3219604073950cd936c68fa6de1f51c031443ab22bf66596e0733f ./ha-addon/repository/gree-controller/config.yaml
3aae6cdae4c3aaab7786a9575e7093b7e2c5b69288167075f4b7475611691a6b ./ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png
0d0b42a7639b128946eec379175436fa1f150294f39d2dfd169077bfa87fd14d ./ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png
2272740b185e9520e948f74b8f45cf98b8b5fd28c93bdee760da626f91fe5c2a ./ha-addon/repository/gree-controller/translations/en.yaml
@@ -46,8 +46,8 @@ cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run
081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh
f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md
253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md
-4bf92ee5627be67b66d76a6106b224159f95071f0aa2a727cd964e113bc3b32a ./lang/en.json
-1e056cd915ba2e808db1287c99c06bbbbb3c98e5008481471b895e6ec20ea43a ./lang/pl.json
+f3c1286c3758a98b6a2945562753262e9083a6c638a1dcae556c3e978b4a69af ./lang/en.json
+ac6e6285a6153c5d8ce3c45d81ca6fe6c688505957e142e79e0ec0b496c400f3 ./lang/pl.json
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
@@ -103,6 +103,7 @@ e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./scripts/netw
b1e7a7a7a1d6e21012808818ff1478f5116a3dc4c7da3650b57a719ca9df7873 ./scripts/test_flow_block_help_ui.js
e3825cfd9e6e149fd227c521d886dc116170f2553d4d7639d61874267f257b6e ./scripts/test_flow_disable_ui.js
2942b34d82d06c950fbf167442fd1a25185de193055e02f2ce8dff62c2039add ./scripts/test_simple_heating_ui.js
+9bef00c3a0956c5c893eff6cf1fee9681140a1e76da630fa5a347bce19f07981 ./scripts/test_temperature_explanation_ui.js
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
c260a6d291ad913f6aca60f23a0acf6928d68cfb0545b2a698d58c5a95fcf934 ./scripts/verify_simple_heating.py
c32caf3ac7b521324d1d27233c271374ccf4efc3d243544ece727372c792e14a ./src/api.rs
@@ -127,7 +128,7 @@ b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/publ
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
7c974a98c72092886247f67c3a1c62736898d4b279ceb9c4c306690a3c6e4294 ./src/api/system.rs
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
-eaa4070afbc19686d5e9bdb2d24fb2c7fc4bf1f90dc1c4f12fef17017d4cb975 ./src/api/zones.rs
+79076544f4145c0eb120d544e9fab767b5c7ae625c2db007ee8be8a06bb0b85e ./src/api/zones.rs
32fa38d4315682cd3ef3ecdc91e571c2729b5e68b6f2bff965e43de3fdd2aeed ./src/config.rs
de0ce7807aebe4c0e325fbef01855e7fc1cfd117529f53f04a416fda3997673a ./src/db.rs
87f1614a80addde69b619421187526d7f86f893c0ac18998854f11675d6e7cca ./src/db/climate.rs
@@ -146,7 +147,7 @@ f90fbb14f29a20b799b3856bdaa22812a9f0d9dfa46693f1341c160dccda8a97 ./src/db/zone_
e6d6bf39e254bd2779d3bcb31d966aa3f6688ebfa91b4088b70fd796cad66596 ./src/engine/automations.rs
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
-62a15ea757c53796149e670863dae1b4e1a16ceb6ef88ed84ebd3089c0b9880d ./src/engine/control_plan.rs
+c6b9651090e89fe0436826f66ae56825bf1e4b209ef8a27beb22fb1deaaa8c36 ./src/engine/control_plan.rs
7b48a33343d519a2ff2d81e9d757eb56a0c1beacfc0b7b8ef61f49c2ad363f9a ./src/engine/deadlines.rs
519f67ab805f61afea107215f5df50562fa1669a48334cc015570952e25e773a ./src/engine/energy.rs
e350aa63f3bb14ca887eaaa7555edbeae00fb584cbcaf552cc972f0c8918f5d8 ./src/engine/groups.rs
@@ -158,13 +159,13 @@ c73691c3637465ab0f79aecf7f5428d0317799a8ab5329ed564cf57963bbe86e ./src/engine/r
48a496c3935f886a90b90461c38a7aa31ebc40c6482d6feaeb2be47bc2387625 ./src/engine/schedules.rs
9adfcb3c3dffbaa870b314904bd1995e7b78c3f12febd69e23433179e4bf3fba ./src/engine/simple_heat.rs
2694363e607b7f4407ed07c24019c457f0e0d093cb336ee2dc5cf77487c528f6 ./src/engine/targets.rs
-d62ac5e97a36bbdb31bc23871ea7de73d8ac64a1e8203153d9faff0b1a075bc9 ./src/engine/temperature.rs
+b35d7e96e50d40bf26be2e47e1fa1d393f4c07caeac41f3595f5d595579e55f7 ./src/engine/temperature.rs
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
-4160811f47eca6e75970fe6c6b95198f86a4b97b7050531c19a19efb3d76d97d ./src/engine/tests.rs
-07c3c462884344f018654267de3d454d895343b4699e34635e4d1ec88546df0a ./src/engine/zone_actions.rs
-e246b6d6e3126941b989735a27e5fbe88dcaf351420aaaa060bb3761620d8244 ./src/engine/zone_control.rs
+f563f2da2409bf291d67daa1b2b2fd6c7a463a3a11343afb08131d20cd26cd69 ./src/engine/tests.rs
+63ded0ec62833699cbb7fcc6e4189ef19feafc23b9838d8fe9ba98889feb3e67 ./src/engine/zone_actions.rs
+38256ec0e5ee735aa2f7ad7f8daaf2aa96077ea16a6c2cbceac43c4fe2bf8aa5 ./src/engine/zone_control.rs
bfac95cec87f0df6ce562153cd3cfe6731359d703e6c592d2c2f7ba01ea62d99 ./src/error.rs
-d681f2aa53c39f37411834c300e9200299f94ee4ea021f44f70a3ea1774bd698 ./src/home_assistant.rs
+8e181f29119ef6c32ee3821fe220ade97298c105c681c41466af0c16e539b7db ./src/home_assistant.rs
bcd14e4d1af3ca580f3b95bcd86164c40f762511e1b8e2501cb8a598a6d8adf4 ./src/influxdb.rs
da4068295f37c23222bb13bebac022d88ba7729604a49ad25438c55e45458a49 ./src/influxdb/codec.rs
2d36405912646b30b1a1a37a7a4e18c2104ab22e865b593316a609dda884aec2 ./src/influxdb/query.rs
@@ -181,7 +182,7 @@ aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/f
17cac849056b677a3872e600a0d99218fb383ad5ec91cde596562df8e2f47508 ./src/models/runtime.rs
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
-79cb16a9f12e8a20fcf0acc8649d771b8a76a05503c0372443d29003536c1bcb ./src/models/zone.rs
+2b861f3b37274f4d0b9713706ae959220935bdaf63afc513329d1c48ac0f4dd9 ./src/models/zone.rs
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
578b9f39110abbeb98323044b6ae1a941733c82021afeb4d7ff78ad4256b5aa5 ./src/protocol/gree.rs
@@ -207,16 +208,16 @@ f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/
d8a602ed8906d77593bd140315ce1bd56716038e789f53ff31762e9b3cb7220d ./src/state.rs
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
-b030e61b30435495f492e15fa9daf228968cc1de2982e8f40a626452dbf5b81a ./web/css/styles.css
+49d7992d8ebaf0b1eddd6538479b00a750897bc0454f3daa37eb01ed914bea8b ./web/css/styles.css
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
-bbe640e99447f632383b2eb4b52da580ab95d60cd45cda47f6fae1c2380e5b8a ./web/index.html
+bb1229db66ab2b510a44308e21796ba01f54c36238f5785ff7fc998dedd09c18 ./web/index.html
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
-ef20fb1e706105d0afdc2fa505d92ea52e43d93c9a687f2a09d381227a464564 ./web/js-dynamic/dashboard.js
-3a6d2edd64fbdc2a8d468b05816366a9e05df284adea752c42a91e2a3a7bd786 ./web/js-dynamic/entities.js
+1bf5b5e295f685b4c54064eb91220a50cb85264f7e90dfcd5bfe5c5e8ac111a2 ./web/js-dynamic/dashboard.js
+4ddfe365f66b968fae490b19ab321e3497f9fba4e220123edd0a4e0353254d97 ./web/js-dynamic/entities.js
bc1e7c4cccda0447fe805c61e655b773375bf503141195b82d4e95cdf093ec80 ./web/js-dynamic/events.js
3e2a5f1aab56e918624e6e7af064eecb9e94e554fd4d185715f8ecf3747a6895 ./web/js-dynamic/flows.js
1d54ae8919edb61414f9ffc46f85394b29c3c355827120264352cccb32f65e68 ./web/js-dynamic/forms.js
diff --git a/README.md b/README.md
index b9157b4..32ea073 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
Self-hosted controller for GREE-compatible air conditioners with a local Web UI, thermostat zones, schedules, Home Assistant integration, history, notifications and a documented HTTP/WebSocket API.
-**Version: 0.16.4**
+**Version: 0.16.5**
-Version 0.16.4 fixes disabled Flows that could resume execution after an asynchronous condition evaluation, hides disabled Flow outputs from control plans and schedules, resets timing gates on re-enable, and releases stale Flow thermostat intent.
+Version 0.16.5 adds a temperature-origin explanation in the zone/thermostat UI, transparent room-sensor diagnostics, and a fail-safe that pauses automatic heating when HA sensor readings cannot be trusted.
> [Full API reference](docs/API.md) — authentication, every endpoint, request bodies, response models, WebSocket events and examples.
> Built-in API: `/api/*` and `/ws`; interactive Swagger docs: `/api-docs`; OpenAPI JSON: `/api-docs/openapi.json`.
@@ -306,7 +306,7 @@ In standalone mode, the Home Assistant connection test checks only `HA_URL` and
- `home_assistant` — Home Assistant room sensor,
- `combined` — weighted GREE + Home Assistant value.
-For `home_assistant` and `combined`, each zone must set its own `ha_entity_id` room-temperature sensor. Room sensors are independent from the outdoor-temperature source and stale/unavailable room data falls back to the GREE sensor when possible.
+For `home_assistant` and `combined`, each zone must set its own `ha_entity_id` room-temperature sensor. Room sensors are independent from the outdoor-temperature source. Stale/unavailable room data falls back to GREE as a **display reading**, but automatic **heating is paused** rather than started or boosted from that fallback. Already-running automatic heating is powered off when compressor protection permits; cooling, explicit manual operation and GREE-only zones retain their existing behavior. The guard also applies when `combined` readings disagree beyond the configured maximum difference. The UI includes a temperature-origin help tooltip and a sensor-safeguard status. Where Home Assistant supplies `last_reported`, it takes precedence over `last_updated` for sensor freshness, so a stable temperature repeatedly reported by HA is not incorrectly treated as stale.
`HA_OUTDOOR_ENTITY_ID` is the global outdoor-temperature source used by outdoor-temperature assist. A zone may leave `ha_outdoor_entity_id` empty to use that global sensor, or set `ha_outdoor_entity_id` to use a different outdoor sensor only for that zone. If a zone override is unavailable, the controller falls back to the global outdoor source. Global and per-zone outdoor sensors are exposed through the normal alias/history/metric paths and appear in Home Assistant entity suggestions used by Visual Flows.
diff --git a/SOURCE_MANIFEST.sha256 b/SOURCE_MANIFEST.sha256
index 5af6dd6..4c4de39 100644
--- a/SOURCE_MANIFEST.sha256
+++ b/SOURCE_MANIFEST.sha256
@@ -1,16 +1,16 @@
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
2fd3bf1fb7ef548c7abc9b26d1cc4ef470784b8637af65446b845b27a45b547b ./.gitignore
-147eed97b308e0c27d6a5ee738a0ee710fa6ba2c6bbf7503df0fe484e0c7f842 ./Cargo.lock
-c604af0060aef4e79d2e11fd1e1222a2444cebc835bc50629cff3d2373c5a05a ./Cargo.toml
+795c232a7845a217a5711da4d9547282ba7f42fd160a249df07d631fac83928b ./Cargo.lock
+2c57e2334ace9e7e320c97b14e1b0372c881717dad4bf3e6ef6abae252ed8056 ./Cargo.toml
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
-75a6652611273ae458cb3509401cdb196d1084db1e49496ecdce448f490e6249 ./README.md
+eb8dd4d456419762d78e8d426326ac33fa99dbf857ab0f34f52dadc30e8a62be ./README.md
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
-bb9840a0dc2b89d057d1461ae581ee61ebf856e3b6d38bc7d1bdd111eef7c550 ./docs/API.md
+e961deaaa82be18f9e2933999dda33f55bfc648200e62043b5e8fffdfdd4464c ./docs/API.md
4f9826f1bf3b42ed2d62d8a8e9221b50175b3a9fdda7e9d219351f7a33039257 ./docs/FLOW.md
-52c8dc48aed987fd28b2b543af1e5a782efb2a44104ccb0fd6b1740255cf384e ./docs/openapi.json
-d8a1b3e34cf8a79ac3296bf600ad47826d639cdef4c1c2c099f8b6b21a391ebd ./flows/simple-heating/igor-simple-heat-v5.flow.json
-bd9a07b8ca4b090ad0089e9037492cc25c47f3456b97ec08152947314ea904a6 ./flows/simple-heating/jan-pieczara-simple-heat-v5.flow.json
-c4e6e3e8df5eb7f9730a79fb90edd155f0510f7bb4862a04b9f35ba649c22376 ./flows/simple-heating/sypialnia-simple-heat-v5.flow.json
+5c24d9f03e5025eda289e6862a7919fe3951992f7120647dc6e0cd0f1f9aa96f ./docs/openapi.json
+a5ad73f7933031dfaa99aa92e27f10223d53fb84b14ee0a378eb5f10eb80e8c8 ./flows/simple-heating/igor-simple-heat-v5.flow.json
+9743f4328a18bc9d89d10a9011d13ca201a4f352db2119335a66a2147a9a486d ./flows/simple-heating/jan-pieczara-simple-heat-v5.flow.json
+2d804831a72577e80dc1ec3cf4f86dc8bcf00b1d8c80929896f11da0230b2b07 ./flows/simple-heating/sypialnia-simple-heat-v5.flow.json
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
@@ -24,7 +24,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./ha-addon/hom
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
-e8d079ebe63cd2916bd219cd45f583940e3d7ba5769af8deb1038415084a1a35 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
+4ecd424fd1969a1bcd81eb85ea5ac50295a0d7691d50013027a7c2e400aec449 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.py
@@ -32,10 +32,10 @@ cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/hom
a745cc458c235158d5d70b6a33d8000fd8b40ff93bfe2f2796af0f4b3a9eef9b ./ha-addon/home-assistant/custom_components/gree_controller/translations/en.json
50b7d6fe6329ad4ea6bee9612ab141c2f73f8382fcc2af0bb076a8e8d4dd4465 ./ha-addon/home-assistant/custom_components/gree_controller/translations/pl.json
c5fc5c87273d82d2834a0a5a365c821a416413ed509613441e24e4dc3507c4d2 ./ha-addon/home-assistant/generated/gree_controller_entities.example.json
-904b21f54c7af0f59d91016d8513baaea5e927a349e18207f4290c51d7f2be8a ./ha-addon/repository/gree-controller/CHANGELOG.md
+a4ae5bfa385365ef5db5332f2635a77e212a82e00a3cf473c5617ad134d3133e ./ha-addon/repository/gree-controller/CHANGELOG.md
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
e3834bf712d4cd2acc715d8c85db96dd6e2fb3fbb758dac537f6034c337b3745 ./ha-addon/repository/gree-controller/README.md
-efe06cb35044b19474ae11e5ef42e3201787ff1d164087cdbdb845a5ad6a16d8 ./ha-addon/repository/gree-controller/config.yaml
+1fa36a0a0a3219604073950cd936c68fa6de1f51c031443ab22bf66596e0733f ./ha-addon/repository/gree-controller/config.yaml
3aae6cdae4c3aaab7786a9575e7093b7e2c5b69288167075f4b7475611691a6b ./ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png
0d0b42a7639b128946eec379175436fa1f150294f39d2dfd169077bfa87fd14d ./ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png
2272740b185e9520e948f74b8f45cf98b8b5fd28c93bdee760da626f91fe5c2a ./ha-addon/repository/gree-controller/translations/en.yaml
@@ -45,8 +45,8 @@ cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run
081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh
f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md
253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md
-4bf92ee5627be67b66d76a6106b224159f95071f0aa2a727cd964e113bc3b32a ./lang/en.json
-1e056cd915ba2e808db1287c99c06bbbbb3c98e5008481471b895e6ec20ea43a ./lang/pl.json
+f3c1286c3758a98b6a2945562753262e9083a6c638a1dcae556c3e978b4a69af ./lang/en.json
+ac6e6285a6153c5d8ce3c45d81ca6fe6c688505957e142e79e0ec0b496c400f3 ./lang/pl.json
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
@@ -102,6 +102,7 @@ e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./scripts/netw
b1e7a7a7a1d6e21012808818ff1478f5116a3dc4c7da3650b57a719ca9df7873 ./scripts/test_flow_block_help_ui.js
e3825cfd9e6e149fd227c521d886dc116170f2553d4d7639d61874267f257b6e ./scripts/test_flow_disable_ui.js
2942b34d82d06c950fbf167442fd1a25185de193055e02f2ce8dff62c2039add ./scripts/test_simple_heating_ui.js
+9bef00c3a0956c5c893eff6cf1fee9681140a1e76da630fa5a347bce19f07981 ./scripts/test_temperature_explanation_ui.js
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
c260a6d291ad913f6aca60f23a0acf6928d68cfb0545b2a698d58c5a95fcf934 ./scripts/verify_simple_heating.py
c32caf3ac7b521324d1d27233c271374ccf4efc3d243544ece727372c792e14a ./src/api.rs
@@ -126,7 +127,7 @@ b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/publ
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
7c974a98c72092886247f67c3a1c62736898d4b279ceb9c4c306690a3c6e4294 ./src/api/system.rs
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
-eaa4070afbc19686d5e9bdb2d24fb2c7fc4bf1f90dc1c4f12fef17017d4cb975 ./src/api/zones.rs
+79076544f4145c0eb120d544e9fab767b5c7ae625c2db007ee8be8a06bb0b85e ./src/api/zones.rs
32fa38d4315682cd3ef3ecdc91e571c2729b5e68b6f2bff965e43de3fdd2aeed ./src/config.rs
de0ce7807aebe4c0e325fbef01855e7fc1cfd117529f53f04a416fda3997673a ./src/db.rs
87f1614a80addde69b619421187526d7f86f893c0ac18998854f11675d6e7cca ./src/db/climate.rs
@@ -145,7 +146,7 @@ f90fbb14f29a20b799b3856bdaa22812a9f0d9dfa46693f1341c160dccda8a97 ./src/db/zone_
e6d6bf39e254bd2779d3bcb31d966aa3f6688ebfa91b4088b70fd796cad66596 ./src/engine/automations.rs
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
-62a15ea757c53796149e670863dae1b4e1a16ceb6ef88ed84ebd3089c0b9880d ./src/engine/control_plan.rs
+c6b9651090e89fe0436826f66ae56825bf1e4b209ef8a27beb22fb1deaaa8c36 ./src/engine/control_plan.rs
7b48a33343d519a2ff2d81e9d757eb56a0c1beacfc0b7b8ef61f49c2ad363f9a ./src/engine/deadlines.rs
519f67ab805f61afea107215f5df50562fa1669a48334cc015570952e25e773a ./src/engine/energy.rs
e350aa63f3bb14ca887eaaa7555edbeae00fb584cbcaf552cc972f0c8918f5d8 ./src/engine/groups.rs
@@ -157,13 +158,13 @@ c73691c3637465ab0f79aecf7f5428d0317799a8ab5329ed564cf57963bbe86e ./src/engine/r
48a496c3935f886a90b90461c38a7aa31ebc40c6482d6feaeb2be47bc2387625 ./src/engine/schedules.rs
9adfcb3c3dffbaa870b314904bd1995e7b78c3f12febd69e23433179e4bf3fba ./src/engine/simple_heat.rs
2694363e607b7f4407ed07c24019c457f0e0d093cb336ee2dc5cf77487c528f6 ./src/engine/targets.rs
-d62ac5e97a36bbdb31bc23871ea7de73d8ac64a1e8203153d9faff0b1a075bc9 ./src/engine/temperature.rs
+b35d7e96e50d40bf26be2e47e1fa1d393f4c07caeac41f3595f5d595579e55f7 ./src/engine/temperature.rs
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
-4160811f47eca6e75970fe6c6b95198f86a4b97b7050531c19a19efb3d76d97d ./src/engine/tests.rs
-07c3c462884344f018654267de3d454d895343b4699e34635e4d1ec88546df0a ./src/engine/zone_actions.rs
-e246b6d6e3126941b989735a27e5fbe88dcaf351420aaaa060bb3761620d8244 ./src/engine/zone_control.rs
+f563f2da2409bf291d67daa1b2b2fd6c7a463a3a11343afb08131d20cd26cd69 ./src/engine/tests.rs
+63ded0ec62833699cbb7fcc6e4189ef19feafc23b9838d8fe9ba98889feb3e67 ./src/engine/zone_actions.rs
+38256ec0e5ee735aa2f7ad7f8daaf2aa96077ea16a6c2cbceac43c4fe2bf8aa5 ./src/engine/zone_control.rs
bfac95cec87f0df6ce562153cd3cfe6731359d703e6c592d2c2f7ba01ea62d99 ./src/error.rs
-d681f2aa53c39f37411834c300e9200299f94ee4ea021f44f70a3ea1774bd698 ./src/home_assistant.rs
+8e181f29119ef6c32ee3821fe220ade97298c105c681c41466af0c16e539b7db ./src/home_assistant.rs
bcd14e4d1af3ca580f3b95bcd86164c40f762511e1b8e2501cb8a598a6d8adf4 ./src/influxdb.rs
da4068295f37c23222bb13bebac022d88ba7729604a49ad25438c55e45458a49 ./src/influxdb/codec.rs
2d36405912646b30b1a1a37a7a4e18c2104ab22e865b593316a609dda884aec2 ./src/influxdb/query.rs
@@ -180,7 +181,7 @@ aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/f
17cac849056b677a3872e600a0d99218fb383ad5ec91cde596562df8e2f47508 ./src/models/runtime.rs
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
-79cb16a9f12e8a20fcf0acc8649d771b8a76a05503c0372443d29003536c1bcb ./src/models/zone.rs
+2b861f3b37274f4d0b9713706ae959220935bdaf63afc513329d1c48ac0f4dd9 ./src/models/zone.rs
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
578b9f39110abbeb98323044b6ae1a941733c82021afeb4d7ff78ad4256b5aa5 ./src/protocol/gree.rs
@@ -206,16 +207,16 @@ f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/
d8a602ed8906d77593bd140315ce1bd56716038e789f53ff31762e9b3cb7220d ./src/state.rs
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
-b030e61b30435495f492e15fa9daf228968cc1de2982e8f40a626452dbf5b81a ./web/css/styles.css
+49d7992d8ebaf0b1eddd6538479b00a750897bc0454f3daa37eb01ed914bea8b ./web/css/styles.css
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
-bbe640e99447f632383b2eb4b52da580ab95d60cd45cda47f6fae1c2380e5b8a ./web/index.html
+bb1229db66ab2b510a44308e21796ba01f54c36238f5785ff7fc998dedd09c18 ./web/index.html
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
-ef20fb1e706105d0afdc2fa505d92ea52e43d93c9a687f2a09d381227a464564 ./web/js-dynamic/dashboard.js
-3a6d2edd64fbdc2a8d468b05816366a9e05df284adea752c42a91e2a3a7bd786 ./web/js-dynamic/entities.js
+1bf5b5e295f685b4c54064eb91220a50cb85264f7e90dfcd5bfe5c5e8ac111a2 ./web/js-dynamic/dashboard.js
+4ddfe365f66b968fae490b19ab321e3497f9fba4e220123edd0a4e0353254d97 ./web/js-dynamic/entities.js
bc1e7c4cccda0447fe805c61e655b773375bf503141195b82d4e95cdf093ec80 ./web/js-dynamic/events.js
3e2a5f1aab56e918624e6e7af064eecb9e94e554fd4d185715f8ecf3747a6895 ./web/js-dynamic/flows.js
1d54ae8919edb61414f9ffc46f85394b29c3c355827120264352cccb32f65e68 ./web/js-dynamic/forms.js
diff --git a/docs/API.md b/docs/API.md
index 1097d4d..fc9235b 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -606,6 +606,14 @@ Important rules:
The returned `Zone` also contains runtime state including sensor readings, resolved/effective setpoints, demand, current preset, manual overrides, local Quick Thermostat ownership, temporary session state, device-manual takeover, control owner/source/reason, lockout timestamps and `created_at`/`updated_at`.
+Temperature diagnostics in `GET /api/zones` and zone WebSocket updates:
+
+- `control_temperature_source`: `device`, `external`, `combined`, `device_fallback`, `device_discrepancy_fallback`, `unavailable`.
+- `room_sensor_status`: `not_used`, `ok`, `stale`, `error`, `discrepancy` (empty until the first cycle on older persisted zones).
+- `heating_sensor_guard_active`: `true` when the thermostat would otherwise fall back to GREE while an automatic heating zone is configured for a Home Assistant room sensor. Automatic heating is paused, pending starts are cancelled, and existing automatic heat is switched OFF when compressor protection permits. The guard clears automatically with a valid room reading. Explicit manual control and GREE-only heating are unchanged.
+
+For a `combined` sensor strategy, the displayed control temperature is GREE × (1 − `external_sensor_weight`) + room × `external_sensor_weight`, rounded to 0.1°C, unless the difference exceeds `max_sensor_difference`. The Web UI provides this explanation in the zone and thermostat temperature help. Home Assistant sample freshness prefers `last_reported` when supplied and otherwise uses `last_updated`.
+
### `GET /api/zones`
Returns `Zone[]`.
diff --git a/docs/openapi.json b/docs/openapi.json
index ac9c462..8a56b5c 100644
--- a/docs/openapi.json
+++ b/docs/openapi.json
@@ -2,7 +2,7 @@
"openapi": "3.1.0",
"info": {
"title": "GREE Controller API",
- "version": "0.16.4",
+ "version": "0.16.5",
"summary": "Local HTTP/WebSocket API for GREE Controller",
"description": "Local API used by the GREE Controller Web UI and Home Assistant integration.",
"license": {
@@ -6079,7 +6079,7 @@
},
"version": {
"type": "string",
- "example": "0.16.4"
+ "example": "0.16.5"
},
"uptime_seconds": {
"type": "integer",
@@ -7267,6 +7267,19 @@
"null"
]
},
+ "control_temperature_source": {
+ "type": "string",
+ "description": "Temperature source selected this cycle: device, external, combined, device_fallback, device_discrepancy_fallback or unavailable."
+ },
+ "room_sensor_status": {
+ "type": "string",
+ "enum": ["not_used", "ok", "stale", "error", "discrepancy"],
+ "description": "Room sensor diagnostic for the latest zone cycle; empty for legacy zones not yet cycled."
+ },
+ "heating_sensor_guard_active": {
+ "type": "boolean",
+ "description": "True when automatic heating is paused due to an untrusted room sensor; the unit is turned OFF as compressor protection allows."
+ },
"effective_mode": {
"type": "string"
},
diff --git a/ha-addon/home-assistant/custom_components/gree_controller/manifest.json b/ha-addon/home-assistant/custom_components/gree_controller/manifest.json
index 2dd93a0..56baf19 100644
--- a/ha-addon/home-assistant/custom_components/gree_controller/manifest.json
+++ b/ha-addon/home-assistant/custom_components/gree_controller/manifest.json
@@ -1,7 +1,7 @@
{
"domain": "gree_controller",
"name": "GREE Controller",
- "version": "0.16.4",
+ "version": "0.16.5",
"config_flow": true,
"integration_type": "hub",
"iot_class": "local_polling",
diff --git a/ha-addon/repository/gree-controller/CHANGELOG.md b/ha-addon/repository/gree-controller/CHANGELOG.md
index aeff2e6..125a778 100644
--- a/ha-addon/repository/gree-controller/CHANGELOG.md
+++ b/ha-addon/repository/gree-controller/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.16.5
+
+- Shows an accessible "Where does this temperature come from?" explanation on each zone and dashboard thermostat: weighted GREE/room formula, rounded result, source fallback and sensor status.
+- Adds fail-safe automatic-heating control: missing, stale or excessively different HA room readings cannot start or boost heat from the GREE fallback. Existing automatic heating turns OFF once compressor minimum-on protection permits. Manual control, cooling and GREE-only configurations are unchanged.
+- Displays blocked heating in the live thermostat status and "Why the controller works this way" control plan, including logs on guard activation/recovery.
+- Uses Home Assistant `last_reported` for freshness when available (otherwise `last_updated`), to avoid treating repeatedly reported stable readings as stale.
+- Extends the zone API diagnostics and ships Rust/UI regression tests plus the three day/night Flow examples.
+
## 0.16.4
- Fixes a race in stateful Flow checkpoint saving: no stale condition evaluation can re-enable, overwrite or resurrect a disabled/deleted/recompiled automation.
diff --git a/ha-addon/repository/gree-controller/config.yaml b/ha-addon/repository/gree-controller/config.yaml
index c81b1bb..0c956e4 100644
--- a/ha-addon/repository/gree-controller/config.yaml
+++ b/ha-addon/repository/gree-controller/config.yaml
@@ -1,5 +1,5 @@
name: "GREE Controller"
-version: "0.16.4"
+version: "0.16.5"
slug: "gree_controller"
description: "Local GREE HVAC controller with Web UI, API, and Home Assistant integration"
url: "https://git.linuxiarz.pl/gru/gree-controller-ha-addon/"
diff --git a/lang/en.json b/lang/en.json
index 770fb96..2960991 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -132,7 +132,7 @@
"zones.roomSensorEntity": "Room sensor entity_id",
"zones.roomSensorWeight": "Room sensor weight %",
"zones.maxDifference": "Max. sensor difference °C",
- "zones.sensorHelp": "Required when the temperature strategy uses Home Assistant. If it becomes unavailable, the controller falls back to the GREE sensor.",
+ "zones.sensorHelp": "Required when the temperature strategy uses Home Assistant. If readings are missing or disagree, GREE is shown as a fallback but automatic heating is paused. Explicit manual control is unaffected.",
"zones.sensorRequired": "Enter the Home Assistant room temperature entity_id for this zone.",
"zones.outdoorSensorEntity": "Outdoor temperature entity_id override",
"zones.outdoorSensorHelp": "Leave empty to use the global Outdoor temperature entity_id. Set another entity_id to use a different outdoor sensor only for this zone. It is used by outdoor-temperature assist and recorded in history/metrics.",
@@ -145,6 +145,25 @@
"zones.sourceFallback": "GREE fallback",
"zones.sourceDiscrepancy": "GREE fallback: sensor mismatch",
"zones.sourceUnavailable": "unavailable",
+ "zones.temperatureHelp": "Where does this temperature come from?",
+ "zones.temperatureFormula": "Weighted average: GREE {gree} × {greeWeight}% + room {room} × {roomWeight}% = {used} (rounded to 0.1°C).",
+ "zones.temperatureFromRoom": "Control temperature {room} comes from the room sensor. The GREE reading {gree} does not affect it.",
+ "zones.temperatureFromGree": "Control temperature {gree} comes only from GREE.",
+ "zones.temperatureDiscrepancy": "Sensors differ by more than {limit}. GREE {gree}, room {room}; using GREE as fallback.",
+ "zones.temperatureFallback": "{reason} Temperature {gree} comes from the GREE fallback.",
+ "zones.temperatureUnavailable": "No reliable temperature measurement is available.",
+ "zones.sensorStale": "The room sensor reading is stale.",
+ "zones.sensorUnavailable": "The room sensor is unavailable or returned an error.",
+ "zones.sensorStatusLabel": "sensor: {status}",
+ "zones.sensorStatusOk": "OK",
+ "zones.sensorStatusStale": "stale",
+ "zones.sensorStatusError": "unavailable",
+ "zones.sensorStatusDiscrepancy": "readings disagree",
+ "zones.heatGuardActive": "Heating sensor safeguard active.",
+ "zones.heatGuardHint": "Automatic heating is paused until a valid room reading returns.",
+ "zones.heatGuardPendingOff": "Heating is paused; the controller will turn the unit off when compressor protection allows or retry.",
+ "zones.heatGuardExplanation": "The fallback GREE reading is for display only. Automatic control will not start or boost heating from it.",
+ "plan.reasonRoomSensorGuard": "Automatic heating paused: no trustworthy room sensor reading.",
"schedules.calendar": "Calendar",
"schedules.weekdays": "Weekdays",
"schedules.crossMidnight": "Intervals crossing midnight are supported",
diff --git a/lang/pl.json b/lang/pl.json
index be2644d..0d3e442 100644
--- a/lang/pl.json
+++ b/lang/pl.json
@@ -132,7 +132,7 @@
"zones.roomSensorEntity": "entity_id czujnika temperatury pokojowej",
"zones.roomSensorWeight": "Waga czujnika pokojowego %",
"zones.maxDifference": "Maks. różnica czujników °C",
- "zones.sensorHelp": "Wymagany, gdy strategia temperatury używa Home Assistant. Gdy czujnik będzie niedostępny, kontroler wróci do sensora GREE.",
+ "zones.sensorHelp": "Wymagany, gdy strategia temperatury używa Home Assistant. Gdy odczyt jest niedostępny lub rozbieżny, GREE służy jako odczyt awaryjny, ale automatyczne grzanie zostaje wstrzymane. Sterowanie ręczne nie jest blokowane.",
"zones.sensorRequired": "Podaj entity_id czujnika temperatury pokojowej z Home Assistant dla tej strefy.",
"zones.outdoorSensorEntity": "Nadpisanie entity_id temperatury zewnętrznej",
"zones.outdoorSensorHelp": "Pozostaw puste, aby użyć globalnego Outdoor temperature entity_id. Wybierz inne entity_id, aby tylko ta strefa używała innego czujnika zewnętrznego. Czujnik jest używany przez wspomaganie temperaturą zewnętrzną oraz zapisywany w historii i metrykach.",
@@ -145,6 +145,25 @@
"zones.sourceFallback": "fallback GREE",
"zones.sourceDiscrepancy": "fallback GREE: rozbieżność",
"zones.sourceUnavailable": "niedostępna",
+ "zones.temperatureHelp": "Skąd ta temperatura?",
+ "zones.temperatureFormula": "Średnia ważona: GREE {gree} × {greeWeight}% + pokój {room} × {roomWeight}% = {used} (wynik zaokrąglony do 0,1°C).",
+ "zones.temperatureFromRoom": "Temperatura sterująca {room} pochodzi z czujnika pokojowego. Odczyt GREE {gree} nie wpływa na wynik.",
+ "zones.temperatureFromGree": "Temperatura sterująca {gree} pochodzi wyłącznie z GREE.",
+ "zones.temperatureDiscrepancy": "Czujniki różnią się o więcej niż {limit}. GREE {gree}, pokój {room}; awaryjnie wybrano GREE.",
+ "zones.temperatureFallback": "{reason} Temperatura {gree} pochodzi awaryjnie z GREE.",
+ "zones.temperatureUnavailable": "Brak wiarygodnego pomiaru temperatury.",
+ "zones.sensorStale": "Odczyt czujnika pokojowego jest nieaktualny.",
+ "zones.sensorUnavailable": "Czujnik pokojowy jest niedostępny lub zgłosił błąd.",
+ "zones.sensorStatusLabel": "czujnik: {status}",
+ "zones.sensorStatusOk": "OK",
+ "zones.sensorStatusStale": "nieaktualny",
+ "zones.sensorStatusError": "niedostępny",
+ "zones.sensorStatusDiscrepancy": "rozbieżność odczytów",
+ "zones.heatGuardActive": "Zabezpieczenie grzania aktywne.",
+ "zones.heatGuardHint": "Automatyczne grzanie wstrzymane do powrotu prawidłowego odczytu pokojowego.",
+ "zones.heatGuardPendingOff": "Grzanie wstrzymane; sterownik wyłączy jednostkę po spełnieniu ochrony sprężarki lub ponowi próbę.",
+ "zones.heatGuardExplanation": "Odczyt awaryjny GREE służy tylko do prezentacji. Automatyka nie uruchomi ani nie zwiększy grzania na jego podstawie.",
+ "plan.reasonRoomSensorGuard": "Automatyczne grzanie wstrzymane: brak wiarygodnego odczytu czujnika pokojowego.",
"schedules.calendar": "Kalendarz",
"schedules.weekdays": "Dni tygodnia",
"schedules.crossMidnight": "Obsługa przedziałów przez północ",
diff --git a/scripts/test_temperature_explanation_ui.js b/scripts/test_temperature_explanation_ui.js
new file mode 100644
index 0000000..c470e16
--- /dev/null
+++ b/scripts/test_temperature_explanation_ui.js
@@ -0,0 +1,49 @@
+// Focused UI checks for zone temperature origin and sensor-safety indicators.
+// Run from the project root: node scripts/test_temperature_explanation_ui.js
+'use strict';
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const vm = require('node:vm');
+const dict = JSON.parse(fs.readFileSync('lang/pl.json', 'utf8')).translations;
+const ctx = vm.createContext({
+ app: { zones: [], devices: [], groups: [] },
+ tr: (key, vars = {}) => String(dict[key] || key).replace(/\{(\w+)\}/g, (match, name) => vars[name] ?? match),
+ fmtTemp: value => value == null ? '--' : `${Number(value).toFixed(1)}°C`,
+ uiIcon: name => ` This room sensor is required when the temperature strategy
- uses Home Assistant. If it becomes unavailable, the controller falls back to the GREE sensor.
${esc(sensorStatus)}
` : ''}${guarded ? `${esc(guardDetail)}
` : ''}${uiIcon('system-info')}${esc(tr('zones.heatGuardActive'))} ${esc(tr(pending ? 'zones.heatGuardPendingOff' : 'zones.heatGuardHint'))}
`; +} + function zonePresetLabel(preset) { const key = `preset.${preset || 'comfort'}`; return tr(key) === key ? (preset || 'comfort') : tr(key); @@ -471,6 +524,7 @@ function zoneRuntimeStatusLabel(zone, effectiveMode, device = null) { if (zone.device_manual_override === true) return tr('zones.manualDeviceControl'); if (!zone.enabled) return tr('common.disabled'); if (zone.local_thermostat_power === false) return tr('zones.localThermostatOff'); + if (zone.heating_sensor_guard_active === true) return tr('zones.heatGuardActive'); if ((effectiveMode || 'off') === 'off') { if (zone.control_source === 'automation.flow' && zone.simple_heat && zone.control_owner === 'automation') return tr(device?.power ? 'zones.flowDayPendingOff' : 'zones.flowDayOff'); return tr('zones.waiting'); @@ -494,8 +548,9 @@ function zoneCard(zone, detailed = true) { const state = zone.enabled ? tr('common.active') : tr('common.disabled'); const sensorDetails = zone.sensor_source === 'device' ? `${tr('zones.greeTemp')}: ${fmtTemp(zone.device_temperature ?? device?.current_temperature)}` - : `${tr('zones.greeTemp')}: ${fmtTemp(zone.device_temperature)} · ${tr('zones.externalTemp')}: ${fmtTemp(zone.external_temperature)} · ${tr('zones.usedSource')}: ${zoneControlSourceLabel(zone.control_temperature_source)}`; + : `${tr('zones.greeTemp')}: ${fmtTemp(zone.device_temperature)} · ${tr('zones.externalTemp')}: ${fmtTemp(zone.external_temperature)} · ${tr('zones.usedSource')}: ${zoneControlSourceLabel(zone.control_temperature_source)}${zoneRoomSensorStatusText(zone) ? ` · ${zoneRoomSensorStatusText(zone)}` : ''}`; const roomTemperature = zone.current_temperature ?? zone.device_temperature ?? device?.current_temperature; + const temperatureTooltip = zoneTemperatureExplanation(zone); const manual = zone.manual_preset || 'auto'; const mode = zone.inherit_house_mode ? 'house' : zone.mode; const effectiveMode = zone.effective_mode || (zone.inherit_house_mode ? (app.settings?.house_mode || 'off') : zone.mode) || 'off'; @@ -556,7 +611,7 @@ function zoneCard(zone, detailed = true) { return `${esc(device?.name || tr('common.noDevice'))} · ${esc(tr('groups.group'))}: ${esc(groupText)}
${esc(device?.name || tr('common.noDevice'))} · ${esc(zonePresetLabel(displayPreset))}