v0.13.10
This commit is contained in:
Generated
+1
-1
@@ -700,7 +700,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gree-controller"
|
name = "gree-controller"
|
||||||
version = "0.13.9"
|
version = "0.13.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gree-controller"
|
name = "gree-controller"
|
||||||
version = "0.13.9"
|
version = "0.13.10"
|
||||||
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"
|
||||||
|
|||||||
+18
-18
@@ -1,16 +1,16 @@
|
|||||||
d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env.example
|
d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env.example
|
||||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||||
36a2cb85329ae82d097a335a6afd81c1ba7bc450eebdac09c600e12befe36ba2 ./Cargo.lock
|
4b2973e384f48aef569a66e2da448f6a82f33adbbc8d11dd33e7c20d8552536d ./Cargo.lock
|
||||||
5f9afa17638997837880ab244301e3921a6444972154b49797a73bc3103fbcad ./Cargo.toml
|
71acebe2a2ebb705089abf834eb7b2494daff9a4c93eae3f61ed6d4b0d21ef99 ./Cargo.toml
|
||||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||||
491aeceb202b240807d0d3cb72cdae622ee9ae8cbfd89439dc4add0bc648144f ./README.md
|
4cf04e8f22fb4e2551ee5fe2fe96fc50dda615b42a0274ba87aba60a59f021fc ./README.md
|
||||||
bb549919b96b0413152dd905aec8640bb53301f07ec7d0b1be48ccbd37936fef ./build.rs
|
bb549919b96b0413152dd905aec8640bb53301f07ec7d0b1be48ccbd37936fef ./build.rs
|
||||||
d590a6df7ec31c274a2437075d76305cfafbe56385c800fbf39b18b54875119e ./docs/API.md
|
abe109ed04cd90336e2e136fc77e9cfeefd6a9818a8c354ece08e96a5edc0730 ./docs/API.md
|
||||||
2e1e18fd8167dabfe2469c26e85cce62486c7cb6a502c63c6f6b0cd74d5885e0 ./docs/FLOW.md
|
2e1e18fd8167dabfe2469c26e85cce62486c7cb6a502c63c6f6b0cd74d5885e0 ./docs/FLOW.md
|
||||||
3e995a5bb3ec40b26c2676818f90adbd5fa42e760b487d18a16b09a586702484 ./docs/openapi.json
|
e3da335c88fe66baaeaf9b1f33cb69b13f47da7415bda39929d86f51174e9026 ./docs/openapi.json
|
||||||
fef9a676b9ce82d0539d44e456237a814169a17a158557d7b803a455985bc020 ./ha-addon/.env.example
|
c65aa543d2e99f8f7d52736307b07ebd182597d80124661d53a16bb23f6f1118 ./ha-addon/.env.example
|
||||||
a18d2460340264d34c267fa704ee51a42e59bcc1783ee4c0dbb2a8ef42fa0f16 ./ha-addon/Dockerfile
|
a18d2460340264d34c267fa704ee51a42e59bcc1783ee4c0dbb2a8ef42fa0f16 ./ha-addon/Dockerfile
|
||||||
9266b7885849007ac7626c0151b401da2fcb8e0a2721a6d196089dee1a67fbf3 ./ha-addon/README.md
|
0417c88a990c72aecf071fba64d5d9da65dcda4d396cd7565477f7a4532c8c5e ./ha-addon/README.md
|
||||||
cd7fd4b937a1d6138699f7effd184e3c077912b881e9c73c37b90bbafba68477 ./ha-addon/build.sh
|
cd7fd4b937a1d6138699f7effd184e3c077912b881e9c73c37b90bbafba68477 ./ha-addon/build.sh
|
||||||
fd041032f353ceb0a2b02005f5a78d74a460b12e4e78de13dcb60129859f8c4e ./ha-addon/home-assistant/README.md
|
fd041032f353ceb0a2b02005f5a78d74a460b12e4e78de13dcb60129859f8c4e ./ha-addon/home-assistant/README.md
|
||||||
f8e8559fe10fe523ac5bc9aac25c6e26e862f679d502e8f3c39f38a0a8e40911 ./ha-addon/home-assistant/custom_components/gree_controller/__init__.py
|
f8e8559fe10fe523ac5bc9aac25c6e26e862f679d502e8f3c39f38a0a8e40911 ./ha-addon/home-assistant/custom_components/gree_controller/__init__.py
|
||||||
@@ -21,7 +21,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./ha-addon/hom
|
|||||||
41a8958a6fe6d10e5679c95ac34832583a2a15ecfd67d09b4e094a83d06f5c47 ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py
|
41a8958a6fe6d10e5679c95ac34832583a2a15ecfd67d09b4e094a83d06f5c47 ./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
|
||||||
500f87cbfac7c933a5c22f974bf15ec8ee25ed1136eac7eeafe6a2918f8cd503 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
8fff53290ad57c49d124777083bbae9f9804e3af398c9d51d44ee29987e2d033 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
||||||
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
|
c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py
|
||||||
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
|
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py
|
||||||
cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.py
|
cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.py
|
||||||
@@ -29,10 +29,10 @@ cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/hom
|
|||||||
6bddb7b4620021ecd2099a86a77ef5c7f2c2dcd3d07d5db4e7b4c4ce6d3e8c03 ./ha-addon/home-assistant/custom_components/gree_controller/translations/en.json
|
6bddb7b4620021ecd2099a86a77ef5c7f2c2dcd3d07d5db4e7b4c4ce6d3e8c03 ./ha-addon/home-assistant/custom_components/gree_controller/translations/en.json
|
||||||
13f30e2dcdcedbd1b6c3f99c2335e0487108fd72c8e86922368b84f2fa2038ae ./ha-addon/home-assistant/custom_components/gree_controller/translations/pl.json
|
13f30e2dcdcedbd1b6c3f99c2335e0487108fd72c8e86922368b84f2fa2038ae ./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
|
||||||
fc6cad1fe53f1ad365cdf0a089ce4156460e6b07ba404b65b78aafd1a0a8ba01 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
7421c100b66152f3a513abd436afbae8ced4a91968ac89159431d7237f50d2c5 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||||
3c06bd11671452ea6472afd5543fb91a39aba73a1d2d95c7762c04f94ab64030 ./ha-addon/repository/gree-controller/DOCS.md
|
3c06bd11671452ea6472afd5543fb91a39aba73a1d2d95c7762c04f94ab64030 ./ha-addon/repository/gree-controller/DOCS.md
|
||||||
c3db302f38d26dc9f8caf37a1a8a07a1334cc07ae301d452f2940a5ec5915719 ./ha-addon/repository/gree-controller/README.md
|
c3db302f38d26dc9f8caf37a1a8a07a1334cc07ae301d452f2940a5ec5915719 ./ha-addon/repository/gree-controller/README.md
|
||||||
fdfb371926690e26a3693acd32d899e222ea66e4cb3476f717f55382e303e0d0 ./ha-addon/repository/gree-controller/config.yaml
|
3cfb0dfc0c41d91d533a889796645c3818eb8e74a8be53610da79c88c0e88efa ./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
|
||||||
d13e4c2b993d209078342c0c86d852b300692089b377aa044f811308dd188a1e ./ha-addon/repository/gree-controller/translations/en.yaml
|
d13e4c2b993d209078342c0c86d852b300692089b377aa044f811308dd188a1e ./ha-addon/repository/gree-controller/translations/en.yaml
|
||||||
@@ -84,7 +84,7 @@ fdcd9a5055d08037278b842e7ab69265345c5811f0a06867136c511d140bb191 ./presets/wind
|
|||||||
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
||||||
01952aa92b217f8eae2493b88870e2dec595100cd15c4d561ff11ae2b936c46f ./regenerate-sha.sh
|
01952aa92b217f8eae2493b88870e2dec595100cd15c4d561ff11ae2b936c46f ./regenerate-sha.sh
|
||||||
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
||||||
b48677a5d38684a524c6a1b0275bb6640fa1fb87e05ba29e92470f2d0aa89957 ./scripts/api_dev_test.py
|
9ca0b7780699d542d6fc6fa567dc5c3f847a57dcb676b02bb83b905862a4ff73 ./scripts/api_dev_test.py
|
||||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
||||||
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
||||||
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
||||||
@@ -103,7 +103,7 @@ b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/upda
|
|||||||
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
9e4fc675306e111ed3db7af9822e2925d88109c31b9e309851665a975c0bd83c ./src/api/automations.rs
|
||||||
368cd843dcac970f1aa6800ba132a4bbc3429e8379ed7abf9878dcb0c633af40 ./src/api/configuration.rs
|
368cd843dcac970f1aa6800ba132a4bbc3429e8379ed7abf9878dcb0c633af40 ./src/api/configuration.rs
|
||||||
19bc141286a336ad67e99079496eb0ae5126f986ab28be62b5f97758e5f6969c ./src/api/debug_tokens.rs
|
19bc141286a336ad67e99079496eb0ae5126f986ab28be62b5f97758e5f6969c ./src/api/debug_tokens.rs
|
||||||
a58fb7dcf3f37e3f9d90363623ec91edb667bca97f5faa6405c8b8f539c3d998 ./src/api/devices.rs
|
e219dafc951207cb70bc7808054a0553a03fb0a948d11af2eb5a119d1340e29e ./src/api/devices.rs
|
||||||
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs
|
||||||
44b74965f3256c2972d6b3cd384a63c68e3b422f414a0902c085bc92e17369bf ./src/api/flows.rs
|
44b74965f3256c2972d6b3cd384a63c68e3b422f414a0902c085bc92e17369bf ./src/api/flows.rs
|
||||||
b61b0b4f196c5e72e47cf06ca0c14df3cf8eedbdef0242a9d2af33623663489f ./src/api/groups.rs
|
b61b0b4f196c5e72e47cf06ca0c14df3cf8eedbdef0242a9d2af33623663489f ./src/api/groups.rs
|
||||||
@@ -159,7 +159,7 @@ da4068295f37c23222bb13bebac022d88ba7729604a49ad25438c55e45458a49 ./src/influxdb
|
|||||||
920e028d96a14cca9f74a964bb296e68a109cfead314d3164c180201e606d151 ./src/models/automation.rs
|
920e028d96a14cca9f74a964bb296e68a109cfead314d3164c180201e606d151 ./src/models/automation.rs
|
||||||
60cac63e6cb41d835e35cc654363f6ffd4386a79f562df0e656b69034acfbac2 ./src/models/control_plan.rs
|
60cac63e6cb41d835e35cc654363f6ffd4386a79f562df0e656b69034acfbac2 ./src/models/control_plan.rs
|
||||||
9cd1904a0f822c557f6ad4576cdb5f96d9668d04e79ea9783885c9f39519752e ./src/models/defaults.rs
|
9cd1904a0f822c557f6ad4576cdb5f96d9668d04e79ea9783885c9f39519752e ./src/models/defaults.rs
|
||||||
f935d35b4a409f12774209f3fa2ffe60f579a8bc1722badbb27d4766efd6368e ./src/models/device.rs
|
fa8ddca35171680dd9afb3bc46236e0a4922a9bbbf3e8e9939991973281534d3 ./src/models/device.rs
|
||||||
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
aeab5b5acf35d38a54fd1d82e964fe56635ade69c0149ad423df317095d01555 ./src/models/flow.rs
|
||||||
cf63af00d98410a1452f205ce00b8a6c5f0f3525b994cba3e971d631db288396 ./src/models/history.rs
|
cf63af00d98410a1452f205ce00b8a6c5f0f3525b994cba3e971d631db288396 ./src/models/history.rs
|
||||||
e1621255217ee6128151cc898373dd404123cb6d54017beec6c7e3e5c3030486 ./src/models/integrations.rs
|
e1621255217ee6128151cc898373dd404123cb6d54017beec6c7e3e5c3030486 ./src/models/integrations.rs
|
||||||
@@ -171,13 +171,13 @@ c4de6101de85d9d7e6814b2504b87d9216561e3d9e274054f081f505fb152140 ./src/models/t
|
|||||||
cca742bb2b500eb9269f69385fcf88053cd08aabf6a98b7669b8a7a2fd67df13 ./src/protocol/crypto.rs
|
cca742bb2b500eb9269f69385fcf88053cd08aabf6a98b7669b8a7a2fd67df13 ./src/protocol/crypto.rs
|
||||||
28969da06a454ddd1ebea5fa606e8446a83dfd21e21352767c189013c508cb90 ./src/protocol/gree.rs
|
28969da06a454ddd1ebea5fa606e8446a83dfd21e21352767c189013c508cb90 ./src/protocol/gree.rs
|
||||||
7907c33e6396c7e0d61705b735b21c84d6ed784012fb61022b872c6745540f1d ./src/protocol/gree/binding.rs
|
7907c33e6396c7e0d61705b735b21c84d6ed784012fb61022b872c6745540f1d ./src/protocol/gree/binding.rs
|
||||||
d4df52f0202eb32290e68dc945c4b0b44f3be8aa90bd57fd116eb20285960616 ./src/protocol/gree/commands.rs
|
d71771855d5ac90fae4994debf53ed09472b216c17bb2155d80aa2de13c57e18 ./src/protocol/gree/commands.rs
|
||||||
4e419ba3b86ca9d154e8c04445cf005965175c54041093b2daad3666d4d852bd ./src/protocol/gree/core.rs
|
4e419ba3b86ca9d154e8c04445cf005965175c54041093b2daad3666d4d852bd ./src/protocol/gree/core.rs
|
||||||
322bcb70016653f8bf71cc620020aa87d4f3f56b4d800ee9425a95b1bcc67d2e ./src/protocol/gree/discovery.rs
|
87d8d9686a416413489d34cb4fc2e5ac4c05f7e1e7e5bc362e9351c06c89fa51 ./src/protocol/gree/discovery.rs
|
||||||
0ab094f609edb99715402d8eeaa7e38cbfd2d271036aec787dcbb3be3e005720 ./src/protocol/gree/merge.rs
|
0ab094f609edb99715402d8eeaa7e38cbfd2d271036aec787dcbb3be3e005720 ./src/protocol/gree/merge.rs
|
||||||
6c18d870f98525e87ba821d69424e6b19a22b2c06106b7fd657f30912cd2705f ./src/protocol/gree/network.rs
|
c09c931fa8d512a8883fb14f3142be694af3c51290e023d25c7c972e156e4bd2 ./src/protocol/gree/network.rs
|
||||||
de37931303fc92a9399e6edc17dfabd6870a38cd2ddb27257427c76768a66ec2 ./src/protocol/gree/polling.rs
|
0a901ff258a74072af3812c5417196da1a9b8daa8a2eff30c96d038558601739 ./src/protocol/gree/polling.rs
|
||||||
c868422c02ba457df21ae92acbd00698c243855ddd1d4e95ac2166796eba9a44 ./src/protocol/gree/tests.rs
|
50152bcad4a843ae04000b991a2365abb302fc1583ea69f09320e25740f3c38c ./src/protocol/gree/tests.rs
|
||||||
3c93e5b254a2a40a07854532cb5fc07e493ad65d9ba2cf28093cff4a4a4e451c ./src/protocol/gree/transport.rs
|
3c93e5b254a2a40a07854532cb5fc07e493ad65d9ba2cf28093cff4a4a4e451c ./src/protocol/gree/transport.rs
|
||||||
923bb4b2d342d6c1870474e43f33b0aba16dc88590c561d765b874412e83df1f ./src/protocol/mod.rs
|
923bb4b2d342d6c1870474e43f33b0aba16dc88590c561d765b874412e83df1f ./src/protocol/mod.rs
|
||||||
830621fa75265da7288b8135704c0ce27e0687f87d55abf3cdbe40a6e2f3d2b1 ./src/queries.rs
|
830621fa75265da7288b8135704c0ce27e0687f87d55abf3cdbe40a6e2f3d2b1 ./src/queries.rs
|
||||||
|
|||||||
@@ -2,7 +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.
|
||||||
|
|
||||||
**Current release: 0.13.9**
|
**Current release: 0.13.10**
|
||||||
|
|
||||||
|
Release 0.13.10 improves compatibility with GREE units that report optional feature fields as multi-state values instead of strict 0/1 flags. In particular, `Quiet=2` or `Quiet=3` no longer invalidates the whole status response, and the controller remembers the active Quiet encoding reported by the unit while keeping `Quiet=1` as the legacy default. Existing AES-ECB/AES-GCM behavior and API contracts remain unchanged.
|
||||||
|
|
||||||
Release 0.13.9 fixes dashboard thermostat card sizing when local thermostat or manual device control is active. Cards now keep their intrinsic height instead of stretching every thermostat in the same grid row; API contracts are unchanged.
|
Release 0.13.9 fixes dashboard thermostat card sizing when local thermostat or manual device control is active. Cards now keep their intrinsic height instead of stretching every thermostat in the same grid row; API contracts are unchanged.
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
# GREE Controller API reference
|
# GREE Controller API reference
|
||||||
|
|
||||||
HTTP and WebSocket API for GREE Controller **0.13.9**.
|
HTTP and WebSocket API for GREE Controller **0.13.10**.
|
||||||
|
|
||||||
[← Main documentation](../README.md)
|
[← Main documentation](../README.md)
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ Response:
|
|||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"name": "gree-controller",
|
"name": "gree-controller",
|
||||||
"version": "0.13.9",
|
"version": "0.13.10",
|
||||||
"uptime_seconds": 1234,
|
"uptime_seconds": 1234,
|
||||||
"control_ready": true,
|
"control_ready": true,
|
||||||
"time": "2026-08-30T06:54:00Z"
|
"time": "2026-08-30T06:54:00Z"
|
||||||
@@ -295,7 +295,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.13.9",
|
"version": "0.13.10",
|
||||||
"uptime_seconds": 1234,
|
"uptime_seconds": 1234,
|
||||||
"auth_required": false,
|
"auth_required": false,
|
||||||
"control_ready": true,
|
"control_ready": true,
|
||||||
|
|||||||
+3
-3
@@ -2,9 +2,9 @@
|
|||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "GREE Controller API",
|
"title": "GREE Controller API",
|
||||||
"version": "0.13.9",
|
"version": "0.13.10",
|
||||||
"summary": "Local HTTP/WebSocket API for GREE HVAC control",
|
"summary": "Local HTTP/WebSocket API for GREE HVAC control",
|
||||||
"description": "Self-hosted API used by the GREE Controller Web UI and Home Assistant integration.\n\n### Authentication\nNormal administrator endpoints accept `Authorization: Bearer <APP_TOKEN>` or `x-api-token: <APP_TOKEN>` when `GREE_CONTROLLER_APP_TOKEN` is configured. If the app token is empty, those endpoints intentionally run in trusted-LAN mode without authentication.\n\nThe restricted `/api/integrations/home-assistant/*` control surface always requires either a generated access token or the administrator app token. Generated tokens do **not** grant normal administrator API access.\n\nIn Swagger UI use **Authorize** and enter the token in either the bearer field or `x-api-token` field.\n\n### Control ownership\nThermostat zones, groups, house rules, direct device control, schedules and visual Flows share an ownership/arbitration model. Prefer zone/group/house endpoints for thermostat intent; direct device commands are technical/manual control and can deliberately take ownership.\n\n### 0.12.0 breaking API\nRuntime settings are split into functional `/api/settings/*` resources. The former `/api/settings`, `/api/debug`, `/api/events/retention`, `/api/settings/export` and `/api/settings/import` endpoints were removed without compatibility aliases.\n\n### 0.13.0\n`control-plan` keeps its existing HTTP response shape but is materialized in memory and pushed to WebSocket clients as `control_plan.updated`; HTTP remains the fallback/resync path. A restricted Home Assistant snapshot endpoint combines devices, groups and the control plan.\n\n### 0.13.1\nRuntime WebSocket churn is reduced without changing contracts: no-op zone regulator heartbeats are deduplicated and device heartbeat polls only invalidate the materialized control plan when plan-relevant device state changes.\n\n### 0.13.2\nCompletes live WebSocket synchronization for entity creation, schedules, automations, configuration imports and runtime device/automation state while preserving the existing HTTP API contracts and control-plan fallback behavior.\n\n### 0.13.3\nLogical thermostat targets are normalized to 0.1 C consistently, including Temporary Quick Thermostat and thermostat-managed automation/Flow actions. The bundled Web UI accepts comma or dot decimal input for the temporary thermostat target; physical GREE setpoint rounding is unchanged.\n\n### 0.13.4\nDisabled-zone thermostat controls remain visually active in the bundled Web UI because explicit temporary/manual thermostat and direct manual control remain available while zone automation is disabled. HTTP and WebSocket contracts are unchanged.\n\n### 0.13.5\nThe zone settings dialog now explains directly beside the enabled/disabled option that disabling a zone stops automation but does not block manual thermostat, temporary thermostat or direct manual control. HTTP and WebSocket contracts remain unchanged.\n\n### 0.13.6\nNotification settings add a dedicated sensor_discrepancy alert type for GREE vs Home Assistant room-temperature divergence. Disabling it suppresses only that notification; the GREE safety fallback remains active.\n\n### 0.13.7\nThe bootstrap snapshot now embeds all eight redacted runtime settings views. The bundled Web UI consumes those settings directly for HTTP startup and WebSocket resynchronization, removing the previous settings-request fan-out while preserving the split settings endpoints for direct reads and writes.\n\n### 0.13.8\nThe bundled Web UI suppresses the initial English-to-selected-language flash with a dedicated pre-paint language initializer and reveals the application only after localization is initialized. Language-pack URLs are content-versioned so immutable browser caching stays safe across updates.\n\n### 0.13.9\nThe bundled Web UI keeps dashboard thermostat cards at their intrinsic height when a local thermostat or manual device-control takeover panel is shown, preventing unrelated thermostat cards in the same grid row from stretching. HTTP and WebSocket contracts are unchanged.",
|
"description": "Self-hosted API used by the GREE Controller Web UI and Home Assistant integration.\n\n### Authentication\nNormal administrator endpoints accept `Authorization: Bearer <APP_TOKEN>` or `x-api-token: <APP_TOKEN>` when `GREE_CONTROLLER_APP_TOKEN` is configured. If the app token is empty, those endpoints intentionally run in trusted-LAN mode without authentication.\n\nThe restricted `/api/integrations/home-assistant/*` control surface always requires either a generated access token or the administrator app token. Generated tokens do **not** grant normal administrator API access.\n\nIn Swagger UI use **Authorize** and enter the token in either the bearer field or `x-api-token` field.\n\n### Control ownership\nThermostat zones, groups, house rules, direct device control, schedules and visual Flows share an ownership/arbitration model. Prefer zone/group/house endpoints for thermostat intent; direct device commands are technical/manual control and can deliberately take ownership.\n\n### 0.12.0 breaking API\nRuntime settings are split into functional `/api/settings/*` resources. The former `/api/settings`, `/api/debug`, `/api/events/retention`, `/api/settings/export` and `/api/settings/import` endpoints were removed without compatibility aliases.\n\n### 0.13.0\n`control-plan` keeps its existing HTTP response shape but is materialized in memory and pushed to WebSocket clients as `control_plan.updated`; HTTP remains the fallback/resync path. A restricted Home Assistant snapshot endpoint combines devices, groups and the control plan.\n\n### 0.13.1\nRuntime WebSocket churn is reduced without changing contracts: no-op zone regulator heartbeats are deduplicated and device heartbeat polls only invalidate the materialized control plan when plan-relevant device state changes.\n\n### 0.13.2\nCompletes live WebSocket synchronization for entity creation, schedules, automations, configuration imports and runtime device/automation state while preserving the existing HTTP API contracts and control-plan fallback behavior.\n\n### 0.13.3\nLogical thermostat targets are normalized to 0.1 C consistently, including Temporary Quick Thermostat and thermostat-managed automation/Flow actions. The bundled Web UI accepts comma or dot decimal input for the temporary thermostat target; physical GREE setpoint rounding is unchanged.\n\n### 0.13.4\nDisabled-zone thermostat controls remain visually active in the bundled Web UI because explicit temporary/manual thermostat and direct manual control remain available while zone automation is disabled. HTTP and WebSocket contracts are unchanged.\n\n### 0.13.5\nThe zone settings dialog now explains directly beside the enabled/disabled option that disabling a zone stops automation but does not block manual thermostat, temporary thermostat or direct manual control. HTTP and WebSocket contracts remain unchanged.\n\n### 0.13.6\nNotification settings add a dedicated sensor_discrepancy alert type for GREE vs Home Assistant room-temperature divergence. Disabling it suppresses only that notification; the GREE safety fallback remains active.\n\n### 0.13.7\nThe bootstrap snapshot now embeds all eight redacted runtime settings views. The bundled Web UI consumes those settings directly for HTTP startup and WebSocket resynchronization, removing the previous settings-request fan-out while preserving the split settings endpoints for direct reads and writes.\n\n### 0.13.8\nThe bundled Web UI suppresses the initial English-to-selected-language flash with a dedicated pre-paint language initializer and reveals the application only after localization is initialized. Language-pack URLs are content-versioned so immutable browser caching stays safe across updates.\n\n### 0.13.9\nThe bundled Web UI keeps dashboard thermostat cards at their intrinsic height when a local thermostat or manual device-control takeover panel is shown, preventing unrelated thermostat cards in the same grid row from stretching. HTTP and WebSocket contracts are unchanged.\n\n### 0.13.10\nGREE status parsing now tolerates multi-state non-zero encodings for optional feature flags. `Quiet` preserves active wire values 1/2/3 reported by the unit and reuses the observed encoding when enabled, while legacy behavior still defaults to `Quiet=1`. Existing API contracts are unchanged.",
|
||||||
"license": {
|
"license": {
|
||||||
"name": "MIT"
|
"name": "MIT"
|
||||||
}
|
}
|
||||||
@@ -4891,7 +4891,7 @@
|
|||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "0.13.9"
|
"example": "0.13.10"
|
||||||
},
|
},
|
||||||
"uptime_seconds": {
|
"uptime_seconds": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Developer smoke/integration tests for GREE Controller API 0.13.9.
|
"""Developer smoke/integration tests for GREE Controller API 0.13.10.
|
||||||
|
|
||||||
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.13.9"
|
DEFAULT_EXPECTED_VERSION = "0.13.10"
|
||||||
|
|
||||||
SETTINGS_PATHS: dict[str, set[str]] = {
|
SETTINGS_PATHS: dict[str, set[str]] = {
|
||||||
"/api/settings/application": {"simulator_enabled"},
|
"/api/settings/application": {"simulator_enabled"},
|
||||||
@@ -553,7 +553,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.13.9 contract", lambda: test_openapi(client, args.expected_version))
|
runner.run("OpenAPI 0.13.10 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))
|
||||||
|
|
||||||
@@ -622,7 +622,7 @@ def run_suite(args: argparse.Namespace) -> int:
|
|||||||
|
|
||||||
def parse_args(argv: Optional[Iterable[str]] = None) -> argparse.Namespace:
|
def parse_args(argv: Optional[Iterable[str]] = None) -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Developer smoke/integration tests for GREE Controller API 0.13.9.",
|
description="Developer smoke/integration tests for GREE Controller API 0.13.10.",
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
epilog="""Examples:
|
epilog="""Examples:
|
||||||
python3 scripts/api_dev_test.py
|
python3 scripts/api_dev_test.py
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ async fn add_device(
|
|||||||
swing_vertical: false,
|
swing_vertical: false,
|
||||||
swing_horizontal: false,
|
swing_horizontal: false,
|
||||||
quiet: false,
|
quiet: false,
|
||||||
|
quiet_wire_value: None,
|
||||||
turbo: false,
|
turbo: false,
|
||||||
light: true,
|
light: true,
|
||||||
air: false,
|
air: false,
|
||||||
|
|||||||
@@ -34,6 +34,14 @@ pub struct Device {
|
|||||||
pub swing_horizontal: bool,
|
pub swing_horizontal: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub quiet: bool,
|
pub quiet: bool,
|
||||||
|
/// Raw non-zero value last reported by the unit for the GREE `Quiet` property.
|
||||||
|
///
|
||||||
|
/// Most of the application intentionally exposes Quiet as a boolean, but the wire
|
||||||
|
/// protocol is not consistently boolean across GREE firmware. Known units report
|
||||||
|
/// values 1, 2 or 3 for active Quiet modes. Remembering the observed value lets us
|
||||||
|
/// preserve the device-specific encoding when a later command enables Quiet again.
|
||||||
|
#[serde(default)]
|
||||||
|
pub quiet_wire_value: Option<u8>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub turbo: bool,
|
pub turbo: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -106,6 +114,7 @@ impl Device {
|
|||||||
swing_vertical: false,
|
swing_vertical: false,
|
||||||
swing_horizontal: false,
|
swing_horizontal: false,
|
||||||
quiet: false,
|
quiet: false,
|
||||||
|
quiet_wire_value: None,
|
||||||
turbo: false,
|
turbo: false,
|
||||||
light: true,
|
light: true,
|
||||||
air: false,
|
air: false,
|
||||||
|
|||||||
@@ -20,13 +20,17 @@ impl GreeClient {
|
|||||||
command: &DeviceCommand,
|
command: &DeviceCommand,
|
||||||
suppress_beep: bool,
|
suppress_beep: bool,
|
||||||
) -> Result<Value> {
|
) -> Result<Value> {
|
||||||
|
// Keep legacy behaviour (Quiet=1) until a device proves that it uses another
|
||||||
|
// active encoding. Once polling observes Quiet=2/3, persist and reuse that value.
|
||||||
|
// This makes newer/multi-state units work without changing working older units.
|
||||||
|
let quiet_on_value = device.quiet_wire_value.unwrap_or(1).clamp(1, 3);
|
||||||
let try_buzzer_suppression = suppress_beep
|
let try_buzzer_suppression = suppress_beep
|
||||||
&& self
|
&& self
|
||||||
.buzzer_unsupported
|
.buzzer_unsupported
|
||||||
.lock()
|
.lock()
|
||||||
.map(|items| !items.contains(&device.id))
|
.map(|items| !items.contains(&device.id))
|
||||||
.unwrap_or(true);
|
.unwrap_or(true);
|
||||||
let inner = Self::command_payload(command, try_buzzer_suppression)?;
|
let inner = Self::command_payload(command, try_buzzer_suppression, quiet_on_value)?;
|
||||||
match self
|
match self
|
||||||
.request(device, &inner, key, false, device.protocol_version)
|
.request(device, &inner, key, false, device.protocol_version)
|
||||||
.await
|
.await
|
||||||
@@ -35,7 +39,7 @@ impl GreeClient {
|
|||||||
Err(first_err) if try_buzzer_suppression => {
|
Err(first_err) if try_buzzer_suppression => {
|
||||||
// Some firmwares reject unknown buzzer properties instead of ignoring them.
|
// Some firmwares reject unknown buzzer properties instead of ignoring them.
|
||||||
// Retry the exact state change without buzzer fields and remember the fallback.
|
// Retry the exact state change without buzzer fields and remember the fallback.
|
||||||
let fallback = Self::command_payload(command, false)?;
|
let fallback = Self::command_payload(command, false, quiet_on_value)?;
|
||||||
match self
|
match self
|
||||||
.request(device, &fallback, key, false, device.protocol_version)
|
.request(device, &fallback, key, false, device.protocol_version)
|
||||||
.await
|
.await
|
||||||
@@ -159,7 +163,11 @@ impl GreeClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_payload(command: &DeviceCommand, suppress_beep: bool) -> Result<Value> {
|
fn command_payload(
|
||||||
|
command: &DeviceCommand,
|
||||||
|
suppress_beep: bool,
|
||||||
|
quiet_on_value: u8,
|
||||||
|
) -> Result<Value> {
|
||||||
let mut opt = Vec::<&str>::new();
|
let mut opt = Vec::<&str>::new();
|
||||||
let mut values = Vec::<Value>::new();
|
let mut values = Vec::<Value>::new();
|
||||||
if let Some(v) = command.power {
|
if let Some(v) = command.power {
|
||||||
@@ -191,7 +199,11 @@ impl GreeClient {
|
|||||||
}
|
}
|
||||||
if let Some(v) = command.quiet {
|
if let Some(v) = command.quiet {
|
||||||
opt.push("Quiet");
|
opt.push("Quiet");
|
||||||
values.push(json!(if v { 1 } else { 0 }));
|
values.push(json!(if v {
|
||||||
|
quiet_on_value.clamp(1, 3)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
if let Some(v) = command.turbo {
|
if let Some(v) = command.turbo {
|
||||||
opt.push("Tur");
|
opt.push("Tur");
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ impl GreeClient {
|
|||||||
swing_vertical: false,
|
swing_vertical: false,
|
||||||
swing_horizontal: false,
|
swing_horizontal: false,
|
||||||
quiet: false,
|
quiet: false,
|
||||||
|
quiet_wire_value: None,
|
||||||
turbo: false,
|
turbo: false,
|
||||||
light: true,
|
light: true,
|
||||||
air: false,
|
air: false,
|
||||||
|
|||||||
@@ -108,6 +108,19 @@ fn status_flag(name: &str, value: &Value) -> Result<bool> {
|
|||||||
other => bail!("invalid GREE flag value for {name}: {other}"),
|
other => bail!("invalid GREE flag value for {name}: {other}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Optional GREE capabilities are not uniformly encoded as strict 0/1 flags.
|
||||||
|
/// For example Quiet is known to use 1, 2 and 3 on different units/firmware,
|
||||||
|
/// and other optional properties can also expose multi-state non-zero values.
|
||||||
|
/// The public application model is boolean, so keep 0 = off and treat any
|
||||||
|
/// positive value as active without allowing one optional field to invalidate
|
||||||
|
/// the complete climate status frame.
|
||||||
|
fn status_feature_flag(name: &str, value: &Value) -> Result<bool> {
|
||||||
|
let raw = status_i64(name, value)?;
|
||||||
|
if raw < 0 {
|
||||||
|
bail!("invalid GREE feature value for {name}: {raw}")
|
||||||
|
}
|
||||||
|
Ok(raw != 0)
|
||||||
|
}
|
||||||
fn mode_name_checked(value: i64) -> Option<&'static str> { match value { 0 => Some("auto"), 1 => Some("cool"), 2 => Some("dry"), 3 => Some("fan"), 4 => Some("heat"), _ => None } }
|
fn mode_name_checked(value: i64) -> Option<&'static str> { match value { 0 => Some("auto"), 1 => Some("cool"), 2 => Some("dry"), 3 => Some("fan"), 4 => Some("heat"), _ => None } }
|
||||||
fn mode_value(value: &str) -> Result<i64> {
|
fn mode_value(value: &str) -> Result<i64> {
|
||||||
match value.to_ascii_lowercase().as_str() {
|
match value.to_ascii_lowercase().as_str() {
|
||||||
|
|||||||
@@ -196,31 +196,41 @@ impl GreeClient {
|
|||||||
"SwUpDn" => next.swing_vertical = status_i64(name, value)? != 0,
|
"SwUpDn" => next.swing_vertical = status_i64(name, value)? != 0,
|
||||||
"SwingLfRig" => next.swing_horizontal = status_i64(name, value)? != 0,
|
"SwingLfRig" => next.swing_horizontal = status_i64(name, value)? != 0,
|
||||||
"Quiet" => {
|
"Quiet" => {
|
||||||
next.quiet = status_flag(name, value)?;
|
let raw = status_i64(name, value)?;
|
||||||
|
if raw < 0 {
|
||||||
|
bail!("invalid GREE feature value for {name}: {raw}")
|
||||||
|
}
|
||||||
|
next.quiet = raw != 0;
|
||||||
|
// Quiet is a multi-state field on some GREE families. Preserve known
|
||||||
|
// active encodings so a later boolean "enable Quiet" command does not
|
||||||
|
// silently change mode (e.g. a unit that reports/uses Quiet=2).
|
||||||
|
if (1..=3).contains(&raw) {
|
||||||
|
next.quiet_wire_value = Some(raw as u8);
|
||||||
|
}
|
||||||
next.supports_quiet = Some(true);
|
next.supports_quiet = Some(true);
|
||||||
}
|
}
|
||||||
"Tur" => {
|
"Tur" => {
|
||||||
next.turbo = status_flag(name, value)?;
|
next.turbo = status_feature_flag(name, value)?;
|
||||||
next.supports_turbo = Some(true);
|
next.supports_turbo = Some(true);
|
||||||
}
|
}
|
||||||
"Lig" => {
|
"Lig" => {
|
||||||
next.light = status_flag(name, value)?;
|
next.light = status_feature_flag(name, value)?;
|
||||||
next.supports_light = Some(true);
|
next.supports_light = Some(true);
|
||||||
}
|
}
|
||||||
"Air" => {
|
"Air" => {
|
||||||
next.air = status_flag(name, value)?;
|
next.air = status_feature_flag(name, value)?;
|
||||||
next.supports_air = Some(true);
|
next.supports_air = Some(true);
|
||||||
}
|
}
|
||||||
"Blo" => {
|
"Blo" => {
|
||||||
next.xfan = status_flag(name, value)?;
|
next.xfan = status_feature_flag(name, value)?;
|
||||||
next.supports_xfan = Some(true);
|
next.supports_xfan = Some(true);
|
||||||
}
|
}
|
||||||
"Health" => {
|
"Health" => {
|
||||||
next.health = status_flag(name, value)?;
|
next.health = status_feature_flag(name, value)?;
|
||||||
next.supports_health = Some(true);
|
next.supports_health = Some(true);
|
||||||
}
|
}
|
||||||
"SwhSlp" => {
|
"SwhSlp" => {
|
||||||
next.sleep = status_flag(name, value)?;
|
next.sleep = status_feature_flag(name, value)?;
|
||||||
next.supports_sleep = Some(true);
|
next.supports_sleep = Some(true);
|
||||||
}
|
}
|
||||||
"TemSen" => {
|
"TemSen" => {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ mod tests {
|
|||||||
..DeviceCommand::default()
|
..DeviceCommand::default()
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
1,
|
||||||
)
|
)
|
||||||
.expect("thermostat command payload");
|
.expect("thermostat command payload");
|
||||||
|
|
||||||
@@ -49,4 +50,65 @@ mod tests {
|
|||||||
Some(serde_json::json!([19, 1, 1, 1]))
|
Some(serde_json::json!([19, 1, 1, 1]))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quiet_status_accepts_multistate_value_and_remembers_wire_mode() {
|
||||||
|
let client = GreeClient::new(
|
||||||
|
"test-controller".into(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
);
|
||||||
|
let mut device = Device::simulated_default();
|
||||||
|
device.quiet = false;
|
||||||
|
device.quiet_wire_value = None;
|
||||||
|
|
||||||
|
let response = json!({
|
||||||
|
"cols": ["Pow", "Quiet"],
|
||||||
|
"dat": [1, 2]
|
||||||
|
});
|
||||||
|
client.apply_status(&mut device, &response).expect("Quiet=2 status");
|
||||||
|
|
||||||
|
assert!(device.power);
|
||||||
|
assert!(device.quiet);
|
||||||
|
assert_eq!(device.quiet_wire_value, Some(2));
|
||||||
|
assert_eq!(device.supports_quiet, Some(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quiet_command_can_preserve_learned_wire_mode() {
|
||||||
|
let payload = GreeClient::command_payload(
|
||||||
|
&DeviceCommand {
|
||||||
|
quiet: Some(true),
|
||||||
|
..DeviceCommand::default()
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.expect("quiet command payload");
|
||||||
|
|
||||||
|
assert_eq!(payload.get("opt").cloned(), Some(serde_json::json!(["Quiet"])));
|
||||||
|
assert_eq!(payload.get("p").cloned(), Some(serde_json::json!([2])));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn optional_multistate_feature_does_not_reject_whole_status() {
|
||||||
|
let client = GreeClient::new(
|
||||||
|
"test-controller".into(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
);
|
||||||
|
let mut device = Device::simulated_default();
|
||||||
|
|
||||||
|
let response = json!({
|
||||||
|
"cols": ["Pow", "Air"],
|
||||||
|
"dat": [1, 3]
|
||||||
|
});
|
||||||
|
client.apply_status(&mut device, &response).expect("Air=3 status");
|
||||||
|
|
||||||
|
assert!(device.power);
|
||||||
|
assert!(device.air);
|
||||||
|
assert_eq!(device.supports_air, Some(true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user