node-red-contrib-senec-cloud-v2 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/assets/fonts/senec-sans.ttf +0 -0
- package/dist/lib/dashboard-html-renderer.d.ts +18 -0
- package/dist/lib/dashboard-html-renderer.d.ts.map +1 -0
- package/dist/lib/dashboard-html-renderer.js +604 -0
- package/dist/lib/dashboard-html-renderer.js.map +1 -0
- package/dist/lib/dashboard-layout.d.ts +152 -0
- package/dist/lib/dashboard-layout.d.ts.map +1 -0
- package/dist/lib/dashboard-layout.js +201 -0
- package/dist/lib/dashboard-layout.js.map +1 -0
- package/dist/lib/geocoding-client.d.ts +61 -0
- package/dist/lib/geocoding-client.d.ts.map +1 -0
- package/dist/lib/geocoding-client.js +77 -0
- package/dist/lib/geocoding-client.js.map +1 -0
- package/dist/lib/senec-image-renderer.d.ts +107 -0
- package/dist/lib/senec-image-renderer.d.ts.map +1 -0
- package/dist/lib/senec-image-renderer.js +872 -0
- package/dist/lib/senec-image-renderer.js.map +1 -0
- package/dist/lib/senec-layout.d.ts +212 -0
- package/dist/lib/senec-layout.d.ts.map +1 -0
- package/dist/lib/senec-layout.js +252 -0
- package/dist/lib/senec-layout.js.map +1 -0
- package/dist/lib/weather-client.d.ts +62 -0
- package/dist/lib/weather-client.d.ts.map +1 -0
- package/dist/lib/weather-client.js +234 -0
- package/dist/lib/weather-client.js.map +1 -0
- package/dist/nodes/senec-data.js +10 -2
- package/dist/nodes/senec-data.js.map +1 -1
- package/dist/nodes/senec-image.html +73 -53
- package/dist/nodes/senec-image.js +189 -14
- package/dist/nodes/senec-image.js.map +1 -1
- package/dist/nodes/weather.d.ts +2 -0
- package/dist/nodes/weather.d.ts.map +1 -0
- package/dist/nodes/weather.html +179 -0
- package/dist/nodes/weather.js +138 -0
- package/dist/nodes/weather.js.map +1 -0
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.2] - 2026-07-05
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Weather fetch resilience**: The weather node no longer drops to its
|
|
12
|
+
fallback payload on the first transient Open-Meteo error (e.g. HTTP 503
|
|
13
|
+
Service Unavailable, HTTP 429 rate limiting, timeouts or network errors).
|
|
14
|
+
`WeatherClient.getWeather` now retries transient failures with exponential
|
|
15
|
+
backoff (configurable via `retries` / `retryDelay`, default 2 retries)
|
|
16
|
+
before giving up. Non-transient errors (HTTP 4xx other than 429) still fail
|
|
17
|
+
immediately, and the last-good/sample fallback remains as a last resort.
|
|
18
|
+
|
|
19
|
+
## [0.2.1] - 2026-07-05
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- **Battery "Heute" (today) value**: The daily battery charge/discharge was
|
|
23
|
+
derived from an energy-balance formula whose two halves were exact negatives
|
|
24
|
+
of each other, so the "aus / ein" split could never be consistent and the
|
|
25
|
+
displayed value was misleading. It now computes a single, physically correct
|
|
26
|
+
net battery energy for the day
|
|
27
|
+
(`net = consumption + gridExport - generation - gridImport`) and shows the
|
|
28
|
+
net discharge/charge accordingly.
|
|
29
|
+
|
|
8
30
|
## [0.2.0] - 2026-07-05
|
|
9
31
|
|
|
10
32
|
### Fixed
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard HTML Renderer
|
|
3
|
+
*
|
|
4
|
+
* Produces the fully resizable 16:9 energy dashboard as a standalone HTML
|
|
5
|
+
* document, faithfully adapting the reference spec in
|
|
6
|
+
* docs/00-input/ux/index.html while binding live SENEC + Weather values
|
|
7
|
+
* from the shared {@link DashboardModel}.
|
|
8
|
+
*
|
|
9
|
+
* The output is a self-contained HTML string (inline CSS, no external
|
|
10
|
+
* assets) suitable for output on the second pin of the senec-image node,
|
|
11
|
+
* for embedding in an iframe, or for serving via an HTTP response node.
|
|
12
|
+
*/
|
|
13
|
+
import { DashboardModel } from './dashboard-layout';
|
|
14
|
+
/**
|
|
15
|
+
* Render the complete dashboard HTML document for the given model.
|
|
16
|
+
*/
|
|
17
|
+
export declare function renderDashboardHtml(model: DashboardModel): string;
|
|
18
|
+
//# sourceMappingURL=dashboard-html-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-html-renderer.d.ts","sourceRoot":"","sources":["../../src/lib/dashboard-html-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAe,cAAc,EAAa,MAAM,oBAAoB,CAAC;AA2I5E;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAkejE"}
|
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dashboard HTML Renderer
|
|
4
|
+
*
|
|
5
|
+
* Produces the fully resizable 16:9 energy dashboard as a standalone HTML
|
|
6
|
+
* document, faithfully adapting the reference spec in
|
|
7
|
+
* docs/00-input/ux/index.html while binding live SENEC + Weather values
|
|
8
|
+
* from the shared {@link DashboardModel}.
|
|
9
|
+
*
|
|
10
|
+
* The output is a self-contained HTML string (inline CSS, no external
|
|
11
|
+
* assets) suitable for output on the second pin of the senec-image node,
|
|
12
|
+
* for embedding in an iframe, or for serving via an HTTP response node.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.renderDashboardHtml = renderDashboardHtml;
|
|
16
|
+
const dashboard_layout_1 = require("./dashboard-layout");
|
|
17
|
+
/** Escape a string for safe insertion into HTML text/attributes. */
|
|
18
|
+
function esc(value) {
|
|
19
|
+
return String(value)
|
|
20
|
+
.replace(/&/g, '&')
|
|
21
|
+
.replace(/</g, '<')
|
|
22
|
+
.replace(/>/g, '>')
|
|
23
|
+
.replace(/"/g, '"')
|
|
24
|
+
.replace(/'/g, ''')
|
|
25
|
+
.replace(/`/g, '`')
|
|
26
|
+
.replace(/\//g, '/');
|
|
27
|
+
}
|
|
28
|
+
/** Render a single energy node for the "Aktuell" (current) card. */
|
|
29
|
+
function currentNode(cls, posCls, v) {
|
|
30
|
+
return (`<div class="energy-node ${cls} ${posCls}">` +
|
|
31
|
+
`<div class="label">${esc(v.label)}</div>` +
|
|
32
|
+
`<div class="value">${esc(v.value)}</div>` +
|
|
33
|
+
`<div class="unit">${esc(v.unit)}</div>` +
|
|
34
|
+
`</div>`);
|
|
35
|
+
}
|
|
36
|
+
/** Render a single energy node for the "Heute" (today) card. */
|
|
37
|
+
function todayNode(cls, posCls, v) {
|
|
38
|
+
if (v.dual) {
|
|
39
|
+
return (`<div class="energy-node ${cls} ${posCls}">` +
|
|
40
|
+
`<div class="label">${esc(v.label)}</div>` +
|
|
41
|
+
`<div class="small-value">${esc(v.dual)}</div>` +
|
|
42
|
+
`<div class="hint">${esc(v.hint || '')}</div>` +
|
|
43
|
+
`<div class="unit">${esc(v.unit)}</div>` +
|
|
44
|
+
`</div>`);
|
|
45
|
+
}
|
|
46
|
+
return (`<div class="energy-node ${cls} ${posCls}">` +
|
|
47
|
+
`<div class="label">${esc(v.label)}</div>` +
|
|
48
|
+
`<div class="value">${esc(v.value)}</div>` +
|
|
49
|
+
`<div class="unit">${esc(v.unit)}</div>` +
|
|
50
|
+
`</div>`);
|
|
51
|
+
}
|
|
52
|
+
/** Coerce to a safe integer percentage (0..100) for inline output. */
|
|
53
|
+
function pct(value) {
|
|
54
|
+
const n = Number(value);
|
|
55
|
+
if (!isFinite(n)) {
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
return Math.min(100, Math.max(0, Math.round(n)));
|
|
59
|
+
}
|
|
60
|
+
/** Degrees for a conic ring given a percentage. */
|
|
61
|
+
function ringDeg(percent) {
|
|
62
|
+
return Math.round((pct(percent) / 100) * 360);
|
|
63
|
+
}
|
|
64
|
+
/** Short HH:MM (local) from an ISO timestamp, or "--:--" if absent. */
|
|
65
|
+
function shortTime(iso) {
|
|
66
|
+
if (!iso) {
|
|
67
|
+
return '--:--';
|
|
68
|
+
}
|
|
69
|
+
const d = new Date(iso);
|
|
70
|
+
if (isNaN(d.getTime())) {
|
|
71
|
+
return '--:--';
|
|
72
|
+
}
|
|
73
|
+
const hh = String(d.getHours()).padStart(2, '0');
|
|
74
|
+
const mm = String(d.getMinutes()).padStart(2, '0');
|
|
75
|
+
return `${hh}:${mm}`;
|
|
76
|
+
}
|
|
77
|
+
/** Render one status chip (SENEC / Weather). */
|
|
78
|
+
function statusChip(label, s) {
|
|
79
|
+
const ok = !!s.ok;
|
|
80
|
+
const dotColor = ok ? '#1fb141' : s.fallback ? '#ff8a00' : '#e5484d';
|
|
81
|
+
const stateText = ok ? 'OK' : s.fallback ? 'Fallback' : 'Fehler';
|
|
82
|
+
return (`<div class="status-chip">` +
|
|
83
|
+
`<span class="status-dot" style="background:${dotColor}"></span>` +
|
|
84
|
+
`<span class="status-label">${esc(label)}</span>` +
|
|
85
|
+
`<span class="status-state">${esc(stateText)}</span>` +
|
|
86
|
+
`<span class="status-time">${esc(shortTime(s.timestamp))}</span>` +
|
|
87
|
+
`</div>`);
|
|
88
|
+
}
|
|
89
|
+
function renderWeatherCard(model) {
|
|
90
|
+
const w = model.weather;
|
|
91
|
+
if (!w) {
|
|
92
|
+
return (`<section class="card">` +
|
|
93
|
+
`<h1 class="title">${esc(dashboard_layout_1.CARD_TITLES.weather)}</h1>` +
|
|
94
|
+
`<div class="weather-layout"><div class="current-weather">` +
|
|
95
|
+
`<div class="weather-icon">❓</div>` +
|
|
96
|
+
`<div><div class="temp">--</div><div class="condition">Keine Wetterdaten</div></div>` +
|
|
97
|
+
`</div></div></section>`);
|
|
98
|
+
}
|
|
99
|
+
const forecast = w.forecast
|
|
100
|
+
.slice(0, 5)
|
|
101
|
+
.map((f) => `<div class="forecast-item">` +
|
|
102
|
+
`<div class="forecast-day">${esc(f.day)}</div>` +
|
|
103
|
+
`<div class="forecast-ico">${esc(f.icon)}</div>` +
|
|
104
|
+
`<div class="forecast-temp">${esc(String(f.temp))}°</div>` +
|
|
105
|
+
`</div>`)
|
|
106
|
+
.join('');
|
|
107
|
+
const locationLine = w.location
|
|
108
|
+
? `<div class="weather-location">📍 ${esc(w.location)}</div>`
|
|
109
|
+
: '';
|
|
110
|
+
return (`<section class="card">` +
|
|
111
|
+
`<h1 class="title">${esc(dashboard_layout_1.CARD_TITLES.weather)}</h1>` +
|
|
112
|
+
locationLine +
|
|
113
|
+
`<div class="weather-layout">` +
|
|
114
|
+
`<div class="current-weather">` +
|
|
115
|
+
`<div class="weather-icon">${esc(w.icon)}</div>` +
|
|
116
|
+
`<div><div class="temp">${esc(String(w.temperature))}°C</div>` +
|
|
117
|
+
`<div class="condition">${esc(w.condition)}</div></div>` +
|
|
118
|
+
`</div>` +
|
|
119
|
+
`<div class="metrics">` +
|
|
120
|
+
`<div class="metric"><div class="mi">🌡️</div><div><div class="mv">${esc(String(w.tempMax))}° / ${esc(String(w.tempMin))}°</div><div class="ml">max / min</div></div></div>` +
|
|
121
|
+
`<div class="metric"><div class="mi">💧</div><div><div class="mv">${esc(String(w.humidity))}%</div><div class="ml">Luftfeuchte</div></div></div>` +
|
|
122
|
+
`<div class="metric"><div class="mi">🌬️</div><div><div class="mv">${esc(String(w.windSpeed))} km/h</div><div class="ml">${esc(w.windDirection)}</div></div></div>` +
|
|
123
|
+
`</div>` +
|
|
124
|
+
`<div class="forecast">${forecast}</div>` +
|
|
125
|
+
`</div></section>`);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Render the complete dashboard HTML document for the given model.
|
|
129
|
+
*/
|
|
130
|
+
function renderDashboardHtml(model) {
|
|
131
|
+
const c = model.current;
|
|
132
|
+
const t = model.today;
|
|
133
|
+
const a = model.autarky;
|
|
134
|
+
const currentCard = `<section class="card">` +
|
|
135
|
+
`<h1 class="title">${esc(dashboard_layout_1.CARD_TITLES.current)}</h1>` +
|
|
136
|
+
`<div class="flow-area">` +
|
|
137
|
+
`<div class="connector c-solar"></div>` +
|
|
138
|
+
`<div class="connector c-grid"></div>` +
|
|
139
|
+
`<div class="connector c-battery"></div>` +
|
|
140
|
+
`<div class="connector c-wallbox"></div>` +
|
|
141
|
+
`<div class="connector c-consumption"></div>` +
|
|
142
|
+
`<div class="hub"></div>` +
|
|
143
|
+
currentNode('solar', 'pos-solar', c.solar) +
|
|
144
|
+
currentNode('grid', 'pos-grid', c.grid) +
|
|
145
|
+
currentNode('battery', 'pos-battery', c.battery) +
|
|
146
|
+
currentNode('orange', 'pos-wallbox', c.wallbox) +
|
|
147
|
+
currentNode('orange', 'pos-consumption', c.consumption) +
|
|
148
|
+
`</div></section>`;
|
|
149
|
+
const todayCard = `<section class="card">` +
|
|
150
|
+
`<h1 class="title">${esc(dashboard_layout_1.CARD_TITLES.today)}</h1>` +
|
|
151
|
+
`<div class="flow-area">` +
|
|
152
|
+
`<div class="connector c-solar"></div>` +
|
|
153
|
+
`<div class="connector c-grid"></div>` +
|
|
154
|
+
`<div class="connector c-battery"></div>` +
|
|
155
|
+
`<div class="connector c-wallbox"></div>` +
|
|
156
|
+
`<div class="connector c-consumption"></div>` +
|
|
157
|
+
`<div class="hub"></div>` +
|
|
158
|
+
todayNode('solar', 'pos-solar', t.solar) +
|
|
159
|
+
todayNode('grid', 'pos-grid', t.grid) +
|
|
160
|
+
todayNode('battery', 'pos-battery', t.battery) +
|
|
161
|
+
todayNode('orange', 'pos-wallbox', t.wallbox) +
|
|
162
|
+
todayNode('orange', 'pos-consumption', t.consumption) +
|
|
163
|
+
`</div></section>`;
|
|
164
|
+
const autarkyCard = `<section class="card">` +
|
|
165
|
+
`<h1 class="title">${esc(dashboard_layout_1.CARD_TITLES.autarky)}</h1>` +
|
|
166
|
+
`<div class="autarky-grid">` +
|
|
167
|
+
`<div class="kpi"><div class="kpi-label">Heute</div><div class="percent">${pct(a.todayPercent)}%</div>` +
|
|
168
|
+
`<div class="ring" style="--p:${ringDeg(a.todayPercent)}deg; --ring-color:#1fb141" data-icon="🍃"></div></div>` +
|
|
169
|
+
`<div class="kpi"><div class="kpi-label">Aktuell</div><div class="percent">${pct(a.currentPercent)}%</div>` +
|
|
170
|
+
`<div class="ring" style="--p:${ringDeg(a.currentPercent)}deg; --ring-color:#1fb141" data-icon="⚡"></div></div>` +
|
|
171
|
+
`<div class="kpi"><div class="kpi-label">Speicherstatus</div><div class="percent" style="color:#2e7bea">${pct(a.batterySocPercent)}%</div>` +
|
|
172
|
+
`<div class="ring" style="--p:${ringDeg(a.batterySocPercent)}deg; --ring-color:#2e7bea" data-icon="🔋"></div></div>` +
|
|
173
|
+
`</div></section>`;
|
|
174
|
+
const weatherCard = renderWeatherCard(model);
|
|
175
|
+
const statusBar = `<div class="status-bar">` +
|
|
176
|
+
statusChip('SENEC', model.status.senec) +
|
|
177
|
+
statusChip('Wetter-API', model.status.weather) +
|
|
178
|
+
`</div>`;
|
|
179
|
+
return `<!doctype html>
|
|
180
|
+
<html lang="de">
|
|
181
|
+
<head>
|
|
182
|
+
<meta charset="utf-8" />
|
|
183
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
184
|
+
<title>Energy Dashboard</title>
|
|
185
|
+
<style>
|
|
186
|
+
:root {
|
|
187
|
+
--blue: #2438c5;
|
|
188
|
+
--solar: #fdb913;
|
|
189
|
+
--solar-hi: #ffd84d;
|
|
190
|
+
--battery: #1237e6;
|
|
191
|
+
--battery-hi: #3157ff;
|
|
192
|
+
--orange: #ff8a00;
|
|
193
|
+
--orange-hi: #ffad33;
|
|
194
|
+
--gray: #8a8a8a;
|
|
195
|
+
--gray-hi: #a8a8a8;
|
|
196
|
+
--green: #1fb141;
|
|
197
|
+
--status-blue: #2e7bea;
|
|
198
|
+
--ring-bg: #e6eaf0;
|
|
199
|
+
--border: #e8e8e8;
|
|
200
|
+
--muted: #6b7280;
|
|
201
|
+
--main: #111827;
|
|
202
|
+
--page: #f3f4f6;
|
|
203
|
+
--white: #ffffff;
|
|
204
|
+
}
|
|
205
|
+
* { box-sizing: border-box; }
|
|
206
|
+
html, body {
|
|
207
|
+
margin: 0;
|
|
208
|
+
min-height: 100%;
|
|
209
|
+
background: var(--page);
|
|
210
|
+
font-family: "Segoe UI", Arial, sans-serif;
|
|
211
|
+
color: var(--main);
|
|
212
|
+
}
|
|
213
|
+
body {
|
|
214
|
+
min-height: 100vh;
|
|
215
|
+
display: grid;
|
|
216
|
+
place-items: center;
|
|
217
|
+
padding: 2vmin;
|
|
218
|
+
}
|
|
219
|
+
.dashboard-wrap {
|
|
220
|
+
width: min(96vw, 1600px);
|
|
221
|
+
max-height: 96vh;
|
|
222
|
+
aspect-ratio: 16 / 9;
|
|
223
|
+
container-type: inline-size;
|
|
224
|
+
}
|
|
225
|
+
.dashboard {
|
|
226
|
+
--u: 1cqw;
|
|
227
|
+
--outer: clamp(10px, calc(var(--u) * 1.5), 24px);
|
|
228
|
+
--gap: clamp(10px, calc(var(--u) * 1.5), 24px);
|
|
229
|
+
--card-radius: clamp(10px, calc(var(--u) * 1.125), 18px);
|
|
230
|
+
--title-font: clamp(16px, calc(var(--u) * 1.875), 30px);
|
|
231
|
+
--title-line: calc(var(--title-font) * 1.13);
|
|
232
|
+
--header-h: clamp(30px, calc(var(--u) * 2.875), 46px);
|
|
233
|
+
--card-pad-x: clamp(10px, calc(var(--u) * 1.25), 20px);
|
|
234
|
+
--card-pad-top: clamp(10px, calc(var(--u) * 1.125), 18px);
|
|
235
|
+
--card-pad-bottom: clamp(10px, calc(var(--u) * 1.25), 20px);
|
|
236
|
+
--node-d: clamp(58px, calc(var(--u) * 7.0), 112px);
|
|
237
|
+
--hub-d: calc(var(--node-d) * .375);
|
|
238
|
+
--connector-h: calc(var(--node-d) * .152);
|
|
239
|
+
--node-label-font: calc(var(--node-d) * .152);
|
|
240
|
+
--node-value-font: calc(var(--node-d) * .357);
|
|
241
|
+
--node-unit-font: calc(var(--node-d) * .134);
|
|
242
|
+
--node-small-font: calc(var(--node-d) * .223);
|
|
243
|
+
--node-hint-font: calc(var(--node-d) * .107);
|
|
244
|
+
--ring-d: clamp(78px, calc(var(--u) * 8.0), 128px);
|
|
245
|
+
--ring-stroke: calc(var(--ring-d) * .105);
|
|
246
|
+
width: 100%;
|
|
247
|
+
height: 100%;
|
|
248
|
+
aspect-ratio: 16 / 9;
|
|
249
|
+
background: var(--white);
|
|
250
|
+
padding: var(--outer);
|
|
251
|
+
display: grid;
|
|
252
|
+
grid-template-columns: 1fr 1fr;
|
|
253
|
+
grid-template-rows: 1fr 1fr;
|
|
254
|
+
gap: var(--gap);
|
|
255
|
+
overflow: hidden;
|
|
256
|
+
}
|
|
257
|
+
.card {
|
|
258
|
+
position: relative;
|
|
259
|
+
min-width: 0;
|
|
260
|
+
min-height: 0;
|
|
261
|
+
background: var(--white);
|
|
262
|
+
border: 1px solid var(--border);
|
|
263
|
+
border-radius: var(--card-radius);
|
|
264
|
+
box-shadow: 0 calc(var(--u) * .18) calc(var(--u) * 1.125) rgba(0,0,0,.08);
|
|
265
|
+
overflow: hidden;
|
|
266
|
+
padding: var(--card-pad-top) var(--card-pad-x) var(--card-pad-bottom);
|
|
267
|
+
}
|
|
268
|
+
.title {
|
|
269
|
+
margin: 0;
|
|
270
|
+
height: var(--header-h);
|
|
271
|
+
line-height: var(--title-line);
|
|
272
|
+
text-align: center;
|
|
273
|
+
font-size: var(--title-font);
|
|
274
|
+
color: var(--blue);
|
|
275
|
+
font-weight: 650;
|
|
276
|
+
letter-spacing: .01em;
|
|
277
|
+
}
|
|
278
|
+
.flow-area {
|
|
279
|
+
position: absolute;
|
|
280
|
+
left: var(--card-pad-x);
|
|
281
|
+
right: var(--card-pad-x);
|
|
282
|
+
top: calc(var(--card-pad-top) + var(--header-h));
|
|
283
|
+
bottom: var(--card-pad-bottom);
|
|
284
|
+
}
|
|
285
|
+
.pos-solar { left: 50.0%; top: 16.0%; }
|
|
286
|
+
.pos-grid { left: 23.5%; top: 49.0%; }
|
|
287
|
+
.pos-battery { left: 76.5%; top: 49.0%; }
|
|
288
|
+
.pos-wallbox { left: 34.0%; top: 81.5%; }
|
|
289
|
+
.pos-consumption { left: 66.0%; top: 81.5%; }
|
|
290
|
+
.energy-node {
|
|
291
|
+
position: absolute;
|
|
292
|
+
width: var(--node-d);
|
|
293
|
+
height: var(--node-d);
|
|
294
|
+
transform: translate(-50%, -50%);
|
|
295
|
+
border-radius: 50%;
|
|
296
|
+
display: grid;
|
|
297
|
+
grid-template-rows: auto auto auto auto;
|
|
298
|
+
align-content: center;
|
|
299
|
+
justify-items: center;
|
|
300
|
+
text-align: center;
|
|
301
|
+
color: #fff;
|
|
302
|
+
z-index: 10;
|
|
303
|
+
padding-top: calc(var(--node-d) * .025);
|
|
304
|
+
box-shadow: 0 calc(var(--node-d) * .045) calc(var(--node-d) * .14) rgba(0,0,0,.18);
|
|
305
|
+
}
|
|
306
|
+
.energy-node .label {
|
|
307
|
+
font-size: var(--node-label-font);
|
|
308
|
+
line-height: 1.02;
|
|
309
|
+
font-weight: 450;
|
|
310
|
+
max-width: 92%;
|
|
311
|
+
white-space: nowrap;
|
|
312
|
+
}
|
|
313
|
+
.energy-node .value {
|
|
314
|
+
font-size: var(--node-value-font);
|
|
315
|
+
line-height: .92;
|
|
316
|
+
font-weight: 760;
|
|
317
|
+
letter-spacing: -.035em;
|
|
318
|
+
margin-top: calc(var(--node-d) * .045);
|
|
319
|
+
}
|
|
320
|
+
.energy-node .unit {
|
|
321
|
+
font-size: var(--node-unit-font);
|
|
322
|
+
line-height: 1;
|
|
323
|
+
font-weight: 450;
|
|
324
|
+
margin-top: calc(var(--node-d) * .054);
|
|
325
|
+
}
|
|
326
|
+
.energy-node .small-value {
|
|
327
|
+
font-size: var(--node-small-font);
|
|
328
|
+
line-height: .95;
|
|
329
|
+
font-weight: 760;
|
|
330
|
+
letter-spacing: -.035em;
|
|
331
|
+
margin-top: calc(var(--node-d) * .054);
|
|
332
|
+
}
|
|
333
|
+
.energy-node .hint {
|
|
334
|
+
font-size: var(--node-hint-font);
|
|
335
|
+
line-height: 1;
|
|
336
|
+
opacity: .95;
|
|
337
|
+
margin-top: calc(var(--node-d) * .036);
|
|
338
|
+
}
|
|
339
|
+
.solar { background: radial-gradient(circle at 35% 25%, var(--solar-hi), var(--solar)); }
|
|
340
|
+
.battery { background: radial-gradient(circle at 35% 25%, var(--battery-hi), var(--battery)); }
|
|
341
|
+
.grid { background: radial-gradient(circle at 35% 25%, var(--gray-hi), var(--gray)); }
|
|
342
|
+
.orange { background: radial-gradient(circle at 35% 25%, var(--orange-hi), var(--orange)); }
|
|
343
|
+
.hub {
|
|
344
|
+
position: absolute;
|
|
345
|
+
left: 50%;
|
|
346
|
+
top: 50%;
|
|
347
|
+
width: var(--hub-d);
|
|
348
|
+
height: var(--hub-d);
|
|
349
|
+
transform: translate(-50%, -50%);
|
|
350
|
+
border-radius: 50%;
|
|
351
|
+
background: #fff;
|
|
352
|
+
box-shadow: 0 calc(var(--hub-d) * .07) calc(var(--hub-d) * .31) rgba(0,0,0,.18);
|
|
353
|
+
z-index: 5;
|
|
354
|
+
}
|
|
355
|
+
.connector {
|
|
356
|
+
position: absolute;
|
|
357
|
+
left: 50%;
|
|
358
|
+
top: 50%;
|
|
359
|
+
height: var(--connector-h);
|
|
360
|
+
border-radius: 999px;
|
|
361
|
+
transform-origin: left center;
|
|
362
|
+
z-index: 2;
|
|
363
|
+
overflow: hidden;
|
|
364
|
+
opacity: .84;
|
|
365
|
+
}
|
|
366
|
+
.connector::after {
|
|
367
|
+
content: "› › › › ›";
|
|
368
|
+
position: absolute;
|
|
369
|
+
left: 0;
|
|
370
|
+
top: calc(var(--connector-h) * -.40);
|
|
371
|
+
color: rgba(255,255,255,.44);
|
|
372
|
+
font-size: calc(var(--connector-h) * 1.47);
|
|
373
|
+
letter-spacing: calc(var(--connector-h) * .52);
|
|
374
|
+
white-space: nowrap;
|
|
375
|
+
animation: flow 1.25s linear infinite;
|
|
376
|
+
}
|
|
377
|
+
@keyframes flow {
|
|
378
|
+
from { transform: translateX(calc(var(--connector-h) * -1.4)); }
|
|
379
|
+
to { transform: translateX(calc(var(--connector-h) * 1.2)); }
|
|
380
|
+
}
|
|
381
|
+
/*
|
|
382
|
+
* Horizontal / diagonal connectors run from the central hub (left/top 50%)
|
|
383
|
+
* outward to each node. transform-origin is the hub end (left center); each
|
|
384
|
+
* bar is rotated toward its node. Lengths are tuned so the bars stop at the
|
|
385
|
+
* node edge rather than overshooting past it.
|
|
386
|
+
*/
|
|
387
|
+
.c-grid { width: 19.0%; transform: rotate(180deg); background: linear-gradient(90deg, rgba(138,138,138,.12), rgba(138,138,138,.72)); }
|
|
388
|
+
.c-battery { width: 19.0%; transform: rotate(0deg); background: linear-gradient(90deg, rgba(18,55,230,.12), rgba(18,55,230,.72)); }
|
|
389
|
+
.c-wallbox { width: 21.0%; transform: rotate(125deg); background: linear-gradient(90deg, rgba(255,138,0,.12), rgba(255,138,0,.72)); }
|
|
390
|
+
.c-consumption { width: 21.0%; transform: rotate(55deg); background: linear-gradient(90deg, rgba(255,138,0,.12), rgba(255,138,0,.72)); }
|
|
391
|
+
/*
|
|
392
|
+
* The solar connector is vertical. Rotating a width-based bar overshoots on
|
|
393
|
+
* wide flow-areas (this produced a stray line running down through the
|
|
394
|
+
* centre). Instead it is drawn as a real vertical bar whose HEIGHT spans the
|
|
395
|
+
* gap between the solar node and the hub, so it can never overshoot.
|
|
396
|
+
*/
|
|
397
|
+
.c-solar {
|
|
398
|
+
left: 50%;
|
|
399
|
+
top: 22.0%;
|
|
400
|
+
width: var(--connector-h);
|
|
401
|
+
height: 28.0%;
|
|
402
|
+
transform: translateX(-50%);
|
|
403
|
+
transform-origin: center top;
|
|
404
|
+
background: linear-gradient(180deg, rgba(253,185,19,.75), rgba(253,185,19,.10));
|
|
405
|
+
}
|
|
406
|
+
.c-solar::after { display: none; }
|
|
407
|
+
.autarky-grid {
|
|
408
|
+
height: calc(100% - var(--header-h));
|
|
409
|
+
display: grid;
|
|
410
|
+
grid-template-columns: repeat(3, 1fr);
|
|
411
|
+
align-items: center;
|
|
412
|
+
gap: calc(var(--u) * .5);
|
|
413
|
+
padding: 0 calc(var(--u) * .4) calc(var(--u) * .6);
|
|
414
|
+
}
|
|
415
|
+
.kpi {
|
|
416
|
+
min-width: 0;
|
|
417
|
+
height: 100%;
|
|
418
|
+
display: grid;
|
|
419
|
+
grid-template-rows: calc(var(--node-d) * .26) calc(var(--node-d) * .46) calc(var(--ring-d) * 1.02);
|
|
420
|
+
justify-items: center;
|
|
421
|
+
align-content: center;
|
|
422
|
+
text-align: center;
|
|
423
|
+
}
|
|
424
|
+
.kpi-label {
|
|
425
|
+
color: var(--blue);
|
|
426
|
+
font-size: calc(var(--node-d) * .168);
|
|
427
|
+
line-height: 1.25;
|
|
428
|
+
font-weight: 650;
|
|
429
|
+
white-space: nowrap;
|
|
430
|
+
}
|
|
431
|
+
.percent {
|
|
432
|
+
font-size: calc(var(--node-d) * .375);
|
|
433
|
+
line-height: 1.12;
|
|
434
|
+
font-weight: 800;
|
|
435
|
+
letter-spacing: -.04em;
|
|
436
|
+
color: var(--green);
|
|
437
|
+
}
|
|
438
|
+
.ring {
|
|
439
|
+
--p: 90deg;
|
|
440
|
+
--ring-color: var(--green);
|
|
441
|
+
width: var(--ring-d);
|
|
442
|
+
height: var(--ring-d);
|
|
443
|
+
border-radius: 50%;
|
|
444
|
+
background: conic-gradient(var(--ring-color) var(--p), var(--ring-bg) 0);
|
|
445
|
+
display: grid;
|
|
446
|
+
place-items: center;
|
|
447
|
+
}
|
|
448
|
+
.ring::before {
|
|
449
|
+
content: attr(data-icon);
|
|
450
|
+
width: calc(var(--ring-d) - 2 * var(--ring-stroke));
|
|
451
|
+
height: calc(var(--ring-d) - 2 * var(--ring-stroke));
|
|
452
|
+
border-radius: 50%;
|
|
453
|
+
background: white;
|
|
454
|
+
display: grid;
|
|
455
|
+
place-items: center;
|
|
456
|
+
font-size: calc(var(--ring-d) * .327);
|
|
457
|
+
}
|
|
458
|
+
.weather-layout {
|
|
459
|
+
height: calc(100% - var(--header-h));
|
|
460
|
+
display: grid;
|
|
461
|
+
grid-template-columns: 1.18fr .92fr;
|
|
462
|
+
grid-template-rows: 1.06fr .84fr;
|
|
463
|
+
gap: calc(var(--u) * .5) calc(var(--u) * 1.375);
|
|
464
|
+
padding: calc(var(--u) * .125) calc(var(--u) * .5) calc(var(--u) * .125);
|
|
465
|
+
}
|
|
466
|
+
.current-weather {
|
|
467
|
+
display: grid;
|
|
468
|
+
grid-template-columns: calc(var(--node-d) * 1.09) 1fr;
|
|
469
|
+
align-items: center;
|
|
470
|
+
gap: calc(var(--u) * .5);
|
|
471
|
+
}
|
|
472
|
+
.weather-icon {
|
|
473
|
+
font-size: calc(var(--node-d) * .768);
|
|
474
|
+
line-height: 1;
|
|
475
|
+
text-align: right;
|
|
476
|
+
}
|
|
477
|
+
.temp {
|
|
478
|
+
font-size: calc(var(--node-d) * .589);
|
|
479
|
+
line-height: .97;
|
|
480
|
+
font-weight: 300;
|
|
481
|
+
letter-spacing: -.06em;
|
|
482
|
+
}
|
|
483
|
+
.condition {
|
|
484
|
+
color: var(--muted);
|
|
485
|
+
font-size: calc(var(--node-d) * .214);
|
|
486
|
+
line-height: 1.25;
|
|
487
|
+
margin-top: calc(var(--node-d) * .054);
|
|
488
|
+
white-space: nowrap;
|
|
489
|
+
}
|
|
490
|
+
.metrics {
|
|
491
|
+
display: grid;
|
|
492
|
+
align-content: center;
|
|
493
|
+
gap: calc(var(--node-d) * .098);
|
|
494
|
+
}
|
|
495
|
+
.metric {
|
|
496
|
+
display: grid;
|
|
497
|
+
grid-template-columns: calc(var(--node-d) * .30) 1fr;
|
|
498
|
+
column-gap: calc(var(--node-d) * .071);
|
|
499
|
+
align-items: center;
|
|
500
|
+
}
|
|
501
|
+
.mi {
|
|
502
|
+
color: var(--blue);
|
|
503
|
+
font-size: calc(var(--node-d) * .223);
|
|
504
|
+
text-align: center;
|
|
505
|
+
line-height: 1;
|
|
506
|
+
}
|
|
507
|
+
.mv {
|
|
508
|
+
font-size: calc(var(--node-d) * .241);
|
|
509
|
+
line-height: 1.07;
|
|
510
|
+
font-weight: 720;
|
|
511
|
+
letter-spacing: -.03em;
|
|
512
|
+
}
|
|
513
|
+
.ml {
|
|
514
|
+
color: var(--muted);
|
|
515
|
+
font-size: calc(var(--node-d) * .125);
|
|
516
|
+
line-height: 1.15;
|
|
517
|
+
}
|
|
518
|
+
.forecast {
|
|
519
|
+
grid-column: 1 / -1;
|
|
520
|
+
display: grid;
|
|
521
|
+
grid-template-columns: repeat(5, 1fr);
|
|
522
|
+
align-items: center;
|
|
523
|
+
border-top: 1px solid #edf0f4;
|
|
524
|
+
padding-top: calc(var(--node-d) * .098);
|
|
525
|
+
}
|
|
526
|
+
.forecast-item {
|
|
527
|
+
text-align: center;
|
|
528
|
+
border-right: 1px solid #edf0f4;
|
|
529
|
+
}
|
|
530
|
+
.forecast-item:last-child { border-right: 0; }
|
|
531
|
+
.forecast-day {
|
|
532
|
+
color: var(--blue);
|
|
533
|
+
font-size: calc(var(--node-d) * .152);
|
|
534
|
+
line-height: 1.18;
|
|
535
|
+
font-weight: 700;
|
|
536
|
+
}
|
|
537
|
+
.forecast-ico {
|
|
538
|
+
font-size: calc(var(--node-d) * .304);
|
|
539
|
+
line-height: 1.12;
|
|
540
|
+
margin: calc(var(--node-d) * .027) 0;
|
|
541
|
+
}
|
|
542
|
+
.forecast-temp {
|
|
543
|
+
font-size: calc(var(--node-d) * .152);
|
|
544
|
+
line-height: 1.18;
|
|
545
|
+
color: var(--muted);
|
|
546
|
+
font-weight: 650;
|
|
547
|
+
}
|
|
548
|
+
.weather-location {
|
|
549
|
+
text-align: center;
|
|
550
|
+
color: var(--muted);
|
|
551
|
+
font-size: clamp(9px, calc(var(--u) * 1.0), 15px);
|
|
552
|
+
line-height: 1.2;
|
|
553
|
+
margin-top: calc(var(--header-h) * -0.2);
|
|
554
|
+
margin-bottom: calc(var(--u) * 0.2);
|
|
555
|
+
}
|
|
556
|
+
.status-bar {
|
|
557
|
+
width: min(96vw, 1600px);
|
|
558
|
+
display: flex;
|
|
559
|
+
justify-content: flex-end;
|
|
560
|
+
gap: 1.5vmin;
|
|
561
|
+
padding: 0.6vmin 0.4vmin 0;
|
|
562
|
+
font-family: "Segoe UI", Arial, sans-serif;
|
|
563
|
+
}
|
|
564
|
+
.status-chip {
|
|
565
|
+
display: inline-flex;
|
|
566
|
+
align-items: center;
|
|
567
|
+
gap: 0.6vmin;
|
|
568
|
+
font-size: clamp(9px, 1.2vmin, 14px);
|
|
569
|
+
color: var(--muted);
|
|
570
|
+
}
|
|
571
|
+
.status-dot {
|
|
572
|
+
width: clamp(6px, 1vmin, 10px);
|
|
573
|
+
height: clamp(6px, 1vmin, 10px);
|
|
574
|
+
border-radius: 50%;
|
|
575
|
+
display: inline-block;
|
|
576
|
+
}
|
|
577
|
+
.status-label { font-weight: 650; color: var(--main); }
|
|
578
|
+
.status-state { font-weight: 600; }
|
|
579
|
+
.status-time { font-variant-numeric: tabular-nums; }
|
|
580
|
+
@supports not (width: 1cqw) {
|
|
581
|
+
.dashboard { --u: min(1vw, 16px); }
|
|
582
|
+
}
|
|
583
|
+
@media (max-width: 760px) {
|
|
584
|
+
body { padding: 0; }
|
|
585
|
+
.dashboard-wrap { width: 100vw; max-height: none; }
|
|
586
|
+
.energy-node .label { font-weight: 500; }
|
|
587
|
+
.condition { white-space: normal; }
|
|
588
|
+
}
|
|
589
|
+
</style>
|
|
590
|
+
</head>
|
|
591
|
+
<body>
|
|
592
|
+
<div class="dashboard-wrap">
|
|
593
|
+
<main class="dashboard" aria-label="Energy dashboard">
|
|
594
|
+
${currentCard}
|
|
595
|
+
${todayCard}
|
|
596
|
+
${autarkyCard}
|
|
597
|
+
${weatherCard}
|
|
598
|
+
</main>
|
|
599
|
+
${statusBar}
|
|
600
|
+
</div>
|
|
601
|
+
</body>
|
|
602
|
+
</html>`;
|
|
603
|
+
}
|
|
604
|
+
//# sourceMappingURL=dashboard-html-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-html-renderer.js","sourceRoot":"","sources":["../../src/lib/dashboard-html-renderer.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;AAgJH,kDAkeC;AAhnBD,yDAA4E;AAG5E,oEAAoE;AACpE,SAAS,GAAG,CAAC,KAAa;IACxB,OAAO,MAAM,CAAC,KAAK,CAAC;SACjB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED,oEAAoE;AACpE,SAAS,WAAW,CAAC,GAAW,EAAE,MAAc,EAAE,CAAY;IAC5D,OAAO,CACL,2BAA2B,GAAG,IAAI,MAAM,IAAI;QAC5C,sBAAsB,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ;QAC1C,sBAAsB,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ;QAC1C,qBAAqB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;QACxC,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,gEAAgE;AAChE,SAAS,SAAS,CAAC,GAAW,EAAE,MAAc,EAAE,CAAY;IAC1D,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACX,OAAO,CACL,2BAA2B,GAAG,IAAI,MAAM,IAAI;YAC5C,sBAAsB,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ;YAC1C,4BAA4B,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;YAC/C,qBAAqB,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,QAAQ;YAC9C,qBAAqB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;YACxC,QAAQ,CACT,CAAC;IACJ,CAAC;IACD,OAAO,CACL,2BAA2B,GAAG,IAAI,MAAM,IAAI;QAC5C,sBAAsB,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ;QAC1C,sBAAsB,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ;QAC1C,qBAAqB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;QACxC,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,SAAS,GAAG,CAAC,KAAa;IACxB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACjB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,mDAAmD;AACnD,SAAS,OAAO,CAAC,OAAe;IAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,uEAAuE;AACvE,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACjD,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnD,OAAO,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC;AACvB,CAAC;AAED,gDAAgD;AAChD,SAAS,UAAU,CAAC,KAAa,EAAE,CAAe;IAChD,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClB,MAAM,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;IACjE,OAAO,CACL,2BAA2B;QAC3B,8CAA8C,QAAQ,WAAW;QACjE,8BAA8B,GAAG,CAAC,KAAK,CAAC,SAAS;QACjD,8BAA8B,GAAG,CAAC,SAAS,CAAC,SAAS;QACrD,6BAA6B,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;QACjE,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAqB;IAC9C,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IACxB,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,CACL,wBAAwB;YACxB,qBAAqB,GAAG,CAAC,8BAAW,CAAC,OAAO,CAAC,OAAO;YACpD,2DAA2D;YAC3D,mCAAmC;YACnC,qFAAqF;YACrF,wBAAwB,CACzB,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ;SACxB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,6BAA6B;QAC7B,6BAA6B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ;QAC/C,6BAA6B,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;QAChD,8BAA8B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;QAC1D,QAAQ,CACX;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,MAAM,YAAY,GAAG,CAAC,CAAC,QAAQ;QAC7B,CAAC,CAAC,oCAAoC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ;QAC7D,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,CACL,wBAAwB;QACxB,qBAAqB,GAAG,CAAC,8BAAW,CAAC,OAAO,CAAC,OAAO;QACpD,YAAY;QACZ,8BAA8B;QAC9B,+BAA+B;QAC/B,6BAA6B,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;QAChD,0BAA0B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,UAAU;QAC9D,0BAA0B,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc;QACxD,QAAQ;QACR,uBAAuB;QACvB,qEAAqE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,oDAAoD;QAC5K,oEAAoE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,sDAAsD;QACjJ,qEAAqE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,8BAA8B,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,oBAAoB;QACnK,QAAQ;QACR,yBAAyB,QAAQ,QAAQ;QACzC,kBAAkB,CACnB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,mBAAmB,CAAC,KAAqB;IACvD,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IACxB,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;IACtB,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IAExB,MAAM,WAAW,GACf,wBAAwB;QACxB,qBAAqB,GAAG,CAAC,8BAAW,CAAC,OAAO,CAAC,OAAO;QACpD,yBAAyB;QACzB,uCAAuC;QACvC,sCAAsC;QACtC,yCAAyC;QACzC,yCAAyC;QACzC,6CAA6C;QAC7C,yBAAyB;QACzB,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC;QAC1C,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC;QACvC,WAAW,CAAC,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC;QAChD,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC;QAC/C,WAAW,CAAC,QAAQ,EAAE,iBAAiB,EAAE,CAAC,CAAC,WAAW,CAAC;QACvD,kBAAkB,CAAC;IAErB,MAAM,SAAS,GACb,wBAAwB;QACxB,qBAAqB,GAAG,CAAC,8BAAW,CAAC,KAAK,CAAC,OAAO;QAClD,yBAAyB;QACzB,uCAAuC;QACvC,sCAAsC;QACtC,yCAAyC;QACzC,yCAAyC;QACzC,6CAA6C;QAC7C,yBAAyB;QACzB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC;QACxC,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC;QACrC,SAAS,CAAC,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC;QAC9C,SAAS,CAAC,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC;QAC7C,SAAS,CAAC,QAAQ,EAAE,iBAAiB,EAAE,CAAC,CAAC,WAAW,CAAC;QACrD,kBAAkB,CAAC;IAErB,MAAM,WAAW,GACf,wBAAwB;QACxB,qBAAqB,GAAG,CAAC,8BAAW,CAAC,OAAO,CAAC,OAAO;QACpD,4BAA4B;QAC5B,2EAA2E,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,SAAS;QACvG,gCAAgC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,wDAAwD;QAC/G,6EAA6E,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS;QAC3G,gCAAgC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,uDAAuD;QAChH,0GAA0G,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,SAAS;QAC3I,gCAAgC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,wDAAwD;QACpH,kBAAkB,CAAC;IAErB,MAAM,WAAW,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,SAAS,GACb,0BAA0B;QAC1B,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;QACvC,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QAC9C,QAAQ,CAAC;IAEX,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+ZP,WAAW;EACX,SAAS;EACT,WAAW;EACX,WAAW;;EAEX,SAAS;;;QAGH,CAAC;AACT,CAAC"}
|