v0.6.9
This commit is contained in:
+12
-1
@@ -1,4 +1,4 @@
|
||||
# GREE Controller v0.6.8 - build and validation report
|
||||
# GREE Controller v0.6.9 - build and validation report
|
||||
|
||||
## Scope
|
||||
|
||||
@@ -180,3 +180,14 @@ Validation performed in this packaging environment:
|
||||
- Kept Control plan, Zones and Devices as collapsed disclosure panels.
|
||||
- Reduced dashboard hero, metric, global-control, group-card and disclosure spacing for a denser layout without removing functionality.
|
||||
- Advanced PWA cache key to `gree-controller-v068-dashboard-wrap`.
|
||||
|
||||
|
||||
## 0.6.9 dashboard fit, compact summary and group-aware control plan
|
||||
|
||||
- Reworked dashboard Quick Thermostats so the target-temperature control occupies its own row and measured/device-target temperatures sit below it; labels no longer collide or clip in medium-width cards.
|
||||
- Kept the quick-thermostat list as a wrapped responsive grid with no horizontal scrolling.
|
||||
- Compressed the Home / Comfort under control summary into one short card; average temperature and small online/active/demand counters now share the same row instead of consuming a separate large hero plus metrics row.
|
||||
- Reduced dashboard control-plan blocks to compact simulator-style cards.
|
||||
- Added explicit climate-group cards to What happens next, combining member demand and upcoming member-zone events.
|
||||
- Zone plan cards identify their climate group membership, and automation plan rows preserve group targets.
|
||||
- Advanced the PWA cache key to `gree-controller-v069-dashboard-fit`.
|
||||
|
||||
Generated
+1
-1
@@ -633,7 +633,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gree-controller"
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-gcm",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "gree-controller"
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
edition = "2021"
|
||||
authors = ["GREE Controller contributors"]
|
||||
description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support"
|
||||
|
||||
+11
-11
@@ -1,10 +1,10 @@
|
||||
d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env.example
|
||||
a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore
|
||||
4c31bf357041b674d29a7ab5751b6060ad8650178e227aa3137d9195a49a2c03 ./BUILD_REPORT.md
|
||||
006a65924eb99c5c911edeea96f6f1498f44734eaf638af81d9562f7e41faaf2 ./Cargo.lock
|
||||
b0dfc13a0710b9f4cc0d2417129335ef277509ce951bcf85781bc38cf8aa892e ./Cargo.toml
|
||||
e6eb0b2882aa446546fc5a4e63fb11973e79483c263fa053fc10cb2e9aaf53bb ./BUILD_REPORT.md
|
||||
9227483cd15d1d93b01b312d119179c8348cffda4bd4870571cdabfa8e972b14 ./Cargo.lock
|
||||
b8fcf5528e25c4823ca90b2565dd025994aafadfa35360af475db66ef53d2359 ./Cargo.toml
|
||||
19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE
|
||||
1dbfc1eab7344a021cdce4859b5ae03faf30974d3b9c7c46d49e933bb34cdfdd ./README.md
|
||||
191d7fd1242937ba1c3df7f0b3014c56a6944f6445cd1453832b4dc8024d37bf ./README.md
|
||||
f2c8eb28fedb26bb8f5636e1805e7a782cc472b87d882f6752d520f16216a248 ./build.rs
|
||||
74fc74d2bb05541f2137250c6a73a9f39a6fbd838e5d95d8c8efd7ebefc384f0 ./docs/API.md
|
||||
234dd200e380a13ecd3e61b4ea455f6f08d64ce89382077dee80684acadb9703 ./docs/HOME_ASSISTANT_MIGRATION.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
|
||||
3019758a902fbd38b1faf7d25ed2bea8e4ab124dca34cc6fe0d05a9d4e5f3714 ./home-assistant/custom_components/gree_controller/manifest.json
|
||||
97117268892b7868ff4001017d422c30a94b68f4c656cbb8c5fa1d65c23fd65f ./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
|
||||
3de7cd88c5c879ba797e805962afa7d449fbd1a016ca8ecf4740d2348ebd39f7 ./lang/en.json
|
||||
1a23e91ed6bec4850cd1304dad9669041b08ea75114f3de3ececd5d5eaeee258 ./lang/pl.json
|
||||
6385512c4d23d53abca0c1e178420733baf30232a20d00e53688c7b8da91ac01 ./lang/en.json
|
||||
067888c4d220169604303f3236e4832e2935bf2a20633cbff7fa86ec8dc9785d ./lang/pl.json
|
||||
028e1f16e9fbaed57cadb88eff04e65b4bd67722c50b4d6b1fb525f5a2f39abf ./make_zip.py
|
||||
bb89bac237e750e9b1bf73761d7df97a6b81853091615878c03f13d7b6399aa7 ./scripts/README.md
|
||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh
|
||||
@@ -62,10 +62,10 @@ a910bd9432a393740c0f6fab52bfcb551f0ea756718d66d290fd2610767cf07c ./src/protocol
|
||||
66d7a1a12843975506ffef2128d161627b96d665d39cedde2d791651e4598ee1 ./src/queries.rs
|
||||
f74ac398c6f2e64ebb965900330324f3ec7403e70339638a408229f29fbd07ae ./src/state.rs
|
||||
b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service
|
||||
d1971287c12f5d022e0607dca6ec065d11903432fa6ec0df5584cb5ad0a524e2 ./web/app.js
|
||||
dfb89acd9b040c32b24146198d66ca98a0bc3dfbc852ac03b2ca3d252923ec3e ./web/app.js
|
||||
e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg
|
||||
e665efcc1fac4138d6ce83309e5c6766e80901292bd25983d35613ff3c754b9d ./web/index.html
|
||||
f3b864558d327d769a6d4a33e88b96a518ec900ce39d6af990bd9cabb3b075a5 ./web/index.html
|
||||
fd26156e9f1d6713d3def564ad000553d9a16a24376059701db2ee762c99ee6c ./web/manifest.webmanifest
|
||||
0e9e57efd2ba706c315c34d43c95930455fa3ce720bfac6a506b89a755fc4349 ./web/styles.css
|
||||
9099a3d1d4b2775241728b22fa81cd07ecebc34cb160a729feeca7af3efcdf03 ./web/sw.js
|
||||
77c96fe9ca242edb44defe2f7df40b99e7ec2c546d8d13c1c31cc55beea6522c ./web/styles.css
|
||||
17f83bd268aac0a48e0ad230b970ca868df330f8884cc03747d8bee9aa8aef5d ./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.6.8**.
|
||||
Current version: **0.6.9**.
|
||||
|
||||
## Highlights
|
||||
|
||||
@@ -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.6.8.zip
|
||||
unzip gree-controller-v0.6.9.zip
|
||||
cd gree-controller
|
||||
chmod +x scripts/*.sh
|
||||
./scripts/dev.sh
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "gree_controller",
|
||||
"name": "GREE Controller",
|
||||
"version": "0.6.8",
|
||||
"version": "0.6.9",
|
||||
"config_flow": true,
|
||||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
|
||||
@@ -362,6 +362,8 @@
|
||||
"plan.rules": "Extra automations",
|
||||
"plan.ruleCount": "{count} active rules",
|
||||
"plan.event": "Change",
|
||||
"plan.groupSummary": "Group zones: {zones} · requesting action: {demand}",
|
||||
"plan.groupOff": "Group is turned off",
|
||||
"zones.enable": "Enable zone",
|
||||
"zones.disable": "Disable zone",
|
||||
"history.90d": "90 days",
|
||||
|
||||
@@ -362,6 +362,8 @@
|
||||
"plan.rules": "Dodatkowe automatyzacje",
|
||||
"plan.ruleCount": "Aktywne reguły: {count}",
|
||||
"plan.event": "Zmiana",
|
||||
"plan.groupSummary": "Strefy w grupie: {zones} · żądające działania: {demand}",
|
||||
"plan.groupOff": "Grupa jest wyłączona",
|
||||
"zones.enable": "Włącz strefę",
|
||||
"zones.disable": "Wyłącz strefę",
|
||||
"history.90d": "90 dni",
|
||||
|
||||
+35
-11
@@ -285,18 +285,42 @@ function renderControlPlan() {
|
||||
host.innerHTML = `<div class="panel plan-loading">${esc(tr('plan.loading'))}</div>`;
|
||||
return;
|
||||
}
|
||||
const houseEvents = (plan.next_events || []).slice(0, 5);
|
||||
const house = `<article class="panel plan-card plan-house"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.house'))}</span><h3>${esc(houseModeLabel(plan.house_mode || 'off'))}</h3></div><span class="badge active">${esc(plan.control_strategy || 'setpoint')}</span></div><p>${esc(tr('plan.houseSummary', {zones:(plan.zones || []).filter(zone=>zone.enabled).length, demand:(plan.zones || []).filter(zone=>zone.enabled && zone.demand).length}))}</p><ul class="plan-events">${houseEvents.length ? houseEvents.map(planEventMarkup).join('') : `<li class="muted">${esc(tr('plan.noEvents'))}</li>`}</ul></article>`;
|
||||
const zones = (plan.zones || []).map(zone => {
|
||||
const events = (zone.next_events || []).slice(0, 3);
|
||||
const target = zone.target_temperature == null ? '--' : Number(zone.target_temperature).toFixed(1);
|
||||
return `<article class="panel plan-card ${zone.enabled ? '' : 'disabled'}"><div class="plan-card-head"><div><span class="eyebrow">${esc(zone.device_name || tr('common.noDevice'))}</span><h3>${esc(zone.zone_name)}</h3></div><span class="badge ${zone.enabled && zone.demand ? 'active' : ''}">${esc(zone.enabled ? (zone.demand ? tr('zones.requesting') : tr('zones.satisfied')) : tr('common.disabled'))}</span></div><div class="plan-temp"><span>${fmtTemp(zone.current_temperature)}</span><b>→</b><strong>${esc(target)}<small>°C</small></strong></div><p>${esc(houseModeLabel(zone.mode || 'off'))} · ${esc(zonePresetLabel(zone.preset))}${zone.current_schedule_name ? ` · ${esc(zone.current_schedule_name)}` : ''}</p><ul class="plan-events">${events.length ? events.map(planEventMarkup).join('') : `<li class="muted">${esc(tr('plan.noEvents'))}</li>`}</ul></article>`;
|
||||
}).join('');
|
||||
const rules = (plan.rules || []).filter(rule => rule.enabled);
|
||||
const ruleCard = rules.length ? `<article class="panel plan-card"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.rules'))}</span><h3>${esc(tr('plan.ruleCount', {count:rules.length}))}</h3></div></div><ul class="plan-events">${rules.slice(0,5).map(rule=>`<li><time>${esc(automationTriggerLabel(rule))}</time><span>${esc(rule.name)} → ${esc(rule.action_device_name || '')}</span></li>`).join('')}</ul></article>` : '';
|
||||
host.innerHTML = house + zones + ruleCard;
|
||||
}
|
||||
|
||||
const allZones = plan.zones || [];
|
||||
const houseEvents = (plan.next_events || []).slice(0, 3);
|
||||
const house = `<article class="panel plan-card plan-house"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.house'))}</span><h3>${esc(houseModeLabel(plan.house_mode || 'off'))}</h3></div><span class="badge active">${esc(plan.control_strategy || 'setpoint')}</span></div><p>${esc(tr('plan.houseSummary', {zones:allZones.filter(zone=>zone.enabled).length, demand:allZones.filter(zone=>zone.enabled && zone.demand).length}))}</p><ul class="plan-events">${houseEvents.length ? houseEvents.map(planEventMarkup).join('') : `<li class="muted">${esc(tr('plan.noEvents'))}</li>`}</ul></article>`;
|
||||
|
||||
const groupCards = (app.groups || []).map(group => {
|
||||
const memberIds = new Set(group.zone_ids || []);
|
||||
const members = allZones.filter(zone => memberIds.has(zone.zone_id));
|
||||
const state = groupState(group);
|
||||
const powerEnabled = group.power_enabled !== false;
|
||||
const demand = members.filter(zone => zone.enabled && zone.demand).length;
|
||||
const mode = state.mode === 'house' ? tr('groups.followHouse') : state.mode === 'mixed' ? tr('groups.mixed') : modeLabel(state.mode);
|
||||
const preset = state.preset === 'mixed' ? tr('groups.mixed') : zonePresetLabel(state.preset);
|
||||
const events = members.flatMap(zone => (zone.next_events || []).map(event => ({...event, label:`${zone.zone_name}: ${event.label}`})))
|
||||
.sort((a,b) => new Date(a.at) - new Date(b.at)).slice(0, 2);
|
||||
return `<article class="panel plan-card plan-group ${powerEnabled ? '' : 'disabled'}"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('groups.group'))}</span><h3>${esc(group.name)}</h3></div><span class="badge ${powerEnabled ? 'active' : ''}">${esc(powerEnabled ? tr('common.on') : tr('common.off'))}</span></div><div class="plan-group-state"><strong>${esc(mode)}</strong><span>·</span><strong>${esc(preset)}</strong></div><p>${esc(tr('plan.groupSummary', {zones:members.length, demand}))}</p><ul class="plan-events">${events.length ? events.map(planEventMarkup).join('') : `<li class="muted">${esc(tr(powerEnabled ? 'plan.noEvents' : 'plan.groupOff'))}</li>`}</ul></article>`;
|
||||
}).join('');
|
||||
|
||||
const zoneGroups = new Map();
|
||||
(app.groups || []).forEach(group => (group.zone_ids || []).forEach(zoneId => {
|
||||
const names = zoneGroups.get(zoneId) || [];
|
||||
names.push(group.name);
|
||||
zoneGroups.set(zoneId, names);
|
||||
}));
|
||||
const zones = allZones.map(zone => {
|
||||
const events = (zone.next_events || []).slice(0, 2);
|
||||
const target = zone.target_temperature == null ? '--' : Number(zone.target_temperature).toFixed(1);
|
||||
const groupNames = zoneGroups.get(zone.zone_id) || [];
|
||||
const scope = groupNames.length ? `${tr('groups.group')}: ${groupNames.join(' · ')}` : (zone.device_name || tr('common.noDevice'));
|
||||
return `<article class="panel plan-card plan-zone ${zone.enabled ? '' : 'disabled'}"><div class="plan-card-head"><div><span class="eyebrow">${esc(scope)}</span><h3>${esc(zone.zone_name)}</h3></div><span class="badge ${zone.enabled && zone.demand ? 'active' : ''}">${esc(zone.enabled ? (zone.demand ? tr('zones.requesting') : tr('zones.satisfied')) : tr('common.disabled'))}</span></div><div class="plan-temp"><span>${fmtTemp(zone.current_temperature)}</span><b>→</b><strong>${esc(target)}<small>°C</small></strong></div><p>${esc(houseModeLabel(zone.mode || 'off'))} · ${esc(zonePresetLabel(zone.preset))}${zone.current_schedule_name ? ` · ${esc(zone.current_schedule_name)}` : ''}</p><ul class="plan-events">${events.length ? events.map(planEventMarkup).join('') : `<li class="muted">${esc(tr('plan.noEvents'))}</li>`}</ul></article>`;
|
||||
}).join('');
|
||||
|
||||
const rules = (plan.rules || []).filter(rule => rule.enabled);
|
||||
const ruleCard = rules.length ? `<article class="panel plan-card plan-rules"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.rules'))}</span><h3>${esc(tr('plan.ruleCount', {count:rules.length}))}</h3></div></div><ul class="plan-events">${rules.slice(0,3).map(rule=>`<li><time>${esc(automationTriggerLabel(rule))}</time><span>${esc(rule.name)} → ${esc(rule.action_group_name ? `${tr('groups.group')}: ${rule.action_group_name}` : (rule.action_device_name || ''))}</span></li>`).join('')}</ul></article>` : '';
|
||||
host.innerHTML = house + groupCards + zones + ruleCard;
|
||||
}
|
||||
async function loadControlPlan() {
|
||||
try { app.controlPlan = await api('/api/control-plan'); renderControlPlan(); renderSimulationPage(); }
|
||||
catch (error) { console.warn('Unable to load control plan:', error); }
|
||||
|
||||
+3
-4
@@ -32,11 +32,10 @@
|
||||
|
||||
<main>
|
||||
<section class="view active" data-view="dashboard">
|
||||
<div class="hero">
|
||||
<div><span class="eyebrow" data-i18n="dashboard.home">Home</span><h1 data-i18n="dashboard.title">Comfort under control</h1><p id="summaryText" data-i18n="dashboard.loading">Loading devices…</p></div>
|
||||
<div class="hero-temp" id="heroTemperature">--<small>°C</small></div>
|
||||
<div class="hero dashboard-summary-card">
|
||||
<div class="dashboard-summary-copy"><span class="eyebrow" data-i18n="dashboard.home">Home</span><h1 data-i18n="dashboard.title">Comfort under control</h1><p id="summaryText" data-i18n="dashboard.loading">Loading devices…</p></div>
|
||||
<div class="dashboard-summary-status"><div class="metrics" id="metrics"></div><div class="hero-temp" id="heroTemperature">--<small>°C</small></div></div>
|
||||
</div>
|
||||
<div class="metrics" id="metrics"></div>
|
||||
<div class="panel dashboard-global-panel">
|
||||
<div class="dashboard-global-power">
|
||||
<div><span class="eyebrow" data-i18n="house.powerAllEyebrow">Global power</span><h3 data-i18n="house.powerAllTitle">All air conditioners</h3><p data-i18n="house.powerAllHint">Master power for all enabled units. Whole-house mode or profile selection automatically switches it back on.</p></div>
|
||||
|
||||
@@ -762,3 +762,82 @@ body.simulation-standalone [data-view="simulation"] { min-height:calc(100vh - 36
|
||||
.dashboard-disclosure > summary { padding:12px 13px; }
|
||||
.dashboard-disclosure-body { padding:10px; }
|
||||
}
|
||||
|
||||
/* 0.6.9 dashboard density and fit fixes */
|
||||
[data-view="dashboard"] .dashboard-summary-card {
|
||||
min-height:0;
|
||||
align-items:center;
|
||||
gap:14px;
|
||||
padding:13px 16px;
|
||||
border-radius:18px;
|
||||
}
|
||||
[data-view="dashboard"] .dashboard-summary-card::after { width:100px; height:100px; right:-44px; bottom:-64px; border-width:18px; }
|
||||
[data-view="dashboard"] .dashboard-summary-copy { min-width:0; }
|
||||
[data-view="dashboard"] .dashboard-summary-copy .eyebrow { font-size:9px; }
|
||||
[data-view="dashboard"] .dashboard-summary-copy h1 { margin:2px 0 3px; font-size:20px; letter-spacing:-.025em; }
|
||||
[data-view="dashboard"] .dashboard-summary-copy p { font-size:11px; line-height:1.3; }
|
||||
[data-view="dashboard"] .dashboard-summary-status { z-index:1; display:flex; align-items:center; justify-content:flex-end; min-width:0; gap:10px; }
|
||||
[data-view="dashboard"] .dashboard-summary-status .metrics { display:flex; align-items:center; gap:5px; margin:0; }
|
||||
[data-view="dashboard"] .dashboard-summary-status .metric { display:flex; align-items:baseline; gap:5px; min-width:0; padding:6px 8px; border-radius:11px; background:var(--surface-muted); }
|
||||
[data-view="dashboard"] .dashboard-summary-status .metric span { margin:0; font-size:9px; white-space:nowrap; }
|
||||
[data-view="dashboard"] .dashboard-summary-status .metric strong { font-size:13px; line-height:1; }
|
||||
[data-view="dashboard"] .dashboard-summary-status .hero-temp { min-width:72px; font-size:31px; letter-spacing:-.045em; text-align:right; }
|
||||
|
||||
/* 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 {
|
||||
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; }
|
||||
|
||||
/* 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; }
|
||||
#controlPlan .plan-card { min-height:0; gap:7px; padding:11px; border-radius:14px; }
|
||||
#controlPlan .plan-card-head { gap:7px; }
|
||||
#controlPlan .plan-card-head h3 { margin-top:1px; font-size:15px; line-height:1.2; }
|
||||
#controlPlan .plan-card-head .eyebrow { font-size:8px; line-height:1.25; }
|
||||
#controlPlan .plan-card .badge { max-width:100px; padding:4px 6px; overflow:hidden; font-size:9px; text-overflow:ellipsis; white-space:nowrap; }
|
||||
#controlPlan .plan-card > p { font-size:10px; line-height:1.3; }
|
||||
#controlPlan .plan-temp { gap:5px; }
|
||||
#controlPlan .plan-temp > span { font-size:15px; }
|
||||
#controlPlan .plan-temp > strong { font-size:21px; }
|
||||
#controlPlan .plan-group-state { display:flex; align-items:center; gap:5px; min-width:0; color:var(--text-soft); font-size:11px; }
|
||||
#controlPlan .plan-group-state strong { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
#controlPlan .plan-group { border-color:color-mix(in srgb,var(--accent) 20%,var(--line)); background:color-mix(in srgb,var(--surface) 97%,var(--accent)); }
|
||||
#controlPlan .plan-events { gap:4px; padding-top:6px; }
|
||||
#controlPlan .plan-events li { grid-template-columns:62px minmax(0,1fr); gap:5px; font-size:9px; line-height:1.3; }
|
||||
#controlPlan .plan-events li span { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||
#controlPlan .plan-events .muted { white-space:normal; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
[data-view="dashboard"] .dashboard-summary-card { align-items:flex-start; }
|
||||
[data-view="dashboard"] .dashboard-summary-status { flex-wrap:wrap; }
|
||||
[data-view="dashboard"] .dashboard-summary-status .metrics { order:2; width:100%; justify-content:flex-end; }
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
[data-view="dashboard"] .dashboard-summary-card { min-height:0; padding:12px 13px; }
|
||||
[data-view="dashboard"] .dashboard-summary-copy h1 { font-size:18px; }
|
||||
[data-view="dashboard"] .dashboard-summary-status { align-self:center; }
|
||||
[data-view="dashboard"] .dashboard-summary-status .metrics { display:none; }
|
||||
[data-view="dashboard"] .dashboard-summary-status .hero-temp { min-width:64px; font-size:27px; }
|
||||
#dashboardZones { grid-template-columns:1fr; }
|
||||
#controlPlan.automation-plan-grid { grid-template-columns:repeat(auto-fit,minmax(min(100%,180px),1fr)); }
|
||||
}
|
||||
[data-view="dashboard"] .dashboard-summary-card { margin-bottom:10px; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'gree-controller-v068-dashboard-wrap';
|
||||
const CACHE = 'gree-controller-v069-dashboard-fit';
|
||||
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