This commit is contained in:
Mateusz Gruszczyński
2026-09-21 23:21:10 +02:00
parent d80005de33
commit 16001d1a55
17 changed files with 93 additions and 60 deletions
Generated
+1 -1
View File
@@ -719,7 +719,7 @@ dependencies = [
[[package]]
name = "gree-controller"
version = "0.15.15"
version = "0.15.16"
dependencies = [
"aes",
"aes-gcm",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "gree-controller"
version = "0.15.15"
version = "0.15.16"
edition = "2021"
authors = ["GREE Controller contributors"]
description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support"
+16 -16
View File
@@ -1,14 +1,14 @@
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
bf494c24be8032cab82e54d9aaebdda6e7544eb5ebef4b2a3e700808636073e7 ./Cargo.lock
34a15c5dbb3a739480b32f40ba51d19ea51db19fd39474df560e35ea9f0ba0e3 ./Cargo.toml
fca6a72e470af800cf39736e8f6e2d603597d1bd0de822da4990532db0ee5da6 ./Cargo.lock
3dc499ec4f51819e80dacf089d8b3a0e0082ad147c2213fe06f51bec5bf68bf0 ./Cargo.toml
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
41bb456a4b4e71298a1859994562e0f582d056b2cfdbb54c59f5b4dfaab6d2f0 ./README.md
bbce332922c081118cf6cace226fb5ae1573139604a8bb9bc27b7ef1c8c04541 ./SOURCE_MANIFEST.sha256
f1032a7aedb9cdc5f2f413b7141926cf6fb2f31af866269e94298df6d7aa8b5d ./README.md
7505d9eb1c7dc8121c5b88f2218d183a36d75e52e551584337592f6cd68a9079 ./SOURCE_MANIFEST.sha256
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
323abb42b1c612d23826e5c01ab89aa1d757490d32e4423430626acb94ae68c9 ./docs/API.md
1be10be5b9a7b3d486a37a21aba15b83a4b26a3107ca3e7cfc7b6d71c6653e73 ./docs/API.md
43af7941ec4d427f9db7ab3cc6baf3d182a72dba7a6791f14094fee138324d8d ./docs/FLOW.md
7abd620b69af9fe82dcf7e1c91a1c27170e00003a5ca26e3089bffca3ed38c96 ./docs/openapi.json
3f0db3f25b597a429d2d49f78685ff57522d58b7cf6d8ecc87a5351a8a4e36d9 ./docs/openapi.json
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
@@ -22,7 +22,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
dc29a75dcc3ce0f3f941f651f77271acdc5e49540baec5050e6c8e294f95da74 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
3d26392fce8e08c2bfc8b88533252fe1defbc976825f986185add3ec6449501b ./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
@@ -30,10 +30,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
e4ce58fc6b0f5f1a1fba802097badc8dad6c357ca2bd5a25ab9b698995ebc5c1 ./ha-addon/repository/gree-controller/CHANGELOG.md
3151dcf9ded40043bf4fb1aa24ad54ce96fc2d728f6b511f65cb1c251bc3e191 ./ha-addon/repository/gree-controller/CHANGELOG.md
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
fc3151e76c08d176bdf695dd5b2537a79d83a768633282e4c72a3c86bb841659 ./ha-addon/repository/gree-controller/README.md
9622d558c6fa9c855cc3bc672980bb5ff8726ac81d622f0ede371580156305c6 ./ha-addon/repository/gree-controller/config.yaml
7d23a1c6af3b95cbaec4e6fb845e4e11d4f75feb1cebff4b2aa009c83da1ac0b ./ha-addon/repository/gree-controller/README.md
87f8cbd24c4588ebd49cab9f46f634ada0a39f7906c776bc3f3b60a12a1cc453 ./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
@@ -43,8 +43,8 @@ cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run
081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh
f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md
253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md
f5251fbd8355f560df97bc2042e05e045f79bffc6b6f18bf84999c16fc4c64df ./lang/en.json
c1c98e668c6b2e615e882c9079b6f198e0360c188264d0083c21d7b332b4cbc9 ./lang/pl.json
0f293c649e47bb102dd2e347e8d73e0aabb3aa75ea900e99adae15e8594fc1c2 ./lang/en.json
b083b31279487ed750a964b97afbb054205a31d5c84818583c938bb3d7fd13c9 ./lang/pl.json
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
@@ -85,7 +85,7 @@ fdcd9a5055d08037278b842e7ab69265345c5811f0a06867136c511d140bb191 ./presets/wind
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
a453bb9c8529c0c37d7004765aef8a47ccc7a5e92080802435b7b1db3a695464 ./scripts/api_dev_test.py
beafb364bff332c40c1039fe09279517609b6b2482315917a1f4e47b6aaad04c ./scripts/api_dev_test.py
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
@@ -199,7 +199,7 @@ f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/
d8a602ed8906d77593bd140315ce1bd56716038e789f53ff31762e9b3cb7220d ./src/state.rs
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
fd56aa620ddd63bf7f8d03f00da3b2bd53993240bb37cebdac1bdffec22d867c ./web/css/styles.css
1d6fa9e291ebbae40a7a8b06a7cef9dbd0ee06d88ec6b664361c37cb6c1aa281 ./web/css/styles.css
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
43f33fc21bb11df0ea149fe7fbca722a57f93890e4d9bb542ced703c1311207e ./web/index.html
@@ -207,7 +207,7 @@ e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
1b444068bf91741c54bd51741a95128e335219275c415fe9ce4531c80593b50f ./web/js-dynamic/dashboard.js
652cb663d40d51b57102b0403ee2bc462af290ff9f2a03f142c65b251acb2db8 ./web/js-dynamic/dashboard.js
930e67432f55e8476513e808edfa9a0809a8da7479307284fcc136082a4129a4 ./web/js-dynamic/entities.js
4612c958d7b9ccf89d6d45a11c3bc7e864bbd2ceaccc16fd7d76811ba4c74196 ./web/js-dynamic/events.js
43c78a3871cfdb0382081ec30b8d48650450c71b44d006414b161349ac1fa659 ./web/js-dynamic/flows.js
@@ -215,7 +215,7 @@ e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynam
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
30cbaebb817d5d416e1a372595b647aae42c14b9cc406c4751d0ced563243b69 ./web/js-dynamic/navigation.js
0c93119b3d5985d3440e1525d908efecd1567ab2caa0e6cc3babeecd87bfde5c ./web/js-dynamic/realtime.js
3ff9c027beb8fb033330bf3cfb3496e06fb38c42041f1ffe522c6c7af18e9e30 ./web/js-dynamic/realtime.js
1b667f0a8bce824b27f56a8d2f44ff8feef9a011289591dc629ee288db70d137 ./web/js-dynamic/router.js
c2710fd9c1f2b31878c1e25ceb02d74ae30456c1bbb400e69fcf7619ecc72bc2 ./web/js-dynamic/select-ui.js
bc42b4a0dd0d20239ce22ea1dff09b3f94e8c0e1395e01251a4585a051968a8f ./web/js-dynamic/settings-ui.js
+2 -2
View File
@@ -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.15.15**
**Version: 0.15.16**
Version 0.15.15 adds a persistent emergency STOP in Global power and extends Control plan with per-zone control reasons plus recent automation trigger context, making it clear why the controller is acting or paused.
Version 0.15.16 makes the Global power emergency STOP context-aware: it stays green while STOP is active, is prominent when units or automatic control are active, becomes subdued while automation is armed but idle, and disappears only when no automation is armed and every unit is off.
> [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`.
+15 -15
View File
@@ -1,13 +1,13 @@
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
bf494c24be8032cab82e54d9aaebdda6e7544eb5ebef4b2a3e700808636073e7 ./Cargo.lock
34a15c5dbb3a739480b32f40ba51d19ea51db19fd39474df560e35ea9f0ba0e3 ./Cargo.toml
fca6a72e470af800cf39736e8f6e2d603597d1bd0de822da4990532db0ee5da6 ./Cargo.lock
3dc499ec4f51819e80dacf089d8b3a0e0082ad147c2213fe06f51bec5bf68bf0 ./Cargo.toml
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
41bb456a4b4e71298a1859994562e0f582d056b2cfdbb54c59f5b4dfaab6d2f0 ./README.md
f1032a7aedb9cdc5f2f413b7141926cf6fb2f31af866269e94298df6d7aa8b5d ./README.md
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
323abb42b1c612d23826e5c01ab89aa1d757490d32e4423430626acb94ae68c9 ./docs/API.md
1be10be5b9a7b3d486a37a21aba15b83a4b26a3107ca3e7cfc7b6d71c6653e73 ./docs/API.md
43af7941ec4d427f9db7ab3cc6baf3d182a72dba7a6791f14094fee138324d8d ./docs/FLOW.md
7abd620b69af9fe82dcf7e1c91a1c27170e00003a5ca26e3089bffca3ed38c96 ./docs/openapi.json
3f0db3f25b597a429d2d49f78685ff57522d58b7cf6d8ecc87a5351a8a4e36d9 ./docs/openapi.json
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
@@ -21,7 +21,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
dc29a75dcc3ce0f3f941f651f77271acdc5e49540baec5050e6c8e294f95da74 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
3d26392fce8e08c2bfc8b88533252fe1defbc976825f986185add3ec6449501b ./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
@@ -29,10 +29,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
e4ce58fc6b0f5f1a1fba802097badc8dad6c357ca2bd5a25ab9b698995ebc5c1 ./ha-addon/repository/gree-controller/CHANGELOG.md
3151dcf9ded40043bf4fb1aa24ad54ce96fc2d728f6b511f65cb1c251bc3e191 ./ha-addon/repository/gree-controller/CHANGELOG.md
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
fc3151e76c08d176bdf695dd5b2537a79d83a768633282e4c72a3c86bb841659 ./ha-addon/repository/gree-controller/README.md
9622d558c6fa9c855cc3bc672980bb5ff8726ac81d622f0ede371580156305c6 ./ha-addon/repository/gree-controller/config.yaml
7d23a1c6af3b95cbaec4e6fb845e4e11d4f75feb1cebff4b2aa009c83da1ac0b ./ha-addon/repository/gree-controller/README.md
87f8cbd24c4588ebd49cab9f46f634ada0a39f7906c776bc3f3b60a12a1cc453 ./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
@@ -42,8 +42,8 @@ cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run
081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh
f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md
253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md
f5251fbd8355f560df97bc2042e05e045f79bffc6b6f18bf84999c16fc4c64df ./lang/en.json
c1c98e668c6b2e615e882c9079b6f198e0360c188264d0083c21d7b332b4cbc9 ./lang/pl.json
0f293c649e47bb102dd2e347e8d73e0aabb3aa75ea900e99adae15e8594fc1c2 ./lang/en.json
b083b31279487ed750a964b97afbb054205a31d5c84818583c938bb3d7fd13c9 ./lang/pl.json
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
@@ -84,7 +84,7 @@ fdcd9a5055d08037278b842e7ab69265345c5811f0a06867136c511d140bb191 ./presets/wind
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
a453bb9c8529c0c37d7004765aef8a47ccc7a5e92080802435b7b1db3a695464 ./scripts/api_dev_test.py
beafb364bff332c40c1039fe09279517609b6b2482315917a1f4e47b6aaad04c ./scripts/api_dev_test.py
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
@@ -198,7 +198,7 @@ f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/
d8a602ed8906d77593bd140315ce1bd56716038e789f53ff31762e9b3cb7220d ./src/state.rs
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
fd56aa620ddd63bf7f8d03f00da3b2bd53993240bb37cebdac1bdffec22d867c ./web/css/styles.css
1d6fa9e291ebbae40a7a8b06a7cef9dbd0ee06d88ec6b664361c37cb6c1aa281 ./web/css/styles.css
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
43f33fc21bb11df0ea149fe7fbca722a57f93890e4d9bb542ced703c1311207e ./web/index.html
@@ -206,7 +206,7 @@ e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
1b444068bf91741c54bd51741a95128e335219275c415fe9ce4531c80593b50f ./web/js-dynamic/dashboard.js
652cb663d40d51b57102b0403ee2bc462af290ff9f2a03f142c65b251acb2db8 ./web/js-dynamic/dashboard.js
930e67432f55e8476513e808edfa9a0809a8da7479307284fcc136082a4129a4 ./web/js-dynamic/entities.js
4612c958d7b9ccf89d6d45a11c3bc7e864bbd2ceaccc16fd7d76811ba4c74196 ./web/js-dynamic/events.js
43c78a3871cfdb0382081ec30b8d48650450c71b44d006414b161349ac1fa659 ./web/js-dynamic/flows.js
@@ -214,7 +214,7 @@ e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynam
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
30cbaebb817d5d416e1a372595b647aae42c14b9cc406c4751d0ced563243b69 ./web/js-dynamic/navigation.js
0c93119b3d5985d3440e1525d908efecd1567ab2caa0e6cc3babeecd87bfde5c ./web/js-dynamic/realtime.js
3ff9c027beb8fb033330bf3cfb3496e06fb38c42041f1ffe522c6c7af18e9e30 ./web/js-dynamic/realtime.js
1b667f0a8bce824b27f56a8d2f44ff8feef9a011289591dc629ee288db70d137 ./web/js-dynamic/router.js
c2710fd9c1f2b31878c1e25ceb02d74ae30456c1bbb400e69fcf7619ecc72bc2 ./web/js-dynamic/select-ui.js
bc42b4a0dd0d20239ce22ea1dff09b3f94e8c0e1395e01251a4585a051968a8f ./web/js-dynamic/settings-ui.js
+3 -3
View File
@@ -1,6 +1,6 @@
# GREE Controller API reference
HTTP and WebSocket API for GREE Controller **0.15.15**.
HTTP and WebSocket API for GREE Controller **0.15.16**.
[← Main documentation](../README.md)
@@ -243,7 +243,7 @@ Response:
{
"status": "ok",
"name": "gree-controller",
"version": "0.15.15",
"version": "0.15.16",
"uptime_seconds": 1234,
"control_ready": true,
"time": "2026-08-30T06:54:00Z"
@@ -306,7 +306,7 @@ Returns the initial Web UI snapshot:
"control_plan": {"generated_at": "2026-09-04T08:00:00Z", "zones": [], "rules": []},
"control_plan_revision": 42,
"system": {
"version": "0.15.15",
"version": "0.15.16",
"uptime_seconds": 1234,
"auth_required": false,
"control_ready": true,
+2 -2
View File
@@ -2,7 +2,7 @@
"openapi": "3.1.0",
"info": {
"title": "GREE Controller API",
"version": "0.15.15",
"version": "0.15.16",
"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.15.15"
"example": "0.15.16"
},
"uptime_seconds": {
"type": "integer",
@@ -1,7 +1,7 @@
{
"domain": "gree_controller",
"name": "GREE Controller",
"version": "0.15.15",
"version": "0.15.16",
"config_flow": true,
"integration_type": "hub",
"iot_class": "local_polling",
@@ -1,5 +1,13 @@
# Changelog
## 0.15.16
- Makes Dashboard → Global power emergency STOP context-aware instead of permanently prominent whenever any zone is enabled.
- Keeps the yellow STOP prominent while any unit is powered or automatic thermostat control is currently active.
- Shows a subdued STOP while schedules, automations, Flows or explicit thermostat control are armed but idle, so the safety gate can still be engaged before the next start.
- Hides STOP only when every unit is off and no automatic control is armed; an active emergency STOP always remains visible as the green return-to-normal action.
- Refreshes the STOP state immediately on device, schedule, automation, Flow and control-plan realtime updates.
## 0.15.15
- Adds a persistent emergency STOP to Dashboard → Global power. Activating it stores a restart-resistant safety gate, clears queued automatic compressor actions, pauses schedules/thermostat automation and makes a one-time attempt to power off enabled units; startup does not replay the OFF command.
@@ -2,7 +2,7 @@
Local GREE HVAC controller packaged as a Home Assistant add-on, with Web UI, HTTP/WebSocket API, built-in Swagger API documentation, UDP discovery/control, `amd64` and `aarch64` support, ingress, and database backup from `/data/gree-controller.db`.
Version 0.15.15 adds a persistent emergency STOP in Global power and expands Control plan with per-zone reasons and recent automation trigger context, so the add-on clearly shows why climate control is running, waiting or paused.
Version 0.15.16 makes the Global power emergency STOP context-aware: active control keeps it prominent, armed-but-idle automation shows a subdued STOP, fully idle systems hide it, and an active STOP always exposes the green return-to-normal action.
![Home Assistant OS + GREE + VLAN topology](https://git.linuxiarz.pl/gru/gree-controller-ha-addon/raw/branch/master/gree-controller/topology-ha-vlan-gree-en.png)
@@ -1,5 +1,5 @@
name: "GREE Controller"
version: "0.15.15"
version: "0.15.16"
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/"
+1
View File
@@ -652,6 +652,7 @@
"house.powerPartial": "Could not change power on {count} devices",
"house.emergencyStop": "Emergency STOP",
"house.emergencyStopHint": "Pauses all automation and makes a one-shot attempt to power off every enabled unit.",
"house.emergencyStopIdleHint": "Automation is enabled but currently idle and all units are off. STOP remains available to block the next automatic start.",
"house.emergencyStopConfirm": "Activate the emergency stop? Automation will remain persistently paused until you resume it manually, and the controller will try to power off all units.",
"house.emergencyResume": "Return to normal mode",
"house.emergencyResumeHint": "Releases the automation pause. It does not force devices on; the controller evaluates current conditions again.",
+1
View File
@@ -652,6 +652,7 @@
"house.powerPartial": "Nie udało się zmienić zasilania {count} urządzeń",
"house.emergencyStop": "STOP awaryjny",
"house.emergencyStopHint": "Wstrzymuje całą automatykę i jednorazowo próbuje wyłączyć wszystkie aktywne jednostki.",
"house.emergencyStopIdleHint": "Automatyka jest włączona, ale obecnie bezczynna i wszystkie jednostki są wyłączone. STOP pozostaje dostępny, aby zablokować kolejne automatyczne uruchomienie.",
"house.emergencyStopConfirm": "Włączyć STOP awaryjny? Automatyka zostanie trwale wstrzymana do ręcznego wznowienia, a sterownik spróbuje wyłączyć wszystkie jednostki.",
"house.emergencyResume": "Powrót do normalnego trybu",
"house.emergencyResumeHint": "Odblokowuje automatykę. Nie wymusza włączenia urządzeń; sterownik ponownie oceni aktualne warunki.",
+3 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Developer smoke/integration tests for GREE Controller API 0.15.15.
"""Developer smoke/integration tests for GREE Controller API 0.15.16.
Default mode is read-only and safe to run against a real controller.
Use --settings-write to additionally round-trip all split settings resources and
@@ -26,7 +26,7 @@ from typing import Any, Callable, Iterable, Optional
DEFAULT_BASE_URL = os.environ.get("GREE_API_URL", "http://127.0.0.1:8787")
DEFAULT_TOKEN = os.environ.get("GREE_API_TOKEN", "")
DEFAULT_EXPECTED_VERSION = "0.15.15"
DEFAULT_EXPECTED_VERSION = "0.15.16"
SETTINGS_PATHS: dict[str, set[str]] = {
"/api/settings/application": {"simulator_enabled"},
@@ -599,7 +599,7 @@ def run_suite(args: argparse.Namespace) -> int:
print(f"Settings write tests: {'ENABLED' if args.settings_write else 'disabled'}\n")
runner.run("public health", lambda: test_health(client, args.expected_version))
runner.run("OpenAPI 0.15.15 contract", lambda: test_openapi(client, args.expected_version))
runner.run("OpenAPI 0.15.16 contract", lambda: test_openapi(client, args.expected_version))
runner.run("bootstrap snapshot contract", lambda: test_bootstrap_contract(client))
runner.run("protected API requires auth", lambda: test_protected_auth(client))
+15
View File
@@ -1897,6 +1897,21 @@ button.outside-pill:focus-visible {
background: color-mix(in srgb, var(--warning-soft) 72%, var(--surface-2));
}
.house-power-actions .house-emergency-stop.idle {
border-color: color-mix(in srgb, var(--warning) 30%, var(--line));
color: color-mix(in srgb, var(--warning) 58%, var(--muted));
background: color-mix(in srgb, var(--warning-soft) 38%, transparent);
font-weight: 650;
opacity: .72;
}
.house-power-actions .house-emergency-stop.idle:hover {
border-color: color-mix(in srgb, var(--warning) 58%, var(--line));
color: var(--warning);
background: var(--warning-soft);
opacity: 1;
}
.house-power-actions .house-emergency-stop.resume,
.house-power-actions .house-emergency-stop.resume.active {
border-color: var(--accent-border);
+13 -5
View File
@@ -49,20 +49,27 @@ function renderHouseClimate() {
const powerOn = $('#housePowerOn'), powerOff = $('#housePowerOff');
const emergencyButton = $('#houseEmergencyStop');
const emergencyActive = !!(app.house?.emergency_stop_enabled || app.controlPlan?.emergency_stop_active);
const hasAutomaticControl = (app.zones || []).some(zone => zone.enabled)
|| (app.schedules || []).some(item => item.enabled)
|| (app.automations || []).some(item => item.enabled);
const planZones = app.controlPlan?.zones || [];
const unitsWorking = (app.devices || []).some(device => device.power === true);
const automationTriggered = planZones.some(zone => zone.desired_power === true || zone.demand === true);
const hasAutomaticControl = (app.schedules || []).some(item => item.enabled)
|| (app.automations || []).some(item => item.enabled)
|| (app.flows || []).some(item => item.enabled && !item.draft)
|| planZones.some(zone => zone.local_thermostat_power === true || !!zone.current_schedule_id || zone.desired_power === true);
const emergencyVisible = emergencyActive || unitsWorking || hasAutomaticControl;
const emergencyIdle = !emergencyActive && !unitsWorking && !automationTriggered && hasAutomaticControl;
if (powerOn) { powerOn.classList.remove('active'); powerOn.removeAttribute('aria-pressed'); }
if (powerOff) { powerOff.classList.remove('active'); powerOff.removeAttribute('aria-pressed'); }
if (emergencyButton) {
emergencyButton.hidden = !emergencyActive && !hasAutomaticControl;
emergencyButton.hidden = !emergencyVisible;
emergencyButton.dataset.value = emergencyActive ? 'false' : 'true';
emergencyButton.classList.toggle('resume', emergencyActive);
emergencyButton.classList.toggle('active', emergencyActive);
emergencyButton.classList.toggle('idle', emergencyIdle);
emergencyButton.setAttribute('aria-pressed', String(emergencyActive));
emergencyButton.textContent = tr(emergencyActive ? 'house.emergencyResume' : 'house.emergencyStop');
emergencyButton.title = tr(emergencyActive ? 'house.emergencyResumeHint' : 'house.emergencyStopHint');
emergencyButton.title = tr(emergencyActive ? 'house.emergencyResumeHint' : emergencyIdle ? 'house.emergencyStopIdleHint' : 'house.emergencyStopHint');
}
node.innerHTML = `<div class="house-climate-head"><div><span class="eyebrow">${esc(tr('house.seasonMode'))}</span><h3>${esc(tr('house.smartThermostat'))}</h3><p>${esc(tr('house.setpointStrategy'))}</p></div><button type="button" class="outside-pill" data-action="open-outdoor-history" title="${esc(tr('house.outdoorHistoryOpen'))}" aria-label="${esc(tr('house.outdoorHistoryOpen'))}"><small>${esc(tr('house.outdoor'))}</small><strong>${esc(outdoor)}</strong></button></div>
<div class="house-mode-row">
@@ -221,6 +228,7 @@ function applyControlPlan(plan, revision = null) {
app.controlPlan = plan;
if (Number.isFinite(nextRevision)) app.controlPlanRevision = nextRevision;
renderControlPlan();
renderHouseClimate();
renderSimulationPage();
return true;
}
+9 -9
View File
@@ -61,8 +61,8 @@ async function handleWebSocketMessage(event) {
}
const data = message.data || {};
if (message.event === 'control_plan.updated') { app.controlPlanPushReady = true; stopControlPlanFallback(); applyControlPlan(data.plan, data.revision); }
else if (message.event === 'device.updated') { updateDevice(data); renderSummary(); renderDevices(); renderGroups(); scheduleControlPlanLoad(); }
else if (message.event === 'device.created') { updateDevice(data); renderSummary(); renderDevices(); renderGroups(); fillSelects(); scheduleControlPlanLoad(); }
else if (message.event === 'device.updated') { updateDevice(data); renderSummary(); renderHouseClimate(); renderDevices(); renderGroups(); scheduleControlPlanLoad(); }
else if (message.event === 'device.created') { updateDevice(data); renderSummary(); renderHouseClimate(); renderDevices(); renderGroups(); fillSelects(); scheduleControlPlanLoad(); }
else if (message.event === 'device.deleted') { app.devices = app.devices.filter(v => v.id !== data.id); renderAll(); scheduleControlPlanLoad(); }
else if (message.event === 'devices.discovered') { (data.devices || []).forEach(updateDevice); renderAll(); scheduleControlPlanLoad(); }
else if (message.event === 'zone.updated') { const i = app.zones.findIndex(v => v.id === data.id); if (i >= 0) app.zones[i] = data; else app.zones.push(data); renderSummary(); renderHouseClimate(); renderZones(); renderDevices(); renderGroups(); scheduleControlPlanLoad(); }
@@ -91,13 +91,13 @@ async function handleWebSocketMessage(event) {
renderDevices();
}
}
else if (['schedule.updated', 'schedule.created'].includes(message.event)) { const i = app.schedules.findIndex(v => v.id === data.id); if (i >= 0) app.schedules[i] = data; else app.schedules.push(data); renderSchedules(); fillSelects(); scheduleControlPlanLoad(); }
else if (message.event === 'schedule.deleted') { app.schedules = app.schedules.filter(v => v.id !== data.id); renderSchedules(); fillSelects(); scheduleControlPlanLoad(); }
else if (message.event === 'schedule.template_applied') { try { app.schedules = await api('/api/schedules'); renderSchedules(); fillSelects(); } catch (_) { } scheduleControlPlanLoad(); }
else if (['automation.updated', 'automation.created'].includes(message.event)) { const i = app.automations.findIndex(v => v.id === data.id); if (i >= 0) app.automations[i] = data; else app.automations.push(data); renderAutomations(); fillSelects(); scheduleControlPlanLoad(); }
else if (message.event === 'automation.deleted') { app.automations = app.automations.filter(v => v.id !== data.id); renderAutomations(); fillSelects(); scheduleControlPlanLoad(); }
else if (['flow.updated', 'flow.created'].includes(message.event)) { const i = app.flows.findIndex(v => v.id === data.id); if (i >= 0) app.flows[i] = data; else app.flows.push(data); renderFlows(); scheduleControlPlanLoad(); }
else if (message.event === 'flow.deleted') { app.flows = app.flows.filter(v => v.id !== data.id); renderFlows(); scheduleControlPlanLoad(); }
else if (['schedule.updated', 'schedule.created'].includes(message.event)) { const i = app.schedules.findIndex(v => v.id === data.id); if (i >= 0) app.schedules[i] = data; else app.schedules.push(data); renderHouseClimate(); renderSchedules(); fillSelects(); scheduleControlPlanLoad(); }
else if (message.event === 'schedule.deleted') { app.schedules = app.schedules.filter(v => v.id !== data.id); renderHouseClimate(); renderSchedules(); fillSelects(); scheduleControlPlanLoad(); }
else if (message.event === 'schedule.template_applied') { try { app.schedules = await api('/api/schedules'); renderHouseClimate(); renderSchedules(); fillSelects(); } catch (_) { } scheduleControlPlanLoad(); }
else if (['automation.updated', 'automation.created'].includes(message.event)) { const i = app.automations.findIndex(v => v.id === data.id); if (i >= 0) app.automations[i] = data; else app.automations.push(data); renderHouseClimate(); renderAutomations(); fillSelects(); scheduleControlPlanLoad(); }
else if (message.event === 'automation.deleted') { app.automations = app.automations.filter(v => v.id !== data.id); renderHouseClimate(); renderAutomations(); fillSelects(); scheduleControlPlanLoad(); }
else if (['flow.updated', 'flow.created'].includes(message.event)) { const i = app.flows.findIndex(v => v.id === data.id); if (i >= 0) app.flows[i] = data; else app.flows.push(data); renderHouseClimate(); renderFlows(); scheduleControlPlanLoad(); }
else if (message.event === 'flow.deleted') { app.flows = app.flows.filter(v => v.id !== data.id); renderHouseClimate(); renderFlows(); scheduleControlPlanLoad(); }
else if (message.event === 'settings.application.updated') { applySettingsSection('application', data); if (!isFormDirty($('#settingsForm'))) renderSettings(); renderSimulationModeBanner(); renderSystemInfo(); }
else if (message.event === 'settings.gree.updated') { applySettingsSection('gree', data); if (!isFormDirty($('#settingsForm'))) renderSettings(); scheduleControlPlanLoad(); }
else if (message.event === 'settings.gree_cloud.updated') { applySettingsSection('greeCloud', data); if (!isFormDirty($('#settingsForm'))) renderSettings(); }