From 3000dbaf016c83e84603a2d6d5f8d12d2d034c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Tue, 1 Sep 2026 15:38:46 +0200 Subject: [PATCH] v0.9.5 --- Cargo.lock | 2 +- Cargo.toml | 2 +- FILE_MANIFEST.sha256 | 75 +- build.rs | 89 +- regenerate-sha.sh | 1 + scripts/dev.sh | 100 - scripts/regenerate-sha.sh | 20 + web/404.html | 5 +- web/index.html | 1159 +++++-- web/js/bootstrap.js | 2 +- web/js/charts.js | 500 +-- web/js/core.js | 72 +- web/js/dashboard.js | 114 +- web/js/entities.js | 150 +- web/js/events.js | 278 +- web/js/forms.js | 50 +- web/js/history.js | 248 +- web/js/main.js | 4 +- web/js/navigation.js | 74 +- web/js/realtime.js | 42 +- web/js/router.js | 5 +- web/js/settings-ui.js | 48 +- web/js/settings.js | 42 +- web/manifest.webmanifest | 18 +- web/styles.css | 6717 +++++++++++++++++++++++++++++++------ web/sw.js | 2 - 26 files changed, 7520 insertions(+), 2299 deletions(-) create mode 120000 regenerate-sha.sh delete mode 100755 scripts/dev.sh create mode 100644 scripts/regenerate-sha.sh diff --git a/Cargo.lock b/Cargo.lock index b2cd7c5..87967e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -633,7 +633,7 @@ dependencies = [ [[package]] name = "gree-controller" -version = "0.9.4" +version = "0.9.5" dependencies = [ "aes", "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index 4293ac6..7645d7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gree-controller" -version = "0.9.4" +version = "0.9.5" 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 33af319..613b904 100644 --- a/FILE_MANIFEST.sha256 +++ b/FILE_MANIFEST.sha256 @@ -1,13 +1,12 @@ -d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env.example -a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore +5330f2eb75251cee43aa853448290bccd0e0d95f89aa1d23bde498eb86e99699 ./build.rs 37ba4d5222b221d9a3d560246d968092fa4402288410eaba7e6bab87ebcc5aea ./Cargo.lock a0976abed3b84a5ba0c726410c87d31017d42098fb7e053af986869ea0c9db74 ./Cargo.toml -19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE -a6391cc7fc014d06205c3fac708e0316e7239eaca47e0a2cb05a18275c58f192 ./README.md -ec0f495ee6a618bdec10b260ee7838f7a872d8c5b0aa91c546226c1ad818f601 ./build.rs +bfa6891d9abc7ac013773e5c7533325981c6887a87af725001a80611bc02e3ea ./data/gree-controller.db e7f085a9b4bd75dd62609e3c9f62bdde532f5cb0675fda2cf5109e57fafef2d2 ./docs/API.md -a0893b2a56eb1523f1a72871842e9be2139a5fafba1f51ae942fc407a6e4ca34 ./home-assistant/README.md -f8e8559fe10fe523ac5bc9aac25c6e26e862f679d502e8f3c39f38a0a8e40911 ./home-assistant/custom_components/gree_controller/__init__.py +d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env +d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env.example +a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore +ad08bba75bbc38d4c48cecef9ee05a19320d9a6a20b6cc950fdbb8115da573f2 ./gree-controller.zip 6910589f27960a28d4de9735884a7e5376e455cd885f19ce2b55947fcd135114 ./home-assistant/custom_components/gree_controller/api.py 71c8058f85fd9ce9f063b7a84dcf2956f23cdbbd02bd4bd99d2fbb9d177c72ad ./home-assistant/custom_components/gree_controller/climate.py 5e4aef2143e81bedb5a15dd4be5c71b64a3ab448ec6d33a20851edd098e5f529 ./home-assistant/custom_components/gree_controller/config_flow.py @@ -15,6 +14,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./home-assista ab08fbe40e9bb48ebcbfff98760aaa0c9434b61b705aaaadea0c09c255d99b0e ./home-assistant/custom_components/gree_controller/coordinator.py 5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./home-assistant/custom_components/gree_controller/entity_map.py c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./home-assistant/custom_components/gree_controller/icon.png +f8e8559fe10fe523ac5bc9aac25c6e26e862f679d502e8f3c39f38a0a8e40911 ./home-assistant/custom_components/gree_controller/__init__.py c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./home-assistant/custom_components/gree_controller/logo.png f30f751b4e561e51a3ff9bf9c8384bc2d160dd157b954dd7061d5844ee410b77 ./home-assistant/custom_components/gree_controller/manifest.json 38d659d0273c0d1428679e44574a2666c605d1b3796d323b626baa40560788cf ./home-assistant/custom_components/gree_controller/number.py @@ -24,22 +24,26 @@ cca65482e36d48035aca178121a378fe7d578d600acff267ae81a6399c0da653 ./home-assista 6bddb7b4620021ecd2099a86a77ef5c7f2c2dcd3d07d5db4e7b4c4ce6d3e8c03 ./home-assistant/custom_components/gree_controller/translations/en.json 13f30e2dcdcedbd1b6c3f99c2335e0487108fd72c8e86922368b84f2fa2038ae ./home-assistant/custom_components/gree_controller/translations/pl.json 4513070521d3dda0efb0d974a86ba674494cfb2b66fe9e5cac5b1b0430dede97 ./home-assistant/generated/gree_controller_entities.example.json -253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md +a0893b2a56eb1523f1a72871842e9be2139a5fafba1f51ae942fc407a6e4ca34 ./home-assistant/README.md a3e5842856e956044aefe840eb14fa36f98ae31d5613a7ac9f546df96c6c2366 ./lang/en.json 1baff114849223295ecf91251f525ec7e88f8261adcfa0462fa69ec01fe54517 ./lang/pl.json +253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md +19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE d8459024f04ca514bd8e9d6bd3af872fb942fd85a7cdd5583f4a9d28aab6faba ./make_zip.py -bb89bac237e750e9b1bf73761d7df97a6b81853091615878c03f13d7b6399aa7 ./scripts/README.md +a6391cc7fc014d06205c3fac708e0316e7239eaca47e0a2cb05a18275c58f192 ./README.md 5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh 6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./scripts/configure-gree-network.sh dbb92ddc27b8724faf709983e4feecd3f188052b2cba39daba96d4bc16914df5 ./scripts/dev.sh +a0cc9f6c6fa7322c48b227b1e28521ac3abd3fde91d9931a2607f18d6adcd783 ./scripts/FILE_MANIFEST.sha256 14076104c042fba1284ebb07531a6c3ff972df1f9f5b18f70da18ab774efed27 ./scripts/generate_ha_migration.py e4849261fd9ed1f01df96c0637c439c0c4eff8fa317b2918026167bba343af79 ./scripts/install-lxc.sh bb7cd2c5b27c9dceec1d1d2846fbad9600df9c08e0ad07d13fcde97af533091c ./scripts/install.sh e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./scripts/network-debug.sh +bb89bac237e750e9b1bf73761d7df97a6b81853091615878c03f13d7b6399aa7 ./scripts/README.md +01952aa92b217f8eae2493b88870e2dec595100cd15c4d561ff11ae2b936c46f ./scripts/regenerate-sha.sh 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 @@ -52,23 +56,23 @@ a13d4e217fe3ddaa73873ba6e0d1bc93750cf61764d867ff7372274ed21d9599 ./src/api/hist 0d7d598f7ed24315ed0547a60a72a06f20ebcf926489e1c454986f42d22cc305 ./src/api/integrations.rs bb2a746ecdcc2da5fe54e18b455c7bd19453486dd5c008e71951a2c81d0e7d64 ./src/api/middleware.rs 2b3feefbafc1f813c00dfe20db147aa1f5067ec5b8455f041ac3793802911be3 ./src/api/public_settings.rs +4c8b0d10d83d6130ed6e2703c233ac380b25a6c57fc23a29821f823df90331f9 ./src/api.rs 8f7f0b8c157e9c97d7dc305b79923646ef47d76497abfcf09e9c67bb12af5dd1 ./src/api/schedules.rs 4ffab50d931ca46dc5708914b6b54256d639cc11ad1b4281f649c310db5d41a0 ./src/api/settings.rs 74c156717b37e83d329f7c52ac622e02f690621544c4eb07c4502194db55e6e9 ./src/api/system.rs 681cccb8d09f4ad9c2e6467a4dbe2f9d991125775a5d2bfc9043daf940fecd29 ./src/api/websocket.rs 923e7434e8f5789150eb34ffc4d18d25fa701f4a9040bf856d7926016f1936aa ./src/api/zones.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 d8a2b323e864f2ec38ee15e3023be37a6aa05387f1ddbeedafe1f51556ad8c7d ./src/db/device_history.rs f3bd91d0bdb7699f319b9341d928d3cc3b25f759653fcd2095a5c5f695d08fac ./src/db/events_tokens.rs 59912e70b688daf5c4d22679d643ed3b724c262d62466aeaa9ef74db87808178 ./src/db/ha_history.rs +369455b5ffb125f1a80b767f62fd8c84a953adb69548dbd8adb653a6fbaaa45b ./src/db.rs 7e2fbce70f40aef360534ba38c6700163db77cb7288b3bf58728fce72a126435 ./src/db/schedules_automations.rs 35cbb521a8ec456756496b8aee71f9dfebad528a2cf2653ba8b48f6035be0c01 ./src/db/tests.rs a384ea4042d1d11692b5f8693df1f54850dcc4e80726780b44b2b096bf667b78 ./src/db/zone_history.rs -952ea5906d39ef917ad71edc345f76b29c07c63079644fd5699b12aa254cb35f ./src/engine.rs 384f7027ca48bd4c4048320bc6d4cacd631bee40e8630ea22e0d6e5f22ddc3a6 ./src/engine/automations.rs 77931af524e9402fc826050b5fcec422d01b02b625c8ce7f2004f1f6520c9b4d ./src/engine/commands.rs e16c1a65057f6fbf11bb6f27493ed975f5c89309b31dec0fa3248b9237494ed3 ./src/engine/control_plan.rs @@ -78,6 +82,7 @@ d15148382f0c84bdef36750a10b463378e2de6a0fb70eecadeee2b07b8648681 ./src/engine/g 4972c199274647bff47122c367e51bae8dab24d7ec4d652288e227a5a64c2310 ./src/engine/local_thermostat.rs ecab67072e756f5a371864282eb470a17de53016593e2e96540674066262c865 ./src/engine/ownership.rs 9c37adf514c96840fd6a5c715c579350fa86d6dcc0655a2aa97d1fd7c7e04299 ./src/engine/polling.rs +952ea5906d39ef917ad71edc345f76b29c07c63079644fd5699b12aa254cb35f ./src/engine.rs 3d48608a065aa52a1fd8b9c59356a74d8216ecee3d47f8ff8ef7d3d47170c617 ./src/engine/runtime.rs ffa72b93502eece187a96adb4ec94aec6048d4a6dc7af66e7c7af0931318787f ./src/engine/schedules.rs 7742046c0067c7cd5a74e2b81f8a8bdef9b76fcacf9b4af0b7eede1b050b9036 ./src/engine/targets.rs @@ -88,24 +93,23 @@ ebf8b9ec5c5a84850d8cad4c4faea090e52625b5a0be56625dc84060ecb35186 ./src/engine/z d2c2f3678bd13ec2d92404993173a691803da215dbdb1edafcd96e1963c7004e ./src/engine/zone_control.rs 4b271b6fc365b1078c01d6178eb563841b2ecaed5d8639196f58e1312d2236fe ./src/error.rs c6ff66da9ad08506f839ec56a869ead3abc824b5311e0ea9244517d11f7f4207 ./src/home_assistant.rs -6f9ef85cd53ba030b477bc3c186f0f7e762674124210eabcb0686f2806eaada1 ./src/influxdb.rs feb50ecd60b9a9fe9de1f7d8e1e26caab5435d59347468f6266a318f431bc3d3 ./src/influxdb/codec.rs 553b44ec0321e62480059bc2451fd34ec7aabe9fc87fc717daba0c4b55679d53 ./src/influxdb/query.rs +6f9ef85cd53ba030b477bc3c186f0f7e762674124210eabcb0686f2806eaada1 ./src/influxdb.rs 5e4d94f8a010df05bae9acab72c518080d2dba94d054df95b87e492305357222 ./src/influxdb/write.rs ef8ad176eb2ee6ceb02bbf3d63b65460013475908fced05388b29727af67ef7e ./src/main.rs -e935e1fcf0d379b627df5d5055dbdad3dd55786fde7191376918b05afdde1727 ./src/models.rs fca124ec30b5c9286f0f44cc18e8953bb2809f5009d4d521deaebfdeb00c1bd1 ./src/models/automation.rs da672b06295370a36c69136e15684ab10ccb8fbdc814d76527a05e01213c2d2b ./src/models/control_plan.rs 8228b9b6d9e8b1d13de29ce88500beacd88c8877893b7a3e41a4bd1e237ca36c ./src/models/defaults.rs 7cdc99a6f0920baf4ac88fbe7e6f1859e8598a00f00b8c3d84f20bc3a4de35f9 ./src/models/device.rs cfbaa853a5185c4be572588577b313a2d618837f7c07a3860d0e8d2cca04ee60 ./src/models/history.rs 668c9cf157679f8c9fe72846f743629c79e263b14827110834b4addbdf9d13ec ./src/models/integrations.rs +e935e1fcf0d379b627df5d5055dbdad3dd55786fde7191376918b05afdde1727 ./src/models.rs 268b4211df93f08423e39374ab9ffe93a7eba647c173168821736af299b36549 ./src/models/runtime.rs e77f8cdcb5fc0e51cdbf2527a69585a2f6f9089e159d1e45ec4a48735ab0e304 ./src/models/temporary_thermostat.rs 5b5c80e25ae2aad1b511bd3f0a82b8fbae2440abf8c656ecb90814587d0be137 ./src/models/zone.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 @@ -113,37 +117,38 @@ c935e441933ba8709432cc3ac33ed868043a89188d0f48ea6fbfe34b1f8936a3 ./src/protocol 97284ad37767625cfc2b82a3de5c40f6eecddabe185bd0bcea7cf9b165e017f0 ./src/protocol/gree/merge.rs a96061fbbcadac6c2df2e9708a48dcf819b951eb147f32c7d0d285ac5ae2491b ./src/protocol/gree/network.rs 45d0cd0ea40b9255bd811362547f5a667dac6bc7324074726130baf60aa41d2b ./src/protocol/gree/polling.rs +f1f765331469f4219a551414833c19b6de413114c22fdd0468e1f417659553c9 ./src/protocol/gree.rs 35fc903404045129e3046b7997e941b88f98ceb42485cccc872c2e4aa530c75b ./src/protocol/gree/tests.rs 55c2d615e76c9e4b08c4067ea734e81d81915144e637f3b51c19bea4fc879137 ./src/protocol/gree/transport.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 +830621fa75265da7288b8135704c0ce27e0687f87d55abf3cdbe40a6e2f3d2b1 ./src/queries.rs f688db6f440ee7b0129ccc22838de0ee0e0cf2b6376173b05f853ca2555c2c1e ./src/queries/schema.rs f13f1be3d5789539eba3fc1b59c14835f8c681eca7634e118fa3763a53aeee2b ./src/queries/zone_history.rs 6ee23fcaa62231d15006db078d4f763d1b67d2365fcc2d736d8a7da8df5ea382 ./src/state.rs b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service -de66cab9b650cb448814bfeda6d216e168dd3e6f07a1a438e4a9de9acbe6b798 ./web/404.html +544a31cb2e5374227026afd6abd0d910381f3fca1638154ebd74e3c3d96a35d1 ./web/404.html e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg -44ce3d17b83c1ab505247ba050a923d5ae109a3a06209aa3d362e9d6daf1ecbf ./web/index.html +525c7e83930cde6ea9d248220d595f52da4ade6e0f511e8b1075509635f20b32 ./web/index.html +fbe1ff0e28708f3b4acdbd361ba28457fe127e6474f4def15125e9d195c929c0 ./web/js/bootstrap.js +72fa280996ba331a05bdb29a1f49b4e61bdd8964953c8ce1db952156620f08fe ./web/js/charts.js +80bcc8d2214c55c13574e146eb099cb371565e55c7c4b337b71ca57ad29f90d2 ./web/js/core.js +83f6a5602da72ed6a307c1b4cc8cd9e4d9a8bf73e58be43af30ba19025a82ea3 ./web/js/dashboard.js +40f2363e79e12446ec8750b56c6df063035cd9655ee89e7c536e3b1f6e71746c ./web/js/entities.js +9a0e87911f78cd8a969f4c57ce74774f5fa829fdecd79494d9a7f9d5804f9792 ./web/js/events.js +cac7615888fd38be3cf714dcf02380c4a41a1802ee1724f91496e7c19cbdffba ./web/js/forms.js +dbf36223863ba882c03eccea4516ba8db7acf0c2e72c6c64490cb994f1e20f96 ./web/js/history.js +7eaa04992ea828dc89f5eaebf674cbbfd160f53eb8d0d6ef0d4e47bd8c28c435 ./web/js/main.js +72f2b4ef1e7de342b1d14a4b2757eb23d5a8acd6e837a0fe9fcc661dc35eeb63 ./web/js/navigation.js 9ba3f6fa05b72aa989139b2a909982571b2a02055052e4c40104f1e81a9aa7eb ./web/js/README.md -b3d5cb473bf9771de78e14c7de730cb86c73f8cdbb6344da67ba7d6abac879ea ./web/js/bootstrap.js -64a43e2142a69ebddf4160c0d0d3b573fa379c650431090fd15c9ba3b62bc4e7 ./web/js/charts.js -f3d2a8725f891266c725e3c67557de1f7305db00454ed4147a5c26a32cda31ff ./web/js/core.js -9767d5f2bd2844245245c0bd86dddd528975c1d1dd1d05b1449feda7a95214c4 ./web/js/dashboard.js -d0233628681160eb0c3d559887e48f0799b6ad0130bc943d0bea3e409ac05bf9 ./web/js/entities.js -69a052771dfafbd4b778cbc9d1b7a7695427621ed66047282088a3a07e6e4c2e ./web/js/events.js -2fe6c804f86b0613a50a56c54f7a0cbee77273759edf3e6fec36ac77d35edbd7 ./web/js/forms.js -be07a341ba4b1f514a5e13553494194b22aa87108cb04de40635a0158921780b ./web/js/history.js -9aa199a9ca8d1109cc06d7a7d69f5c5ed45c53f488128ca520258e9c91c31352 ./web/js/main.js -e27d5bb4a1579fd5b1d9762a5067b953004ca8ae63506af4e8c2cad754c0c532 ./web/js/navigation.js -40446c6bbfa21b645732bb56d7d8a1dd2d7e940b84fc882670d3a3607d127a55 ./web/js/realtime.js -45a666dbc1cddcc0ece507130cde7719f6232c710b4288d6a16e1232791f2a3d ./web/js/router.js -f9d23d7142ebcd77085ad30b8b5bf927b754b51f10976309d5b0a3f22f3cfafb ./web/js/settings-ui.js -a189f4f7cc253087ac271b3c5b7a82751176032fc0f7040f073cdc451d354e06 ./web/js/settings.js -fd26156e9f1d6713d3def564ad000553d9a16a24376059701db2ee762c99ee6c ./web/manifest.webmanifest -a447e27f1c6f5cd126e4ea08781603afaca0877c495bc38095e192618efbd942 ./web/styles.css -bc95830ec8273818ffde0a6b50eb3cc9ecbeee65a7a1d3aeff08cde86d05dc7b ./web/sw.js +262208c2487399ebbc94747c4d71267b01b5c4ac5bb20c682fc926840e048828 ./web/js/realtime.js +415348a93ac3423c491d5b222035a5ccd073eae9dc82e9c0560829afb6e02767 ./web/js/router.js +4a05126dc95c291ede08452b179d77791e270ed0c9ab9806036934ccf7e0f704 ./web/js/settings.js +b3e796778d6eee59d663f198115d0c2a45497fdfc23a88ff4b85893ba079d586 ./web/js/settings-ui.js +6b653e4fe2d4db4ffae6ff8f39b8ce57a10a990dc0000ee4aae51d949afadd52 ./web/manifest.webmanifest +d2e533ca1a445036327095647d15eef89eb810295f4222cf7284ac31ee6b5e10 ./web/styles.css +ae1b03f30b494f474a781a5d32072f1e73eba2b7c768109fc1cc8028cb6662a4 ./web/sw.js d505d793ce7cc9485b45b78bba1c0d51887adc7451ab59a42702946e5b991382 ./web/theme-init.js diff --git a/build.rs b/build.rs index 8980996..b4f8641 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,8 @@ use serde_json::{json, Value}; -use std::{env, fs, path::{Path, PathBuf}}; +use std::{ + env, fs, + path::{Path, PathBuf}, +}; fn required_string<'a>(meta: &'a Value, key: &str, file: &str) -> &'a str { meta.get(key) @@ -8,7 +11,6 @@ fn required_string<'a>(meta: &'a Value, key: &str, file: &str) -> &'a str { .unwrap_or_else(|| panic!("{file}: meta.{key} must be a non-empty string")) } - fn fnv1a_update(mut hash: u64, bytes: &[u8]) -> u64 { for byte in bytes { hash ^= u64::from(*byte); @@ -69,14 +71,24 @@ fn main() { let sw_path = web_dir.join("sw.js"); let manifest_path = web_dir.join("manifest.webmanifest"); let favicon_path = web_dir.join("favicon.svg"); - for path in [&theme_init_path, &styles_path, &index_path, ¬_found_path, &sw_path, &manifest_path, &favicon_path] { + for path in [ + &theme_init_path, + &styles_path, + &index_path, + ¬_found_path, + &sw_path, + &manifest_path, + &favicon_path, + ] { println!("cargo:rerun-if-changed={}", path.display()); } let app_js = bundle_app_js(&web_dir); let app_js_hash = content_hash(app_js.as_bytes()); - let theme_init_bytes = fs::read(&theme_init_path).unwrap_or_else(|error| panic!("cannot read {}: {error}", theme_init_path.display())); - let styles_bytes = fs::read(&styles_path).unwrap_or_else(|error| panic!("cannot read {}: {error}", styles_path.display())); + let theme_init_bytes = fs::read(&theme_init_path) + .unwrap_or_else(|error| panic!("cannot read {}: {error}", theme_init_path.display())); + let styles_bytes = fs::read(&styles_path) + .unwrap_or_else(|error| panic!("cannot read {}: {error}", styles_path.display())); let theme_init_hash = content_hash(&theme_init_bytes); let styles_hash = content_hash(&styles_bytes); let app_js_asset_path = format!("/app-{}.js", &app_js_hash[..12]); @@ -84,8 +96,17 @@ fn main() { let styles_asset_path = format!("/styles-{}.css", &styles_hash[..12]); let mut build_hash = fnv1a_update(0xcbf29ce484222325u64, app_js.as_bytes()); - for path in [&theme_init_path, &styles_path, &index_path, ¬_found_path, &sw_path, &manifest_path, &favicon_path] { - let bytes = fs::read(path).unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display())); + for path in [ + &theme_init_path, + &styles_path, + &index_path, + ¬_found_path, + &sw_path, + &manifest_path, + &favicon_path, + ] { + let bytes = fs::read(path) + .unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display())); build_hash = fnv1a_update(build_hash, &bytes); } @@ -107,9 +128,18 @@ fn main() { for path in files { println!("cargo:rerun-if-changed={}", path.display()); - let filename = path.file_name().and_then(|v| v.to_str()).expect("UTF-8 language filename"); - let stem = path.file_stem().and_then(|v| v.to_str()).expect("UTF-8 language code"); - if !stem.chars().all(|ch| ch.is_ascii_alphanumeric() || ch == '-' || ch == '_') { + let filename = path + .file_name() + .and_then(|v| v.to_str()) + .expect("UTF-8 language filename"); + let stem = path + .file_stem() + .and_then(|v| v.to_str()) + .expect("UTF-8 language code"); + if !stem + .chars() + .all(|ch| ch.is_ascii_alphanumeric() || ch == '-' || ch == '_') + { panic!("{filename}: filename may only contain ASCII letters, digits, '-' and '_'"); } @@ -118,15 +148,20 @@ fn main() { build_hash = fnv1a_update(build_hash, source.as_bytes()); let document: Value = serde_json::from_str(&source) .unwrap_or_else(|error| panic!("{filename}: invalid JSON: {error}")); - let root_object = document.as_object().unwrap_or_else(|| panic!("{filename}: language pack root must be an object")); - let unexpected: Vec<&str> = root_object.keys() + let root_object = document + .as_object() + .unwrap_or_else(|| panic!("{filename}: language pack root must be an object")); + let unexpected: Vec<&str> = root_object + .keys() .filter(|key| key.as_str() != "meta" && key.as_str() != "translations") .map(|key| key.as_str()) .collect(); if !unexpected.is_empty() { panic!("{filename}: unexpected top-level keys {:?}; translation keys must be inside 'translations'", unexpected); } - let meta = document.get("meta").unwrap_or_else(|| panic!("{filename}: missing meta object")); + let meta = document + .get("meta") + .unwrap_or_else(|| panic!("{filename}: missing meta object")); let code = required_string(meta, "code", filename); let name = required_string(meta, "name", filename); let native_name = required_string(meta, "native_name", filename); @@ -158,15 +193,31 @@ fn main() { let manifest_json = serde_json::to_string(&json!({ "default": "en", "languages": manifest_languages - })).expect("serialize language manifest"); + })) + .expect("serialize language manifest"); let mut generated = String::new(); generated.push_str("// @generated by build.rs - do not edit.\n"); - generated.push_str(&format!("pub const APP_JS_ASSET_PATH: &str = {:?};\n", app_js_asset_path)); - generated.push_str(&format!("pub const THEME_INIT_ASSET_PATH: &str = {:?};\n", theme_init_asset_path)); - generated.push_str(&format!("pub const STYLES_CSS_ASSET_PATH: &str = {:?};\n", styles_asset_path)); - generated.push_str(&format!("pub const ASSET_BUILD_ID: &str = \"{:016x}\";\n", build_hash)); - generated.push_str(&format!("pub const LANGUAGE_MANIFEST_JSON: &str = {:?};\n", manifest_json)); + generated.push_str(&format!( + "pub const APP_JS_ASSET_PATH: &str = {:?};\n", + app_js_asset_path + )); + generated.push_str(&format!( + "pub const THEME_INIT_ASSET_PATH: &str = {:?};\n", + theme_init_asset_path + )); + generated.push_str(&format!( + "pub const STYLES_CSS_ASSET_PATH: &str = {:?};\n", + styles_asset_path + )); + generated.push_str(&format!( + "pub const ASSET_BUILD_ID: &str = \"{:016x}\";\n", + build_hash + )); + generated.push_str(&format!( + "pub const LANGUAGE_MANIFEST_JSON: &str = {:?};\n", + manifest_json + )); generated.push_str("pub const LANGUAGE_ASSETS: &[(&str, &str)] = &[\n"); for (code, path) in assets { generated.push_str(&format!( diff --git a/regenerate-sha.sh b/regenerate-sha.sh new file mode 120000 index 0000000..ab40103 --- /dev/null +++ b/regenerate-sha.sh @@ -0,0 +1 @@ +scripts/regenerate-sha.sh \ No newline at end of file diff --git a/scripts/dev.sh b/scripts/dev.sh deleted file mode 100755 index 5c25b50..0000000 --- a/scripts/dev.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env bash -set -Eeuo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# shellcheck source=common.sh -source "$SCRIPT_DIR/common.sh" -cd "$PROJECT_ROOT" - -MODE="debug" -ACTION="run" -INSTALL_DEPS=1 -RESET_DB=0 -HOST="" -PORT="" - -usage() { - cat <<'TXT' -GREE Controller - development environment - -Usage: - ./scripts/dev.sh install missing tools, build and run - ./scripts/dev.sh --release run an optimized build - ./scripts/dev.sh --check formatting, tests, build and API smoke test - ./scripts/dev.sh --reset remove the local database before startup - ./scripts/dev.sh --no-install do not install system packages or Rust - ./scripts/dev.sh --host 0.0.0.0 --port 8787 -TXT -} - -while [[ $# -gt 0 ]]; do - case "$1" in - --release) MODE="release"; shift ;; - --check) ACTION="check"; shift ;; - --reset) RESET_DB=1; shift ;; - --no-install) INSTALL_DEPS=0; shift ;; - --host) HOST="${2:?missing value for --host}"; shift 2 ;; - --port) PORT="${2:?missing value for --port}"; shift 2 ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown argument: $1" >&2; usage; exit 2 ;; - esac -done - -if [[ "$INSTALL_DEPS" -eq 1 ]]; then - install_build_dependencies - ensure_rust -else - command -v cargo >/dev/null 2>&1 || fail "Cargo is not installed and --no-install was requested." -fi - - -if [[ ! -f .env ]]; then - cp .env.example .env - say "Created .env from the example configuration" -fi -mkdir -p data -if [[ "$RESET_DB" -eq 1 ]]; then - rm -f data/gree-controller.db data/gree-controller.db-shm data/gree-controller.db-wal - say "Removed the local database" -fi - -set -a -# shellcheck disable=SC1091 -source ./.env -set +a - -if [[ -n "$HOST" || -n "$PORT" ]]; then - current="${GREE_CONTROLLER_BIND:-0.0.0.0:8787}" - current_host="${current%:*}" - current_port="${current##*:}" - export GREE_CONTROLLER_BIND="${HOST:-$current_host}:${PORT:-$current_port}" -fi - -if [[ "$ACTION" == "check" ]]; then - say "Checking formatting" - cargo fmt --all -- --check - say "Running Rust tests" - cargo test --all-targets - say "Building the application" - cargo build - say "Running HTTP/API smoke test" - "$SCRIPT_DIR/smoke.sh" - say "All checks passed" - exit 0 -fi - -if [[ "$MODE" == "release" ]]; then - say "Building release version" - cargo build --release - BINARY="$PROJECT_ROOT/target/release/gree-controller" -else - say "Building debug version" - cargo build - BINARY="$PROJECT_ROOT/target/debug/gree-controller" -fi - -bind="${GREE_CONTROLLER_BIND:-0.0.0.0:8787}" -display_host="${bind%:*}" -[[ "$display_host" == "0.0.0.0" ]] && display_host="127.0.0.1" -say "Panel: http://${display_host}:${bind##*:}" -say "Stop: Ctrl+C" -exec "$BINARY" diff --git a/scripts/regenerate-sha.sh b/scripts/regenerate-sha.sh new file mode 100644 index 0000000..6c8fb74 --- /dev/null +++ b/scripts/regenerate-sha.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +MANIFEST="FILE_MANIFEST.sha256" +TMP="${MANIFEST}.tmp" + +find . -type f \ + ! -path "./${MANIFEST}" \ + ! -path "./${TMP}" \ + ! -path "./.git/*" \ + ! -path "./target/*" \ + -print0 \ + | sort -z \ + | xargs -0 sha256sum > "$TMP" + +mv "$TMP" "$MANIFEST" + +sha256sum -c "$MANIFEST" diff --git a/web/404.html b/web/404.html index a84165c..e9eb1ab 100644 --- a/web/404.html +++ b/web/404.html @@ -1,5 +1,6 @@ + @@ -10,6 +11,7 @@ Page not found · GREE Controller +
@@ -29,4 +31,5 @@ - + + \ No newline at end of file diff --git a/web/index.html b/web/index.html index 2e509f9..3ff256b 100644 --- a/web/index.html +++ b/web/index.html @@ -1,5 +1,6 @@ + @@ -11,17 +12,20 @@ GREE Controller +
-
GREE Controller
+
GREE Controller
0/0
-
@@ -34,16 +38,19 @@
- +
@@ -57,20 +64,35 @@
-
Home

Comfort under control

Loading devices…

-
--°C
+
Home +

Comfort under control

+

Loading devices…

+
+
+
+
--°C
+
-
Global power

All air conditioners

Bulk power action for all units. OFF leaves thermostats locally off until you explicitly turn a thermostat, group or the whole house back on.

-
+
Global power +

All air conditioners

+

Bulk power action for all units. OFF leaves thermostats locally off + until you explicitly turn a thermostat, group or the whole house back on.

+
+
-
Control plan

What happens next

+
Control plan +

What happens next

+
@@ -79,15 +101,25 @@