v0.15.6
This commit is contained in:
Generated
+1
-1
@@ -719,7 +719,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gree-controller"
|
name = "gree-controller"
|
||||||
version = "0.15.4"
|
version = "0.15.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gree-controller"
|
name = "gree-controller"
|
||||||
version = "0.15.4"
|
version = "0.15.6"
|
||||||
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"
|
||||||
|
|||||||
+27
-27
@@ -1,13 +1,14 @@
|
|||||||
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
||||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||||
37e255dd094e4720f96b74e501a179cf81c838ff327cbc1924b8fc4b5ea555fa ./Cargo.toml
|
03fd19b33f6b87a3cf55957c44c619dc65b6c1ef6ab35a275f7142e063fa130d ./Cargo.lock
|
||||||
|
0f1ebc69bb39b480c603772736bbc968cdeada29efecca6a056e4ebd0b071cb7 ./Cargo.toml
|
||||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||||
c1cac4ca532e73d64e5b090ad410a2161e9790b8d77fc6732adf52c248fb3472 ./README.md
|
252802f9b072e316a1898b5b39922788c0ed7ab769aba6e1cb33e5ac5595d7fb ./README.md
|
||||||
8dc1f42dd20c98873fcd0a979475560f9ec79e098f01375de47f8fd96ef6e80d ./SOURCE_MANIFEST.sha256
|
fb12007189dad1c860aeed1eacc561c54afb01204396982b42bfa68706dc583e ./SOURCE_MANIFEST.sha256
|
||||||
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
||||||
0da3db314314e19a00c08700f94495075fecd1e54a5b188f6a62cb237307e96f ./docs/API.md
|
c52fb51807e46086e6bb44dd532dde346a742d12d52ce703055f1d4f433fd253 ./docs/API.md
|
||||||
a881ba0808dd3f189d92038f6163c39d6b6208f6dbff5c32c0ea1a2628f3fbb4 ./docs/FLOW.md
|
a881ba0808dd3f189d92038f6163c39d6b6208f6dbff5c32c0ea1a2628f3fbb4 ./docs/FLOW.md
|
||||||
55ffdcc5b0a43d930823cea7c94235990dbbc4a35b60832b9adca1faf606d036 ./docs/openapi.json
|
524977fb867f2b56b56df827df0b8f646e366c00fa31d62c577a5a118f76406f ./docs/openapi.json
|
||||||
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
||||||
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
||||||
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
|
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
|
||||||
@@ -21,7 +22,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./ha-addon/hom
|
|||||||
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
|
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
|
||||||
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
|
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
|
||||||
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
|
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
|
||||||
d1ec103c9a167382e33877183b6df221a5cced5db58cf5d6eefd2eb24ab16077 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
45af41dd19b03ddc1d786730603ff2067bf3b3a794b7663acea8e45b5c1b7200 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
||||||
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
|
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
|
||||||
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
|
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
|
||||||
cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.py
|
cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.py
|
||||||
@@ -29,21 +30,21 @@ 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
|
||||||
1d1508e247b67487e6aac52b532adec2842494ec2e60144491c5144967a4f8f3 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
732265515c1d45eb11b966919cfa29ed9f600b6dd02a05e1abee8afda4151213 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||||
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
||||||
e3834bf712d4cd2acc715d8c85db96dd6e2fb3fbb758dac537f6034c337b3745 ./ha-addon/repository/gree-controller/README.md
|
e3834bf712d4cd2acc715d8c85db96dd6e2fb3fbb758dac537f6034c337b3745 ./ha-addon/repository/gree-controller/README.md
|
||||||
d060ee39cdea4de68bd9ededaebb7b81d974821e418b69e00c39cbdebd61f801 ./ha-addon/repository/gree-controller/config.yaml
|
d742c16e5f5f24be48f8055c2131ff335fb5072469d7d515328fd41e9c3aca34 ./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
|
||||||
e6b93d3701d83a31506617a0e6f44ff32d8c24a52be038c8540c862b13546b50 ./ha-addon/repository/gree-controller/translations/pl.yaml
|
e6b93d3701d83a31506617a0e6f44ff32d8c24a52be038c8540c862b13546b50 ./ha-addon/repository/gree-controller/translations/pl.yaml
|
||||||
2153b230357aab4766398577e23a7a9ff83bb135ddc86d1b3f2cbfc852efe0a1 ./ha-addon/repository/repository.yaml
|
b54c60ceeac06d32af7797da9e83b90cb037600318e00512da523d37b94d3081 ./ha-addon/repository/repository.yaml
|
||||||
cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run.sh
|
cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run.sh
|
||||||
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
|
||||||
e5c6a6051381e28d74b75e48232de13d02eb87519d29c847b7ab7979ce47d082 ./lang/en.json
|
2175f4ce8b5d994e1e4f912244b03721a93a41e2970f39826d6ea15fdaa7078e ./lang/en.json
|
||||||
02c650f603bca1bd17d9d59193c176ee6ddccb8304f2e658d7ba7730f8ff5f83 ./lang/pl.json
|
1055d5835329c03eb6f5b8fab4ee438c76b23d6eebdadf70effa42998c03a279 ./lang/pl.json
|
||||||
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
|
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
|
||||||
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
|
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
|
||||||
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
|
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
|
||||||
@@ -84,7 +85,7 @@ fdcd9a5055d08037278b842e7ab69265345c5811f0a06867136c511d140bb191 ./presets/wind
|
|||||||
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
||||||
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
||||||
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
||||||
b9bd0f1db6bf879ca3778c4969dd56d3c35b7bdd137df517dbd2cd2e11856080 ./scripts/api_dev_test.py
|
03d5ee12306c95d5ae5df4a2a3a422fe5044e0961bb2be4b7ee49641a9ee7fc1 ./scripts/api_dev_test.py
|
||||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
||||||
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
||||||
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
||||||
@@ -97,14 +98,14 @@ e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./scripts/netw
|
|||||||
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./scripts/service.sh
|
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./scripts/service.sh
|
||||||
bd703ef841f9763f13367aaa5764381b9c59ee87210e806c24617f3cc0739a9b ./scripts/smoke.sh
|
bd703ef841f9763f13367aaa5764381b9c59ee87210e806c24617f3cc0739a9b ./scripts/smoke.sh
|
||||||
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
|
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
|
||||||
f4ace9a1104ab1d0016db1d34c3ac2c4010fc1a5534af5fbf02d2042d5815d8c ./src/api.rs
|
b53e1afab342634fedc218ea4517446f19a5ba7985dee599e191b37d9cc8058a ./src/api.rs
|
||||||
0db0d81da4b8161004ddcbed2096812c285ecec9d944771670e6c8edf0775175 ./src/api/assets.rs
|
0db0d81da4b8161004ddcbed2096812c285ecec9d944771670e6c8edf0775175 ./src/api/assets.rs
|
||||||
1139dacbd3f6ff94bf721eb5a60d469016fb935ddcf484d71bfeb249b72c833a ./src/api/auth.rs
|
1139dacbd3f6ff94bf721eb5a60d469016fb935ddcf484d71bfeb249b72c833a ./src/api/auth.rs
|
||||||
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
||||||
27a788efe385ad7180cb7fc7ec66c34ebd5e6062d9cdf35b32fb461e52a42863 ./src/api/configuration.rs
|
27a788efe385ad7180cb7fc7ec66c34ebd5e6062d9cdf35b32fb461e52a42863 ./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
|
||||||
5245d5a70c4a1919d8b7757db399e1caf692c8b3a8af0343d8e462bf90373137 ./src/api/devices.rs
|
f775aa2f3c5c554e4197d47cf381f3d2133ea4c1430a5510f045188dd41e5e51 ./src/api/devices.rs
|
||||||
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
||||||
2ef2f64b77886e2abd6b410adf8de91a2333d3ebdb8fcee8376ba566e5bba029 ./src/api/flows.rs
|
2ef2f64b77886e2abd6b410adf8de91a2333d3ebdb8fcee8376ba566e5bba029 ./src/api/flows.rs
|
||||||
82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs
|
82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs
|
||||||
@@ -113,7 +114,7 @@ b6d586bc42b166ac05648f3da7d9aeb88efde2e9eaab5642685eb4d9cf15f95a ./src/api/hist
|
|||||||
e8edfbb725d6de650442444b0a929ad2be0bd2c2d7d227d2b3fdd99d05c94f94 ./src/api/house.rs
|
e8edfbb725d6de650442444b0a929ad2be0bd2c2d7d227d2b3fdd99d05c94f94 ./src/api/house.rs
|
||||||
8495967c3df72110f591c744975984c78cafc8181b2d430e7fd5b9baf57eba95 ./src/api/integrations.rs
|
8495967c3df72110f591c744975984c78cafc8181b2d430e7fd5b9baf57eba95 ./src/api/integrations.rs
|
||||||
7658111f7d125db55a6e23f88157339b110b2cd1ed13ceabd8f860b42dba5101 ./src/api/middleware.rs
|
7658111f7d125db55a6e23f88157339b110b2cd1ed13ceabd8f860b42dba5101 ./src/api/middleware.rs
|
||||||
c01b8b942e64a46dac19ce08346aa844df7e9ac7147c0ad6d0f2cfd90e071ec0 ./src/api/openapi.rs
|
9d996d326f2ec192c79fb108a8d2c06efaa1c9634d9e9e54fa65a65524f92a75 ./src/api/openapi.rs
|
||||||
b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/public_settings.rs
|
b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/public_settings.rs
|
||||||
5b29f2e5aa2c422c5829ef89c7b59cf753883aa7f60661c95ab0f95cf676fa3f ./src/api/schedules.rs
|
5b29f2e5aa2c422c5829ef89c7b59cf753883aa7f60661c95ab0f95cf676fa3f ./src/api/schedules.rs
|
||||||
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
||||||
@@ -169,25 +170,24 @@ fc5f2802a073a175a8985e56bcb87eb1ff481226d5b4eeabad73dbba1839fc45 ./src/models/c
|
|||||||
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
||||||
6e44df6bde2f73c282095c80ac17287e5bef77d9fc1d344e440d863dae5f5031 ./src/models/history.rs
|
6e44df6bde2f73c282095c80ac17287e5bef77d9fc1d344e440d863dae5f5031 ./src/models/history.rs
|
||||||
1fee89f939018bf1f3686c15f25434f01ab96b3239164108db7f348a1f1f8d82 ./src/models/integrations.rs
|
1fee89f939018bf1f3686c15f25434f01ab96b3239164108db7f348a1f1f8d82 ./src/models/integrations.rs
|
||||||
f1359b68f1d041cd441f80429e77893eeb6f6194613b58fd3e7a212fb511db3e ./src/models/runtime.rs
|
53caf9ba4667a2ae33d5c3ce69f85005d9f8540f2ee3c16c2cb3795a53e9d21f ./src/models/runtime.rs
|
||||||
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
|
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
|
||||||
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
|
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
|
||||||
bc2818ce8d0dae1edfb1b98e5d1c00b193f03eeaa28e343d3532f6023288738c ./src/models/zone.rs
|
bc2818ce8d0dae1edfb1b98e5d1c00b193f03eeaa28e343d3532f6023288738c ./src/models/zone.rs
|
||||||
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
|
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
|
||||||
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
|
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
|
||||||
daca14e9a4dd65394add7c7753c8ae5e7e43e991323a5ca23d8788aae4428925 ./src/protocol/gree.rs
|
578b9f39110abbeb98323044b6ae1a941733c82021afeb4d7ff78ad4256b5aa5 ./src/protocol/gree.rs
|
||||||
7907c33e6396c7e0d61705b735b21c84d6ed784012fb61022b872c6745540f1d ./src/protocol/gree/binding.rs
|
5f9f46d3ee6d410b8848ad46f2c482159eebfd692e739b0f09941a43c8d97211 ./src/protocol/gree/binding.rs
|
||||||
91d82c3ecae21615e4edad31b7ea95dca9b578650348666a2b61475b5a644669 ./src/protocol/gree/commands.rs
|
91d82c3ecae21615e4edad31b7ea95dca9b578650348666a2b61475b5a644669 ./src/protocol/gree/commands.rs
|
||||||
4e419ba3b86ca9d154e8c04445cf005965175c54041093b2daad3666d4d852bd ./src/protocol/gree/core.rs
|
4e419ba3b86ca9d154e8c04445cf005965175c54041093b2daad3666d4d852bd ./src/protocol/gree/core.rs
|
||||||
668e3bfcc4be84d1a86909bfc18e0d3816e0343cdc0d9b43189f7b0c6e0666d4 ./src/protocol/gree/discovery.rs
|
034344ab9b86a271854358cfc733d45386c90e8ea66e1f232afd927428422e3a ./src/protocol/gree/discovery.rs
|
||||||
0ab094f609edb99715402d8eeaa7e38cbfd2d271036aec787dcbb3be3e005720 ./src/protocol/gree/merge.rs
|
|
||||||
ff33db623f8f1bf30015b4a00e1f247a99de713da3d297c672e42c0795ec845c ./src/protocol/gree/network.rs
|
ff33db623f8f1bf30015b4a00e1f247a99de713da3d297c672e42c0795ec845c ./src/protocol/gree/network.rs
|
||||||
1076cb80950be00ca19f9e2370c73040902d1483b0e80c0ca349c7830d38c9f7 ./src/protocol/gree/polling.rs
|
1076cb80950be00ca19f9e2370c73040902d1483b0e80c0ca349c7830d38c9f7 ./src/protocol/gree/polling.rs
|
||||||
e0555fac67d1dd0129ecfea6d1fd2fbeb1a73ac89ad95d1b74a7094e50e29ae4 ./src/protocol/gree/tests.rs
|
c438719392dfda8ddf1bccf4ad451038aa4f6aa8dc5477533b0aca96f3661bed ./src/protocol/gree/tests.rs
|
||||||
3c93e5b254a2a40a07854532cb5fc07e493ad65d9ba2cf28093cff4a4a4e451c ./src/protocol/gree/transport.rs
|
3c93e5b254a2a40a07854532cb5fc07e493ad65d9ba2cf28093cff4a4a4e451c ./src/protocol/gree/transport.rs
|
||||||
b4e285f499dd787b75259777a14e3a51a1a071c7d176bcf30c1de0933920e0dc ./src/protocol/gree_cloud.rs
|
b4e285f499dd787b75259777a14e3a51a1a071c7d176bcf30c1de0933920e0dc ./src/protocol/gree_cloud.rs
|
||||||
b4b1bad9cad889dd69681b2afc3e6a09ef19e19b2a1f2900fd417c4b1f55ec2c ./src/protocol/gree_cloud_mqtt.rs
|
b4b1bad9cad889dd69681b2afc3e6a09ef19e19b2a1f2900fd417c4b1f55ec2c ./src/protocol/gree_cloud_mqtt.rs
|
||||||
6258ee3888a5f9a4f8bb6cfbbac97f2621de07f9da6bcb2fa596d452f47f2e49 ./src/protocol/mod.rs
|
5f70211b73215d9ae7af8fc306d15dc0b6cfbf0df9f79e34f1c785957fdf8bd4 ./src/protocol/mod.rs
|
||||||
a677ce61d3d0f4cf358055ddbc343e73b452c68daed80798499147370819615e ./src/provider.rs
|
a677ce61d3d0f4cf358055ddbc343e73b452c68daed80798499147370819615e ./src/provider.rs
|
||||||
830621fa75265da7288b8135704c0ce27e0687f87d55abf3cdbe40a6e2f3d2b1 ./src/queries.rs
|
830621fa75265da7288b8135704c0ce27e0687f87d55abf3cdbe40a6e2f3d2b1 ./src/queries.rs
|
||||||
7fac65a9dcbcecee09816f5447c7792dc94f736b1efb66ac85629db75f6280be ./src/queries/device_history.rs
|
7fac65a9dcbcecee09816f5447c7792dc94f736b1efb66ac85629db75f6280be ./src/queries/device_history.rs
|
||||||
@@ -199,22 +199,22 @@ f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/
|
|||||||
8c34b70fb5f80e3249c2e0c63686e60ce1697cf44a7e6f11b3ad6f689ce17792 ./src/state.rs
|
8c34b70fb5f80e3249c2e0c63686e60ce1697cf44a7e6f11b3ad6f689ce17792 ./src/state.rs
|
||||||
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
||||||
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
||||||
2822a98a4d7f72f2b89b70878aa0f9bd3f391fde4ea04ba1b94e89a527dcd1fb ./web/css/styles.css
|
0b783d6cea599e53fed4ed82fa95e2048c493057c6d57b0dfa423e7cfa10a7ad ./web/css/styles.css
|
||||||
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
||||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||||
d5115a9dc59b4e753dcff9239a77fe49886951d7a4ae7456be80d9201cc42365 ./web/index.html
|
baff48bb71ba348fe39d52fe8d2bef0614bb0c53c1ff5ee748ce09458b3983b2 ./web/index.html
|
||||||
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
||||||
e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js
|
e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js
|
||||||
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
||||||
b5fd43eeb9c15fe8289b9e718dfbc2651417379462c4b16b88e090b436abf7c5 ./web/js-dynamic/core.js
|
81012bd88282034cdc4e876af5eb5057289f4db8505847568bc3c3701a348d4b ./web/js-dynamic/core.js
|
||||||
752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js
|
752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js
|
||||||
6b166a9311e30fe948bd3236189436eea769c238f963ba00acec5cdb51ff1dd7 ./web/js-dynamic/entities.js
|
6b166a9311e30fe948bd3236189436eea769c238f963ba00acec5cdb51ff1dd7 ./web/js-dynamic/entities.js
|
||||||
a48cb69ae4a2d0d6d512baef346fe499a5a6ea267e071856829df982d4a0f129 ./web/js-dynamic/events.js
|
b4204241eecf387256364f4230b23ed50d947a362f4ceb1e528d69494e93d7cf ./web/js-dynamic/events.js
|
||||||
8e0f294ceeec1d258edb56271469a8a05381e6443a475f76fbeded6637d17503 ./web/js-dynamic/flows.js
|
8e0f294ceeec1d258edb56271469a8a05381e6443a475f76fbeded6637d17503 ./web/js-dynamic/flows.js
|
||||||
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
||||||
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
||||||
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
||||||
c974b6ef41ce8b6def35f5349849a06855c46ec31cb4122a39fcb5c78f6d362a ./web/js-dynamic/navigation.js
|
57d85a638359d720798068ef34c88237ab92ee08dcd7478055b806ff0c858aac ./web/js-dynamic/navigation.js
|
||||||
0e398bad2cde85d4a62d8733c06e73f2b6a5b07db791d4570feba38c7ff8741f ./web/js-dynamic/realtime.js
|
0e398bad2cde85d4a62d8733c06e73f2b6a5b07db791d4570feba38c7ff8741f ./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.15.4**
|
**Version: 0.15.6**
|
||||||
|
|
||||||
Version 0.15.4 refreshes the Rust dependency stack, including Axum 0.8, Reqwest 0.13, Rusqlite 0.40, Rand 0.10, RustCrypto AES/AES-GCM and utoipa-swagger-ui 9, with the required compatibility migrations.
|
Version 0.15.6 simplifies Local discovery to one API flow: scan candidates, select units, then add them. The obsolete add-all discovery endpoint and its compatibility code are removed.
|
||||||
|
|
||||||
> [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`.
|
||||||
@@ -185,8 +185,8 @@ Cloud REST and MQTT use TLS with certificate/hostname validation enabled. Passwo
|
|||||||
|
|
||||||
1. Place the controller host on a network that can reach the air-conditioner Wi-Fi modules by UDP.
|
1. Place the controller host on a network that can reach the air-conditioner Wi-Fi modules by UDP.
|
||||||
2. Open **Devices** and start discovery.
|
2. Open **Devices** and start discovery.
|
||||||
3. Auto protocol mode is recommended; the controller accepts both supported GREE encryption generations.
|
3. Auto protocol mode accepts both supported GREE encryption generations. Selecting V1 or V2 strictly filters discovery and bind to that generation.
|
||||||
4. Newly discovered devices are bound automatically when possible. Manual bind is also available.
|
4. Review the discovery results (model, MAC and IP), select the units you want, then choose **Add selected**. Only selected units are persisted and bound.
|
||||||
5. Create a thermostat zone for each unit you want the thermostat engine to own.
|
5. Create a thermostat zone for each unit you want the thermostat engine to own.
|
||||||
|
|
||||||
Discovery uses UDP broadcast, so routed/VLAN networks must explicitly permit or relay the required traffic.
|
Discovery uses UDP broadcast, so routed/VLAN networks must explicitly permit or relay the required traffic.
|
||||||
|
|||||||
+26
-26
@@ -1,12 +1,13 @@
|
|||||||
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
||||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||||
37e255dd094e4720f96b74e501a179cf81c838ff327cbc1924b8fc4b5ea555fa ./Cargo.toml
|
03fd19b33f6b87a3cf55957c44c619dc65b6c1ef6ab35a275f7142e063fa130d ./Cargo.lock
|
||||||
|
0f1ebc69bb39b480c603772736bbc968cdeada29efecca6a056e4ebd0b071cb7 ./Cargo.toml
|
||||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||||
c1cac4ca532e73d64e5b090ad410a2161e9790b8d77fc6732adf52c248fb3472 ./README.md
|
252802f9b072e316a1898b5b39922788c0ed7ab769aba6e1cb33e5ac5595d7fb ./README.md
|
||||||
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
||||||
0da3db314314e19a00c08700f94495075fecd1e54a5b188f6a62cb237307e96f ./docs/API.md
|
c52fb51807e46086e6bb44dd532dde346a742d12d52ce703055f1d4f433fd253 ./docs/API.md
|
||||||
a881ba0808dd3f189d92038f6163c39d6b6208f6dbff5c32c0ea1a2628f3fbb4 ./docs/FLOW.md
|
a881ba0808dd3f189d92038f6163c39d6b6208f6dbff5c32c0ea1a2628f3fbb4 ./docs/FLOW.md
|
||||||
55ffdcc5b0a43d930823cea7c94235990dbbc4a35b60832b9adca1faf606d036 ./docs/openapi.json
|
524977fb867f2b56b56df827df0b8f646e366c00fa31d62c577a5a118f76406f ./docs/openapi.json
|
||||||
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
||||||
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
||||||
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
|
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./ha-addon/README.md
|
||||||
@@ -20,7 +21,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./ha-addon/hom
|
|||||||
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
|
6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
|
||||||
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
|
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py
|
||||||
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
|
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png
|
||||||
d1ec103c9a167382e33877183b6df221a5cced5db58cf5d6eefd2eb24ab16077 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
45af41dd19b03ddc1d786730603ff2067bf3b3a794b7663acea8e45b5c1b7200 ./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
|
||||||
@@ -28,21 +29,21 @@ 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
|
||||||
1d1508e247b67487e6aac52b532adec2842494ec2e60144491c5144967a4f8f3 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
732265515c1d45eb11b966919cfa29ed9f600b6dd02a05e1abee8afda4151213 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||||
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
||||||
e3834bf712d4cd2acc715d8c85db96dd6e2fb3fbb758dac537f6034c337b3745 ./ha-addon/repository/gree-controller/README.md
|
e3834bf712d4cd2acc715d8c85db96dd6e2fb3fbb758dac537f6034c337b3745 ./ha-addon/repository/gree-controller/README.md
|
||||||
d060ee39cdea4de68bd9ededaebb7b81d974821e418b69e00c39cbdebd61f801 ./ha-addon/repository/gree-controller/config.yaml
|
d742c16e5f5f24be48f8055c2131ff335fb5072469d7d515328fd41e9c3aca34 ./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
|
||||||
e6b93d3701d83a31506617a0e6f44ff32d8c24a52be038c8540c862b13546b50 ./ha-addon/repository/gree-controller/translations/pl.yaml
|
e6b93d3701d83a31506617a0e6f44ff32d8c24a52be038c8540c862b13546b50 ./ha-addon/repository/gree-controller/translations/pl.yaml
|
||||||
2153b230357aab4766398577e23a7a9ff83bb135ddc86d1b3f2cbfc852efe0a1 ./ha-addon/repository/repository.yaml
|
b54c60ceeac06d32af7797da9e83b90cb037600318e00512da523d37b94d3081 ./ha-addon/repository/repository.yaml
|
||||||
cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run.sh
|
cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run.sh
|
||||||
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
|
||||||
e5c6a6051381e28d74b75e48232de13d02eb87519d29c847b7ab7979ce47d082 ./lang/en.json
|
2175f4ce8b5d994e1e4f912244b03721a93a41e2970f39826d6ea15fdaa7078e ./lang/en.json
|
||||||
02c650f603bca1bd17d9d59193c176ee6ddccb8304f2e658d7ba7730f8ff5f83 ./lang/pl.json
|
1055d5835329c03eb6f5b8fab4ee438c76b23d6eebdadf70effa42998c03a279 ./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
|
||||||
@@ -83,7 +84,7 @@ fdcd9a5055d08037278b842e7ab69265345c5811f0a06867136c511d140bb191 ./presets/wind
|
|||||||
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
||||||
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
||||||
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
||||||
b9bd0f1db6bf879ca3778c4969dd56d3c35b7bdd137df517dbd2cd2e11856080 ./scripts/api_dev_test.py
|
03d5ee12306c95d5ae5df4a2a3a422fe5044e0961bb2be4b7ee49641a9ee7fc1 ./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
|
||||||
@@ -96,14 +97,14 @@ e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./scripts/netw
|
|||||||
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./scripts/service.sh
|
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./scripts/service.sh
|
||||||
bd703ef841f9763f13367aaa5764381b9c59ee87210e806c24617f3cc0739a9b ./scripts/smoke.sh
|
bd703ef841f9763f13367aaa5764381b9c59ee87210e806c24617f3cc0739a9b ./scripts/smoke.sh
|
||||||
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
|
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
|
||||||
f4ace9a1104ab1d0016db1d34c3ac2c4010fc1a5534af5fbf02d2042d5815d8c ./src/api.rs
|
b53e1afab342634fedc218ea4517446f19a5ba7985dee599e191b37d9cc8058a ./src/api.rs
|
||||||
0db0d81da4b8161004ddcbed2096812c285ecec9d944771670e6c8edf0775175 ./src/api/assets.rs
|
0db0d81da4b8161004ddcbed2096812c285ecec9d944771670e6c8edf0775175 ./src/api/assets.rs
|
||||||
1139dacbd3f6ff94bf721eb5a60d469016fb935ddcf484d71bfeb249b72c833a ./src/api/auth.rs
|
1139dacbd3f6ff94bf721eb5a60d469016fb935ddcf484d71bfeb249b72c833a ./src/api/auth.rs
|
||||||
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
||||||
27a788efe385ad7180cb7fc7ec66c34ebd5e6062d9cdf35b32fb461e52a42863 ./src/api/configuration.rs
|
27a788efe385ad7180cb7fc7ec66c34ebd5e6062d9cdf35b32fb461e52a42863 ./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
|
||||||
5245d5a70c4a1919d8b7757db399e1caf692c8b3a8af0343d8e462bf90373137 ./src/api/devices.rs
|
f775aa2f3c5c554e4197d47cf381f3d2133ea4c1430a5510f045188dd41e5e51 ./src/api/devices.rs
|
||||||
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
||||||
2ef2f64b77886e2abd6b410adf8de91a2333d3ebdb8fcee8376ba566e5bba029 ./src/api/flows.rs
|
2ef2f64b77886e2abd6b410adf8de91a2333d3ebdb8fcee8376ba566e5bba029 ./src/api/flows.rs
|
||||||
82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs
|
82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs
|
||||||
@@ -112,7 +113,7 @@ b6d586bc42b166ac05648f3da7d9aeb88efde2e9eaab5642685eb4d9cf15f95a ./src/api/hist
|
|||||||
e8edfbb725d6de650442444b0a929ad2be0bd2c2d7d227d2b3fdd99d05c94f94 ./src/api/house.rs
|
e8edfbb725d6de650442444b0a929ad2be0bd2c2d7d227d2b3fdd99d05c94f94 ./src/api/house.rs
|
||||||
8495967c3df72110f591c744975984c78cafc8181b2d430e7fd5b9baf57eba95 ./src/api/integrations.rs
|
8495967c3df72110f591c744975984c78cafc8181b2d430e7fd5b9baf57eba95 ./src/api/integrations.rs
|
||||||
7658111f7d125db55a6e23f88157339b110b2cd1ed13ceabd8f860b42dba5101 ./src/api/middleware.rs
|
7658111f7d125db55a6e23f88157339b110b2cd1ed13ceabd8f860b42dba5101 ./src/api/middleware.rs
|
||||||
c01b8b942e64a46dac19ce08346aa844df7e9ac7147c0ad6d0f2cfd90e071ec0 ./src/api/openapi.rs
|
9d996d326f2ec192c79fb108a8d2c06efaa1c9634d9e9e54fa65a65524f92a75 ./src/api/openapi.rs
|
||||||
b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/public_settings.rs
|
b777e7fbeccef699e11ffd16bb18633843c99afecacce2221280e54b09f6d2da ./src/api/public_settings.rs
|
||||||
5b29f2e5aa2c422c5829ef89c7b59cf753883aa7f60661c95ab0f95cf676fa3f ./src/api/schedules.rs
|
5b29f2e5aa2c422c5829ef89c7b59cf753883aa7f60661c95ab0f95cf676fa3f ./src/api/schedules.rs
|
||||||
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
fd08162d0f9287700196028ad0ca2aec7c242238056b0a228ca5bdcb319ece4e ./src/api/settings.rs
|
||||||
@@ -168,25 +169,24 @@ fc5f2802a073a175a8985e56bcb87eb1ff481226d5b4eeabad73dbba1839fc45 ./src/models/c
|
|||||||
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
||||||
6e44df6bde2f73c282095c80ac17287e5bef77d9fc1d344e440d863dae5f5031 ./src/models/history.rs
|
6e44df6bde2f73c282095c80ac17287e5bef77d9fc1d344e440d863dae5f5031 ./src/models/history.rs
|
||||||
1fee89f939018bf1f3686c15f25434f01ab96b3239164108db7f348a1f1f8d82 ./src/models/integrations.rs
|
1fee89f939018bf1f3686c15f25434f01ab96b3239164108db7f348a1f1f8d82 ./src/models/integrations.rs
|
||||||
f1359b68f1d041cd441f80429e77893eeb6f6194613b58fd3e7a212fb511db3e ./src/models/runtime.rs
|
53caf9ba4667a2ae33d5c3ce69f85005d9f8540f2ee3c16c2cb3795a53e9d21f ./src/models/runtime.rs
|
||||||
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
|
41b85cff6fd234c8d6ea007b2c3c22df5fee5107dcd57c64dcc94a356ff70c12 ./src/models/settings_api.rs
|
||||||
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
|
c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/temporary_thermostat.rs
|
||||||
bc2818ce8d0dae1edfb1b98e5d1c00b193f03eeaa28e343d3532f6023288738c ./src/models/zone.rs
|
bc2818ce8d0dae1edfb1b98e5d1c00b193f03eeaa28e343d3532f6023288738c ./src/models/zone.rs
|
||||||
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
|
4c1f676eaf0b6da45b4d4b090ae3ac862fb97c1f9e53284a1640767ae9591d8f ./src/notifications.rs
|
||||||
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
|
7746bca683809b82529ed152272ae5c0cd3971758a02e076def58f770e7adf10 ./src/protocol/crypto.rs
|
||||||
daca14e9a4dd65394add7c7753c8ae5e7e43e991323a5ca23d8788aae4428925 ./src/protocol/gree.rs
|
578b9f39110abbeb98323044b6ae1a941733c82021afeb4d7ff78ad4256b5aa5 ./src/protocol/gree.rs
|
||||||
7907c33e6396c7e0d61705b735b21c84d6ed784012fb61022b872c6745540f1d ./src/protocol/gree/binding.rs
|
5f9f46d3ee6d410b8848ad46f2c482159eebfd692e739b0f09941a43c8d97211 ./src/protocol/gree/binding.rs
|
||||||
91d82c3ecae21615e4edad31b7ea95dca9b578650348666a2b61475b5a644669 ./src/protocol/gree/commands.rs
|
91d82c3ecae21615e4edad31b7ea95dca9b578650348666a2b61475b5a644669 ./src/protocol/gree/commands.rs
|
||||||
4e419ba3b86ca9d154e8c04445cf005965175c54041093b2daad3666d4d852bd ./src/protocol/gree/core.rs
|
4e419ba3b86ca9d154e8c04445cf005965175c54041093b2daad3666d4d852bd ./src/protocol/gree/core.rs
|
||||||
668e3bfcc4be84d1a86909bfc18e0d3816e0343cdc0d9b43189f7b0c6e0666d4 ./src/protocol/gree/discovery.rs
|
034344ab9b86a271854358cfc733d45386c90e8ea66e1f232afd927428422e3a ./src/protocol/gree/discovery.rs
|
||||||
0ab094f609edb99715402d8eeaa7e38cbfd2d271036aec787dcbb3be3e005720 ./src/protocol/gree/merge.rs
|
|
||||||
ff33db623f8f1bf30015b4a00e1f247a99de713da3d297c672e42c0795ec845c ./src/protocol/gree/network.rs
|
ff33db623f8f1bf30015b4a00e1f247a99de713da3d297c672e42c0795ec845c ./src/protocol/gree/network.rs
|
||||||
1076cb80950be00ca19f9e2370c73040902d1483b0e80c0ca349c7830d38c9f7 ./src/protocol/gree/polling.rs
|
1076cb80950be00ca19f9e2370c73040902d1483b0e80c0ca349c7830d38c9f7 ./src/protocol/gree/polling.rs
|
||||||
e0555fac67d1dd0129ecfea6d1fd2fbeb1a73ac89ad95d1b74a7094e50e29ae4 ./src/protocol/gree/tests.rs
|
c438719392dfda8ddf1bccf4ad451038aa4f6aa8dc5477533b0aca96f3661bed ./src/protocol/gree/tests.rs
|
||||||
3c93e5b254a2a40a07854532cb5fc07e493ad65d9ba2cf28093cff4a4a4e451c ./src/protocol/gree/transport.rs
|
3c93e5b254a2a40a07854532cb5fc07e493ad65d9ba2cf28093cff4a4a4e451c ./src/protocol/gree/transport.rs
|
||||||
b4e285f499dd787b75259777a14e3a51a1a071c7d176bcf30c1de0933920e0dc ./src/protocol/gree_cloud.rs
|
b4e285f499dd787b75259777a14e3a51a1a071c7d176bcf30c1de0933920e0dc ./src/protocol/gree_cloud.rs
|
||||||
b4b1bad9cad889dd69681b2afc3e6a09ef19e19b2a1f2900fd417c4b1f55ec2c ./src/protocol/gree_cloud_mqtt.rs
|
b4b1bad9cad889dd69681b2afc3e6a09ef19e19b2a1f2900fd417c4b1f55ec2c ./src/protocol/gree_cloud_mqtt.rs
|
||||||
6258ee3888a5f9a4f8bb6cfbbac97f2621de07f9da6bcb2fa596d452f47f2e49 ./src/protocol/mod.rs
|
5f70211b73215d9ae7af8fc306d15dc0b6cfbf0df9f79e34f1c785957fdf8bd4 ./src/protocol/mod.rs
|
||||||
a677ce61d3d0f4cf358055ddbc343e73b452c68daed80798499147370819615e ./src/provider.rs
|
a677ce61d3d0f4cf358055ddbc343e73b452c68daed80798499147370819615e ./src/provider.rs
|
||||||
830621fa75265da7288b8135704c0ce27e0687f87d55abf3cdbe40a6e2f3d2b1 ./src/queries.rs
|
830621fa75265da7288b8135704c0ce27e0687f87d55abf3cdbe40a6e2f3d2b1 ./src/queries.rs
|
||||||
7fac65a9dcbcecee09816f5447c7792dc94f736b1efb66ac85629db75f6280be ./src/queries/device_history.rs
|
7fac65a9dcbcecee09816f5447c7792dc94f736b1efb66ac85629db75f6280be ./src/queries/device_history.rs
|
||||||
@@ -198,22 +198,22 @@ f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/
|
|||||||
8c34b70fb5f80e3249c2e0c63686e60ce1697cf44a7e6f11b3ad6f689ce17792 ./src/state.rs
|
8c34b70fb5f80e3249c2e0c63686e60ce1697cf44a7e6f11b3ad6f689ce17792 ./src/state.rs
|
||||||
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
||||||
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
||||||
2822a98a4d7f72f2b89b70878aa0f9bd3f391fde4ea04ba1b94e89a527dcd1fb ./web/css/styles.css
|
0b783d6cea599e53fed4ed82fa95e2048c493057c6d57b0dfa423e7cfa10a7ad ./web/css/styles.css
|
||||||
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
||||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||||
d5115a9dc59b4e753dcff9239a77fe49886951d7a4ae7456be80d9201cc42365 ./web/index.html
|
baff48bb71ba348fe39d52fe8d2bef0614bb0c53c1ff5ee748ce09458b3983b2 ./web/index.html
|
||||||
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
||||||
e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js
|
e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js
|
||||||
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
||||||
b5fd43eeb9c15fe8289b9e718dfbc2651417379462c4b16b88e090b436abf7c5 ./web/js-dynamic/core.js
|
81012bd88282034cdc4e876af5eb5057289f4db8505847568bc3c3701a348d4b ./web/js-dynamic/core.js
|
||||||
752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js
|
752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js
|
||||||
6b166a9311e30fe948bd3236189436eea769c238f963ba00acec5cdb51ff1dd7 ./web/js-dynamic/entities.js
|
6b166a9311e30fe948bd3236189436eea769c238f963ba00acec5cdb51ff1dd7 ./web/js-dynamic/entities.js
|
||||||
a48cb69ae4a2d0d6d512baef346fe499a5a6ea267e071856829df982d4a0f129 ./web/js-dynamic/events.js
|
b4204241eecf387256364f4230b23ed50d947a362f4ceb1e528d69494e93d7cf ./web/js-dynamic/events.js
|
||||||
8e0f294ceeec1d258edb56271469a8a05381e6443a475f76fbeded6637d17503 ./web/js-dynamic/flows.js
|
8e0f294ceeec1d258edb56271469a8a05381e6443a475f76fbeded6637d17503 ./web/js-dynamic/flows.js
|
||||||
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
||||||
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
||||||
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
||||||
c974b6ef41ce8b6def35f5349849a06855c46ec31cb4122a39fcb5c78f6d362a ./web/js-dynamic/navigation.js
|
57d85a638359d720798068ef34c88237ab92ee08dcd7478055b806ff0c858aac ./web/js-dynamic/navigation.js
|
||||||
0e398bad2cde85d4a62d8733c06e73f2b6a5b07db791d4570feba38c7ff8741f ./web/js-dynamic/realtime.js
|
0e398bad2cde85d4a62d8733c06e73f2b6a5b07db791d4570feba38c7ff8741f ./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
|
||||||
|
|||||||
+46
-17
@@ -1,6 +1,6 @@
|
|||||||
# GREE Controller API reference
|
# GREE Controller API reference
|
||||||
|
|
||||||
HTTP and WebSocket API for GREE Controller **0.15.4**.
|
HTTP and WebSocket API for GREE Controller **0.15.6**.
|
||||||
|
|
||||||
[← Main documentation](../README.md)
|
[← Main documentation](../README.md)
|
||||||
|
|
||||||
@@ -132,7 +132,8 @@ Common statuses:
|
|||||||
|
|
||||||
| Method | Endpoint | Description |
|
| Method | Endpoint | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| POST | `/api/discovery` | Discover/bind GREE devices. |
|
| POST | `/api/discovery/scan` | Scan for local GREE devices without adding them. |
|
||||||
|
| POST | `/api/discovery/add` | Add and bind selected scan results. |
|
||||||
| GET | `/api/devices` | List devices. |
|
| GET | `/api/devices` | List devices. |
|
||||||
| POST | `/api/devices` | Add a device manually. |
|
| POST | `/api/devices` | Add a device manually. |
|
||||||
| GET | `/api/devices/{id}` | Read a device. |
|
| GET | `/api/devices/{id}` | Read a device. |
|
||||||
@@ -240,7 +241,7 @@ Response:
|
|||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"name": "gree-controller",
|
"name": "gree-controller",
|
||||||
"version": "0.15.4",
|
"version": "0.15.6",
|
||||||
"uptime_seconds": 1234,
|
"uptime_seconds": 1234,
|
||||||
"control_ready": true,
|
"control_ready": true,
|
||||||
"time": "2026-08-30T06:54:00Z"
|
"time": "2026-08-30T06:54:00Z"
|
||||||
@@ -303,7 +304,7 @@ Returns the initial Web UI snapshot:
|
|||||||
"control_plan": {"generated_at": "2026-09-04T08:00:00Z", "zones": [], "rules": []},
|
"control_plan": {"generated_at": "2026-09-04T08:00:00Z", "zones": [], "rules": []},
|
||||||
"control_plan_revision": 42,
|
"control_plan_revision": 42,
|
||||||
"system": {
|
"system": {
|
||||||
"version": "0.15.4",
|
"version": "0.15.6",
|
||||||
"uptime_seconds": 1234,
|
"uptime_seconds": 1234,
|
||||||
"auth_required": false,
|
"auth_required": false,
|
||||||
"control_ready": true,
|
"control_ready": true,
|
||||||
@@ -374,9 +375,9 @@ A device response contains:
|
|||||||
| `communication_failures` | integer | Consecutive/recorded communication failure counter. |
|
| `communication_failures` | integer | Consecutive/recorded communication failure counter. |
|
||||||
| `created_at`, `updated_at` | ISO-8601 | Resource timestamps. |
|
| `created_at`, `updated_at` | ISO-8601 | Resource timestamps. |
|
||||||
|
|
||||||
### `POST /api/discovery`
|
### `POST /api/discovery/scan`
|
||||||
|
|
||||||
Request body, all fields optional:
|
Scans without adding or binding anything. Request body fields are optional:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -387,23 +388,51 @@ Request body, all fields optional:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Rules:
|
`protocol_version` accepts `0` (auto/both), `1` (AES-ECB only) or `2` (AES-GCM only). Explicit V1/V2 selection rejects replies from the other protocol generation before decryption. The UDP scan packet itself is common to both generations.
|
||||||
|
|
||||||
- `timeout_ms`: effective range `500..30000` ms.
|
The response contains candidates with MAC, IP, detected protocol and an `already_added` flag:
|
||||||
- `protocol_version`: `0` auto/both, `1` AES-ECB only, `2` AES-GCM only.
|
|
||||||
- `passes`: `1..10`.
|
|
||||||
- Missing values use runtime GREE settings.
|
|
||||||
|
|
||||||
Successful discovery merges known devices, tries binding devices that do not have a key, persists results and returns:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"devices": [],
|
"devices": [
|
||||||
"new_device_ids": ["gree-aabbccddeeff"]
|
{
|
||||||
|
"name": "GREE EEFF",
|
||||||
|
"mac": "AABBCCDDEEFF",
|
||||||
|
"ip": "192.168.50.30",
|
||||||
|
"port": 7000,
|
||||||
|
"protocol_version": 1,
|
||||||
|
"model": "GREE",
|
||||||
|
"firmware": "",
|
||||||
|
"already_added": false
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `POST /api/discovery/add`
|
||||||
|
|
||||||
|
Add only selected candidates returned by `/api/discovery/scan`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"name": "Living room",
|
||||||
|
"mac": "AABBCCDDEEFF",
|
||||||
|
"ip": "192.168.50.30",
|
||||||
|
"port": 7000,
|
||||||
|
"protocol_version": 1,
|
||||||
|
"model": "GREE",
|
||||||
|
"firmware": "",
|
||||||
|
"already_added": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each selected candidate is bound strictly with its detected `protocol_version`. Existing MAC addresses are skipped.
|
||||||
|
|
||||||
### `GET /api/devices`
|
### `GET /api/devices`
|
||||||
|
|
||||||
Returns `Device[]`.
|
Returns `Device[]`.
|
||||||
@@ -1599,10 +1628,10 @@ BASE='http://127.0.0.1:8787'
|
|||||||
AUTH='Authorization: Bearer APP_TOKEN'
|
AUTH='Authorization: Bearer APP_TOKEN'
|
||||||
```
|
```
|
||||||
|
|
||||||
Discover devices:
|
Scan local devices:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST "$BASE/api/discovery" -H "$AUTH" -H 'Content-Type: application/json' \
|
curl -X POST "$BASE/api/discovery/scan" -H "$AUTH" -H 'Content-Type: application/json' \
|
||||||
-d '{"protocol_version":0,"passes":3}'
|
-d '{"protocol_version":0,"passes":3}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+195
-39
@@ -2,7 +2,7 @@
|
|||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "GREE Controller API",
|
"title": "GREE Controller API",
|
||||||
"version": "0.15.4",
|
"version": "0.15.6",
|
||||||
"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": {
|
||||||
@@ -198,14 +198,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/discovery": {
|
"/api/discovery/scan": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
"Devices"
|
"Devices"
|
||||||
],
|
],
|
||||||
"summary": "Discover GREE devices",
|
"summary": "Scan for local GREE devices",
|
||||||
"description": "Broadcasts GREE discovery, merges known devices, attempts binding for devices without a key, persists results and reports newly discovered controller IDs.",
|
"description": "Broadcasts GREE discovery and returns local device candidates without persisting or binding them. protocol_version=1 or 2 strictly accepts only that protocol; protocol_version=0 accepts both.",
|
||||||
"operationId": "discoverDevices",
|
"operationId": "scanDiscoveredDevices",
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"required": true,
|
"required": true,
|
||||||
"content": {
|
"content": {
|
||||||
@@ -232,11 +232,77 @@
|
|||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Discovery result",
|
"description": "Discovery candidates; no devices are added automatically",
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/components/schemas/DiscoveryResponse"
|
"$ref": "#/components/schemas/DiscoveryScanResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"$ref": "#/components/responses/BadRequest"
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"$ref": "#/components/responses/Unauthorized"
|
||||||
|
},
|
||||||
|
"502": {
|
||||||
|
"$ref": "#/components/responses/BadGateway"
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"$ref": "#/components/responses/InternalError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/discovery/add": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Devices"
|
||||||
|
],
|
||||||
|
"summary": "Add selected discovered devices",
|
||||||
|
"description": "Persists and binds only the local discovery candidates selected by the caller. Each selected unit is bound strictly with the protocol version detected during the scan.",
|
||||||
|
"operationId": "addDiscoveredDevices",
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/AddDiscoveredDevicesRequest"
|
||||||
|
},
|
||||||
|
"example": {
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"name": "Living room",
|
||||||
|
"mac": "AABBCCDDEEFF",
|
||||||
|
"ip": "192.168.50.30",
|
||||||
|
"port": 7000,
|
||||||
|
"protocol_version": 1,
|
||||||
|
"model": "GREE",
|
||||||
|
"firmware": "",
|
||||||
|
"already_added": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerToken": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ApiTokenHeader": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Selected devices added",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/AddDiscoveredDevicesResponse"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5900,7 +5966,7 @@
|
|||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "0.15.4"
|
"example": "0.15.6"
|
||||||
},
|
},
|
||||||
"uptime_seconds": {
|
"uptime_seconds": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@@ -6633,37 +6699,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DiscoveryResponse": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"count": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0,
|
|
||||||
"example": 1
|
|
||||||
},
|
|
||||||
"devices": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/components/schemas/Device"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"new_device_ids": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"example": [
|
|
||||||
"gree-aabbccddeeff"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"count",
|
|
||||||
"devices",
|
|
||||||
"new_device_ids"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"TemporaryQuickThermostatRequest": {
|
"TemporaryQuickThermostatRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Persisted temporary thermostat session with delayed/absolute start and several finish rules.",
|
"description": "Persisted temporary thermostat session with delayed/absolute start and several finish rules.",
|
||||||
@@ -9602,6 +9637,127 @@
|
|||||||
"source"
|
"source"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"LocalDiscoveryCandidate": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Local GREE unit returned by discovery. No device is persisted until it is submitted to /api/discovery/add.",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Living room"
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "AABBCCDDEEFF"
|
||||||
|
},
|
||||||
|
"ip": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "192.168.50.30"
|
||||||
|
},
|
||||||
|
"port": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 65535,
|
||||||
|
"default": 7000
|
||||||
|
},
|
||||||
|
"protocol_version": {
|
||||||
|
"type": "integer",
|
||||||
|
"enum": [
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"description": "Detected protocol generation"
|
||||||
|
},
|
||||||
|
"model": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "GREE"
|
||||||
|
},
|
||||||
|
"firmware": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "1.0"
|
||||||
|
},
|
||||||
|
"already_added": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "True when this MAC already exists in the controller"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"mac",
|
||||||
|
"ip",
|
||||||
|
"port",
|
||||||
|
"protocol_version",
|
||||||
|
"model",
|
||||||
|
"firmware",
|
||||||
|
"already_added"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"AddDiscoveredDevicesRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"devices": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"maxItems": 64,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/LocalDiscoveryCandidate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"devices"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"AddDiscoveredDevicesResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"devices": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/Device"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"skipped_macs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "MAC addresses skipped because they were already registered"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"count",
|
||||||
|
"devices",
|
||||||
|
"skipped_macs"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"DiscoveryScanResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"count": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"devices": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/LocalDiscoveryCandidate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"count",
|
||||||
|
"devices"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "gree_controller",
|
"domain": "gree_controller",
|
||||||
"name": "GREE Controller",
|
"name": "GREE Controller",
|
||||||
"version": "0.15.4",
|
"version": "0.15.6",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.15.6
|
||||||
|
|
||||||
|
- Removes the obsolete add-all `POST /api/discovery` endpoint and its compatibility-only merge path.
|
||||||
|
- Local discovery now has one flow only: `POST /api/discovery/scan` followed by `POST /api/discovery/add`.
|
||||||
|
- Simplifies selected-device binding: `/api/discovery/add` no longer accepts a redundant top-level `protocol_version`; each unit is bound strictly with the protocol detected during scan.
|
||||||
|
- Removes unused discovery merge/export code and the matching obsolete OpenAPI schema.
|
||||||
|
|
||||||
|
## 0.15.5
|
||||||
|
|
||||||
|
- Local discovery no longer adds every detected unit automatically; scan results are reviewed first and only selected units are persisted and bound.
|
||||||
|
- Discovery results show each unit model, MAC address, IP address and detected protocol, with already-added units clearly marked.
|
||||||
|
- Explicit V1 AES-ECB or V2 AES-GCM discovery now strictly filters the other protocol and binds only with the selected protocol; Auto keeps V1/V2 fallback.
|
||||||
|
- Adds `POST /api/discovery/scan` and `POST /api/discovery/add` for the selective local-discovery flow.
|
||||||
|
|
||||||
## 0.15.4
|
## 0.15.4
|
||||||
|
|
||||||
- Updates the Rust dependency stack to current major/minor releases, including Axum 0.8, Reqwest 0.13, Rusqlite 0.40, Rand 0.10, AES 0.9, AES-GCM 0.11, Base64 0.23, SHA-2 0.11, Tower HTTP 0.7 and utoipa-swagger-ui 9.
|
- Updates the Rust dependency stack to current major/minor releases, including Axum 0.8, Reqwest 0.13, Rusqlite 0.40, Rand 0.10, AES 0.9, AES-GCM 0.11, Base64 0.23, SHA-2 0.11, Tower HTTP 0.7 and utoipa-swagger-ui 9.
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
name: "GREE Controller"
|
name: "GREE Controller"
|
||||||
version: "0.15.4"
|
version: "0.15.6"
|
||||||
slug: "gree_controller"
|
slug: "gree_controller"
|
||||||
description: "Local GREE HVAC controller with Web UI, API, Swagger API docs and Home Assistant integration"
|
description: "Local GREE HVAC controller with Web UI, API, Swagger API docs and Home Assistant integration"
|
||||||
url: "https://git.linuxiarz.pl/gru/gree-controller-ha-addon/"
|
url: "https://repo.example.com/gree-controller-ha-addon/"
|
||||||
arch:
|
arch:
|
||||||
- amd64
|
- amd64
|
||||||
- aarch64
|
- aarch64
|
||||||
image: "zot.linuxiarz.pl/gree-controller"
|
image: "repo.example.com/gree-controller"
|
||||||
startup: application
|
startup: application
|
||||||
boot: auto
|
boot: auto
|
||||||
init: false
|
init: false
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
name: GREE Controller
|
name: GREE Controller
|
||||||
url: https://git.linuxiarz.pl/gru/gree-controller-ha-addon/
|
url: https://repo.example.com/gree-controller-ha-addon/
|
||||||
maintainer: linuxiarz.pl
|
maintainer: linuxiarz.pl
|
||||||
|
|||||||
+10
-4
@@ -296,14 +296,14 @@
|
|||||||
"devices.savedAndChecked": "Saved and connection confirmed",
|
"devices.savedAndChecked": "Saved and connection confirmed",
|
||||||
"devices.savedCheckFailed": "Configuration saved, but the connection test failed",
|
"devices.savedCheckFailed": "Configuration saved, but the connection test failed",
|
||||||
"discovery.title": "Discover GREE devices",
|
"discovery.title": "Discover GREE devices",
|
||||||
"discovery.help": "Auto searches both AES-ECB and AES-GCM devices. Multiple passes improve discovery when several Wi-Fi modules answer the same broadcast.",
|
"discovery.help": "Auto accepts both AES-ECB and AES-GCM devices. Selecting V1 or V2 accepts and binds only that protocol. Multiple passes improve discovery when several Wi-Fi modules answer the same broadcast.",
|
||||||
"discovery.protocol": "Discovery protocol",
|
"discovery.protocol": "Discovery protocol",
|
||||||
"discovery.passes": "Scan passes",
|
"discovery.passes": "Scan passes",
|
||||||
"discovery.timeout": "Total scan time (ms)",
|
"discovery.timeout": "Total scan time (ms)",
|
||||||
"zones.quickHint": "Quick target and mode control",
|
"zones.quickHint": "Quick target and mode control",
|
||||||
"actions.later": "Later",
|
"actions.later": "Later",
|
||||||
"discovery.nameDevices": "Name discovered devices",
|
"discovery.nameDevices": "Discovered local devices",
|
||||||
"discovery.nameDevicesHelp": "Give each new unit a friendly room name. The technical model and MAC remain available in diagnostics.",
|
"discovery.nameDevicesHelp": "Select the units you want to add. MAC address, IP address and detected protocol are shown for every result.",
|
||||||
"dashboard.thermostats": "Thermostats",
|
"dashboard.thermostats": "Thermostats",
|
||||||
"common.unavailable": "Unavailable",
|
"common.unavailable": "Unavailable",
|
||||||
"mode.off": "Off",
|
"mode.off": "Off",
|
||||||
@@ -1408,6 +1408,12 @@
|
|||||||
"publicChart.field.temperature": "Temperature",
|
"publicChart.field.temperature": "Temperature",
|
||||||
"flow.invalidPresetFilename": "Invalid preset filename.",
|
"flow.invalidPresetFilename": "Invalid preset filename.",
|
||||||
"flow.invalidPresetFile": "{filename}: invalid preset.",
|
"flow.invalidPresetFile": "{filename}: invalid preset.",
|
||||||
"flow.presetNotFound": "Preset not found."
|
"flow.presetNotFound": "Preset not found.",
|
||||||
|
"discovery.addSelected": "Add selected",
|
||||||
|
"discovery.selectAtLeastOne": "Select at least one device to add.",
|
||||||
|
"discovery.addedCount": "Added: {count}",
|
||||||
|
"discovery.alreadyAdded": "Already added",
|
||||||
|
"discovery.empty": "No local devices found",
|
||||||
|
"discovery.emptyHint": "Try a longer scan, another protocol, or verify that the unit answers on UDP port 7000."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -296,14 +296,14 @@
|
|||||||
"devices.savedAndChecked": "Zapisano i potwierdzono połączenie",
|
"devices.savedAndChecked": "Zapisano i potwierdzono połączenie",
|
||||||
"devices.savedCheckFailed": "Zapisano konfigurację, ale test połączenia nie powiódł się",
|
"devices.savedCheckFailed": "Zapisano konfigurację, ale test połączenia nie powiódł się",
|
||||||
"discovery.title": "Wykrywanie urządzeń GREE",
|
"discovery.title": "Wykrywanie urządzeń GREE",
|
||||||
"discovery.help": "Auto wyszukuje urządzenia AES-ECB i AES-GCM. Kilka przebiegów zwiększa skuteczność, gdy wiele modułów Wi-Fi odpowiada na ten sam broadcast.",
|
"discovery.help": "Auto akceptuje urządzenia AES-ECB i AES-GCM. Wybranie V1 lub V2 powoduje wyszukiwanie i bindowanie wyłącznie tym protokołem. Kilka przebiegów pomaga, gdy wiele modułów Wi-Fi odpowiada na ten sam broadcast.",
|
||||||
"discovery.protocol": "Protokół wykrywania",
|
"discovery.protocol": "Protokół wykrywania",
|
||||||
"discovery.passes": "Liczba przebiegów",
|
"discovery.passes": "Liczba przebiegów",
|
||||||
"discovery.timeout": "Łączny czas wyszukiwania (ms)",
|
"discovery.timeout": "Łączny czas wyszukiwania (ms)",
|
||||||
"zones.quickHint": "Szybka zmiana celu i trybu",
|
"zones.quickHint": "Szybka zmiana celu i trybu",
|
||||||
"actions.later": "Później",
|
"actions.later": "Później",
|
||||||
"discovery.nameDevices": "Nazwij znalezione urządzenia",
|
"discovery.nameDevices": "Wyszukane urządzenia lokalne",
|
||||||
"discovery.nameDevicesHelp": "Nadaj każdej nowej jednostce przyjazną nazwę pokoju. Model techniczny i MAC pozostaną dostępne w diagnostyce.",
|
"discovery.nameDevicesHelp": "Wybierz jednostki, które chcesz dodać. Dla każdego wyniku widoczny jest adres MAC, adres IP i wykryty protokół.",
|
||||||
"dashboard.quickThermostats": "Termostaty",
|
"dashboard.quickThermostats": "Termostaty",
|
||||||
"common.unavailable": "Niedostępne",
|
"common.unavailable": "Niedostępne",
|
||||||
"mode.off": "Wyłączone",
|
"mode.off": "Wyłączone",
|
||||||
@@ -1408,6 +1408,12 @@
|
|||||||
"publicChart.field.temperature": "Temperatura",
|
"publicChart.field.temperature": "Temperatura",
|
||||||
"flow.invalidPresetFilename": "Nieprawidłowa nazwa pliku presetu.",
|
"flow.invalidPresetFilename": "Nieprawidłowa nazwa pliku presetu.",
|
||||||
"flow.invalidPresetFile": "{filename}: nieprawidłowy preset.",
|
"flow.invalidPresetFile": "{filename}: nieprawidłowy preset.",
|
||||||
"flow.presetNotFound": "Nie znaleziono presetu."
|
"flow.presetNotFound": "Nie znaleziono presetu.",
|
||||||
|
"discovery.addSelected": "Dodaj wybrane",
|
||||||
|
"discovery.selectAtLeastOne": "Wybierz co najmniej jedno urządzenie do dodania.",
|
||||||
|
"discovery.addedCount": "Dodano: {count}",
|
||||||
|
"discovery.alreadyAdded": "Już dodane",
|
||||||
|
"discovery.empty": "Nie znaleziono urządzeń lokalnych",
|
||||||
|
"discovery.emptyHint": "Spróbuj dłuższego skanowania, innego protokołu albo sprawdź, czy jednostka odpowiada na UDP 7000."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Developer smoke/integration tests for GREE Controller API 0.15.4.
|
"""Developer smoke/integration tests for GREE Controller API 0.15.6.
|
||||||
|
|
||||||
Default mode is read-only and safe to run against a real controller.
|
Default mode is read-only and safe to run against a real controller.
|
||||||
Use --settings-write to additionally round-trip all split settings resources and
|
Use --settings-write to additionally round-trip all split settings resources and
|
||||||
@@ -26,7 +26,7 @@ from typing import Any, Callable, Iterable, Optional
|
|||||||
|
|
||||||
DEFAULT_BASE_URL = os.environ.get("GREE_API_URL", "http://127.0.0.1:8787")
|
DEFAULT_BASE_URL = os.environ.get("GREE_API_URL", "http://127.0.0.1:8787")
|
||||||
DEFAULT_TOKEN = os.environ.get("GREE_API_TOKEN", "")
|
DEFAULT_TOKEN = os.environ.get("GREE_API_TOKEN", "")
|
||||||
DEFAULT_EXPECTED_VERSION = "0.15.4"
|
DEFAULT_EXPECTED_VERSION = "0.15.6"
|
||||||
|
|
||||||
SETTINGS_PATHS: dict[str, set[str]] = {
|
SETTINGS_PATHS: dict[str, set[str]] = {
|
||||||
"/api/settings/application": {"simulator_enabled"},
|
"/api/settings/application": {"simulator_enabled"},
|
||||||
@@ -593,7 +593,7 @@ def run_suite(args: argparse.Namespace) -> int:
|
|||||||
print(f"Settings write tests: {'ENABLED' if args.settings_write else 'disabled'}\n")
|
print(f"Settings write tests: {'ENABLED' if args.settings_write else 'disabled'}\n")
|
||||||
|
|
||||||
runner.run("public health", lambda: test_health(client, args.expected_version))
|
runner.run("public health", lambda: test_health(client, args.expected_version))
|
||||||
runner.run("OpenAPI 0.15.4 contract", lambda: test_openapi(client, args.expected_version))
|
runner.run("OpenAPI 0.15.6 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))
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -3,20 +3,19 @@ use crate::{
|
|||||||
error::AppError,
|
error::AppError,
|
||||||
home_assistant, influxdb,
|
home_assistant, influxdb,
|
||||||
models::{
|
models::{
|
||||||
ApiTokenInfo, ApplicationSettings, Automation, ClimateGroup, ConfigurationExport,
|
AddDiscoveredDevicesRequest, ApiTokenInfo, ApplicationSettings, Automation, ClimateGroup,
|
||||||
ConnectionStatus, ConnectionType, DebugSettings, Device, DeviceCommand, DeviceGroup,
|
ConfigurationExport, ConnectionStatus, ConnectionType, DebugSettings, Device, DeviceCommand, DeviceGroup,
|
||||||
DeviceGroupKind, DevicePatch, DiscoveryRequest, EnergyReading, EnergySourcePreference,
|
DeviceGroupKind, DevicePatch, DiscoveryRequest, EnergyReading, EnergySourcePreference,
|
||||||
Flow, GreeCloudSettings, GreeCloudSettingsUpdate, GreeCloudSettingsView, GreeSettings,
|
Flow, GreeCloudSettings, GreeCloudSettingsUpdate, GreeCloudSettingsView, GreeSettings,
|
||||||
GroupControlPatch, HaReading, HistorySettings, HomeAssistantSettings,
|
GroupControlPatch, HaReading, HistorySettings, HomeAssistantSettings,
|
||||||
HomeAssistantSettingsUpdate, HomeAssistantSettingsView, InfluxDbSettings,
|
HomeAssistantSettingsUpdate, HomeAssistantSettingsView, InfluxDbSettings,
|
||||||
InfluxDbSettingsUpdate, InfluxDbSettingsView, ManualDeviceRequest, NetworkReading,
|
InfluxDbSettingsUpdate, InfluxDbSettingsView, LocalDiscoveryCandidate, ManualDeviceRequest, NetworkReading,
|
||||||
NightModeSettings, NotificationSettings, NotificationSettingsUpdate,
|
NightModeSettings, NotificationSettings, NotificationSettingsUpdate,
|
||||||
NotificationSettingsView, Reading, RuntimeSettings, Schedule, SettingsSnapshot,
|
NotificationSettingsView, Reading, RuntimeSettings, Schedule, SettingsSnapshot,
|
||||||
TemporaryQuickThermostat, TemporaryQuickThermostatRequest, Zone, ZoneControlPatch,
|
TemporaryQuickThermostat, TemporaryQuickThermostatRequest, Zone, ZoneControlPatch,
|
||||||
ZoneReading,
|
ZoneReading,
|
||||||
},
|
},
|
||||||
notifications,
|
notifications,
|
||||||
protocol::merge_discovered,
|
|
||||||
state::AppState,
|
state::AppState,
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
@@ -87,7 +86,8 @@ pub fn router(state: AppState) -> Router {
|
|||||||
let protected = Router::new()
|
let protected = Router::new()
|
||||||
.route("/api/bootstrap", get(bootstrap))
|
.route("/api/bootstrap", get(bootstrap))
|
||||||
.route("/api/system/info", get(system_info))
|
.route("/api/system/info", get(system_info))
|
||||||
.route("/api/discovery", post(discover))
|
.route("/api/discovery/scan", post(scan_discovery))
|
||||||
|
.route("/api/discovery/add", post(add_discovered_devices))
|
||||||
.route("/api/devices", get(list_devices).post(add_device))
|
.route("/api/devices", get(list_devices).post(add_device))
|
||||||
.route(
|
.route(
|
||||||
"/api/devices/{id}",
|
"/api/devices/{id}",
|
||||||
|
|||||||
+214
-39
@@ -1,15 +1,130 @@
|
|||||||
async fn discover(
|
fn normalize_local_discovery_mac(value: &str) -> String {
|
||||||
State(state): State<AppState>,
|
value.replace([':', '-'], "").trim().to_ascii_uppercase()
|
||||||
Json(request): Json<DiscoveryRequest>,
|
}
|
||||||
) -> Result<Json<Value>, AppError> {
|
|
||||||
let _configuration_guard = state.lock_configuration_operation().await;
|
fn local_discovery_candidate(device: &Device, already_added: bool) -> LocalDiscoveryCandidate {
|
||||||
|
LocalDiscoveryCandidate {
|
||||||
|
name: device.name.clone(),
|
||||||
|
mac: device.mac.clone(),
|
||||||
|
ip: device.ip.clone(),
|
||||||
|
port: device.port,
|
||||||
|
protocol_version: device.protocol_version,
|
||||||
|
model: device.model.clone(),
|
||||||
|
firmware: device.firmware.clone(),
|
||||||
|
already_added,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_from_local_discovery(candidate: LocalDiscoveryCandidate) -> Result<Device, AppError> {
|
||||||
|
let mac = normalize_local_discovery_mac(&candidate.mac);
|
||||||
|
if mac.is_empty() {
|
||||||
|
return Err(AppError::BadRequest("discovered device MAC is required".into()));
|
||||||
|
}
|
||||||
|
candidate
|
||||||
|
.ip
|
||||||
|
.parse::<IpAddr>()
|
||||||
|
.map_err(|_| AppError::BadRequest(format!("invalid IP address for {mac}")))?;
|
||||||
|
if !matches!(candidate.protocol_version, 1 | 2) {
|
||||||
|
return Err(AppError::BadRequest(format!(
|
||||||
|
"invalid protocol version for {mac}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let model = candidate.model.trim().to_string();
|
||||||
|
let fallback_model = if model.is_empty() { "GREE" } else { &model };
|
||||||
|
let suffix = mac
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.take(4)
|
||||||
|
.collect::<String>()
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.collect::<String>();
|
||||||
|
let name = if candidate.name.trim().is_empty() {
|
||||||
|
format!("{fallback_model} {suffix}")
|
||||||
|
} else {
|
||||||
|
candidate.name.trim().to_string()
|
||||||
|
};
|
||||||
|
let now = Utc::now();
|
||||||
|
|
||||||
|
Ok(Device {
|
||||||
|
id: format!("gree-{}", mac.to_ascii_lowercase()),
|
||||||
|
mac,
|
||||||
|
name,
|
||||||
|
connection_type: ConnectionType::Local,
|
||||||
|
connection_status: ConnectionStatus::Unknown,
|
||||||
|
cloud_device_id: None,
|
||||||
|
cloud_parent_mac: None,
|
||||||
|
cloud_account_id: None,
|
||||||
|
ip: candidate.ip,
|
||||||
|
port: if candidate.port == 0 { 7000 } else { candidate.port },
|
||||||
|
protocol_version: candidate.protocol_version,
|
||||||
|
model,
|
||||||
|
firmware: candidate.firmware.trim().to_string(),
|
||||||
|
key: None,
|
||||||
|
cid: Some("app".into()),
|
||||||
|
enabled: true,
|
||||||
|
simulated: false,
|
||||||
|
power: false,
|
||||||
|
mode: "cool".into(),
|
||||||
|
target_temperature: 24.0,
|
||||||
|
fan_speed: 0,
|
||||||
|
swing_vertical: 0,
|
||||||
|
swing_horizontal: 0,
|
||||||
|
quiet: false,
|
||||||
|
quiet_wire_value: None,
|
||||||
|
turbo: false,
|
||||||
|
light: true,
|
||||||
|
air: false,
|
||||||
|
xfan: false,
|
||||||
|
health: false,
|
||||||
|
sleep: false,
|
||||||
|
supports_light: None,
|
||||||
|
supports_quiet: None,
|
||||||
|
supports_turbo: None,
|
||||||
|
supports_air: None,
|
||||||
|
supports_xfan: None,
|
||||||
|
supports_health: None,
|
||||||
|
supports_sleep: None,
|
||||||
|
supports_buzzer_control: None,
|
||||||
|
supports_energy_meter: None,
|
||||||
|
total_energy_kwh: None,
|
||||||
|
compressor_frequency_hz: None,
|
||||||
|
last_cloud_sync: None,
|
||||||
|
current_temperature: None,
|
||||||
|
outdoor_temperature: None,
|
||||||
|
temperature_sensor_offset: None,
|
||||||
|
online: true,
|
||||||
|
response_time_ms: None,
|
||||||
|
last_seen: Some(now),
|
||||||
|
last_error: None,
|
||||||
|
communication_failures: 0,
|
||||||
|
pending_command: false,
|
||||||
|
capabilities: crate::models::DeviceCapabilities::default(),
|
||||||
|
energy_source: EnergySourcePreference::Auto,
|
||||||
|
ha_energy_entity_id: None,
|
||||||
|
ha_energy_unit: None,
|
||||||
|
ha_energy_device_class: None,
|
||||||
|
ha_energy_state_class: None,
|
||||||
|
created_at: now,
|
||||||
|
updated_at: now,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_local_discovery(
|
||||||
|
state: &AppState,
|
||||||
|
request: DiscoveryRequest,
|
||||||
|
) -> Result<(u8, u8, Vec<Device>), AppError> {
|
||||||
let settings = state.settings.read().await.clone();
|
let settings = state.settings.read().await.clone();
|
||||||
let timeout_ms = request
|
let timeout_ms = request
|
||||||
.timeout_ms
|
.timeout_ms
|
||||||
.unwrap_or(settings.discovery_timeout_ms)
|
.unwrap_or(settings.discovery_timeout_ms)
|
||||||
.clamp(500, 30_000);
|
.clamp(500, 30_000);
|
||||||
let broadcast = request.broadcast.unwrap_or(settings.discovery_broadcast);
|
let broadcast = request.broadcast.unwrap_or(settings.discovery_broadcast);
|
||||||
let protocol_version = request.protocol_version.unwrap_or(0).min(2);
|
let protocol_version = request.protocol_version.unwrap_or(0);
|
||||||
|
if protocol_version > 2 {
|
||||||
|
return Err(AppError::BadRequest("protocol_version must be 0, 1 or 2".into()));
|
||||||
|
}
|
||||||
let passes = request.passes.unwrap_or(3).clamp(1, 10);
|
let passes = request.passes.unwrap_or(3).clamp(1, 10);
|
||||||
let discovered = state
|
let discovered = state
|
||||||
.providers
|
.providers
|
||||||
@@ -23,52 +138,112 @@ async fn discover(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| AppError::Device(e.to_string()))?;
|
.map_err(|e| AppError::Device(e.to_string()))?;
|
||||||
let mut saved = Vec::new();
|
Ok((protocol_version, passes, discovered))
|
||||||
let mut new_device_ids = Vec::new();
|
}
|
||||||
for item in discovered {
|
|
||||||
let existing = state.db.get_device_by_mac(&item.mac)?;
|
/// Scan for local GREE units without persisting or binding them.
|
||||||
let is_new = existing.is_none();
|
async fn scan_discovery(
|
||||||
let mut merged = merge_discovered(existing, item);
|
State(state): State<AppState>,
|
||||||
let _device_guard = state.lock_device_operation(&merged.id).await;
|
Json(request): Json<DiscoveryRequest>,
|
||||||
// A poll/command may have updated the same known device between discovery and
|
) -> Result<Json<Value>, AppError> {
|
||||||
// acquiring its operation lock. Re-merge against the freshest persisted state.
|
let (protocol_version, passes, discovered) = run_local_discovery(&state, request).await?;
|
||||||
if !is_new {
|
let mut candidates = Vec::with_capacity(discovered.len());
|
||||||
if let Some(current) = state.db.get_device(&merged.id)? {
|
for device in discovered {
|
||||||
merged = merge_discovered(Some(current), merged);
|
let mac = normalize_local_discovery_mac(&device.mac);
|
||||||
|
let already_added = state.db.get_device_by_mac(&mac)?.is_some();
|
||||||
|
candidates.push(local_discovery_candidate(&device, already_added));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.log(
|
||||||
|
"info",
|
||||||
|
"discovery.scan_complete",
|
||||||
|
&format!("Discovery scan found {} device(s)", candidates.len()),
|
||||||
|
json!({
|
||||||
|
"count": candidates.len(),
|
||||||
|
"protocol_version": protocol_version,
|
||||||
|
"passes": passes,
|
||||||
|
"persisted": false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
Ok(Json(json!({
|
||||||
|
"count": candidates.len(),
|
||||||
|
"devices": candidates,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_discovered_devices(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Json(request): Json<AddDiscoveredDevicesRequest>,
|
||||||
|
) -> Result<Json<Value>, AppError> {
|
||||||
|
let _configuration_guard = state.lock_configuration_operation().await;
|
||||||
|
if request.devices.is_empty() {
|
||||||
|
return Err(AppError::BadRequest("select at least one discovered device".into()));
|
||||||
}
|
}
|
||||||
// Bind right after discovery. GREE modules can have a short bind window;
|
if request.devices.len() > 64 {
|
||||||
// bind() also refreshes it with a direct scan before the handshake.
|
return Err(AppError::BadRequest("too many discovered devices selected".into()));
|
||||||
if !merged.simulated && merged.key.as_deref().unwrap_or_default().is_empty() {
|
}
|
||||||
match state.providers.local().client().bind(&merged).await {
|
|
||||||
|
let selected_count = request.devices.len();
|
||||||
|
let mut added = Vec::new();
|
||||||
|
let mut skipped = Vec::new();
|
||||||
|
|
||||||
|
for candidate in request.devices {
|
||||||
|
let mut device = device_from_local_discovery(candidate)?;
|
||||||
|
let _device_guard = state.lock_device_operation(&device.id).await;
|
||||||
|
if state.db.get_device_by_mac(&device.mac)?.is_some() {
|
||||||
|
skipped.push(device.mac.clone());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match state
|
||||||
|
.providers
|
||||||
|
.local()
|
||||||
|
.client()
|
||||||
|
.bind_exact(&device, device.protocol_version)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(bound) => {
|
Ok(bound) => {
|
||||||
merged.key = Some(bound.key);
|
device.key = Some(bound.key);
|
||||||
merged.protocol_version = bound.protocol_version;
|
device.protocol_version = bound.protocol_version;
|
||||||
merged.communication_failures = 0;
|
device.communication_failures = 0;
|
||||||
merged.last_error = None;
|
device.last_error = None;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
merged.last_error = Some(format!("discovered, bind pending: {err}"));
|
device.last_error = Some(format!("added, bind pending: {err}"));
|
||||||
state.log(
|
state.log(
|
||||||
"warn",
|
"warn",
|
||||||
"device.bind_after_discovery",
|
"device.bind_after_discovery",
|
||||||
&format!("{}: {err}", merged.name),
|
&format!("{}: {err}", device.name),
|
||||||
json!({"device_id": merged.id}),
|
json!({
|
||||||
|
"device_id": device.id,
|
||||||
|
"protocol_version": device.protocol_version,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.db.save_device(&device)?;
|
||||||
|
added.push(device);
|
||||||
}
|
}
|
||||||
state.db.save_device(&merged)?;
|
|
||||||
if is_new {
|
state.log(
|
||||||
new_device_ids.push(merged.id.clone());
|
"info",
|
||||||
|
"discovery.devices_added",
|
||||||
|
&format!("Added {} discovered device(s)", added.len()),
|
||||||
|
json!({
|
||||||
|
"selected": selected_count,
|
||||||
|
"added": added.len(),
|
||||||
|
"skipped": skipped.len(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if !added.is_empty() {
|
||||||
|
state.broadcast("devices.discovered", json!({"devices": added}));
|
||||||
}
|
}
|
||||||
saved.push(merged);
|
Ok(Json(json!({
|
||||||
}
|
"count": added.len(),
|
||||||
state.log("info", "discovery.complete", &format!("Discovery found {} device(s)", saved.len()), json!({"count": saved.len(), "protocol_version": protocol_version, "passes": passes, "new_devices": new_device_ids.len()}));
|
"devices": added,
|
||||||
state.broadcast("devices.discovered", json!({"devices": saved}));
|
"skipped_macs": skipped,
|
||||||
Ok(Json(
|
})))
|
||||||
json!({"count": saved.len(), "devices": saved, "new_device_ids": new_device_ids}),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_devices(State(state): State<AppState>) -> Result<Json<Vec<Device>>, AppError> {
|
async fn list_devices(State(state): State<AppState>) -> Result<Json<Vec<Device>>, AppError> {
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ mod tests {
|
|||||||
fn every_documented_operation_has_summary_description_and_responses() {
|
fn every_documented_operation_has_summary_description_and_responses() {
|
||||||
let document = document("");
|
let document = document("");
|
||||||
let paths = document["paths"].as_object().expect("OpenAPI paths object");
|
let paths = document["paths"].as_object().expect("OpenAPI paths object");
|
||||||
|
assert!(!paths.contains_key("/api/discovery"));
|
||||||
|
assert!(paths.contains_key("/api/discovery/scan"));
|
||||||
|
assert!(paths.contains_key("/api/discovery/add"));
|
||||||
for (path, item) in paths {
|
for (path, item) in paths {
|
||||||
let methods = item.as_object().expect("OpenAPI path item");
|
let methods = item.as_object().expect("OpenAPI path item");
|
||||||
for (method, operation) in methods {
|
for (method, operation) in methods {
|
||||||
|
|||||||
@@ -117,6 +117,29 @@ pub struct DiscoveryRequest {
|
|||||||
pub passes: Option<u8>,
|
pub passes: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct LocalDiscoveryCandidate {
|
||||||
|
pub name: String,
|
||||||
|
pub mac: String,
|
||||||
|
pub ip: String,
|
||||||
|
#[serde(default = "default_port")]
|
||||||
|
pub port: u16,
|
||||||
|
/// Detected protocol: 1 = AES-ECB, 2 = AES-GCM.
|
||||||
|
pub protocol_version: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub model: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub firmware: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub already_added: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AddDiscoveredDevicesRequest {
|
||||||
|
#[serde(default)]
|
||||||
|
pub devices: Vec<LocalDiscoveryCandidate>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ManualDeviceRequest {
|
pub struct ManualDeviceRequest {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ use tokio::{
|
|||||||
sync::broadcast,
|
sync::broadcast,
|
||||||
time::{timeout, Instant},
|
time::{timeout, Instant},
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BindResult {
|
pub struct BindResult {
|
||||||
@@ -51,5 +50,4 @@ include!("gree/polling.rs");
|
|||||||
include!("gree/commands.rs");
|
include!("gree/commands.rs");
|
||||||
include!("gree/transport.rs");
|
include!("gree/transport.rs");
|
||||||
include!("gree/network.rs");
|
include!("gree/network.rs");
|
||||||
include!("gree/merge.rs");
|
|
||||||
include!("gree/tests.rs");
|
include!("gree/tests.rs");
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
impl GreeClient {
|
impl GreeClient {
|
||||||
pub async fn bind(&self, device: &Device) -> Result<BindResult> {
|
pub async fn bind(&self, device: &Device) -> Result<BindResult> {
|
||||||
let versions: &[u8] = match device.protocol_version {
|
let versions = Self::auto_bind_versions(device.protocol_version);
|
||||||
2 => &[2, 1],
|
self.bind_with_versions(device, versions).await
|
||||||
_ => &[1, 2],
|
}
|
||||||
};
|
|
||||||
|
/// Bind using exactly the protocol detected during discovery.
|
||||||
|
pub async fn bind_exact(&self, device: &Device, protocol_version: u8) -> Result<BindResult> {
|
||||||
|
let versions = Self::exact_bind_versions(protocol_version)?;
|
||||||
|
self.bind_with_versions(device, versions).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn bind_with_versions(&self, device: &Device, versions: &[u8]) -> Result<BindResult> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
for &version in versions {
|
for &version in versions {
|
||||||
match self.bind_attempt(device, version).await {
|
match self.bind_attempt(device, version).await {
|
||||||
@@ -22,6 +29,18 @@ impl GreeClient {
|
|||||||
bail!("unable to bind device ({})", errors.join("; "))
|
bail!("unable to bind device ({})", errors.join("; "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn auto_bind_versions(device_protocol: u8) -> &'static [u8] {
|
||||||
|
if device_protocol == 2 { &[2, 1] } else { &[1, 2] }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exact_bind_versions(protocol_version: u8) -> Result<&'static [u8]> {
|
||||||
|
match protocol_version {
|
||||||
|
1 => Ok(&[1]),
|
||||||
|
2 => Ok(&[2]),
|
||||||
|
other => bail!("unsupported GREE protocol version: {other}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// GREE Wi-Fi modules use the 12-hex device id as a protocol identifier.
|
/// GREE Wi-Fi modules use the 12-hex device id as a protocol identifier.
|
||||||
/// Older V1 modules (notably 502cc6...) can silently ignore bind/status
|
/// Older V1 modules (notably 502cc6...) can silently ignore bind/status
|
||||||
/// packets when tcid/mac casing differs from the lowercase value returned
|
/// packets when tcid/mac casing differs from the lowercase value returned
|
||||||
|
|||||||
@@ -52,11 +52,8 @@ impl GreeClient {
|
|||||||
let Ok(value) = serde_json::from_slice::<Value>(&buffer[..size]) else {
|
let Ok(value) = serde_json::from_slice::<Value>(&buffer[..size]) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
match self.parse_discovery(value, source) {
|
match self.parse_discovery(value, source, protocol_filter) {
|
||||||
Ok(Some(mut device)) => {
|
Ok(Some(mut device)) => {
|
||||||
if protocol_filter != 0 && device.protocol_version != protocol_filter {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let key = device.mac.to_ascii_lowercase();
|
let key = device.mac.to_ascii_lowercase();
|
||||||
if seen.insert(key) {
|
if seen.insert(key) {
|
||||||
device.last_seen = Some(Utc::now());
|
device.last_seen = Some(Utc::now());
|
||||||
@@ -77,11 +74,24 @@ impl GreeClient {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_discovery(&self, mut value: Value, source: SocketAddr) -> Result<Option<Device>> {
|
fn parse_discovery(
|
||||||
|
&self,
|
||||||
|
mut value: Value,
|
||||||
|
source: SocketAddr,
|
||||||
|
protocol_filter: u8,
|
||||||
|
) -> Result<Option<Device>> {
|
||||||
let mut detected_protocol = 1_u8;
|
let mut detected_protocol = 1_u8;
|
||||||
if value.get("t").and_then(Value::as_str) == Some("pack") {
|
if value.get("t").and_then(Value::as_str) == Some("pack") {
|
||||||
if let Some(pack_value) = value.get("pack") {
|
if let Some(pack_value) = value.get("pack") {
|
||||||
if let Some(pack) = pack_value.as_str() {
|
if let Some(pack) = pack_value.as_str() {
|
||||||
|
let packet_protocol = if value.get("tag").and_then(Value::as_str).is_some() {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
if protocol_filter != 0 && packet_protocol != protocol_filter {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
let clear = if let Some(tag) = value.get("tag").and_then(Value::as_str) {
|
let clear = if let Some(tag) = value.get("tag").and_then(Value::as_str) {
|
||||||
detected_protocol = 2;
|
detected_protocol = 2;
|
||||||
decrypt_v2(GENERIC_GREE_V2_KEY, pack, tag)?
|
decrypt_v2(GENERIC_GREE_V2_KEY, pack, tag)?
|
||||||
@@ -91,10 +101,16 @@ impl GreeClient {
|
|||||||
value = serde_json::from_slice::<Value>(&clear)
|
value = serde_json::from_slice::<Value>(&clear)
|
||||||
.context("invalid decrypted discovery JSON")?;
|
.context("invalid decrypted discovery JSON")?;
|
||||||
} else if pack_value.is_object() {
|
} else if pack_value.is_object() {
|
||||||
|
if protocol_filter == 2 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
value = pack_value.clone();
|
value = pack_value.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if protocol_filter != 0 && detected_protocol != protocol_filter {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
let kind = value
|
let kind = value
|
||||||
.get("t")
|
.get("t")
|
||||||
.and_then(Value::as_str)
|
.and_then(Value::as_str)
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
pub fn merge_discovered(existing: Option<Device>, discovered: Device) -> Device {
|
|
||||||
if let Some(mut old) = existing {
|
|
||||||
old.ip = discovered.ip;
|
|
||||||
old.port = discovered.port;
|
|
||||||
if old.name.trim().is_empty()
|
|
||||||
|| old.name == "Klimatyzator GREE"
|
|
||||||
|| old.name == "GREE air conditioner"
|
|
||||||
{
|
|
||||||
old.name = discovered.name;
|
|
||||||
}
|
|
||||||
if !discovered.model.is_empty() {
|
|
||||||
old.model = discovered.model;
|
|
||||||
}
|
|
||||||
if !discovered.firmware.is_empty() {
|
|
||||||
old.firmware = discovered.firmware;
|
|
||||||
}
|
|
||||||
if old.protocol_version != discovered.protocol_version {
|
|
||||||
old.protocol_version = discovered.protocol_version;
|
|
||||||
old.key = None;
|
|
||||||
}
|
|
||||||
old.online = true;
|
|
||||||
old.communication_failures = 0;
|
|
||||||
old.last_seen = Some(Utc::now());
|
|
||||||
old.last_error = None;
|
|
||||||
old.updated_at = Utc::now();
|
|
||||||
old
|
|
||||||
} else {
|
|
||||||
let mut new = discovered;
|
|
||||||
if new.id.is_empty() {
|
|
||||||
new.id = Uuid::new_v4().to_string();
|
|
||||||
}
|
|
||||||
new
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -118,4 +118,40 @@ mod tests {
|
|||||||
assert!(device.air);
|
assert!(device.air);
|
||||||
assert_eq!(device.supports_air, Some(true));
|
assert_eq!(device.supports_air, Some(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn discovery_bind_uses_detected_protocol_exactly() {
|
||||||
|
assert_eq!(GreeClient::exact_bind_versions(1).unwrap(), &[1]);
|
||||||
|
assert_eq!(GreeClient::exact_bind_versions(2).unwrap(), &[2]);
|
||||||
|
assert!(GreeClient::exact_bind_versions(0).is_err());
|
||||||
|
assert!(GreeClient::exact_bind_versions(3).is_err());
|
||||||
|
assert_eq!(GreeClient::auto_bind_versions(1), &[1, 2]);
|
||||||
|
assert_eq!(GreeClient::auto_bind_versions(2), &[2, 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn discovery_protocol_filter_rejects_other_envelope_before_decode() {
|
||||||
|
let client = GreeClient::new(
|
||||||
|
"test-controller".into(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
);
|
||||||
|
let source: SocketAddr = "192.0.2.20:7000".parse().unwrap();
|
||||||
|
let v1 = json!({
|
||||||
|
"t": "pack",
|
||||||
|
"pack": {
|
||||||
|
"t": "dev",
|
||||||
|
"mac": "AABBCCDDEEFF",
|
||||||
|
"name": "Test"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(client
|
||||||
|
.parse_discovery(v1.clone(), source, 1)
|
||||||
|
.unwrap()
|
||||||
|
.is_some());
|
||||||
|
assert!(client.parse_discovery(v1, source, 2).unwrap().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,4 +3,4 @@ pub mod gree;
|
|||||||
pub mod gree_cloud;
|
pub mod gree_cloud;
|
||||||
pub mod gree_cloud_mqtt;
|
pub mod gree_cloud_mqtt;
|
||||||
|
|
||||||
pub use gree::{merge_discovered, GreeClient};
|
pub use gree::GreeClient;
|
||||||
|
|||||||
@@ -1538,6 +1538,34 @@ legend {
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.local-discovery-row {
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.local-discovery-head {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.local-discovery-head > input[type="checkbox"] {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.local-discovery-head > label {
|
||||||
|
display: grid;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.local-discovery-head > .badge {
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: color-mix(in srgb, var(--accent) 42%, var(--surface-2)) var(--surface-muted);
|
scrollbar-color: color-mix(in srgb, var(--accent) 42%, var(--surface-2)) var(--surface-muted);
|
||||||
|
|||||||
+5
-7
@@ -1263,8 +1263,7 @@
|
|||||||
<h2 data-i18n="discovery.title">Discover GREE devices</h2><button type="button" data-close
|
<h2 data-i18n="discovery.title">Discover GREE devices</h2><button type="button" data-close
|
||||||
data-ui-icon="close"></button>
|
data-ui-icon="close"></button>
|
||||||
</div>
|
</div>
|
||||||
<p class="field-note" data-i18n="discovery.help">Auto searches both AES-ECB and AES-GCM devices. Multiple passes
|
<p class="field-note" data-i18n="discovery.help">Auto accepts both AES-ECB and AES-GCM devices. Selecting V1 or V2 accepts and binds only that protocol. Multiple passes improve discovery when several Wi-Fi modules answer the same broadcast.</p>
|
||||||
improve discovery when several Wi-Fi modules answer the same broadcast.</p>
|
|
||||||
<label><span data-i18n="discovery.protocol">Discovery protocol</span><select name="protocol_version">
|
<label><span data-i18n="discovery.protocol">Discovery protocol</span><select name="protocol_version">
|
||||||
<option value="0" data-i18n="devices.protocolAuto">Auto (V1 + V2)</option>
|
<option value="0" data-i18n="devices.protocolAuto">Auto (V1 + V2)</option>
|
||||||
<option value="1">V1 AES-ECB</option>
|
<option value="1">V1 AES-ECB</option>
|
||||||
@@ -1282,15 +1281,14 @@
|
|||||||
<dialog id="discoveryNamesDialog">
|
<dialog id="discoveryNamesDialog">
|
||||||
<form method="dialog" id="discoveryNamesForm" class="dialog-form">
|
<form method="dialog" id="discoveryNamesForm" class="dialog-form">
|
||||||
<div class="dialog-head">
|
<div class="dialog-head">
|
||||||
<h2 data-i18n="discovery.nameDevices">Name discovered devices</h2><button type="button" data-close
|
<h2 data-i18n="discovery.nameDevices">Discovered local devices</h2><button type="button" data-close
|
||||||
data-ui-icon="close"></button>
|
data-ui-icon="close"></button>
|
||||||
</div>
|
</div>
|
||||||
<p class="field-note" data-i18n="discovery.nameDevicesHelp">Give each new unit a friendly room name. The technical
|
<p class="field-note" data-i18n="discovery.nameDevicesHelp">Select the units you want to add. MAC address, IP address and detected protocol are shown for every result.</p>
|
||||||
model and MAC remain available in diagnostics.</p>
|
|
||||||
<div id="discoveryNamesList" class="discovery-name-list"></div>
|
<div id="discoveryNamesList" class="discovery-name-list"></div>
|
||||||
<div class="form-actions"><button type="button" class="secondary" data-close
|
<div class="form-actions"><button type="button" class="secondary" data-close
|
||||||
data-i18n="actions.later">Later</button><button class="primary" type="submit"
|
data-i18n="actions.cancel">Cancel</button><button class="primary" type="submit"
|
||||||
data-i18n="actions.save">Save</button></div>
|
data-i18n="discovery.addSelected">Add selected</button></div>
|
||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ const app = {
|
|||||||
simulationScope: 'units', simulationTarget: 'all', standaloneSimulation: false, dashboardTab: 'main', settingsTab: 'app', systemSnapshotAt: Date.now(),
|
simulationScope: 'units', simulationTarget: 'all', standaloneSimulation: false, dashboardTab: 'main', settingsTab: 'app', systemSnapshotAt: Date.now(),
|
||||||
pingMonitor: { targetId: '', all: false, running: false, timer: null, inFlight: false, samples: {} },
|
pingMonitor: { targetId: '', all: false, running: false, timer: null, inFlight: false, samples: {} },
|
||||||
flowDraft: null, flowSelectedNodeId: null, flowSelectedNodeIds: [], flowConnectFrom: null, flowDirty: false, flowZoom: 1,
|
flowDraft: null, flowSelectedNodeId: null, flowSelectedNodeIds: [], flowConnectFrom: null, flowDirty: false, flowZoom: 1,
|
||||||
|
localDiscoveryCandidates: [],
|
||||||
};
|
};
|
||||||
try { app.savedCharts = JSON.parse(localStorage.getItem('gree_controller_saved_charts') || '[]'); } catch (_) { app.savedCharts = []; }
|
try { app.savedCharts = JSON.parse(localStorage.getItem('gree_controller_saved_charts') || '[]'); } catch (_) { app.savedCharts = []; }
|
||||||
|
|
||||||
|
|||||||
@@ -366,22 +366,39 @@ $('#tokenForm').addEventListener('submit', async event => {
|
|||||||
$('#discoverForm').addEventListener('submit', async event => {
|
$('#discoverForm').addEventListener('submit', async event => {
|
||||||
event.preventDefault(); const form = event.currentTarget, raw = Object.fromEntries(new FormData(form));
|
event.preventDefault(); const form = event.currentTarget, raw = Object.fromEntries(new FormData(form));
|
||||||
await runFormTask(form, async () => {
|
await runFormTask(form, async () => {
|
||||||
const result = await api('/api/discovery', { method: 'POST', body: { protocol_version: Number(raw.protocol_version), passes: Number(raw.passes), timeout_ms: Number(raw.timeout_ms) } });
|
const protocolVersion = Number(raw.protocol_version);
|
||||||
form.closest('dialog').close(); await loadBootstrap(); toast(tr('toast.found', { count: result.count })); showDiscoveryNames(result.new_device_ids || []);
|
const result = await api('/api/discovery/scan', { method: 'POST', body: { protocol_version: protocolVersion, passes: Number(raw.passes), timeout_ms: Number(raw.timeout_ms) } });
|
||||||
|
form.closest('dialog').close();
|
||||||
|
toast(tr('toast.found', { count: result.count }));
|
||||||
|
showDiscoveryNames(result.devices || []);
|
||||||
}, { busyKey: 'actions.discovering' });
|
}, { busyKey: 'actions.discovering' });
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#discoveryNamesForm').addEventListener('submit', async event => {
|
$('#discoveryNamesForm').addEventListener('submit', async event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const form = event.currentTarget;
|
const form = event.currentTarget;
|
||||||
const inputs = $$('input[data-device-id]', form);
|
const selected = $$('[data-discovery-select]:checked', form);
|
||||||
|
if (!selected.length) {
|
||||||
|
toast(tr('discovery.selectAtLeastOne'), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await runFormTask(form, async () => {
|
await runFormTask(form, async () => {
|
||||||
const updated = await Promise.all(inputs.map(input => api(`/api/devices/${encodeURIComponent(input.dataset.deviceId)}`, { method: 'PATCH', body: { name: input.value.trim() } })));
|
const devices = selected.map(input => {
|
||||||
updated.forEach(updateDevice);
|
const index = Number(input.dataset.discoveryIndex);
|
||||||
form.closest('dialog').close();
|
const candidate = app.localDiscoveryCandidates[index];
|
||||||
renderAll();
|
const nameInput = $(`[data-discovery-name="${index}"]`, form);
|
||||||
toast(tr('common.saved'));
|
return { ...candidate, name: String(nameInput?.value || candidate?.name || '').trim() };
|
||||||
|
}).filter(Boolean);
|
||||||
|
const result = await api('/api/discovery/add', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { devices },
|
||||||
});
|
});
|
||||||
|
await loadBootstrap();
|
||||||
|
form.closest('dialog').close();
|
||||||
|
app.localDiscoveryCandidates = [];
|
||||||
|
renderAll();
|
||||||
|
toast(tr('discovery.addedCount', { count: result.count }));
|
||||||
|
}, { validate: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#renameDeviceForm').addEventListener('submit', async event => {
|
$('#renameDeviceForm').addEventListener('submit', async event => {
|
||||||
|
|||||||
@@ -469,16 +469,27 @@ function beginInlineTemperatureEdit(target) {
|
|||||||
input.addEventListener('blur', () => finish(true), { once: true });
|
input.addEventListener('blur', () => finish(true), { once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function showDiscoveryNames(ids) {
|
function showDiscoveryNames(devices) {
|
||||||
const wanted = new Set(Array.isArray(ids) ? ids : []);
|
const items = Array.isArray(devices) ? devices : [];
|
||||||
const devices = app.devices.filter(device => wanted.has(device.id));
|
app.localDiscoveryCandidates = items.map(item => ({ ...item }));
|
||||||
if (!devices.length) return;
|
|
||||||
const list = $('#discoveryNamesList');
|
const list = $('#discoveryNamesList');
|
||||||
list.innerHTML = devices.map(device => `
|
if (!list) return;
|
||||||
<label class="discovery-name-row">
|
list.innerHTML = items.length ? items.map((device, index) => {
|
||||||
<span><strong>${esc(device.model || 'GREE')}</strong><small>${esc(device.ip)} · ${esc(device.mac)} · ${device.protocol_version === 2 ? 'V2 GCM' : 'V1 ECB'}</small></span>
|
const alreadyAdded = device.already_added === true;
|
||||||
<input data-device-id="${esc(device.id)}" maxlength="80" required value="${esc(device.name)}">
|
const protocol = Number(device.protocol_version) === 2 ? 'V2 AES-GCM' : 'V1 AES-ECB';
|
||||||
</label>`).join('');
|
const selectorId = `local-discovery-${index}`;
|
||||||
|
return `<div class="discovery-name-row local-discovery-row">
|
||||||
|
<div class="local-discovery-head">
|
||||||
|
<input type="checkbox" id="${esc(selectorId)}" data-discovery-select data-discovery-index="${index}" ${alreadyAdded ? 'disabled' : 'checked'}>
|
||||||
|
<label for="${esc(selectorId)}">
|
||||||
|
<strong>${esc(device.model || device.name || 'GREE')}</strong>
|
||||||
|
<small>IP ${esc(device.ip || '—')} · MAC ${esc(device.mac || '—')} · ${esc(protocol)}</small>
|
||||||
|
</label>
|
||||||
|
${alreadyAdded ? `<span class="badge">${esc(tr('discovery.alreadyAdded'))}</span>` : ''}
|
||||||
|
</div>
|
||||||
|
<input data-discovery-name="${index}" maxlength="80" value="${esc(device.name || device.model || 'GREE')}" ${alreadyAdded ? 'disabled' : ''} aria-label="${esc(tr('common.name'))}">
|
||||||
|
</div>`;
|
||||||
|
}).join('') : `<div class="empty"><strong>${esc(tr('discovery.empty'))}</strong><span>${esc(tr('discovery.emptyHint'))}</span></div>`;
|
||||||
openDialog('discoveryNamesDialog');
|
openDialog('discoveryNamesDialog');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user