diff --git a/Cargo.lock b/Cargo.lock index 71d8656..29d56a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -719,7 +719,7 @@ dependencies = [ [[package]] name = "gree-controller" -version = "0.15.15" +version = "0.15.16" dependencies = [ "aes", "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index 361d239..0f29ad0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/FILE_MANIFEST.sha256 b/FILE_MANIFEST.sha256 index 87b5f32..40851e6 100644 --- a/FILE_MANIFEST.sha256 +++ b/FILE_MANIFEST.sha256 @@ -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 diff --git a/README.md b/README.md index 363b15e..5e61f25 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.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`. diff --git a/SOURCE_MANIFEST.sha256 b/SOURCE_MANIFEST.sha256 index 3762227..e47edc8 100644 --- a/SOURCE_MANIFEST.sha256 +++ b/SOURCE_MANIFEST.sha256 @@ -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 diff --git a/docs/API.md b/docs/API.md index 8b21020..a9d4276 100644 --- a/docs/API.md +++ b/docs/API.md @@ -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, diff --git a/docs/openapi.json b/docs/openapi.json index 82c01cf..7e860f1 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -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", 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 1bbab5c..74aabbb 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.15.15", + "version": "0.15.16", "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 f3dde83..c04bb11 100644 --- a/ha-addon/repository/gree-controller/CHANGELOG.md +++ b/ha-addon/repository/gree-controller/CHANGELOG.md @@ -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. diff --git a/ha-addon/repository/gree-controller/README.md b/ha-addon/repository/gree-controller/README.md index 63f96b8..5d05a43 100644 --- a/ha-addon/repository/gree-controller/README.md +++ b/ha-addon/repository/gree-controller/README.md @@ -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) diff --git a/ha-addon/repository/gree-controller/config.yaml b/ha-addon/repository/gree-controller/config.yaml index 3745fc3..24b78fd 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.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/" diff --git a/lang/en.json b/lang/en.json index 1ebd36d..b5abffc 100644 --- a/lang/en.json +++ b/lang/en.json @@ -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.", diff --git a/lang/pl.json b/lang/pl.json index 79ceb0d..a6d8381 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -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.", diff --git a/scripts/api_dev_test.py b/scripts/api_dev_test.py index fbe7807..d1cb3ae 100644 --- a/scripts/api_dev_test.py +++ b/scripts/api_dev_test.py @@ -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)) diff --git a/web/css/styles.css b/web/css/styles.css index 1631a4f..58cb076 100644 --- a/web/css/styles.css +++ b/web/css/styles.css @@ -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); diff --git a/web/js-dynamic/dashboard.js b/web/js-dynamic/dashboard.js index 2d94c66..59a8473 100644 --- a/web/js-dynamic/dashboard.js +++ b/web/js-dynamic/dashboard.js @@ -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 = `
${esc(tr('house.seasonMode'))}

${esc(tr('house.smartThermostat'))}

${esc(tr('house.setpointStrategy'))}

@@ -221,6 +228,7 @@ function applyControlPlan(plan, revision = null) { app.controlPlan = plan; if (Number.isFinite(nextRevision)) app.controlPlanRevision = nextRevision; renderControlPlan(); + renderHouseClimate(); renderSimulationPage(); return true; } diff --git a/web/js-dynamic/realtime.js b/web/js-dynamic/realtime.js index 706a8a1..0157e60 100644 --- a/web/js-dynamic/realtime.js +++ b/web/js-dynamic/realtime.js @@ -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(); }