v0.7.1
This commit is contained in:
+21
-1
@@ -1,4 +1,4 @@
|
||||
# GREE Controller v0.7.0 - build and validation report
|
||||
# GREE Controller v0.7.1 - build and validation report
|
||||
|
||||
## Scope
|
||||
|
||||
@@ -225,3 +225,23 @@ Validation for the 0.7.0 package:
|
||||
- All built-in schedule templates were exercised against the weekly minute-mask model: no overlaps; Family/Child/Bedroom/Always cover the full week, and Workday's intentional uncovered period falls back to the normal Comfort profile.
|
||||
- SQLite `INIT_SCHEMA` and device-delete orphan-schedule cleanup SQL were executed successfully against temporary in-memory databases.
|
||||
- Rust source delimiter/static checks passed. Rust compile/test execution is unavailable here because no Rust toolchain is installed.
|
||||
|
||||
|
||||
## 0.7.1 quick-control visual consistency
|
||||
|
||||
- Refactored dashboard-only thermostat/device controls to semantic `quick-control-card` and `quick-control-row-*` classes.
|
||||
- Quick thermostat preset/mode controls and quick manual HVAC/fan/function controls now share the same compact height, spacing, typography and fixed responsive grids.
|
||||
- Removed the superseded `#dashboardDevices` desktop-only size override; full Devices/Zones views remain unchanged.
|
||||
- Changed the compact inherited-mode label from `Dom/House` to `Globalny/Global`, while retaining the internal `house` value for API/backward compatibility.
|
||||
- Changed `Sen teraz/Sleep now` to `Sen/Sleep`.
|
||||
|
||||
Validation for the 0.7.1 package:
|
||||
|
||||
- `node --check` passed for `web/app.js` and `web/sw.js`.
|
||||
- PL/EN JSON parsed successfully and translation key sets are identical (574 keys each).
|
||||
- Home Assistant manifest parsed successfully and Home Assistant Python modules passed `python3 -m py_compile`.
|
||||
- `web/index.html` parsed with Python's HTML parser and CSS brace balance passed.
|
||||
- Static assertions confirm the compact classes are attached only to dashboard quick cards (`detailed == false`), while full Zone/Device cards keep their existing classes and sizes.
|
||||
- Quick controls use explicit 3/4/5-column grids with no horizontal overflow behavior; all compact option buttons share the same 28 px minimum height, 9 px typography and 4 px gap.
|
||||
- User-facing inherited-mode labels are `Globalny/Global`; the internal/API value remains `house`, preserving backward compatibility.
|
||||
- No thermostat, schedule, automation, group, database or device-control logic was changed in 0.7.1.
|
||||
|
||||
Generated
+1
-1
@@ -633,7 +633,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gree-controller"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-gcm",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "gree-controller"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
edition = "2021"
|
||||
authors = ["GREE Controller contributors"]
|
||||
description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support"
|
||||
|
||||
+13
-13
@@ -1,16 +1,16 @@
|
||||
d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env.example
|
||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||
88a25226a5831a9ca8aa8cad30c6dea3d0df8138d0f2a5b1542218b5ac1f9864 ./BUILD_REPORT.md
|
||||
180a882f534228ac685d1a97696f8d2874aa500b905d4dbbe42ee38eddbe57b8 ./Cargo.lock
|
||||
72d6873d30d9d76f7e402c3370f0f0bf8e7515a1ab50a67fcda4ddd396bc8036 ./Cargo.toml
|
||||
6145cb2513f1813a252721b295d7b98b48c916c4e6f29e1ea83b1401ba24a817 ./BUILD_REPORT.md
|
||||
9206ce86a9de52a5bfae90c1b9c0d820d4b60c3f871596f6850d8ccc8ffa0beb ./Cargo.lock
|
||||
0dffb9821d84c54f9f6115cdfe38c1549f70bf5f470c660fef1f819eacb9247f ./Cargo.toml
|
||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||
f53e008970ef05699efca9f15e6de487af5c7f7bef6e226eb409e9f8c17e54b8 ./README.md
|
||||
34fb4263e9248c6887020502c61c43825a0d396e5fe4c071de8875ad42af7d5e ./README.md
|
||||
f2c8eb28fedb26bb8f5636e1805e7a782cc472b87d882f6752d520f16216a248 ./build.rs
|
||||
53b04025be8be67e6970e825f5c80a067715eea5ecd2d592aa26a9d37a0dcb51 ./docs/API.md
|
||||
cd45c288c3e002925656f7e06656034fd07013774f4ba0a00ba2bfb1fe60195a ./docs/API.md
|
||||
234dd200e380a13ecd3e61b4ea455f6f08d64ce89382077dee80684acadb9703 ./docs/HOME_ASSISTANT_MIGRATION.md
|
||||
7a88d6e76fda21e5d34ab351e26bc10dc1f8f7b3055505aefad1df7c56d65ae4 ./docs/LOCALIZATION.md
|
||||
10a0722e1100fb4a05e3067daeb67dc47b0c0a096b43b1cbf2bf002967ce7d98 ./docs/LXC.md
|
||||
f135c029c528f55918e394d98dae7f92794ff28e00d49cc7e9f519e409c0d3ee ./docs/PROJECT_SPEC.md
|
||||
2ef5cb04cc74be3aab77a9d117b9040f186c73a7dcf24375f1cca2d73e428449 ./docs/PROJECT_SPEC.md
|
||||
7748b1230029439a405139750b2e0607b8e463bfd56ad86982394105069697c5 ./docs/REVERSE_PROXY.md
|
||||
28f1403d84b01b37bebb72548aa39a75bb14bdbb611a43bfa761319957cd6f39 ./docs/SECURITY.md
|
||||
1192c5442cb0439723294bbed8b4610728c51bca23e1d8c0ef030270d1c697ba ./home-assistant/README.md
|
||||
@@ -23,7 +23,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./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
|
||||
9032738bebf9c0e030f711f22853f6541ed6a4ab80a659b930756ba722052848 ./home-assistant/custom_components/gree_controller/manifest.json
|
||||
fc73252d4c9d6b747fb6290adcd6fd1063d87e2a75e12437c7359dc80245958d ./home-assistant/custom_components/gree_controller/manifest.json
|
||||
d0206766d76f70ea1c75a618e41bf1e8e8c0429f42ee97f40daac1ea12f16658 ./home-assistant/custom_components/gree_controller/number.py
|
||||
459233ccefa7827fc1c6887146d8a69e01149a497635fd5e1802c220f418eb28 ./home-assistant/custom_components/gree_controller/select.py
|
||||
ec35191e27a018a72cb341914e2abd7fecd25f6b641ea25bebb9e809dd8ecaff ./home-assistant/custom_components/gree_controller/sensor.py
|
||||
@@ -32,8 +32,8 @@ ec35191e27a018a72cb341914e2abd7fecd25f6b641ea25bebb9e809dd8ecaff ./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
|
||||
54c6f8405d090d9be4fe8247636679603132662d1c3359fdcb272bf86a144609 ./lang/en.json
|
||||
caa87ffcace5e00a1cc116f1183481f2a191d5a377d04f870d1ac9f9751213ad ./lang/pl.json
|
||||
00a4d668e9665c7e98211e19974ded8aaf7bc5e89f2d29688d9080e4cd34ef3b ./lang/en.json
|
||||
44923d70326c62f1e766928360ea4b4909f82b240e5f3773ef44a37f02b75ac4 ./lang/pl.json
|
||||
028e1f16e9fbaed57cadb88eff04e65b4bd67722c50b4d6b1fb525f5a2f39abf ./make_zip.py
|
||||
bb89bac237e750e9b1bf73761d7df97a6b81853091615878c03f13d7b6399aa7 ./scripts/README.md
|
||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
||||
@@ -62,10 +62,10 @@ a910bd9432a393740c0f6fab52bfcb551f0ea756718d66d290fd2610767cf07c ./src/protocol
|
||||
6a1c0cab3eab80ecd254c5e486b1fba068523edb8d699c64054c17f67f4a31d0 ./src/queries.rs
|
||||
f74ac398c6f2e64ebb965900330324f3ec7403e70339638a408229f29fbd07ae ./src/state.rs
|
||||
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
||||
a51b4bc14f9fbc4d5b936c1f56b4c53a5aaded69f5334991da3328cb7b3f3cb6 ./web/app.js
|
||||
e8776757097634bf09d75705a93b4225f619345f3b9bf242e37f1948a7edf394 ./web/app.js
|
||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||
954b5fdabef37d7788790fb15a5f95a96aeb810cba7ccf24763aa28615d4749c ./web/index.html
|
||||
ff8c37afb1091b46a5a37683d352226d8cb5bc7ca3e2c8a11ee22d3144bd0494 ./web/index.html
|
||||
fd26156e9f1d6713d3def564ad000553d9a16a24376059701db2ee762c99ee6c ./web/manifest.webmanifest
|
||||
9bc2a34daeb2b7083af44215e904aaadf1d4da9d003e6fcef2d3fc9f994de363 ./web/styles.css
|
||||
bf2db03bf27c567121a3ebe3bd11d84d13dce91f3c1f068aadff6e11c5120a26 ./web/sw.js
|
||||
e624f7673e0f0fa44f49771e535db5f360ad766e411fb0a810b572459e4617d4 ./web/styles.css
|
||||
a80e68a9afc2cec5d12249cdf4c5cdb83abd96690fb58b306ec7f36d49ca70fa ./web/sw.js
|
||||
d505d793ce7cc9485b45b78bba1c0d51887adc7451ab59a42702946e5b991382 ./web/theme-init.js
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Standalone local GREE air-conditioner controller written in Rust. It runs on a regular Linux host or an LXC container and provides a mobile-first web interface without depending on the vendor cloud.
|
||||
|
||||
Current version: **0.7.0**.
|
||||
Current version: **0.7.1**.
|
||||
|
||||
## Highlights
|
||||
|
||||
@@ -17,7 +17,7 @@ Current version: **0.7.0**.
|
||||
- automatic SQLite history compaction plus optional InfluxDB 1.x/2.x long-term archive,
|
||||
- smart thermostat zones with global house Heat/Cool/Off mode and per-zone overrides,
|
||||
- setpoint modulation that keeps indoor units powered during normal operation instead of repeatedly cycling power,
|
||||
- Comfort/Sleep/Away profiles, temporary overrides and one-tap **Sleep now**,
|
||||
- Comfort/Sleep/Away profiles, temporary overrides and one-tap **Sleep**,
|
||||
- named climate groups (for example Upstairs/Downstairs) with independent power, Heat/Cool/house-follow mode and profile control, including automation targets,
|
||||
- editable weekly schedules plus Family/Child room/Bedroom/Workday/Always-comfort templates,
|
||||
- temperature/time automations for advanced exceptions,
|
||||
@@ -47,7 +47,7 @@ See [`BUILD_REPORT.md`](BUILD_REPORT.md) for package validation details and [`do
|
||||
On Debian, Ubuntu or an LXC container:
|
||||
|
||||
```bash
|
||||
unzip gree-controller-v0.7.0.zip
|
||||
unzip gree-controller-v0.7.1.zip
|
||||
cd gree-controller
|
||||
chmod +x scripts/*.sh
|
||||
./scripts/dev.sh
|
||||
@@ -218,15 +218,15 @@ For combined control, `max_sensor_difference` protects against an obviously inco
|
||||
|
||||
The zone API exposes `device_temperature`, `external_temperature`, `current_temperature` (the actual control temperature) and `control_temperature_source` for diagnostics. Existing SQLite zone records remain compatible because the new fields have defaults and are stored in the existing JSON payload.
|
||||
|
||||
Zone cards are fast thermostats: `- / +`, **Auto schedule / Comfort / Sleep now / Away**, and **House / Heat / Cool** are available without opening the edit dialog. Temporary overrides automatically end at the next schedule boundary; if a zone has no future schedule transition, the override stays active until it is cleared. The zone engine, not the browser, owns device commands so schedules, sensors and manual overrides cannot fight each other.
|
||||
Zone cards are fast thermostats: `- / +`, **Auto schedule / Comfort / Sleep / Away**, and **Global / Heat / Cool** are available without opening the edit dialog. Temporary overrides automatically end at the next schedule boundary; if a zone has no future schedule transition, the override stays active until it is cleared. The zone engine, not the browser, owns device commands so schedules, sensors and manual overrides cannot fight each other.
|
||||
|
||||
## Smart thermostat and schedules
|
||||
|
||||
Version 0.4.0 is designed around a simple rule: during normal heating/cooling, keep the indoor unit powered and modulate its target instead of repeatedly switching the unit OFF and ON. When a room is satisfied, cooling moves the device target above the comfort target and heating moves it below the comfort target. When hysteresis requests conditioning again, the working target is restored. **House Off** is now a no-control state for inherited zones, so manual device operation is not overwritten; whole-house master power on/off is available separately and can authoritatively keep every unit off without changing the selected thermostat mode.
|
||||
|
||||
Choose one seasonal **House mode** — Cooling in summer, Heating in winter, or Off. Zones follow the house mode by default, while a zone may explicitly override Heat/Cool when required. In **Mode policy**, leave **Follow house mode** unchanged when the zone should inherit the mode selected globally in GREE Controller; **Cooling only** / **Heating only** are deliberate per-zone overrides. Each zone has separate Comfort/Sleep/Away temperatures for cooling and heating.
|
||||
Choose one seasonal **global mode** — Cooling in summer, Heating in winter, or Off. Zones inherit this global mode by default, while a zone may explicitly override Heat/Cool when required. In **Mode policy**, leave **Follow global mode** unchanged when the zone should inherit the mode selected globally in GREE Controller; **Cooling only** / **Heating only** are deliberate per-zone overrides. Each zone has separate Comfort/Sleep/Away temperatures for cooling and heating.
|
||||
|
||||
Schedules store profiles rather than duplicating temperatures. Ready-made **Family**, **Child room**, **Bedroom**, **Workday** and **Always comfort** templates generate ordinary editable schedule entries. For example, a child room template can enter Sleep earlier; if the child goes to bed even earlier, press **Sleep now** and that zone remains in Sleep until its next schedule boundary (or until manually cleared when no future boundary exists). Whole-house **Comfort all / Sleep all / Away all / Auto schedule** actions are also available.
|
||||
Schedules store profiles rather than duplicating temperatures. Ready-made **Family**, **Child room**, **Bedroom**, **Workday** and **Always comfort** templates generate ordinary editable schedule entries. For example, a child room template can enter Sleep earlier; if the child goes to bed even earlier, press **Sleep** and that zone remains in Sleep until its next schedule boundary (or until manually cleared when no future boundary exists). Whole-house **Comfort all / Sleep all / Away all / Auto schedule** actions are also available.
|
||||
|
||||
The optional HA outdoor sensor is an assist signal only. It never replaces the room-control temperature. In extreme weather it can make the active device setpoint slightly more assertive and let Smart Fan increase airflow.
|
||||
|
||||
@@ -415,3 +415,13 @@ GREE protocol identifiers are now emitted as canonical lowercase hexadecimal in
|
||||
### v0.4.0 smart thermostat
|
||||
|
||||
Zones now use setpoint modulation, global seasonal house mode, cooling/heating Comfort/Sleep/Away profiles, temporary per-zone overrides, ready-made editable schedule templates, whole-house preset actions and optional outdoor-temperature assist. The UI also uses theme-aware thin scrollbars and can opt in to invalid/self-signed certificates for the outbound Home Assistant sensor connection. Existing zones remain readable; a legacy zone keeps its previous setpoint as its comfort target until it is edited and saved with the new profile fields.
|
||||
|
||||
|
||||
## 0.7.1 quick-control visual consistency
|
||||
|
||||
- Unified dashboard Quick Thermostats and Quick Manual Control under shared `quick-control-*` CSS classes instead of separate dashboard-ID overrides.
|
||||
- Standardized compact option buttons, gaps and row grids across presets, HVAC modes, fan speeds and quick unit toggles.
|
||||
- Kept full Zones and Devices pages at their existing larger control sizes; the compact style applies only to dashboard quick controls.
|
||||
- Renamed the compact zone mode from House to Global to make its meaning explicit: the zone inherits the global Heating/Cooling mode selected in GREE Controller.
|
||||
- Renamed Sleep now to Sleep in quick thermostat presets.
|
||||
- Advanced the PWA cache key to `gree-controller-v071-quick-controls`.
|
||||
|
||||
+2
-2
@@ -111,7 +111,7 @@ A zone stores separate profile temperatures for both seasons:
|
||||
Quick control does not directly fight the schedule engine:
|
||||
|
||||
```bash
|
||||
# Sleep now until the next schedule boundary (or until cleared if there is no future transition)
|
||||
# Sleep until the next schedule boundary (or until cleared if there is no future transition)
|
||||
curl -X POST "$BASE/api/zones/ZONE_ID/control" -H "$AUTH" -H 'Content-Type: application/json' \
|
||||
-d '{"preset":"sleep"}'
|
||||
|
||||
@@ -124,7 +124,7 @@ curl -X POST "$BASE/api/zones/ZONE_ID/control" -H "$AUTH" -H 'Content-Type: appl
|
||||
-d '{"setpoint":22.5}'
|
||||
```
|
||||
|
||||
`mode` on the quick zone endpoint accepts `house`, `cool`, or `heat`. `house` means **Follow house mode** and should be left unchanged when the zone is meant to inherit the global GREE Controller mode. A fixed `cool`/`heat` is an explicit per-zone override; therefore house mode `off` stops inherited zones but does not disable a zone deliberately fixed to Cooling or Heating. Whole-house master power remains authoritative over all zones.
|
||||
`mode` on the quick zone endpoint accepts `house`, `cool`, or `heat`. `house` means **Follow global mode** in the UI and should be left unchanged when the zone is meant to inherit the global GREE Controller mode. A fixed `cool`/`heat` is an explicit per-zone override; therefore house mode `off` stops inherited zones but does not disable a zone deliberately fixed to Cooling or Heating. Whole-house master power remains authoritative over all zones.
|
||||
|
||||
### Climate groups
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ Each device stores:
|
||||
|
||||
## Smart thermostat, zones and schedules
|
||||
|
||||
The primary user model is a fast thermostat, not raw device automation. A global **House mode** selects the season (`cool`, `heat`, `off`). Zones follow it by default and can optionally override Heat/Cool. The UI labels this as **Mode policy**: keep **Follow house mode** when the zone should inherit the mode selected globally in GREE Controller; the fixed Cooling/Heating policies are explicit overrides. House `off` is a **do not control** state for inherited zones: manual device operation is left untouched, while a zone explicitly switched to Heat/Cool can operate independently. Whole-house power on/off is a separate master state. Master Off is authoritative for zones and controller automations but does not modify the selected house thermostat mode.
|
||||
The primary user model is a fast thermostat, not raw device automation. A global climate mode selects the season (`cool`, `heat`, `off`). Zones follow it by default and can optionally override Heat/Cool. The UI labels this as **Mode policy**: keep **Follow global mode** when the zone should inherit the mode selected globally in GREE Controller; the fixed Cooling/Heating policies are explicit overrides. House `off` is a **do not control** state for inherited zones: manual device operation is left untouched, while a zone explicitly switched to Heat/Cool can operate independently. Whole-house power on/off is a separate master state. Master Off is authoritative for zones and controller automations but does not modify the selected house thermostat mode.
|
||||
|
||||
During normal heating/cooling the controller minimizes unit power cycling. It keeps the indoor unit powered and uses **setpoint modulation**:
|
||||
|
||||
@@ -84,7 +84,7 @@ Each zone has separate seasonal profile temperatures:
|
||||
|
||||
The zone can use the GREE internal sensor, its own Home Assistant room sensor, or a weighted combination. External room sensors are configured per zone; loss/discrepancy falls back safely to the GREE sensor.
|
||||
|
||||
Weekly schedules reference profiles instead of duplicating temperatures. Ready-made Family, Child room, Bedroom, Workday and Always-comfort templates create ordinary schedule rows that remain fully editable. A manual **Sleep now**, Comfort, Away or custom-temperature override ends automatically at the next schedule boundary; if no future transition exists, it remains active until explicitly cleared. Whole-house preset actions apply the same temporary policy to every zone.
|
||||
Weekly schedules reference profiles instead of duplicating temperatures. Ready-made Family, Child room, Bedroom, Workday and Always-comfort templates create ordinary schedule rows that remain fully editable. A manual **Sleep**, Comfort, Away or custom-temperature override ends automatically at the next schedule boundary; if no future transition exists, it remains active until explicitly cleared. Whole-house preset actions apply the same temporary policy to every zone.
|
||||
|
||||
Automations remain available for advanced exceptions. Daily comfort should be implemented with zones/profiles/schedules so direct-device automation commands do not compete with the thermostat engine.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "gree_controller",
|
||||
"name": "GREE Controller",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"config_flow": true,
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
|
||||
+5
-5
@@ -248,9 +248,9 @@
|
||||
"house.outdoor": "Outdoor",
|
||||
"house.modeUpdated": "Home climate mode updated",
|
||||
"zones.modePolicy": "Mode policy",
|
||||
"zones.modePolicyHint": "If this zone should inherit the mode selected globally in the app, leave “Follow house mode” unchanged. “Cooling only” or “Heating only” forces a fixed mode only for this zone.",
|
||||
"zones.followHouse": "Follow house mode",
|
||||
"zones.followHouseShort": "House",
|
||||
"zones.modePolicyHint": "If this zone should use the global Heating/Cooling mode selected in GREE Controller, leave “Follow global mode” unchanged. “Cooling only” or “Heating only” forces a fixed mode only for this zone.",
|
||||
"zones.followHouse": "Follow global mode",
|
||||
"zones.followHouseShort": "Global",
|
||||
"zones.profileTemperatures": "Comfort / sleep / away temperatures",
|
||||
"zones.minAdjust": "Minimum adjustment interval (s)",
|
||||
"zones.standbyOffset": "Standby setpoint offset °C",
|
||||
@@ -281,7 +281,7 @@
|
||||
"settings.outdoorAssist": "Use outdoor temperature as smart-control assist",
|
||||
"settings.allowInvalidTls": "Allow invalid/self-signed HTTPS certificate",
|
||||
"settings.allowInvalidTlsHint": "Use only for a trusted local Home Assistant server, for example https://192.168.50.25.",
|
||||
"zones.sleepNow": "Sleep now",
|
||||
"zones.sleepNow": "Sleep",
|
||||
"house.autoAll": "Auto schedule",
|
||||
"house.comfortAll": "Comfort all",
|
||||
"house.sleepAll": "Sleep all",
|
||||
@@ -558,7 +558,7 @@
|
||||
"groups.membersHint": "Groups are built from thermostat zones so their temperature profiles and schedules keep working.",
|
||||
"groups.noMembers": "No members",
|
||||
"groups.mixed": "Mixed",
|
||||
"groups.followHouse": "House / Auto",
|
||||
"groups.followHouse": "Global mode",
|
||||
"groups.mode": "Mode",
|
||||
"groups.profile": "Profile",
|
||||
"groups.masterOff": "Global power is off. Group settings are saved, but units will stay off until global power is enabled.",
|
||||
|
||||
+5
-5
@@ -248,9 +248,9 @@
|
||||
"house.outdoor": "Na zewnątrz",
|
||||
"house.modeUpdated": "Zmieniono tryb klimatu domu",
|
||||
"zones.modePolicy": "Polityka trybu",
|
||||
"zones.modePolicyHint": "Jeśli strefa ma dziedziczyć tryb ustawiony globalnie w aplikacji, zostaw „Dziedzicz tryb domu” i nie zmieniaj tej opcji. „Tylko chłodzenie” lub „Tylko grzanie” wymusza stały tryb tylko dla tej strefy.",
|
||||
"zones.followHouse": "Dziedzicz tryb domu",
|
||||
"zones.followHouseShort": "Dom",
|
||||
"zones.modePolicyHint": "Jeśli strefa ma korzystać z globalnego trybu Grzanie/Chłodzenie ustawionego w GREE Controller, zostaw „Dziedzicz tryb globalny” i nie zmieniaj tej opcji. „Tylko chłodzenie” lub „Tylko grzanie” wymusza stały tryb tylko dla tej strefy.",
|
||||
"zones.followHouse": "Dziedzicz tryb globalny",
|
||||
"zones.followHouseShort": "Globalny",
|
||||
"zones.profileTemperatures": "Temperatury komfort / sen / poza domem",
|
||||
"zones.minAdjust": "Minimalny odstęp regulacji (s)",
|
||||
"zones.standbyOffset": "Przesunięcie temperatury standby °C",
|
||||
@@ -281,7 +281,7 @@
|
||||
"settings.outdoorAssist": "Używaj temperatury zewnętrznej jako wsparcia inteligentnego sterowania",
|
||||
"settings.allowInvalidTls": "Zezwól na nieważny/samopodpisany certyfikat HTTPS",
|
||||
"settings.allowInvalidTlsHint": "Używaj tylko dla zaufanego lokalnego serwera Home Assistant, np. https://192.168.50.25.",
|
||||
"zones.sleepNow": "Sen teraz",
|
||||
"zones.sleepNow": "Sen",
|
||||
"house.autoAll": "Auto harmonogram",
|
||||
"house.comfortAll": "Komfort wszędzie",
|
||||
"house.sleepAll": "Sen wszędzie",
|
||||
@@ -558,7 +558,7 @@
|
||||
"groups.membersHint": "Grupy bazują na strefach termostatów, więc nadal działają ich profile temperatur i harmonogramy.",
|
||||
"groups.noMembers": "Brak członków",
|
||||
"groups.mixed": "Różne",
|
||||
"groups.followHouse": "Dom / Auto",
|
||||
"groups.followHouse": "Tryb globalny",
|
||||
"groups.mode": "Tryb",
|
||||
"groups.profile": "Profil",
|
||||
"groups.masterOff": "Zasilanie globalne jest wyłączone. Ustawienia grupy zostaną zapisane, ale jednostki pozostaną wyłączone do włączenia zasilania globalnego.",
|
||||
|
||||
+11
-7
@@ -621,7 +621,9 @@ function deviceCard(device, detailed = false) {
|
||||
const responseTime = hasResponseTime ? `${Math.max(0, Math.round(Number(device.response_time_ms)))} ms` : '— ms';
|
||||
const protocol = device.simulated ? tr('devices.simulator') : device.protocol_version === 2 ? 'V2 GCM' : device.protocol_version === 1 ? 'V1 ECB' : tr('devices.protocolAuto');
|
||||
const networkInfo = `<div class="device-network-info"><small>${esc(device.ip)} · ${esc(responseTime)} · ${esc(protocol)}</small>${device.last_error ? `<small class="device-network-error" title="${esc(device.last_error)}">${esc(device.last_error)}</small>` : ''}</div>`;
|
||||
return `<article class="device-card ${device.power ? '' : 'off'}" data-device-card="${esc(device.id)}">
|
||||
const quickClass = detailed ? '' : ' quick-control-card quick-device-control';
|
||||
const quickRow = detailed ? '' : ' quick-control-row';
|
||||
return `<article class="device-card${quickClass} ${device.power ? '' : 'off'}" data-device-card="${esc(device.id)}">
|
||||
<div class="device-head">
|
||||
<div class="device-title"><h3>${esc(device.name)}</h3><p><span class="status ${device.online ? 'online' : ''}">${esc(tr(device.online ? 'status.online' : 'status.offline'))}</span> · ${esc(device.model || device.mac)}</p></div>
|
||||
<button class="power-button ${device.power ? 'on' : ''}" data-action="power" data-device="${esc(device.id)}" aria-label="${esc(tr('devices.power'))}">⏻</button>
|
||||
@@ -632,9 +634,9 @@ function deviceCard(device, detailed = false) {
|
||||
<button data-action="temperature" data-delta="1" data-device="${esc(device.id)}">+</button>
|
||||
</div>
|
||||
<div class="current-line">${esc(tr('devices.currentTemperature'))}: <strong>${fmtTemp(device.current_temperature)}</strong>${device.outdoor_temperature == null ? '' : ` · ${esc(tr('devices.outdoor'))} ${fmtTemp(device.outdoor_temperature)}`}</div>
|
||||
<div class="mode-row">${modes.map(mode => `<button class="${device.mode === mode ? 'active' : ''}" data-action="mode" data-value="${mode}" data-device="${esc(device.id)}">${esc(modeLabel(mode))}</button>`).join('')}</div>
|
||||
<div class="fan-row">${fans.map(fan => `<button class="${Number(device.fan_speed) === fan ? 'active' : ''}" data-action="fan" data-value="${fan}" data-device="${esc(device.id)}">${esc(fanLabel(fan))}</button>`).join('')}</div>
|
||||
<div class="device-toggles">
|
||||
<div class="mode-row${quickRow}${detailed ? '' : ' quick-control-row-5'}">${modes.map(mode => `<button class="${device.mode === mode ? 'active' : ''}" data-action="mode" data-value="${mode}" data-device="${esc(device.id)}">${esc(modeLabel(mode))}</button>`).join('')}</div>
|
||||
<div class="fan-row${quickRow}${detailed ? '' : ' quick-control-row-4'}">${fans.map(fan => `<button class="${Number(device.fan_speed) === fan ? 'active' : ''}" data-action="fan" data-value="${fan}" data-device="${esc(device.id)}">${esc(fanLabel(fan))}</button>`).join('')}</div>
|
||||
<div class="device-toggles${quickRow}${detailed ? '' : ' quick-control-row-3'}">
|
||||
<button class="${device.swing_vertical ? 'active' : ''}" data-action="toggle" data-field="swing_vertical" data-device="${esc(device.id)}">${esc(tr('devices.swing'))}</button>
|
||||
${device.supports_quiet === false ? '' : `<button class="${device.quiet ? 'active' : ''}" data-action="toggle" data-field="quiet" data-device="${esc(device.id)}">${esc(tr('devices.quiet'))}</button>`}
|
||||
${device.supports_turbo === false ? '' : `<button class="${device.turbo ? 'active' : ''}" data-action="toggle" data-field="turbo" data-device="${esc(device.id)}">${esc(tr('devices.turbo'))}</button>`}
|
||||
@@ -691,13 +693,15 @@ function zoneCard(zone, detailed = true) {
|
||||
const enabledControl = detailed
|
||||
? `<span class="badge ${zone.enabled ? 'active' : ''}">${esc(state)}</span>`
|
||||
: `<button type="button" class="zone-enable-toggle ${zone.enabled ? 'active' : ''}" data-action="zone-enabled" data-id="${esc(zone.id)}" data-value="${zone.enabled ? 'false' : 'true'}" aria-label="${esc(tr(zone.enabled ? 'zones.disable' : 'zones.enable'))}"><span>${zone.enabled ? '✓' : '○'}</span>${esc(state)}</button>`;
|
||||
return `<article class="list-card zone-thermostat ${zone.demand ? 'demanding' : ''} ${zone.enabled ? '' : 'zone-disabled'}">
|
||||
const quickClass = detailed ? '' : ' quick-control-card quick-thermostat-control';
|
||||
const quickRow = detailed ? '' : ' quick-control-row';
|
||||
return `<article class="list-card zone-thermostat${quickClass} ${zone.demand ? 'demanding' : ''} ${zone.enabled ? '' : 'zone-disabled'}">
|
||||
<div class="list-card-head"><div><h3>${esc(zone.name)}</h3><p>${esc(device?.name || tr('common.noDevice'))} · ${esc(zonePresetLabel(zone.active_preset))}</p></div>${enabledControl}</div>
|
||||
<div class="thermostat-main"><div><small>${esc(tr('zones.measurement'))}</small><strong>${fmtTemp(roomTemperature)}</strong></div><div class="temperature-control compact"><button data-action="zone-temperature" data-id="${esc(zone.id)}" data-delta="-0.5">−</button><div class="target-temp compact">${Number.isFinite(target)?target.toFixed(1):'--'}<small>°C</small></div><button data-action="zone-temperature" data-id="${esc(zone.id)}" data-delta="0.5">+</button></div><div><small>${esc(tr('zones.deviceTarget'))}</small><strong>${fmtTemp(zone.device_setpoint)}</strong></div></div>
|
||||
<div class="preset-row">
|
||||
<div class="preset-row${quickRow}${detailed ? '' : ' quick-control-row-4'}">
|
||||
${['auto','comfort','sleep','away'].map(preset=>`<button class="${manual===preset?'active':''}" data-action="zone-preset" data-id="${esc(zone.id)}" data-value="${preset}">${esc(preset==='sleep'?tr('zones.sleepNow'):zonePresetLabel(preset))}</button>`).join('')}
|
||||
</div>
|
||||
<div class="mode-row zone-mode-row"><button class="${mode==='house'?'active':''}" data-action="zone-mode" data-id="${esc(zone.id)}" data-value="house">${esc(tr('zones.followHouseShort'))}</button><button class="${mode==='heat'?'active':''}" data-action="zone-mode" data-id="${esc(zone.id)}" data-value="heat">${esc(modeLabel('heat'))}</button><button class="${mode==='cool'?'active':''}" data-action="zone-mode" data-id="${esc(zone.id)}" data-value="cool">${esc(modeLabel('cool'))}</button></div>
|
||||
<div class="mode-row zone-mode-row${quickRow}${detailed ? '' : ' quick-control-row-3'}"><button class="${mode==='house'?'active':''}" data-action="zone-mode" data-id="${esc(zone.id)}" data-value="house">${esc(tr('zones.followHouseShort'))}</button><button class="${mode==='heat'?'active':''}" data-action="zone-mode" data-id="${esc(zone.id)}" data-value="heat">${esc(modeLabel('heat'))}</button><button class="${mode==='cool'?'active':''}" data-action="zone-mode" data-id="${esc(zone.id)}" data-value="cool">${esc(modeLabel('cool'))}</button></div>
|
||||
<div class="zone-state-line"><span>${zone.demand ? esc(tr('zones.requesting')) : esc(tr('zones.satisfied'))}</span><span>${esc(override)}</span></div>
|
||||
${detailed ? `<div class="sensor-detail">${esc(sensorDetails)}</div><div class="card-footer"><small>${esc(tr('zones.quickHintSmart'))}</small><div class="card-menu"><button data-action="edit-zone" data-id="${esc(zone.id)}">${esc(tr('actions.edit'))}</button><button class="danger" data-action="delete-zone" data-id="${esc(zone.id)}">${esc(tr('actions.delete'))}</button></div></div>` : ''}
|
||||
</article>`;
|
||||
|
||||
+2
-2
@@ -375,8 +375,8 @@
|
||||
<label><span data-i18n="common.name">Name</span><input name="name" required data-i18n-placeholder="placeholder.livingRoom" placeholder="Living room"></label>
|
||||
<label><span data-i18n="common.device">Device</span><select name="device_id" required></select></label>
|
||||
<div class="copy-settings-row"><select id="copyZoneSource"><option value="" data-i18n="zones.copyFrom">Copy thermostat settings from…</option></select><button type="button" class="secondary" id="copyZoneSettings" data-i18n="zones.copySettings">Copy settings</button></div>
|
||||
<div class="two"><label><span data-i18n="zones.modePolicy">Mode policy</span><select name="mode_policy"><option value="house" data-i18n="zones.followHouse">Follow house mode</option><option value="cool" data-i18n="mode.cool">Cooling only</option><option value="heat" data-i18n="mode.heat">Heating only</option></select></label><label><span data-i18n="common.targetC">Manual target °C</span><input type="text" inputmode="decimal" step="0.5" min="8" max="30" name="setpoint" value="23"></label></div>
|
||||
<p class="field-note" data-i18n="zones.modePolicyHint">If this zone should inherit the mode selected globally in the app, leave “Follow house mode” unchanged. Cooling only / Heating only forces a fixed mode for this zone.</p>
|
||||
<div class="two"><label><span data-i18n="zones.modePolicy">Mode policy</span><select name="mode_policy"><option value="house" data-i18n="zones.followHouse">Follow global mode</option><option value="cool" data-i18n="mode.cool">Cooling only</option><option value="heat" data-i18n="mode.heat">Heating only</option></select></label><label><span data-i18n="common.targetC">Manual target °C</span><input type="text" inputmode="decimal" step="0.5" min="8" max="30" name="setpoint" value="23"></label></div>
|
||||
<p class="field-note" data-i18n="zones.modePolicyHint">If this zone should use the global Heating/Cooling mode selected in GREE Controller, leave “Follow global mode” unchanged. Cooling only / Heating only forces a fixed mode for this zone.</p>
|
||||
<details class="profile-editor"><summary data-i18n="zones.profileTemperatures">Comfort / sleep / away temperatures</summary>
|
||||
<div class="profile-grid"><strong data-i18n="mode.cool">Cooling</strong><label><span data-i18n="preset.comfort">Comfort</span><input type="text" inputmode="decimal" step="0.5" name="cool_comfort_setpoint" value="23"></label><label><span data-i18n="preset.sleep">Sleep</span><input type="text" inputmode="decimal" step="0.5" name="cool_sleep_setpoint" value="24.5"></label><label><span data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" step="0.5" name="cool_away_setpoint" value="27"></label></div>
|
||||
<div class="profile-grid"><strong data-i18n="mode.heat">Heating</strong><label><span data-i18n="preset.comfort">Comfort</span><input type="text" inputmode="decimal" step="0.5" name="heat_comfort_setpoint" value="21"></label><label><span data-i18n="preset.sleep">Sleep</span><input type="text" inputmode="decimal" step="0.5" name="heat_sleep_setpoint" value="19"></label><label><span data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" step="0.5" name="heat_away_setpoint" value="17"></label></div>
|
||||
|
||||
+51
-46
@@ -732,19 +732,41 @@ body.simulation-standalone [data-view="simulation"] { min-height:calc(100vh - 36
|
||||
.dashboard-disclosure-body .device-grid,
|
||||
.dashboard-disclosure-body .list-grid,
|
||||
.dashboard-disclosure-body .automation-plan-grid { gap:10px; grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr)); }
|
||||
#dashboardZones .list-card, #dashboardDevices .device-card { padding:15px; }
|
||||
#dashboardZones .preset-row,
|
||||
#dashboardZones .mode-row,
|
||||
#dashboardDevices .mode-row,
|
||||
#dashboardDevices .fan-row { display:grid; grid-template-columns:repeat(auto-fit,minmax(70px,1fr)); gap:5px; overflow:visible; padding:2px 0 6px; }
|
||||
#dashboardZones .preset-row button,
|
||||
#dashboardZones .mode-row button,
|
||||
#dashboardDevices .mode-row button,
|
||||
#dashboardDevices .fan-row button { min-width:0; min-height:32px; padding:6px 7px; font-size:10px; }
|
||||
#dashboardDevices .temperature-control { gap:16px; padding:18px 0 14px; }
|
||||
#dashboardDevices .target-temp { min-width:92px; font-size:40px; }
|
||||
#dashboardDevices .current-line { margin:-6px 0 13px; }
|
||||
#dashboardZones .thermostat-main { margin:12px 0 8px; }
|
||||
/* Dashboard quick controls share one compact visual language. */
|
||||
.quick-control-card {
|
||||
--quick-control-height:28px;
|
||||
--quick-control-gap:4px;
|
||||
padding:14px;
|
||||
}
|
||||
.quick-control-card .quick-control-row {
|
||||
display:grid;
|
||||
gap:var(--quick-control-gap);
|
||||
overflow:visible;
|
||||
padding:2px 0 5px;
|
||||
}
|
||||
.quick-control-card .quick-control-row-3 { grid-template-columns:repeat(3,minmax(0,1fr)); }
|
||||
.quick-control-card .quick-control-row-4 { grid-template-columns:repeat(4,minmax(0,1fr)); }
|
||||
.quick-control-card .quick-control-row-5 { grid-template-columns:repeat(5,minmax(0,1fr)); }
|
||||
.quick-control-card .quick-control-row button {
|
||||
min-width:0;
|
||||
min-height:var(--quick-control-height);
|
||||
padding:4px 5px;
|
||||
font-size:9px;
|
||||
line-height:1.05;
|
||||
white-space:nowrap;
|
||||
}
|
||||
.quick-control-card .power-button { width:38px; height:38px; font-size:18px; }
|
||||
.quick-control-card .temperature-control button { width:34px; height:34px; font-size:18px; }
|
||||
.quick-control-card .target-temp { min-width:80px; font-size:32px; }
|
||||
.quick-control-card .device-title h3,
|
||||
.quick-control-card .list-card-head h3 { font-size:16px; }
|
||||
.quick-control-card .device-title p,
|
||||
.quick-control-card .list-card-head p { font-size:11px; }
|
||||
.quick-device-control .temperature-control { gap:12px; padding:12px 0 10px; }
|
||||
.quick-device-control .current-line { margin:-3px 0 10px; font-size:11px; }
|
||||
.quick-device-control .device-toggles.quick-control-row { margin-top:1px; }
|
||||
.quick-thermostat-control .thermostat-main { margin:8px 0 7px; }
|
||||
.quick-thermostat-control .zone-enable-toggle { min-height:28px; padding:4px 8px; font-size:10px; }
|
||||
|
||||
@media (max-width: 700px) {
|
||||
[data-view="dashboard"] .hero { min-height:145px; padding:20px; }
|
||||
@@ -785,26 +807,26 @@ body.simulation-standalone [data-view="simulation"] { min-height:calc(100vh - 36
|
||||
|
||||
/* Quick thermostat: target control gets its own row, readings no longer collide with it. */
|
||||
#dashboardZones { grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr)); }
|
||||
#dashboardZones .zone-thermostat { min-width:0; }
|
||||
#dashboardZones .thermostat-main {
|
||||
.quick-thermostat-control { min-width:0; }
|
||||
.quick-thermostat-control .thermostat-main {
|
||||
grid-template-columns:minmax(0,1fr) minmax(0,1fr);
|
||||
grid-template-areas:"control control" "room device";
|
||||
gap:5px 10px;
|
||||
margin:8px 0 7px;
|
||||
}
|
||||
#dashboardZones .thermostat-main > div:first-child { grid-area:room; min-width:0; text-align:left; }
|
||||
#dashboardZones .thermostat-main > .temperature-control { grid-area:control; }
|
||||
#dashboardZones .thermostat-main > div:last-child { grid-area:device; min-width:0; text-align:right; }
|
||||
#dashboardZones .thermostat-main small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
#dashboardZones .thermostat-main strong { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:14px; }
|
||||
#dashboardZones .thermostat-main .temperature-control.compact { gap:12px; padding:0 0 2px; }
|
||||
#dashboardZones .thermostat-main .temperature-control.compact button { width:36px; height:36px; font-size:18px; }
|
||||
#dashboardZones .thermostat-main .target-temp.compact { min-width:80px; font-size:30px; }
|
||||
#dashboardZones .list-card-head h3 { font-size:16px; }
|
||||
#dashboardZones .list-card-head p { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:11px; }
|
||||
#dashboardZones .zone-enable-toggle { flex:0 0 auto; }
|
||||
#dashboardZones .zone-state-line { gap:8px; font-size:10px; }
|
||||
#dashboardZones .zone-state-line span { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.quick-thermostat-control .thermostat-main > div:first-child { grid-area:room; min-width:0; text-align:left; }
|
||||
.quick-thermostat-control .thermostat-main > .temperature-control { grid-area:control; }
|
||||
.quick-thermostat-control .thermostat-main > div:last-child { grid-area:device; min-width:0; text-align:right; }
|
||||
.quick-thermostat-control .thermostat-main small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
.quick-thermostat-control .thermostat-main strong { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:14px; }
|
||||
.quick-thermostat-control .thermostat-main .temperature-control.compact { gap:12px; padding:0 0 2px; }
|
||||
.quick-thermostat-control .thermostat-main .temperature-control.compact button { width:34px; height:34px; font-size:18px; }
|
||||
.quick-thermostat-control .thermostat-main .target-temp.compact { min-width:80px; font-size:32px; }
|
||||
.quick-thermostat-control .list-card-head h3 { font-size:16px; }
|
||||
.quick-thermostat-control .list-card-head p { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:11px; }
|
||||
.quick-thermostat-control .zone-enable-toggle { flex:0 0 auto; }
|
||||
.quick-thermostat-control .zone-state-line { gap:8px; font-size:10px; }
|
||||
.quick-thermostat-control .zone-state-line span { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
|
||||
/* Smaller Node-RED/simulator-like plan blocks with explicit group context. */
|
||||
#controlPlan.automation-plan-grid { grid-template-columns:repeat(auto-fit,minmax(min(100%,210px),1fr)); gap:8px; }
|
||||
@@ -854,21 +876,4 @@ body.simulation-standalone [data-view="simulation"] { min-height:calc(100vh - 36
|
||||
}
|
||||
#zoneList .zone-thermostat .card-menu { flex:0 0 auto; }
|
||||
|
||||
@media (min-width: 701px) {
|
||||
#dashboardDevices .mode-row {
|
||||
grid-template-columns:repeat(5,minmax(0,1fr));
|
||||
gap:4px;
|
||||
}
|
||||
#dashboardDevices .fan-row {
|
||||
grid-template-columns:repeat(4,minmax(0,1fr));
|
||||
gap:4px;
|
||||
}
|
||||
#dashboardDevices .mode-row button,
|
||||
#dashboardDevices .fan-row button {
|
||||
min-height:26px;
|
||||
padding:4px 3px;
|
||||
font-size:9px;
|
||||
line-height:1.05;
|
||||
white-space:nowrap;
|
||||
}
|
||||
}
|
||||
/* 0.7.1: quick thermostats and quick manual control now use the shared quick-control classes above. */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'gree-controller-v070-schedule-audit-ui';
|
||||
const CACHE = 'gree-controller-v071-quick-controls';
|
||||
const SCOPE = new URL(self.registration.scope).pathname.replace(/\/$/, '');
|
||||
const path = value => `${SCOPE}${value.startsWith('/') ? value : `/${value}`}` || '/';
|
||||
const ASSETS = [path('/'), path('/styles.css'), path('/app.js'), path('/theme-init.js'), path('/favicon.svg'), path('/manifest.webmanifest'), path('/lang/index.json'), path('/lang/en.json')];
|
||||
|
||||
Reference in New Issue
Block a user