v0.9.1
This commit is contained in:
Generated
+1
-1
@@ -633,7 +633,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gree-controller"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-gcm",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "gree-controller"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
edition = "2021"
|
||||
authors = ["GREE Controller contributors"]
|
||||
description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support"
|
||||
|
||||
+19
-19
@@ -1,11 +1,11 @@
|
||||
b38e05f8a00969270280b0062ee129fccee39ae9c11cb614a6ad7b2b7ae2dfc7 ./.env.example
|
||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||
71363f41de7b4cd53b99e5c97fc6c6f8d5c1065f8fa4cbd02496c928d31be195 ./Cargo.lock
|
||||
c68db6fcbaea3aee0864ed78d12d84aa6e669f1865666d97aca7ac530918d75e ./Cargo.toml
|
||||
d16e2c4867f9e651053bbbb080927134e7e72df96ca1b26f62f7818f0a1f7f09 ./Cargo.lock
|
||||
c180bd9e6cdd1db88d195237ccce4e8a8bef44888468e0fb1afd3b5e5dbefb6f ./Cargo.toml
|
||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||
0d465ef4bc80d52bbca3357b57d61d18724ca322e930cec47274a61a9ffd5d44 ./README.md
|
||||
57ed2504d5924581030d74ca4899813532165d08f6317704ee2f2002d4cc8ad0 ./README.md
|
||||
ec0f495ee6a618bdec10b260ee7838f7a872d8c5b0aa91c546226c1ad818f601 ./build.rs
|
||||
c5e8a97b7b35ff6c14ec3e36aca6a88a02d4791878fffb8f498fd141d65ecc9a ./docs/API.md
|
||||
027a96e4254e45305f5f573d3562920d4f253cba32bcd2c96b15eaaae94b3cfd ./docs/API.md
|
||||
a4571b7d31c96be8b3f540574444742f99a208fc0cdf27a14bebad70bc916ab3 ./home-assistant/README.md
|
||||
f8e8559fe10fe523ac5bc9aac25c6e26e862f679d502e8f3c39f38a0a8e40911 ./home-assistant/custom_components/gree_controller/__init__.py
|
||||
6910589f27960a28d4de9735884a7e5376e455cd885f19ce2b55947fcd135114 ./home-assistant/custom_components/gree_controller/api.py
|
||||
@@ -16,7 +16,7 @@ ab08fbe40e9bb48ebcbfff98760aaa0c9434b61b705aaaadea0c09c255d99b0e ./home-assista
|
||||
5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./home-assistant/custom_components/gree_controller/entity_map.py
|
||||
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./home-assistant/custom_components/gree_controller/icon.png
|
||||
c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./home-assistant/custom_components/gree_controller/logo.png
|
||||
b13f95ff5b6d2fbf884142360c907ef4e26b44d5097d596624e16b1b554be05d ./home-assistant/custom_components/gree_controller/manifest.json
|
||||
01df8a32e3159ce761f395b859e4b00f0485c8cf8b37cfdc844dbdf979268bce ./home-assistant/custom_components/gree_controller/manifest.json
|
||||
38d659d0273c0d1428679e44574a2666c605d1b3796d323b626baa40560788cf ./home-assistant/custom_components/gree_controller/number.py
|
||||
39c4309001b75abb56234f05662bc06e077054986876f1927937edbce528ec95 ./home-assistant/custom_components/gree_controller/select.py
|
||||
1cae12876eb81085910907cf1fcf340a18dcba4f8c967ef222c9496325057849 ./home-assistant/custom_components/gree_controller/sensor.py
|
||||
@@ -25,8 +25,8 @@ b13f95ff5b6d2fbf884142360c907ef4e26b44d5097d596624e16b1b554be05d ./home-assista
|
||||
13f30e2dcdcedbd1b6c3f99c2335e0487108fd72c8e86922368b84f2fa2038ae ./home-assistant/custom_components/gree_controller/translations/pl.json
|
||||
4513070521d3dda0efb0d974a86ba674494cfb2b66fe9e5cac5b1b0430dede97 ./home-assistant/generated/gree_controller_entities.example.json
|
||||
253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md
|
||||
953f215e80df7896a6b5d1da8c00d8e8d954f7c8aaed488e846cbd461f688cfc ./lang/en.json
|
||||
b0baaff08e70757841c238c5f689fb054774608ef70a4d43f75cb2ca66cd53d2 ./lang/pl.json
|
||||
09f9b9184f09ae8439368299ae2922a04738266fa261240bcc74adba549522db ./lang/en.json
|
||||
0d566784bb9c8f0a4420f24ffd86946919d7075ce867d0d78606144c26945559 ./lang/pl.json
|
||||
d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py
|
||||
bb89bac237e750e9b1bf73761d7df97a6b81853091615878c03f13d7b6399aa7 ./scripts/README.md
|
||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
||||
@@ -39,7 +39,6 @@ e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./scripts/netw
|
||||
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./scripts/service.sh
|
||||
b48fc84d79aab381226363ac8473f981bcba5e4911c4cc0011261182debf4250 ./scripts/smoke.sh
|
||||
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./scripts/update.sh
|
||||
4c8b0d10d83d6130ed6e2703c233ac380b25a6c57fc23a29821f823df90331f9 ./src/api.rs
|
||||
50b653f5ae1c9d24eb2e158fb4af713f5ccabe259c83053fa3cd2a310c7780da ./src/api/assets.rs
|
||||
6c34b1294d76b0eba4c56bfd0bf69bdd502de0e40838104f6ddf2db4e922683d ./src/api/auth.rs
|
||||
08a732a445f44d34c644109762ac389b58c96d0fe47b309ce34f4479d67c3712 ./src/api/automations.rs
|
||||
@@ -57,8 +56,8 @@ d3ddc310a4033e400f521babec02ec06990092d7349c40cd271a3d8406f70f4a ./src/api/publ
|
||||
74c156717b37e83d329f7c52ac622e02f690621544c4eb07c4502194db55e6e9 ./src/api/system.rs
|
||||
681cccb8d09f4ad9c2e6467a4dbe2f9d991125775a5d2bfc9043daf940fecd29 ./src/api/websocket.rs
|
||||
9d8ab056dbd2047fd00062e7252c3e9bb972c988a56de23daea61b8be871bd61 ./src/api/zones.rs
|
||||
4c8b0d10d83d6130ed6e2703c233ac380b25a6c57fc23a29821f823df90331f9 ./src/api.rs
|
||||
2b6eecead75cd4cdb98a9f74dd1c374dbf11ebfa716abbc3476aa12398af9acd ./src/config.rs
|
||||
369455b5ffb125f1a80b767f62fd8c84a953adb69548dbd8adb653a6fbaaa45b ./src/db.rs
|
||||
24580b279cfcd5388ba584eaba2c9bb729b8aae81e77b781c23fb804c5b5fa5d ./src/db/climate.rs
|
||||
3de3fa4262be00029dde2344c4cfa41acd4acc0adec176f344761ff35ea730b6 ./src/db/configuration.rs
|
||||
56b0efe84dbf6bd3ee2397b099220a68966311c187f4d5b53007a1693a5ef327 ./src/db/core_devices.rs
|
||||
@@ -68,7 +67,7 @@ f3bd91d0bdb7699f319b9341d928d3cc3b25f759653fcd2095a5c5f695d08fac ./src/db/event
|
||||
7e2fbce70f40aef360534ba38c6700163db77cb7288b3bf58728fce72a126435 ./src/db/schedules_automations.rs
|
||||
35cbb521a8ec456756496b8aee71f9dfebad528a2cf2653ba8b48f6035be0c01 ./src/db/tests.rs
|
||||
a384ea4042d1d11692b5f8693df1f54850dcc4e80726780b44b2b096bf667b78 ./src/db/zone_history.rs
|
||||
952ea5906d39ef917ad71edc345f76b29c07c63079644fd5699b12aa254cb35f ./src/engine.rs
|
||||
369455b5ffb125f1a80b767f62fd8c84a953adb69548dbd8adb653a6fbaaa45b ./src/db.rs
|
||||
d418f27edde4a8a8ea89817899abfc48df632ec581deacf64db1dc409eef920f ./src/engine/automations.rs
|
||||
910248f30ac9d697816458cd393c5f7d4bbdd3a12e48cb41cbb3ddb68a1ce395 ./src/engine/commands.rs
|
||||
d91c50e68baa8f90a30fd94be1fda8cf548ed7386c1058f14f8d5b32625c0b25 ./src/engine/control_plan.rs
|
||||
@@ -86,14 +85,14 @@ a9188b588b2617ce1547a45d46141f7e5f1bb0027a48675dd86b274f807d0875 ./src/engine/t
|
||||
3fe9825d9ff5f41c9f988489ebe606b4700322e553cbe6cae1bec7c82affd25c ./src/engine/tests.rs
|
||||
6f4541bad148d172a44cd3ddd22b49dc97e4b755554a7a8820222169090e7022 ./src/engine/zone_actions.rs
|
||||
856bd291229ae9d0b3f21fb7fd47058b7af5a18bd195e48829f8162533613472 ./src/engine/zone_control.rs
|
||||
952ea5906d39ef917ad71edc345f76b29c07c63079644fd5699b12aa254cb35f ./src/engine.rs
|
||||
4b271b6fc365b1078c01d6178eb563841b2ecaed5d8639196f58e1312d2236fe ./src/error.rs
|
||||
c6ff66da9ad08506f839ec56a869ead3abc824b5311e0ea9244517d11f7f4207 ./src/home_assistant.rs
|
||||
6f9ef85cd53ba030b477bc3c186f0f7e762674124210eabcb0686f2806eaada1 ./src/influxdb.rs
|
||||
feb50ecd60b9a9fe9de1f7d8e1e26caab5435d59347468f6266a318f431bc3d3 ./src/influxdb/codec.rs
|
||||
553b44ec0321e62480059bc2451fd34ec7aabe9fc87fc717daba0c4b55679d53 ./src/influxdb/query.rs
|
||||
5e4d94f8a010df05bae9acab72c518080d2dba94d054df95b87e492305357222 ./src/influxdb/write.rs
|
||||
6f9ef85cd53ba030b477bc3c186f0f7e762674124210eabcb0686f2806eaada1 ./src/influxdb.rs
|
||||
c3ff7232a2fa1d29428872a10cfd72ee60a3a1faaa662eff6c2d8c7dffc1ed3a ./src/main.rs
|
||||
e935e1fcf0d379b627df5d5055dbdad3dd55786fde7191376918b05afdde1727 ./src/models.rs
|
||||
fca124ec30b5c9286f0f44cc18e8953bb2809f5009d4d521deaebfdeb00c1bd1 ./src/models/automation.rs
|
||||
60ff96965cf9531b3964add783a6a45578aa1986844e99a239f182eb4f962d5b ./src/models/control_plan.rs
|
||||
8228b9b6d9e8b1d13de29ce88500beacd88c8877893b7a3e41a4bd1e237ca36c ./src/models/defaults.rs
|
||||
@@ -103,9 +102,9 @@ cfbaa853a5185c4be572588577b313a2d618837f7c07a3860d0e8d2cca04ee60 ./src/models/h
|
||||
b1af7f8d4c3607b1c0ae978feaab4dfa03faa0154c7328712bb4eeafc27faa02 ./src/models/runtime.rs
|
||||
e77f8cdcb5fc0e51cdbf2527a69585a2f6f9089e159d1e45ec4a48735ab0e304 ./src/models/temporary_thermostat.rs
|
||||
0bdc68fc1b3d1fd4d1e1253bd48b5c149cfc9b87e2fedb13288b3ea8a096af3d ./src/models/zone.rs
|
||||
e935e1fcf0d379b627df5d5055dbdad3dd55786fde7191376918b05afdde1727 ./src/models.rs
|
||||
9c735d7c475f21f2f388c21be784376f7184f0bf18af1886544ef51585a3b290 ./src/notifications.rs
|
||||
7fc31fbf8841a073a1544b8c7a6390f1a15b56087486ca0596a8418340fa232a ./src/protocol/crypto.rs
|
||||
f1f765331469f4219a551414833c19b6de413114c22fdd0468e1f417659553c9 ./src/protocol/gree.rs
|
||||
c935e441933ba8709432cc3ac33ed868043a89188d0f48ea6fbfe34b1f8936a3 ./src/protocol/gree/binding.rs
|
||||
563ded1758b135eb523f1fa0b1bf55fe5430ef55935b301fd6d79cbb980e1820 ./src/protocol/gree/commands.rs
|
||||
5d4328199e2285016fd1941e909e5c09f7cdf9fd21ab6538c1bf8bc2f4af3ba3 ./src/protocol/gree/core.rs
|
||||
@@ -115,35 +114,36 @@ a96061fbbcadac6c2df2e9708a48dcf819b951eb147f32c7d0d285ac5ae2491b ./src/protocol
|
||||
45d0cd0ea40b9255bd811362547f5a667dac6bc7324074726130baf60aa41d2b ./src/protocol/gree/polling.rs
|
||||
35fc903404045129e3046b7997e941b88f98ceb42485cccc872c2e4aa530c75b ./src/protocol/gree/tests.rs
|
||||
55c2d615e76c9e4b08c4067ea734e81d81915144e637f3b51c19bea4fc879137 ./src/protocol/gree/transport.rs
|
||||
f1f765331469f4219a551414833c19b6de413114c22fdd0468e1f417659553c9 ./src/protocol/gree.rs
|
||||
a910bd9432a393740c0f6fab52bfcb551f0ea756718d66d290fd2610767cf07c ./src/protocol/mod.rs
|
||||
830621fa75265da7288b8135704c0ce27e0687f87d55abf3cdbe40a6e2f3d2b1 ./src/queries.rs
|
||||
7fac65a9dcbcecee09816f5447c7792dc94f736b1efb66ac85629db75f6280be ./src/queries/device_history.rs
|
||||
cd5d0befe9f51a7067201a6eaa8d8ed72182d359e42cbdc67c80aa12d722654b ./src/queries/entities.rs
|
||||
2c5fba462b72158b04dbaaa53c8536263f8d72a1338a6c4ff06cb28a36f6ae27 ./src/queries/ha_history.rs
|
||||
2b46a8cdc5efb5f930dd8ff7b9f6c0e720cbca096a51d9ae047711dd280fa3b8 ./src/queries/maintenance.rs
|
||||
f688db6f440ee7b0129ccc22838de0ee0e0cf2b6376173b05f853ca2555c2c1e ./src/queries/schema.rs
|
||||
f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/zone_history.rs
|
||||
830621fa75265da7288b8135704c0ce27e0687f87d55abf3cdbe40a6e2f3d2b1 ./src/queries.rs
|
||||
6ee23fcaa62231d15006db078d4f763d1b67d2365fcc2d736d8a7da8df5ea382 ./src/state.rs
|
||||
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
||||
de66cab9b650cb448814bfeda6d216e168dd3e6f07a1a438e4a9de9acbe6b798 ./web/404.html
|
||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||
3c2a8f09e64a7bc3002aee713a0edfb1441e95711de5fb60b9d177e8014192a4 ./web/index.html
|
||||
2f29bd02e3a74089b28621978882b1191e26d7ec612e1ccd306b65f204b68e99 ./web/index.html
|
||||
9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js/README.md
|
||||
b3d5cb473bf9771de78e14c7de730cb86c73f8cdbb6344da67ba7d6abac879ea ./web/js/bootstrap.js
|
||||
64a43e2142a69ebddf4160c0d0d3b573fa379c650431090fd15c9ba3b62bc4e7 ./web/js/charts.js
|
||||
cf8d97770985b8cf920382725fa33d5f61497a639544bfc528af72b276de0b6a ./web/js/core.js
|
||||
6e421388c7f6deb2aa42f1205dfdc52839f9f178fbd672129972528b8517a870 ./web/js/dashboard.js
|
||||
2ce40c25e2e0bf4dbdd77f22155fb9ed5f64a7f866dd2fe40df188cdb38183d8 ./web/js/entities.js
|
||||
77fbf1ad0e0f12a5ca8ea65716fb993cba07bdfe03cd6d43966ae79efb971908 ./web/js/events.js
|
||||
1bc7b2c4ab81b9942dd04b8b445de087d439f56c8dac3531fa4a62b8287d4a60 ./web/js/entities.js
|
||||
e448b48ead4b63ca4c498f2743c571876789a26fd4233cb63b00cbf2e84ca4ad ./web/js/events.js
|
||||
2fe6c804f86b0613a50a56c54f7a0cbee77273759edf3e6fec36ac77d35edbd7 ./web/js/forms.js
|
||||
be07a341ba4b1f514a5e13553494194b22aa87108cb04de40635a0158921780b ./web/js/history.js
|
||||
6d4a9f17ff241eaf44e8d2260163d9226fca3c8d6e1a287d31a2c96b976532f9 ./web/js/main.js
|
||||
9aa199a9ca8d1109cc06d7a7d69f5c5ed45c53f488128ca520258e9c91c31352 ./web/js/main.js
|
||||
e27d5bb4a1579fd5b1d9762a5067b953004ca8ae63506af4e8c2cad754c0c532 ./web/js/navigation.js
|
||||
935a9282f8c7a0e1405752931607751cb0d314eabca74e3f1e0b5f21e1b93988 ./web/js/realtime.js
|
||||
45a666dbc1cddcc0ece507130cde7719f6232c710b4288d6a16e1232791f2a3d ./web/js/router.js
|
||||
f9d23d7142ebcd77085ad30b8b5bf927b754b51f10976309d5b0a3f22f3cfafb ./web/js/settings-ui.js
|
||||
a189f4f7cc253087ac271b3c5b7a82751176032fc0f7040f073cdc451d354e06 ./web/js/settings.js
|
||||
fd26156e9f1d6713d3def564ad000553d9a16a24376059701db2ee762c99ee6c ./web/manifest.webmanifest
|
||||
7e52b34a45d70714659e18aa92839b0059d67cbbeb2774ee3a59d452421a6139 ./web/styles.css
|
||||
a447e27f1c6f5cd126e4ea08781603afaca0877c495bc38095e192618efbd942 ./web/styles.css
|
||||
bc95830ec8273818ffde0a6b50eb3cc9ecbeee65a7a1d3aeff08cde86d05dc7b ./web/sw.js
|
||||
d505d793ce7cc9485b45b78bba1c0d51887adc7451ab59a42702946e5b991382 ./web/theme-init.js
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Self-hosted controller for GREE-compatible air conditioners with a local Web UI, thermostat zones, schedules, Home Assistant integration, history, notifications and a documented HTTP/WebSocket API.
|
||||
|
||||
**Current release: 0.9.0**
|
||||
**Current release: 0.9.1**
|
||||
|
||||
> [Full API reference](docs/API.md) — authentication, every endpoint, request bodies, response models, WebSocket events and examples.
|
||||
|
||||
@@ -181,7 +181,7 @@ A **Zone** is the thermostat owner for a device. It decides demand from room tem
|
||||
|
||||
### Compressor protection
|
||||
|
||||
Controller-side compressor protection is enabled by default and uses a 3-minute window. The switch and duration are available under **Settings → GREE**. Thermostat, group and whole-house starts or Heat/Cool reversals that fall inside the window are kept as visible pending tasks instead of being discarded. The pending task is shown on both the thermostat and its device card, can be cancelled individually, and all pending tasks can be cancelled from the Thermostats header. Safety OFF commands remain immediate.
|
||||
Controller-side compressor protection is enabled by default and uses a 3-minute window. The switch and duration are available under **Settings → GREE**. Thermostat, group and whole-house starts or Heat/Cool reversals that fall inside the window are kept as visible pending tasks instead of being discarded. The pending task is shown on both the thermostat and its device card. A permanent **Queue** button above Thermostats opens a global modal sorted by execution time, with device/zone, command, owner, protection deadline, live countdown, per-task cancellation and **Cancel all**. Safety OFF commands remain immediate.
|
||||
|
||||
This distinction is important: direct device control intentionally behaves differently from thermostat control.
|
||||
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
# GREE Controller API reference
|
||||
|
||||
HTTP and WebSocket API for GREE Controller **0.9.0**.
|
||||
HTTP and WebSocket API for GREE Controller **0.9.1**.
|
||||
|
||||
[← Main documentation](../README.md)
|
||||
|
||||
@@ -212,7 +212,7 @@ Response:
|
||||
{
|
||||
"status": "ok",
|
||||
"name": "gree-controller",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"uptime_seconds": 1234,
|
||||
"control_ready": true,
|
||||
"time": "2026-08-30T06:54:00Z"
|
||||
@@ -236,7 +236,7 @@ Returns the initial Web UI snapshot:
|
||||
"settings": {},
|
||||
"outdoor_temperature": null,
|
||||
"system": {
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"uptime_seconds": 1234,
|
||||
"auth_required": false,
|
||||
"control_ready": true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "gree_controller",
|
||||
"name": "GREE Controller",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"config_flow": true,
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
|
||||
+14
-2
@@ -783,7 +783,7 @@
|
||||
"groups.enableControlFirst": "Turn the group on first.",
|
||||
"settings.compressorProtection": "Compressor protection",
|
||||
"settings.compressorProtectionTime": "Protection time (min)",
|
||||
"settings.compressorProtectionHint": "Recommended: 3 minutes. Starts and Heat/Cool changes requested during protection are queued and can be cancelled from Thermostats.",
|
||||
"settings.compressorProtectionHint": "Recommended: 3 minutes. Starts and Heat/Cool changes requested during protection are queued. Open the full list with the Queue button above Thermostats.",
|
||||
"zones.compressorQueueTitle": "Waiting for compressor protection",
|
||||
"zones.queuedPowerOn": "Queued: power on · {mode} · {temperature}°C",
|
||||
"zones.queuedModeChange": "Queued: change mode to {mode} · {temperature}°C",
|
||||
@@ -794,6 +794,18 @@
|
||||
"zones.queuedCancelled": "Queued task cancelled.",
|
||||
"zones.noQueuedTask": "There is no queued task to cancel.",
|
||||
"zones.queuedCancelledAll": "Cancelled queued tasks: {count}.",
|
||||
"zones.queueCancelledStatus": "Protection task cancelled — waiting for a new command"
|
||||
"zones.queueCancelledStatus": "Protection task cancelled — waiting for a new command",
|
||||
"zones.queueOverview": "Queue",
|
||||
"zones.queueEyebrow": "Compressor protection",
|
||||
"zones.queueTitle": "Command queue",
|
||||
"zones.queueHint": "Commands waiting for compressor protection to end. You can cancel a single task or clear the whole queue.",
|
||||
"zones.queuePendingLabel": "Waiting tasks",
|
||||
"zones.queueEmptyTitle": "The queue is empty",
|
||||
"zones.queueEmptyText": "There are no commands waiting for compressor protection to end.",
|
||||
"zones.queueReasonProtection": "Compressor protection",
|
||||
"zones.queueSource": "Source: {source}",
|
||||
"zones.queueRemaining": "Remaining {time}",
|
||||
"zones.queueReady": "Ready to execute",
|
||||
"zones.queueCancelAllConfirm": "Cancel all tasks ({count}) waiting for compressor protection?"
|
||||
}
|
||||
}
|
||||
|
||||
+14
-2
@@ -783,7 +783,7 @@
|
||||
"groups.enableControlFirst": "Najpierw włącz grupę.",
|
||||
"settings.compressorProtection": "Ochrona sprężarki",
|
||||
"settings.compressorProtectionTime": "Czas ochrony (min)",
|
||||
"settings.compressorProtectionHint": "Zalecane: 3 minuty. Uruchomienia i zmiany trybu Grzanie/Chłodzenie zlecone podczas ochrony są kolejkowane i można je anulować w Termostatach.",
|
||||
"settings.compressorProtectionHint": "Zalecane: 3 minuty. Uruchomienia i zmiany trybu Grzanie/Chłodzenie zlecone podczas ochrony są kolejkowane. Pełną kolejkę otworzysz przyciskiem „Kolejka” nad Termostatami.",
|
||||
"zones.compressorQueueTitle": "Oczekuje po ochronie sprężarki",
|
||||
"zones.queuedPowerOn": "Zakolejkowane: włączenie · {mode} · {temperature}°C",
|
||||
"zones.queuedModeChange": "Zakolejkowane: zmiana trybu na {mode} · {temperature}°C",
|
||||
@@ -794,6 +794,18 @@
|
||||
"zones.queuedCancelled": "Zakolejkowane zadanie anulowane.",
|
||||
"zones.noQueuedTask": "Brak oczekującego zadania do anulowania.",
|
||||
"zones.queuedCancelledAll": "Anulowano oczekujące zadania: {count}.",
|
||||
"zones.queueCancelledStatus": "Zadanie po ochronie anulowane — oczekuje na nowe polecenie"
|
||||
"zones.queueCancelledStatus": "Zadanie po ochronie anulowane — oczekuje na nowe polecenie",
|
||||
"zones.queueOverview": "Kolejka",
|
||||
"zones.queueEyebrow": "Ochrona sprężarki",
|
||||
"zones.queueTitle": "Kolejka poleceń",
|
||||
"zones.queueHint": "Polecenia czekające na zakończenie ochrony sprężarki. Możesz anulować pojedyncze zadanie albo wyczyścić całą kolejkę.",
|
||||
"zones.queuePendingLabel": "Oczekujące zadania",
|
||||
"zones.queueEmptyTitle": "Kolejka jest pusta",
|
||||
"zones.queueEmptyText": "Nie ma poleceń oczekujących na zakończenie ochrony sprężarki.",
|
||||
"zones.queueReasonProtection": "Ochrona sprężarki",
|
||||
"zones.queueSource": "Źródło: {source}",
|
||||
"zones.queueRemaining": "Pozostało {time}",
|
||||
"zones.queueReady": "Gotowe do wykonania",
|
||||
"zones.queueCancelAllConfirm": "Anulować wszystkie oczekujące zadania ({count}) ochrony sprężarki?"
|
||||
}
|
||||
}
|
||||
|
||||
+12
-2
@@ -87,7 +87,7 @@
|
||||
<section class="panel dashboard-section-panel">
|
||||
<div class="dashboard-section-head compact-head">
|
||||
<div><span class="eyebrow" data-i18n="nav.zones">Zones</span><h2 data-i18n="dashboard.quickThermostats">Thermostats</h2></div>
|
||||
<div class="dashboard-section-actions"><button type="button" class="secondary" id="cancelAllCompressorTasks" data-compressor-cancel-all data-action="cancel-all-compressor-tasks" data-i18n="zones.cancelAllQueued" hidden>Cancel all waiting</button><span class="badge" id="dashboardZoneCount">0</span></div>
|
||||
<div class="dashboard-section-actions"><button type="button" class="secondary queue-overview-button" data-action="open-compressor-queue"><span aria-hidden="true">⏳</span><span data-i18n="zones.queueOverview">Queue</span><b class="queue-overview-count" data-compressor-queue-count>0</b></button><span class="badge" id="dashboardZoneCount">0</span></div>
|
||||
</div>
|
||||
<div class="list-grid dashboard-zones" id="dashboardZones"></div>
|
||||
</section>
|
||||
@@ -113,7 +113,7 @@
|
||||
</section>
|
||||
|
||||
<section class="view" data-view="zones">
|
||||
<div class="section-heading"><div><span class="eyebrow" data-i18n="zones.automation">Automation</span><h1 data-i18n="nav.zones">Zones</h1></div><div class="section-heading-actions"><button type="button" class="secondary" data-compressor-cancel-all data-action="cancel-all-compressor-tasks" data-i18n="zones.cancelAllQueued" hidden>Cancel all waiting</button><button class="secondary" data-open="zoneDialog" data-i18n="zones.new">New zone</button></div></div>
|
||||
<div class="section-heading"><div><span class="eyebrow" data-i18n="zones.automation">Automation</span><h1 data-i18n="nav.zones">Zones</h1></div><div class="section-heading-actions"><button type="button" class="secondary queue-overview-button" data-action="open-compressor-queue"><span aria-hidden="true">⏳</span><span data-i18n="zones.queueOverview">Queue</span><b class="queue-overview-count" data-compressor-queue-count>0</b></button><button class="secondary" data-open="zoneDialog" data-i18n="zones.new">New zone</button></div></div>
|
||||
<p class="lead" data-i18n="zones.description">Zones define thermostat logic and sensor configuration. Use Quick thermostats on the Dashboard for current temperature changes and presets.</p>
|
||||
<div class="list-grid" id="zoneList"></div>
|
||||
</section>
|
||||
@@ -457,6 +457,16 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<dialog id="compressorQueueDialog" class="compressor-queue-dialog">
|
||||
<div class="dialog-form compressor-queue-dialog-body">
|
||||
<div class="dialog-head"><div><span class="eyebrow" data-i18n="zones.queueEyebrow">Compressor protection</span><h2 data-i18n="zones.queueTitle">Command queue</h2></div><button type="button" data-close>×</button></div>
|
||||
<p class="field-note" data-i18n="zones.queueHint">Commands waiting for the compressor protection window to end. You can cancel one task or clear the whole queue.</p>
|
||||
<div class="compressor-queue-summary"><span data-i18n="zones.queuePendingLabel">Waiting</span><strong id="compressorQueueModalCount">0</strong></div>
|
||||
<div id="compressorQueueList" class="compressor-queue-list"></div>
|
||||
<div class="form-actions compressor-queue-actions"><button type="button" class="danger" id="compressorQueueCancelAll" data-action="cancel-all-compressor-tasks" data-i18n="zones.cancelAllQueued">Cancel all waiting</button><button type="button" class="primary" data-close data-i18n="actions.done">Done</button></div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog id="temporaryThermostatDialog">
|
||||
<form method="dialog" id="temporaryThermostatForm" class="dialog-form temporary-thermostat-form">
|
||||
<input type="hidden" name="zone_id">
|
||||
|
||||
+65
-4
@@ -126,6 +126,68 @@ function compressorQueuePanel(zone) {
|
||||
return `<div class="compressor-queue-panel"><div><strong>${esc(tr('zones.compressorQueueTitle'))}</strong><span>${esc(compressorPendingDescription(zone))}</span><small>${esc(tr('zones.compressorQueueUntil', {time:when}))}</small></div><button type="button" class="secondary" data-action="cancel-compressor-task" data-id="${esc(zone.id)}">${esc(tr('actions.cancel'))}</button></div>`;
|
||||
}
|
||||
|
||||
function compressorQueueRemainingText(until) {
|
||||
const timestamp = until ? new Date(until).getTime() : NaN;
|
||||
if (!Number.isFinite(timestamp)) return tr('zones.afterProtection');
|
||||
const seconds = Math.max(0, Math.ceil((timestamp - Date.now()) / 1000));
|
||||
return seconds > 0 ? tr('zones.queueRemaining', {time:formatExtendedCountdown(seconds)}) : tr('zones.queueReady');
|
||||
}
|
||||
|
||||
function renderCompressorQueueModal() {
|
||||
const list = $('#compressorQueueList');
|
||||
const countNode = $('#compressorQueueModalCount');
|
||||
const cancelAll = $('#compressorQueueCancelAll');
|
||||
if (!list || !countNode || !cancelAll) return;
|
||||
const pending = app.zones
|
||||
.filter(zone => !!zone.compressor_pending_action)
|
||||
.slice()
|
||||
.sort((a,b) => {
|
||||
const at = new Date(a.compressor_pending_until || 0).getTime();
|
||||
const bt = new Date(b.compressor_pending_until || 0).getTime();
|
||||
return (Number.isFinite(at) ? at : Number.MAX_SAFE_INTEGER) - (Number.isFinite(bt) ? bt : Number.MAX_SAFE_INTEGER);
|
||||
});
|
||||
countNode.textContent = String(pending.length);
|
||||
cancelAll.disabled = pending.length === 0;
|
||||
cancelAll.hidden = pending.length === 0;
|
||||
cancelAll.textContent = pending.length ? `${tr('zones.cancelAllQueued')} (${pending.length})` : tr('zones.cancelAllQueued');
|
||||
if (!pending.length) {
|
||||
list.innerHTML = `<div class="compressor-queue-empty"><span aria-hidden="true">✓</span><strong>${esc(tr('zones.queueEmptyTitle'))}</strong><p>${esc(tr('zones.queueEmptyText'))}</p></div>`;
|
||||
return;
|
||||
}
|
||||
list.innerHTML = pending.map((zone,index) => {
|
||||
const device = app.devices.find(item => item.id === zone.device_id);
|
||||
const until = zone.compressor_pending_until || zone.lockout_until;
|
||||
const group = groupControlForZone(zone);
|
||||
const source = group ? `${zoneControlOwnerLabel(zone)}: ${group.name}` : zoneControlOwnerLabel(zone);
|
||||
const exact = until ? dateTime(until) : tr('zones.afterProtection');
|
||||
return `<article class="compressor-queue-item">
|
||||
<div class="compressor-queue-index">${index + 1}</div>
|
||||
<div class="compressor-queue-item-main">
|
||||
<div class="compressor-queue-item-head"><div><span class="eyebrow">${esc(zone.name)}</span><h3>${esc(device?.name || tr('common.noDevice'))}</h3></div><span class="compressor-queue-reason">${esc(tr('zones.queueReasonProtection'))}</span></div>
|
||||
<p class="compressor-queue-command">${esc(compressorPendingDescription(zone))}</p>
|
||||
<div class="compressor-queue-meta">
|
||||
<span>${esc(tr('zones.queueSource', {source}))}</span>
|
||||
<span>${esc(tr('zones.compressorQueueUntil', {time:exact}))}</span>
|
||||
<strong data-compressor-queue-countdown="${esc(until || '')}">${esc(compressorQueueRemainingText(until))}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="secondary compressor-queue-cancel" data-action="cancel-compressor-task" data-id="${esc(zone.id)}">${esc(tr('actions.cancel'))}</button>
|
||||
</article>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function updateCompressorQueueCountdowns() {
|
||||
$$('[data-compressor-queue-countdown]').forEach(node => {
|
||||
node.textContent = compressorQueueRemainingText(node.dataset.compressorQueueCountdown);
|
||||
});
|
||||
}
|
||||
|
||||
function openCompressorQueueDialog() {
|
||||
renderCompressorQueueModal();
|
||||
openDialog('compressorQueueDialog');
|
||||
updateCompressorQueueCountdowns();
|
||||
}
|
||||
|
||||
function zoneControlOwnerLabel(zone) {
|
||||
const owner=zone.control_owner || (zone.device_manual_override ? 'direct_manual' : (zone.local_thermostat_power != null ? 'local_thermostat' : 'automation'));
|
||||
const source=zone.control_source || '';
|
||||
@@ -265,10 +327,9 @@ function renderZones() {
|
||||
const dashboardCount = $('#dashboardZoneCount');
|
||||
if (dashboardCount) dashboardCount.textContent = String(app.zones.length);
|
||||
const pendingCount = app.zones.filter(zone => !!zone.compressor_pending_action).length;
|
||||
$$('[data-compressor-cancel-all]').forEach(cancelAll => {
|
||||
cancelAll.hidden = pendingCount === 0;
|
||||
cancelAll.textContent = pendingCount ? `${tr('zones.cancelAllQueued')} (${pendingCount})` : tr('zones.cancelAllQueued');
|
||||
});
|
||||
$$('[data-compressor-queue-count]').forEach(node => { node.textContent = String(pendingCount); });
|
||||
$$('.queue-overview-button').forEach(button => button.classList.toggle('has-items', pendingCount > 0));
|
||||
renderCompressorQueueModal();
|
||||
}
|
||||
|
||||
function groupZones(group) {
|
||||
|
||||
+2
-1
@@ -144,8 +144,9 @@ document.addEventListener('click', async event => {
|
||||
if(!Number.isFinite(value) || value<8 || value>30) return toast(tr('groups.customTemperatureRange'),true);
|
||||
return sendGroupControl(button.dataset.id,{preset:'custom',setpoint:Math.round(value*10)/10});
|
||||
}
|
||||
if (action === 'open-compressor-queue') return openCompressorQueueDialog();
|
||||
if (action === 'cancel-compressor-task') return cancelCompressorTask(button.dataset.id);
|
||||
if (action === 'cancel-all-compressor-tasks') return cancelAllCompressorTasks();
|
||||
if (action === 'cancel-all-compressor-tasks') { if (!confirm(tr('zones.queueCancelAllConfirm', {count:app.zones.filter(zone => !!zone.compressor_pending_action).length}))) return; return cancelAllCompressorTasks(); }
|
||||
if (action === 'zone-device-power') return sendZoneLocalPower(button.dataset.id, button.dataset.value === 'true');
|
||||
if (action === 'zone-open-temporary') return populateTemporaryThermostat(button.dataset.id);
|
||||
if (action === 'zone-temperature') { const zone=app.zones.find(v=>v.id===button.dataset.id); if(zone) { const base=Number(zone.manual_setpoint ?? zone.effective_setpoint ?? zone.setpoint); queueZoneTemperature(zone, base+Number(button.dataset.delta)); } return; }
|
||||
|
||||
@@ -17,4 +17,5 @@ async function startApplication() {
|
||||
|
||||
setInterval(updateLocalThermostatCountdowns, 1000);
|
||||
setInterval(updateTemporaryThermostatCountdowns, 1000);
|
||||
setInterval(updateCompressorQueueCountdowns, 1000);
|
||||
startApplication().catch(error => console.error('Application startup failed:', error));
|
||||
|
||||
@@ -1421,3 +1421,32 @@ textarea[aria-invalid="true"] {
|
||||
.compressor-queue-panel strong { font-size:.88rem; }
|
||||
.compressor-queue-panel span,.compressor-queue-panel small { overflow-wrap:anywhere; }
|
||||
.compressor-queue-panel button { flex:0 0 auto; }
|
||||
|
||||
|
||||
/* v0.9.1: global compressor queue overview. */
|
||||
.queue-overview-button { display:inline-flex; align-items:center; gap:7px; }
|
||||
.queue-overview-count { display:inline-grid; place-items:center; min-width:23px; height:23px; padding:0 6px; border-radius:999px; background:color-mix(in srgb,var(--text) 9%,transparent); font-size:.78rem; }
|
||||
.queue-overview-button.has-items { border-color:var(--warning, #b7791f); box-shadow:0 0 0 1px color-mix(in srgb,var(--warning, #b7791f) 22%,transparent); }
|
||||
.queue-overview-button.has-items .queue-overview-count { background:color-mix(in srgb,var(--warning, #b7791f) 18%,transparent); color:var(--text); }
|
||||
.compressor-queue-dialog { width:min(780px, calc(100% - 24px)); }
|
||||
.compressor-queue-dialog-body { gap:16px; }
|
||||
.compressor-queue-summary { display:flex; align-items:center; justify-content:space-between; padding:10px 13px; border:1px solid var(--line); border-radius:14px; background:var(--surface); }
|
||||
.compressor-queue-summary span { color:var(--muted); font-size:.86rem; }
|
||||
.compressor-queue-summary strong { font-size:1.15rem; }
|
||||
.compressor-queue-list { display:grid; gap:10px; }
|
||||
.compressor-queue-item { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:12px; padding:14px; border:1px solid var(--line); border-radius:16px; background:linear-gradient(135deg,color-mix(in srgb,var(--warning, #b7791f) 7%,var(--surface)),var(--surface)); }
|
||||
.compressor-queue-index { display:grid; place-items:center; width:32px; height:32px; border-radius:10px; background:color-mix(in srgb,var(--warning, #b7791f) 15%,transparent); font-weight:800; }
|
||||
.compressor-queue-item-main { min-width:0; display:grid; gap:8px; }
|
||||
.compressor-queue-item-head { display:flex; align-items:flex-start; justify-content:space-between; gap:10px; }
|
||||
.compressor-queue-item-head h3 { margin:2px 0 0; font-size:1rem; }
|
||||
.compressor-queue-reason { flex:0 0 auto; padding:5px 8px; border-radius:999px; background:color-mix(in srgb,var(--warning, #b7791f) 14%,transparent); font-size:.73rem; font-weight:700; }
|
||||
.compressor-queue-command { margin:0; font-weight:700; overflow-wrap:anywhere; }
|
||||
.compressor-queue-meta { display:flex; flex-wrap:wrap; gap:6px 12px; color:var(--muted); font-size:.78rem; }
|
||||
.compressor-queue-meta strong { color:var(--text); font-weight:700; }
|
||||
.compressor-queue-cancel { align-self:center; }
|
||||
.compressor-queue-empty { display:grid; justify-items:center; gap:7px; padding:30px 18px; text-align:center; border:1px dashed var(--line); border-radius:16px; color:var(--muted); }
|
||||
.compressor-queue-empty > span { display:grid; place-items:center; width:42px; height:42px; border-radius:999px; background:var(--surface); font-size:1.35rem; color:var(--text); }
|
||||
.compressor-queue-empty strong { color:var(--text); font-size:1rem; }
|
||||
.compressor-queue-empty p { margin:0; max-width:440px; }
|
||||
.compressor-queue-actions { position:sticky; bottom:0; padding-top:10px; background:linear-gradient(transparent,var(--dialog-bg) 28%); }
|
||||
@media (max-width:620px) { .compressor-queue-item { grid-template-columns:auto minmax(0,1fr); } .compressor-queue-cancel { grid-column:1 / -1; width:100%; } .compressor-queue-item-head { display:grid; } .compressor-queue-reason { justify-self:start; } }
|
||||
|
||||
Reference in New Issue
Block a user