v0.16.1
This commit is contained in:
1 parent
ccf85a2fb8
commit
f376f0a7b6
32 files changed
+312
-114
No files matched your search
Generated
+1
-1
@@ -719,7 +719,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gree-controller"
|
name = "gree-controller"
|
||||||
version = "0.16.0"
|
version = "0.16.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gree-controller"
|
name = "gree-controller"
|
||||||
version = "0.16.0"
|
version = "0.16.1"
|
||||||
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"
|
||||||
|
|||||||
+36
-35
@@ -1,17 +1,18 @@
|
|||||||
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
||||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
2fd3bf1fb7ef548c7abc9b26d1cc4ef470784b8637af65446b845b27a45b547b ./.gitignore
|
||||||
3c92ae20c070a1db66390a8ec95ed87bc0fed9ec82188e5348fc862ddcbff06e ./Cargo.lock
|
1ece15d35e56470fbc0e9f0d9b1963f518c81a7b80270f2d2d91e53a4eb463ce ./Cargo.lock
|
||||||
5a7d380c42118a8ab57939bfc99fe15dcf4ad4c1f4bfd5fd808164012ca2c0df ./Cargo.toml
|
8ad83bc5b719a3c0e058d0d6d28cc5af834f7a6d07d8fdc89c4c8ae804e1eb25 ./Cargo.toml
|
||||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||||
8f2573aeec2240fa85c090b616575a9f5f79acc06ccfe8a98bf216ab13d3176a ./README.md
|
3da89b24afe096e116b5725a279c2e84b493ea2ead4aa24895647e083c02298f ./README.md
|
||||||
43cc9dbec1deb5c9f2fdddf03c4f19e745014a0f5ef44de556ca75b312b3acd8 ./SOURCE_MANIFEST.sha256
|
9d39a66e1b041fef40a180e918ebfc676b9bf0406af3237327201b2b60ab53e8 ./SOURCE_MANIFEST.sha256
|
||||||
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
||||||
dcbfdc5a281adb94c3e4186f88248d48d6e995b45591cdba7183f28e658698e5 ./docs/API.md
|
a1f77b4c93063376a8568667713fc708d8e59541260834f9bb353ca44f1670cc ./docs/API.md
|
||||||
4c2a0ea22061c39ae4ff534134392edfb31264c9ccaa453e8448b60b881c3847 ./docs/FLOW.md
|
dd90e743bb9d3743ea677300a2ce0276f87f02fec2db0a3ffdcf5cc1bdf6cc60 ./docs/FLOW.md
|
||||||
1a675292d181edb313850b26fa264b6a9daa3ccc4a632fb86828a04b3803ea36 ./docs/openapi.json
|
7f8afa5a07a021f3f0fd49b1c65b14724e34229620ff58565b73dfe1fe7722a5 ./docs/openapi.json
|
||||||
26130f86bc8bd020f0a1668d11bb0220f577779cbe2b008fb194d62dbb385ddd ./flows/stability/igor---heating-ha-stable.flow.json
|
716523299bfdf791276cc7c590caa3ea54530207c5e05cebb45e2ba6ef9da915 ./flows/multisplit/v4/INSTRUKCJA.txt
|
||||||
63553ab25775d332b8aae0bf7715c37a3d2a19b12ebabbbf09335534f369c29a ./flows/stability/jan---heating-ha-stable.flow.json
|
0a2f74baec4202b45708aecd7456d7339e4dbbe6c65fa497fb8b89c9d0e1d7c9 ./flows/multisplit/v4/igor-multisplit-soft-coast-v4.flow.json
|
||||||
8ffec14bd7ad358134e25cb901906494186a8a73788111fd4ced450f87cf3b6f ./flows/stability/sypialnia---heating-ha-stable.flow.json
|
1e91ca707015988ea01b944570b88fb27fa9a7b8aca48a8493885969ce69b452 ./flows/multisplit/v4/jan-multisplit-soft-coast-v4.flow.json
|
||||||
|
ef649edd25d41c2cf348149896003d21143bcadd599665c21322f6ee0d52ed36 ./flows/multisplit/v4/sypialnia-multisplit-soft-coast-v4.flow.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
|
||||||
@@ -25,7 +26,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
|
||||||
002170e041762ba17d2a082722a5c23fe13d126281e04aa5438a835ce98542a0 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
8df63d9ae43ac72e47e1004ece2afc397f777c169f5715ad6148e3bfebedfc36 ./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
|
||||||
@@ -33,10 +34,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
|
||||||
bfce426a0151c5616122446ded457bdefb20d691afa64003fd57e8b67b26ff85 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
eaca083a91d15b31ed3415171f9bf4773312245ebd80ef0e9873d4ea11266986 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||||
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
||||||
40036e3de45a68322e26d970eca50d3f17b57572f00fb13b0d4939d4a0fb36e0 ./ha-addon/repository/gree-controller/README.md
|
f7038a752048ecb1d2004e9dcccb9b43e5534bae4046331ceb33322316ad83b4 ./ha-addon/repository/gree-controller/README.md
|
||||||
94cd29892fe53337bf5cf20ddd5317da514f311be14e056652ecfc3c45c7d45a ./ha-addon/repository/gree-controller/config.yaml
|
878d3d369c458bc0c2d261b339be1843379e9f77f28141e3db0f19b77cd11105 ./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
|
||||||
@@ -46,8 +47,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
|
||||||
ea015719d066cab86aff9daab33c3a83a29def711195eca416652be45a033ef8 ./lang/en.json
|
271aa0ff32672f7888f0cfcb12dc1543ab22640549d5a43c4f3db9d6d45b64ee ./lang/en.json
|
||||||
fefb2710c34195a2d50c62aa107e2f284875990fa63f7da42c8019b0398c3387 ./lang/pl.json
|
0f6a09664d90b2a5edf9b7c33d77baf1ea39e94392ae3cc868d68169b4a8ad4f ./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
|
||||||
@@ -88,7 +89,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
|
||||||
0636d1f517e866ea5d92960dd16a37eb848715e0f5b6c5e4e5a2f7fd550e2e4e ./scripts/api_dev_test.py
|
532ed10e2b6b52ad07685e90858ead075d51124b2cba4da57d19221224bfca19 ./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
|
||||||
@@ -104,13 +105,13 @@ b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/upda
|
|||||||
ea84f5230da97447c60dfb4f8dadd0747c92fd1f7a74d5a65d6fdb6f23cf4b6d ./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
|
||||||
1eb8c3cd86beb4f23aef21a23d31c144a9b31ec5bbaeaf24536723da5e78b768 ./src/api/automations.rs
|
36ad26dfc488e74359355069e5ea3309527b3d03bf879abf51c5d28497389901 ./src/api/automations.rs
|
||||||
fffad7874465739cd6b508e9d7791387b864fa88420ce0f7e2fc933d129a2751 ./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
|
||||||
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
||||||
1c5bc76fca312e6cd5fce988b3bfee785068322337b609405c12e18c1448665a ./src/api/flows.rs
|
a4bd4d09ee1f6ea6039d2eb563e446142aab6b28f7de510ca8a27ba3465abb92 ./src/api/flows.rs
|
||||||
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
|
||||||
@@ -123,7 +124,7 @@ b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/publ
|
|||||||
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
||||||
7c974a98c72092886247f67c3a1c62736898d4b279ceb9c4c306690a3c6e4294 ./src/api/system.rs
|
7c974a98c72092886247f67c3a1c62736898d4b279ceb9c4c306690a3c6e4294 ./src/api/system.rs
|
||||||
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
|
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
|
||||||
ddd1bfc8625b66f6e5f3d24b3122ac86c25f89b0af527e3dfce9102551a8f142 ./src/api/zones.rs
|
95e636288b46cdeb00f9fbf34ac2dbd226184e0eca5a3a111f188a305b0a5258 ./src/api/zones.rs
|
||||||
32fa38d4315682cd3ef3ecdc91e571c2729b5e68b6f2bff965e43de3fdd2aeed ./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
|
||||||
@@ -139,7 +140,7 @@ 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
|
||||||
fbd0c387f48133cf7b1119bdd2830c3e3bb9238d94bcb3b76133b98ab657b601 ./src/engine/automations.rs
|
0bad57d36f6f58c40b0be595c3fe47d981460d61736fb48f389f649ccd4bea3b ./src/engine/automations.rs
|
||||||
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
|
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
|
||||||
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
|
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
|
||||||
828657b3c13b68792991b5ca2a26ae0b1d5c417191c83c4e5f650f62b6440096 ./src/engine/control_plan.rs
|
828657b3c13b68792991b5ca2a26ae0b1d5c417191c83c4e5f650f62b6440096 ./src/engine/control_plan.rs
|
||||||
@@ -149,15 +150,15 @@ e350aa63f3bb14ca887eaaa7555edbeae00fb584cbcaf552cc972f0c8918f5d8 ./src/engine/g
|
|||||||
4ec11426ef860811348f2bf1b458bc5713fe8a7794fb4c8045dd637652e8e696 ./src/engine/history.rs
|
4ec11426ef860811348f2bf1b458bc5713fe8a7794fb4c8045dd637652e8e696 ./src/engine/history.rs
|
||||||
41b7045877771b7c352328998cb45ce2433e7f9631fad3eb1a882ab4897dc31f ./src/engine/local_thermostat.rs
|
41b7045877771b7c352328998cb45ce2433e7f9631fad3eb1a882ab4897dc31f ./src/engine/local_thermostat.rs
|
||||||
e61d7dea4b60e06fd8d08daaceecf53bd71b13ea2b0ad462692049f3cdaa5cef ./src/engine/ownership.rs
|
e61d7dea4b60e06fd8d08daaceecf53bd71b13ea2b0ad462692049f3cdaa5cef ./src/engine/ownership.rs
|
||||||
76c1594f31af64c9a34e2555ac015e8719d42273c687b4c85b9c8f306b3c69a2 ./src/engine/polling.rs
|
5fafacadbc4a3d7e375a8079f9ae1b80eb013c2fb651b8fcc65f5f17cf023426 ./src/engine/polling.rs
|
||||||
c73691c3637465ab0f79aecf7f5428d0317799a8ab5329ed564cf57963bbe86e ./src/engine/runtime.rs
|
c73691c3637465ab0f79aecf7f5428d0317799a8ab5329ed564cf57963bbe86e ./src/engine/runtime.rs
|
||||||
48a496c3935f886a90b90461c38a7aa31ebc40c6482d6feaeb2be47bc2387625 ./src/engine/schedules.rs
|
48a496c3935f886a90b90461c38a7aa31ebc40c6482d6feaeb2be47bc2387625 ./src/engine/schedules.rs
|
||||||
b0189f28d0f337dfd3ce0934da7a565e4a5535afc1c242214c26ed584b3b7840 ./src/engine/targets.rs
|
b0189f28d0f337dfd3ce0934da7a565e4a5535afc1c242214c26ed584b3b7840 ./src/engine/targets.rs
|
||||||
975298f6dec43bd962de67a1fdd2d6d9466bb0b16cc5dc46d04c2b23229e7a27 ./src/engine/temperature.rs
|
d62ac5e97a36bbdb31bc23871ea7de73d8ac64a1e8203153d9faff0b1a075bc9 ./src/engine/temperature.rs
|
||||||
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
|
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
|
||||||
bb7758192868f6e7589e70a3d2962fe7d05357821609f4e1eb8237cfb56af597 ./src/engine/tests.rs
|
ca5fbcdf44842766dd0741d2b728656535ff3a00f4a8a6da989d7183ceda51b6 ./src/engine/tests.rs
|
||||||
c89ff36c6a67bf4fbf68c43e18aa0a556de2dc69263fd79db376363afc741a4f ./src/engine/zone_actions.rs
|
c89ff36c6a67bf4fbf68c43e18aa0a556de2dc69263fd79db376363afc741a4f ./src/engine/zone_actions.rs
|
||||||
08cb170ab73152720dbcf137bf8f3190e18ea2d498a49df128790fa59c2f3416 ./src/engine/zone_control.rs
|
edee10929eb89d7a774474cd9054890ee53b8c1fdf54e3d6f143265dead8f6b8 ./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
|
||||||
@@ -166,7 +167,7 @@ da4068295f37c23222bb13bebac022d88ba7729604a49ad25438c55e45458a49 ./src/influxdb
|
|||||||
67e42a92295480d2f1d364958f184654cfb5d4b77e7e1df47f588bdec18868a0 ./src/influxdb/write.rs
|
67e42a92295480d2f1d364958f184654cfb5d4b77e7e1df47f588bdec18868a0 ./src/influxdb/write.rs
|
||||||
62f9da6ef4f5fd92701d0857773f9f6236ef25356f21b9b42236abab6ba7da53 ./src/main.rs
|
62f9da6ef4f5fd92701d0857773f9f6236ef25356f21b9b42236abab6ba7da53 ./src/main.rs
|
||||||
23688a5cb68efded89471ce959328d27adff370d451bd9f378a283ab51437e76 ./src/models.rs
|
23688a5cb68efded89471ce959328d27adff370d451bd9f378a283ab51437e76 ./src/models.rs
|
||||||
615fba5dc5f3ad75497d637b02cc83b7aacfe7baa6372c034dd5609bfff398a0 ./src/models/automation.rs
|
75bb9db327ae4ab1f29a5aa888e130be3a8e75b714fb5dbad4307bebee583c66 ./src/models/automation.rs
|
||||||
e4195a40135f408302a78cb200a54e6ef1c0e6031fb069a2431d068e5dbbdd8d ./src/models/control_plan.rs
|
e4195a40135f408302a78cb200a54e6ef1c0e6031fb069a2431d068e5dbbdd8d ./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
|
||||||
@@ -176,7 +177,7 @@ aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/f
|
|||||||
17cac849056b677a3872e600a0d99218fb383ad5ec91cde596562df8e2f47508 ./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
|
94ed531788a118f2f3fb5dd1748b2f416480a5945d8c7c7a722c201ce03212dd ./src/models/zone.rs
|
||||||
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
|
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
|
||||||
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
|
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
|
||||||
578b9f39110abbeb98323044b6ae1a941733c82021afeb4d7ff78ad4256b5aa5 ./src/protocol/gree.rs
|
578b9f39110abbeb98323044b6ae1a941733c82021afeb4d7ff78ad4256b5aa5 ./src/protocol/gree.rs
|
||||||
@@ -205,19 +206,19 @@ b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree
|
|||||||
6a30a5ecee99d2cf40381b9f770ed574e9e85b8b50e3425a77e6f64283faeee6 ./web/css/styles.css
|
6a30a5ecee99d2cf40381b9f770ed574e9e85b8b50e3425a77e6f64283faeee6 ./web/css/styles.css
|
||||||
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
||||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||||
78996e460243aa3043bd889891a99b9417d930beb7c9ea81b4a7f496e3d58844 ./web/index.html
|
477de38c1cfbf2b925059795ef1b30522813d70908c36d3a471faf16da6935c8 ./web/index.html
|
||||||
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
||||||
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
|
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
|
||||||
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
||||||
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
|
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
|
||||||
80aaf5c56375345c67fd30c4fc1622319e4749b2859ec10613f77ac117f0441e ./web/js-dynamic/dashboard.js
|
008a85cddf6a5357eff35764eabddf1d7baaecf949b481d195124de50431e0d2 ./web/js-dynamic/dashboard.js
|
||||||
930e67432f55e8476513e808edfa9a0809a8da7479307284fcc136082a4129a4 ./web/js-dynamic/entities.js
|
c58dddbd1238ed9dfb590fc810e569c9a8408113cd85623e86190874591079e6 ./web/js-dynamic/entities.js
|
||||||
4612c958d7b9ccf89d6d45a11c3bc7e864bbd2ceaccc16fd7d76811ba4c74196 ./web/js-dynamic/events.js
|
bc1e7c4cccda0447fe805c61e655b773375bf503141195b82d4e95cdf093ec80 ./web/js-dynamic/events.js
|
||||||
3e52db4f0ab5d721a047bf5a8f690c2d634c1cb24433d50d95bcbaa617115bcc ./web/js-dynamic/flows.js
|
c4e14ebc7ddcec3c7346a037d488ceacd19606f76e84004b4f7c7dbd5fef646f ./web/js-dynamic/flows.js
|
||||||
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
1d54ae8919edb61414f9ffc46f85394b29c3c355827120264352cccb32f65e68 ./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
|
d9596cef1b86c0d3f73ccfc8d3a73c2b5293d81293fdbdcefd77346cbd3fe983 ./web/js-dynamic/navigation.js
|
||||||
3ff9c027beb8fb033330bf3cfb3496e06fb38c42041f1ffe522c6c7af18e9e30 ./web/js-dynamic/realtime.js
|
3ff9c027beb8fb033330bf3cfb3496e06fb38c42041f1ffe522c6c7af18e9e30 ./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
|
||||||
|
|||||||
@@ -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.16.0**
|
**Version: 0.16.1**
|
||||||
|
|
||||||
Version 0.16.0 improves heating stability with Home Assistant room-temperature sensor control, reliable Flow ownership, and safer shutdown behavior.
|
Version 0.16.1 adds configurable early heat coasting without unit ON/OFF cycling, per-Flow settings for coasting and deep standby, and separate Flow/manual status in the Web UI. Existing compressor protection and shared multisplit OFF logic are unchanged.
|
||||||
|
|
||||||
> [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`.
|
||||||
|
|||||||
+35
-34
@@ -1,16 +1,17 @@
|
|||||||
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
||||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
2fd3bf1fb7ef548c7abc9b26d1cc4ef470784b8637af65446b845b27a45b547b ./.gitignore
|
||||||
3c92ae20c070a1db66390a8ec95ed87bc0fed9ec82188e5348fc862ddcbff06e ./Cargo.lock
|
1ece15d35e56470fbc0e9f0d9b1963f518c81a7b80270f2d2d91e53a4eb463ce ./Cargo.lock
|
||||||
5a7d380c42118a8ab57939bfc99fe15dcf4ad4c1f4bfd5fd808164012ca2c0df ./Cargo.toml
|
8ad83bc5b719a3c0e058d0d6d28cc5af834f7a6d07d8fdc89c4c8ae804e1eb25 ./Cargo.toml
|
||||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||||
8f2573aeec2240fa85c090b616575a9f5f79acc06ccfe8a98bf216ab13d3176a ./README.md
|
3da89b24afe096e116b5725a279c2e84b493ea2ead4aa24895647e083c02298f ./README.md
|
||||||
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
||||||
dcbfdc5a281adb94c3e4186f88248d48d6e995b45591cdba7183f28e658698e5 ./docs/API.md
|
a1f77b4c93063376a8568667713fc708d8e59541260834f9bb353ca44f1670cc ./docs/API.md
|
||||||
4c2a0ea22061c39ae4ff534134392edfb31264c9ccaa453e8448b60b881c3847 ./docs/FLOW.md
|
dd90e743bb9d3743ea677300a2ce0276f87f02fec2db0a3ffdcf5cc1bdf6cc60 ./docs/FLOW.md
|
||||||
1a675292d181edb313850b26fa264b6a9daa3ccc4a632fb86828a04b3803ea36 ./docs/openapi.json
|
7f8afa5a07a021f3f0fd49b1c65b14724e34229620ff58565b73dfe1fe7722a5 ./docs/openapi.json
|
||||||
26130f86bc8bd020f0a1668d11bb0220f577779cbe2b008fb194d62dbb385ddd ./flows/stability/igor---heating-ha-stable.flow.json
|
716523299bfdf791276cc7c590caa3ea54530207c5e05cebb45e2ba6ef9da915 ./flows/multisplit/v4/INSTRUKCJA.txt
|
||||||
63553ab25775d332b8aae0bf7715c37a3d2a19b12ebabbbf09335534f369c29a ./flows/stability/jan---heating-ha-stable.flow.json
|
0a2f74baec4202b45708aecd7456d7339e4dbbe6c65fa497fb8b89c9d0e1d7c9 ./flows/multisplit/v4/igor-multisplit-soft-coast-v4.flow.json
|
||||||
8ffec14bd7ad358134e25cb901906494186a8a73788111fd4ced450f87cf3b6f ./flows/stability/sypialnia---heating-ha-stable.flow.json
|
1e91ca707015988ea01b944570b88fb27fa9a7b8aca48a8493885969ce69b452 ./flows/multisplit/v4/jan-multisplit-soft-coast-v4.flow.json
|
||||||
|
ef649edd25d41c2cf348149896003d21143bcadd599665c21322f6ee0d52ed36 ./flows/multisplit/v4/sypialnia-multisplit-soft-coast-v4.flow.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
|
||||||
@@ -24,7 +25,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
|
||||||
002170e041762ba17d2a082722a5c23fe13d126281e04aa5438a835ce98542a0 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
8df63d9ae43ac72e47e1004ece2afc397f777c169f5715ad6148e3bfebedfc36 ./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
|
||||||
@@ -32,10 +33,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
|
||||||
bfce426a0151c5616122446ded457bdefb20d691afa64003fd57e8b67b26ff85 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
eaca083a91d15b31ed3415171f9bf4773312245ebd80ef0e9873d4ea11266986 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||||
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
||||||
40036e3de45a68322e26d970eca50d3f17b57572f00fb13b0d4939d4a0fb36e0 ./ha-addon/repository/gree-controller/README.md
|
f7038a752048ecb1d2004e9dcccb9b43e5534bae4046331ceb33322316ad83b4 ./ha-addon/repository/gree-controller/README.md
|
||||||
94cd29892fe53337bf5cf20ddd5317da514f311be14e056652ecfc3c45c7d45a ./ha-addon/repository/gree-controller/config.yaml
|
878d3d369c458bc0c2d261b339be1843379e9f77f28141e3db0f19b77cd11105 ./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
|
||||||
@@ -45,8 +46,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
|
||||||
ea015719d066cab86aff9daab33c3a83a29def711195eca416652be45a033ef8 ./lang/en.json
|
271aa0ff32672f7888f0cfcb12dc1543ab22640549d5a43c4f3db9d6d45b64ee ./lang/en.json
|
||||||
fefb2710c34195a2d50c62aa107e2f284875990fa63f7da42c8019b0398c3387 ./lang/pl.json
|
0f6a09664d90b2a5edf9b7c33d77baf1ea39e94392ae3cc868d68169b4a8ad4f ./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
|
||||||
@@ -87,7 +88,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
|
||||||
0636d1f517e866ea5d92960dd16a37eb848715e0f5b6c5e4e5a2f7fd550e2e4e ./scripts/api_dev_test.py
|
532ed10e2b6b52ad07685e90858ead075d51124b2cba4da57d19221224bfca19 ./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
|
||||||
@@ -103,13 +104,13 @@ b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/upda
|
|||||||
ea84f5230da97447c60dfb4f8dadd0747c92fd1f7a74d5a65d6fdb6f23cf4b6d ./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
|
||||||
1eb8c3cd86beb4f23aef21a23d31c144a9b31ec5bbaeaf24536723da5e78b768 ./src/api/automations.rs
|
36ad26dfc488e74359355069e5ea3309527b3d03bf879abf51c5d28497389901 ./src/api/automations.rs
|
||||||
fffad7874465739cd6b508e9d7791387b864fa88420ce0f7e2fc933d129a2751 ./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
|
||||||
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
||||||
1c5bc76fca312e6cd5fce988b3bfee785068322337b609405c12e18c1448665a ./src/api/flows.rs
|
a4bd4d09ee1f6ea6039d2eb563e446142aab6b28f7de510ca8a27ba3465abb92 ./src/api/flows.rs
|
||||||
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
|
||||||
@@ -122,7 +123,7 @@ b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/publ
|
|||||||
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
||||||
7c974a98c72092886247f67c3a1c62736898d4b279ceb9c4c306690a3c6e4294 ./src/api/system.rs
|
7c974a98c72092886247f67c3a1c62736898d4b279ceb9c4c306690a3c6e4294 ./src/api/system.rs
|
||||||
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
|
3b0124a76dbc654584bcf186d43f89d20b6e7fd980ce0ffbf5a9e8eced5ec7f6 ./src/api/websocket.rs
|
||||||
ddd1bfc8625b66f6e5f3d24b3122ac86c25f89b0af527e3dfce9102551a8f142 ./src/api/zones.rs
|
95e636288b46cdeb00f9fbf34ac2dbd226184e0eca5a3a111f188a305b0a5258 ./src/api/zones.rs
|
||||||
32fa38d4315682cd3ef3ecdc91e571c2729b5e68b6f2bff965e43de3fdd2aeed ./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
|
||||||
@@ -138,7 +139,7 @@ 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
|
||||||
fbd0c387f48133cf7b1119bdd2830c3e3bb9238d94bcb3b76133b98ab657b601 ./src/engine/automations.rs
|
0bad57d36f6f58c40b0be595c3fe47d981460d61736fb48f389f649ccd4bea3b ./src/engine/automations.rs
|
||||||
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
|
1dccb8779f87dfdbd4ccda3f02ec51273e7be2e34e85166861668685528eca79 ./src/engine/commands.rs
|
||||||
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
|
357ef92c3cc42b243af4da31ac52bb360d7444ae423f58d7a13bdee79bf0a748 ./src/engine/connectivity.rs
|
||||||
828657b3c13b68792991b5ca2a26ae0b1d5c417191c83c4e5f650f62b6440096 ./src/engine/control_plan.rs
|
828657b3c13b68792991b5ca2a26ae0b1d5c417191c83c4e5f650f62b6440096 ./src/engine/control_plan.rs
|
||||||
@@ -148,15 +149,15 @@ e350aa63f3bb14ca887eaaa7555edbeae00fb584cbcaf552cc972f0c8918f5d8 ./src/engine/g
|
|||||||
4ec11426ef860811348f2bf1b458bc5713fe8a7794fb4c8045dd637652e8e696 ./src/engine/history.rs
|
4ec11426ef860811348f2bf1b458bc5713fe8a7794fb4c8045dd637652e8e696 ./src/engine/history.rs
|
||||||
41b7045877771b7c352328998cb45ce2433e7f9631fad3eb1a882ab4897dc31f ./src/engine/local_thermostat.rs
|
41b7045877771b7c352328998cb45ce2433e7f9631fad3eb1a882ab4897dc31f ./src/engine/local_thermostat.rs
|
||||||
e61d7dea4b60e06fd8d08daaceecf53bd71b13ea2b0ad462692049f3cdaa5cef ./src/engine/ownership.rs
|
e61d7dea4b60e06fd8d08daaceecf53bd71b13ea2b0ad462692049f3cdaa5cef ./src/engine/ownership.rs
|
||||||
76c1594f31af64c9a34e2555ac015e8719d42273c687b4c85b9c8f306b3c69a2 ./src/engine/polling.rs
|
5fafacadbc4a3d7e375a8079f9ae1b80eb013c2fb651b8fcc65f5f17cf023426 ./src/engine/polling.rs
|
||||||
c73691c3637465ab0f79aecf7f5428d0317799a8ab5329ed564cf57963bbe86e ./src/engine/runtime.rs
|
c73691c3637465ab0f79aecf7f5428d0317799a8ab5329ed564cf57963bbe86e ./src/engine/runtime.rs
|
||||||
48a496c3935f886a90b90461c38a7aa31ebc40c6482d6feaeb2be47bc2387625 ./src/engine/schedules.rs
|
48a496c3935f886a90b90461c38a7aa31ebc40c6482d6feaeb2be47bc2387625 ./src/engine/schedules.rs
|
||||||
b0189f28d0f337dfd3ce0934da7a565e4a5535afc1c242214c26ed584b3b7840 ./src/engine/targets.rs
|
b0189f28d0f337dfd3ce0934da7a565e4a5535afc1c242214c26ed584b3b7840 ./src/engine/targets.rs
|
||||||
975298f6dec43bd962de67a1fdd2d6d9466bb0b16cc5dc46d04c2b23229e7a27 ./src/engine/temperature.rs
|
d62ac5e97a36bbdb31bc23871ea7de73d8ac64a1e8203153d9faff0b1a075bc9 ./src/engine/temperature.rs
|
||||||
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
|
833b72907375e75c3f42b3921ad70a8e298f5a93743bf0d0c19059aa272462d1 ./src/engine/temporary_thermostat.rs
|
||||||
bb7758192868f6e7589e70a3d2962fe7d05357821609f4e1eb8237cfb56af597 ./src/engine/tests.rs
|
ca5fbcdf44842766dd0741d2b728656535ff3a00f4a8a6da989d7183ceda51b6 ./src/engine/tests.rs
|
||||||
c89ff36c6a67bf4fbf68c43e18aa0a556de2dc69263fd79db376363afc741a4f ./src/engine/zone_actions.rs
|
c89ff36c6a67bf4fbf68c43e18aa0a556de2dc69263fd79db376363afc741a4f ./src/engine/zone_actions.rs
|
||||||
08cb170ab73152720dbcf137bf8f3190e18ea2d498a49df128790fa59c2f3416 ./src/engine/zone_control.rs
|
edee10929eb89d7a774474cd9054890ee53b8c1fdf54e3d6f143265dead8f6b8 ./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
|
||||||
@@ -165,7 +166,7 @@ da4068295f37c23222bb13bebac022d88ba7729604a49ad25438c55e45458a49 ./src/influxdb
|
|||||||
67e42a92295480d2f1d364958f184654cfb5d4b77e7e1df47f588bdec18868a0 ./src/influxdb/write.rs
|
67e42a92295480d2f1d364958f184654cfb5d4b77e7e1df47f588bdec18868a0 ./src/influxdb/write.rs
|
||||||
62f9da6ef4f5fd92701d0857773f9f6236ef25356f21b9b42236abab6ba7da53 ./src/main.rs
|
62f9da6ef4f5fd92701d0857773f9f6236ef25356f21b9b42236abab6ba7da53 ./src/main.rs
|
||||||
23688a5cb68efded89471ce959328d27adff370d451bd9f378a283ab51437e76 ./src/models.rs
|
23688a5cb68efded89471ce959328d27adff370d451bd9f378a283ab51437e76 ./src/models.rs
|
||||||
615fba5dc5f3ad75497d637b02cc83b7aacfe7baa6372c034dd5609bfff398a0 ./src/models/automation.rs
|
75bb9db327ae4ab1f29a5aa888e130be3a8e75b714fb5dbad4307bebee583c66 ./src/models/automation.rs
|
||||||
e4195a40135f408302a78cb200a54e6ef1c0e6031fb069a2431d068e5dbbdd8d ./src/models/control_plan.rs
|
e4195a40135f408302a78cb200a54e6ef1c0e6031fb069a2431d068e5dbbdd8d ./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
|
||||||
@@ -175,7 +176,7 @@ aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/f
|
|||||||
17cac849056b677a3872e600a0d99218fb383ad5ec91cde596562df8e2f47508 ./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
|
94ed531788a118f2f3fb5dd1748b2f416480a5945d8c7c7a722c201ce03212dd ./src/models/zone.rs
|
||||||
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
|
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
|
||||||
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
|
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
|
||||||
578b9f39110abbeb98323044b6ae1a941733c82021afeb4d7ff78ad4256b5aa5 ./src/protocol/gree.rs
|
578b9f39110abbeb98323044b6ae1a941733c82021afeb4d7ff78ad4256b5aa5 ./src/protocol/gree.rs
|
||||||
@@ -204,19 +205,19 @@ b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree
|
|||||||
6a30a5ecee99d2cf40381b9f770ed574e9e85b8b50e3425a77e6f64283faeee6 ./web/css/styles.css
|
6a30a5ecee99d2cf40381b9f770ed574e9e85b8b50e3425a77e6f64283faeee6 ./web/css/styles.css
|
||||||
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
||||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||||
78996e460243aa3043bd889891a99b9417d930beb7c9ea81b4a7f496e3d58844 ./web/index.html
|
477de38c1cfbf2b925059795ef1b30522813d70908c36d3a471faf16da6935c8 ./web/index.html
|
||||||
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
||||||
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
|
e7318cd92b76e8a0109dc01adc2431474be17d8bb1488cdf6ffb39dc5450909d ./web/js-dynamic/bootstrap.js
|
||||||
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
||||||
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
|
07629b331bd498e8bea769d577321bbd12d9b076264242706d4ef16165fb22fa ./web/js-dynamic/core.js
|
||||||
80aaf5c56375345c67fd30c4fc1622319e4749b2859ec10613f77ac117f0441e ./web/js-dynamic/dashboard.js
|
008a85cddf6a5357eff35764eabddf1d7baaecf949b481d195124de50431e0d2 ./web/js-dynamic/dashboard.js
|
||||||
930e67432f55e8476513e808edfa9a0809a8da7479307284fcc136082a4129a4 ./web/js-dynamic/entities.js
|
c58dddbd1238ed9dfb590fc810e569c9a8408113cd85623e86190874591079e6 ./web/js-dynamic/entities.js
|
||||||
4612c958d7b9ccf89d6d45a11c3bc7e864bbd2ceaccc16fd7d76811ba4c74196 ./web/js-dynamic/events.js
|
bc1e7c4cccda0447fe805c61e655b773375bf503141195b82d4e95cdf093ec80 ./web/js-dynamic/events.js
|
||||||
3e52db4f0ab5d721a047bf5a8f690c2d634c1cb24433d50d95bcbaa617115bcc ./web/js-dynamic/flows.js
|
c4e14ebc7ddcec3c7346a037d488ceacd19606f76e84004b4f7c7dbd5fef646f ./web/js-dynamic/flows.js
|
||||||
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
1d54ae8919edb61414f9ffc46f85394b29c3c355827120264352cccb32f65e68 ./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
|
d9596cef1b86c0d3f73ccfc8d3a73c2b5293d81293fdbdcefd77346cbd3fe983 ./web/js-dynamic/navigation.js
|
||||||
3ff9c027beb8fb033330bf3cfb3496e06fb38c42041f1ffe522c6c7af18e9e30 ./web/js-dynamic/realtime.js
|
3ff9c027beb8fb033330bf3cfb3496e06fb38c42041f1ffe522c6c7af18e9e30 ./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
|
||||||
|
|||||||
+6
-3
@@ -1,6 +1,9 @@
|
|||||||
# GREE Controller API reference
|
# GREE Controller API reference
|
||||||
|
|
||||||
HTTP and WebSocket API for GREE Controller **0.16.0**.
|
HTTP and WebSocket API for GREE Controller **0.16.1**.
|
||||||
|
|
||||||
|
Version 0.16.1: Zone configuration exposes `heat_anticipation_c` (0..2.5°C, default 0), runtime Zone exposes `heat_coasting`, and optional Flow `zone_thermostat` node configuration supports `heat_anticipation_c` and `standby_offset_c`. These settings alter the GREE temperature target, not physical power.
|
||||||
|
|
||||||
|
|
||||||
[← Main documentation](../README.md)
|
[← Main documentation](../README.md)
|
||||||
|
|
||||||
@@ -243,7 +246,7 @@ Response:
|
|||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"name": "gree-controller",
|
"name": "gree-controller",
|
||||||
"version": "0.16.0",
|
"version": "0.16.1",
|
||||||
"uptime_seconds": 1234,
|
"uptime_seconds": 1234,
|
||||||
"control_ready": true,
|
"control_ready": true,
|
||||||
"time": "2026-08-30T06:54:00Z"
|
"time": "2026-08-30T06:54:00Z"
|
||||||
@@ -306,7 +309,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.16.0",
|
"version": "0.16.1",
|
||||||
"uptime_seconds": 1234,
|
"uptime_seconds": 1234,
|
||||||
"auth_required": false,
|
"auth_required": false,
|
||||||
"control_ready": true,
|
"control_ready": true,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ Flow is an authoring/orchestration layer. It does not replace the thermostat, sc
|
|||||||
|
|
||||||
## Existing control domains remain authoritative
|
## Existing control domains remain authoritative
|
||||||
|
|
||||||
|
- Optional `zone_thermostat` fields `heat_anticipation_c` (0–2.5 °C) and `standby_offset_c` (0.5–8 °C) persist into the target Zone when the Flow action executes. The regulator enters a hysteretic low-GREE-setpoint *coasting* state when room temperature reaches `target - heat_anticipation_c`, and resumes full setpoint heating once it falls another 0.4 °C. Coasting changes the requested GREE temperature and smart fan/Quiet behavior, **never the indoor unit power**; pre-existing compressor protection and multisplit shared-OFF flow gates remain unchanged. For older Flows the fields are absent and the previous regulator behavior remains.
|
||||||
- `zone_thermostat` writes the existing persistent Zone intent and wakes the normal thermostat cycle. `power=false` sends an explicit physical OFF to the assigned unit but does **not** change the persistent Zone `enabled` flag; Zone enable/disable remains an administrative control gate. A concrete Flow mode (`heat`/`cool`) is also written with the OFF frame so a stale device `dry`/`fan` mode is not retained; Flow `auto` remains an ownership policy and is never mapped to physical GREE Auto on OFF. While a zone is owned by `automation.flow`, the global outdoor-assist bias is suppressed so the Flow's own room-temperature thresholds remain authoritative. It can also issue one-shot granular vertical/horizontal louver commands to the assigned unit; these auxiliary fields do not take over thermostat ownership.
|
- `zone_thermostat` writes the existing persistent Zone intent and wakes the normal thermostat cycle. `power=false` sends an explicit physical OFF to the assigned unit but does **not** change the persistent Zone `enabled` flag; Zone enable/disable remains an administrative control gate. A concrete Flow mode (`heat`/`cool`) is also written with the OFF frame so a stale device `dry`/`fan` mode is not retained; Flow `auto` remains an ownership policy and is never mapped to physical GREE Auto on OFF. While a zone is owned by `automation.flow`, the global outdoor-assist bias is suppressed so the Flow's own room-temperature thresholds remain authoritative. It can also issue one-shot granular vertical/horizontal louver commands to the assigned unit; these auxiliary fields do not take over thermostat ownership.
|
||||||
- `group_action` calls the existing `control_group` path, including custom group targets.
|
- `group_action` calls the existing `control_group` path, including custom group targets.
|
||||||
- `device_action` uses the existing `DeviceCommand` / automatic-device path.
|
- `device_action` uses the existing `DeviceCommand` / automatic-device path.
|
||||||
|
|||||||
+20
-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.16.0",
|
"version": "0.16.1",
|
||||||
"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": {
|
||||||
@@ -6079,7 +6079,7 @@
|
|||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "0.16.0"
|
"example": "0.16.1"
|
||||||
},
|
},
|
||||||
"uptime_seconds": {
|
"uptime_seconds": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@@ -7173,6 +7173,13 @@
|
|||||||
"description": "Optimistic concurrency revision when updating",
|
"description": "Optimistic concurrency revision when updating",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"example": 12
|
"example": 12
|
||||||
|
},
|
||||||
|
"heat_anticipation_c": {
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 0.0,
|
||||||
|
"maximum": 2.5,
|
||||||
|
"default": 0.0,
|
||||||
|
"description": "Temperature margin before target for early coasting without turning the unit OFF."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -7362,6 +7369,17 @@
|
|||||||
"updated_at": {
|
"updated_at": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "date-time"
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"heat_anticipation_c": {
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 0.0,
|
||||||
|
"maximum": 2.5,
|
||||||
|
"default": 0.0,
|
||||||
|
"description": "Temperature margin before target for early coasting without turning the unit OFF."
|
||||||
|
},
|
||||||
|
"heat_coasting": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "True when the controller requested low setpoint while keeping unit powered ON."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "gree_controller",
|
"domain": "gree_controller",
|
||||||
"name": "GREE Controller",
|
"name": "GREE Controller",
|
||||||
"version": "0.16.0",
|
"version": "0.16.1",
|
||||||
"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.16.1
|
||||||
|
|
||||||
|
- Adds hysteretic early-heating coasting: lower the GREE target before reaching room comfort temperature without switching the indoor unit OFF.
|
||||||
|
- Supports `heat_anticipation_c` and `standby_offset_c` settings in Visual Flow thermostat actions, including import/export and zone editing.
|
||||||
|
- Shows Flow heating/coasting/standby distinctly from real direct-manual device takeover.
|
||||||
|
- Preserves existing compressor protection and backward compatibility with existing Flow definitions.
|
||||||
|
|
||||||
## 0.16.0
|
## 0.16.0
|
||||||
|
|
||||||
- Improves heating stability when Visual Flow automations use Home Assistant room-temperature sensors.
|
- Improves heating stability when Visual Flow automations use Home Assistant room-temperature sensors.
|
||||||
|
|||||||
@@ -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.16.0 improves heating stability with Home Assistant room-temperature sensor control, reliable Flow ownership, and safer shutdown behavior.
|
Version 0.16.1 adds early heating coasting with GREE setpoint modulation, configurable from Visual Flow, plus distinct Flow/manual status. Compressor protection and shared multisplit shutdown logic remain unchanged.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: "GREE Controller"
|
name: "GREE Controller"
|
||||||
version: "0.16.0"
|
version: "0.16.1"
|
||||||
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/"
|
||||||
|
|||||||
+8
-1
@@ -1548,6 +1548,13 @@
|
|||||||
"flow.contextDeleteConnection": "Delete connection",
|
"flow.contextDeleteConnection": "Delete connection",
|
||||||
"flow.rightClickKey": "Right click",
|
"flow.rightClickKey": "Right click",
|
||||||
"flow.shortcutContextMenu": "Flow context menu",
|
"flow.shortcutContextMenu": "Flow context menu",
|
||||||
"flow.heatingCurveDryRunValue": "Value passed from curve"
|
"flow.heatingCurveDryRunValue": "Value passed from curve",
|
||||||
|
"zones.ownerFlow": "Flow automation",
|
||||||
|
"zones.flowHeating": "Flow active - heating",
|
||||||
|
"zones.flowCoasting": "Flow active - coasting (unit ON, low target)",
|
||||||
|
"zones.flowStandby": "Flow active - soft standby",
|
||||||
|
"zones.heatAnticipation": "Early heat coasting (°C ahead)",
|
||||||
|
"zones.heatAnticipationHint": "Lowers the GREE target before the comfort target without powering the unit off. 0 disables coasting.",
|
||||||
|
"simulation.reasonCoasting": "Early heating coasting before {target}°C: GREE stays ON with a {standby}°C setpoint."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-1
@@ -1548,6 +1548,13 @@
|
|||||||
"flow.contextDeleteConnection": "Usuń połączenie",
|
"flow.contextDeleteConnection": "Usuń połączenie",
|
||||||
"flow.rightClickKey": "Prawy przycisk",
|
"flow.rightClickKey": "Prawy przycisk",
|
||||||
"flow.shortcutContextMenu": "Menu kontekstowe Flow",
|
"flow.shortcutContextMenu": "Menu kontekstowe Flow",
|
||||||
"flow.heatingCurveDryRunValue": "Wartość przekazana z krzywej"
|
"flow.heatingCurveDryRunValue": "Wartość przekazana z krzywej",
|
||||||
|
"zones.ownerFlow": "Flow (automatyka)",
|
||||||
|
"zones.flowHeating": "Flow aktywny - grzanie",
|
||||||
|
"zones.flowCoasting": "Flow aktywny - wyhamowanie (ON, niska nastawa)",
|
||||||
|
"zones.flowStandby": "Flow aktywny - soft standby",
|
||||||
|
"zones.heatAnticipation": "Wyprzedzenie wyhamowania grzania (°C)",
|
||||||
|
"zones.heatAnticipationHint": "Obniża nastawę GREE przed osiągnięciem celu, bez OFF jednostki. 0 wyłącza funkcję.",
|
||||||
|
"simulation.reasonCoasting": "Wyhamowanie grzania przed celem {target}°C: GREE utrzymuje jednostkę ON z nastawą {standby}°C."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Developer smoke/integration tests for GREE Controller API 0.16.0.
|
"""Developer smoke/integration tests for GREE Controller API 0.16.1.
|
||||||
|
|
||||||
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.16.0"
|
DEFAULT_EXPECTED_VERSION = "0.16.1"
|
||||||
|
|
||||||
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.16.0 contract", lambda: test_openapi(client, args.expected_version))
|
runner.run("OpenAPI 0.16.1 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))
|
||||||
|
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ impl AutomationInput {
|
|||||||
last_trigger_trace: vec![],
|
last_trigger_trace: vec![],
|
||||||
action_zone_id: None,
|
action_zone_id: None,
|
||||||
action_zone_preset: None,
|
action_zone_preset: None,
|
||||||
|
action_zone_heat_anticipation_c: None,
|
||||||
|
action_zone_standby_offset_c: None,
|
||||||
action_ha_domain: None,
|
action_ha_domain: None,
|
||||||
action_ha_service: None,
|
action_ha_service: None,
|
||||||
action_ha_entity_id: None,
|
action_ha_entity_id: None,
|
||||||
|
|||||||
@@ -1563,6 +1563,8 @@ fn compile_flow(
|
|||||||
last_trigger_trace,
|
last_trigger_trace,
|
||||||
action_zone_id: None,
|
action_zone_id: None,
|
||||||
action_zone_preset: None,
|
action_zone_preset: None,
|
||||||
|
action_zone_heat_anticipation_c: None,
|
||||||
|
action_zone_standby_offset_c: None,
|
||||||
action_ha_domain: None,
|
action_ha_domain: None,
|
||||||
action_ha_service: None,
|
action_ha_service: None,
|
||||||
action_ha_entity_id: None,
|
action_ha_entity_id: None,
|
||||||
@@ -1606,6 +1608,32 @@ fn compile_flow(
|
|||||||
return Err(AppError::BadRequest("unsupported thermostat preset".into()));
|
return Err(AppError::BadRequest("unsupported thermostat preset".into()));
|
||||||
}
|
}
|
||||||
item.action_zone_preset = Some(preset.clone());
|
item.action_zone_preset = Some(preset.clone());
|
||||||
|
if let Some(value) = action_node.config.get("heat_anticipation_c") {
|
||||||
|
if !value.is_null() {
|
||||||
|
let offset = value.as_f64().ok_or_else(|| AppError::BadRequest(
|
||||||
|
"Flow heat anticipation must be numeric".into(),
|
||||||
|
))?;
|
||||||
|
if !offset.is_finite() || !(0.0..=2.5).contains(&offset) {
|
||||||
|
return Err(AppError::BadRequest(
|
||||||
|
"Flow heat anticipation must be between 0 and 2.5 C".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
item.action_zone_heat_anticipation_c = Some(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(value) = action_node.config.get("standby_offset_c") {
|
||||||
|
if !value.is_null() {
|
||||||
|
let offset = value.as_f64().ok_or_else(|| AppError::BadRequest(
|
||||||
|
"Flow standby offset must be numeric".into(),
|
||||||
|
))?;
|
||||||
|
if !offset.is_finite() || !(0.5..=8.0).contains(&offset) {
|
||||||
|
return Err(AppError::BadRequest(
|
||||||
|
"Flow standby offset must be between 0.5 and 8 C".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
item.action_zone_standby_offset_c = Some(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
if preset == "custom" && value_binding.is_none() {
|
if preset == "custom" && value_binding.is_none() {
|
||||||
let target = flow_f64(&action_node.config, "setpoint").ok_or_else(|| {
|
let target = flow_f64(&action_node.config, "setpoint").ok_or_else(|| {
|
||||||
AppError::BadRequest("custom thermostat block needs a target".into())
|
AppError::BadRequest("custom thermostat block needs a target".into())
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ struct ZoneInput {
|
|||||||
min_off_seconds: u64,
|
min_off_seconds: u64,
|
||||||
#[serde(default = "min_adjust")]
|
#[serde(default = "min_adjust")]
|
||||||
min_adjust_seconds: u64,
|
min_adjust_seconds: u64,
|
||||||
|
#[serde(default)]
|
||||||
|
heat_anticipation_c: f64,
|
||||||
#[serde(default = "standby_offset")]
|
#[serde(default = "standby_offset")]
|
||||||
standby_offset_c: f64,
|
standby_offset_c: f64,
|
||||||
#[serde(default = "yes")]
|
#[serde(default = "yes")]
|
||||||
@@ -142,6 +144,13 @@ impl ZoneInput {
|
|||||||
"heating hysteresis must be between 0.1 and 5 C".into(),
|
"heating hysteresis must be between 0.1 and 5 C".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if !self.heat_anticipation_c.is_finite()
|
||||||
|
|| !(0.0..=2.5).contains(&self.heat_anticipation_c)
|
||||||
|
{
|
||||||
|
return Err(AppError::BadRequest(
|
||||||
|
"heat anticipation must be between 0 and 2.5 C".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
if !(0.5..=8.0).contains(&self.standby_offset_c) {
|
if !(0.5..=8.0).contains(&self.standby_offset_c) {
|
||||||
return Err(AppError::BadRequest(
|
return Err(AppError::BadRequest(
|
||||||
"standby offset must be between 0.5 and 8 C".into(),
|
"standby offset must be between 0.5 and 8 C".into(),
|
||||||
@@ -205,6 +214,8 @@ impl ZoneInput {
|
|||||||
min_on_seconds: self.min_on_seconds,
|
min_on_seconds: self.min_on_seconds,
|
||||||
min_off_seconds: self.min_off_seconds,
|
min_off_seconds: self.min_off_seconds,
|
||||||
min_adjust_seconds: self.min_adjust_seconds,
|
min_adjust_seconds: self.min_adjust_seconds,
|
||||||
|
heat_anticipation_c: self.heat_anticipation_c,
|
||||||
|
heat_coasting: false,
|
||||||
standby_offset_c: self.standby_offset_c,
|
standby_offset_c: self.standby_offset_c,
|
||||||
smart_fan: self.smart_fan,
|
smart_fan: self.smart_fan,
|
||||||
sensor_source: self.sensor_source,
|
sensor_source: self.sensor_source,
|
||||||
@@ -400,6 +411,7 @@ async fn update_zone(
|
|||||||
zone.effective_mode = existing.effective_mode;
|
zone.effective_mode = existing.effective_mode;
|
||||||
zone.effective_setpoint = existing.effective_setpoint;
|
zone.effective_setpoint = existing.effective_setpoint;
|
||||||
zone.device_setpoint = existing.device_setpoint;
|
zone.device_setpoint = existing.device_setpoint;
|
||||||
|
zone.heat_coasting = existing.heat_coasting;
|
||||||
zone.demand = existing.demand;
|
zone.demand = existing.demand;
|
||||||
zone.demand_since = existing.demand_since;
|
zone.demand_since = existing.demand_since;
|
||||||
zone.target_alerted_at = existing.target_alerted_at;
|
zone.target_alerted_at = existing.target_alerted_at;
|
||||||
|
|||||||
@@ -31,7 +31,14 @@ fn flow_zone_action_already_satisfied(
|
|||||||
device_swing_horizontal: u8,
|
device_swing_horizontal: u8,
|
||||||
preset: Option<&str>,
|
preset: Option<&str>,
|
||||||
action: &DeviceCommand,
|
action: &DeviceCommand,
|
||||||
|
heat_anticipation_c: Option<f64>,
|
||||||
|
standby_offset_c: Option<f64>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
if heat_anticipation_c.is_some_and(|value| (zone.heat_anticipation_c - value).abs() > 0.001)
|
||||||
|
|| standby_offset_c.is_some_and(|value| (zone.standby_offset_c - value).abs() > 0.001)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
let mode_satisfied = match action.mode.as_deref() {
|
let mode_satisfied = match action.mode.as_deref() {
|
||||||
Some("auto") => zone.inherit_house_mode,
|
Some("auto") => zone.inherit_house_mode,
|
||||||
Some(mode) if matches!(mode, "heat" | "cool") => {
|
Some(mode) if matches!(mode, "heat" | "cool") => {
|
||||||
@@ -193,6 +200,8 @@ async fn run_automations(state: &AppState) -> Result<()> {
|
|||||||
device.swing_horizontal,
|
device.swing_horizontal,
|
||||||
item.action_zone_preset.as_deref(),
|
item.action_zone_preset.as_deref(),
|
||||||
&effective_action,
|
&effective_action,
|
||||||
|
item.action_zone_heat_anticipation_c,
|
||||||
|
item.action_zone_standby_offset_c,
|
||||||
) {
|
) {
|
||||||
let before_runtime = item.flow_runtime.clone();
|
let before_runtime = item.flow_runtime.clone();
|
||||||
flow_acknowledge_change_gates(&mut item);
|
flow_acknowledge_change_gates(&mut item);
|
||||||
@@ -277,7 +286,10 @@ async fn run_automations(state: &AppState) -> Result<()> {
|
|||||||
Err(err) => Err(AppError::BadRequest(format!("Home Assistant service action failed: {err}"))),
|
Err(err) => Err(AppError::BadRequest(format!("Home Assistant service action failed: {err}"))),
|
||||||
}
|
}
|
||||||
} else if let Some(zone_id) = item.action_zone_id.as_deref() {
|
} else if let Some(zone_id) = item.action_zone_id.as_deref() {
|
||||||
apply_flow_zone_action(state, zone_id, item.action_zone_preset.as_deref(), &effective_action).await
|
apply_flow_zone_action(
|
||||||
|
state, zone_id, item.action_zone_preset.as_deref(), &effective_action,
|
||||||
|
item.action_zone_heat_anticipation_c, item.action_zone_standby_offset_c,
|
||||||
|
).await
|
||||||
} else if let Some(group_id) = item.action_group_id.as_deref() {
|
} else if let Some(group_id) = item.action_group_id.as_deref() {
|
||||||
let group_mode = effective_action.mode.as_deref().map(|mode| if mode == "auto" { "house".to_string() } else { mode.to_string() });
|
let group_mode = effective_action.mode.as_deref().map(|mode| if mode == "auto" { "house".to_string() } else { mode.to_string() });
|
||||||
control_group(state, group_id, GroupControlPatch {
|
control_group(state, group_id, GroupControlPatch {
|
||||||
@@ -1105,7 +1117,10 @@ fn flow_condition_kind_runtime(kind: &str) -> bool {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn apply_flow_zone_action(state: &AppState, zone_id: &str, preset: Option<&str>, action: &DeviceCommand) -> Result<bool, AppError> {
|
async fn apply_flow_zone_action(
|
||||||
|
state: &AppState, zone_id: &str, preset: Option<&str>, action: &DeviceCommand,
|
||||||
|
heat_anticipation_c: Option<f64>, standby_offset_c: Option<f64>,
|
||||||
|
) -> Result<bool, AppError> {
|
||||||
let _schedule_guard = state.lock_schedule_operation().await;
|
let _schedule_guard = state.lock_schedule_operation().await;
|
||||||
// Match the canonical thermostat ordering used by Web/HA zone control. This prevents
|
// Match the canonical thermostat ordering used by Web/HA zone control. This prevents
|
||||||
// a Flow action from racing a thermostat arbitration cycle that already holds a stale snapshot.
|
// a Flow action from racing a thermostat arbitration cycle that already holds a stale snapshot.
|
||||||
@@ -1117,6 +1132,8 @@ async fn apply_flow_zone_action(state: &AppState, zone_id: &str, preset: Option<
|
|||||||
// Flow power commands control only the assigned unit runtime; they never change zone.enabled.
|
// Flow power commands control only the assigned unit runtime; they never change zone.enabled.
|
||||||
if !zone.enabled { return Ok(false); }
|
if !zone.enabled { return Ok(false); }
|
||||||
let device_id = zone.device_id.clone();
|
let device_id = zone.device_id.clone();
|
||||||
|
if let Some(value) = heat_anticipation_c { zone.heat_anticipation_c = value; }
|
||||||
|
if let Some(value) = standby_offset_c { zone.standby_offset_c = value; }
|
||||||
if let Some(mode) = action.mode.as_deref() {
|
if let Some(mode) = action.mode.as_deref() {
|
||||||
match mode { "auto" => zone.inherit_house_mode = true, "heat" | "cool" => { zone.mode = mode.to_string(); zone.inherit_house_mode = false; }, _ => return Err(AppError::BadRequest("unsupported Flow thermostat mode".into())) }
|
match mode { "auto" => zone.inherit_house_mode = true, "heat" | "cool" => { zone.mode = mode.to_string(); zone.inherit_house_mode = false; }, _ => return Err(AppError::BadRequest("unsupported Flow thermostat mode".into())) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -638,7 +638,7 @@ fn externally_changed_control_fields(before: &Device, after: &Device, zone: &Zon
|
|||||||
// again without user interaction. Treat that one known normalization as firmware drift,
|
// again without user interaction. Treat that one known normalization as firmware drift,
|
||||||
// not as a remote-control takeover. Other fan changes remain meaningful manual input.
|
// not as a remote-control takeover. Other fan changes remain meaningful manual input.
|
||||||
let standby_low_to_auto =
|
let standby_low_to_auto =
|
||||||
zone.smart_fan && !zone.demand && before.fan_speed == 1 && after.fan_speed == 0;
|
zone.smart_fan && (!zone.demand || zone.heat_coasting) && before.fan_speed == 1 && after.fan_speed == 0;
|
||||||
if before.fan_speed != after.fan_speed && !standby_low_to_auto {
|
if before.fan_speed != after.fan_speed && !standby_low_to_auto {
|
||||||
fields.push("fan_speed".to_string());
|
fields.push("fan_speed".to_string());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,24 @@
|
|||||||
|
/// GREE stays powered during coasting: only its temperature target is lowered.
|
||||||
|
/// Separate enter/exit thresholds prevent target chatter on a noisy HA sensor.
|
||||||
|
fn heat_coasting_next_state(
|
||||||
|
was_coasting: bool,
|
||||||
|
mode: &str,
|
||||||
|
temperature: f64,
|
||||||
|
comfort_target: f64,
|
||||||
|
anticipation_c: f64,
|
||||||
|
) -> bool {
|
||||||
|
if mode != "heat" || anticipation_c <= 0.0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let enter_at = comfort_target - anticipation_c;
|
||||||
|
let resume_heat_at = enter_at - 0.4;
|
||||||
|
if was_coasting {
|
||||||
|
temperature > resume_heat_at + 1e-6
|
||||||
|
} else {
|
||||||
|
temperature + 1e-6 >= enter_at
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn select_zone_temperature(
|
fn select_zone_temperature(
|
||||||
zone: &Zone,
|
zone: &Zone,
|
||||||
device_temperature: Option<f64>,
|
device_temperature: Option<f64>,
|
||||||
|
|||||||
+28
-5
@@ -3,6 +3,17 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn heat_coasting_modulates_setpoint_without_power_cycle() {
|
||||||
|
assert!(!heat_coasting_next_state(false, "heat", 19.8, 20.8, 0.9));
|
||||||
|
assert!(heat_coasting_next_state(false, "heat", 19.9, 20.8, 0.9));
|
||||||
|
assert!(heat_coasting_next_state(true, "heat", 19.6, 20.8, 0.9));
|
||||||
|
assert!(!heat_coasting_next_state(true, "heat", 19.5, 20.8, 0.9));
|
||||||
|
assert!(!heat_coasting_next_state(true, "heat", 21.0, 20.8, 0.0));
|
||||||
|
assert!(!heat_coasting_next_state(true, "cool", 21.0, 20.8, 0.9));
|
||||||
|
assert_eq!(round_device_setpoint("heat", false, 20.8 - 5.0), 16.0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn thermostat_target_normalization_preserves_tenths() {
|
fn thermostat_target_normalization_preserves_tenths() {
|
||||||
assert!((normalize_thermostat_target(24.2) - 24.2).abs() < 1e-9);
|
assert!((normalize_thermostat_target(24.2) - 24.2).abs() < 1e-9);
|
||||||
@@ -242,7 +253,15 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
assert!(flow_zone_action_already_satisfied(
|
assert!(flow_zone_action_already_satisfied(
|
||||||
&zone, false, 0, 0, Some("custom"), &on
|
&zone, false, 0, 0, Some("custom"), &on, None, None
|
||||||
|
));
|
||||||
|
assert!(!flow_zone_action_already_satisfied(
|
||||||
|
&zone, false, 0, 0, Some("custom"), &on, Some(1.1), Some(5.0)
|
||||||
|
));
|
||||||
|
zone.heat_anticipation_c = 1.1;
|
||||||
|
zone.standby_offset_c = 5.0;
|
||||||
|
assert!(flow_zone_action_already_satisfied(
|
||||||
|
&zone, false, 0, 0, Some("custom"), &on, Some(1.1), Some(5.0)
|
||||||
));
|
));
|
||||||
|
|
||||||
zone.manual_preset = None;
|
zone.manual_preset = None;
|
||||||
@@ -257,10 +276,10 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
assert!(flow_zone_action_already_satisfied(
|
assert!(flow_zone_action_already_satisfied(
|
||||||
&zone, false, 0, 0, Some("auto"), &off
|
&zone, false, 0, 0, Some("auto"), &off, None, None
|
||||||
));
|
));
|
||||||
assert!(!flow_zone_action_already_satisfied(
|
assert!(!flow_zone_action_already_satisfied(
|
||||||
&zone, true, 0, 0, Some("auto"), &off
|
&zone, true, 0, 0, Some("auto"), &off, None, None
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,10 +298,10 @@ mod tests {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
assert!(!flow_zone_action_already_satisfied(
|
assert!(!flow_zone_action_already_satisfied(
|
||||||
&zone, true, 0, 0, Some("custom"), &action
|
&zone, true, 0, 0, Some("custom"), &action, None, None
|
||||||
));
|
));
|
||||||
assert!(flow_zone_action_already_satisfied(
|
assert!(flow_zone_action_already_satisfied(
|
||||||
&zone, true, 1, 0, Some("custom"), &action
|
&zone, true, 1, 0, Some("custom"), &action, None, None
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +566,8 @@ mod tests {
|
|||||||
last_trigger_trace: vec![],
|
last_trigger_trace: vec![],
|
||||||
action_zone_id: None,
|
action_zone_id: None,
|
||||||
action_zone_preset: None,
|
action_zone_preset: None,
|
||||||
|
action_zone_heat_anticipation_c: None,
|
||||||
|
action_zone_standby_offset_c: None,
|
||||||
action_ha_domain: None,
|
action_ha_domain: None,
|
||||||
action_ha_service: None,
|
action_ha_service: None,
|
||||||
action_ha_entity_id: None,
|
action_ha_entity_id: None,
|
||||||
@@ -589,6 +610,8 @@ mod tests {
|
|||||||
min_on_seconds: 180,
|
min_on_seconds: 180,
|
||||||
min_off_seconds: 180,
|
min_off_seconds: 180,
|
||||||
min_adjust_seconds: 120,
|
min_adjust_seconds: 120,
|
||||||
|
heat_anticipation_c: 0.0,
|
||||||
|
heat_coasting: false,
|
||||||
standby_offset_c: 2.0,
|
standby_offset_c: 2.0,
|
||||||
smart_fan: true,
|
smart_fan: true,
|
||||||
sensor_source: source.into(),
|
sensor_source: source.into(),
|
||||||
|
|||||||
@@ -683,6 +683,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
// publishing the zone, but never overwrite manual device state while it follows
|
// publishing the zone, but never overwrite manual device state while it follows
|
||||||
// the house mode. Explicit per-zone heat/cool bypasses this branch above.
|
// the house mode. Explicit per-zone heat/cool bypasses this branch above.
|
||||||
if effective_mode == "off" {
|
if effective_mode == "off" {
|
||||||
|
zone.heat_coasting = false;
|
||||||
zone.effective_setpoint = None;
|
zone.effective_setpoint = None;
|
||||||
zone.device_setpoint = None;
|
zone.device_setpoint = None;
|
||||||
zone.demand = false;
|
zone.demand = false;
|
||||||
@@ -709,6 +710,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
// controller stays physically OFF. This also makes gaps between schedule windows true
|
// controller stays physically OFF. This also makes gaps between schedule windows true
|
||||||
// OFF periods instead of silently falling back to Comfort and recreating demand.
|
// OFF periods instead of silently falling back to Comfort and recreating demand.
|
||||||
if !zone_has_active_thermostat_intent(&zone, active_schedule, Utc::now()) {
|
if !zone_has_active_thermostat_intent(&zone, active_schedule, Utc::now()) {
|
||||||
|
zone.heat_coasting = false;
|
||||||
clear_compressor_pending(&mut zone, true);
|
clear_compressor_pending(&mut zone, true);
|
||||||
zone.effective_mode = "off".into();
|
zone.effective_mode = "off".into();
|
||||||
zone.effective_setpoint = None;
|
zone.effective_setpoint = None;
|
||||||
@@ -802,6 +804,14 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
|
|
||||||
let half = zone.hysteresis_for_mode(effective_mode) / 2.0;
|
let half = zone.hysteresis_for_mode(effective_mode) / 2.0;
|
||||||
let previous_demand = zone.demand;
|
let previous_demand = zone.demand;
|
||||||
|
let previous_heat_coasting = zone.heat_coasting;
|
||||||
|
zone.heat_coasting = heat_coasting_next_state(
|
||||||
|
zone.heat_coasting,
|
||||||
|
effective_mode,
|
||||||
|
temp,
|
||||||
|
target,
|
||||||
|
zone.heat_anticipation_c,
|
||||||
|
);
|
||||||
zone.demand = match effective_mode {
|
zone.demand = match effective_mode {
|
||||||
"heat" => {
|
"heat" => {
|
||||||
if temp <= target - half {
|
if temp <= target - half {
|
||||||
@@ -857,14 +867,17 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
"heat" => target + outdoor_assist,
|
"heat" => target + outdoor_assist,
|
||||||
_ => target - demand_assist,
|
_ => target - demand_assist,
|
||||||
};
|
};
|
||||||
|
// Early heat coasting changes only GREE's requested temperature. It does NOT
|
||||||
|
// toggle unit power or touch compressor protection/whole-multisplit OFF policy.
|
||||||
|
let delivering_heat = zone.demand && !(effective_mode == "heat" && zone.heat_coasting);
|
||||||
let standby_target = match effective_mode {
|
let standby_target = match effective_mode {
|
||||||
"heat" => target - zone.standby_offset_c.max(0.5),
|
"heat" => target - zone.standby_offset_c.max(0.5),
|
||||||
_ => target + zone.standby_offset_c.max(0.5),
|
_ => target + zone.standby_offset_c.max(0.5),
|
||||||
};
|
};
|
||||||
let desired_device_target = round_device_setpoint(
|
let desired_device_target = round_device_setpoint(
|
||||||
effective_mode,
|
effective_mode,
|
||||||
zone.demand,
|
delivering_heat,
|
||||||
if zone.demand {
|
if delivering_heat {
|
||||||
active_target
|
active_target
|
||||||
} else {
|
} else {
|
||||||
standby_target
|
standby_target
|
||||||
@@ -878,7 +891,10 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let demand_changed = previous_demand != zone.demand;
|
let demand_changed = previous_demand != zone.demand
|
||||||
|
|| (effective_mode == "heat" && previous_heat_coasting != zone.heat_coasting);
|
||||||
|
let previous_delivering_heat = previous_demand
|
||||||
|
&& !(effective_mode == "heat" && previous_heat_coasting);
|
||||||
let desired_fan = if night_active {
|
let desired_fan = if night_active {
|
||||||
let max_fan = settings.night_mode.max_fan_speed.clamp(1, 5);
|
let max_fan = settings.night_mode.max_fan_speed.clamp(1, 5);
|
||||||
if zone.smart_fan {
|
if zone.smart_fan {
|
||||||
@@ -888,7 +904,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
temp,
|
temp,
|
||||||
target,
|
target,
|
||||||
outdoor_assist_temperature,
|
outdoor_assist_temperature,
|
||||||
zone.demand,
|
delivering_heat,
|
||||||
),
|
),
|
||||||
max_fan,
|
max_fan,
|
||||||
))
|
))
|
||||||
@@ -903,7 +919,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
temp,
|
temp,
|
||||||
target,
|
target,
|
||||||
outdoor_assist_temperature,
|
outdoor_assist_temperature,
|
||||||
zone.demand,
|
delivering_heat,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -934,8 +950,8 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
let desired_quiet = smart_quiet_command(
|
let desired_quiet = smart_quiet_command(
|
||||||
zone.smart_fan,
|
zone.smart_fan,
|
||||||
quiet_supported,
|
quiet_supported,
|
||||||
previous_demand,
|
previous_delivering_heat,
|
||||||
zone.demand,
|
delivering_heat,
|
||||||
device.quiet,
|
device.quiet,
|
||||||
settings.night_mode.enabled,
|
settings.night_mode.enabled,
|
||||||
night_active,
|
night_active,
|
||||||
@@ -1113,7 +1129,7 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
let fan_needs_command = desired_fan
|
let fan_needs_command = desired_fan
|
||||||
.map(|fan| fan != device.fan_speed)
|
.map(|fan| fan != device.fan_speed)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
&& (zone.demand || demand_changed || core_needs_command || night_active);
|
&& (delivering_heat || demand_changed || core_needs_command || (night_active && !zone.smart_fan));
|
||||||
let needs_command = core_needs_command
|
let needs_command = core_needs_command
|
||||||
|| fan_needs_command
|
|| fan_needs_command
|
||||||
|| desired_quiet
|
|| desired_quiet
|
||||||
@@ -1150,10 +1166,12 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
|||||||
};
|
};
|
||||||
zone.last_action_at = Some(transition_at);
|
zone.last_action_at = Some(transition_at);
|
||||||
clear_compressor_pending(&mut zone, true);
|
clear_compressor_pending(&mut zone, true);
|
||||||
state.log("info", "zone.setpoint_modulation", &format!("Zone {} -> {:.1} C ({})", zone.name, desired_device_target, if zone.demand { "demand" } else { "standby" }), json!({
|
state.log("info", "zone.setpoint_modulation", &format!("Zone {} -> {:.1} C ({})", zone.name, desired_device_target, if zone.heat_coasting { "coasting" } else if zone.demand { "demand" } else { "standby" }), json!({
|
||||||
"zone_id": zone.id,
|
"zone_id": zone.id,
|
||||||
"room_temperature": temp,
|
"room_temperature": temp,
|
||||||
"comfort_target": target,
|
"comfort_target": target,
|
||||||
|
"heat_coasting": zone.heat_coasting,
|
||||||
|
"heat_anticipation_c": zone.heat_anticipation_c,
|
||||||
"device_target": desired_device_target,
|
"device_target": desired_device_target,
|
||||||
"mode": effective_mode,
|
"mode": effective_mode,
|
||||||
"preset": zone.active_preset,
|
"preset": zone.active_preset,
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ pub struct Automation {
|
|||||||
pub action_zone_id: Option<String>,
|
pub action_zone_id: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub action_zone_preset: Option<String>,
|
pub action_zone_preset: Option<String>,
|
||||||
|
/// Optional soft-coast parameters set by a Flow thermostat action.
|
||||||
|
#[serde(default)]
|
||||||
|
pub action_zone_heat_anticipation_c: Option<f64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub action_zone_standby_offset_c: Option<f64>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub action_ha_domain: Option<String>,
|
pub action_ha_domain: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ pub struct Zone {
|
|||||||
/// Minimum interval between automatic setpoint/fan adjustments.
|
/// Minimum interval between automatic setpoint/fan adjustments.
|
||||||
#[serde(default = "default_min_adjust")]
|
#[serde(default = "default_min_adjust")]
|
||||||
pub min_adjust_seconds: u64,
|
pub min_adjust_seconds: u64,
|
||||||
|
/// Degrees before the comfort target at which GREE enters low-heat coasting.
|
||||||
|
/// Zero disables predictive soft standby (the pre-upgrade behavior).
|
||||||
|
#[serde(default)]
|
||||||
|
pub heat_anticipation_c: f64,
|
||||||
|
/// Hysteretic coasting state; never changes the physical unit power state.
|
||||||
|
#[serde(default)]
|
||||||
|
pub heat_coasting: bool,
|
||||||
/// Difference applied to the AC setpoint while the room is satisfied.
|
/// Difference applied to the AC setpoint while the room is satisfied.
|
||||||
#[serde(default = "default_standby_offset")]
|
#[serde(default = "default_standby_offset")]
|
||||||
pub standby_offset_c: f64,
|
pub standby_offset_c: f64,
|
||||||
|
|||||||
@@ -1598,6 +1598,9 @@
|
|||||||
type="number" min="15" name="min_adjust_seconds" value="120"></label><label><span
|
type="number" min="15" name="min_adjust_seconds" value="120"></label><label><span
|
||||||
data-i18n="zones.standbyOffset">Standby setpoint offset °C</span><input type="text" inputmode="decimal"
|
data-i18n="zones.standbyOffset">Standby setpoint offset °C</span><input type="text" inputmode="decimal"
|
||||||
step="0.5" min="0.5" max="8" name="standby_offset_c" value="2"></label></div>
|
step="0.5" min="0.5" max="8" name="standby_offset_c" value="2"></label></div>
|
||||||
|
<label><span data-i18n="zones.heatAnticipation">Early heat coasting (°C before target)</span><input
|
||||||
|
type="text" inputmode="decimal" step="0.1" min="0" max="2.5" name="heat_anticipation_c" value="0"></label>
|
||||||
|
<p class="field-note" data-i18n="zones.heatAnticipationHint">A positive value lowers the GREE setpoint early without powering the indoor unit off. 0 disables early coasting.</p>
|
||||||
<label class="check"><input type="checkbox" name="smart_fan" checked> <span data-i18n="zones.smartFan">Smart fan
|
<label class="check"><input type="checkbox" name="smart_fan" checked> <span data-i18n="zones.smartFan">Smart fan
|
||||||
assist</span></label>
|
assist</span></label>
|
||||||
<input type="hidden" name="min_on_seconds" value="180"><input type="hidden" name="min_off_seconds"
|
<input type="hidden" name="min_on_seconds" value="180"><input type="hidden" name="min_off_seconds"
|
||||||
|
|||||||
@@ -405,15 +405,20 @@ function buildZoneSimulation(zone, planZone) {
|
|||||||
status = demand ? 'demand' : 'satisfied';
|
status = demand ? 'demand' : 'satisfied';
|
||||||
}
|
}
|
||||||
const standbyOffset = Math.max(simulationNumeric(zone?.standby_offset_c) || 0.5, 0.5);
|
const standbyOffset = Math.max(simulationNumeric(zone?.standby_offset_c) || 0.5, 0.5);
|
||||||
const assist = current == null || target == null ? 0 : simulationOutdoorAssist(mode, outdoor, current, target);
|
const anticipation = Math.max(simulationNumeric(zone?.heat_anticipation_c) || 0, 0);
|
||||||
|
const coastEnter = target == null ? null : target - anticipation;
|
||||||
|
const heatCoasting = mode === 'heat' && anticipation > 0 && current != null && coastEnter != null
|
||||||
|
&& (zone?.heat_coasting ? current > coastEnter - 0.4 + 1e-6 : current + 1e-6 >= coastEnter);
|
||||||
|
const deliveringHeat = demand && !heatCoasting;
|
||||||
|
const assist = current == null || target == null || zone?.control_source === 'automation.flow' ? 0 : simulationOutdoorAssist(mode, outdoor, current, target);
|
||||||
const activeTarget = current == null || target == null ? null : (mode === 'heat' ? target + assist : target - assist);
|
const activeTarget = current == null || target == null ? null : (mode === 'heat' ? target + assist : target - assist);
|
||||||
const standbyTarget = target == null ? null : (mode === 'heat' ? target - standbyOffset : target + standbyOffset);
|
const standbyTarget = target == null ? null : (mode === 'heat' ? target - standbyOffset : target + standbyOffset);
|
||||||
const desiredDeviceTarget = current == null || target == null ? null : simulationRoundDeviceSetpoint(mode, demand, demand ? activeTarget : standbyTarget);
|
const desiredDeviceTarget = current == null || target == null ? null : simulationRoundDeviceSetpoint(mode, deliveringHeat, deliveringHeat ? activeTarget : standbyTarget);
|
||||||
const nightActive = !!app.controlPlan?.night_mode_active;
|
const nightActive = !!app.controlPlan?.night_mode_active;
|
||||||
const nightMaxFan = clamp(Number(app.controlPlan?.night_mode_max_fan_speed || 1), 1, 5);
|
const nightMaxFan = clamp(Number(app.controlPlan?.night_mode_max_fan_speed || 1), 1, 5);
|
||||||
let fanSpeed = simulationNumeric(device?.fan_speed);
|
let fanSpeed = simulationNumeric(device?.fan_speed);
|
||||||
if (mode !== 'off') {
|
if (mode !== 'off') {
|
||||||
fanSpeed = zone?.smart_fan && current != null && target != null ? simulationSmartFanSpeed(mode, current, target, outdoor, demand) : fanSpeed;
|
fanSpeed = zone?.smart_fan && current != null && target != null ? simulationSmartFanSpeed(mode, current, target, outdoor, deliveringHeat) : fanSpeed;
|
||||||
if (nightActive) {
|
if (nightActive) {
|
||||||
if (zone?.smart_fan) fanSpeed = fanSpeed === 0 ? 1 : Math.min(fanSpeed ?? nightMaxFan, nightMaxFan);
|
if (zone?.smart_fan) fanSpeed = fanSpeed === 0 ? 1 : Math.min(fanSpeed ?? nightMaxFan, nightMaxFan);
|
||||||
else if (fanSpeed === 0 || fanSpeed == null || fanSpeed > nightMaxFan) fanSpeed = nightMaxFan;
|
else if (fanSpeed === 0 || fanSpeed == null || fanSpeed > nightMaxFan) fanSpeed = nightMaxFan;
|
||||||
@@ -423,7 +428,7 @@ function buildZoneSimulation(zone, planZone) {
|
|||||||
? tr('simulation.manual')
|
? tr('simulation.manual')
|
||||||
: nightActive && app.settings?.night_mode?.force_quiet
|
: nightActive && app.settings?.night_mode?.force_quiet
|
||||||
? tr('simulation.quietIfSupported')
|
? tr('simulation.quietIfSupported')
|
||||||
: (zone?.smart_fan ? (!demand ? tr('simulation.quietIfSupported') : tr('common.off')) : tr('simulation.manual'));
|
: (zone?.smart_fan ? (!deliveringHeat ? tr('simulation.quietIfSupported') : tr('common.off')) : tr('simulation.manual'));
|
||||||
const nativeSleep = mode !== 'off' && nightActive && app.settings?.night_mode?.use_native_sleep && device?.supports_sleep === true;
|
const nativeSleep = mode !== 'off' && nightActive && app.settings?.night_mode?.use_native_sleep && device?.supports_sleep === true;
|
||||||
const reasoning = !zone?.enabled || planZoneEffectiveEnabled(planZone) === false
|
const reasoning = !zone?.enabled || planZoneEffectiveEnabled(planZone) === false
|
||||||
? tr('simulation.reasonDisabled')
|
? tr('simulation.reasonDisabled')
|
||||||
@@ -431,10 +436,12 @@ function buildZoneSimulation(zone, planZone) {
|
|||||||
? tr('simulation.reasonHouseOff')
|
? tr('simulation.reasonHouseOff')
|
||||||
: current == null || target == null
|
: current == null || target == null
|
||||||
? tr('simulation.reasonWaiting')
|
? tr('simulation.reasonWaiting')
|
||||||
: demand
|
: heatCoasting
|
||||||
|
? tr('simulation.reasonCoasting', { target: target.toFixed(1), standby: standbyTarget.toFixed(1) })
|
||||||
|
: demand
|
||||||
? tr('simulation.reasonDemand', { mode: modeLabel(mode), target: target.toFixed(1), hysteresis: hysteresis.toFixed(1) })
|
? tr('simulation.reasonDemand', { mode: modeLabel(mode), target: target.toFixed(1), hysteresis: hysteresis.toFixed(1) })
|
||||||
: tr('simulation.reasonSatisfied', { target: target.toFixed(1), standby: standbyTarget.toFixed(1) });
|
: tr('simulation.reasonSatisfied', { target: target.toFixed(1), standby: standbyTarget.toFixed(1) });
|
||||||
return { device, mode, current, target, demand, status, outdoor, hysteresis, assist, activeTarget, standbyTarget, desiredDeviceTarget, fanSpeed, quiet, nativeSleep, reasoning };
|
return { device, mode, current, target, demand, heatCoasting, status, outdoor, hysteresis, assist, activeTarget, standbyTarget, desiredDeviceTarget, fanSpeed, quiet, nativeSleep, reasoning };
|
||||||
}
|
}
|
||||||
|
|
||||||
function simulationRuleAction(rule) {
|
function simulationRuleAction(rule) {
|
||||||
|
|||||||
@@ -453,6 +453,7 @@ function zoneControlOwnerLabel(zone) {
|
|||||||
}
|
}
|
||||||
if (owner === 'local_thermostat') return tr('zones.ownerLocalThermostat');
|
if (owner === 'local_thermostat') return tr('zones.ownerLocalThermostat');
|
||||||
if (source.startsWith('group:')) return tr('zones.ownerGroup');
|
if (source.startsWith('group:')) return tr('zones.ownerGroup');
|
||||||
|
if (source === 'automation.flow') return tr('zones.ownerFlow');
|
||||||
return tr('zones.ownerAutomation');
|
return tr('zones.ownerAutomation');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -478,6 +479,10 @@ function zoneRuntimeStatusLabel(zone, effectiveMode, device = null) {
|
|||||||
if (zone.compressor_cancelled_action && device && (!device.power || (['heat', 'cool'].includes(effectiveMode) && device.mode !== effectiveMode))) return tr('zones.queueCancelledStatus');
|
if (zone.compressor_cancelled_action && device && (!device.power || (['heat', 'cool'].includes(effectiveMode) && device.mode !== effectiveMode))) return tr('zones.queueCancelledStatus');
|
||||||
if (zone.demand && device && !device.power) return tr('zones.waitingStart');
|
if (zone.demand && device && !device.power) return tr('zones.waitingStart');
|
||||||
if (zone.demand && device?.power && ['heat', 'cool'].includes(effectiveMode) && device.mode !== effectiveMode) return tr('zones.waitingMode');
|
if (zone.demand && device?.power && ['heat', 'cool'].includes(effectiveMode) && device.mode !== effectiveMode) return tr('zones.waitingMode');
|
||||||
|
if (zone.control_owner === 'automation' && zone.control_source === 'automation.flow') {
|
||||||
|
if (zone.demand && zone.heat_coasting && device?.power) return tr('zones.flowCoasting');
|
||||||
|
return zone.demand ? tr('zones.flowHeating') : tr('zones.flowStandby');
|
||||||
|
}
|
||||||
return zone.demand ? tr('zones.runningDemand') : tr('zones.satisfied');
|
return zone.demand ? tr('zones.runningDemand') : tr('zones.satisfied');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,6 +560,7 @@ function zoneCard(zone, detailed = true) {
|
|||||||
<div><small>${esc(tr('zones.source'))}</small><strong>${esc(zoneStrategyLabel(zone))}</strong></div>
|
<div><small>${esc(tr('zones.source'))}</small><strong>${esc(zoneStrategyLabel(zone))}</strong></div>
|
||||||
<div><small>${esc(tr('zones.hysteresis'))}</small><strong>${zone.separate_hysteresis ? `${esc(tr('mode.cool'))} ${Number(zone.cool_hysteresis ?? zone.hysteresis ?? 0.6).toFixed(1)}°C · ${esc(tr('mode.heat'))} ${Number(zone.heat_hysteresis ?? zone.hysteresis ?? 0.6).toFixed(1)}°C` : `${Number(zone.hysteresis ?? 0.6).toFixed(1)}°C`}</strong></div>
|
<div><small>${esc(tr('zones.hysteresis'))}</small><strong>${zone.separate_hysteresis ? `${esc(tr('mode.cool'))} ${Number(zone.cool_hysteresis ?? zone.hysteresis ?? 0.6).toFixed(1)}°C · ${esc(tr('mode.heat'))} ${Number(zone.heat_hysteresis ?? zone.hysteresis ?? 0.6).toFixed(1)}°C` : `${Number(zone.hysteresis ?? 0.6).toFixed(1)}°C`}</strong></div>
|
||||||
<div><small>${esc(tr('zones.smartFan'))}</small><strong>${esc(tr(zone.smart_fan === false ? 'common.off' : 'common.on'))}</strong></div>
|
<div><small>${esc(tr('zones.smartFan'))}</small><strong>${esc(tr(zone.smart_fan === false ? 'common.off' : 'common.on'))}</strong></div>
|
||||||
|
<div><small>${esc(tr('zones.heatAnticipation'))}</small><strong>${Number(zone.heat_anticipation_c ?? 0).toFixed(1)}°C</strong></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sensor-detail">${esc(sensorDetails)}</div>
|
<div class="sensor-detail">${esc(sensorDetails)}</div>
|
||||||
${compressorQueuePanel(zone)}
|
${compressorQueuePanel(zone)}
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ $('#copyZoneSettings')?.addEventListener('click', () => {
|
|||||||
ha_outdoor_entity_id: f.elements.ha_outdoor_entity_id.value,
|
ha_outdoor_entity_id: f.elements.ha_outdoor_entity_id.value,
|
||||||
enabled: f.elements.enabled.checked,
|
enabled: f.elements.enabled.checked,
|
||||||
};
|
};
|
||||||
const copyFields = ['setpoint', 'cool_comfort_setpoint', 'cool_sleep_setpoint', 'cool_away_setpoint', 'heat_comfort_setpoint', 'heat_sleep_setpoint', 'heat_away_setpoint', 'hysteresis', 'cool_hysteresis', 'heat_hysteresis', 'min_on_seconds', 'min_off_seconds', 'min_adjust_seconds', 'standby_offset_c', 'external_sensor_weight_percent', 'max_sensor_difference', 'sensor_stale_after_seconds'];
|
const copyFields = ['setpoint', 'cool_comfort_setpoint', 'cool_sleep_setpoint', 'cool_away_setpoint', 'heat_comfort_setpoint', 'heat_sleep_setpoint', 'heat_away_setpoint', 'hysteresis', 'cool_hysteresis', 'heat_hysteresis', 'min_on_seconds', 'min_off_seconds', 'min_adjust_seconds', 'standby_offset_c', 'heat_anticipation_c', 'external_sensor_weight_percent', 'max_sensor_difference', 'sensor_stale_after_seconds'];
|
||||||
copyFields.forEach(name => {
|
copyFields.forEach(name => {
|
||||||
const input = f.elements[name];
|
const input = f.elements[name];
|
||||||
if (!input) return;
|
if (!input) return;
|
||||||
@@ -583,7 +583,8 @@ $('#zoneForm').addEventListener('submit', async event => {
|
|||||||
cool_comfort_setpoint: parseDecimal(raw.cool_comfort_setpoint), cool_sleep_setpoint: parseDecimal(raw.cool_sleep_setpoint), cool_away_setpoint: parseDecimal(raw.cool_away_setpoint),
|
cool_comfort_setpoint: parseDecimal(raw.cool_comfort_setpoint), cool_sleep_setpoint: parseDecimal(raw.cool_sleep_setpoint), cool_away_setpoint: parseDecimal(raw.cool_away_setpoint),
|
||||||
heat_comfort_setpoint: parseDecimal(raw.heat_comfort_setpoint), heat_sleep_setpoint: parseDecimal(raw.heat_sleep_setpoint), heat_away_setpoint: parseDecimal(raw.heat_away_setpoint),
|
heat_comfort_setpoint: parseDecimal(raw.heat_comfort_setpoint), heat_sleep_setpoint: parseDecimal(raw.heat_sleep_setpoint), heat_away_setpoint: parseDecimal(raw.heat_away_setpoint),
|
||||||
hysteresis: parseDecimal(raw.hysteresis), separate_hysteresis: form.separate_hysteresis.checked, cool_hysteresis: parseDecimal(raw.cool_hysteresis), heat_hysteresis: parseDecimal(raw.heat_hysteresis), min_on_seconds: Number(raw.min_on_seconds), min_off_seconds: Number(raw.min_off_seconds),
|
hysteresis: parseDecimal(raw.hysteresis), separate_hysteresis: form.separate_hysteresis.checked, cool_hysteresis: parseDecimal(raw.cool_hysteresis), heat_hysteresis: parseDecimal(raw.heat_hysteresis), min_on_seconds: Number(raw.min_on_seconds), min_off_seconds: Number(raw.min_off_seconds),
|
||||||
min_adjust_seconds: Number(raw.min_adjust_seconds), standby_offset_c: parseDecimal(raw.standby_offset_c), smart_fan: form.smart_fan.checked,
|
min_adjust_seconds: Number(raw.min_adjust_seconds), standby_offset_c: parseDecimal(raw.standby_offset_c),
|
||||||
|
heat_anticipation_c: parseDecimal(raw.heat_anticipation_c), smart_fan: form.smart_fan.checked,
|
||||||
sensor_source: raw.sensor_source, ha_entity_id: raw.ha_entity_id || null, ha_outdoor_entity_id: raw.ha_outdoor_entity_id || null, external_sensor_weight: Number(raw.external_sensor_weight_percent) / 100, max_sensor_difference: parseDecimal(raw.max_sensor_difference), sensor_stale_after_seconds: Number(raw.sensor_stale_after_seconds || 300), revision: raw.revision ? Number(raw.revision) : null
|
sensor_source: raw.sensor_source, ha_entity_id: raw.ha_entity_id || null, ha_outdoor_entity_id: raw.ha_outdoor_entity_id || null, external_sensor_weight: Number(raw.external_sensor_weight_percent) / 100, max_sensor_difference: parseDecimal(raw.max_sensor_difference), sensor_stale_after_seconds: Number(raw.sensor_stale_after_seconds || 300), revision: raw.revision ? Number(raw.revision) : null
|
||||||
};
|
};
|
||||||
await runFormTask(form, async () => {
|
await runFormTask(form, async () => {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ function flowDefaultConfig(kind) {
|
|||||||
if (kind === 'night_mode') return {};
|
if (kind === 'night_mode') return {};
|
||||||
if (kind === 'constant') return { value: true };
|
if (kind === 'constant') return { value: true };
|
||||||
if (kind === 'shared_input') return sharedFlowReferenceDefaultConfig(app.flowSharedInputs?.[0]);
|
if (kind === 'shared_input') return sharedFlowReferenceDefaultConfig(app.flowSharedInputs?.[0]);
|
||||||
if (kind === 'zone_thermostat') return { zone_id: app.zones[0]?.id || '', preset: 'comfort', setpoint: 21, mode: 'auto', power: null, swing_vertical: null, swing_horizontal: null, cooldown_seconds: 60 };
|
if (kind === 'zone_thermostat') return { zone_id: app.zones[0]?.id || '', preset: 'comfort', setpoint: 21, mode: 'auto', power: null, heat_anticipation_c: null, standby_offset_c: null, swing_vertical: null, swing_horizontal: null, cooldown_seconds: 60 };
|
||||||
if (kind === 'device_action') return { device_id: app.devices[0]?.id || '', power: true, mode: '', target_temperature: null, fan_speed: null, swing_vertical: null, swing_horizontal: null, quiet: null, turbo: null, light: null, air: null, xfan: null, health: null, sleep: null, cooldown_seconds: 60 };
|
if (kind === 'device_action') return { device_id: app.devices[0]?.id || '', power: true, mode: '', target_temperature: null, fan_speed: null, swing_vertical: null, swing_horizontal: null, quiet: null, turbo: null, light: null, air: null, xfan: null, health: null, sleep: null, cooldown_seconds: 60 };
|
||||||
if (kind === 'device_feature_action') return { device_id: app.devices[0]?.id || '', feature: 'light', value: true, cooldown_seconds: 60 };
|
if (kind === 'device_feature_action') return { device_id: app.devices[0]?.id || '', feature: 'light', value: true, cooldown_seconds: 60 };
|
||||||
if (kind === 'group_action') return { group_id: app.groups[0]?.id || '', power: true, mode: '', preset: '', setpoint: 21, cooldown_seconds: 60 };
|
if (kind === 'group_action') return { group_id: app.groups[0]?.id || '', power: true, mode: '', preset: '', setpoint: 21, cooldown_seconds: 60 };
|
||||||
@@ -813,7 +813,7 @@ function renderFlowInspector() {
|
|||||||
else if (node.kind === 'logic_and') fields = `<p class="field-note">${esc(tr('flow.andHint'))}</p>`;
|
else if (node.kind === 'logic_and') fields = `<p class="field-note">${esc(tr('flow.andHint'))}</p>`;
|
||||||
else if (node.kind === 'logic_or') fields = `<p class="field-note">${esc(tr('flow.orHint'))}</p>`;
|
else if (node.kind === 'logic_or') fields = `<p class="field-note">${esc(tr('flow.orHint'))}</p>`;
|
||||||
else if (node.kind === 'logic_not') fields = `<p class="field-note">${esc(tr('flow.notHint'))}</p>`;
|
else if (node.kind === 'logic_not') fields = `<p class="field-note">${esc(tr('flow.notHint'))}</p>`;
|
||||||
else if (node.kind === 'zone_thermostat') fields = `<label><span>${esc(tr('common.zone'))}</span><select data-flow-config="zone_id">${flowSelectOptions(app.zones, c.zone_id)}</select></label><div class="two"><label><span>${esc(tr('schedules.profile'))}</span><select data-flow-config="preset">${['auto', 'comfort', 'sleep', 'away', 'custom'].map(v => `<option value="${v}" ${c.preset === v ? 'selected' : ''}>${esc(zonePresetLabel(v))}</option>`).join('')}</select></label><label><span>${esc(tr('common.targetC'))}</span><input type="number" min="8" max="30" step="0.1" data-flow-config="setpoint" value="${Number(c.setpoint ?? 21)}"></label></div><div class="two"><label><span>${esc(tr('common.mode'))}</span><select data-flow-config="mode"><option value="auto" ${c.mode === 'auto' ? 'selected' : ''}>Auto</option><option value="heat" ${c.mode === 'heat' ? 'selected' : ''}>${esc(tr('mode.heat'))}</option><option value="cool" ${c.mode === 'cool' ? 'selected' : ''}>${esc(tr('mode.cool'))}</option></select></label><label><span>${esc(tr('common.power'))}</span><select data-flow-config="power"><option value="" ${c.power == null ? 'selected' : ''}>${esc(tr('actions.noChange'))}</option><option value="true" ${c.power === true ? 'selected' : ''}>${esc(tr('common.on'))}</option><option value="false" ${c.power === false ? 'selected' : ''}>${esc(tr('common.off'))}</option></select></label></div><details class="flow-device-options"><summary>${esc(tr('flow.thermostatDeviceOptions'))}</summary><div class="two">${flowOptionalLouverField(c, 'swing_vertical', 'vertical')}${flowOptionalLouverField(c, 'swing_horizontal', 'horizontal')}</div><p class="field-note">${esc(tr('flow.thermostatDeviceOptionsHint'))}</p></details><label><span>${esc(tr('common.cooldownSeconds'))}</span><input type="number" min="30" data-flow-config="cooldown_seconds" value="${Number(c.cooldown_seconds || 60)}"></label>`;
|
else if (node.kind === 'zone_thermostat') fields = `<label><span>${esc(tr('common.zone'))}</span><select data-flow-config="zone_id">${flowSelectOptions(app.zones, c.zone_id)}</select></label><div class="two"><label><span>${esc(tr('schedules.profile'))}</span><select data-flow-config="preset">${['auto', 'comfort', 'sleep', 'away', 'custom'].map(v => `<option value="${v}" ${c.preset === v ? 'selected' : ''}>${esc(zonePresetLabel(v))}</option>`).join('')}</select></label><label><span>${esc(tr('common.targetC'))}</span><input type="number" min="8" max="30" step="0.1" data-flow-config="setpoint" value="${Number(c.setpoint ?? 21)}"></label></div><div class="two"><label><span>${esc(tr('common.mode'))}</span><select data-flow-config="mode"><option value="auto" ${c.mode === 'auto' ? 'selected' : ''}>Auto</option><option value="heat" ${c.mode === 'heat' ? 'selected' : ''}>${esc(tr('mode.heat'))}</option><option value="cool" ${c.mode === 'cool' ? 'selected' : ''}>${esc(tr('mode.cool'))}</option></select></label><label><span>${esc(tr('common.power'))}</span><select data-flow-config="power"><option value="" ${c.power == null ? 'selected' : ''}>${esc(tr('actions.noChange'))}</option><option value="true" ${c.power === true ? 'selected' : ''}>${esc(tr('common.on'))}</option><option value="false" ${c.power === false ? 'selected' : ''}>${esc(tr('common.off'))}</option></select></label></div><div class="two"><label><span>${esc(tr('zones.heatAnticipation'))}</span><input type="number" min="0" max="2.5" step="0.1" data-flow-config="heat_anticipation_c" value="${c.heat_anticipation_c == null ? '' : Number(c.heat_anticipation_c)}" placeholder="—"></label><label><span>${esc(tr('zones.standbyOffset'))}</span><input type="number" min="0.5" max="8" step="0.1" data-flow-config="standby_offset_c" value="${c.standby_offset_c == null ? '' : Number(c.standby_offset_c)}" placeholder="—"></label></div><details class="flow-device-options"><summary>${esc(tr('flow.thermostatDeviceOptions'))}</summary><div class="two">${flowOptionalLouverField(c, 'swing_vertical', 'vertical')}${flowOptionalLouverField(c, 'swing_horizontal', 'horizontal')}</div><p class="field-note">${esc(tr('flow.thermostatDeviceOptionsHint'))}</p></details><label><span>${esc(tr('common.cooldownSeconds'))}</span><input type="number" min="30" data-flow-config="cooldown_seconds" value="${Number(c.cooldown_seconds || 60)}"></label>`;
|
||||||
else if (node.kind === 'device_action') fields = `<label><span>${esc(tr('common.device'))}</span><select data-flow-config="device_id">${flowSelectOptions(app.devices, c.device_id)}</select></label>${flowActionFields(c, false)}`;
|
else if (node.kind === 'device_action') fields = `<label><span>${esc(tr('common.device'))}</span><select data-flow-config="device_id">${flowSelectOptions(app.devices, c.device_id)}</select></label>${flowActionFields(c, false)}`;
|
||||||
else if (node.kind === 'device_feature_action') fields = `<label><span>${esc(tr('common.device'))}</span><select data-flow-config="device_id">${flowSelectOptions(app.devices, c.device_id)}</select></label><label><span>${esc(tr('flow.deviceFeature'))}</span><select data-flow-config="feature">${flowDeviceFeatureOptions(c.feature)}</select></label>${flowDeviceFeatureValueField(c)}<p class="field-note">${esc(tr('flow.deviceFeatureHint'))}</p><label><span>${esc(tr('common.cooldownSeconds'))}</span><input type="number" min="30" data-flow-config="cooldown_seconds" value="${Number(c.cooldown_seconds || 60)}"></label>`;
|
else if (node.kind === 'device_feature_action') fields = `<label><span>${esc(tr('common.device'))}</span><select data-flow-config="device_id">${flowSelectOptions(app.devices, c.device_id)}</select></label><label><span>${esc(tr('flow.deviceFeature'))}</span><select data-flow-config="feature">${flowDeviceFeatureOptions(c.feature)}</select></label>${flowDeviceFeatureValueField(c)}<p class="field-note">${esc(tr('flow.deviceFeatureHint'))}</p><label><span>${esc(tr('common.cooldownSeconds'))}</span><input type="number" min="30" data-flow-config="cooldown_seconds" value="${Number(c.cooldown_seconds || 60)}"></label>`;
|
||||||
else if (node.kind === 'group_action') fields = `<label><span>${esc(tr('groups.group'))}</span><select data-flow-config="group_id">${flowSelectOptions(app.groups, c.group_id)}</select></label>${flowActionFields(c, true)}`;
|
else if (node.kind === 'group_action') fields = `<label><span>${esc(tr('groups.group'))}</span><select data-flow-config="group_id">${flowSelectOptions(app.groups, c.group_id)}</select></label>${flowActionFields(c, true)}`;
|
||||||
@@ -1736,6 +1736,8 @@ function updateFlowConfig(input) {
|
|||||||
else if (wasLouver) node.config.value = '';
|
else if (wasLouver) node.config.value = '';
|
||||||
}
|
}
|
||||||
else if (node.kind === 'device_state' && key === 'value' && ['swing_vertical', 'swing_horizontal'].includes(node.config.field)) node.config.value = Number(input.value);
|
else if (node.kind === 'device_state' && key === 'value' && ['swing_vertical', 'swing_horizontal'].includes(node.config.field)) node.config.value = Number(input.value);
|
||||||
|
else if (node.kind === 'zone_thermostat' && ['heat_anticipation_c', 'standby_offset_c'].includes(key))
|
||||||
|
node.config[key] = input.value === '' ? null : Number(input.value);
|
||||||
else if (['swing_vertical', 'swing_horizontal'].includes(key)) node.config[key] = input.value === '' ? null : Number(input.value);
|
else if (['swing_vertical', 'swing_horizontal'].includes(key)) node.config[key] = input.value === '' ? null : Number(input.value);
|
||||||
else if (['power', 'quiet', 'turbo', 'light', 'air', 'xfan', 'health', 'sleep'].includes(key)) node.config[key] = input.value === '' ? null : input.value === 'true';
|
else if (['power', 'quiet', 'turbo', 'light', 'air', 'xfan', 'health', 'sleep'].includes(key)) node.config[key] = input.value === '' ? null : input.value === 'true';
|
||||||
else if (key === 'value' && node.kind === 'constant') node.config[key] = input.value === 'true';
|
else if (key === 'value' && node.kind === 'constant') node.config[key] = input.value === 'true';
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ function validateForm(form) {
|
|||||||
const zoneDecimalFields = [
|
const zoneDecimalFields = [
|
||||||
['cool_comfort_setpoint', 8, 30], ['cool_sleep_setpoint', 8, 30], ['cool_away_setpoint', 8, 30],
|
['cool_comfort_setpoint', 8, 30], ['cool_sleep_setpoint', 8, 30], ['cool_away_setpoint', 8, 30],
|
||||||
['heat_comfort_setpoint', 8, 30], ['heat_sleep_setpoint', 8, 30], ['heat_away_setpoint', 8, 30],
|
['heat_comfort_setpoint', 8, 30], ['heat_sleep_setpoint', 8, 30], ['heat_away_setpoint', 8, 30],
|
||||||
['max_sensor_difference', 0.1, 20], ['standby_offset_c', 0.5, 8],
|
['max_sensor_difference', 0.1, 20], ['standby_offset_c', 0.5, 8], ['heat_anticipation_c', 0, 2.5],
|
||||||
...(form.elements.separate_hysteresis?.checked
|
...(form.elements.separate_hysteresis?.checked
|
||||||
? [['cool_hysteresis', 0.1, 5], ['heat_hysteresis', 0.1, 5]]
|
? [['cool_hysteresis', 0.1, 5], ['heat_hysteresis', 0.1, 5]]
|
||||||
: [['hysteresis', 0.1, 5]])
|
: [['hysteresis', 0.1, 5]])
|
||||||
|
|||||||
@@ -598,6 +598,7 @@ function populateZone(id) {
|
|||||||
form.max_sensor_difference.value = Number(item.max_sensor_difference ?? 3);
|
form.max_sensor_difference.value = Number(item.max_sensor_difference ?? 3);
|
||||||
form.min_adjust_seconds.value = Number(item.min_adjust_seconds ?? 120);
|
form.min_adjust_seconds.value = Number(item.min_adjust_seconds ?? 120);
|
||||||
form.standby_offset_c.value = Number(item.standby_offset_c ?? 2);
|
form.standby_offset_c.value = Number(item.standby_offset_c ?? 2);
|
||||||
|
form.heat_anticipation_c.value = Number(item.heat_anticipation_c ?? 0);
|
||||||
form.smart_fan.checked = item.smart_fan !== false;
|
form.smart_fan.checked = item.smart_fan !== false;
|
||||||
form.separate_hysteresis.checked = item.separate_hysteresis === true;
|
form.separate_hysteresis.checked = item.separate_hysteresis === true;
|
||||||
const commonHysteresis = Number(item.hysteresis ?? 0.6);
|
const commonHysteresis = Number(item.hysteresis ?? 0.6);
|
||||||
|
|||||||
Reference in new issue
Block a user