v0.15.15
This commit is contained in:
Generated
+1
-1
@@ -719,7 +719,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gree-controller"
|
name = "gree-controller"
|
||||||
version = "0.15.14"
|
version = "0.15.15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gree-controller"
|
name = "gree-controller"
|
||||||
version = "0.15.14"
|
version = "0.15.15"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["GREE Controller contributors"]
|
authors = ["GREE Controller contributors"]
|
||||||
description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support"
|
description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support"
|
||||||
|
|||||||
+32
-32
@@ -1,14 +1,14 @@
|
|||||||
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
||||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||||
11082066dd96ca1b6c606593cbb1264ad19d30e92db64aa0ca14b0203f880e81 ./Cargo.lock
|
bf494c24be8032cab82e54d9aaebdda6e7544eb5ebef4b2a3e700808636073e7 ./Cargo.lock
|
||||||
909fd22e0fb139f17f3996be8088c42150761706e8b6c495d9f016ba26191491 ./Cargo.toml
|
34a15c5dbb3a739480b32f40ba51d19ea51db19fd39474df560e35ea9f0ba0e3 ./Cargo.toml
|
||||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||||
3bb541af2907e9f55ab3ab683822966834b8f6d81881ab0ce812d48cadac344b ./README.md
|
41bb456a4b4e71298a1859994562e0f582d056b2cfdbb54c59f5b4dfaab6d2f0 ./README.md
|
||||||
dab9da1afb385bc96ba4888c9eb0c8fcef60f5983bd8fdbb808632f269d8f02d ./SOURCE_MANIFEST.sha256
|
bbce332922c081118cf6cace226fb5ae1573139604a8bb9bc27b7ef1c8c04541 ./SOURCE_MANIFEST.sha256
|
||||||
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
||||||
69c7604b2bdeef946dd95675a07b77bd798a0c4ab17025670df09cd339d38278 ./docs/API.md
|
323abb42b1c612d23826e5c01ab89aa1d757490d32e4423430626acb94ae68c9 ./docs/API.md
|
||||||
43af7941ec4d427f9db7ab3cc6baf3d182a72dba7a6791f14094fee138324d8d ./docs/FLOW.md
|
43af7941ec4d427f9db7ab3cc6baf3d182a72dba7a6791f14094fee138324d8d ./docs/FLOW.md
|
||||||
01186f7403248378373edebb990cb016e9ff9f48df5874ab95eec4b1fa6e4273 ./docs/openapi.json
|
7abd620b69af9fe82dcf7e1c91a1c27170e00003a5ca26e3089bffca3ed38c96 ./docs/openapi.json
|
||||||
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
||||||
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
||||||
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
|
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
|
||||||
@@ -22,7 +22,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./ha-addon/hom
|
|||||||
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
|
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
|
||||||
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
|
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
|
||||||
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
|
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
|
||||||
f84b8abfd72a2f4eb69cff85aa4e9ec46b06a44f038e6a75afdd6b5accf570e8 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
dc29a75dcc3ce0f3f941f651f77271acdc5e49540baec5050e6c8e294f95da74 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
||||||
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
|
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
|
||||||
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
|
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
|
||||||
cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.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
|
a745cc458c235158d5d70b6a33d8000fd8b40ff93bfe2f2796af0f4b3a9eef9b ./ha-addon/home-assistant/custom_components/gree_controller/translations/en.json
|
||||||
50b7d6fe6329ad4ea6bee9612ab141c2f73f8382fcc2af0bb076a8e8d4dd4465 ./ha-addon/home-assistant/custom_components/gree_controller/translations/pl.json
|
50b7d6fe6329ad4ea6bee9612ab141c2f73f8382fcc2af0bb076a8e8d4dd4465 ./ha-addon/home-assistant/custom_components/gree_controller/translations/pl.json
|
||||||
c5fc5c87273d82d2834a0a5a365c821a416413ed509613441e24e4dc3507c4d2 ./ha-addon/home-assistant/generated/gree_controller_entities.example.json
|
c5fc5c87273d82d2834a0a5a365c821a416413ed509613441e24e4dc3507c4d2 ./ha-addon/home-assistant/generated/gree_controller_entities.example.json
|
||||||
b1aa529d58658ca1538904e86a42d0b5d23fc39c35a4bae9c785f3288d75af55 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
e4ce58fc6b0f5f1a1fba802097badc8dad6c357ca2bd5a25ab9b698995ebc5c1 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||||
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
||||||
f583a7f66d024da96f55c4aa45ea403294a433265da2312a0d81502a2f7079c3 ./ha-addon/repository/gree-controller/README.md
|
fc3151e76c08d176bdf695dd5b2537a79d83a768633282e4c72a3c86bb841659 ./ha-addon/repository/gree-controller/README.md
|
||||||
2b4470debd1ad26fbddb3c1c70ba6ad994f5509c2c604a3dcc0ba62b4779572e ./ha-addon/repository/gree-controller/config.yaml
|
9622d558c6fa9c855cc3bc672980bb5ff8726ac81d622f0ede371580156305c6 ./ha-addon/repository/gree-controller/config.yaml
|
||||||
3aae6cdae4c3aaab7786a9575e7093b7e2c5b69288167075f4b7475611691a6b ./ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png
|
3aae6cdae4c3aaab7786a9575e7093b7e2c5b69288167075f4b7475611691a6b ./ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png
|
||||||
0d0b42a7639b128946eec379175436fa1f150294f39d2dfd169077bfa87fd14d ./ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png
|
0d0b42a7639b128946eec379175436fa1f150294f39d2dfd169077bfa87fd14d ./ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png
|
||||||
2272740b185e9520e948f74b8f45cf98b8b5fd28c93bdee760da626f91fe5c2a ./ha-addon/repository/gree-controller/translations/en.yaml
|
2272740b185e9520e948f74b8f45cf98b8b5fd28c93bdee760da626f91fe5c2a ./ha-addon/repository/gree-controller/translations/en.yaml
|
||||||
@@ -43,8 +43,8 @@ cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run
|
|||||||
081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh
|
081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh
|
||||||
f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md
|
f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md
|
||||||
253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md
|
253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md
|
||||||
df4b0545d6f522a7f1bdff3b47b8a6fe0f711c0ad2013c0d35cfad7b61d73a92 ./lang/en.json
|
f5251fbd8355f560df97bc2042e05e045f79bffc6b6f18bf84999c16fc4c64df ./lang/en.json
|
||||||
cfc4ef4a6f6dd26063ed8673d6f74173f193d531f2255cc89e53e9137cf6a09e ./lang/pl.json
|
c1c98e668c6b2e615e882c9079b6f198e0360c188264d0083c21d7b332b4cbc9 ./lang/pl.json
|
||||||
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
|
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
|
||||||
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
|
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
|
||||||
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
|
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
|
||||||
@@ -85,7 +85,7 @@ fdcd9a5055d08037278b842e7ab69265345c5811f0a06867136c511d140bb191 ./presets/wind
|
|||||||
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
||||||
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
||||||
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
||||||
17a8e34882ad7c3f4cd53d3ccdff74a9d533963bd14b7434fc7d4ca2d9e56093 ./scripts/api_dev_test.py
|
a453bb9c8529c0c37d7004765aef8a47ccc7a5e92080802435b7b1db3a695464 ./scripts/api_dev_test.py
|
||||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
||||||
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
||||||
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
||||||
@@ -98,11 +98,11 @@ e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./scripts/netw
|
|||||||
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./scripts/service.sh
|
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./scripts/service.sh
|
||||||
bd703ef841f9763f13367aaa5764381b9c59ee87210e806c24617f3cc0739a9b ./scripts/smoke.sh
|
bd703ef841f9763f13367aaa5764381b9c59ee87210e806c24617f3cc0739a9b ./scripts/smoke.sh
|
||||||
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
|
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
|
||||||
b53e1afab342634fedc218ea4517446f19a5ba7985dee599e191b37d9cc8058a ./src/api.rs
|
ea84f5230da97447c60dfb4f8dadd0747c92fd1f7a74d5a65d6fdb6f23cf4b6d ./src/api.rs
|
||||||
0db0d81da4b8161004ddcbed2096812c285ecec9d944771670e6c8edf0775175 ./src/api/assets.rs
|
0db0d81da4b8161004ddcbed2096812c285ecec9d944771670e6c8edf0775175 ./src/api/assets.rs
|
||||||
1139dacbd3f6ff94bf721eb5a60d469016fb935ddcf484d71bfeb249b72c833a ./src/api/auth.rs
|
1139dacbd3f6ff94bf721eb5a60d469016fb935ddcf484d71bfeb249b72c833a ./src/api/auth.rs
|
||||||
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
||||||
27a788efe385ad7180cb7fc7ec66c34ebd5e6062d9cdf35b32fb461e52a42863 ./src/api/configuration.rs
|
fffad7874465739cd6b508e9d7791387b864fa88420ce0f7e2fc933d129a2751 ./src/api/configuration.rs
|
||||||
19bc141286a336ad67e99079496eb0ae5126f986ab28be62b5f97758e5f6969c ./src/api/debug_tokens.rs
|
19bc141286a336ad67e99079496eb0ae5126f986ab28be62b5f97758e5f6969c ./src/api/debug_tokens.rs
|
||||||
ca21f6d5a407e2e3532899d53ba4e8682c2fe961a46d8cd870ddc459721513ef ./src/api/device_groups.rs
|
ca21f6d5a407e2e3532899d53ba4e8682c2fe961a46d8cd870ddc459721513ef ./src/api/device_groups.rs
|
||||||
8f92909dc575ea6238f4a003f8bd4056c2752fa3247b9ab226e47f875c80b916 ./src/api/devices.rs
|
8f92909dc575ea6238f4a003f8bd4056c2752fa3247b9ab226e47f875c80b916 ./src/api/devices.rs
|
||||||
@@ -111,17 +111,17 @@ ca5bc21e051222dcaf0c507e84cb3e60f52a0b612a6056847fa0aee3a4d1a672 ./src/api/flow
|
|||||||
82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs
|
82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs
|
||||||
b61b0b4f196c5e72e47cf06ca0c14df3cf8eedbdef0242a9d2af33623663489f ./src/api/groups.rs
|
b61b0b4f196c5e72e47cf06ca0c14df3cf8eedbdef0242a9d2af33623663489f ./src/api/groups.rs
|
||||||
b6d586bc42b166ac05648f3da7d9aeb88efde2e9eaab5642685eb4d9cf15f95a ./src/api/history.rs
|
b6d586bc42b166ac05648f3da7d9aeb88efde2e9eaab5642685eb4d9cf15f95a ./src/api/history.rs
|
||||||
e8edfbb725d6de650442444b0a929ad2be0bd2c2d7d227d2b3fdd99d05c94f94 ./src/api/house.rs
|
655bc8135fbe0b8fa491666942e80817a0eabc7dea694c68c2e5ebb30c8ecf75 ./src/api/house.rs
|
||||||
4ed7835ed622806124a805f54ffde7c39854f2447de0141d756d96f22446fb90 ./src/api/integrations.rs
|
4ed7835ed622806124a805f54ffde7c39854f2447de0141d756d96f22446fb90 ./src/api/integrations.rs
|
||||||
7658111f7d125db55a6e23f88157339b110b2cd1ed13ceabd8f860b42dba5101 ./src/api/middleware.rs
|
7658111f7d125db55a6e23f88157339b110b2cd1ed13ceabd8f860b42dba5101 ./src/api/middleware.rs
|
||||||
9d996d326f2ec192c79fb108a8d2c06efaa1c9634d9e9e54fa65a65524f92a75 ./src/api/openapi.rs
|
9d996d326f2ec192c79fb108a8d2c06efaa1c9634d9e9e54fa65a65524f92a75 ./src/api/openapi.rs
|
||||||
b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/public_settings.rs
|
b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/public_settings.rs
|
||||||
5b29f2e5aa2c422c5829ef89c7b59cf753883aa7f60661c95ab0f95cf676fa3f ./src/api/schedules.rs
|
5b29f2e5aa2c422c5829ef89c7b59cf753883aa7f60661c95ab0f95cf676fa3f ./src/api/schedules.rs
|
||||||
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
||||||
bb57f4b28df75ba350c9706719650fff3a689afee0cb6979c1fcae79396ac0cf ./src/api/system.rs
|
7c974a98c72092886247f67c3a1c62736898d4b279ceb9c4c306690a3c6e4294 ./src/api/system.rs
|
||||||
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
|
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
|
||||||
ddd1bfc8625b66f6e5f3d24b3122ac86c25f89b0af527e3dfce9102551a8f142 ./src/api/zones.rs
|
ddd1bfc8625b66f6e5f3d24b3122ac86c25f89b0af527e3dfce9102551a8f142 ./src/api/zones.rs
|
||||||
0b7d2ace2e8cb1dd0cb59aa6ca3e80cec14078e93530fd7b58081798585cd833 ./src/config.rs
|
32fa38d4315682cd3ef3ecdc91e571c2729b5e68b6f2bff965e43de3fdd2aeed ./src/config.rs
|
||||||
de0ce7807aebe4c0e325fbef01855e7fc1cfd117529f53f04a416fda3997673a ./src/db.rs
|
de0ce7807aebe4c0e325fbef01855e7fc1cfd117529f53f04a416fda3997673a ./src/db.rs
|
||||||
87f1614a80addde69b619421187526d7f86f893c0ac18998854f11675d6e7cca ./src/db/climate.rs
|
87f1614a80addde69b619421187526d7f86f893c0ac18998854f11675d6e7cca ./src/db/climate.rs
|
||||||
a8fe3636ac79df191e2f2c9d8c1eec1e5181e048ef8c9036cfa18f84aef1a462 ./src/db/configuration.rs
|
a8fe3636ac79df191e2f2c9d8c1eec1e5181e048ef8c9036cfa18f84aef1a462 ./src/db/configuration.rs
|
||||||
@@ -136,10 +136,10 @@ d6b9b70a04c774173c97f7426487d5e2932d7713e12ae3fb7f64cf2b4e0b3c10 ./src/db/sched
|
|||||||
28ee42d08147b0f154af818065d7d014d74ee8ddd59fdef98115f5c5368056cc ./src/db/tests.rs
|
28ee42d08147b0f154af818065d7d014d74ee8ddd59fdef98115f5c5368056cc ./src/db/tests.rs
|
||||||
f90fbb14f29a20b799b3856bdaa22812a9f0d9dfa46693f1341c160dccda8a97 ./src/db/zone_history.rs
|
f90fbb14f29a20b799b3856bdaa22812a9f0d9dfa46693f1341c160dccda8a97 ./src/db/zone_history.rs
|
||||||
0e49ea4a52017b0a8501d5101b1906a7a1e8d5158acb0bb65caa43cde2836ec0 ./src/engine.rs
|
0e49ea4a52017b0a8501d5101b1906a7a1e8d5158acb0bb65caa43cde2836ec0 ./src/engine.rs
|
||||||
a0d60ad81d024488dfec586b0d9e9f447e9a3485985604415465cacba59d645f ./src/engine/automations.rs
|
f3cc7d2cbf44180372d987d7502409547eadb1c725bcce6eeb5a26cc4277b83f ./src/engine/automations.rs
|
||||||
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
|
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
|
||||||
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
|
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
|
||||||
ad53cd7be24deb759e03cbc6472522b69ebd36d8fac35398a9b19c899d3b17f2 ./src/engine/control_plan.rs
|
315116a0dbeeba1af2f72513ea5fcc238da1b5ce9501b039c5188e44976978dc ./src/engine/control_plan.rs
|
||||||
7b48a33343d519a2ff2d81e9d757eb56a0c1beacfc0b7b8ef61f49c2ad363f9a ./src/engine/deadlines.rs
|
7b48a33343d519a2ff2d81e9d757eb56a0c1beacfc0b7b8ef61f49c2ad363f9a ./src/engine/deadlines.rs
|
||||||
519f67ab805f61afea107215f5df50562fa1669a48334cc015570952e25e773a ./src/engine/energy.rs
|
519f67ab805f61afea107215f5df50562fa1669a48334cc015570952e25e773a ./src/engine/energy.rs
|
||||||
e350aa63f3bb14ca887eaaa7555edbeae00fb584cbcaf552cc972f0c8918f5d8 ./src/engine/groups.rs
|
e350aa63f3bb14ca887eaaa7555edbeae00fb584cbcaf552cc972f0c8918f5d8 ./src/engine/groups.rs
|
||||||
@@ -152,9 +152,9 @@ c73691c3637465ab0f79aecf7f5428d0317799a8ab5329ed564cf57963bbe86e ./src/engine/r
|
|||||||
b0189f28d0f337dfd3ce0934da7a565e4a5535afc1c242214c26ed584b3b7840 ./src/engine/targets.rs
|
b0189f28d0f337dfd3ce0934da7a565e4a5535afc1c242214c26ed584b3b7840 ./src/engine/targets.rs
|
||||||
682c8b3956c657b655746cbd19ee97ee2f4cf953bc9de67d6d70a1b14f3dd746 ./src/engine/temperature.rs
|
682c8b3956c657b655746cbd19ee97ee2f4cf953bc9de67d6d70a1b14f3dd746 ./src/engine/temperature.rs
|
||||||
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
|
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
|
||||||
0404e040c5d6f388682edb3d103a658e0b963a402e4e847fc92fd0aea1c0e773 ./src/engine/tests.rs
|
bf1a3840135a71ce64148559cce0fbd570ff99d31c8972cdea7447a49f6868f7 ./src/engine/tests.rs
|
||||||
c89ff36c6a67bf4fbf68c43e18aa0a556de2dc69263fd79db376363afc741a4f ./src/engine/zone_actions.rs
|
c89ff36c6a67bf4fbf68c43e18aa0a556de2dc69263fd79db376363afc741a4f ./src/engine/zone_actions.rs
|
||||||
fb169bcc20f95abe9892367df6dd3a683584bdbc721792d6ffb9311b81ca30d9 ./src/engine/zone_control.rs
|
87ab87d434a6462a2c3f25249b42c59ecb2bca6ed700ec73e95e4613349cf16f ./src/engine/zone_control.rs
|
||||||
bfac95cec87f0df6ce562153cd3cfe6731359d703e6c592d2c2f7ba01ea62d99 ./src/error.rs
|
bfac95cec87f0df6ce562153cd3cfe6731359d703e6c592d2c2f7ba01ea62d99 ./src/error.rs
|
||||||
d681f2aa53c39f37411834c300e9200299f94ee4ea021f44f70a3ea1774bd698 ./src/home_assistant.rs
|
d681f2aa53c39f37411834c300e9200299f94ee4ea021f44f70a3ea1774bd698 ./src/home_assistant.rs
|
||||||
bcd14e4d1af3ca580f3b95bcd86164c40f762511e1b8e2501cb8a598a6d8adf4 ./src/influxdb.rs
|
bcd14e4d1af3ca580f3b95bcd86164c40f762511e1b8e2501cb8a598a6d8adf4 ./src/influxdb.rs
|
||||||
@@ -164,13 +164,13 @@ da4068295f37c23222bb13bebac022d88ba7729604a49ad25438c55e45458a49 ./src/influxdb
|
|||||||
62f9da6ef4f5fd92701d0857773f9f6236ef25356f21b9b42236abab6ba7da53 ./src/main.rs
|
62f9da6ef4f5fd92701d0857773f9f6236ef25356f21b9b42236abab6ba7da53 ./src/main.rs
|
||||||
23688a5cb68efded89471ce959328d27adff370d451bd9f378a283ab51437e76 ./src/models.rs
|
23688a5cb68efded89471ce959328d27adff370d451bd9f378a283ab51437e76 ./src/models.rs
|
||||||
e7b6e49d369aaf665a8aea2464950fd795957de0ad7528a002e11c55c7983f35 ./src/models/automation.rs
|
e7b6e49d369aaf665a8aea2464950fd795957de0ad7528a002e11c55c7983f35 ./src/models/automation.rs
|
||||||
fc5f2802a073a175a8985e56bcb87eb1ff481226d5b4eeabad73dbba1839fc45 ./src/models/control_plan.rs
|
a7a35e60318a620d411ec16ab8faecd34410ccba3310f612e89c06904a16539b ./src/models/control_plan.rs
|
||||||
06357b352ff6494d83e72796b869573a8b8f36b835985a5900ac571c922b8da9 ./src/models/defaults.rs
|
06357b352ff6494d83e72796b869573a8b8f36b835985a5900ac571c922b8da9 ./src/models/defaults.rs
|
||||||
698dcbeba72037a251c25d07c95833f121716d949f9217f1f9429e8cd40638e5 ./src/models/device.rs
|
698dcbeba72037a251c25d07c95833f121716d949f9217f1f9429e8cd40638e5 ./src/models/device.rs
|
||||||
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
||||||
6e44df6bde2f73c282095c80ac17287e5bef77d9fc1d344e440d863dae5f5031 ./src/models/history.rs
|
6e44df6bde2f73c282095c80ac17287e5bef77d9fc1d344e440d863dae5f5031 ./src/models/history.rs
|
||||||
1fee89f939018bf1f3686c15f25434f01ab96b3239164108db7f348a1f1f8d82 ./src/models/integrations.rs
|
1fee89f939018bf1f3686c15f25434f01ab96b3239164108db7f348a1f1f8d82 ./src/models/integrations.rs
|
||||||
e557ee4a443de586a7ea7dd04318694fb30d6ec053076429503e18f1bcd65c9e ./src/models/runtime.rs
|
17cac849056b677a3872e600a0d99218fb383ad5ec91cde596562df8e2f47508 ./src/models/runtime.rs
|
||||||
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
|
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
|
||||||
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
|
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
|
||||||
bc2818ce8d0dae1edfb1b98e5d1c00b193f03eeaa28e343d3532f6023288738c ./src/models/zone.rs
|
bc2818ce8d0dae1edfb1b98e5d1c00b193f03eeaa28e343d3532f6023288738c ./src/models/zone.rs
|
||||||
@@ -196,26 +196,26 @@ a677ce61d3d0f4cf358055ddbc343e73b452c68daed80798499147370819615e ./src/provider
|
|||||||
7e12e8704faa7ac15a312bcc1d714ee1c6f27c1dce0972d4ccd6e5a40c69c884 ./src/queries/maintenance.rs
|
7e12e8704faa7ac15a312bcc1d714ee1c6f27c1dce0972d4ccd6e5a40c69c884 ./src/queries/maintenance.rs
|
||||||
7da8b73a1c8c30e2a12071062d36f5393938fde0b5ffebbe4af1891da40f3233 ./src/queries/schema.rs
|
7da8b73a1c8c30e2a12071062d36f5393938fde0b5ffebbe4af1891da40f3233 ./src/queries/schema.rs
|
||||||
f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/zone_history.rs
|
f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/zone_history.rs
|
||||||
8c34b70fb5f80e3249c2e0c63686e60ce1697cf44a7e6f11b3ad6f689ce17792 ./src/state.rs
|
d8a602ed8906d77593bd140315ce1bd56716038e789f53ff31762e9b3cb7220d ./src/state.rs
|
||||||
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
||||||
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
||||||
2d227963624338f958033ec9b211b6bfa2c869ba3d7dc4f8f8125a29d343f7d9 ./web/css/styles.css
|
fd56aa620ddd63bf7f8d03f00da3b2bd53993240bb37cebdac1bdffec22d867c ./web/css/styles.css
|
||||||
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
||||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||||
92847143a358e6869ed4f21b156b10bda3f8abfcb9a12fc86256221c93be3b09 ./web/index.html
|
43f33fc21bb11df0ea149fe7fbca722a57f93890e4d9bb542ced703c1311207e ./web/index.html
|
||||||
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
||||||
e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js
|
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
|
||||||
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
||||||
81012bd88282034cdc4e876af5eb5057289f4db8505847568bc3c3701a348d4b ./web/js-dynamic/core.js
|
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
|
||||||
752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js
|
1b444068bf91741c54bd51741a95128e335219275c415fe9ce4531c80593b50f ./web/js-dynamic/dashboard.js
|
||||||
930e67432f55e8476513e808edfa9a0809a8da7479307284fcc136082a4129a4 ./web/js-dynamic/entities.js
|
930e67432f55e8476513e808edfa9a0809a8da7479307284fcc136082a4129a4 ./web/js-dynamic/entities.js
|
||||||
b4204241eecf387256364f4230b23ed50d947a362f4ceb1e528d69494e93d7cf ./web/js-dynamic/events.js
|
4612c958d7b9ccf89d6d45a11c3bc7e864bbd2ceaccc16fd7d76811ba4c74196 ./web/js-dynamic/events.js
|
||||||
43c78a3871cfdb0382081ec30b8d48650450c71b44d006414b161349ac1fa659 ./web/js-dynamic/flows.js
|
43c78a3871cfdb0382081ec30b8d48650450c71b44d006414b161349ac1fa659 ./web/js-dynamic/flows.js
|
||||||
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
||||||
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
||||||
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
||||||
30cbaebb817d5d416e1a372595b647aae42c14b9cc406c4751d0ced563243b69 ./web/js-dynamic/navigation.js
|
30cbaebb817d5d416e1a372595b647aae42c14b9cc406c4751d0ced563243b69 ./web/js-dynamic/navigation.js
|
||||||
0e398bad2cde85d4a62d8733c06e73f2b6a5b07db791d4570feba38c7ff8741f ./web/js-dynamic/realtime.js
|
0c93119b3d5985d3440e1525d908efecd1567ab2caa0e6cc3babeecd87bfde5c ./web/js-dynamic/realtime.js
|
||||||
1b667f0a8bce824b27f56a8d2f44ff8feef9a011289591dc629ee288db70d137 ./web/js-dynamic/router.js
|
1b667f0a8bce824b27f56a8d2f44ff8feef9a011289591dc629ee288db70d137 ./web/js-dynamic/router.js
|
||||||
c2710fd9c1f2b31878c1e25ceb02d74ae30456c1bbb400e69fcf7619ecc72bc2 ./web/js-dynamic/select-ui.js
|
c2710fd9c1f2b31878c1e25ceb02d74ae30456c1bbb400e69fcf7619ecc72bc2 ./web/js-dynamic/select-ui.js
|
||||||
bc42b4a0dd0d20239ce22ea1dff09b3f94e8c0e1395e01251a4585a051968a8f ./web/js-dynamic/settings-ui.js
|
bc42b4a0dd0d20239ce22ea1dff09b3f94e8c0e1395e01251a4585a051968a8f ./web/js-dynamic/settings-ui.js
|
||||||
|
|||||||
@@ -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.
|
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.14**
|
**Version: 0.15.15**
|
||||||
|
|
||||||
Version 0.15.14 fixes Home Assistant Supervisor authentication detection for background energy sampling and centralizes runtime HA readiness checks, so add-on installations use the same Supervisor-aware connection logic as entity tests, temperatures and Flow integrations.
|
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.
|
||||||
|
|
||||||
> [Full API reference](docs/API.md) — authentication, every endpoint, request bodies, response models, WebSocket events and examples.
|
> [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`.
|
> Built-in API: `/api/*` and `/ws`; interactive Swagger docs: `/api-docs`; OpenAPI JSON: `/api-docs/openapi.json`.
|
||||||
|
|||||||
+31
-31
@@ -1,13 +1,13 @@
|
|||||||
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
||||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||||
11082066dd96ca1b6c606593cbb1264ad19d30e92db64aa0ca14b0203f880e81 ./Cargo.lock
|
bf494c24be8032cab82e54d9aaebdda6e7544eb5ebef4b2a3e700808636073e7 ./Cargo.lock
|
||||||
909fd22e0fb139f17f3996be8088c42150761706e8b6c495d9f016ba26191491 ./Cargo.toml
|
34a15c5dbb3a739480b32f40ba51d19ea51db19fd39474df560e35ea9f0ba0e3 ./Cargo.toml
|
||||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||||
3bb541af2907e9f55ab3ab683822966834b8f6d81881ab0ce812d48cadac344b ./README.md
|
41bb456a4b4e71298a1859994562e0f582d056b2cfdbb54c59f5b4dfaab6d2f0 ./README.md
|
||||||
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
||||||
69c7604b2bdeef946dd95675a07b77bd798a0c4ab17025670df09cd339d38278 ./docs/API.md
|
323abb42b1c612d23826e5c01ab89aa1d757490d32e4423430626acb94ae68c9 ./docs/API.md
|
||||||
43af7941ec4d427f9db7ab3cc6baf3d182a72dba7a6791f14094fee138324d8d ./docs/FLOW.md
|
43af7941ec4d427f9db7ab3cc6baf3d182a72dba7a6791f14094fee138324d8d ./docs/FLOW.md
|
||||||
01186f7403248378373edebb990cb016e9ff9f48df5874ab95eec4b1fa6e4273 ./docs/openapi.json
|
7abd620b69af9fe82dcf7e1c91a1c27170e00003a5ca26e3089bffca3ed38c96 ./docs/openapi.json
|
||||||
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
||||||
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
||||||
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
|
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
|
||||||
@@ -21,7 +21,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./ha-addon/hom
|
|||||||
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
|
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
|
||||||
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
|
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
|
||||||
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
|
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
|
||||||
f84b8abfd72a2f4eb69cff85aa4e9ec46b06a44f038e6a75afdd6b5accf570e8 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
dc29a75dcc3ce0f3f941f651f77271acdc5e49540baec5050e6c8e294f95da74 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
||||||
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
|
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
|
||||||
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
|
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
|
||||||
cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.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
|
a745cc458c235158d5d70b6a33d8000fd8b40ff93bfe2f2796af0f4b3a9eef9b ./ha-addon/home-assistant/custom_components/gree_controller/translations/en.json
|
||||||
50b7d6fe6329ad4ea6bee9612ab141c2f73f8382fcc2af0bb076a8e8d4dd4465 ./ha-addon/home-assistant/custom_components/gree_controller/translations/pl.json
|
50b7d6fe6329ad4ea6bee9612ab141c2f73f8382fcc2af0bb076a8e8d4dd4465 ./ha-addon/home-assistant/custom_components/gree_controller/translations/pl.json
|
||||||
c5fc5c87273d82d2834a0a5a365c821a416413ed509613441e24e4dc3507c4d2 ./ha-addon/home-assistant/generated/gree_controller_entities.example.json
|
c5fc5c87273d82d2834a0a5a365c821a416413ed509613441e24e4dc3507c4d2 ./ha-addon/home-assistant/generated/gree_controller_entities.example.json
|
||||||
b1aa529d58658ca1538904e86a42d0b5d23fc39c35a4bae9c785f3288d75af55 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
e4ce58fc6b0f5f1a1fba802097badc8dad6c357ca2bd5a25ab9b698995ebc5c1 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||||
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
||||||
f583a7f66d024da96f55c4aa45ea403294a433265da2312a0d81502a2f7079c3 ./ha-addon/repository/gree-controller/README.md
|
fc3151e76c08d176bdf695dd5b2537a79d83a768633282e4c72a3c86bb841659 ./ha-addon/repository/gree-controller/README.md
|
||||||
2b4470debd1ad26fbddb3c1c70ba6ad994f5509c2c604a3dcc0ba62b4779572e ./ha-addon/repository/gree-controller/config.yaml
|
9622d558c6fa9c855cc3bc672980bb5ff8726ac81d622f0ede371580156305c6 ./ha-addon/repository/gree-controller/config.yaml
|
||||||
3aae6cdae4c3aaab7786a9575e7093b7e2c5b69288167075f4b7475611691a6b ./ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png
|
3aae6cdae4c3aaab7786a9575e7093b7e2c5b69288167075f4b7475611691a6b ./ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png
|
||||||
0d0b42a7639b128946eec379175436fa1f150294f39d2dfd169077bfa87fd14d ./ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png
|
0d0b42a7639b128946eec379175436fa1f150294f39d2dfd169077bfa87fd14d ./ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png
|
||||||
2272740b185e9520e948f74b8f45cf98b8b5fd28c93bdee760da626f91fe5c2a ./ha-addon/repository/gree-controller/translations/en.yaml
|
2272740b185e9520e948f74b8f45cf98b8b5fd28c93bdee760da626f91fe5c2a ./ha-addon/repository/gree-controller/translations/en.yaml
|
||||||
@@ -42,8 +42,8 @@ cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run
|
|||||||
081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh
|
081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh
|
||||||
f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md
|
f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md
|
||||||
253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md
|
253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md
|
||||||
df4b0545d6f522a7f1bdff3b47b8a6fe0f711c0ad2013c0d35cfad7b61d73a92 ./lang/en.json
|
f5251fbd8355f560df97bc2042e05e045f79bffc6b6f18bf84999c16fc4c64df ./lang/en.json
|
||||||
cfc4ef4a6f6dd26063ed8673d6f74173f193d531f2255cc89e53e9137cf6a09e ./lang/pl.json
|
c1c98e668c6b2e615e882c9079b6f198e0360c188264d0083c21d7b332b4cbc9 ./lang/pl.json
|
||||||
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
|
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
|
||||||
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
|
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
|
||||||
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
|
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
|
||||||
@@ -84,7 +84,7 @@ fdcd9a5055d08037278b842e7ab69265345c5811f0a06867136c511d140bb191 ./presets/wind
|
|||||||
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
||||||
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
||||||
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
||||||
17a8e34882ad7c3f4cd53d3ccdff74a9d533963bd14b7434fc7d4ca2d9e56093 ./scripts/api_dev_test.py
|
a453bb9c8529c0c37d7004765aef8a47ccc7a5e92080802435b7b1db3a695464 ./scripts/api_dev_test.py
|
||||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
||||||
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
||||||
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
||||||
@@ -97,11 +97,11 @@ e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./scripts/netw
|
|||||||
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./scripts/service.sh
|
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./scripts/service.sh
|
||||||
bd703ef841f9763f13367aaa5764381b9c59ee87210e806c24617f3cc0739a9b ./scripts/smoke.sh
|
bd703ef841f9763f13367aaa5764381b9c59ee87210e806c24617f3cc0739a9b ./scripts/smoke.sh
|
||||||
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
|
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
|
||||||
b53e1afab342634fedc218ea4517446f19a5ba7985dee599e191b37d9cc8058a ./src/api.rs
|
ea84f5230da97447c60dfb4f8dadd0747c92fd1f7a74d5a65d6fdb6f23cf4b6d ./src/api.rs
|
||||||
0db0d81da4b8161004ddcbed2096812c285ecec9d944771670e6c8edf0775175 ./src/api/assets.rs
|
0db0d81da4b8161004ddcbed2096812c285ecec9d944771670e6c8edf0775175 ./src/api/assets.rs
|
||||||
1139dacbd3f6ff94bf721eb5a60d469016fb935ddcf484d71bfeb249b72c833a ./src/api/auth.rs
|
1139dacbd3f6ff94bf721eb5a60d469016fb935ddcf484d71bfeb249b72c833a ./src/api/auth.rs
|
||||||
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
||||||
27a788efe385ad7180cb7fc7ec66c34ebd5e6062d9cdf35b32fb461e52a42863 ./src/api/configuration.rs
|
fffad7874465739cd6b508e9d7791387b864fa88420ce0f7e2fc933d129a2751 ./src/api/configuration.rs
|
||||||
19bc141286a336ad67e99079496eb0ae5126f986ab28be62b5f97758e5f6969c ./src/api/debug_tokens.rs
|
19bc141286a336ad67e99079496eb0ae5126f986ab28be62b5f97758e5f6969c ./src/api/debug_tokens.rs
|
||||||
ca21f6d5a407e2e3532899d53ba4e8682c2fe961a46d8cd870ddc459721513ef ./src/api/device_groups.rs
|
ca21f6d5a407e2e3532899d53ba4e8682c2fe961a46d8cd870ddc459721513ef ./src/api/device_groups.rs
|
||||||
8f92909dc575ea6238f4a003f8bd4056c2752fa3247b9ab226e47f875c80b916 ./src/api/devices.rs
|
8f92909dc575ea6238f4a003f8bd4056c2752fa3247b9ab226e47f875c80b916 ./src/api/devices.rs
|
||||||
@@ -110,17 +110,17 @@ ca5bc21e051222dcaf0c507e84cb3e60f52a0b612a6056847fa0aee3a4d1a672 ./src/api/flow
|
|||||||
82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs
|
82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs
|
||||||
b61b0b4f196c5e72e47cf06ca0c14df3cf8eedbdef0242a9d2af33623663489f ./src/api/groups.rs
|
b61b0b4f196c5e72e47cf06ca0c14df3cf8eedbdef0242a9d2af33623663489f ./src/api/groups.rs
|
||||||
b6d586bc42b166ac05648f3da7d9aeb88efde2e9eaab5642685eb4d9cf15f95a ./src/api/history.rs
|
b6d586bc42b166ac05648f3da7d9aeb88efde2e9eaab5642685eb4d9cf15f95a ./src/api/history.rs
|
||||||
e8edfbb725d6de650442444b0a929ad2be0bd2c2d7d227d2b3fdd99d05c94f94 ./src/api/house.rs
|
655bc8135fbe0b8fa491666942e80817a0eabc7dea694c68c2e5ebb30c8ecf75 ./src/api/house.rs
|
||||||
4ed7835ed622806124a805f54ffde7c39854f2447de0141d756d96f22446fb90 ./src/api/integrations.rs
|
4ed7835ed622806124a805f54ffde7c39854f2447de0141d756d96f22446fb90 ./src/api/integrations.rs
|
||||||
7658111f7d125db55a6e23f88157339b110b2cd1ed13ceabd8f860b42dba5101 ./src/api/middleware.rs
|
7658111f7d125db55a6e23f88157339b110b2cd1ed13ceabd8f860b42dba5101 ./src/api/middleware.rs
|
||||||
9d996d326f2ec192c79fb108a8d2c06efaa1c9634d9e9e54fa65a65524f92a75 ./src/api/openapi.rs
|
9d996d326f2ec192c79fb108a8d2c06efaa1c9634d9e9e54fa65a65524f92a75 ./src/api/openapi.rs
|
||||||
b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/public_settings.rs
|
b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/public_settings.rs
|
||||||
5b29f2e5aa2c422c5829ef89c7b59cf753883aa7f60661c95ab0f95cf676fa3f ./src/api/schedules.rs
|
5b29f2e5aa2c422c5829ef89c7b59cf753883aa7f60661c95ab0f95cf676fa3f ./src/api/schedules.rs
|
||||||
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
||||||
bb57f4b28df75ba350c9706719650fff3a689afee0cb6979c1fcae79396ac0cf ./src/api/system.rs
|
7c974a98c72092886247f67c3a1c62736898d4b279ceb9c4c306690a3c6e4294 ./src/api/system.rs
|
||||||
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
|
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
|
||||||
ddd1bfc8625b66f6e5f3d24b3122ac86c25f89b0af527e3dfce9102551a8f142 ./src/api/zones.rs
|
ddd1bfc8625b66f6e5f3d24b3122ac86c25f89b0af527e3dfce9102551a8f142 ./src/api/zones.rs
|
||||||
0b7d2ace2e8cb1dd0cb59aa6ca3e80cec14078e93530fd7b58081798585cd833 ./src/config.rs
|
32fa38d4315682cd3ef3ecdc91e571c2729b5e68b6f2bff965e43de3fdd2aeed ./src/config.rs
|
||||||
de0ce7807aebe4c0e325fbef01855e7fc1cfd117529f53f04a416fda3997673a ./src/db.rs
|
de0ce7807aebe4c0e325fbef01855e7fc1cfd117529f53f04a416fda3997673a ./src/db.rs
|
||||||
87f1614a80addde69b619421187526d7f86f893c0ac18998854f11675d6e7cca ./src/db/climate.rs
|
87f1614a80addde69b619421187526d7f86f893c0ac18998854f11675d6e7cca ./src/db/climate.rs
|
||||||
a8fe3636ac79df191e2f2c9d8c1eec1e5181e048ef8c9036cfa18f84aef1a462 ./src/db/configuration.rs
|
a8fe3636ac79df191e2f2c9d8c1eec1e5181e048ef8c9036cfa18f84aef1a462 ./src/db/configuration.rs
|
||||||
@@ -135,10 +135,10 @@ d6b9b70a04c774173c97f7426487d5e2932d7713e12ae3fb7f64cf2b4e0b3c10 ./src/db/sched
|
|||||||
28ee42d08147b0f154af818065d7d014d74ee8ddd59fdef98115f5c5368056cc ./src/db/tests.rs
|
28ee42d08147b0f154af818065d7d014d74ee8ddd59fdef98115f5c5368056cc ./src/db/tests.rs
|
||||||
f90fbb14f29a20b799b3856bdaa22812a9f0d9dfa46693f1341c160dccda8a97 ./src/db/zone_history.rs
|
f90fbb14f29a20b799b3856bdaa22812a9f0d9dfa46693f1341c160dccda8a97 ./src/db/zone_history.rs
|
||||||
0e49ea4a52017b0a8501d5101b1906a7a1e8d5158acb0bb65caa43cde2836ec0 ./src/engine.rs
|
0e49ea4a52017b0a8501d5101b1906a7a1e8d5158acb0bb65caa43cde2836ec0 ./src/engine.rs
|
||||||
a0d60ad81d024488dfec586b0d9e9f447e9a3485985604415465cacba59d645f ./src/engine/automations.rs
|
f3cc7d2cbf44180372d987d7502409547eadb1c725bcce6eeb5a26cc4277b83f ./src/engine/automations.rs
|
||||||
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
|
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
|
||||||
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
|
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
|
||||||
ad53cd7be24deb759e03cbc6472522b69ebd36d8fac35398a9b19c899d3b17f2 ./src/engine/control_plan.rs
|
315116a0dbeeba1af2f72513ea5fcc238da1b5ce9501b039c5188e44976978dc ./src/engine/control_plan.rs
|
||||||
7b48a33343d519a2ff2d81e9d757eb56a0c1beacfc0b7b8ef61f49c2ad363f9a ./src/engine/deadlines.rs
|
7b48a33343d519a2ff2d81e9d757eb56a0c1beacfc0b7b8ef61f49c2ad363f9a ./src/engine/deadlines.rs
|
||||||
519f67ab805f61afea107215f5df50562fa1669a48334cc015570952e25e773a ./src/engine/energy.rs
|
519f67ab805f61afea107215f5df50562fa1669a48334cc015570952e25e773a ./src/engine/energy.rs
|
||||||
e350aa63f3bb14ca887eaaa7555edbeae00fb584cbcaf552cc972f0c8918f5d8 ./src/engine/groups.rs
|
e350aa63f3bb14ca887eaaa7555edbeae00fb584cbcaf552cc972f0c8918f5d8 ./src/engine/groups.rs
|
||||||
@@ -151,9 +151,9 @@ c73691c3637465ab0f79aecf7f5428d0317799a8ab5329ed564cf57963bbe86e ./src/engine/r
|
|||||||
b0189f28d0f337dfd3ce0934da7a565e4a5535afc1c242214c26ed584b3b7840 ./src/engine/targets.rs
|
b0189f28d0f337dfd3ce0934da7a565e4a5535afc1c242214c26ed584b3b7840 ./src/engine/targets.rs
|
||||||
682c8b3956c657b655746cbd19ee97ee2f4cf953bc9de67d6d70a1b14f3dd746 ./src/engine/temperature.rs
|
682c8b3956c657b655746cbd19ee97ee2f4cf953bc9de67d6d70a1b14f3dd746 ./src/engine/temperature.rs
|
||||||
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
|
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
|
||||||
0404e040c5d6f388682edb3d103a658e0b963a402e4e847fc92fd0aea1c0e773 ./src/engine/tests.rs
|
bf1a3840135a71ce64148559cce0fbd570ff99d31c8972cdea7447a49f6868f7 ./src/engine/tests.rs
|
||||||
c89ff36c6a67bf4fbf68c43e18aa0a556de2dc69263fd79db376363afc741a4f ./src/engine/zone_actions.rs
|
c89ff36c6a67bf4fbf68c43e18aa0a556de2dc69263fd79db376363afc741a4f ./src/engine/zone_actions.rs
|
||||||
fb169bcc20f95abe9892367df6dd3a683584bdbc721792d6ffb9311b81ca30d9 ./src/engine/zone_control.rs
|
87ab87d434a6462a2c3f25249b42c59ecb2bca6ed700ec73e95e4613349cf16f ./src/engine/zone_control.rs
|
||||||
bfac95cec87f0df6ce562153cd3cfe6731359d703e6c592d2c2f7ba01ea62d99 ./src/error.rs
|
bfac95cec87f0df6ce562153cd3cfe6731359d703e6c592d2c2f7ba01ea62d99 ./src/error.rs
|
||||||
d681f2aa53c39f37411834c300e9200299f94ee4ea021f44f70a3ea1774bd698 ./src/home_assistant.rs
|
d681f2aa53c39f37411834c300e9200299f94ee4ea021f44f70a3ea1774bd698 ./src/home_assistant.rs
|
||||||
bcd14e4d1af3ca580f3b95bcd86164c40f762511e1b8e2501cb8a598a6d8adf4 ./src/influxdb.rs
|
bcd14e4d1af3ca580f3b95bcd86164c40f762511e1b8e2501cb8a598a6d8adf4 ./src/influxdb.rs
|
||||||
@@ -163,13 +163,13 @@ da4068295f37c23222bb13bebac022d88ba7729604a49ad25438c55e45458a49 ./src/influxdb
|
|||||||
62f9da6ef4f5fd92701d0857773f9f6236ef25356f21b9b42236abab6ba7da53 ./src/main.rs
|
62f9da6ef4f5fd92701d0857773f9f6236ef25356f21b9b42236abab6ba7da53 ./src/main.rs
|
||||||
23688a5cb68efded89471ce959328d27adff370d451bd9f378a283ab51437e76 ./src/models.rs
|
23688a5cb68efded89471ce959328d27adff370d451bd9f378a283ab51437e76 ./src/models.rs
|
||||||
e7b6e49d369aaf665a8aea2464950fd795957de0ad7528a002e11c55c7983f35 ./src/models/automation.rs
|
e7b6e49d369aaf665a8aea2464950fd795957de0ad7528a002e11c55c7983f35 ./src/models/automation.rs
|
||||||
fc5f2802a073a175a8985e56bcb87eb1ff481226d5b4eeabad73dbba1839fc45 ./src/models/control_plan.rs
|
a7a35e60318a620d411ec16ab8faecd34410ccba3310f612e89c06904a16539b ./src/models/control_plan.rs
|
||||||
06357b352ff6494d83e72796b869573a8b8f36b835985a5900ac571c922b8da9 ./src/models/defaults.rs
|
06357b352ff6494d83e72796b869573a8b8f36b835985a5900ac571c922b8da9 ./src/models/defaults.rs
|
||||||
698dcbeba72037a251c25d07c95833f121716d949f9217f1f9429e8cd40638e5 ./src/models/device.rs
|
698dcbeba72037a251c25d07c95833f121716d949f9217f1f9429e8cd40638e5 ./src/models/device.rs
|
||||||
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
||||||
6e44df6bde2f73c282095c80ac17287e5bef77d9fc1d344e440d863dae5f5031 ./src/models/history.rs
|
6e44df6bde2f73c282095c80ac17287e5bef77d9fc1d344e440d863dae5f5031 ./src/models/history.rs
|
||||||
1fee89f939018bf1f3686c15f25434f01ab96b3239164108db7f348a1f1f8d82 ./src/models/integrations.rs
|
1fee89f939018bf1f3686c15f25434f01ab96b3239164108db7f348a1f1f8d82 ./src/models/integrations.rs
|
||||||
e557ee4a443de586a7ea7dd04318694fb30d6ec053076429503e18f1bcd65c9e ./src/models/runtime.rs
|
17cac849056b677a3872e600a0d99218fb383ad5ec91cde596562df8e2f47508 ./src/models/runtime.rs
|
||||||
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
|
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
|
||||||
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
|
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
|
||||||
bc2818ce8d0dae1edfb1b98e5d1c00b193f03eeaa28e343d3532f6023288738c ./src/models/zone.rs
|
bc2818ce8d0dae1edfb1b98e5d1c00b193f03eeaa28e343d3532f6023288738c ./src/models/zone.rs
|
||||||
@@ -195,26 +195,26 @@ a677ce61d3d0f4cf358055ddbc343e73b452c68daed80798499147370819615e ./src/provider
|
|||||||
7e12e8704faa7ac15a312bcc1d714ee1c6f27c1dce0972d4ccd6e5a40c69c884 ./src/queries/maintenance.rs
|
7e12e8704faa7ac15a312bcc1d714ee1c6f27c1dce0972d4ccd6e5a40c69c884 ./src/queries/maintenance.rs
|
||||||
7da8b73a1c8c30e2a12071062d36f5393938fde0b5ffebbe4af1891da40f3233 ./src/queries/schema.rs
|
7da8b73a1c8c30e2a12071062d36f5393938fde0b5ffebbe4af1891da40f3233 ./src/queries/schema.rs
|
||||||
f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/zone_history.rs
|
f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/zone_history.rs
|
||||||
8c34b70fb5f80e3249c2e0c63686e60ce1697cf44a7e6f11b3ad6f689ce17792 ./src/state.rs
|
d8a602ed8906d77593bd140315ce1bd56716038e789f53ff31762e9b3cb7220d ./src/state.rs
|
||||||
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
||||||
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
||||||
2d227963624338f958033ec9b211b6bfa2c869ba3d7dc4f8f8125a29d343f7d9 ./web/css/styles.css
|
fd56aa620ddd63bf7f8d03f00da3b2bd53993240bb37cebdac1bdffec22d867c ./web/css/styles.css
|
||||||
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
||||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||||
92847143a358e6869ed4f21b156b10bda3f8abfcb9a12fc86256221c93be3b09 ./web/index.html
|
43f33fc21bb11df0ea149fe7fbca722a57f93890e4d9bb542ced703c1311207e ./web/index.html
|
||||||
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
||||||
e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js
|
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
|
||||||
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
||||||
81012bd88282034cdc4e876af5eb5057289f4db8505847568bc3c3701a348d4b ./web/js-dynamic/core.js
|
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
|
||||||
752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js
|
1b444068bf91741c54bd51741a95128e335219275c415fe9ce4531c80593b50f ./web/js-dynamic/dashboard.js
|
||||||
930e67432f55e8476513e808edfa9a0809a8da7479307284fcc136082a4129a4 ./web/js-dynamic/entities.js
|
930e67432f55e8476513e808edfa9a0809a8da7479307284fcc136082a4129a4 ./web/js-dynamic/entities.js
|
||||||
b4204241eecf387256364f4230b23ed50d947a362f4ceb1e528d69494e93d7cf ./web/js-dynamic/events.js
|
4612c958d7b9ccf89d6d45a11c3bc7e864bbd2ceaccc16fd7d76811ba4c74196 ./web/js-dynamic/events.js
|
||||||
43c78a3871cfdb0382081ec30b8d48650450c71b44d006414b161349ac1fa659 ./web/js-dynamic/flows.js
|
43c78a3871cfdb0382081ec30b8d48650450c71b44d006414b161349ac1fa659 ./web/js-dynamic/flows.js
|
||||||
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
||||||
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
||||||
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
||||||
30cbaebb817d5d416e1a372595b647aae42c14b9cc406c4751d0ced563243b69 ./web/js-dynamic/navigation.js
|
30cbaebb817d5d416e1a372595b647aae42c14b9cc406c4751d0ced563243b69 ./web/js-dynamic/navigation.js
|
||||||
0e398bad2cde85d4a62d8733c06e73f2b6a5b07db791d4570feba38c7ff8741f ./web/js-dynamic/realtime.js
|
0c93119b3d5985d3440e1525d908efecd1567ab2caa0e6cc3babeecd87bfde5c ./web/js-dynamic/realtime.js
|
||||||
1b667f0a8bce824b27f56a8d2f44ff8feef9a011289591dc629ee288db70d137 ./web/js-dynamic/router.js
|
1b667f0a8bce824b27f56a8d2f44ff8feef9a011289591dc629ee288db70d137 ./web/js-dynamic/router.js
|
||||||
c2710fd9c1f2b31878c1e25ceb02d74ae30456c1bbb400e69fcf7619ecc72bc2 ./web/js-dynamic/select-ui.js
|
c2710fd9c1f2b31878c1e25ceb02d74ae30456c1bbb400e69fcf7619ecc72bc2 ./web/js-dynamic/select-ui.js
|
||||||
bc42b4a0dd0d20239ce22ea1dff09b3f94e8c0e1395e01251a4585a051968a8f ./web/js-dynamic/settings-ui.js
|
bc42b4a0dd0d20239ce22ea1dff09b3f94e8c0e1395e01251a4585a051968a8f ./web/js-dynamic/settings-ui.js
|
||||||
|
|||||||
+23
-3
@@ -1,6 +1,6 @@
|
|||||||
# GREE Controller API reference
|
# GREE Controller API reference
|
||||||
|
|
||||||
HTTP and WebSocket API for GREE Controller **0.15.14**.
|
HTTP and WebSocket API for GREE Controller **0.15.15**.
|
||||||
|
|
||||||
[← Main documentation](../README.md)
|
[← Main documentation](../README.md)
|
||||||
|
|
||||||
@@ -163,6 +163,7 @@ Common statuses:
|
|||||||
| POST | `/api/groups/{id}/control` | Group control enable/mode/preset/custom-temperature control. |
|
| POST | `/api/groups/{id}/control` | Group control enable/mode/preset/custom-temperature control. |
|
||||||
| POST | `/api/house/control` | Set global thermostat mode. |
|
| POST | `/api/house/control` | Set global thermostat mode. |
|
||||||
| POST | `/api/house/power` | Bulk ON/OFF for all thermostats and enabled units; no persistent global gate. |
|
| POST | `/api/house/power` | Bulk ON/OFF for all thermostats and enabled units; no persistent global gate. |
|
||||||
|
| POST | `/api/house/emergency-stop` | Persistently pause/resume automation; activation sends one-shot OFF to enabled units. |
|
||||||
| POST | `/api/house/preset` | Set/clear whole-house preset override. |
|
| POST | `/api/house/preset` | Set/clear whole-house preset override. |
|
||||||
|
|
||||||
### Schedules and automations
|
### Schedules and automations
|
||||||
@@ -225,6 +226,7 @@ Common statuses:
|
|||||||
| POST | `/api/integrations/home-assistant/house/control` | Restricted house mode. |
|
| POST | `/api/integrations/home-assistant/house/control` | Restricted house mode. |
|
||||||
| POST | `/api/integrations/home-assistant/house/preset` | Restricted house preset. |
|
| POST | `/api/integrations/home-assistant/house/preset` | Restricted house preset. |
|
||||||
| POST | `/api/integrations/home-assistant/house/power` | Restricted bulk all-thermostat/all-unit power action. |
|
| POST | `/api/integrations/home-assistant/house/power` | Restricted bulk all-thermostat/all-unit power action. |
|
||||||
|
| POST | `/api/integrations/home-assistant/house/emergency-stop` | Restricted persistent emergency automation pause/resume. |
|
||||||
| POST | `/api/integrations/home-assistant/zones/{id}/control` | Restricted thermostat-zone control. |
|
| POST | `/api/integrations/home-assistant/zones/{id}/control` | Restricted thermostat-zone control. |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -241,7 +243,7 @@ Response:
|
|||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"name": "gree-controller",
|
"name": "gree-controller",
|
||||||
"version": "0.15.14",
|
"version": "0.15.15",
|
||||||
"uptime_seconds": 1234,
|
"uptime_seconds": 1234,
|
||||||
"control_ready": true,
|
"control_ready": true,
|
||||||
"time": "2026-08-30T06:54:00Z"
|
"time": "2026-08-30T06:54:00Z"
|
||||||
@@ -304,7 +306,7 @@ Returns the initial Web UI snapshot:
|
|||||||
"control_plan": {"generated_at": "2026-09-04T08:00:00Z", "zones": [], "rules": []},
|
"control_plan": {"generated_at": "2026-09-04T08:00:00Z", "zones": [], "rules": []},
|
||||||
"control_plan_revision": 42,
|
"control_plan_revision": 42,
|
||||||
"system": {
|
"system": {
|
||||||
"version": "0.15.14",
|
"version": "0.15.15",
|
||||||
"uptime_seconds": 1234,
|
"uptime_seconds": 1234,
|
||||||
"auth_required": false,
|
"auth_required": false,
|
||||||
"control_ready": true,
|
"control_ready": true,
|
||||||
@@ -845,6 +847,20 @@ Response includes:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `POST /api/house/emergency-stop`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`true` persistently pauses thermostat/schedule/automation execution, clears stale compressor-protection tasks, and makes a one-shot best-effort OFF request to every enabled unit. The pause survives controller restarts, but startup does **not** replay OFF commands. Manual/direct device control remains available while automation is paused.
|
||||||
|
|
||||||
|
`false` releases only the automation safety gate. It does not force devices ON; the controller immediately evaluates current schedules, temperatures and ownership again.
|
||||||
|
|
||||||
|
Response includes `active`, `since`, `changed`, `failed`, and `cleared_queues`.
|
||||||
|
|
||||||
### `POST /api/house/preset`
|
### `POST /api/house/preset`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -1489,6 +1505,10 @@ Same `{ "preset": "auto|comfort|sleep|away" }` semantics.
|
|||||||
|
|
||||||
Same `{ "power": true|false }` semantics.
|
Same `{ "power": true|false }` semantics.
|
||||||
|
|
||||||
|
### `POST /api/integrations/home-assistant/house/emergency-stop`
|
||||||
|
|
||||||
|
Same `{ "active": true|false }` emergency-stop semantics as the administrator endpoint.
|
||||||
|
|
||||||
### `POST /api/integrations/home-assistant/zones/{id}/control`
|
### `POST /api/integrations/home-assistant/zones/{id}/control`
|
||||||
|
|
||||||
Same `ZoneControlPatch` thermostat semantics as the normal zone control endpoint. The internal source is recorded as Home Assistant thermostat control.
|
Same `ZoneControlPatch` thermostat semantics as the normal zone control endpoint. The internal source is recorded as Home Assistant thermostat control.
|
||||||
|
|||||||
+127
-2
@@ -2,7 +2,7 @@
|
|||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "GREE Controller API",
|
"title": "GREE Controller API",
|
||||||
"version": "0.15.14",
|
"version": "0.15.15",
|
||||||
"summary": "Local HTTP/WebSocket API for GREE Controller",
|
"summary": "Local HTTP/WebSocket API for GREE Controller",
|
||||||
"description": "Local API used by the GREE Controller Web UI and Home Assistant integration.",
|
"description": "Local API used by the GREE Controller Web UI and Home Assistant integration.",
|
||||||
"license": {
|
"license": {
|
||||||
@@ -1814,6 +1814,62 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/house/emergency-stop": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"House"
|
||||||
|
],
|
||||||
|
"summary": "Emergency automation stop",
|
||||||
|
"description": "Persistently pauses thermostat and automation execution. Activating the stop first stores the safety gate, clears pending compressor-protection tasks, then makes a one-shot best-effort OFF request to every enabled unit. The pause survives controller restarts, but OFF commands are not replayed on startup. Releasing the stop does not force devices ON; current control conditions are evaluated again.",
|
||||||
|
"operationId": "setHouseEmergencyStop",
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HouseEmergencyStopPatch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerToken": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ApiTokenHeader": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Emergency stop state and one-shot OFF result",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"$ref": "#/components/responses/BadRequest"
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"$ref": "#/components/responses/Unauthorized"
|
||||||
|
},
|
||||||
|
"502": {
|
||||||
|
"$ref": "#/components/responses/BadGateway"
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"$ref": "#/components/responses/InternalError"
|
||||||
|
},
|
||||||
|
"424": {
|
||||||
|
"$ref": "#/components/responses/FailedDependency"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/house/preset": {
|
"/api/house/preset": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -3937,6 +3993,62 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/integrations/home-assistant/house/emergency-stop": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Home Assistant"
|
||||||
|
],
|
||||||
|
"summary": "Emergency automation stop from Home Assistant",
|
||||||
|
"description": "Restricted Home Assistant surface for the same persistent emergency-stop semantics as the administrator endpoint. Activation pauses automatic control across restarts and sends OFF only once; release resumes evaluation without forcing devices ON.",
|
||||||
|
"operationId": "haSetHouseEmergencyStop",
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HouseEmergencyStopPatch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerToken": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ApiTokenHeader": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Emergency stop state and one-shot OFF result",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"$ref": "#/components/responses/BadRequest"
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"$ref": "#/components/responses/Unauthorized"
|
||||||
|
},
|
||||||
|
"502": {
|
||||||
|
"$ref": "#/components/responses/BadGateway"
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"$ref": "#/components/responses/InternalError"
|
||||||
|
},
|
||||||
|
"424": {
|
||||||
|
"$ref": "#/components/responses/FailedDependency"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/integrations/home-assistant/zones/{id}/control": {
|
"/api/integrations/home-assistant/zones/{id}/control": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -5967,7 +6079,7 @@
|
|||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "0.15.14"
|
"example": "0.15.15"
|
||||||
},
|
},
|
||||||
"uptime_seconds": {
|
"uptime_seconds": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@@ -7412,6 +7524,19 @@
|
|||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
|
"HouseEmergencyStopPatch": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"active": {
|
||||||
|
"type": "boolean",
|
||||||
|
"example": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"active"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
"HousePresetPatch": {
|
"HousePresetPatch": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "gree_controller",
|
"domain": "gree_controller",
|
||||||
"name": "GREE Controller",
|
"name": "GREE Controller",
|
||||||
"version": "0.15.14",
|
"version": "0.15.15",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
- Adds a green return-to-normal action that releases the safety gate and immediately re-evaluates current conditions without forcing devices ON.
|
||||||
|
- Expands Control plan with a full-width “why the controller is acting this way” section showing per-zone control source/reason and the most recently triggered automations with trigger context.
|
||||||
|
- Keeps emergency-stop state operationally separate from configuration backup/import, and documents the new `POST /api/house/emergency-stop` endpoint in the API/OpenAPI contract.
|
||||||
|
|
||||||
## 0.15.14
|
## 0.15.14
|
||||||
|
|
||||||
- Fixes Home Assistant energy sampling in the packaged add-on when authentication comes from `SUPERVISOR_TOKEN`; background sampling no longer requires persisted manual URL/token fields.
|
- Fixes Home Assistant energy sampling in the packaged add-on when authentication comes from `SUPERVISOR_TOKEN`; background sampling no longer requires persisted manual URL/token fields.
|
||||||
|
|||||||
@@ -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`.
|
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.14 fixes Home Assistant Supervisor authentication detection for background energy sampling and centralizes runtime HA readiness checks, so the add-on uses Supervisor credentials consistently across entity discovery, sensor reads, Flow integrations and energy collection.
|
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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: "GREE Controller"
|
name: "GREE Controller"
|
||||||
version: "0.15.14"
|
version: "0.15.15"
|
||||||
slug: "gree_controller"
|
slug: "gree_controller"
|
||||||
description: "Local GREE HVAC controller with Web UI, API, and Home Assistant integration"
|
description: "Local GREE HVAC controller with Web UI, API, and Home Assistant integration"
|
||||||
url: "https://git.linuxiarz.pl/gru/gree-controller-ha-addon/"
|
url: "https://git.linuxiarz.pl/gru/gree-controller-ha-addon/"
|
||||||
|
|||||||
@@ -441,9 +441,33 @@
|
|||||||
"plan.loading": "Loading current automation plan…",
|
"plan.loading": "Loading current automation plan…",
|
||||||
"plan.rules": "Extra automations",
|
"plan.rules": "Extra automations",
|
||||||
"plan.ruleCount": "{count} active rules",
|
"plan.ruleCount": "{count} active rules",
|
||||||
|
"plan.ruleCountPaused": "Rules paused by emergency STOP: {count}",
|
||||||
"plan.event": "Change",
|
"plan.event": "Change",
|
||||||
"plan.groupSummary": "Group zones: {zones} · requesting action: {demand}",
|
"plan.groupSummary": "Group zones: {zones} · requesting action: {demand}",
|
||||||
"plan.groupOff": "Group off — group units are powered down and group ownership is released.",
|
"plan.groupOff": "Group off — group units are powered down and group ownership is released.",
|
||||||
|
"plan.reasonsEyebrow": "Control reason",
|
||||||
|
"plan.reasonsTitle": "Why the controller is acting this way",
|
||||||
|
"plan.currentReasons": "Current reasons",
|
||||||
|
"plan.recentTriggers": "Recently triggered automations",
|
||||||
|
"plan.noReasons": "No active control reasons.",
|
||||||
|
"plan.reasonEmergency": "Automation is paused by the emergency stop.",
|
||||||
|
"plan.reasonManual": "Direct manual device control has priority.",
|
||||||
|
"plan.reasonDisabled": "The zone is disabled.",
|
||||||
|
"plan.reasonOffline": "The device is offline or has a communication error.",
|
||||||
|
"plan.reasonNoIntent": "No active schedule or explicit thermostat request.",
|
||||||
|
"plan.reasonLocalOff": "The local thermostat explicitly keeps this zone off.",
|
||||||
|
"plan.reasonLocalOn": "The local thermostat owns this zone.",
|
||||||
|
"plan.reasonHeatDemand": "{current} is below the {target} target — heating demand is active.",
|
||||||
|
"plan.reasonCoolDemand": "{current} is above the {target} target — cooling demand is active.",
|
||||||
|
"plan.reasonTargetSatisfied": "Temperature {current}; target {target} — no extra demand is required.",
|
||||||
|
"plan.reasonSchedule": "Active schedule: {schedule}.",
|
||||||
|
"plan.reasonWaiting": "The controller is waiting for a condition that requires action.",
|
||||||
|
"plan.reasonSourceSchedule": "Schedule: {schedule}",
|
||||||
|
"plan.reasonSourceManual": "Manual control",
|
||||||
|
"plan.reasonSourceThermostat": "Local thermostat",
|
||||||
|
"plan.reasonSourceAutomation": "Automation",
|
||||||
|
"plan.reasonTriggerReading": "{trigger} · now {current}",
|
||||||
|
"plan.reasonLastTriggered": "Last triggered: {when}",
|
||||||
"zones.enable": "Enable zone",
|
"zones.enable": "Enable zone",
|
||||||
"zones.disable": "Disable zone",
|
"zones.disable": "Disable zone",
|
||||||
"history.90d": "90 days",
|
"history.90d": "90 days",
|
||||||
@@ -626,6 +650,16 @@
|
|||||||
"house.powerOnDone": "All thermostats enabled and ON sent to all units",
|
"house.powerOnDone": "All thermostats enabled and ON sent to all units",
|
||||||
"house.powerOffDone": "All units turned off; thermostats remain locally off",
|
"house.powerOffDone": "All units turned off; thermostats remain locally off",
|
||||||
"house.powerPartial": "Could not change power on {count} devices",
|
"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.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.",
|
||||||
|
"house.emergencyActive": "Emergency stop is active",
|
||||||
|
"house.emergencyActiveHint": "The state survives controller restarts. After a restart automation remains blocked, but the OFF command is not replayed.",
|
||||||
|
"house.emergencyStopped": "Emergency stop active — automation paused",
|
||||||
|
"house.emergencyStoppedPartial": "Emergency stop is active, but {count} devices could not be powered off",
|
||||||
|
"house.emergencyResumed": "Normal automation mode restored",
|
||||||
"preset.manual": "Manual",
|
"preset.manual": "Manual",
|
||||||
"nav.groups": "Groups",
|
"nav.groups": "Groups",
|
||||||
"groups.eyebrow": "Group control",
|
"groups.eyebrow": "Group control",
|
||||||
|
|||||||
@@ -441,9 +441,33 @@
|
|||||||
"plan.loading": "Wczytywanie aktualnego planu automatyki…",
|
"plan.loading": "Wczytywanie aktualnego planu automatyki…",
|
||||||
"plan.rules": "Dodatkowe automatyzacje",
|
"plan.rules": "Dodatkowe automatyzacje",
|
||||||
"plan.ruleCount": "Aktywne reguły: {count}",
|
"plan.ruleCount": "Aktywne reguły: {count}",
|
||||||
|
"plan.ruleCountPaused": "Reguły wstrzymane przez STOP: {count}",
|
||||||
"plan.event": "Zmiana",
|
"plan.event": "Zmiana",
|
||||||
"plan.groupSummary": "Strefy w grupie: {zones} · żądające działania: {demand}",
|
"plan.groupSummary": "Strefy w grupie: {zones} · żądające działania: {demand}",
|
||||||
"plan.groupOff": "Grupa wyłączona — jednostki grupy są wyłączone, a blokada grupowa jest zwolniona.",
|
"plan.groupOff": "Grupa wyłączona — jednostki grupy są wyłączone, a blokada grupowa jest zwolniona.",
|
||||||
|
"plan.reasonsEyebrow": "Powód sterowania",
|
||||||
|
"plan.reasonsTitle": "Dlaczego sterownik działa właśnie tak",
|
||||||
|
"plan.currentReasons": "Bieżące powody",
|
||||||
|
"plan.recentTriggers": "Ostatnio wyzwolone automatyzacje",
|
||||||
|
"plan.noReasons": "Brak aktywnych powodów sterowania.",
|
||||||
|
"plan.reasonEmergency": "Automatyka wstrzymana przez STOP awaryjny.",
|
||||||
|
"plan.reasonManual": "Ręczne sterowanie urządzeniem ma pierwszeństwo.",
|
||||||
|
"plan.reasonDisabled": "Strefa jest wyłączona.",
|
||||||
|
"plan.reasonOffline": "Urządzenie jest offline lub ma błąd komunikacji.",
|
||||||
|
"plan.reasonNoIntent": "Brak aktywnego harmonogramu lub jawnego żądania termostatu.",
|
||||||
|
"plan.reasonLocalOff": "Lokalny termostat jawnie utrzymuje strefę wyłączoną.",
|
||||||
|
"plan.reasonLocalOn": "Lokalny termostat przejął sterowanie strefą.",
|
||||||
|
"plan.reasonHeatDemand": "{current} jest poniżej celu {target} — aktywne żądanie grzania.",
|
||||||
|
"plan.reasonCoolDemand": "{current} jest powyżej celu {target} — aktywne żądanie chłodzenia.",
|
||||||
|
"plan.reasonTargetSatisfied": "Temperatura {current}; cel {target} — brak potrzeby zwiększania mocy.",
|
||||||
|
"plan.reasonSchedule": "Aktywny harmonogram: {schedule}.",
|
||||||
|
"plan.reasonWaiting": "Sterownik czeka na warunek wymagający działania.",
|
||||||
|
"plan.reasonSourceSchedule": "Harmonogram: {schedule}",
|
||||||
|
"plan.reasonSourceManual": "Sterowanie ręczne",
|
||||||
|
"plan.reasonSourceThermostat": "Termostat lokalny",
|
||||||
|
"plan.reasonSourceAutomation": "Automatyka",
|
||||||
|
"plan.reasonTriggerReading": "{trigger} · teraz {current}",
|
||||||
|
"plan.reasonLastTriggered": "Ostatnie wyzwolenie: {when}",
|
||||||
"zones.enable": "Włącz strefę",
|
"zones.enable": "Włącz strefę",
|
||||||
"zones.disable": "Wyłącz strefę",
|
"zones.disable": "Wyłącz strefę",
|
||||||
"history.90d": "90 dni",
|
"history.90d": "90 dni",
|
||||||
@@ -626,6 +650,16 @@
|
|||||||
"house.powerOnDone": "Włączono wszystkie termostaty i wysłano polecenie włączenia jednostek",
|
"house.powerOnDone": "Włączono wszystkie termostaty i wysłano polecenie włączenia jednostek",
|
||||||
"house.powerOffDone": "Wyłączono wszystkie jednostki; termostaty pozostają lokalnie wyłączone",
|
"house.powerOffDone": "Wyłączono wszystkie jednostki; termostaty pozostają lokalnie wyłączone",
|
||||||
"house.powerPartial": "Nie udało się zmienić zasilania {count} urządzeń",
|
"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.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.",
|
||||||
|
"house.emergencyActive": "STOP awaryjny jest aktywny",
|
||||||
|
"house.emergencyActiveHint": "Stan przetrwa restart kontrolera. Po restarcie automatyka nadal pozostaje zablokowana, ale polecenie OFF nie jest wysyłane ponownie.",
|
||||||
|
"house.emergencyStopped": "STOP awaryjny aktywny — automatyka wstrzymana",
|
||||||
|
"house.emergencyStoppedPartial": "STOP awaryjny aktywny, ale nie udało się wyłączyć {count} urządzeń",
|
||||||
|
"house.emergencyResumed": "Przywrócono normalny tryb automatyki",
|
||||||
"preset.manual": "Ręcznie",
|
"preset.manual": "Ręcznie",
|
||||||
"nav.groups": "Grupy",
|
"nav.groups": "Grupy",
|
||||||
"groups.eyebrow": "Sterowanie grupami",
|
"groups.eyebrow": "Sterowanie grupami",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Developer smoke/integration tests for GREE Controller API 0.15.14.
|
"""Developer smoke/integration tests for GREE Controller API 0.15.15.
|
||||||
|
|
||||||
Default mode is read-only and safe to run against a real controller.
|
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
|
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_BASE_URL = os.environ.get("GREE_API_URL", "http://127.0.0.1:8787")
|
||||||
DEFAULT_TOKEN = os.environ.get("GREE_API_TOKEN", "")
|
DEFAULT_TOKEN = os.environ.get("GREE_API_TOKEN", "")
|
||||||
DEFAULT_EXPECTED_VERSION = "0.15.14"
|
DEFAULT_EXPECTED_VERSION = "0.15.15"
|
||||||
|
|
||||||
SETTINGS_PATHS: dict[str, set[str]] = {
|
SETTINGS_PATHS: dict[str, set[str]] = {
|
||||||
"/api/settings/application": {"simulator_enabled"},
|
"/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")
|
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("public health", lambda: test_health(client, args.expected_version))
|
||||||
runner.run("OpenAPI 0.15.14 contract", lambda: test_openapi(client, args.expected_version))
|
runner.run("OpenAPI 0.15.15 contract", lambda: test_openapi(client, args.expected_version))
|
||||||
runner.run("bootstrap snapshot contract", lambda: test_bootstrap_contract(client))
|
runner.run("bootstrap snapshot contract", lambda: test_bootstrap_contract(client))
|
||||||
runner.run("protected API requires auth", lambda: test_protected_auth(client))
|
runner.run("protected API requires auth", lambda: test_protected_auth(client))
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ pub fn router(state: AppState) -> Router {
|
|||||||
.route("/api/groups/{id}/control", post(update_group_control))
|
.route("/api/groups/{id}/control", post(update_group_control))
|
||||||
.route("/api/house/control", post(update_house_control))
|
.route("/api/house/control", post(update_house_control))
|
||||||
.route("/api/house/power", post(update_house_power))
|
.route("/api/house/power", post(update_house_power))
|
||||||
|
.route("/api/house/emergency-stop", post(update_house_emergency_stop))
|
||||||
.route("/api/house/preset", post(update_house_preset))
|
.route("/api/house/preset", post(update_house_preset))
|
||||||
.route("/api/schedules", get(list_schedules).post(create_schedule))
|
.route("/api/schedules", get(list_schedules).post(create_schedule))
|
||||||
.route(
|
.route(
|
||||||
@@ -297,6 +298,10 @@ pub fn router(state: AppState) -> Router {
|
|||||||
"/api/integrations/home-assistant/house/power",
|
"/api/integrations/home-assistant/house/power",
|
||||||
post(update_house_power),
|
post(update_house_power),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/api/integrations/home-assistant/house/emergency-stop",
|
||||||
|
post(update_house_emergency_stop),
|
||||||
|
)
|
||||||
.route(
|
.route(
|
||||||
"/api/integrations/home-assistant/zones/{id}/control",
|
"/api/integrations/home-assistant/zones/{id}/control",
|
||||||
post(update_home_assistant_zone_control),
|
post(update_home_assistant_zone_control),
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ async fn export_configuration(
|
|||||||
// including configuration exports. Import can reuse the already configured password on the
|
// including configuration exports. Import can reuse the already configured password on the
|
||||||
// target controller when account/region match.
|
// target controller when account/region match.
|
||||||
export.settings.gree_cloud.password.clear();
|
export.settings.gree_cloud.password.clear();
|
||||||
|
// Emergency STOP is live safety state, not configuration. Never transfer it in backups:
|
||||||
|
// exporting/importing configuration must not arm or release the controller safety gate.
|
||||||
|
export.settings.emergency_stop_enabled = false;
|
||||||
|
export.settings.emergency_stop_since = None;
|
||||||
Ok(Json(export))
|
Ok(Json(export))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,6 +962,14 @@ async fn import_configuration(
|
|||||||
let _resource_guards =
|
let _resource_guards =
|
||||||
lock_configuration_resources(&state, ¤t_zones, ¤t_devices, &export).await;
|
lock_configuration_resources(&state, ¤t_zones, ¤t_devices, &export).await;
|
||||||
|
|
||||||
|
// Keep the operational safety gate independent from configuration replacement. The locks
|
||||||
|
// above serialize this snapshot against the emergency-stop endpoint.
|
||||||
|
{
|
||||||
|
let settings = state.settings.read().await;
|
||||||
|
export.settings.emergency_stop_enabled = settings.emergency_stop_enabled;
|
||||||
|
export.settings.emergency_stop_since = settings.emergency_stop_since.clone();
|
||||||
|
}
|
||||||
|
|
||||||
power_off_detached_devices(&state, ¤t_zones, &export).await?;
|
power_off_detached_devices(&state, ¤t_zones, &export).await?;
|
||||||
sanitize_configuration_runtime(&mut export);
|
sanitize_configuration_runtime(&mut export);
|
||||||
state
|
state
|
||||||
|
|||||||
@@ -106,6 +106,38 @@ async fn command_all_enabled_devices_power(
|
|||||||
Ok(failed)
|
Ok(failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn clear_all_automation_compressor_queues(state: &AppState) -> Result<usize, AppError> {
|
||||||
|
let mut zone_ids: Vec<String> = state
|
||||||
|
.db
|
||||||
|
.list_zones()?
|
||||||
|
.into_iter()
|
||||||
|
.map(|zone| zone.id)
|
||||||
|
.collect();
|
||||||
|
zone_ids.sort();
|
||||||
|
zone_ids.dedup();
|
||||||
|
let mut changed = 0usize;
|
||||||
|
for zone_id in zone_ids {
|
||||||
|
let _zone_guard = state.lock_zone_operation(&zone_id).await;
|
||||||
|
let Some(mut zone) = state.db.get_zone(&zone_id)? else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if zone.compressor_pending_action.is_none()
|
||||||
|
&& zone.compressor_cancelled_action.is_none()
|
||||||
|
&& zone.lockout_until.is_none()
|
||||||
|
&& zone.lockout_reason.is_none()
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
engine::rearm_compressor_queue(&mut zone);
|
||||||
|
zone.revision = zone.revision.saturating_add(1);
|
||||||
|
zone.updated_at = Utc::now();
|
||||||
|
state.db.save_zone(&zone)?;
|
||||||
|
state.broadcast("zone.updated", serde_json::to_value(&zone)?);
|
||||||
|
changed += 1;
|
||||||
|
}
|
||||||
|
Ok(changed)
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_house_control(
|
async fn update_house_control(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(input): Json<HouseControlPatch>,
|
Json(input): Json<HouseControlPatch>,
|
||||||
@@ -155,6 +187,96 @@ struct HousePowerPatch {
|
|||||||
power: bool,
|
power: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct HouseEmergencyStopPatch {
|
||||||
|
active: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_house_emergency_stop(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Json(input): Json<HouseEmergencyStopPatch>,
|
||||||
|
) -> Result<Json<Value>, AppError> {
|
||||||
|
// Automation execution already uses this lock before committing an action. Taking it first
|
||||||
|
// gives the emergency stop a clean barrier: an in-flight action finishes, then no newer
|
||||||
|
// automatic action can cross the persisted safety flag.
|
||||||
|
let _automation_guard = state.lock_automation_operation().await;
|
||||||
|
let _house_guard = state.lock_house_operation().await;
|
||||||
|
// Persist the safety gate while automatic control is serialized. Once the flag is stored,
|
||||||
|
// neither the background regulator nor an immediate thermostat run can emit automation
|
||||||
|
// commands until the user explicitly resumes normal operation.
|
||||||
|
let cycle_guard = state.lock_zone_control_cycle().await;
|
||||||
|
let (changed, emergency_stop_since) = {
|
||||||
|
let mut settings = state.settings.write().await;
|
||||||
|
let changed = settings.emergency_stop_enabled != input.active;
|
||||||
|
if changed {
|
||||||
|
settings.emergency_stop_enabled = input.active;
|
||||||
|
settings.emergency_stop_since = if input.active { Some(Utc::now()) } else { None };
|
||||||
|
state.db.save_runtime_settings(&settings)?;
|
||||||
|
}
|
||||||
|
(changed, settings.emergency_stop_since.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
// OFF is intentionally a one-shot side effect of activating the emergency stop. The
|
||||||
|
// persistent flag survives restarts, but startup never replays physical commands from it.
|
||||||
|
let mut failed = Vec::new();
|
||||||
|
let mut cleared_queues = 0usize;
|
||||||
|
if input.active && changed {
|
||||||
|
cleared_queues = clear_all_automation_compressor_queues(&state).await?;
|
||||||
|
failed = command_all_enabled_devices_power(&state, false, "house_emergency_stop").await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = json!({
|
||||||
|
"active": input.active,
|
||||||
|
"since": emergency_stop_since,
|
||||||
|
"changed": changed,
|
||||||
|
"failed": failed,
|
||||||
|
"cleared_queues": cleared_queues,
|
||||||
|
});
|
||||||
|
state.broadcast("house.emergency_stop_changed", payload.clone());
|
||||||
|
drop(cycle_guard);
|
||||||
|
state.wake_zone_control();
|
||||||
|
|
||||||
|
// Resuming does not force any unit ON. It only releases the persistent safety gate and
|
||||||
|
// immediately re-evaluates current schedules, temperatures and ownership from fresh state.
|
||||||
|
if !input.active
|
||||||
|
&& changed
|
||||||
|
&& state.initial_device_sync_complete.load(Ordering::Acquire)
|
||||||
|
{
|
||||||
|
if let Err(err) = engine::run_zone_control_now(&state).await {
|
||||||
|
state.log(
|
||||||
|
"error",
|
||||||
|
"house.emergency_resume_control_error",
|
||||||
|
&err.to_string(),
|
||||||
|
json!({"active": false}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.log(
|
||||||
|
"info",
|
||||||
|
if input.active {
|
||||||
|
"house.emergency_stop_activated"
|
||||||
|
} else {
|
||||||
|
"house.emergency_stop_released"
|
||||||
|
},
|
||||||
|
if input.active {
|
||||||
|
"Emergency stop activated; automatic climate control paused"
|
||||||
|
} else {
|
||||||
|
"Emergency stop released; automatic climate control resumed"
|
||||||
|
},
|
||||||
|
json!({
|
||||||
|
"active": input.active,
|
||||||
|
"changed": changed,
|
||||||
|
"failed_devices": payload["failed"].as_array().map(Vec::len).unwrap_or(0),
|
||||||
|
"cleared_queues": cleared_queues,
|
||||||
|
"persistent_across_restart": true,
|
||||||
|
"off_replayed_on_restart": false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Json(payload))
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_house_power(
|
async fn update_house_power(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(input): Json<HousePowerPatch>,
|
Json(input): Json<HousePowerPatch>,
|
||||||
|
|||||||
+14
-3
@@ -1,6 +1,8 @@
|
|||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
struct HouseSnapshot {
|
struct HouseSnapshot {
|
||||||
mode: String,
|
mode: String,
|
||||||
|
emergency_stop_enabled: bool,
|
||||||
|
emergency_stop_since: Option<chrono::DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
@@ -140,9 +142,14 @@ async fn bootstrap(State(state): State<AppState>) -> Result<Json<BootstrapRespon
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn build_bootstrap(state: &AppState) -> Result<BootstrapResponse, AppError> {
|
async fn build_bootstrap(state: &AppState) -> Result<BootstrapResponse, AppError> {
|
||||||
let (settings, house_mode) = {
|
let (settings, house_mode, emergency_stop_enabled, emergency_stop_since) = {
|
||||||
let settings = state.settings.read().await;
|
let settings = state.settings.read().await;
|
||||||
(settings_snapshot(&settings), settings.house_mode.clone())
|
(
|
||||||
|
settings_snapshot(&settings),
|
||||||
|
settings.house_mode.clone(),
|
||||||
|
settings.emergency_stop_enabled,
|
||||||
|
settings.emergency_stop_since.clone(),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let devices = state.db.list_devices()?;
|
let devices = state.db.list_devices()?;
|
||||||
let device_groups = state.db.list_device_groups()?;
|
let device_groups = state.db.list_device_groups()?;
|
||||||
@@ -166,7 +173,11 @@ async fn build_bootstrap(state: &AppState) -> Result<BootstrapResponse, AppError
|
|||||||
flows: state.db.list_flows()?,
|
flows: state.db.list_flows()?,
|
||||||
access_tokens: state.db.list_api_tokens()?,
|
access_tokens: state.db.list_api_tokens()?,
|
||||||
settings,
|
settings,
|
||||||
house: HouseSnapshot { mode: house_mode },
|
house: HouseSnapshot {
|
||||||
|
mode: house_mode,
|
||||||
|
emergency_stop_enabled,
|
||||||
|
emergency_stop_since,
|
||||||
|
},
|
||||||
outdoor_temperature: *state.outdoor_temperature.read().await,
|
outdoor_temperature: *state.outdoor_temperature.read().await,
|
||||||
control_plan: control_plan.plan.as_ref().clone(),
|
control_plan: control_plan.plan.as_ref().clone(),
|
||||||
control_plan_revision: control_plan.revision,
|
control_plan_revision: control_plan.revision,
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ impl Config {
|
|||||||
.unwrap_or(3)
|
.unwrap_or(3)
|
||||||
.clamp(1, 10),
|
.clamp(1, 10),
|
||||||
house_mode: env::var("GREE_CONTROLLER_HOUSE_MODE").unwrap_or_else(|_| "cool".into()),
|
house_mode: env::var("GREE_CONTROLLER_HOUSE_MODE").unwrap_or_else(|_| "cool".into()),
|
||||||
|
emergency_stop_enabled: false,
|
||||||
|
emergency_stop_since: None,
|
||||||
control_strategy: "setpoint".into(),
|
control_strategy: "setpoint".into(),
|
||||||
outdoor_assist_enabled: env_bool("GREE_CONTROLLER_OUTDOOR_ASSIST_ENABLED")
|
outdoor_assist_enabled: env_bool("GREE_CONTROLLER_OUTDOOR_ASSIST_ENABLED")
|
||||||
.unwrap_or(true),
|
.unwrap_or(true),
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ fn device_has_thermostat_zone(device_id: &str, zones: &[Zone]) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn run_automations(state: &AppState) -> Result<()> {
|
async fn run_automations(state: &AppState) -> Result<()> {
|
||||||
|
if state.settings.read().await.emergency_stop_enabled {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
let devices = state.db.list_devices()?;
|
let devices = state.db.list_devices()?;
|
||||||
let mut automations = state.db.list_automations()?;
|
let mut automations = state.db.list_automations()?;
|
||||||
// Stable arbitration for same-cycle conflicts: the oldest configured rule wins, then ID.
|
// Stable arbitration for same-cycle conflicts: the oldest configured rule wins, then ID.
|
||||||
@@ -51,6 +54,11 @@ async fn run_automations(state: &AppState) -> Result<()> {
|
|||||||
// changed since this cycle snapshot was taken, skip it now and evaluate the new
|
// changed since this cycle snapshot was taken, skip it now and evaluate the new
|
||||||
// definition on the next cycle instead of firing stale configuration.
|
// definition on the next cycle instead of firing stale configuration.
|
||||||
let _automation_guard = state.lock_automation_operation().await;
|
let _automation_guard = state.lock_automation_operation().await;
|
||||||
|
// The emergency-stop handler takes the same lock before persisting its safety flag.
|
||||||
|
// Re-check here so a rule evaluated immediately before STOP can never execute after it.
|
||||||
|
if state.settings.read().await.emergency_stop_enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let Some(latest_item) = state.db.get_automation(&item.id)? else { continue; };
|
let Some(latest_item) = state.db.get_automation(&item.id)? else { continue; };
|
||||||
if latest_item.updated_at != item.updated_at { continue; }
|
if latest_item.updated_at != item.updated_at { continue; }
|
||||||
item = latest_item;
|
item = latest_item;
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ async fn control_plan_cache_loop(state: AppState) {
|
|||||||
|
|
||||||
pub async fn build_control_plan(state: &AppState) -> Result<ControlPlan, AppError> {
|
pub async fn build_control_plan(state: &AppState) -> Result<ControlPlan, AppError> {
|
||||||
let settings = state.settings.read().await.clone();
|
let settings = state.settings.read().await.clone();
|
||||||
|
let emergency_stop_active = settings.emergency_stop_enabled;
|
||||||
let schedules = state.db.list_schedules()?;
|
let schedules = state.db.list_schedules()?;
|
||||||
let devices = state.db.list_devices()?;
|
let devices = state.db.list_devices()?;
|
||||||
let zones = state.db.list_zones()?;
|
let zones = state.db.list_zones()?;
|
||||||
@@ -131,12 +132,14 @@ pub async fn build_control_plan(state: &AppState) -> Result<ControlPlan, AppErro
|
|||||||
let active_for_target = active_schedule_for_zone(&zone, &schedules, now);
|
let active_for_target = active_schedule_for_zone(&zone, &schedules, now);
|
||||||
let has_intent_source =
|
let has_intent_source =
|
||||||
zone_has_thermostat_intent_source(&zone, active_for_target, Utc::now());
|
zone_has_thermostat_intent_source(&zone, active_for_target, Utc::now());
|
||||||
let automation_idle = zone.enabled
|
let automation_idle = !emergency_stop_active
|
||||||
|
&& zone.enabled
|
||||||
&& !zone.device_manual_override
|
&& !zone.device_manual_override
|
||||||
&& zone.local_thermostat_power != Some(false)
|
&& zone.local_thermostat_power != Some(false)
|
||||||
&& configured_effective_mode != "off"
|
&& configured_effective_mode != "off"
|
||||||
&& !has_intent_source;
|
&& !has_intent_source;
|
||||||
let automatic_active = zone.enabled
|
let automatic_active = !emergency_stop_active
|
||||||
|
&& zone.enabled
|
||||||
&& !zone.device_manual_override
|
&& !zone.device_manual_override
|
||||||
&& zone.local_thermostat_power != Some(false)
|
&& zone.local_thermostat_power != Some(false)
|
||||||
&& configured_effective_mode != "off"
|
&& configured_effective_mode != "off"
|
||||||
@@ -175,7 +178,9 @@ pub async fn build_control_plan(state: &AppState) -> Result<ControlPlan, AppErro
|
|||||||
event.label = format!("{}: {}", zone.name, event.label);
|
event.label = format!("{}: {}", zone.name, event.label);
|
||||||
house_events.push(event);
|
house_events.push(event);
|
||||||
}
|
}
|
||||||
let effective_enabled = zone.enabled && zone.local_thermostat_power != Some(false);
|
let effective_enabled = !emergency_stop_active
|
||||||
|
&& zone.enabled
|
||||||
|
&& zone.local_thermostat_power != Some(false);
|
||||||
zones_out.push(ZoneControlPlan {
|
zones_out.push(ZoneControlPlan {
|
||||||
zone_id: zone.id.clone(),
|
zone_id: zone.id.clone(),
|
||||||
zone_name: zone.name.clone(),
|
zone_name: zone.name.clone(),
|
||||||
@@ -232,12 +237,16 @@ pub async fn build_control_plan(state: &AppState) -> Result<ControlPlan, AppErro
|
|||||||
control_command_source: zone.control_source.clone(),
|
control_command_source: zone.control_source.clone(),
|
||||||
control_since: zone.control_since,
|
control_since: zone.control_since,
|
||||||
resume_at: zone.control_resume_at,
|
resume_at: zone.control_resume_at,
|
||||||
control_reason: if automation_idle {
|
control_reason: if emergency_stop_active {
|
||||||
|
"Emergency stop pauses automatic climate control".into()
|
||||||
|
} else if automation_idle {
|
||||||
"Automation idle: no active schedule or explicit thermostat request".into()
|
"Automation idle: no active schedule or explicit thermostat request".into()
|
||||||
} else {
|
} else {
|
||||||
zone.control_reason.clone()
|
zone.control_reason.clone()
|
||||||
},
|
},
|
||||||
blocked_reason: if zone.device_manual_override {
|
blocked_reason: if emergency_stop_active {
|
||||||
|
Some("emergency_stop".into())
|
||||||
|
} else if zone.device_manual_override {
|
||||||
Some("manual_override".into())
|
Some("manual_override".into())
|
||||||
} else if zone
|
} else if zone
|
||||||
.lockout_until
|
.lockout_until
|
||||||
@@ -327,6 +336,8 @@ pub async fn build_control_plan(state: &AppState) -> Result<ControlPlan, AppErro
|
|||||||
Ok(ControlPlan {
|
Ok(ControlPlan {
|
||||||
generated_at: Utc::now(),
|
generated_at: Utc::now(),
|
||||||
house_mode: settings.house_mode,
|
house_mode: settings.house_mode,
|
||||||
|
emergency_stop_active,
|
||||||
|
emergency_stop_since: settings.emergency_stop_since,
|
||||||
house_preset,
|
house_preset,
|
||||||
house_power,
|
house_power,
|
||||||
outdoor_temperature: *state.outdoor_temperature.read().await,
|
outdoor_temperature: *state.outdoor_temperature.read().await,
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ mod tests {
|
|||||||
let left = ControlPlan {
|
let left = ControlPlan {
|
||||||
generated_at: generated_at.clone(),
|
generated_at: generated_at.clone(),
|
||||||
house_mode: "cool".into(),
|
house_mode: "cool".into(),
|
||||||
|
emergency_stop_active: false,
|
||||||
|
emergency_stop_since: None,
|
||||||
house_preset: Some("comfort".into()),
|
house_preset: Some("comfort".into()),
|
||||||
house_power: true,
|
house_power: true,
|
||||||
outdoor_temperature: Some(24.0),
|
outdoor_temperature: Some(24.0),
|
||||||
@@ -65,6 +67,9 @@ mod tests {
|
|||||||
assert!(control_plans_semantically_equal(&left, &right).unwrap());
|
assert!(control_plans_semantically_equal(&left, &right).unwrap());
|
||||||
right.house_mode = "heat".into();
|
right.house_mode = "heat".into();
|
||||||
assert!(!control_plans_semantically_equal(&left, &right).unwrap());
|
assert!(!control_plans_semantically_equal(&left, &right).unwrap());
|
||||||
|
right = left.clone();
|
||||||
|
right.emergency_stop_active = true;
|
||||||
|
assert!(!control_plans_semantically_equal(&left, &right).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -535,8 +535,11 @@ async fn handle_zone_pre_control_state(
|
|||||||
|
|
||||||
async fn control_zones(state: &AppState) -> Result<()> {
|
async fn control_zones(state: &AppState) -> Result<()> {
|
||||||
let _cycle_guard = state.lock_zone_control_cycle().await;
|
let _cycle_guard = state.lock_zone_control_cycle().await;
|
||||||
let schedules = state.db.list_schedules()?;
|
|
||||||
let settings = state.settings.read().await.clone();
|
let settings = state.settings.read().await.clone();
|
||||||
|
if settings.emergency_stop_enabled {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let schedules = state.db.list_schedules()?;
|
||||||
let mut zone_snapshot = state.db.list_zones()?;
|
let mut zone_snapshot = state.db.list_zones()?;
|
||||||
// Local/temporary thermostat ownership has its own deadlines. Expire and activate sessions independently.
|
// Local/temporary thermostat ownership has its own deadlines. Expire and activate sessions independently.
|
||||||
expire_local_thermostat_overrides(state, &mut zone_snapshot, &schedules, &settings.house_mode)
|
expire_local_thermostat_overrides(state, &mut zone_snapshot, &schedules, &settings.house_mode)
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ pub struct AutomationPlanRule {
|
|||||||
pub struct ControlPlan {
|
pub struct ControlPlan {
|
||||||
pub generated_at: DateTime<Utc>,
|
pub generated_at: DateTime<Utc>,
|
||||||
pub house_mode: String,
|
pub house_mode: String,
|
||||||
|
pub emergency_stop_active: bool,
|
||||||
|
pub emergency_stop_since: Option<DateTime<Utc>>,
|
||||||
/// Uniform whole-house preset when every zone uses the same override; None for a mixed state.
|
/// Uniform whole-house preset when every zone uses the same override; None for a mixed state.
|
||||||
pub house_preset: Option<String>,
|
pub house_preset: Option<String>,
|
||||||
/// Compatibility field. Global ON/OFF does not use a persistent master gate; this remains true in v0.9.4+.
|
/// Compatibility field. Global ON/OFF does not use a persistent master gate; this remains true in v0.9.4+.
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ pub struct RuntimeSettings {
|
|||||||
/// Global seasonal mode. Zones follow this by default. Values: cool/heat/off; off pauses house-level thermostat control.
|
/// Global seasonal mode. Zones follow this by default. Values: cool/heat/off; off pauses house-level thermostat control.
|
||||||
#[serde(default = "default_house_mode")]
|
#[serde(default = "default_house_mode")]
|
||||||
pub house_mode: String,
|
pub house_mode: String,
|
||||||
|
/// Persistent emergency pause for all automatic climate control. The flag survives
|
||||||
|
/// process restarts; physical OFF commands are sent only when the pause is activated.
|
||||||
|
#[serde(default)]
|
||||||
|
pub emergency_stop_enabled: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub emergency_stop_since: Option<DateTime<Utc>>,
|
||||||
/// `setpoint` keeps units powered and modulates compressor demand by changing target temperature.
|
/// `setpoint` keeps units powered and modulates compressor demand by changing target temperature.
|
||||||
#[serde(default = "default_control_strategy")]
|
#[serde(default = "default_control_strategy")]
|
||||||
pub control_strategy: String,
|
pub control_strategy: String,
|
||||||
|
|||||||
+5
-1
@@ -240,7 +240,10 @@ fn control_plan_event_affects_plan(event: &str) -> bool {
|
|||||||
|| event.starts_with("settings.")
|
|| event.starts_with("settings.")
|
||||||
|| matches!(
|
|| matches!(
|
||||||
event,
|
event,
|
||||||
"house.mode_changed" | "outdoor.updated" | "configuration.imported"
|
"house.mode_changed"
|
||||||
|
| "house.emergency_stop_changed"
|
||||||
|
| "outdoor.updated"
|
||||||
|
| "configuration.imported"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +262,7 @@ mod tests {
|
|||||||
"flow.updated",
|
"flow.updated",
|
||||||
"settings.night.updated",
|
"settings.night.updated",
|
||||||
"house.mode_changed",
|
"house.mode_changed",
|
||||||
|
"house.emergency_stop_changed",
|
||||||
"outdoor.updated",
|
"outdoor.updated",
|
||||||
"configuration.imported",
|
"configuration.imported",
|
||||||
] {
|
] {
|
||||||
|
|||||||
@@ -1885,6 +1885,25 @@ button.outside-pill:focus-visible {
|
|||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.house-power-actions .house-emergency-stop {
|
||||||
|
border-color: color-mix(in srgb, var(--warning) 58%, var(--line));
|
||||||
|
color: var(--warning);
|
||||||
|
background: var(--warning-soft);
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.house-power-actions .house-emergency-stop:hover {
|
||||||
|
border-color: var(--warning);
|
||||||
|
background: color-mix(in srgb, var(--warning-soft) 72%, var(--surface-2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.house-power-actions .house-emergency-stop.resume,
|
||||||
|
.house-power-actions .house-emergency-stop.resume.active {
|
||||||
|
border-color: var(--accent-border);
|
||||||
|
color: var(--accent-text);
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
.zone-thermostat {
|
.zone-thermostat {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@@ -4795,6 +4814,93 @@ body.simulation-standalone [data-view="simulation"] {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reasons {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-subsection {
|
||||||
|
display: grid;
|
||||||
|
gap: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-subsection h4 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: .08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-list li {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(130px, .75fr) minmax(0, 1.8fr);
|
||||||
|
gap: 14px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--surface-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-list li>div {
|
||||||
|
display: grid;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-list strong,
|
||||||
|
#controlPlan .plan-reason-list small,
|
||||||
|
#controlPlan .plan-reason-list span {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-list strong {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-list small {
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-list span {
|
||||||
|
color: var(--text-soft);
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-alert {
|
||||||
|
display: grid;
|
||||||
|
gap: 3px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--warning) 48%, var(--line));
|
||||||
|
border-radius: 11px;
|
||||||
|
background: var(--warning-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-alert strong {
|
||||||
|
color: var(--warning);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controlPlan .plan-reason-alert span,
|
||||||
|
#controlPlan .plan-reason-empty {
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
#controlPlan .plan-events .plan-rule-summary {
|
#controlPlan .plan-events .plan-rule-summary {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
@@ -10812,3 +10918,10 @@ select.select-native-proxy[aria-invalid="true"]+.custom-select .custom-select-tr
|
|||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 620px) {
|
||||||
|
#controlPlan .plan-reason-list li {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -85,7 +85,8 @@
|
|||||||
<div class="house-power-actions"><button id="housePowerOn" type="button" class="secondary"
|
<div class="house-power-actions"><button id="housePowerOn" type="button" class="secondary"
|
||||||
data-action="house-power" data-value="true" data-i18n="house.powerOnAll">Turn all on</button><button
|
data-action="house-power" data-value="true" data-i18n="house.powerOnAll">Turn all on</button><button
|
||||||
id="housePowerOff" type="button" class="secondary" data-action="house-power" data-value="false"
|
id="housePowerOff" type="button" class="secondary" data-action="house-power" data-value="false"
|
||||||
data-i18n="house.powerOffAll">Turn all off</button></div>
|
data-i18n="house.powerOffAll">Turn all off</button><button id="houseEmergencyStop" type="button"
|
||||||
|
class="secondary house-emergency-stop" data-action="house-emergency-stop" data-value="true" hidden>Emergency stop</button></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dashboard-global-divider"></div>
|
<div class="dashboard-global-divider"></div>
|
||||||
<div class="house-climate dashboard-house-climate" id="houseClimate"></div>
|
<div class="house-climate dashboard-house-climate" id="houseClimate"></div>
|
||||||
|
|||||||
Vendored
+1
@@ -45,6 +45,7 @@ function applyBootstrapSnapshot(data) {
|
|||||||
app.automations = data.automations || [];
|
app.automations = data.automations || [];
|
||||||
app.flows = data.flows || [];
|
app.flows = data.flows || [];
|
||||||
app.accessTokens = data.access_tokens || [];
|
app.accessTokens = data.access_tokens || [];
|
||||||
|
app.house = data.house || { mode: 'cool', emergency_stop_enabled: false, emergency_stop_since: null };
|
||||||
app.settings = settingsFromSnapshot(data.settings, data.house?.mode || 'cool');
|
app.settings = settingsFromSnapshot(data.settings, data.house?.mode || 'cool');
|
||||||
app.sensorAliases = { ...(app.settings.home_assistant?.sensor_aliases || {}) };
|
app.sensorAliases = { ...(app.settings.home_assistant?.sensor_aliases || {}) };
|
||||||
app.flowSharedInputs = JSON.parse(JSON.stringify(app.settings.home_assistant?.flow_inputs || []));
|
app.flowSharedInputs = JSON.parse(JSON.stringify(app.settings.home_assistant?.flow_inputs || []));
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const preferredLanguage = getCookie('gree_controller_language') || DEFAULT_LANGU
|
|||||||
const preferredTheme = ['system', 'light', 'dark'].includes(getCookie('gree_controller_theme')) ? getCookie('gree_controller_theme') : 'system';
|
const preferredTheme = ['system', 'light', 'dark'].includes(getCookie('gree_controller_theme')) ? getCookie('gree_controller_theme') : 'system';
|
||||||
|
|
||||||
const app = {
|
const app = {
|
||||||
devices: [], zones: [], groups: [], deviceGroups: [], deviceGroupEnergy: {}, schedules: [], automations: [], flows: [], accessTokens: [], settings: null, system: {}, outdoorTemperature: null,
|
devices: [], zones: [], groups: [], deviceGroups: [], deviceGroupEnergy: {}, schedules: [], automations: [], flows: [], accessTokens: [], settings: null, house: { mode: 'cool', emergency_stop_enabled: false, emergency_stop_since: null }, system: {}, outdoorTemperature: null,
|
||||||
token: localStorage.getItem('gree_controller_token') || '', authTokenSubmitted: false, ws: null, wsTimer: null, wsErrorTimer: null,
|
token: localStorage.getItem('gree_controller_token') || '', authTokenSubmitted: false, ws: null, wsTimer: null, wsErrorTimer: null,
|
||||||
currentView: 'dashboard', loading: false, bootstrapReloadPending: false, language: preferredLanguage, theme: preferredTheme, connectionStatus: 'connecting',
|
currentView: 'dashboard', loading: false, bootstrapReloadPending: false, language: preferredLanguage, theme: preferredTheme, connectionStatus: 'connecting',
|
||||||
languages: [], translations: {}, locales: {}, defaultLanguage: DEFAULT_LANGUAGE,
|
languages: [], translations: {}, locales: {}, defaultLanguage: DEFAULT_LANGUAGE,
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ function renderSummary() {
|
|||||||
const average = temperatures.length ? temperatures.reduce((a, b) => a + b, 0) / temperatures.length : null;
|
const average = temperatures.length ? temperatures.reduce((a, b) => a + b, 0) / temperatures.length : null;
|
||||||
const online = app.devices.filter(d => d.online).length;
|
const online = app.devices.filter(d => d.online).length;
|
||||||
const active = app.devices.filter(d => d.power).length;
|
const active = app.devices.filter(d => d.power).length;
|
||||||
const demand = app.zones.filter(z => z.enabled && z.demand).length;
|
const emergencyActive = !!(app.house?.emergency_stop_enabled || app.controlPlan?.emergency_stop_active);
|
||||||
|
const demand = emergencyActive ? 0 : app.zones.filter(z => z.enabled && z.demand).length;
|
||||||
const toolbarStatus = $('#toolbarRuntimeStatus');
|
const toolbarStatus = $('#toolbarRuntimeStatus');
|
||||||
if (toolbarStatus) toolbarStatus.textContent = tr('dashboard.toolbarWorking', { active, total: app.devices.length });
|
if (toolbarStatus) toolbarStatus.textContent = tr('dashboard.toolbarWorking', { active, total: app.devices.length });
|
||||||
$('#heroTemperature').innerHTML = `${average === null ? '--' : average.toFixed(1)}<small>°C</small>`;
|
$('#heroTemperature').innerHTML = `${average === null ? '--' : average.toFixed(1)}<small>°C</small>`;
|
||||||
@@ -46,9 +47,23 @@ function renderHouseClimate() {
|
|||||||
const preset = zonePresets.length && zonePresets.every(value => value === zonePresets[0]) ? zonePresets[0] : null;
|
const preset = zonePresets.length && zonePresets.every(value => value === zonePresets[0]) ? zonePresets[0] : null;
|
||||||
const outdoor = app.outdoorTemperature == null ? tr('common.unavailable') : fmtTemp(app.outdoorTemperature);
|
const outdoor = app.outdoorTemperature == null ? tr('common.unavailable') : fmtTemp(app.outdoorTemperature);
|
||||||
const powerOn = $('#housePowerOn'), powerOff = $('#housePowerOff');
|
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);
|
||||||
|
|
||||||
if (powerOn) { powerOn.classList.remove('active'); powerOn.removeAttribute('aria-pressed'); }
|
if (powerOn) { powerOn.classList.remove('active'); powerOn.removeAttribute('aria-pressed'); }
|
||||||
if (powerOff) { powerOff.classList.remove('active'); powerOff.removeAttribute('aria-pressed'); }
|
if (powerOff) { powerOff.classList.remove('active'); powerOff.removeAttribute('aria-pressed'); }
|
||||||
|
if (emergencyButton) {
|
||||||
|
emergencyButton.hidden = !emergencyActive && !hasAutomaticControl;
|
||||||
|
emergencyButton.dataset.value = emergencyActive ? 'false' : 'true';
|
||||||
|
emergencyButton.classList.toggle('resume', emergencyActive);
|
||||||
|
emergencyButton.classList.toggle('active', emergencyActive);
|
||||||
|
emergencyButton.setAttribute('aria-pressed', String(emergencyActive));
|
||||||
|
emergencyButton.textContent = tr(emergencyActive ? 'house.emergencyResume' : 'house.emergencyStop');
|
||||||
|
emergencyButton.title = tr(emergencyActive ? 'house.emergencyResumeHint' : '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>
|
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">
|
<div class="house-mode-row">
|
||||||
<button class="${mode === 'cool' ? 'active' : ''}" data-action="house-mode" data-value="cool" aria-pressed="${mode === 'cool'}">${esc(tr('mode.cool'))}</button>
|
<button class="${mode === 'cool' ? 'active' : ''}" data-action="house-mode" data-value="cool" aria-pressed="${mode === 'cool'}">${esc(tr('mode.cool'))}</button>
|
||||||
@@ -58,6 +73,68 @@ function renderHouseClimate() {
|
|||||||
<div class="preset-row house-preset-row">${['auto', 'comfort', 'sleep', 'away'].map(p => `<button class="${preset === p ? 'active' : ''}" data-action="house-preset" data-value="${p}" aria-pressed="${preset === p}">${esc(p === 'sleep' ? tr('house.sleepAll') : p === 'comfort' ? tr('house.comfortAll') : p === 'away' ? tr('house.awayAll') : tr('house.autoAll'))}</button>`).join('')}</div>`;
|
<div class="preset-row house-preset-row">${['auto', 'comfort', 'sleep', 'away'].map(p => `<button class="${preset === p ? 'active' : ''}" data-action="house-preset" data-value="${p}" aria-pressed="${preset === p}">${esc(p === 'sleep' ? tr('house.sleepAll') : p === 'comfort' ? tr('house.comfortAll') : p === 'away' ? tr('house.awayAll') : tr('house.autoAll'))}</button>`).join('')}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function planBlockedReasonLabel(reason) {
|
||||||
|
const key = {
|
||||||
|
emergency_stop: 'plan.reasonEmergency',
|
||||||
|
manual_override: 'plan.reasonManual',
|
||||||
|
zone_disabled: 'plan.reasonDisabled',
|
||||||
|
offline: 'plan.reasonOffline',
|
||||||
|
no_active_thermostat_intent: 'plan.reasonNoIntent',
|
||||||
|
}[reason];
|
||||||
|
return key ? tr(key) : reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
function planZoneReason(zone) {
|
||||||
|
if (zone.blocked_reason) return planBlockedReasonLabel(zone.blocked_reason);
|
||||||
|
if (zone.local_thermostat_power === false) return tr('plan.reasonLocalOff');
|
||||||
|
if (zone.local_thermostat_power === true) return tr('plan.reasonLocalOn');
|
||||||
|
|
||||||
|
const current = zone.current_temperature == null ? NaN : Number(zone.current_temperature);
|
||||||
|
const target = zone.target_temperature == null ? NaN : Number(zone.target_temperature);
|
||||||
|
if (zone.demand && Number.isFinite(current) && Number.isFinite(target)) {
|
||||||
|
if (zone.mode === 'heat') return tr('plan.reasonHeatDemand', { current: fmtTemp(current), target: fmtTemp(target) });
|
||||||
|
if (zone.mode === 'cool') return tr('plan.reasonCoolDemand', { current: fmtTemp(current), target: fmtTemp(target) });
|
||||||
|
}
|
||||||
|
if (Number.isFinite(current) && Number.isFinite(target)) {
|
||||||
|
return tr('plan.reasonTargetSatisfied', { current: fmtTemp(current), target: fmtTemp(target) });
|
||||||
|
}
|
||||||
|
if (zone.current_schedule_name) return tr('plan.reasonSchedule', { schedule: zone.current_schedule_name });
|
||||||
|
return tr('plan.reasonWaiting');
|
||||||
|
}
|
||||||
|
|
||||||
|
function planReasonCard(plan) {
|
||||||
|
const zones = (plan.zones || []).map(zone => {
|
||||||
|
const reason = planZoneReason(zone);
|
||||||
|
const source = zone.control_owner === 'direct_manual' ? tr('plan.reasonSourceManual')
|
||||||
|
: zone.control_owner === 'local_thermostat' ? tr('plan.reasonSourceThermostat')
|
||||||
|
: zone.current_schedule_name ? tr('plan.reasonSourceSchedule', { schedule: zone.current_schedule_name })
|
||||||
|
: tr('plan.reasonSourceAutomation');
|
||||||
|
return `<li><div><strong>${esc(zone.zone_name)}</strong><small>${esc(source)}</small></div><span>${esc(reason)}</span></li>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
const recentRules = (plan.rules || [])
|
||||||
|
.filter(rule => rule.enabled && rule.last_fired_at)
|
||||||
|
.sort((a, b) => new Date(b.last_fired_at) - new Date(a.last_fired_at))
|
||||||
|
.slice(0, 4)
|
||||||
|
.map(rule => {
|
||||||
|
const when = new Date(rule.last_fired_at).toLocaleString(locale(), { weekday: 'short', hour: '2-digit', minute: '2-digit' });
|
||||||
|
const triggerDevice = rule.trigger_device_id ? (app.devices || []).find(device => device.id === rule.trigger_device_id) : null;
|
||||||
|
const current = triggerDevice?.current_temperature == null ? null : Number(triggerDevice.current_temperature);
|
||||||
|
const trigger = Number.isFinite(current)
|
||||||
|
? tr('plan.reasonTriggerReading', { trigger: automationTriggerLabel(rule), current: fmtTemp(current) })
|
||||||
|
: automationTriggerLabel(rule);
|
||||||
|
return `<li><div><strong>${esc(rule.name)}</strong><small>${esc(trigger)}</small></div><span>${esc(tr('plan.reasonLastTriggered', { when }))}</span></li>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
const emergency = plan.emergency_stop_active
|
||||||
|
? `<div class="plan-reason-alert"><strong>${esc(tr('house.emergencyActive'))}</strong><span>${esc(tr('house.emergencyActiveHint'))}</span></div>`
|
||||||
|
: '';
|
||||||
|
const rules = recentRules
|
||||||
|
? `<div class="plan-reason-subsection"><h4>${esc(tr('plan.recentTriggers'))}</h4><ul class="plan-reason-list">${recentRules}</ul></div>`
|
||||||
|
: '';
|
||||||
|
return `<article class="panel plan-card plan-reasons"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.reasonsEyebrow'))}</span><h3>${esc(tr('plan.reasonsTitle'))}</h3></div></div>${emergency}<div class="plan-reason-subsection"><h4>${esc(tr('plan.currentReasons'))}</h4><ul class="plan-reason-list">${zones || `<li class="plan-reason-empty">${esc(tr('plan.noReasons'))}</li>`}</ul></div>${rules}</article>`;
|
||||||
|
}
|
||||||
|
|
||||||
function planEventMarkup(event) {
|
function planEventMarkup(event) {
|
||||||
const when = event?.at ? new Date(event.at).toLocaleString(locale(), { weekday: 'short', hour: '2-digit', minute: '2-digit' }) : '—';
|
const when = event?.at ? new Date(event.at).toLocaleString(locale(), { weekday: 'short', hour: '2-digit', minute: '2-digit' }) : '—';
|
||||||
const target = event?.target_temperature == null ? '' : ` · ${fmtTemp(event.target_temperature)}`;
|
const target = event?.target_temperature == null ? '' : ` · ${fmtTemp(event.target_temperature)}`;
|
||||||
@@ -121,7 +198,7 @@ function renderControlPlan() {
|
|||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
const rules = (plan.rules || []).filter(rule => rule.enabled);
|
const rules = (plan.rules || []).filter(rule => rule.enabled);
|
||||||
const ruleCard = rules.length ? `<article class="panel plan-card plan-rules"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.rules'))}</span><h3>${esc(tr('plan.ruleCount', { count: rules.length }))}</h3></div></div><ul class="plan-events">${rules.slice(0, 3).map(rule => {
|
const ruleCard = rules.length ? `<article class="panel plan-card plan-rules ${plan.emergency_stop_active ? 'disabled' : ''}"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.rules'))}</span><h3>${esc(tr(plan.emergency_stop_active ? 'plan.ruleCountPaused' : 'plan.ruleCount', { count: rules.length }))}</h3></div></div><ul class="plan-events">${rules.slice(0, 3).map(rule => {
|
||||||
const automation = (app.automations || []).find(item => item.id === rule.id);
|
const automation = (app.automations || []).find(item => item.id === rule.id);
|
||||||
const flow = automation?.flow_id ? (app.flows || []).find(item => item.id === automation.flow_id) : null;
|
const flow = automation?.flow_id ? (app.flows || []).find(item => item.id === automation.flow_id) : null;
|
||||||
const displayName = flow?.name || rule.name;
|
const displayName = flow?.name || rule.name;
|
||||||
@@ -129,7 +206,8 @@ function renderControlPlan() {
|
|||||||
return `<li><time>${esc(automationTriggerLabel(rule))}</time><span class="plan-rule-summary"><strong>${esc(displayName)}</strong>${target ? `<small>→ ${esc(target)}</small>` : ''}</span></li>`;
|
return `<li><time>${esc(automationTriggerLabel(rule))}</time><span class="plan-rule-summary"><strong>${esc(displayName)}</strong>${target ? `<small>→ ${esc(target)}</small>` : ''}</span></li>`;
|
||||||
}).join('')}</ul></article>` : '';
|
}).join('')}</ul></article>` : '';
|
||||||
|
|
||||||
const content = house + groupCards + zones + ruleCard;
|
const reasonCard = planReasonCard(plan);
|
||||||
|
const content = house + groupCards + zones + ruleCard + reasonCard;
|
||||||
host.innerHTML = content;
|
host.innerHTML = content;
|
||||||
if (section) section.hidden = !content;
|
if (section) section.hidden = !content;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,23 @@ document.addEventListener('click', async event => {
|
|||||||
finally { button.disabled = false; }
|
finally { button.disabled = false; }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (action === 'house-emergency-stop') {
|
||||||
|
const active = button.dataset.value === 'true';
|
||||||
|
if (active && !confirm(tr('house.emergencyStopConfirm'))) return;
|
||||||
|
try {
|
||||||
|
button.disabled = true;
|
||||||
|
const result = await enqueueClimateControlTask(async () => {
|
||||||
|
const response = await api('/api/house/emergency-stop', { method: 'POST', body: { active } });
|
||||||
|
await loadBootstrap();
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
const failed = Array.isArray(result.failed) ? result.failed.length : 0;
|
||||||
|
if (active && failed) toast(tr('house.emergencyStoppedPartial', { count: failed }), true);
|
||||||
|
else toast(tr(active ? 'house.emergencyStopped' : 'house.emergencyResumed'));
|
||||||
|
} catch (error) { toast(error.message, true); }
|
||||||
|
finally { button.disabled = false; }
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (action === 'house-preset') {
|
if (action === 'house-preset') {
|
||||||
try {
|
try {
|
||||||
const result = await enqueueClimateControlTask(async () => {
|
const result = await enqueueClimateControlTask(async () => {
|
||||||
|
|||||||
@@ -111,7 +111,14 @@ async function handleWebSocketMessage(event) {
|
|||||||
if (!isFormDirty($('#homeAssistantForm'))) { app.sensorAliases = { ...(data.sensor_aliases || {}) }; app.flowSharedInputs = JSON.parse(JSON.stringify(data.flow_inputs || [])); renderHomeAssistantSettings(); }
|
if (!isFormDirty($('#homeAssistantForm'))) { app.sensorAliases = { ...(data.sensor_aliases || {}) }; app.flowSharedInputs = JSON.parse(JSON.stringify(data.flow_inputs || [])); renderHomeAssistantSettings(); }
|
||||||
renderHouseClimate(); if (app.flowDraft) renderFlowEditor(); scheduleControlPlanLoad();
|
renderHouseClimate(); if (app.flowDraft) renderFlowEditor(); scheduleControlPlanLoad();
|
||||||
}
|
}
|
||||||
else if (message.event === 'house.mode_changed') { app.settings = app.settings || {}; app.settings.house_mode = data.mode || 'cool'; renderHouseClimate(); renderGroups(); scheduleControlPlanLoad(); }
|
else if (message.event === 'house.mode_changed') { app.settings = app.settings || {}; app.settings.house_mode = data.mode || 'cool'; app.house = app.house || {}; app.house.mode = data.mode || 'cool'; renderHouseClimate(); renderGroups(); scheduleControlPlanLoad(); }
|
||||||
|
else if (message.event === 'house.emergency_stop_changed') {
|
||||||
|
app.house = app.house || { mode: app.settings?.house_mode || 'cool' };
|
||||||
|
app.house.emergency_stop_enabled = !!data.active;
|
||||||
|
app.house.emergency_stop_since = data.since || null;
|
||||||
|
renderHouseClimate();
|
||||||
|
scheduleControlPlanLoad();
|
||||||
|
}
|
||||||
else if (message.event === 'outdoor.updated') { app.outdoorTemperature = Number.isFinite(Number(data.temperature)) ? Number(data.temperature) : null; renderHouseClimate(); scheduleControlPlanLoad(); }
|
else if (message.event === 'outdoor.updated') { app.outdoorTemperature = Number.isFinite(Number(data.temperature)) ? Number(data.temperature) : null; renderHouseClimate(); scheduleControlPlanLoad(); }
|
||||||
else if (message.event === 'configuration.imported') { await loadBootstrap(); return; }
|
else if (message.event === 'configuration.imported') { await loadBootstrap(); return; }
|
||||||
else if (message.event === 'gree.frame_received') {
|
else if (message.event === 'gree.frame_received') {
|
||||||
|
|||||||
Reference in New Issue
Block a user