diff --git a/Cargo.lock b/Cargo.lock index 1aa2d6f..87e5e00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -700,7 +700,7 @@ dependencies = [ [[package]] name = "gree-controller" -version = "0.15.1" +version = "0.15.2" dependencies = [ "aes", "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index b19be73..f554c90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gree-controller" -version = "0.15.1" +version = "0.15.2" edition = "2021" authors = ["GREE Controller contributors"] description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support" diff --git a/FILE_MANIFEST.sha256 b/FILE_MANIFEST.sha256 index 0778768..4063ca2 100644 --- a/FILE_MANIFEST.sha256 +++ b/FILE_MANIFEST.sha256 @@ -1,14 +1,14 @@ de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore -852768ed3da99ac2e9bbe6c9b67e9a4a667e7115ad9482b6ce4a29ff72d8f914 ./Cargo.lock -8e58dd01d95c93ea4f5d0cc5e379cb192221705c9dda41b7b2b7182c49dbff0f ./Cargo.toml +82bb41f94fdf5c66ebd9e6caa92e0f1794431067298e0a27c95c8e5bfd68de89 ./Cargo.lock +39bdb16196253559e3487aa912ff770c09c7b08cdfbb21e9db1d8af1304ea7e0 ./Cargo.toml 19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE -4a6570c14ba56fc9a4921de6726816264e961a5b82a7fe3fcf475ef96417dad9 ./README.md -283ffafdffd463f5d670acaba251fb92fab7c88e51a6fc1e06732f6cade88b5e ./SOURCE_MANIFEST.sha256 +84a0803bc75dc3199e23ef56c11c6950e8bd888c686ecda7cc369829e8e3073f ./README.md +f21732756af313565bfe0788230e45cc11c384cee1187f006d426c5463183c0c ./SOURCE_MANIFEST.sha256 29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs -9873e774746783d65e447b439d069f84c13d2f4e556661e4eb509af59293c2f7 ./docs/API.md -58b79297607303151c1de2da74344cd4f6277bc53d80ebe04ed4567d906c963b ./docs/FLOW.md -9483ad7cb1e0c8e38f08c722600b6ba059d4273cfb9dcd1af57c11de522d187b ./docs/openapi.json +604a795dec077dd7e4f3462a564fdda68e8084aef07da0fcf3dfb6053a46cc58 ./docs/API.md +a881ba0808dd3f189d92038f6163c39d6b6208f6dbff5c32c0ea1a2628f3fbb4 ./docs/FLOW.md +8babaa6830fa70b2d3d8208ef51808e79c9d68c4e007b9ba1537fe582ba03b3d ./docs/openapi.json c65aa543d2e99f8f7d52736307b07ebd182597d80124661d53a16bb23f6f1118 ./ha-addon/.env.example 0993076ca45b6d31859680835204cdc111b3f9bdbf460d50b4efec56cd5e685b ./ha-addon/Dockerfile adf7ff248d782a3b1cba82b8789c57b6849ddaaaf5fbc108af75e18b1534a885 ./ha-addon/README.md @@ -22,7 +22,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./ha-addon/hom 6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py 5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png -a6d69fad7ea1f22436e9f643ceb36c206f1450d8337aede0c5d7a1f0613baeeb ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json +d467809d894b8cb9e8e04a2cc279e0cec3227c1d1c689098bdc3c0380aa9043e ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py 39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.py @@ -30,10 +30,10 @@ cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/hom a745cc458c235158d5d70b6a33d8000fd8b40ff93bfe2f2796af0f4b3a9eef9b ./ha-addon/home-assistant/custom_components/gree_controller/translations/en.json 50b7d6fe6329ad4ea6bee9612ab141c2f73f8382fcc2af0bb076a8e8d4dd4465 ./ha-addon/home-assistant/custom_components/gree_controller/translations/pl.json c5fc5c87273d82d2834a0a5a365c821a416413ed509613441e24e4dc3507c4d2 ./ha-addon/home-assistant/generated/gree_controller_entities.example.json -ea5c4eb4ca2656f8d9a2be37a35ba04bb4c53d735c9c04b416b112e456699cff ./ha-addon/repository/gree-controller/CHANGELOG.md +e3a227ffe23368b152d0ba4a4e81a95b70d35e9870b2a8dc7b2048d6901a7f05 ./ha-addon/repository/gree-controller/CHANGELOG.md 1b5599195329cc3e643c23aa1caadcec80f133e8e515eec7d92b9f3cbda5b80b ./ha-addon/repository/gree-controller/DOCS.md c3db302f38d26dc9f8caf37a1a8a07a1334cc07ae301d452f2940a5ec5915719 ./ha-addon/repository/gree-controller/README.md -c16d31ae815dc4e324ce56b0b46529bcc78c5223688c33aa30d7463abe4f1184 ./ha-addon/repository/gree-controller/config.yaml +21b82216d0d84cc66ecbed69457caf61174fa9304528fe051c98e8ef7c634025 ./ha-addon/repository/gree-controller/config.yaml 3aae6cdae4c3aaab7786a9575e7093b7e2c5b69288167075f4b7475611691a6b ./ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png 0d0b42a7639b128946eec379175436fa1f150294f39d2dfd169077bfa87fd14d ./ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png 2272740b185e9520e948f74b8f45cf98b8b5fd28c93bdee760da626f91fe5c2a ./ha-addon/repository/gree-controller/translations/en.yaml @@ -43,8 +43,8 @@ cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run 081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md 253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md -f7fc1aee1129cee46a30d5876e3e53f11d2661925eae19cb1e14b47deae83e9e ./lang/en.json -c05d100e9efa1a82747ffc709157a9bdd6d109b7a80c6e140d582a28d8c3de13 ./lang/pl.json +e5c6a6051381e28d74b75e48232de13d02eb87519d29c847b7ab7979ce47d082 ./lang/en.json +02c650f603bca1bd17d9d59193c176ee6ddccb8304f2e658d7ba7730f8ff5f83 ./lang/pl.json d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json 1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json @@ -107,7 +107,7 @@ b24092f34a46626d64628b510eba82193f0b203fd097f6a621b91af5d91eb571 ./src/api/auth ca21f6d5a407e2e3532899d53ba4e8682c2fe961a46d8cd870ddc459721513ef ./src/api/device_groups.rs 5245d5a70c4a1919d8b7757db399e1caf692c8b3a8af0343d8e462bf90373137 ./src/api/devices.rs 4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs -b9a9a68e47fdaa577c28063fa68bf95075bc355bff81addca4ee448ef70dd818 ./src/api/flows.rs +2ef2f64b77886e2abd6b410adf8de91a2333d3ebdb8fcee8376ba566e5bba029 ./src/api/flows.rs 82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs b61b0b4f196c5e72e47cf06ca0c14df3cf8eedbdef0242a9d2af33623663489f ./src/api/groups.rs 3cc64b9c992805f3fbf01c0785cf29ce3121750bf30bd6420ed6e5cf2f8df422 ./src/api/history.rs @@ -203,19 +203,19 @@ b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree 2822a98a4d7f72f2b89b70878aa0f9bd3f391fde4ea04ba1b94e89a527dcd1fb ./web/css/styles.css 183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg -263d5554e62d2dd26b251f2c43a7e4ec14c8d8d790fa67e55c9bebf16f9d55c0 ./web/index.html +d5115a9dc59b4e753dcff9239a77fe49886951d7a4ae7456be80d9201cc42365 ./web/index.html 9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js 5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js -bb2b49361103145bd8c502f2867962d48ced409c1a1474af1214c4d8d0b378b2 ./web/js-dynamic/core.js +b5fd43eeb9c15fe8289b9e718dfbc2651417379462c4b16b88e090b436abf7c5 ./web/js-dynamic/core.js 752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js -0e1992a7b93767f1fe4634121ae5aa43900d99f8c2614c2a4cbd5770a49b87c1 ./web/js-dynamic/entities.js -eeeef31f767f0aa3efb78bc44a2fea2b9eb86f4d4c50cf1ccae69b5ca30cdfda ./web/js-dynamic/events.js -22da18288de3282245f88f9f525d9ad29ec027571e7ea893720a9470170eccd2 ./web/js-dynamic/flows.js +6b166a9311e30fe948bd3236189436eea769c238f963ba00acec5cdb51ff1dd7 ./web/js-dynamic/entities.js +a48cb69ae4a2d0d6d512baef346fe499a5a6ea267e071856829df982d4a0f129 ./web/js-dynamic/events.js +8e0f294ceeec1d258edb56271469a8a05381e6443a475f76fbeded6637d17503 ./web/js-dynamic/flows.js 3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js 92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js 511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js -86031d4f7f7d1b46412c8fbb09ff1ad1b2b4cd84eb949bc749b27663f1dc27fa ./web/js-dynamic/navigation.js +c974b6ef41ce8b6def35f5349849a06855c46ec31cb4122a39fcb5c78f6d362a ./web/js-dynamic/navigation.js 0e398bad2cde85d4a62d8733c06e73f2b6a5b07db791d4570feba38c7ff8741f ./web/js-dynamic/realtime.js 1b667f0a8bce824b27f56a8d2f44ff8feef9a011289591dc629ee288db70d137 ./web/js-dynamic/router.js c2710fd9c1f2b31878c1e25ceb02d74ae30456c1bbb400e69fcf7619ecc72bc2 ./web/js-dynamic/select-ui.js diff --git a/README.md b/README.md index a665508..717437b 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ Self-hosted controller for GREE-compatible air conditioners with a local Web UI, thermostat zones, schedules, Home Assistant integration, history, notifications and a documented HTTP/WebSocket API. -**Version: 0.15.1** +**Version: 0.15.2** -Version 0.15.1 improves WebSocket connection-state handling in the Web UI: transient disconnects show a reconnecting state, persistent failures escalate after a short grace period, and successful reconnects restore the connected state without affecting the existing HTTP fallback. +Version 0.15.2 adds granular single-function unit actions to Visual Flow and exposes fan/Quiet/Turbo/Panel Light/Air/X-Fan/Health/Sleep controls in legacy automations, so automations can change one unit feature without touching unrelated settings. > [Full API reference](docs/API.md) — authentication, every endpoint, request bodies, response models, WebSocket events and examples. diff --git a/SOURCE_MANIFEST.sha256 b/SOURCE_MANIFEST.sha256 index 2b9ae16..8692991 100644 --- a/SOURCE_MANIFEST.sha256 +++ b/SOURCE_MANIFEST.sha256 @@ -1,13 +1,13 @@ de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore -852768ed3da99ac2e9bbe6c9b67e9a4a667e7115ad9482b6ce4a29ff72d8f914 ./Cargo.lock -8e58dd01d95c93ea4f5d0cc5e379cb192221705c9dda41b7b2b7182c49dbff0f ./Cargo.toml +82bb41f94fdf5c66ebd9e6caa92e0f1794431067298e0a27c95c8e5bfd68de89 ./Cargo.lock +39bdb16196253559e3487aa912ff770c09c7b08cdfbb21e9db1d8af1304ea7e0 ./Cargo.toml 19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE -4a6570c14ba56fc9a4921de6726816264e961a5b82a7fe3fcf475ef96417dad9 ./README.md +84a0803bc75dc3199e23ef56c11c6950e8bd888c686ecda7cc369829e8e3073f ./README.md 29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs -9873e774746783d65e447b439d069f84c13d2f4e556661e4eb509af59293c2f7 ./docs/API.md -58b79297607303151c1de2da74344cd4f6277bc53d80ebe04ed4567d906c963b ./docs/FLOW.md -9483ad7cb1e0c8e38f08c722600b6ba059d4273cfb9dcd1af57c11de522d187b ./docs/openapi.json +604a795dec077dd7e4f3462a564fdda68e8084aef07da0fcf3dfb6053a46cc58 ./docs/API.md +a881ba0808dd3f189d92038f6163c39d6b6208f6dbff5c32c0ea1a2628f3fbb4 ./docs/FLOW.md +8babaa6830fa70b2d3d8208ef51808e79c9d68c4e007b9ba1537fe582ba03b3d ./docs/openapi.json c65aa543d2e99f8f7d52736307b07ebd182597d80124661d53a16bb23f6f1118 ./ha-addon/.env.example 0993076ca45b6d31859680835204cdc111b3f9bdbf460d50b4efec56cd5e685b ./ha-addon/Dockerfile adf7ff248d782a3b1cba82b8789c57b6849ddaaaf5fbc108af75e18b1534a885 ./ha-addon/README.md @@ -21,7 +21,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./ha-addon/hom 6509286c7b7de638dcc5d40ce53181e65d1eddc5dc36a13306dc6d87220fedbb ./ha-addon/home-assistant/custom_components/gree_controller/coordinator.py 5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./ha-addon/home-assistant/custom_components/gree_controller/entity_map.py c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./ha-addon/home-assistant/custom_components/gree_controller/icon.png -a6d69fad7ea1f22436e9f643ceb36c206f1450d8337aede0c5d7a1f0613baeeb ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json +d467809d894b8cb9e8e04a2cc279e0cec3227c1d1c689098bdc3c0380aa9043e ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json c52a484b671ce738ecc00228a1db19a5d703f69ab27530c2896a5c2ae3b4a96f ./ha-addon/home-assistant/custom_components/gree_controller/number.py 39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./ha-addon/home-assistant/custom_components/gree_controller/select.py cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/home-assistant/custom_components/gree_controller/sensor.py @@ -29,10 +29,10 @@ cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./ha-addon/hom a745cc458c235158d5d70b6a33d8000fd8b40ff93bfe2f2796af0f4b3a9eef9b ./ha-addon/home-assistant/custom_components/gree_controller/translations/en.json 50b7d6fe6329ad4ea6bee9612ab141c2f73f8382fcc2af0bb076a8e8d4dd4465 ./ha-addon/home-assistant/custom_components/gree_controller/translations/pl.json c5fc5c87273d82d2834a0a5a365c821a416413ed509613441e24e4dc3507c4d2 ./ha-addon/home-assistant/generated/gree_controller_entities.example.json -ea5c4eb4ca2656f8d9a2be37a35ba04bb4c53d735c9c04b416b112e456699cff ./ha-addon/repository/gree-controller/CHANGELOG.md +e3a227ffe23368b152d0ba4a4e81a95b70d35e9870b2a8dc7b2048d6901a7f05 ./ha-addon/repository/gree-controller/CHANGELOG.md 1b5599195329cc3e643c23aa1caadcec80f133e8e515eec7d92b9f3cbda5b80b ./ha-addon/repository/gree-controller/DOCS.md c3db302f38d26dc9f8caf37a1a8a07a1334cc07ae301d452f2940a5ec5915719 ./ha-addon/repository/gree-controller/README.md -c16d31ae815dc4e324ce56b0b46529bcc78c5223688c33aa30d7463abe4f1184 ./ha-addon/repository/gree-controller/config.yaml +21b82216d0d84cc66ecbed69457caf61174fa9304528fe051c98e8ef7c634025 ./ha-addon/repository/gree-controller/config.yaml 3aae6cdae4c3aaab7786a9575e7093b7e2c5b69288167075f4b7475611691a6b ./ha-addon/repository/gree-controller/topologia-ha-vlan-gree-pl.png 0d0b42a7639b128946eec379175436fa1f150294f39d2dfd169077bfa87fd14d ./ha-addon/repository/gree-controller/topology-ha-vlan-gree-en.png 2272740b185e9520e948f74b8f45cf98b8b5fd28c93bdee760da626f91fe5c2a ./ha-addon/repository/gree-controller/translations/en.yaml @@ -42,8 +42,8 @@ cea12bc74c066a588a5ef0ef7bad9d4906bdbcea99f2d275429b0eefc94b4b5f ./ha-addon/run 081e27b6e43070d44f865335c30a42a5a29668eaa6a5ea5ab6967ac4447b8e06 ./ha-addon/sync-repository.sh f9e31de3d109ead32bf531c074d46a087df398d62488caee0b46b5e0a1c027f7 ./ha_addon.md 253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md -f7fc1aee1129cee46a30d5876e3e53f11d2661925eae19cb1e14b47deae83e9e ./lang/en.json -c05d100e9efa1a82747ffc709157a9bdd6d109b7a80c6e140d582a28d8c3de13 ./lang/pl.json +e5c6a6051381e28d74b75e48232de13d02eb87519d29c847b7ab7979ce47d082 ./lang/en.json +02c650f603bca1bd17d9d59193c176ee6ddccb8304f2e658d7ba7730f8ff5f83 ./lang/pl.json d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json 1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json @@ -106,7 +106,7 @@ b24092f34a46626d64628b510eba82193f0b203fd097f6a621b91af5d91eb571 ./src/api/auth ca21f6d5a407e2e3532899d53ba4e8682c2fe961a46d8cd870ddc459721513ef ./src/api/device_groups.rs 5245d5a70c4a1919d8b7757db399e1caf692c8b3a8af0343d8e462bf90373137 ./src/api/devices.rs 4b5b784670ae761a6848b9a33fe3fb391bebd39d65e87936907428711e96ca44 ./src/api/events.rs -b9a9a68e47fdaa577c28063fa68bf95075bc355bff81addca4ee448ef70dd818 ./src/api/flows.rs +2ef2f64b77886e2abd6b410adf8de91a2333d3ebdb8fcee8376ba566e5bba029 ./src/api/flows.rs 82c02fa135132ea3ebdb4d88dd1210dc536d22f987151d7f82595ee3d3d03440 ./src/api/gree_cloud.rs b61b0b4f196c5e72e47cf06ca0c14df3cf8eedbdef0242a9d2af33623663489f ./src/api/groups.rs 3cc64b9c992805f3fbf01c0785cf29ce3121750bf30bd6420ed6e5cf2f8df422 ./src/api/history.rs @@ -202,19 +202,19 @@ b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree 2822a98a4d7f72f2b89b70878aa0f9bd3f391fde4ea04ba1b94e89a527dcd1fb ./web/css/styles.css 183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg -263d5554e62d2dd26b251f2c43a7e4ec14c8d8d790fa67e55c9bebf16f9d55c0 ./web/index.html +d5115a9dc59b4e753dcff9239a77fe49886951d7a4ae7456be80d9201cc42365 ./web/index.html 9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js 5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js -bb2b49361103145bd8c502f2867962d48ced409c1a1474af1214c4d8d0b378b2 ./web/js-dynamic/core.js +b5fd43eeb9c15fe8289b9e718dfbc2651417379462c4b16b88e090b436abf7c5 ./web/js-dynamic/core.js 752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js -0e1992a7b93767f1fe4634121ae5aa43900d99f8c2614c2a4cbd5770a49b87c1 ./web/js-dynamic/entities.js -eeeef31f767f0aa3efb78bc44a2fea2b9eb86f4d4c50cf1ccae69b5ca30cdfda ./web/js-dynamic/events.js -22da18288de3282245f88f9f525d9ad29ec027571e7ea893720a9470170eccd2 ./web/js-dynamic/flows.js +6b166a9311e30fe948bd3236189436eea769c238f963ba00acec5cdb51ff1dd7 ./web/js-dynamic/entities.js +a48cb69ae4a2d0d6d512baef346fe499a5a6ea267e071856829df982d4a0f129 ./web/js-dynamic/events.js +8e0f294ceeec1d258edb56271469a8a05381e6443a475f76fbeded6637d17503 ./web/js-dynamic/flows.js 3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js 92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js 511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js -86031d4f7f7d1b46412c8fbb09ff1ad1b2b4cd84eb949bc749b27663f1dc27fa ./web/js-dynamic/navigation.js +c974b6ef41ce8b6def35f5349849a06855c46ec31cb4122a39fcb5c78f6d362a ./web/js-dynamic/navigation.js 0e398bad2cde85d4a62d8733c06e73f2b6a5b07db791d4570feba38c7ff8741f ./web/js-dynamic/realtime.js 1b667f0a8bce824b27f56a8d2f44ff8feef9a011289591dc629ee288db70d137 ./web/js-dynamic/router.js c2710fd9c1f2b31878c1e25ceb02d74ae30456c1bbb400e69fcf7619ecc72bc2 ./web/js-dynamic/select-ui.js diff --git a/docs/API.md b/docs/API.md index d107769..6a5b596 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,6 +1,6 @@ # GREE Controller API reference -HTTP and WebSocket API for GREE Controller **0.15.1**. +HTTP and WebSocket API for GREE Controller **0.15.2**. [← Main documentation](../README.md) @@ -240,7 +240,7 @@ Response: { "status": "ok", "name": "gree-controller", - "version": "0.15.1", + "version": "0.15.2", "uptime_seconds": 1234, "control_ready": true, "time": "2026-08-30T06:54:00Z" @@ -303,7 +303,7 @@ Returns the initial Web UI snapshot: "control_plan": {"generated_at": "2026-09-04T08:00:00Z", "zones": [], "rules": []}, "control_plan_revision": 42, "system": { - "version": "0.15.1", + "version": "0.15.2", "uptime_seconds": 1234, "auth_required": false, "control_ready": true, @@ -1694,6 +1694,12 @@ Additional Flow condition blocks are `house_mode`, `device_state`, `zone_state`, +## Single-function unit automation — 0.15.2 + +Visual Flow adds `device_feature_action`, a dedicated action block that targets one GREE unit and sends exactly one `DeviceCommand` field. Supported fields are power, HVAC mode, target temperature, fan speed, vertical/horizontal louver position, Quiet, Turbo, Panel Light, Air, X-Fan, Health and Sleep. The block compiles to the normal Flow-owned `Automation` path, so cooldown, ownership checks and thermostat conflict protection remain unchanged. + +Legacy direct-device automations expose fan speed and the same optional Quiet/Turbo/Panel Light/Air/X-Fan/Health/Sleep fields. Leaving all other fields as `No change` sends only the selected function. + ## Granular louver positions — 0.15.0 Vertical (`swing_vertical`) and horizontal (`swing_horizontal`) controls now carry the raw GREE louver position instead of only on/off. Vertical supports `0..11`; horizontal supports `0..6`. The first values map to Off, Full range (Auto) and the fixed positions shown by GREE remotes/apps; vertical `7..11` additionally represent partial swing ranges. diff --git a/docs/FLOW.md b/docs/FLOW.md index 6f36cd0..a800ea6 100644 --- a/docs/FLOW.md +++ b/docs/FLOW.md @@ -17,6 +17,7 @@ Flow is an authoring/orchestration layer. It does not replace the thermostat, sc - `zone_thermostat` writes the existing persistent Zone intent and wakes the normal thermostat cycle. It can also issue one-shot granular vertical/horizontal louver commands to the assigned unit; these auxiliary fields do not take over thermostat ownership. - `group_action` calls the existing `control_group` path, including custom group targets. - `device_action` uses the existing `DeviceCommand` / automatic-device path. +- `device_feature_action` uses the same path but compiles exactly one selected `DeviceCommand` field, leaving all other unit settings untouched. - Thermostat hysteresis, compressor protection, schedule hand-back, manual/local ownership and Temporary Quick Thermostat keep their existing priority. - GREE fan/quiet/sleep and dry/fan HVAC modes are suppressed on thermostat-assigned devices when they would fight the regulator. Device-only features such as light/turbo/louver/air/xfan/health remain available through the normal command path. @@ -61,7 +62,7 @@ Dry-run never mutates thermostats, devices, groups, schedules or automations. Fo Condition/source blocks include weekday, time range, date range, optional 5-field CRON triggers, application Night mode, outdoor/device/zone temperature, house mode, device state, thermostat/zone state, group state, Home Assistant state/numeric/attribute/availability, rolling mean/median windows, numeric oscillation detection, a diagnostic constant, and `shared_input`. Stateful gates include `stable_for`, bounded `state_duration`, `on_change` (condition-result or raw-value edge), `rate_limit` (maximum successful executions in a rolling period), and `delay`. Their runtime state, together with rolling statistics and oscillation samples, is persisted in the generated automation payload so restarts do not silently reset timing/history. Editing a stateful block resets only that block's incompatible runtime state. Flow state continues to be observed while the action itself is in cooldown, preventing stale continuity/change state. A `rate_limit` block must be placed directly before an action and consumes quota only after a successful execution; suppressed or failed actions do not consume it. Shared inputs are configured centrally in Home Assistant / Sensors and referenced by ID. Shared inputs store reusable value sources only; operators and comparison values are configured exclusively in each Flow reference. Logic blocks are AND, OR and NOT. -Actions include thermostat zone, GREE device, climate group, and generic Home Assistant service calls (`domain`, `service`, optional `entity_id`, JSON service data). Direct GREE actions expose power, HVAC mode, target temperature, fan speed, vertical/horizontal louver position, quiet, turbo, light, air, xfan, health and sleep. +Actions include thermostat zone, full GREE device command, single GREE function, climate group, and generic Home Assistant service calls (`domain`, `service`, optional `entity_id`, JSON service data). Direct GREE actions expose power, HVAC mode, target temperature, fan speed, vertical/horizontal louver position, quiet, turbo, light, air, xfan, health and sleep. The single-function block writes exactly one of those fields. ## Built-in presets diff --git a/docs/openapi.json b/docs/openapi.json index def16aa..4b17fda 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -2,9 +2,9 @@ "openapi": "3.1.0", "info": { "title": "GREE Controller API", - "version": "0.15.1", + "version": "0.15.2", "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\nAdministrator endpoints accept `Authorization: Bearer ` or `x-api-token: ` when configured. Restricted Home Assistant control endpoints also accept generated integration tokens.\n\n### Local and GREE Cloud\nDevices use one API model and one command surface. `connection_type=local` is dispatched only to the existing LAN/UDP provider; `connection_type=gree_cloud` is dispatched only to the GREE Cloud provider. No automatic transport fallback is performed.\n\n### GREE Cloud security\nCloud passwords, REST/MQTT tokens, Authorization values and device cipher keys are never returned in normal API responses or Cloud diagnostics. MQTT uses TLS with certificate and hostname validation.\n\n### Energy\nGREE Cloud `ElcAll` and selected Home Assistant cumulative energy sensors are normalized to kWh and converted to consumption deltas before aggregation. Raw cumulative values are retained only as meter samples/baselines.\n\n### 0.15.1\nImproves Web UI WebSocket state handling: temporary disconnects show a reconnecting state, persistent failures escalate after 15 seconds, and successful reconnects restore the connected state while the existing HTTP fallback remains active.\n\n### 0.15.0\nAdds granular vertical/horizontal louver positions across direct control, thermostat views, legacy automations, Visual Flow and the bundled Home Assistant integration. Legacy boolean swing command payloads remain accepted. Direct add-on access now shows an invalid-token error only after an explicit token submission.\n\n### 0.14.24\nPublic Custom Charts now fetch chart metadata before language assets, use English for missing-chart errors, support pinch-to-zoom/panning and clickable metric legends, and include the related missing-ID, favicon and Access Token UX fixes.\n\n### 0.14.23\nCode cleanup and small UX changes.\n\n### 0.14.22\nCode cleanup and small UX changes.\n\n### 0.14.21\nCode cleanup and small UX changes.\n\n### 0.14.20\nFixes the ARM cross-build bootstrap by removing the invalid xx-cargo version probe, prepares the target Rust standard library in a cacheable pre-source layer, shares dependency fetching across target architectures, and isolates compiled target caches per architecture while preserving DISTRO-selected Alpine/musl and Debian Trixie/glibc runtimes.\n\n### 0.14.19\nHome Assistant OCI builds now compile Rust for amd64/arm64 on the native build platform through xx cross-compilation, keep Cargo registry/target caches between builds, and send a smaller Docker context while preserving the selected Alpine (musl) or Debian Trixie (glibc) runtime. The Web UI also deduplicates the initial GREE Cloud runtime-status GET across the HTTP and WebSocket bootstrap renders.\n\n### 0.14.18\nShared Web UI CSS now uses generic reusable class names for segmented tabs, configuration layouts, chart cards, enable toggles, diagram primitives and Flow block categories; behavior and visual styling are unchanged.\n\n### 0.14.17\nHistory navigation now reuses the same segmented tab component as Flow / Schedules / Automations, with redundant History-specific tab CSS removed.\n\n### 0.14.16\nCustom Charts now expose saved-chart editing in the Web UI, public chart pages can switch history range through an optional hours override, and the standalone chart layout fits the viewport without vertical page scrolling.\n\n### 0.14.15\nHome Assistant add-on Custom Chart links now resolve the primary HA host IPv4 through Supervisor, support an explicit public_chart_base_url override, and enforce the fixed 8787 ingress/application port contract at startup and build time. Standalone bind/port behavior remains configurable.\n\n### 0.14.14\nShared Flow input Home Assistant sources now expose a dedicated entity search box separate from the selected entity_id, making large live entity catalogs easy to filter by name, ID and state.\n\n### 0.14.13\nHome Assistant add-on authentication is now hidden behind an automatic Supervisor status, manual URL/token fallback is unlocked only after a failed HA test, connection tests return a live sample entity reading, and Shared Flow input HA sources include searchable live entity suggestions.\n\n### 0.14.12\nThe Home Assistant add-on now uses the Supervisor Core API proxy and runtime SUPERVISOR_TOKEN automatically; standalone Home Assistant URL/token configuration is unchanged.\n\n### 0.14.11\nWeb UI icons now use a shared inline SVG set instead of font-dependent Unicode glyphs across navigation, dialogs, Flow, charts, controls and event categories for consistent cross-platform rendering.\n\n\n### 0.14.10\nNight mode and Home Assistant / Sensors standalone settings now use the full available page width, matching the other Web UI sections.\n\n### 0.14.9\nPower controls use an embedded SVG icon instead of a font-dependent Unicode glyph. Home Assistant room-sensor stale/error events identify the affected entity and zone, and historical event rows can recover that context from metadata.\n\n### 0.14.8\nRemaining Home Assistant and notification diagnostic errors no longer use device-transport 502 responses. Missing/invalid integration configuration returns 400, configured external dependency failures return 424, and 502 is reserved for Local/LAN GREE device communication.\n\n### 0.14.7\nHome Assistant energy-sensor discovery now returns an empty optional result when HA is not configured, uses 424 for configured external-dependency failures, and custom select popovers use their rendered height when positioning above a field.\n\n\n### 0.14.6\nFixes dynamic custom-select duplication in History → Energy and hides Energy history controls when no energy source is configured.\n\n### 0.14.5\nWeb UI single-choice controls now use one consistent custom dropdown, language packs are loaded on demand, and the dashboard outdoor temperature opens a 24-hour history modal.\n\n### 0.14.4\nHome Assistant connection tests validate only the configured server/API token and require no entity. `outdoor_entity_id` is the global Home Assistant outdoor-temperature sensor; zones can override it with `ha_outdoor_entity_id`. Room-temperature Home Assistant sensors remain configured per zone with `ha_entity_id`. Global/per-zone outdoor sensors are integrated with aliases, metrics/history and Visual Flow entity suggestions.\n\n### 0.14.2\nAdds independent vertical/horizontal swing controls across manual device control, thermostat zones, legacy automations, Visual Flow and Home Assistant zone climates. Swing is treated as an auxiliary one-shot unit setting and does not replace thermostat ownership.\n\n### 0.14.1\nAdds split/multisplit installation groups with shared energy and outdoor-temperature sources, multi-target energy charts with period comparison, cleaner Cloud status/alerts and safe MQTT cleanup after the last Cloud device is removed.\n\n### 0.14.0\nAdds first-class GREE Cloud MQTT status/control, reconnect/resubscribe lifecycle, Cloud diagnostics, per-device capabilities, Cloud/HA energy sources and energy-history aggregation while preserving the LAN transport contract.", + "description": "Self-hosted API used by the GREE Controller Web UI and Home Assistant integration.\n\n### Authentication\nAdministrator endpoints accept `Authorization: Bearer ` or `x-api-token: ` when configured. Restricted Home Assistant control endpoints also accept generated integration tokens.\n\n### Local and GREE Cloud\nDevices use one API model and one command surface. `connection_type=local` is dispatched only to the existing LAN/UDP provider; `connection_type=gree_cloud` is dispatched only to the GREE Cloud provider. No automatic transport fallback is performed.\n\n### GREE Cloud security\nCloud passwords, REST/MQTT tokens, Authorization values and device cipher keys are never returned in normal API responses or Cloud diagnostics. MQTT uses TLS with certificate and hostname validation.\n\n### Energy\nGREE Cloud `ElcAll` and selected Home Assistant cumulative energy sensors are normalized to kWh and converted to consumption deltas before aggregation. Raw cumulative values are retained only as meter samples/baselines.\n\n### 0.15.2\nAdds a dedicated Visual Flow single-function GREE action and optional fan/Quiet/Turbo/Panel Light/Air/X-Fan/Health/Sleep fields to legacy device automations, allowing one unit function to change without touching unrelated settings.\n\n### 0.15.1\nImproves Web UI WebSocket state handling: temporary disconnects show a reconnecting state, persistent failures escalate after 15 seconds, and successful reconnects restore the connected state while the existing HTTP fallback remains active.\n\n### 0.15.0\nAdds granular vertical/horizontal louver positions across direct control, thermostat views, legacy automations, Visual Flow and the bundled Home Assistant integration. Legacy boolean swing command payloads remain accepted. Direct add-on access now shows an invalid-token error only after an explicit token submission.\n\n### 0.14.24\nPublic Custom Charts now fetch chart metadata before language assets, use English for missing-chart errors, support pinch-to-zoom/panning and clickable metric legends, and include the related missing-ID, favicon and Access Token UX fixes.\n\n### 0.14.23\nCode cleanup and small UX changes.\n\n### 0.14.22\nCode cleanup and small UX changes.\n\n### 0.14.21\nCode cleanup and small UX changes.\n\n### 0.14.20\nFixes the ARM cross-build bootstrap by removing the invalid xx-cargo version probe, prepares the target Rust standard library in a cacheable pre-source layer, shares dependency fetching across target architectures, and isolates compiled target caches per architecture while preserving DISTRO-selected Alpine/musl and Debian Trixie/glibc runtimes.\n\n### 0.14.19\nHome Assistant OCI builds now compile Rust for amd64/arm64 on the native build platform through xx cross-compilation, keep Cargo registry/target caches between builds, and send a smaller Docker context while preserving the selected Alpine (musl) or Debian Trixie (glibc) runtime. The Web UI also deduplicates the initial GREE Cloud runtime-status GET across the HTTP and WebSocket bootstrap renders.\n\n### 0.14.18\nShared Web UI CSS now uses generic reusable class names for segmented tabs, configuration layouts, chart cards, enable toggles, diagram primitives and Flow block categories; behavior and visual styling are unchanged.\n\n### 0.14.17\nHistory navigation now reuses the same segmented tab component as Flow / Schedules / Automations, with redundant History-specific tab CSS removed.\n\n### 0.14.16\nCustom Charts now expose saved-chart editing in the Web UI, public chart pages can switch history range through an optional hours override, and the standalone chart layout fits the viewport without vertical page scrolling.\n\n### 0.14.15\nHome Assistant add-on Custom Chart links now resolve the primary HA host IPv4 through Supervisor, support an explicit public_chart_base_url override, and enforce the fixed 8787 ingress/application port contract at startup and build time. Standalone bind/port behavior remains configurable.\n\n### 0.14.14\nShared Flow input Home Assistant sources now expose a dedicated entity search box separate from the selected entity_id, making large live entity catalogs easy to filter by name, ID and state.\n\n### 0.14.13\nHome Assistant add-on authentication is now hidden behind an automatic Supervisor status, manual URL/token fallback is unlocked only after a failed HA test, connection tests return a live sample entity reading, and Shared Flow input HA sources include searchable live entity suggestions.\n\n### 0.14.12\nThe Home Assistant add-on now uses the Supervisor Core API proxy and runtime SUPERVISOR_TOKEN automatically; standalone Home Assistant URL/token configuration is unchanged.\n\n### 0.14.11\nWeb UI icons now use a shared inline SVG set instead of font-dependent Unicode glyphs across navigation, dialogs, Flow, charts, controls and event categories for consistent cross-platform rendering.\n\n\n### 0.14.10\nNight mode and Home Assistant / Sensors standalone settings now use the full available page width, matching the other Web UI sections.\n\n### 0.14.9\nPower controls use an embedded SVG icon instead of a font-dependent Unicode glyph. Home Assistant room-sensor stale/error events identify the affected entity and zone, and historical event rows can recover that context from metadata.\n\n### 0.14.8\nRemaining Home Assistant and notification diagnostic errors no longer use device-transport 502 responses. Missing/invalid integration configuration returns 400, configured external dependency failures return 424, and 502 is reserved for Local/LAN GREE device communication.\n\n### 0.14.7\nHome Assistant energy-sensor discovery now returns an empty optional result when HA is not configured, uses 424 for configured external-dependency failures, and custom select popovers use their rendered height when positioning above a field.\n\n\n### 0.14.6\nFixes dynamic custom-select duplication in History → Energy and hides Energy history controls when no energy source is configured.\n\n### 0.14.5\nWeb UI single-choice controls now use one consistent custom dropdown, language packs are loaded on demand, and the dashboard outdoor temperature opens a 24-hour history modal.\n\n### 0.14.4\nHome Assistant connection tests validate only the configured server/API token and require no entity. `outdoor_entity_id` is the global Home Assistant outdoor-temperature sensor; zones can override it with `ha_outdoor_entity_id`. Room-temperature Home Assistant sensors remain configured per zone with `ha_entity_id`. Global/per-zone outdoor sensors are integrated with aliases, metrics/history and Visual Flow entity suggestions.\n\n### 0.14.2\nAdds independent vertical/horizontal swing controls across manual device control, thermostat zones, legacy automations, Visual Flow and Home Assistant zone climates. Swing is treated as an auxiliary one-shot unit setting and does not replace thermostat ownership.\n\n### 0.14.1\nAdds split/multisplit installation groups with shared energy and outdoor-temperature sources, multi-target energy charts with period comparison, cleaner Cloud status/alerts and safe MQTT cleanup after the last Cloud device is removed.\n\n### 0.14.0\nAdds first-class GREE Cloud MQTT status/control, reconnect/resubscribe lifecycle, Cloud diagnostics, per-device capabilities, Cloud/HA energy sources and energy-history aggregation while preserving the LAN transport contract.", "license": { "name": "MIT" } @@ -5900,7 +5900,7 @@ }, "version": { "type": "string", - "example": "0.15.1" + "example": "0.15.2" }, "uptime_seconds": { "type": "integer", diff --git a/ha-addon/home-assistant/custom_components/gree_controller/manifest.json b/ha-addon/home-assistant/custom_components/gree_controller/manifest.json index 3b8d6aa..7a788b5 100644 --- a/ha-addon/home-assistant/custom_components/gree_controller/manifest.json +++ b/ha-addon/home-assistant/custom_components/gree_controller/manifest.json @@ -1,7 +1,7 @@ { "domain": "gree_controller", "name": "GREE Controller", - "version": "0.15.1", + "version": "0.15.2", "config_flow": true, "integration_type": "hub", "iot_class": "local_polling", diff --git a/ha-addon/repository/gree-controller/CHANGELOG.md b/ha-addon/repository/gree-controller/CHANGELOG.md index 79b4d9b..bed02e9 100644 --- a/ha-addon/repository/gree-controller/CHANGELOG.md +++ b/ha-addon/repository/gree-controller/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.15.2 + +- Adds a dedicated Visual Flow action for changing exactly one GREE unit function without modifying unrelated settings. +- Legacy device automations now expose fan speed plus Quiet, Turbo, Panel Light, Air, X-Fan, Health and Sleep as optional single-field controls. + ## 0.15.1 - Web UI now treats temporary WebSocket loss as a reconnecting state, escalates to a connection error only after 15 seconds, and returns cleanly to connected after recovery while preserving the existing HTTP fallback. diff --git a/ha-addon/repository/gree-controller/config.yaml b/ha-addon/repository/gree-controller/config.yaml index 918db40..523dec3 100644 --- a/ha-addon/repository/gree-controller/config.yaml +++ b/ha-addon/repository/gree-controller/config.yaml @@ -1,5 +1,5 @@ name: "GREE Controller" -version: "0.15.1" +version: "0.15.2" slug: "gree_controller" description: "Local GREE HVAC controller with Web UI and Home Assistant integration" url: "https://git.linuxiarz.pl/gru/gree-controller-ha-addon/" diff --git a/lang/en.json b/lang/en.json index 30641a1..5eb405e 100644 --- a/lang/en.json +++ b/lang/en.json @@ -652,6 +652,9 @@ "groups.noZones": "Create thermostat zones before creating a group.", "automations.targetType": "Target type", "automations.groupHint": "For a group, Auto means follow the whole-house heating/cooling mode. Group profiles are applied to all member zones.", + "automations.singleDeviceFunctions": "Single device functions", + "automations.deviceFunction": "Unit function", + "automations.singleDeviceFunctionsHint": "Leave the other fields as No change to modify only the selected unit function. Fan, Quiet and Sleep still follow the existing thermostat ownership rules.", "simulation.openTab": "Open simulator only in new tab", "simulation.fullscreen": "Full screen", "simulation.exitFullscreen": "Exit full screen", @@ -952,6 +955,7 @@ "flow.node.not": "NOT", "flow.node.thermostat": "Thermostat", "flow.node.greeDevice": "GREE device", + "flow.node.greeFeature": "Single GREE function", "flow.node.group": "Group", "common.yes": "Yes", "common.no": "No", @@ -1002,6 +1006,8 @@ "flow.haAvailableHint": "This condition is true only when the Home Assistant entity returns a state other than unknown/unavailable. Connection errors fail closed as false. Useful as a guard before NOT and safety logic.", "flow.deviceOptions": "Advanced GREE options", "flow.deviceOptionsHint": "These fields use the existing DeviceCommand. Louver positions are one-shot auxiliary settings; fan/quiet/sleep and dry/fan HVAC modes are blocked on a thermostat-assigned device when they would fight the regulator; use the Thermostat action there instead.", + "flow.deviceFeature": "Unit function", + "flow.deviceFeatureHint": "This action sends exactly one DeviceCommand field. All other unit settings remain unchanged.", "settings.haConnection": "Home Assistant connection", "settings.haConnectionHint": "Server address and credentials used by all Home Assistant features.", "settings.haThermostatSources": "Thermostat temperature sources", diff --git a/lang/pl.json b/lang/pl.json index 8929626..5b33f1f 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -652,6 +652,9 @@ "groups.noZones": "Najpierw utwórz strefy termostatów.", "automations.targetType": "Typ celu", "automations.groupHint": "Dla grupy Auto oznacza dziedziczenie globalnego trybu grzania/chłodzenia. Profil grupy jest stosowany do wszystkich stref w grupie.", + "automations.singleDeviceFunctions": "Pojedyncze funkcje urządzenia", + "automations.deviceFunction": "Funkcja jednostki", + "automations.singleDeviceFunctionsHint": "Pozostaw pozostałe pola jako Bez zmian, aby zmienić wyłącznie wybraną funkcję jednostki. Fan, Quiet i Sleep nadal respektują istniejące zasady właściciela termostatu.", "simulation.openTab": "Otwórz sam symulator w nowej karcie", "simulation.fullscreen": "Pełny ekran", "simulation.exitFullscreen": "Wyjdź z pełnego ekranu", @@ -952,6 +955,7 @@ "flow.node.not": "NOT", "flow.node.thermostat": "Termostat", "flow.node.greeDevice": "Urządzenie GREE", + "flow.node.greeFeature": "Pojedyncza funkcja GREE", "flow.node.group": "Grupa", "common.yes": "Tak", "common.no": "Nie", @@ -1002,6 +1006,8 @@ "flow.haAvailableHint": "Warunek jest prawdziwy tylko wtedy, gdy encja Home Assistant odpowiada stanem innym niż unknown/unavailable. Błąd połączenia jest traktowany bezpiecznie jako false. Przydatne jako bramka przed NOT i logiką bezpieczeństwa.", "flow.deviceOptions": "Zaawansowane opcje GREE", "flow.deviceOptionsHint": "Te pola używają istniejącego DeviceCommand. Pozycje żaluzji są jednorazowymi ustawieniami pomocniczymi; fan/quiet/sleep oraz tryby dry/fan są blokowane dla urządzenia przypisanego do termostatu, gdy mogłyby walczyć z regulatorem; użyj wtedy akcji Termostat.", + "flow.deviceFeature": "Funkcja jednostki", + "flow.deviceFeatureHint": "Ta akcja wysyła dokładnie jedno pole DeviceCommand. Pozostałe ustawienia jednostki nie są zmieniane.", "settings.haConnection": "Połączenie z Home Assistant", "settings.haConnectionHint": "Adres serwera i dane dostępu używane przez wszystkie funkcje Home Assistant.", "settings.haThermostatSources": "Źródła temperatury sterowania", diff --git a/src/api/flows.rs b/src/api/flows.rs index edb041c..172d339 100644 --- a/src/api/flows.rs +++ b/src/api/flows.rs @@ -63,6 +63,66 @@ fn flow_louver_position(config: &Value, key: &str) -> Option { } } +fn flow_device_feature_command(config: &Value) -> Result { + let feature = flow_string(config, "feature") + .ok_or_else(|| AppError::BadRequest("device feature action needs a feature".into()))?; + let mut command = DeviceCommand::default(); + match feature.as_str() { + "power" => { + command.power = Some(flow_bool(config, "value").ok_or_else(|| { + AppError::BadRequest("device feature power needs a boolean value".into()) + })?); + } + "mode" => { + command.mode = Some(flow_string(config, "value").ok_or_else(|| { + AppError::BadRequest("device feature mode needs a value".into()) + })?); + } + "target_temperature" => { + command.target_temperature = Some(flow_f64(config, "value").ok_or_else(|| { + AppError::BadRequest("device feature target temperature needs a numeric value".into()) + })?); + } + "fan_speed" => { + command.fan_speed = Some(flow_u8(config, "value").ok_or_else(|| { + AppError::BadRequest("device feature fan speed needs an integer value".into()) + })?); + } + "swing_vertical" => { + command.swing_vertical = Some(flow_louver_position(config, "value").ok_or_else(|| { + AppError::BadRequest("device feature vertical louver needs a position".into()) + })?); + } + "swing_horizontal" => { + command.swing_horizontal = Some(flow_louver_position(config, "value").ok_or_else(|| { + AppError::BadRequest("device feature horizontal louver needs a position".into()) + })?); + } + "quiet" | "turbo" | "light" | "air" | "xfan" | "health" | "sleep" => { + let value = flow_bool(config, "value").ok_or_else(|| { + AppError::BadRequest("device feature action needs an on/off value".into()) + })?; + match feature.as_str() { + "quiet" => command.quiet = Some(value), + "turbo" => command.turbo = Some(value), + "light" => command.light = Some(value), + "air" => command.air = Some(value), + "xfan" => command.xfan = Some(value), + "health" => command.health = Some(value), + "sleep" => command.sleep = Some(value), + _ => unreachable!(), + } + } + _ => { + return Err(AppError::BadRequest(format!( + "unsupported device feature action: {feature}" + ))) + } + } + engine::validate_command(&command)?; + Ok(command) +} + fn generated_flow_name(flow_id: &str, action_node_id: &str) -> String { let digest = Sha256::digest(format!("{flow_id}:{action_node_id}").as_bytes()); format!("flow-{}", URL_SAFE_NO_PAD.encode(&digest[..12])) @@ -104,7 +164,11 @@ fn flow_logic_kind(kind: &str) -> bool { fn flow_action_kind(kind: &str) -> bool { matches!( kind, - "zone_thermostat" | "device_action" | "group_action" | "ha_service_action" + "zone_thermostat" + | "device_action" + | "device_feature_action" + | "group_action" + | "ha_service_action" ) } @@ -1332,6 +1396,18 @@ fn compile_flow( return Err(AppError::BadRequest("device action cannot be empty".into())); } } + "device_feature_action" => { + let id = flow_string(&action_node.config, "device_id").ok_or_else(|| { + AppError::BadRequest("device feature action needs a device".into()) + })?; + if !devices.iter().any(|d| d.id == id) { + return Err(AppError::BadRequest( + "device feature action references a missing device".into(), + )); + } + item.action_device_id = id; + item.action = flow_device_feature_command(&action_node.config)?; + } "ha_service_action" => { let domain = flow_string(&action_node.config, "domain").ok_or_else(|| { AppError::BadRequest("Home Assistant action needs a domain".into()) @@ -1708,7 +1784,7 @@ fn dry_run_block_reason( } Ok(None) } - "device_action" => { + "device_action" | "device_feature_action" => { let Some(device_id) = flow_string(&action.config, "device_id") else { return Ok(Some("missing_device".into())); }; @@ -1737,25 +1813,30 @@ fn dry_run_block_reason( }) { return Ok(Some("temporary_quick_thermostat".into())); } + let command = if action.kind == "device_feature_action" { + flow_device_feature_command(&action.config)? + } else { + let mut command = DeviceCommand::default(); + command.power = flow_bool(&action.config, "power"); + command.mode = flow_string(&action.config, "mode"); + command.target_temperature = flow_f64(&action.config, "target_temperature"); + command.fan_speed = flow_u8(&action.config, "fan_speed"); + command.swing_vertical = flow_louver_position(&action.config, "swing_vertical"); + command.swing_horizontal = flow_louver_position(&action.config, "swing_horizontal"); + command.quiet = flow_bool(&action.config, "quiet"); + command.turbo = flow_bool(&action.config, "turbo"); + command.light = flow_bool(&action.config, "light"); + command.air = flow_bool(&action.config, "air"); + command.xfan = flow_bool(&action.config, "xfan"); + command.health = flow_bool(&action.config, "health"); + command.sleep = flow_bool(&action.config, "sleep"); + command + }; if zones.iter().any(|z| z.device_id == device_id && !z.enabled) - && flow_bool(&action.config, "power") != Some(true) + && command.power != Some(true) { return Ok(Some("zone_disabled".into())); } - let mut command = DeviceCommand::default(); - command.power = flow_bool(&action.config, "power"); - command.mode = flow_string(&action.config, "mode"); - command.target_temperature = flow_f64(&action.config, "target_temperature"); - command.fan_speed = flow_u8(&action.config, "fan_speed"); - command.swing_vertical = flow_louver_position(&action.config, "swing_vertical"); - command.swing_horizontal = flow_louver_position(&action.config, "swing_horizontal"); - command.quiet = flow_bool(&action.config, "quiet"); - command.turbo = flow_bool(&action.config, "turbo"); - command.light = flow_bool(&action.config, "light"); - command.air = flow_bool(&action.config, "air"); - command.xfan = flow_bool(&action.config, "xfan"); - command.health = flow_bool(&action.config, "health"); - command.sleep = flow_bool(&action.config, "sleep"); if zones.iter().any(|z| z.device_id == device_id) && engine::automation_action_conflicts_with_thermostat(&command) { @@ -1950,4 +2031,20 @@ mod flow_draft_tests { assert!(flow.compiled_schedule_ids.is_empty()); assert!(flow.compiled_automation_ids.is_empty()); } + + #[test] + fn device_feature_action_builds_only_selected_field() { + let command = flow_device_feature_command(&json!({"feature": "light", "value": true})) + .expect("valid feature action"); + assert_eq!(command.light, Some(true)); + assert!(command.power.is_none()); + assert!(command.mode.is_none()); + assert!(command.swing_vertical.is_none()); + } + + #[test] + fn device_feature_action_validates_louver_range() { + let result = flow_device_feature_command(&json!({"feature": "swing_vertical", "value": 99})); + assert!(result.is_err()); + } } diff --git a/web/index.html b/web/index.html index 1e59ed3..5220b99 100644 --- a/web/index.html +++ b/web/index.html @@ -982,7 +982,8 @@
Akcje
Home Assistant @@ -1786,6 +1787,28 @@
+
+ Single device functions +
+ + + + + + + + +
+

Leave every other field as No change to modify only the selected unit function.

+