v0.15.10
This commit is contained in:
Generated
+1
-1
@@ -719,7 +719,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gree-controller"
|
||||
version = "0.15.9"
|
||||
version = "0.15.10"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-gcm",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "gree-controller"
|
||||
version = "0.15.9"
|
||||
version = "0.15.10"
|
||||
edition = "2021"
|
||||
authors = ["GREE Controller contributors"]
|
||||
description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support"
|
||||
|
||||
+17
-17
@@ -1,14 +1,14 @@
|
||||
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||
0c5cd71516b15e7b51a39fd694d8532a38485d8a8d358caab8ae850274a41127 ./Cargo.lock
|
||||
e3b77008eebcb6db5b188102f788cd06f5159c1a66577bb212d96d125aa72510 ./Cargo.toml
|
||||
5fad199d087740ebfaa9d10ed484a409c0968947b1cd63cb9195d96eec08bd4b ./Cargo.lock
|
||||
7587c59b86b2d68649c7b8c815495d16d21626271f2795519a395c988f168e10 ./Cargo.toml
|
||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||
d5e5fdafaf8d777e3848b5c8f3aacfd0cf4c80735fbbb6bda14e86468329de88 ./README.md
|
||||
f4806d66acea0baaf5b1457afb708771b2765f7c08b4c4824c5ff1eace42c83d ./SOURCE_MANIFEST.sha256
|
||||
c85d6b49b2e81e8f8b553d48fa23188c78fbfc9dca73d341984cc88741e9ad54 ./README.md
|
||||
1357e8d73dcd936f73b93a445b8efaa41b25ef40c30e3f212ba38cd24cb7bfae ./SOURCE_MANIFEST.sha256
|
||||
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
||||
96144a5c5f3688854ab19dcd97794bd74da8ed26133c1468d91f6659a54faa4b ./docs/API.md
|
||||
0884be80021ff93826525f0b5bd5c81418f0c9c4eadec9583386ec61ae8e82ba ./docs/FLOW.md
|
||||
d883d4a58b19b10b5b33dacd4a58b0a4eb69dcc133a478524407027956ed9f70 ./docs/openapi.json
|
||||
d0fa90377cb302dfe4e1eb41e976e5ca9b67f6ba7c33bfda0611a3a8925b2ccf ./docs/API.md
|
||||
af38b4976805099c8060f279a313808bfa2bfbbdff48f2cbed627e3f9b07510d ./docs/FLOW.md
|
||||
d37cb744c1153bb8ba555575108c5a56e179afa850900a307a0d7f78559ddb69 ./docs/openapi.json
|
||||
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
||||
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
||||
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./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
|
||||
62e7bb884b342349bb175ce874d11ccb845e8219512ff6cf4d6c9ed03cb12294 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
||||
f73f2ef3ce1af50aaa0834c9bc4136c40b4e8d6074c899b63af4dd07db26c8fa ./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
|
||||
9cb2e827567e8cb06050e24fd3811dda6f51350d8bec9b605189fa8ab85b331a ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||
969eae3da93f9ded22b2d4e77181274dd0aef1e95664cf8b2856467afee83696 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
||||
0bd253135a67d295dfca355b735b350dbf36ed396a6c8e30387d5218d4e5afcf ./ha-addon/repository/gree-controller/README.md
|
||||
cb348473b59a76dfd29adc04d281128a9b8848b92107b928073aac4e047fdc4b ./ha-addon/repository/gree-controller/config.yaml
|
||||
d5fcb2ace520975ba35d7e97bdb17854b5d14d90d0434a8de70536e7cdc099cd ./ha-addon/repository/gree-controller/README.md
|
||||
0aa28495bd7951f3437f7acd17e4df18708ad66612010793d07d3229963b6159 ./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
|
||||
e9f8da620e017a3c06363f9b3d8709d9abaee9bbac4e53e60f27db7c072bb647 ./lang/en.json
|
||||
efc35994018e526ed829ee26b3cb1c74df2ac3655433e3b9bd0bf12f087e3223 ./lang/pl.json
|
||||
f362982769d78851084392f6e4311e023e49bc7856ce197f3cc21b09dac13679 ./lang/en.json
|
||||
2a465025571229fe381888676ba144a8382dc952961cfb5b58791d2cdfa96937 ./lang/pl.json
|
||||
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
|
||||
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
|
||||
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
|
||||
@@ -85,7 +85,7 @@ fdcd9a5055d08037278b842e7ab69265345c5811f0a06867136c511d140bb191 ./presets/wind
|
||||
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
||||
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
||||
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
||||
863f087740abee259d51dc5935c6b60dfe25b31a6a9fe64657d25a05d200a6fa ./scripts/api_dev_test.py
|
||||
81f0140c2a90ae97e93a1dbe0219032b4de0174f40af62e18fa8df211a876f8c ./scripts/api_dev_test.py
|
||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
||||
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
||||
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
||||
@@ -199,10 +199,10 @@ f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/
|
||||
8c34b70fb5f80e3249c2e0c63686e60ce1697cf44a7e6f11b3ad6f689ce17792 ./src/state.rs
|
||||
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
||||
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
||||
c60667f270dc66ee463f425ca5eda676363fcd2390e5d652220f6b4af9ded6c3 ./web/css/styles.css
|
||||
5124a8a70061e35762cc9ee8646d7203c57dd7959a4a4ee818f4bc9a5fc22bda ./web/css/styles.css
|
||||
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||
ca887343ec089cbbb83c4ce5685edbade1266c89a29b5570c02f75c9c50b1c10 ./web/index.html
|
||||
80798cec4157be8beff6d69747270ee1842b386d07caddfd7c3c008f9662cd73 ./web/index.html
|
||||
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
||||
e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js
|
||||
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
||||
@@ -210,7 +210,7 @@ e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynam
|
||||
752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js
|
||||
6b166a9311e30fe948bd3236189436eea769c238f963ba00acec5cdb51ff1dd7 ./web/js-dynamic/entities.js
|
||||
b4204241eecf387256364f4230b23ed50d947a362f4ceb1e528d69494e93d7cf ./web/js-dynamic/events.js
|
||||
e0f604f54eafb9a6d6ac1c6c24eb27f3e3c1e31ed80ead57ac0340c7228318b8 ./web/js-dynamic/flows.js
|
||||
99f6399c6748be7c6eb77db64367668a7d0b1f8736c27e981c77edb70b493166 ./web/js-dynamic/flows.js
|
||||
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
||||
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
||||
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
Self-hosted controller for GREE-compatible air conditioners with a local Web UI, thermostat zones, schedules, Home Assistant integration, history, notifications and a documented HTTP/WebSocket API.
|
||||
|
||||
**Version: 0.15.9**
|
||||
**Version: 0.15.10**
|
||||
|
||||
Version 0.15.9 strengthens Visual Flow execution and editing: change events remain pending until their action succeeds, rolling mean/median blocks wait for a complete configured window, thermostat action summaries show power/mode explicitly, and the canvas adds box selection plus copy/paste/duplicate for groups of blocks.
|
||||
Version 0.15.10 expands Visual Flow keyboard authoring and portability: blocks can be created, selected, moved, copied, pasted, duplicated and deleted from the keyboard, the editor includes an in-app shortcut reference, and any Flow can be exported directly from the Flow list without opening it.
|
||||
|
||||
> [Full API reference](docs/API.md) — authentication, every endpoint, request bodies, response models, WebSocket events and examples.
|
||||
> Built-in API: `/api/*` and `/ws`; interactive Swagger docs: `/api-docs`; OpenAPI JSON: `/api-docs/openapi.json`.
|
||||
@@ -271,7 +271,7 @@ Schedules select profiles or a custom target for selected ISO weekdays (`1=Monda
|
||||
|
||||
The reusable Flow preset library is stored as standalone JSON files in `presets/`. Each file contains its name, description and complete Flow graph, while the UI loads the embedded preset catalog dynamically instead of hard-coding graphs in JavaScript. The library has category tabs, search, local favorites/recent presets, a graph preview and requirement checks before applying a preset.
|
||||
|
||||
Available blocks include weekday/time/date ranges, the application Night mode, outdoor/device/zone temperature, house/device/zone/group state, arbitrary Home Assistant state/numeric/attribute/availability checks, reusable Shared Inputs, a constant diagnostic source, AND/OR/NOT, thermostat actions, direct GREE actions and group actions. Flow exports keep the existing format version and carry definitions of referenced Shared Inputs, so import can create missing sources or safely remap ID collisions. The GREE action uses the existing `DeviceCommand` fields (power, mode, target, fan, swing, quiet, turbo, light, air, xfan, health and sleep). Multiple branches can feed an action; the compiled runtime preserves the graph logic. The editor keeps the Interpretation / Flow cycle details collapsed until requested.
|
||||
Available blocks include weekday/time/date ranges, the application Night mode, outdoor/device/zone temperature, house/device/zone/group state, arbitrary Home Assistant state/numeric/attribute/availability checks, reusable Shared Inputs, a constant diagnostic source, AND/OR/NOT, thermostat actions, direct GREE actions and group actions. The editor supports keyboard-first block search/creation, save, multi-select, copy/cut/paste/duplicate, delete, movement and canvas zoom/fit with an in-app shortcut reference. Saved Flows can also be exported directly from the Flow list. Flow exports keep the existing format version and carry definitions of referenced Shared Inputs, so import can create missing sources or safely remap ID collisions. The GREE action uses the existing `DeviceCommand` fields (power, mode, target, fan, swing, quiet, turbo, light, air, xfan, health and sleep). Multiple branches can feed an action; the compiled runtime preserves the graph logic. The editor keeps the Interpretation / Flow cycle details collapsed until requested.
|
||||
|
||||
Quick preset/setpoint overrides normally hand control back at the next schedule boundary. Temporary Quick Thermostat adds explicit start/finish rules such as duration, exact time, temperature reached/stable or next schedule boundary.
|
||||
|
||||
@@ -505,4 +505,4 @@ The editor supports optimistic revisions, so saving an older copy from another b
|
||||
|
||||
Diagnostics include a non-mutating dry-run endpoint/UI with a selectable simulation time, optional per-block sensor/state overrides, a per-node condition trace and an ownership/block reason. Shared Home Assistant Flow inputs can also be tested directly in their editor against the current HA entity state. Flow-scoped execution and dry-run events can be viewed from the editor.
|
||||
|
||||
Individual Flows can be exported/imported as versioned `gree-controller-flow` JSON. Generated schedules and automations are intentionally excluded from the portable document and are regenerated from the source graph on import. Incomplete Flows can also be saved as disabled drafts; drafts preserve the editor graph but intentionally generate no schedules or automations until completed and saved normally. Shared inputs show which Flows reference them and link directly to those editors. The preset library includes 37 categorized scenarios for comfort, energy, safety, night, reliability, Home Assistant heat-source coordination and advanced multi-branch logic.
|
||||
Individual Flows can be exported/imported as versioned `gree-controller-flow` JSON. A saved Flow can be exported directly from its card in the Flow list without opening the editor; the editor export remains available for saved and unsaved drafts. Generated schedules and automations are intentionally excluded from the portable document and are regenerated from the source graph on import. Incomplete Flows can also be saved as disabled drafts; drafts preserve the editor graph but intentionally generate no schedules or automations until completed and saved normally. Shared inputs show which Flows reference them and link directly to those editors. The preset library includes 37 categorized scenarios for comfort, energy, safety, night, reliability, Home Assistant heat-source coordination and advanced multi-branch logic.
|
||||
|
||||
+16
-16
@@ -1,13 +1,13 @@
|
||||
de0793949cf01d27d903653226ecc9b5f72523d8711f20bcab022cf42c4c4ed0 ./.env.example
|
||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||
0c5cd71516b15e7b51a39fd694d8532a38485d8a8d358caab8ae850274a41127 ./Cargo.lock
|
||||
e3b77008eebcb6db5b188102f788cd06f5159c1a66577bb212d96d125aa72510 ./Cargo.toml
|
||||
5fad199d087740ebfaa9d10ed484a409c0968947b1cd63cb9195d96eec08bd4b ./Cargo.lock
|
||||
7587c59b86b2d68649c7b8c815495d16d21626271f2795519a395c988f168e10 ./Cargo.toml
|
||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||
d5e5fdafaf8d777e3848b5c8f3aacfd0cf4c80735fbbb6bda14e86468329de88 ./README.md
|
||||
c85d6b49b2e81e8f8b553d48fa23188c78fbfc9dca73d341984cc88741e9ad54 ./README.md
|
||||
29e9821e5ed216ea3565dd6d8bc0b174ed3cf9fa72c4ccebbfa99e98db3ad8f9 ./build.rs
|
||||
96144a5c5f3688854ab19dcd97794bd74da8ed26133c1468d91f6659a54faa4b ./docs/API.md
|
||||
0884be80021ff93826525f0b5bd5c81418f0c9c4eadec9583386ec61ae8e82ba ./docs/FLOW.md
|
||||
d883d4a58b19b10b5b33dacd4a58b0a4eb69dcc133a478524407027956ed9f70 ./docs/openapi.json
|
||||
d0fa90377cb302dfe4e1eb41e976e5ca9b67f6ba7c33bfda0611a3a8925b2ccf ./docs/API.md
|
||||
af38b4976805099c8060f279a313808bfa2bfbbdff48f2cbed627e3f9b07510d ./docs/FLOW.md
|
||||
d37cb744c1153bb8ba555575108c5a56e179afa850900a307a0d7f78559ddb69 ./docs/openapi.json
|
||||
77b8becf7a2c16a384ea85721d878b76e2d520549cb4455d9aa806e57e5a2ecd ./ha-addon/.env.example
|
||||
0ca6e58170c2b8925194431ebbdb55f0c8b377fec5cd422cd2bd257e2d9328e3 ./ha-addon/Dockerfile
|
||||
0951696a2c2e66d4d969f8e7f8641184e3012f261583b31074bc38a7a1c0cc1a ./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
|
||||
62e7bb884b342349bb175ce874d11ccb845e8219512ff6cf4d6c9ed03cb12294 ./ha-addon/home-assistant/custom_components/gree_controller/manifest.json
|
||||
f73f2ef3ce1af50aaa0834c9bc4136c40b4e8d6074c899b63af4dd07db26c8fa ./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
|
||||
9cb2e827567e8cb06050e24fd3811dda6f51350d8bec9b605189fa8ab85b331a ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||
969eae3da93f9ded22b2d4e77181274dd0aef1e95664cf8b2856467afee83696 ./ha-addon/repository/gree-controller/CHANGELOG.md
|
||||
50b8ba050c6b4919e4d057f7b38c1e23d3913b5b7c87552626452e9695d37251 ./ha-addon/repository/gree-controller/DOCS.md
|
||||
0bd253135a67d295dfca355b735b350dbf36ed396a6c8e30387d5218d4e5afcf ./ha-addon/repository/gree-controller/README.md
|
||||
cb348473b59a76dfd29adc04d281128a9b8848b92107b928073aac4e047fdc4b ./ha-addon/repository/gree-controller/config.yaml
|
||||
d5fcb2ace520975ba35d7e97bdb17854b5d14d90d0434a8de70536e7cdc099cd ./ha-addon/repository/gree-controller/README.md
|
||||
0aa28495bd7951f3437f7acd17e4df18708ad66612010793d07d3229963b6159 ./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
|
||||
e9f8da620e017a3c06363f9b3d8709d9abaee9bbac4e53e60f27db7c072bb647 ./lang/en.json
|
||||
efc35994018e526ed829ee26b3cb1c74df2ac3655433e3b9bd0bf12f087e3223 ./lang/pl.json
|
||||
f362982769d78851084392f6e4311e023e49bc7856ce197f3cc21b09dac13679 ./lang/en.json
|
||||
2a465025571229fe381888676ba144a8382dc952961cfb5b58791d2cdfa96937 ./lang/pl.json
|
||||
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
|
||||
b14233a8987e53bbbdd6770386ba10fa166ec25c1e098275c173c544b37846fc ./presets/bedroom_window_night.json
|
||||
1960841119c0b673fb2f03621b32237552fd828045304cd221401214b997c910 ./presets/device_resilience.json
|
||||
@@ -84,7 +84,7 @@ fdcd9a5055d08037278b842e7ab69265345c5811f0a06867136c511d140bb191 ./presets/wind
|
||||
804f22123cd3e8db0fac791826c8dd9f758fb866c8e3b5655fb6d25d259dccf1 ./presets/workday_comfort.json
|
||||
fd7390d64a2378352c09382accf6efff43903d952d111b22a982f06c5b2a3b4b ./regenerate-sha.sh
|
||||
c64b1c6deeb24f20af662bfce7fa3683703914987241107ce664fa2f6d1a9dcb ./scripts/README.md
|
||||
863f087740abee259d51dc5935c6b60dfe25b31a6a9fe64657d25a05d200a6fa ./scripts/api_dev_test.py
|
||||
81f0140c2a90ae97e93a1dbe0219032b4de0174f40af62e18fa8df211a876f8c ./scripts/api_dev_test.py
|
||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
||||
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh
|
||||
1d6e14e26e49aa9d3527f30a23668bf8d9c48b67e6628ef686c3155c012155de ./scripts/dev.sh
|
||||
@@ -198,10 +198,10 @@ f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/
|
||||
8c34b70fb5f80e3249c2e0c63686e60ce1697cf44a7e6f11b3ad6f689ce17792 ./src/state.rs
|
||||
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
||||
544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html
|
||||
c60667f270dc66ee463f425ca5eda676363fcd2390e5d652220f6b4af9ded6c3 ./web/css/styles.css
|
||||
5124a8a70061e35762cc9ee8646d7203c57dd7959a4a4ee818f4bc9a5fc22bda ./web/css/styles.css
|
||||
183d122ba366303d9952ac0e68aff4bced4e0d9a468fbe51316c13f15227402b ./web/custom-chart.html
|
||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||
ca887343ec089cbbb83c4ce5685edbade1266c89a29b5570c02f75c9c50b1c10 ./web/index.html
|
||||
80798cec4157be8beff6d69747270ee1842b386d07caddfd7c3c008f9662cd73 ./web/index.html
|
||||
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js-dynamic/README.md
|
||||
e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynamic/bootstrap.js
|
||||
5d57253a013fd1cb79e4b8701d4be6e394e3f2acbce73dfcd726a86e5878f1a1 ./web/js-dynamic/charts.js
|
||||
@@ -209,7 +209,7 @@ e74bb06dba646f298a4d118b5c2dca6ba24f1179ce8ea8d53c1acd6000ca332c ./web/js-dynam
|
||||
752b3c3b59558b42d867d4a15329238f0213f354b76f7011c10e31b4e6dbb311 ./web/js-dynamic/dashboard.js
|
||||
6b166a9311e30fe948bd3236189436eea769c238f963ba00acec5cdb51ff1dd7 ./web/js-dynamic/entities.js
|
||||
b4204241eecf387256364f4230b23ed50d947a362f4ceb1e528d69494e93d7cf ./web/js-dynamic/events.js
|
||||
e0f604f54eafb9a6d6ac1c6c24eb27f3e3c1e31ed80ead57ac0340c7228318b8 ./web/js-dynamic/flows.js
|
||||
99f6399c6748be7c6eb77db64367668a7d0b1f8736c27e981c77edb70b493166 ./web/js-dynamic/flows.js
|
||||
3ae714de6b71c068ba665c81a7689f994b1716284e8d937db669ad500f12cf63 ./web/js-dynamic/forms.js
|
||||
92d013bb5d64f64aa8c55380704540a5f72732bde7ccd16554965e401daf50ea ./web/js-dynamic/history.js
|
||||
511c4e7cc4c48fbbdab64558a9d1c27e6759c9b6dca255d11f301b9708e955c7 ./web/js-dynamic/main.js
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
# GREE Controller API reference
|
||||
|
||||
HTTP and WebSocket API for GREE Controller **0.15.9**.
|
||||
HTTP and WebSocket API for GREE Controller **0.15.10**.
|
||||
|
||||
[← Main documentation](../README.md)
|
||||
|
||||
@@ -241,7 +241,7 @@ Response:
|
||||
{
|
||||
"status": "ok",
|
||||
"name": "gree-controller",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.10",
|
||||
"uptime_seconds": 1234,
|
||||
"control_ready": true,
|
||||
"time": "2026-08-30T06:54:00Z"
|
||||
@@ -304,7 +304,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.9",
|
||||
"version": "0.15.10",
|
||||
"uptime_seconds": 1234,
|
||||
"auth_required": false,
|
||||
"control_ready": true,
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ Shared Flow inputs show the Flows that reference them and link directly to those
|
||||
|
||||
Presets are data files, not JavaScript definitions. Every preset lives in `presets/<id>.json` and contains `id`, `category`, localized `name`, localized `description`, and the complete `flow` graph (`nodes` + `edges`). `build.rs` validates and embeds the directory, exposes `/presets/index.json` plus `/presets/<file>.json`, and the Flow editor loads that library dynamically. Device/zone/group placeholders such as `$zone1`, `$zone2`, `$device1` and `$group1` are resolved when a preset is applied. Adding a preset therefore does not require adding graph-building code to `web/js-dynamic/flows.js`.
|
||||
|
||||
The canvas supports drag-box multi-selection, additive Shift/Ctrl/Cmd selection, `Ctrl/Cmd+A`, Select all / Clear selection toolbar actions, and dragging the whole selected group while preserving relative positions. Selected blocks can be duplicated from the toolbar or with `Ctrl/Cmd+D`; `Ctrl/Cmd+C` and `Ctrl/Cmd+V` copy/paste the selected subgraph and preserve connections whose endpoints are both selected.
|
||||
The canvas supports drag-box multi-selection, additive Shift/Ctrl/Cmd selection, `Ctrl/Cmd+A`, Select all / Clear selection toolbar actions, and dragging the whole selected group while preserving relative positions. Selected blocks can be duplicated from the toolbar or with `Ctrl/Cmd+D`; `Ctrl/Cmd+C`, `Ctrl/Cmd+X` and `Ctrl/Cmd+V` copy/cut/paste the selected subgraph and preserve connections whose endpoints are both selected. Keyboard-first authoring also supports `A`, `Insert` or `Ctrl/Cmd+K` to open block search, `Enter` to insert the first matching block, `Ctrl/Cmd+S` to save, Delete/Backspace to remove selected blocks, Escape to clear selection/cancel a pending connection, arrow keys to move the selection (Shift for 1 px precision), `Ctrl/Cmd+0` to fit, `Ctrl/Cmd++/-` to zoom and `?` to open the built-in shortcut reference.
|
||||
|
||||
## Good next stateful blocks
|
||||
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "GREE Controller API",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.10",
|
||||
"summary": "Local HTTP/WebSocket API for GREE Controller",
|
||||
"description": "Local API used by the GREE Controller Web UI and Home Assistant integration.",
|
||||
"license": {
|
||||
@@ -5967,7 +5967,7 @@
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"example": "0.15.9"
|
||||
"example": "0.15.10"
|
||||
},
|
||||
"uptime_seconds": {
|
||||
"type": "integer",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "gree_controller",
|
||||
"name": "GREE Controller",
|
||||
"version": "0.15.9",
|
||||
"version": "0.15.10",
|
||||
"config_flow": true,
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## 0.15.10
|
||||
|
||||
- Visual Flow adds keyboard-first authoring: `A`, `Insert` or `Ctrl/Cmd+K` opens block search, and `Enter` inserts the first matching block.
|
||||
- Flow editing now supports `Ctrl/Cmd+S` save, `Ctrl/Cmd+A/C/X/V/D`, Delete/Backspace, Escape, arrow-key movement, precise Shift+arrow movement, `Ctrl/Cmd+0` fit-to-view and `Ctrl/Cmd++/-` zoom.
|
||||
- The Flow editor includes an in-app keyboard shortcut reference available from the toolbar/mobile actions and with `?`.
|
||||
- Every Flow card can now export its `.flow.json` directly from the Flow list without opening the editor; the existing export endpoint/portable format is reused.
|
||||
|
||||
## 0.15.9
|
||||
|
||||
- Visual Flow `on_change` events are now latched while their upstream condition remains true and are acknowledged only after the downstream action executes successfully, so cooldowns, ownership suppression and same-cycle target conflicts no longer silently consume a change event.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Local GREE HVAC controller packaged as a Home Assistant add-on, with Web UI, HTTP/WebSocket API, built-in Swagger API documentation, UDP discovery/control, `amd64` and `aarch64` support, ingress, and database backup from `/data/gree-controller.db`.
|
||||
|
||||
Version 0.15.9 strengthens Visual Flow execution and editing with latched change events, full rolling-stat windows, clearer thermostat action summaries, box selection and multi-block copy/paste/duplicate.
|
||||
Version 0.15.10 expands Visual Flow keyboard authoring and portability with keyboard-first block creation/editing, a built-in shortcut reference and direct Flow export from the Flow list.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: "GREE Controller"
|
||||
version: "0.15.9"
|
||||
version: "0.15.10"
|
||||
slug: "gree_controller"
|
||||
description: "Local GREE HVAC controller with Web UI, API, and Home Assistant integration"
|
||||
url: "https://git.linuxiarz.pl/gru/gree-controller-ha-addon/"
|
||||
|
||||
+19
-1
@@ -1418,6 +1418,24 @@
|
||||
"discovery.addedCount": "Added: {count}",
|
||||
"discovery.alreadyAdded": "Already added",
|
||||
"discovery.empty": "No local devices found",
|
||||
"discovery.emptyHint": "Try a longer scan, another protocol, or verify that the unit answers on UDP port 7000."
|
||||
"discovery.emptyHint": "Try a longer scan, another protocol, or verify that the unit answers on UDP port 7000.",
|
||||
"flow.shortcuts": "Shortcuts",
|
||||
"flow.shortcutsHint": "Shortcuts work while focus is outside form fields.",
|
||||
"flow.shortcutAdd": "Add / search for a block",
|
||||
"flow.shortcutAddFirst": "Add the first search result",
|
||||
"flow.shortcutSave": "Save Flow",
|
||||
"flow.shortcutSelectAll": "Select all blocks",
|
||||
"flow.shortcutCopy": "Copy selected blocks",
|
||||
"flow.shortcutPaste": "Paste blocks",
|
||||
"flow.shortcutDuplicate": "Duplicate selection",
|
||||
"flow.shortcutDelete": "Delete selected blocks",
|
||||
"flow.shortcutMove": "Move selection by 10 px",
|
||||
"flow.shortcutMoveFine": "Move selection precisely by 1 px",
|
||||
"flow.shortcutClear": "Clear selection / cancel connection",
|
||||
"flow.shortcutFit": "Fit the whole Flow to the view",
|
||||
"flow.shortcutZoom": "Zoom in / out",
|
||||
"flow.shortcutHelp": "Show this shortcut list",
|
||||
"flow.shortcutCut": "Cut selected blocks",
|
||||
"flow.blocksCut": "Cut blocks: {count}"
|
||||
}
|
||||
}
|
||||
|
||||
+19
-1
@@ -1418,6 +1418,24 @@
|
||||
"discovery.addedCount": "Dodano: {count}",
|
||||
"discovery.alreadyAdded": "Już dodane",
|
||||
"discovery.empty": "Nie znaleziono urządzeń lokalnych",
|
||||
"discovery.emptyHint": "Spróbuj dłuższego skanowania, innego protokołu albo sprawdź, czy jednostka odpowiada na UDP 7000."
|
||||
"discovery.emptyHint": "Spróbuj dłuższego skanowania, innego protokołu albo sprawdź, czy jednostka odpowiada na UDP 7000.",
|
||||
"flow.shortcuts": "Skróty",
|
||||
"flow.shortcutsHint": "Skróty działają, gdy kursor nie znajduje się w polu formularza.",
|
||||
"flow.shortcutAdd": "Dodaj / wyszukaj blok",
|
||||
"flow.shortcutAddFirst": "Dodaj pierwszy wynik wyszukiwania",
|
||||
"flow.shortcutSave": "Zapisz Flow",
|
||||
"flow.shortcutSelectAll": "Zaznacz wszystkie bloki",
|
||||
"flow.shortcutCopy": "Kopiuj zaznaczone bloki",
|
||||
"flow.shortcutPaste": "Wklej bloki",
|
||||
"flow.shortcutDuplicate": "Duplikuj zaznaczenie",
|
||||
"flow.shortcutDelete": "Usuń zaznaczone bloki",
|
||||
"flow.shortcutMove": "Przesuń zaznaczenie o 10 px",
|
||||
"flow.shortcutMoveFine": "Przesuń precyzyjnie o 1 px",
|
||||
"flow.shortcutClear": "Wyczyść zaznaczenie / anuluj łączenie",
|
||||
"flow.shortcutFit": "Dopasuj cały Flow do widoku",
|
||||
"flow.shortcutZoom": "Powiększ / pomniejsz",
|
||||
"flow.shortcutHelp": "Pokaż tę listę skrótów",
|
||||
"flow.shortcutCut": "Wytnij zaznaczone bloki",
|
||||
"flow.blocksCut": "Wycięto bloki: {count}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Developer smoke/integration tests for GREE Controller API 0.15.9.
|
||||
"""Developer smoke/integration tests for GREE Controller API 0.15.10.
|
||||
|
||||
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
|
||||
@@ -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_TOKEN = os.environ.get("GREE_API_TOKEN", "")
|
||||
DEFAULT_EXPECTED_VERSION = "0.15.9"
|
||||
DEFAULT_EXPECTED_VERSION = "0.15.10"
|
||||
|
||||
SETTINGS_PATHS: dict[str, set[str]] = {
|
||||
"/api/settings/application": {"simulator_enabled"},
|
||||
@@ -599,7 +599,7 @@ def run_suite(args: argparse.Namespace) -> int:
|
||||
print(f"Settings write tests: {'ENABLED' if args.settings_write else 'disabled'}\n")
|
||||
|
||||
runner.run("public health", lambda: test_health(client, args.expected_version))
|
||||
runner.run("OpenAPI 0.15.9 contract", lambda: test_openapi(client, args.expected_version))
|
||||
runner.run("OpenAPI 0.15.10 contract", lambda: test_openapi(client, args.expected_version))
|
||||
runner.run("bootstrap snapshot contract", lambda: test_bootstrap_contract(client))
|
||||
runner.run("protected API requires auth", lambda: test_protected_auth(client))
|
||||
|
||||
|
||||
@@ -10416,3 +10416,50 @@ select.select-native-proxy[aria-invalid="true"]+.custom-select .custom-select-tr
|
||||
.flow-natural-preview .flow-preview-runtime span {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
|
||||
.flow-shortcut-dialog {
|
||||
width: min(680px, calc(100vw - 28px));
|
||||
}
|
||||
|
||||
.flow-shortcut-grid {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.flow-shortcut-grid > div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, auto) 1fr;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: 34px;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.flow-shortcut-keys {
|
||||
grid-column: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.flow-shortcut-grid > div > span:last-child {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.flow-shortcut-grid kbd {
|
||||
display: inline-block;
|
||||
min-width: 34px;
|
||||
padding: 4px 7px;
|
||||
border: 1px solid var(--line-strong, var(--line));
|
||||
border-radius: 6px;
|
||||
background: var(--surface-2);
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, .08);
|
||||
font: inherit;
|
||||
font-size: .78rem;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
+31
-1
@@ -1001,7 +1001,9 @@
|
||||
class="secondary flow-selection-action" data-action="flow-duplicate-selection"
|
||||
data-i18n="flow.duplicateSelection" disabled>Duplikuj</button><button type="button"
|
||||
class="secondary flow-selection-action" data-action="flow-clear-selection"
|
||||
data-i18n="flow.clearSelection">Wyczyść zaznaczenie</button><span id="flowSelectionCount"
|
||||
data-i18n="flow.clearSelection">Wyczyść zaznaczenie</button><button type="button"
|
||||
class="secondary flow-selection-action" data-action="flow-shortcuts"
|
||||
data-i18n="flow.shortcuts">Skróty</button><span id="flowSelectionCount"
|
||||
class="badge"></span></div>
|
||||
<div class="flow-zoom-controls" role="group" data-i18n-aria="flow.zoomControls" aria-label="Canvas zoom">
|
||||
<button type="button" class="secondary icon-button" data-action="flow-zoom-out"
|
||||
@@ -1043,6 +1045,32 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<dialog id="flowShortcutDialog" class="sheet flow-shortcut-dialog">
|
||||
<div class="dialog-head">
|
||||
<div><span class="eyebrow" data-i18n="flow.editorEyebrow">Flow editor</span>
|
||||
<h2 data-i18n="flow.shortcuts">Skróty klawiaturowe</h2>
|
||||
</div><button type="button" data-close data-ui-icon="close"></button>
|
||||
</div>
|
||||
<p class="field-note" data-i18n="flow.shortcutsHint">Skróty działają, gdy kursor nie znajduje się w polu formularza.</p>
|
||||
<div class="flow-shortcut-grid">
|
||||
<div><span class="flow-shortcut-keys"><kbd>A</kbd><kbd>Insert</kbd><kbd>Ctrl/Cmd+K</kbd></span><span data-i18n="flow.shortcutAdd">Dodaj / wyszukaj blok</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Enter</kbd></span><span data-i18n="flow.shortcutAddFirst">Dodaj pierwszy wynik wyszukiwania</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Ctrl/Cmd+S</kbd></span><span data-i18n="flow.shortcutSave">Zapisz Flow</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Ctrl/Cmd+A</kbd></span><span data-i18n="flow.shortcutSelectAll">Zaznacz wszystkie bloki</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Ctrl/Cmd+C</kbd></span><span data-i18n="flow.shortcutCopy">Kopiuj zaznaczone bloki</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Ctrl/Cmd+X</kbd></span><span data-i18n="flow.shortcutCut">Wytnij zaznaczone bloki</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Ctrl/Cmd+V</kbd></span><span data-i18n="flow.shortcutPaste">Wklej bloki</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Ctrl/Cmd+D</kbd></span><span data-i18n="flow.shortcutDuplicate">Duplikuj zaznaczenie</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Delete</kbd><kbd>Backspace</kbd></span><span data-i18n="flow.shortcutDelete">Usuń zaznaczone bloki</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>← ↑ ↓ →</kbd></span><span data-i18n="flow.shortcutMove">Przesuń zaznaczenie o 10 px</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Shift+← ↑ ↓ →</kbd></span><span data-i18n="flow.shortcutMoveFine">Przesuń precyzyjnie o 1 px</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Esc</kbd></span><span data-i18n="flow.shortcutClear">Wyczyść zaznaczenie / anuluj łączenie</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Ctrl/Cmd+0</kbd></span><span data-i18n="flow.shortcutFit">Dopasuj cały Flow do widoku</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>Ctrl/Cmd++ / -</kbd></span><span data-i18n="flow.shortcutZoom">Powiększ / pomniejsz</span></div>
|
||||
<div><span class="flow-shortcut-keys"><kbd>?</kbd></span><span data-i18n="flow.shortcutHelp">Pokaż tę listę skrótów</span></div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog id="flowBlockDialog" class="sheet flow-block-dialog">
|
||||
<div class="dialog-head">
|
||||
<div><span class="eyebrow" data-i18n="flow.blocks">Blocks</span>
|
||||
@@ -1069,6 +1097,8 @@
|
||||
data-ui-icon="chevron-right" aria-hidden="true"></span></button>
|
||||
<button type="button" data-action="flow-logs"><span data-i18n="flow.logs">Logs</span><span
|
||||
data-ui-icon="chevron-right" aria-hidden="true"></span></button>
|
||||
<button type="button" data-action="flow-shortcuts"><span data-i18n="flow.shortcuts">Shortcuts</span><span
|
||||
data-ui-icon="chevron-right" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
|
||||
+78
-8
@@ -110,7 +110,7 @@ function renderFlows() {
|
||||
host.innerHTML = app.flows.length ? app.flows.map(flow => `<article class="list-card flow-card ${flow.enabled && !flow.draft ? '' : 'is-disabled'} ${flow.draft ? 'is-draft' : ''}" data-flow-card-id="${esc(flow.id)}" tabindex="0" aria-label="${esc(`${tr('flow.openEditor')}: ${flow.name}`)}">
|
||||
<div class="list-card-head"><div><h3>${esc(flow.name)}${flow.draft ? ` <span class="badge flow-draft-badge">${esc(tr('flow.draft'))}</span>` : ''}</h3><p>${esc(flow.description || tr('flow.defaultDescription'))}</p></div>${flow.draft ? `<button type="button" class="enable-toggle" disabled title="${esc(tr('flow.draftDisabledHint'))}"><span>${uiIcon('edit')}</span>${esc(tr('flow.disabledState'))}</button>` : `<button type="button" class="enable-toggle ${flow.enabled ? 'active' : ''}" data-action="toggle-flow-enabled" data-id="${esc(flow.id)}" data-value="${flow.enabled ? 'false' : 'true'}" aria-label="${esc(tr(flow.enabled ? 'flow.disable' : 'flow.enable'))}" title="${esc(tr('flow.quickToggleHint'))}"><span>${uiIcon(flow.enabled ? 'check' : 'circle')}</span>${esc(flow.enabled ? tr('flow.enabledState') : tr('flow.disabledState'))}</button>`}</div>
|
||||
<div class="card-stats"><div class="card-stat"><small>${esc(tr('flow.blocks'))}</small><strong>${flow.nodes?.length || 0}</strong></div><div class="card-stat"><small>${esc(tr('nav.schedules'))}</small><strong>${flow.compiled_schedule_ids?.length || 0}</strong></div><div class="card-stat"><small>${esc(tr('nav.automations'))}</small><strong>${flow.compiled_automation_ids?.length || 0}</strong></div></div>
|
||||
<div class="card-footer"><small>${esc(flow.draft ? tr('flow.draftNoExecution') : tr('flow.compileCount', { schedules: flow.compiled_schedule_ids?.length || 0, automations: flow.compiled_automation_ids?.length || 0 }))}</small><div class="card-menu"><button class="card-primary-action" data-action="edit-flow" data-id="${esc(flow.id)}">${esc(tr('flow.openEditor'))}</button><details class="card-overflow"><summary aria-label="${esc(tr('actions.more'))}" title="${esc(tr('actions.more'))}">${uiIcon('more-horizontal')}</summary><div class="card-overflow-menu"><button class="danger" data-action="delete-flow" data-id="${esc(flow.id)}">${esc(tr('actions.delete'))}</button></div></details></div></div>
|
||||
<div class="card-footer"><small>${esc(flow.draft ? tr('flow.draftNoExecution') : tr('flow.compileCount', { schedules: flow.compiled_schedule_ids?.length || 0, automations: flow.compiled_automation_ids?.length || 0 }))}</small><div class="card-menu"><button class="card-primary-action" data-action="edit-flow" data-id="${esc(flow.id)}">${esc(tr('flow.openEditor'))}</button><details class="card-overflow"><summary aria-label="${esc(tr('actions.more'))}" title="${esc(tr('actions.more'))}">${uiIcon('more-horizontal')}</summary><div class="card-overflow-menu"><button data-action="export-flow-by-id" data-id="${esc(flow.id)}">${esc(tr('flow.export'))}</button><button class="danger" data-action="delete-flow" data-id="${esc(flow.id)}">${esc(tr('actions.delete'))}</button></div></details></div></div>
|
||||
</article>`).join('') : `<div class="empty"><strong>${esc(tr('flow.emptyTitle'))}</strong>${esc(tr('flow.emptyText'))}</div>`;
|
||||
}
|
||||
|
||||
@@ -757,14 +757,28 @@ function downloadJsonFile(filename, data) {
|
||||
setTimeout(() => URL.revokeObjectURL(url), 0);
|
||||
}
|
||||
|
||||
function flowExportFilename(name) {
|
||||
const safe = String(name || 'flow').replace(/[^a-z0-9_-]+/gi, '-').replace(/^-+|-+$/g, '').toLowerCase() || 'flow';
|
||||
return `${safe}.flow.json`;
|
||||
}
|
||||
|
||||
async function exportFlowById(id) {
|
||||
if (!id) return;
|
||||
try {
|
||||
const flow = app.flows.find(item => item.id === id);
|
||||
const envelope = await api(`/api/flows/${encodeURIComponent(id)}/export`);
|
||||
downloadJsonFile(flowExportFilename(flow?.name || envelope?.flow?.name), envelope);
|
||||
toast(tr('flow.exported'));
|
||||
} catch (error) { toast(error.message, true); }
|
||||
}
|
||||
|
||||
async function exportFlow() {
|
||||
if (!app.flowDraft) return;
|
||||
try {
|
||||
const envelope = app.flowDraft.id && !app.flowDirty
|
||||
? await api(`/api/flows/${encodeURIComponent(app.flowDraft.id)}/export`)
|
||||
: { format: 'gree-controller-flow', version: 1, exported_at: new Date().toISOString(), shared_inputs: flowExportSharedInputs(), flow: flowSourcePayload() };
|
||||
const safe = (flowSourcePayload()?.name || 'flow').replace(/[^a-z0-9_-]+/gi, '-').replace(/^-+|-+$/g, '').toLowerCase() || 'flow';
|
||||
downloadJsonFile(`${safe}.flow.json`, envelope); toast(tr('flow.exported'));
|
||||
downloadJsonFile(flowExportFilename(flowSourcePayload()?.name), envelope); toast(tr('flow.exported'));
|
||||
} catch (error) { toast(error.message, true); }
|
||||
}
|
||||
|
||||
@@ -1250,6 +1264,14 @@ function duplicateSelectedFlowNodes() {
|
||||
if (pasteFlowNodes({ notify: false })) toast(tr('flow.blocksDuplicated', { count }));
|
||||
}
|
||||
|
||||
function cutSelectedFlowNodes() {
|
||||
const count = (app.flowSelectedNodeIds || []).length;
|
||||
if (!count || !copySelectedFlowNodes({ notify: false })) return false;
|
||||
removeSelectedFlowNodes();
|
||||
toast(tr('flow.blocksCut', { count }));
|
||||
return true;
|
||||
}
|
||||
|
||||
function removeSelectedFlowNodes() {
|
||||
if (!app.flowDraft || !(app.flowSelectedNodeIds || []).length) return;
|
||||
const selected = new Set(app.flowSelectedNodeIds);
|
||||
@@ -1259,6 +1281,24 @@ function removeSelectedFlowNodes() {
|
||||
app.flowSelectedNodeIds = []; app.flowSelectedNodeId = null; app.flowDirty = true; renderFlowEditor();
|
||||
}
|
||||
|
||||
function nudgeSelectedFlowNodes(dx, dy) {
|
||||
if (!app.flowDraft || !(app.flowSelectedNodeIds || []).length) return false;
|
||||
const selected = new Set(app.flowSelectedNodeIds);
|
||||
app.flowDraft.nodes.forEach(node => {
|
||||
if (!selected.has(node.id)) return;
|
||||
node.x = Math.max(12, Number(node.x || 0) + dx);
|
||||
node.y = Math.max(12, Number(node.y || 0) + dy);
|
||||
});
|
||||
app.flowDirty = true;
|
||||
renderFlowEditor();
|
||||
return true;
|
||||
}
|
||||
|
||||
function openFlowShortcuts() {
|
||||
const dialog = $('#flowShortcutDialog');
|
||||
if (dialog && !dialog.open) dialog.showModal();
|
||||
}
|
||||
|
||||
function updateFlowConfig(input) {
|
||||
const node = flowNodeById(app.flowSelectedNodeId); if (!node) return;
|
||||
const key = input.dataset.flowConfig; if (!key) return;
|
||||
@@ -1428,11 +1468,33 @@ document.addEventListener('keydown', event => {
|
||||
if (flowCard && event.target === flowCard && (event.key === 'Enter' || event.key === ' ')) { event.preventDefault(); openFlowEditor(flowCard.dataset.flowCardId); return; }
|
||||
if ($('#flowEditor')?.hidden || !app.flowDraft) return;
|
||||
if (event.target.closest?.('input,select,textarea,[contenteditable="true"]')) return;
|
||||
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'a') { event.preventDefault(); selectAllFlowNodes(); return; }
|
||||
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'c') { event.preventDefault(); copySelectedFlowNodes(); return; }
|
||||
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'v') { event.preventDefault(); pasteFlowNodes(); return; }
|
||||
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'd') { event.preventDefault(); duplicateSelectedFlowNodes(); return; }
|
||||
if ((event.key === 'Delete' || event.key === 'Backspace') && (app.flowSelectedNodeIds || []).length) { event.preventDefault(); removeSelectedFlowNodes(); }
|
||||
if (event.target.closest?.('dialog[open]') || document.querySelector('dialog[open]')) return;
|
||||
|
||||
const modifier = event.ctrlKey || event.metaKey;
|
||||
const key = event.key.toLowerCase();
|
||||
if (modifier && key === 's') { event.preventDefault(); saveFlow(); return; }
|
||||
if (modifier && key === 'k') { event.preventDefault(); openFlowBlockLibrary(); return; }
|
||||
if (modifier && key === 'a') { event.preventDefault(); selectAllFlowNodes(); return; }
|
||||
if (modifier && key === 'c') { event.preventDefault(); copySelectedFlowNodes(); return; }
|
||||
if (modifier && key === 'x') { event.preventDefault(); cutSelectedFlowNodes(); return; }
|
||||
if (modifier && key === 'v') { event.preventDefault(); pasteFlowNodes(); return; }
|
||||
if (modifier && key === 'd') { event.preventDefault(); duplicateSelectedFlowNodes(); return; }
|
||||
if (modifier && event.key === '0') { event.preventDefault(); fitFlowToView(); return; }
|
||||
if (modifier && (event.key === '=' || event.key === '+')) { event.preventDefault(); setFlowZoom((app.flowZoom || 1) + .1); return; }
|
||||
if (modifier && event.key === '-') { event.preventDefault(); setFlowZoom((app.flowZoom || 1) - .1); return; }
|
||||
if (!modifier && !event.altKey && (key === 'a' || event.key === 'Insert')) { event.preventDefault(); openFlowBlockLibrary(); return; }
|
||||
if (!modifier && event.key === '?') { event.preventDefault(); openFlowShortcuts(); return; }
|
||||
if (event.key === 'Escape' && ((app.flowSelectedNodeIds || []).length || app.flowConnectFrom)) {
|
||||
event.preventDefault(); app.flowConnectFrom = null; clearFlowSelection(); return;
|
||||
}
|
||||
if ((event.key === 'Delete' || event.key === 'Backspace') && (app.flowSelectedNodeIds || []).length) { event.preventDefault(); removeSelectedFlowNodes(); return; }
|
||||
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key) && (app.flowSelectedNodeIds || []).length) {
|
||||
event.preventDefault();
|
||||
const step = event.shiftKey ? 1 : 10;
|
||||
const dx = event.key === 'ArrowLeft' ? -step : event.key === 'ArrowRight' ? step : 0;
|
||||
const dy = event.key === 'ArrowUp' ? -step : event.key === 'ArrowDown' ? step : 0;
|
||||
nudgeSelectedFlowNodes(dx, dy);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', event => {
|
||||
@@ -1454,6 +1516,7 @@ document.addEventListener('click', event => {
|
||||
if (action === 'new-flow') openFlowEditor();
|
||||
else if (action === 'edit-flow') openFlowEditor(actionButton.dataset.id);
|
||||
else if (action === 'delete-flow') deleteFlow(actionButton.dataset.id);
|
||||
else if (action === 'export-flow-by-id') exportFlowById(actionButton.dataset.id);
|
||||
else if (action === 'toggle-flow-enabled') toggleFlowEnabled(actionButton.dataset.id, actionButton.dataset.value === 'true');
|
||||
else if (action === 'save-flow') saveFlow();
|
||||
else if (action === 'flow-mobile-actions') $('#flowEditorActionsDialog')?.showModal();
|
||||
@@ -1476,6 +1539,7 @@ document.addEventListener('click', event => {
|
||||
else if (action === 'flow-select-all') selectAllFlowNodes();
|
||||
else if (action === 'flow-duplicate-selection') duplicateSelectedFlowNodes();
|
||||
else if (action === 'flow-clear-selection') clearFlowSelection();
|
||||
else if (action === 'flow-shortcuts') openFlowShortcuts();
|
||||
else if (action === 'edit-flow-name') editFlowName();
|
||||
else if (action === 'flow-shared-input-settings') {
|
||||
if (!showView('homeassistant')) return;
|
||||
@@ -1495,6 +1559,12 @@ document.addEventListener('change', event => {
|
||||
$('#flowImportFile')?.addEventListener('change', event => importFlowFile(event.target.files?.[0]));
|
||||
$('#flowTemplateSearch')?.addEventListener('input', event => { flowPresetSearch = event.target.value || ''; renderFlowPresetBrowser(flowPresetActiveCategory); });
|
||||
$('#flowBlockSearch')?.addEventListener('input', event => renderFlowBlockLibrary(event.target.value));
|
||||
$('#flowBlockSearch')?.addEventListener('keydown', event => {
|
||||
if (event.key !== 'Enter') return;
|
||||
const first = $('#flowBlockLibrary [data-flow-add]');
|
||||
if (!first) return;
|
||||
event.preventDefault(); first.click();
|
||||
});
|
||||
$('#flowName')?.addEventListener('input', () => { if (app.flowDraft) { app.flowDirty = true; renderFlowSaveStatus(); } });
|
||||
$('#flowName')?.addEventListener('keydown', event => {
|
||||
if (event.key === 'Enter') { event.preventDefault(); commitFlowName(); }
|
||||
|
||||
Reference in New Issue
Block a user