@kipk/ha-better-history 0.1.0

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.
@@ -0,0 +1,4410 @@
1
+ import { LitElement as e, css as t, html as n, nothing as r, svg as i } from "lit";
2
+ import { property as a, state as o } from "lit/decorators.js";
3
+ import { loadHaComponents as s } from "@kipk/load-ha-components";
4
+ //#region src/data/format.ts
5
+ var c = new Set(["unknown", "unavailable"]);
6
+ function l(e) {
7
+ return e == null || typeof e == "string" && c.has(e);
8
+ }
9
+ function u(e) {
10
+ if (!(l(e) || typeof e != "string" || e.trim() === "")) return e;
11
+ }
12
+ function d(e) {
13
+ if (l(e)) return;
14
+ if (typeof e == "number") return Number.isFinite(e) ? e : void 0;
15
+ if (typeof e != "string" || e.trim() === "") return;
16
+ let t = Number(e);
17
+ return Number.isFinite(t) ? t : void 0;
18
+ }
19
+ //#endregion
20
+ //#region src/utils/performance.ts
21
+ function f() {
22
+ return typeof performance < "u" ? performance.now() : Date.now();
23
+ }
24
+ function p(e, t, n) {
25
+ e && console.debug("[ha-better-history][perf]", t, n);
26
+ }
27
+ //#endregion
28
+ //#region src/data/history-queue.ts
29
+ async function m(e, t = {}) {
30
+ let n = Math.max(1, Math.floor(t.concurrency ?? 1)), r = [], i = 0, a = 0, o = 0, s = (n, r) => {
31
+ t.onEvent?.({
32
+ event: n,
33
+ taskId: r,
34
+ queuedCount: Math.max(e.length - i, 0),
35
+ activeCount: a,
36
+ completedCount: o
37
+ });
38
+ };
39
+ s("queue.start");
40
+ async function c() {
41
+ for (; i < e.length;) {
42
+ if (t.isCancelled?.()) {
43
+ s("queue.cancelled");
44
+ return;
45
+ }
46
+ let n = e[i++];
47
+ a += 1, s("queue.task_start", n.id);
48
+ try {
49
+ let e = typeof performance < "u" ? performance.now() : Date.now(), i = {
50
+ task: n,
51
+ value: await n.run(),
52
+ durationMs: (typeof performance < "u" ? performance.now() : Date.now()) - e
53
+ };
54
+ r.push(i), o += 1, s("queue.task_complete", n.id), await t.onResult?.(i);
55
+ } finally {
56
+ --a;
57
+ }
58
+ }
59
+ }
60
+ return await Promise.all(Array.from({ length: Math.min(n, e.length) }, () => c())), r;
61
+ }
62
+ //#endregion
63
+ //#region src/data/history.ts
64
+ var h = 6e4, g = 3, _ = 350, v = 360 * 60 * 1e3, y = 3600 * 1e3, b = 720 * 60 * 1e3, x = 2500, ee = 8e3, te = 15e3, ne = 300, S = 700, re = 1100, C = 80;
65
+ function ie(e) {
66
+ if (e.length <= 2) return e;
67
+ let t = [e[0]];
68
+ for (let n = 1; n < e.length - 1; n++) {
69
+ let r = e[n], i = e[n - 1], a = e[n + 1];
70
+ (r.value !== i.value || a.value !== r.value) && t.push(r);
71
+ }
72
+ return t.push(e[e.length - 1]), t;
73
+ }
74
+ var w = class extends Error {
75
+ constructor(e) {
76
+ super(`History chunk timed out after ${e}ms`), this.name = "HistoryChunkTimeoutError";
77
+ }
78
+ };
79
+ function T(e) {
80
+ return typeof e == "object" && !!e && !Array.isArray(e);
81
+ }
82
+ function E(e, t) {
83
+ return t.reduce((e, t) => T(e) ? e[t] : void 0, e);
84
+ }
85
+ function ae(e) {
86
+ return e[e.length - 1] ?? "";
87
+ }
88
+ function D(e) {
89
+ return e instanceof Error ? e.message : String(e);
90
+ }
91
+ function O(e) {
92
+ if (!T(e)) return;
93
+ let t = e.status ?? e.statusCode ?? e.status_code;
94
+ return typeof t == "number" ? t : void 0;
95
+ }
96
+ function oe(e) {
97
+ if (!T(e)) return "";
98
+ let t = e.code;
99
+ return typeof t == "string" ? t.toLowerCase() : "";
100
+ }
101
+ function k(e) {
102
+ if (e instanceof w) return !0;
103
+ let t = O(e);
104
+ if (t !== void 0) return t === 408 || t === 429 || t >= 500;
105
+ let n = D(e).toLowerCase(), r = `${oe(e)} ${n}`;
106
+ return r.includes("timeout") || r.includes("timed out") || r.includes("network") || r.includes("failed to fetch") || r.includes("connection") || r.includes("temporarily unavailable") || r.includes("unavailable") || r.includes("aborted");
107
+ }
108
+ function se(e, t) {
109
+ let n = Math.floor(Math.random() * Math.max(1, t));
110
+ return t * 2 ** Math.max(0, e - 1) + n;
111
+ }
112
+ function ce(e) {
113
+ return new Promise((t) => setTimeout(t, e));
114
+ }
115
+ function A(e = 80) {
116
+ let t = globalThis.requestIdleCallback;
117
+ return t ? new Promise((n) => t(() => n(), { timeout: e })) : new Promise((e) => {
118
+ typeof requestAnimationFrame == "function" ? requestAnimationFrame(() => e()) : setTimeout(e, 0);
119
+ });
120
+ }
121
+ async function le(e, t) {
122
+ let n;
123
+ try {
124
+ return await Promise.race([e, new Promise((e, r) => {
125
+ n = setTimeout(() => r(new w(t)), t);
126
+ })]);
127
+ } finally {
128
+ n !== void 0 && clearTimeout(n);
129
+ }
130
+ }
131
+ function j(e) {
132
+ if (typeof e == "number" && Number.isFinite(e)) return "number";
133
+ if (typeof e == "boolean") return "boolean";
134
+ if (typeof e == "string" && e !== "") return "string";
135
+ }
136
+ function ue(e) {
137
+ let t = j(Number.isFinite(Number(e.state)) ? Number(e.state) : e.state), n = e.attributes.unit_of_measurement;
138
+ if (t) return {
139
+ id: `state:${e.entity_id}`,
140
+ kind: "entity_state",
141
+ entityId: e.entity_id,
142
+ label: e.attributes.friendly_name && typeof e.attributes.friendly_name == "string" ? e.attributes.friendly_name : e.entity_id,
143
+ valueType: t,
144
+ unit: t === "number" && typeof n == "string" && n !== "" ? n : void 0
145
+ };
146
+ }
147
+ function M(e, t, n) {
148
+ let r = j(E(e.attributes, t));
149
+ if (r) return {
150
+ id: `attr:${e.entity_id}:${t.join(".")}`,
151
+ kind: "entity_attribute",
152
+ entityId: e.entity_id,
153
+ label: n ?? ae(t),
154
+ path: t,
155
+ valueType: r
156
+ };
157
+ }
158
+ function de(e, t) {
159
+ return t === "number" ? d(e) : t === "boolean" ? typeof e == "boolean" ? e : void 0 : u(e);
160
+ }
161
+ function fe(e, t) {
162
+ let n = e.attributes ?? e.a ?? {};
163
+ return de(t.kind === "entity_state" ? e.state ?? e.s : E(n, t.path ?? []), t.valueType);
164
+ }
165
+ function pe(e) {
166
+ if (typeof e.lu == "number") return e.lu * 1e3;
167
+ let t = e.last_changed ?? e.last_updated;
168
+ return t ? Date.parse(t) : NaN;
169
+ }
170
+ function me(e, t, n) {
171
+ if (e.length === 0) return e;
172
+ let r = t.getTime(), i = Math.min(n.getTime(), Date.now()), a = [...e].sort((e, t) => e.time - t.time), o = a[0], s = a[a.length - 1];
173
+ return [
174
+ ...o.time > r ? [{
175
+ time: r,
176
+ value: o.value
177
+ }] : [],
178
+ ...a,
179
+ ...s.time < i ? [{
180
+ time: i,
181
+ value: s.value
182
+ }] : []
183
+ ];
184
+ }
185
+ function he(e, t) {
186
+ let n = /* @__PURE__ */ new Map();
187
+ if (Array.isArray(e)) return e.forEach((e, r) => {
188
+ let i = e[0]?.entity_id ?? t[r];
189
+ i && n.set(i, e);
190
+ }), n;
191
+ for (let [t, r] of Object.entries(e)) Array.isArray(r) && n.set(t, r);
192
+ return n;
193
+ }
194
+ function ge(e, t, n = Date.now()) {
195
+ let r = e.states[t.entityId];
196
+ if (!r) return;
197
+ let i = {
198
+ entity_id: r.entity_id,
199
+ state: r.state,
200
+ last_changed: r.last_changed,
201
+ last_updated: r.last_updated,
202
+ attributes: r.attributes
203
+ }, a = fe(i, t), o = pe(i), s = Number.isFinite(o) ? o : n;
204
+ return a === void 0 || !Number.isFinite(s) ? void 0 : {
205
+ time: s,
206
+ value: a
207
+ };
208
+ }
209
+ function _e(e, t, n, r) {
210
+ let i = ge(e, t, n.getTime());
211
+ return i ? [{
212
+ time: n.getTime(),
213
+ value: i.value
214
+ }, {
215
+ time: Math.min(r.getTime(), Date.now()),
216
+ value: i.value
217
+ }] : [];
218
+ }
219
+ var ve = class {
220
+ constructor() {
221
+ this._entities = /* @__PURE__ */ new Map();
222
+ }
223
+ hasStates(e) {
224
+ return (this._entities.get(e)?.states.length ?? 0) > 0;
225
+ }
226
+ hasFullStates(e) {
227
+ let t = this._entities.get(e);
228
+ return t !== void 0 && t.fullCoverage.length > 0 && t.states.length > 0;
229
+ }
230
+ hasCoverage(e, t, n, r) {
231
+ let i = this._entities.get(e);
232
+ return i ? ye(r === "full" ? i.fullCoverage : [...i.stateCoverage, ...i.fullCoverage], t.getTime(), n.getTime()) : !1;
233
+ }
234
+ missingIntervals(e, t, n, r) {
235
+ let i = this._entities.get(e);
236
+ return xe(i ? r === "full" ? i.fullCoverage : [...i.stateCoverage, ...i.fullCoverage] : [], t.getTime(), n.getTime()).map((e) => ({
237
+ start: new Date(e.startTime),
238
+ end: new Date(e.endTime)
239
+ }));
240
+ }
241
+ integrate(e, t, n, r, i) {
242
+ let a = this._entities.get(e) ?? {
243
+ states: [],
244
+ stateCoverage: [],
245
+ fullCoverage: []
246
+ };
247
+ a.states = Se([...a.states, ...t]), a.stateCoverage = N([...a.stateCoverage, {
248
+ startTime: n.getTime(),
249
+ endTime: r.getTime()
250
+ }]), i === "full" && (a.fullCoverage = N([...a.fullCoverage, {
251
+ startTime: n.getTime(),
252
+ endTime: r.getTime()
253
+ }])), this._entities.set(e, a);
254
+ }
255
+ buildSeries(e, t, n, r) {
256
+ let i = e.kind === "entity_attribute" ? "full" : "state", a = this.coverageEnd(e.entityId, n, r, i);
257
+ return De(e, this._entities.get(e.entityId)?.states ?? [], t, n, new Date(a));
258
+ }
259
+ coverageEnd(e, t, n, r) {
260
+ let i = this._entities.get(e);
261
+ return i ? be(r === "full" ? i.fullCoverage : [...i.stateCoverage, ...i.fullCoverage], t.getTime(), n.getTime()) : n.getTime();
262
+ }
263
+ };
264
+ function N(e) {
265
+ let t = e.filter((e) => e.endTime > e.startTime).sort((e, t) => e.startTime - t.startTime), n = [];
266
+ for (let e of t) {
267
+ let t = n[n.length - 1];
268
+ t && e.startTime <= t.endTime + 1 ? t.endTime = Math.max(t.endTime, e.endTime) : n.push({ ...e });
269
+ }
270
+ return n;
271
+ }
272
+ function ye(e, t, n) {
273
+ return be(e, t, n) >= n - 1;
274
+ }
275
+ function be(e, t, n) {
276
+ if (n <= t) return n;
277
+ let r = t;
278
+ for (let t of N(e)) if (!(t.endTime < r)) {
279
+ if (t.startTime > r + 1) break;
280
+ if (r = Math.max(r, t.endTime), r >= n - 1) return n;
281
+ }
282
+ return r;
283
+ }
284
+ function xe(e, t, n) {
285
+ if (n <= t) return [];
286
+ let r = [], i = t;
287
+ for (let t of N(e)) if (!(t.endTime <= i) && (t.startTime > i + 1 && r.push({
288
+ startTime: i,
289
+ endTime: Math.min(t.startTime, n)
290
+ }), i = Math.max(i, t.endTime), i >= n)) break;
291
+ return i < n && r.push({
292
+ startTime: i,
293
+ endTime: n
294
+ }), r;
295
+ }
296
+ function Se(e) {
297
+ let t = /* @__PURE__ */ new Map();
298
+ for (let n of e) {
299
+ let e = pe(n);
300
+ Number.isFinite(e) && t.set(e, n);
301
+ }
302
+ return [...t.entries()].sort(([e], [t]) => e - t).map(([, e]) => e);
303
+ }
304
+ function Ce(e, t) {
305
+ let n = t.normalizeDurationMs + t.mergeDurationMs + t.buildDurationMs, r = t.stateCount >= te || t.requestDurationMs >= re, i = r || t.stateCount >= ee || t.requestDurationMs >= S || n >= C, a = t.stateCount <= x && t.requestDurationMs <= ne && n <= C / 2;
306
+ return i && e > y ? {
307
+ nextChunkMs: Math.max(y, Math.floor(e / (r ? 4 : 2))),
308
+ reason: "decrease"
309
+ } : a && e < b ? {
310
+ nextChunkMs: Math.min(b, e * 2),
311
+ reason: "increase"
312
+ } : {
313
+ nextChunkMs: e,
314
+ reason: "keep"
315
+ };
316
+ }
317
+ async function we(e, t, n, r, i, a, o) {
318
+ if (e.callWS) return e.callWS({
319
+ type: "history/history_during_period",
320
+ start_time: n.toISOString(),
321
+ end_time: r.toISOString(),
322
+ entity_ids: t,
323
+ minimal_response: i,
324
+ no_attributes: a,
325
+ significant_changes_only: o
326
+ });
327
+ let s = new URLSearchParams({
328
+ filter_entity_id: t.join(","),
329
+ end_time: r.toISOString()
330
+ });
331
+ return i && s.set("minimal_response", "1"), a && s.set("no_attributes", "1"), o && s.set("significant_changes_only", "1"), e.callApi("GET", `history/period/${encodeURIComponent(n.toISOString())}?${s.toString()}`);
332
+ }
333
+ async function Te(e, t) {
334
+ let n = 1;
335
+ for (;;) {
336
+ if (t.isCancelled?.()) throw Error("History request cancelled");
337
+ let r = t.onPerformance ? f() : 0;
338
+ try {
339
+ t.onPerformance?.({
340
+ event: "history.chunk_attempt",
341
+ details: {
342
+ taskId: t.taskId,
343
+ attempt: n,
344
+ maxAttempts: t.maxAttempts,
345
+ timeoutMs: t.timeoutMs
346
+ }
347
+ });
348
+ let i = await le(e(), t.timeoutMs);
349
+ return t.onPerformance?.({
350
+ event: "history.chunk_success",
351
+ details: {
352
+ taskId: t.taskId,
353
+ attempt: n,
354
+ durationMs: Math.round(f() - r)
355
+ }
356
+ }), i;
357
+ } catch (e) {
358
+ let i = k(e), a = i && n < t.maxAttempts && !t.isCancelled?.();
359
+ if (t.onPerformance?.({
360
+ event: a ? "history.chunk_retry" : "history.chunk_error",
361
+ details: {
362
+ taskId: t.taskId,
363
+ attempt: n,
364
+ maxAttempts: t.maxAttempts,
365
+ retryable: i,
366
+ error: D(e),
367
+ durationMs: Math.round(f() - r)
368
+ }
369
+ }), !a) throw e;
370
+ await ce(se(n, t.retryBaseDelayMs)), n += 1;
371
+ }
372
+ }
373
+ }
374
+ async function Ee(e, t, n, r, i, a, o = {}) {
375
+ if (!e.callWS && !e.callApi) throw Error("Home Assistant history API is unavailable");
376
+ let s = [...new Set(t.map((e) => e.entityId))], c = new Set(t.filter((e) => e.kind === "entity_attribute").map((e) => e.entityId)), l = s.filter((e) => !c.has(e)), u = s.filter((e) => c.has(e)), d = o.accumulator ?? new ve(), p = [], x = Math.max(1, Math.floor(o.chunkTimeoutMs ?? h)), ee = Math.max(1, Math.floor(o.maxChunkAttempts ?? g)), te = Math.max(0, Math.floor(o.chunkRetryBaseDelayMs ?? _)), ne = (e, t) => Te(t, {
377
+ taskId: e,
378
+ timeoutMs: x,
379
+ maxAttempts: ee,
380
+ retryBaseDelayMs: te,
381
+ isCancelled: o.isCancelled,
382
+ onPerformance: a
383
+ }), S = /* @__PURE__ */ new Map(), re = (e, t, n, r, i, a, o) => {
384
+ let s = [
385
+ r,
386
+ t.toISOString(),
387
+ n.toISOString(),
388
+ i ? "minimal" : "full",
389
+ a ? "noattr" : "attrs",
390
+ o ? "significant" : "all"
391
+ ].join("|"), c = S.get(s);
392
+ c ? c.entityIds.push(e) : S.set(s, {
393
+ entityIds: [e],
394
+ start: t,
395
+ end: n,
396
+ coverageKind: r,
397
+ minimalResponse: i,
398
+ noAttributes: a,
399
+ significantChangesOnly: o
400
+ });
401
+ }, C = [];
402
+ for (let e of l) for (let t of d.missingIntervals(e, n, r, "state")) re(e, t.start, t.end, "state", !0, !0, !0);
403
+ for (let e of u) for (let t of d.missingIntervals(e, n, r, "full")) C.push({
404
+ entityId: e,
405
+ start: t.start,
406
+ end: t.end
407
+ });
408
+ let ie = C.reduce((e, t) => {
409
+ let n = t.end.getTime() - t.start.getTime();
410
+ return e + Math.max(1, Math.ceil(n / v));
411
+ }, 0), w = S.size + ie, T = 0, E = /* @__PURE__ */ new Set(), ae = async (s, c, l) => {
412
+ let u = T;
413
+ if (T += 1, o.isCancelled?.()) return {
414
+ stateCount: 0,
415
+ requestDurationMs: Math.round(l),
416
+ normalizeDurationMs: 0,
417
+ mergeDurationMs: 0,
418
+ buildDurationMs: 0
419
+ };
420
+ await A();
421
+ let p = f(), m = he(c, s.entityIds), h = f() - p, g = [...m.values()].reduce((e, t) => e + t.length, 0);
422
+ a?.({
423
+ event: "history.batch",
424
+ details: {
425
+ batchIndex: u,
426
+ batchCount: w,
427
+ entityCount: s.entityIds.length,
428
+ stateCount: g,
429
+ requestDurationMs: Math.round(l),
430
+ normalizeDurationMs: Math.round(h)
431
+ }
432
+ });
433
+ let _ = f(), v = /* @__PURE__ */ new Set();
434
+ for (let [e, t] of m) d.integrate(e, t, s.start, s.end, s.coverageKind), v.add(e), E.add(e);
435
+ let y = f() - _;
436
+ a?.({
437
+ event: "history.merge",
438
+ details: {
439
+ batchIndex: u,
440
+ entityCount: s.entityIds.length,
441
+ stateCount: g,
442
+ mergeDurationMs: Math.round(y)
443
+ }
444
+ });
445
+ let b = 0;
446
+ if (i) {
447
+ await A();
448
+ let o = f();
449
+ for (let i of t) (v.has(i.entityId) || !D.has(i.id)) && (i.kind === "entity_attribute" ? d.hasFullStates(i.entityId) : d.hasStates(i.entityId)) && D.set(i.id, d.buildSeries(i, e, n, r));
450
+ let s = t.map((e) => D.get(e.id)).filter((e) => e !== void 0);
451
+ b = f() - o, a?.({
452
+ event: "history.progress_series",
453
+ details: {
454
+ batchIndex: u,
455
+ seriesCount: s.length,
456
+ pointCount: s.reduce((e, t) => e + t.points.length, 0),
457
+ buildDurationMs: Math.round(b)
458
+ }
459
+ }), i(s), await A(120);
460
+ }
461
+ return {
462
+ stateCount: g,
463
+ requestDurationMs: Math.round(l),
464
+ normalizeDurationMs: Math.round(h),
465
+ mergeDurationMs: Math.round(y),
466
+ buildDurationMs: Math.round(b)
467
+ };
468
+ };
469
+ for (let t of S.values()) {
470
+ let n = t.coverageKind === "full" ? "attr" : "state", r = [...new Set(t.entityIds)], i = `${n}:${r.join(",")}:${t.start.toISOString()}:${t.end.toISOString()}`;
471
+ p.push({
472
+ id: i,
473
+ entityIds: r,
474
+ start: t.start,
475
+ end: t.end,
476
+ coverageKind: t.coverageKind,
477
+ run: () => ne(i, () => we(e, r, t.start, t.end, t.minimalResponse, t.noAttributes, t.significantChangesOnly))
478
+ });
479
+ }
480
+ a?.({
481
+ event: "history.start",
482
+ details: {
483
+ sourceCount: t.length,
484
+ entityCount: s.length,
485
+ batchCount: w,
486
+ attributeChunkHours: v / 36e5,
487
+ minAttributeChunkHours: y / 36e5,
488
+ maxAttributeChunkHours: b / 36e5,
489
+ adaptiveAttributeChunks: C.length > 0,
490
+ cachedSourceCount: t.filter((e) => d.hasCoverage(e.entityId, n, r, e.kind === "entity_attribute" ? "full" : "state")).length,
491
+ chunkTimeoutMs: x,
492
+ maxChunkAttempts: ee,
493
+ rangeHours: Math.round((r.getTime() - n.getTime()) / 36e3) / 100
494
+ }
495
+ });
496
+ let D = /* @__PURE__ */ new Map();
497
+ for (let i of t) (i.kind === "entity_attribute" ? d.hasFullStates(i.entityId) : d.hasStates(i.entityId)) && D.set(i.id, d.buildSeries(i, e, n, r));
498
+ await m(p, {
499
+ concurrency: o.concurrency ?? 1,
500
+ isCancelled: o.isCancelled,
501
+ onEvent: (e) => {
502
+ a?.({
503
+ event: `history.${e.event}`,
504
+ details: {
505
+ taskId: e.taskId,
506
+ queuedCount: e.queuedCount,
507
+ activeCount: e.activeCount,
508
+ completedCount: e.completedCount
509
+ }
510
+ });
511
+ },
512
+ onResult: async ({ task: e, value: t, durationMs: n }) => {
513
+ await ae(e, t, n);
514
+ }
515
+ });
516
+ let O = 0;
517
+ for (let t of C) {
518
+ let n = v;
519
+ for (let r = t.start.getTime(); r < t.end.getTime() && !o.isCancelled?.();) {
520
+ let i = new Date(r), o = new Date(Math.min(r + n, t.end.getTime())), s = o.getTime() - i.getTime(), c = `attr:${t.entityId}:${i.toISOString()}:${o.toISOString()}`;
521
+ a?.({
522
+ event: "history.queue.task_start",
523
+ details: {
524
+ taskId: c,
525
+ queuedCount: void 0,
526
+ activeCount: 1,
527
+ completedCount: O
528
+ }
529
+ });
530
+ let l = f(), u = await ne(c, () => we(e, [t.entityId], i, o, !1, !1, !1)), d = f() - l;
531
+ O += 1, a?.({
532
+ event: "history.queue.task_complete",
533
+ details: {
534
+ taskId: c,
535
+ queuedCount: void 0,
536
+ activeCount: 0,
537
+ completedCount: O
538
+ }
539
+ });
540
+ let p = await ae({
541
+ id: c,
542
+ entityIds: [t.entityId],
543
+ start: i,
544
+ end: o,
545
+ coverageKind: "full"
546
+ }, u, d), m = Ce(n, p);
547
+ a?.({
548
+ event: "history.adaptive_chunk",
549
+ details: {
550
+ taskId: c,
551
+ entityId: t.entityId,
552
+ chunkHours: Math.round(s / 36e3) / 100,
553
+ nextChunkHours: Math.round(m.nextChunkMs / 36e3) / 100,
554
+ stateCount: p.stateCount,
555
+ requestDurationMs: p.requestDurationMs,
556
+ processingDurationMs: p.normalizeDurationMs + p.mergeDurationMs + p.buildDurationMs,
557
+ reason: m.reason
558
+ }
559
+ }), n = m.nextChunkMs, r = o.getTime();
560
+ }
561
+ }
562
+ let oe = a ? f() : 0, k = t.map((t) => {
563
+ let i = D.get(t.id);
564
+ return i && !E.has(t.entityId) ? i : d.buildSeries(t, e, n, r);
565
+ }), se = a ? f() - oe : 0;
566
+ return a?.({
567
+ event: "history.final_series",
568
+ details: {
569
+ seriesCount: k.length,
570
+ pointCount: k.reduce((e, t) => e + t.points.length, 0),
571
+ buildDurationMs: Math.round(se)
572
+ }
573
+ }), k;
574
+ }
575
+ function De(e, t, n, r, i) {
576
+ let a = t.flatMap((t) => {
577
+ let n = fe(t, e), r = pe(t);
578
+ return n !== void 0 && Number.isFinite(r) ? [{
579
+ time: r,
580
+ value: n
581
+ }] : [];
582
+ });
583
+ return {
584
+ source: e,
585
+ points: ie(a.length > 0 ? me(a, r, i) : _e(n, e, r, i))
586
+ };
587
+ }
588
+ //#endregion
589
+ //#region src/controllers/data-controller.ts
590
+ var Oe = 6e4, ke = 48;
591
+ function Ae(e) {
592
+ requestAnimationFrame(() => requestAnimationFrame(e));
593
+ }
594
+ function je(e) {
595
+ return e instanceof Error ? e.message : String(e);
596
+ }
597
+ function P(e) {
598
+ return `${e.kind === "entity_attribute" ? "full" : "state"}:${e.entityId}`;
599
+ }
600
+ function Me(e, t) {
601
+ if (e.length !== t.length) return !1;
602
+ for (let n = 0; n < e.length; n++) {
603
+ let r = e[n], i = t[n];
604
+ if (r.source.id !== i.source.id || r.points.length !== i.points.length) return !1;
605
+ for (let e = 0; e < r.points.length; e++) {
606
+ let t = r.points[e], n = i.points[e];
607
+ if (t.time !== n.time || t.value !== n.value) return !1;
608
+ }
609
+ }
610
+ return !0;
611
+ }
612
+ function Ne(e, t) {
613
+ let n = e.findIndex((e) => e.time === t.time);
614
+ if (n !== -1) {
615
+ if (e[n].value === t.value) return e;
616
+ let r = [...e];
617
+ return r[n] = t, r;
618
+ }
619
+ return [...e].reverse().find((e) => e.time < t.time)?.value === t.value ? e : [...e, t].sort((e, t) => e.time - t.time);
620
+ }
621
+ var Pe = class {
622
+ constructor(e) {
623
+ this.series = [], this.loading = !1, this.error = "", this.debugPerformance = !1, this._prevKey = "", this._nextSessionId = 0, this._progressUpdateScheduled = !1, this._lastProgressUpdateMs = 0, this.host = e, e.addController(this);
624
+ }
625
+ hostConnected() {}
626
+ hostDisconnected() {}
627
+ _createSession(e, t, n) {
628
+ this._session && (this._session.cancelled = !0);
629
+ let r = {
630
+ id: ++this._nextSessionId,
631
+ startTime: t.getTime(),
632
+ endTime: n.getTime(),
633
+ cancelled: !1,
634
+ activeLoads: 0,
635
+ sources: [...e],
636
+ sourceStates: new Map(e.map((e) => [e.id, "queued"])),
637
+ activeEntityLoads: /* @__PURE__ */ new Map(),
638
+ accumulator: new ve()
639
+ };
640
+ return this._session = r, r;
641
+ }
642
+ _cancelSession() {
643
+ this._session && (this._session.cancelled = !0), this._session = void 0, this._progressUpdateScheduled = !1;
644
+ }
645
+ _activeSession(e, t) {
646
+ let n = this._session;
647
+ if (!(!n || n.cancelled)) return n.startTime === e.getTime() && n.endTime === t.getTime() ? n : void 0;
648
+ }
649
+ _isCurrentSession(e) {
650
+ return this._session === e && !e.cancelled;
651
+ }
652
+ _addSessionSources(e, t) {
653
+ let n = new Set(e.sources.map((e) => e.id));
654
+ for (let r of t) n.has(r.id) || (n.add(r.id), e.sources.push(r));
655
+ }
656
+ _hasActiveEntityLoad(e, t) {
657
+ return (e.activeEntityLoads.get(P(t)) ?? 0) > 0;
658
+ }
659
+ _beginLoad(e, t) {
660
+ e.activeLoads += 1;
661
+ for (let n of t) {
662
+ e.sourceStates.set(n.id, "loading");
663
+ let t = P(n);
664
+ e.activeEntityLoads.set(t, (e.activeEntityLoads.get(t) ?? 0) + 1);
665
+ }
666
+ }
667
+ _completeLoad(e, t) {
668
+ e.activeLoads = Math.max(0, e.activeLoads - 1);
669
+ for (let n of t) {
670
+ let t = P(n), r = Math.max(0, (e.activeEntityLoads.get(t) ?? 0) - 1);
671
+ r > 0 ? e.activeEntityLoads.set(t, r) : e.activeEntityLoads.delete(t);
672
+ }
673
+ this.loading = e.activeLoads > 0;
674
+ }
675
+ _sessionSources(e, t) {
676
+ return t.filter((t) => e.sourceStates.has(t.source.id));
677
+ }
678
+ _hasAccumulatorSeries(e, t) {
679
+ return t.kind === "entity_attribute" ? e.accumulator.hasFullStates(t.entityId) : e.accumulator.hasStates(t.entityId);
680
+ }
681
+ _availableSessionSeries(e, t, n, r, i) {
682
+ let a = this._sessionSources(e, i), o = new Set(a.map((e) => e.source.id));
683
+ for (let i of e.sources) o.has(i.id) || !e.sourceStates.has(i.id) || this._hasAccumulatorSeries(e, i) && (a.push(e.accumulator.buildSeries(i, t, n, r)), o.add(i.id));
684
+ return a;
685
+ }
686
+ _requestProgressUpdate(e) {
687
+ if (this._progressUpdateScheduled) return;
688
+ this._progressUpdateScheduled = !0;
689
+ let t = f() - this._lastProgressUpdateMs, n = Math.max(0, ke - t);
690
+ setTimeout(() => {
691
+ requestAnimationFrame(() => {
692
+ this._progressUpdateScheduled = !1, this._isCurrentSession(e) && (this._lastProgressUpdateMs = f(), this.host.requestUpdate());
693
+ });
694
+ }, n);
695
+ }
696
+ fetch(e, t, n, r) {
697
+ let i = `${t.map((e) => e.id).join("|")}|${n.getTime()}|${r.getTime()}`;
698
+ if (i === this._prevKey && !this.error) return;
699
+ if (this._prevKey = i, !e || t.length === 0) {
700
+ this.series = [], this.loading = !1, this.error = e ? "No sources provided" : "No hass object", this.host.requestUpdate();
701
+ return;
702
+ }
703
+ let a = this._createSession(t, n, r), o = f();
704
+ this.series = [], this.loading = !0, this.error = "", this._beginLoad(a, t), this.debugPerformance && p(this.debugPerformance, "controller.fetch_start", {
705
+ sessionId: a.id,
706
+ sourceCount: t.length,
707
+ rangeHours: Math.round((r.getTime() - n.getTime()) / 36e3) / 100
708
+ }), this.host.requestUpdate(), Ee(e, a.sources, n, r, (t) => {
709
+ if (!this._isCurrentSession(a)) return;
710
+ let i = f(), o = this._availableSessionSeries(a, e, n, r, t);
711
+ this.series = this._mergeSeries(this.series.filter((e) => !a.sources.some((t) => t.id === e.source.id)), o);
712
+ for (let e of o) a.sourceStates.set(e.source.id, "partial");
713
+ this._requestProgressUpdate(a), this.debugPerformance && p(this.debugPerformance, "controller.progress_update", {
714
+ sessionId: a.id,
715
+ sourceCount: t.length,
716
+ pointCount: t.reduce((e, t) => e + t.points.length, 0),
717
+ updateDurationMs: Math.round(f() - i)
718
+ });
719
+ }, this.debugPerformance ? (e) => {
720
+ p(this.debugPerformance, e.event, e.details);
721
+ } : void 0, {
722
+ isCancelled: () => !this._isCurrentSession(a),
723
+ chunkTimeoutMs: Oe,
724
+ accumulator: a.accumulator
725
+ }).then((i) => {
726
+ this._isCurrentSession(a) && Ae(() => {
727
+ if (!this._isCurrentSession(a)) return;
728
+ let s = f(), c = this._availableSessionSeries(a, e, n, r, i), l = this._mergeSeries(this.series.filter((e) => !a.sources.some((t) => t.id === e.source.id)), c);
729
+ Me(this.series, l) || (this.series = l);
730
+ for (let e of c) a.sourceStates.set(e.source.id, "ready");
731
+ this._completeLoad(a, t), this.host.requestUpdate(), this.debugPerformance && p(this.debugPerformance, "controller.fetch_complete", {
732
+ sessionId: a.id,
733
+ sourceCount: i.length,
734
+ pointCount: i.reduce((e, t) => e + t.points.length, 0),
735
+ totalDurationMs: Math.round(f() - o),
736
+ updateDurationMs: Math.round(f() - s)
737
+ });
738
+ });
739
+ }).catch((e) => {
740
+ if (this._isCurrentSession(a)) {
741
+ for (let e of t) a.sourceStates.set(e.id, "error");
742
+ this.error = je(e), this._completeLoad(a, t), this.host.requestUpdate(), this.debugPerformance && p(this.debugPerformance, "controller.fetch_error", {
743
+ sessionId: a.id,
744
+ totalDurationMs: Math.round(f() - o),
745
+ error: this.error
746
+ });
747
+ }
748
+ });
749
+ }
750
+ setImportedSeries(e, t, n) {
751
+ this._cancelSession(), this.series = e, this.loading = !1, this.error = "", this._prevKey = `${e.map((e) => e.source.id).join("|")}|${t.getTime()}|${n.getTime()}`, this.host.requestUpdate();
752
+ }
753
+ setError(e) {
754
+ this._cancelSession(), this.loading = !1, this.error = e, this.host.requestUpdate();
755
+ }
756
+ addSources(e, t, n, r) {
757
+ if (!e || t.length === 0) return;
758
+ let i = this._activeSession(n, r) ?? this._createSession(this.series.map((e) => e.source), n, r), a = new Set([...this.series.map((e) => e.source.id), ...i.sourceStates.keys()]), o = t.filter((e) => !a.has(e.id));
759
+ if (o.length === 0) return;
760
+ let s = new Set(i.activeEntityLoads.keys());
761
+ this._addSessionSources(i, o);
762
+ let c = o.filter((e) => !this._hasActiveEntityLoad(i, e)), l = new Set(c.map((e) => e.id)), u = i.sources.filter((e) => l.has(e.id) || !s.has(P(e))), d = f();
763
+ for (let e of o) i.sourceStates.set(e.id, c.includes(e) ? "queued" : "loading");
764
+ if (c.length === 0) {
765
+ let t = this._availableSessionSeries(i, e, n, r, []);
766
+ if (t.length > 0) {
767
+ this._mergePartial(t);
768
+ for (let e of t) i.sourceStates.set(e.source.id, "partial");
769
+ }
770
+ this.loading = i.activeLoads > 0, this._requestProgressUpdate(i), this.debugPerformance && p(this.debugPerformance, "controller.add_sources_joined_active_load", {
771
+ sessionId: i.id,
772
+ sourceCount: o.length,
773
+ existingSourceCount: this.series.length
774
+ });
775
+ return;
776
+ }
777
+ this.loading = !0, this._beginLoad(i, c), this.debugPerformance && p(this.debugPerformance, "controller.add_sources_start", {
778
+ sessionId: i.id,
779
+ sourceCount: c.length,
780
+ joinedActiveSourceCount: o.length - c.length,
781
+ existingSourceCount: this.series.length,
782
+ rangeHours: Math.round((r.getTime() - n.getTime()) / 36e3) / 100
783
+ }), this.host.requestUpdate(), Ee(e, u, n, r, (t) => {
784
+ if (!this._isCurrentSession(i)) return;
785
+ let a = f(), o = this._availableSessionSeries(i, e, n, r, t);
786
+ this._mergePartial(o);
787
+ for (let e of o) i.sourceStates.set(e.source.id, "partial");
788
+ this._requestProgressUpdate(i), this.debugPerformance && p(this.debugPerformance, "controller.add_sources_progress", {
789
+ sessionId: i.id,
790
+ sourceCount: t.length,
791
+ pointCount: t.reduce((e, t) => e + t.points.length, 0),
792
+ mergeDurationMs: Math.round(f() - a)
793
+ });
794
+ }, this.debugPerformance ? (e) => {
795
+ p(this.debugPerformance, e.event, e.details);
796
+ } : void 0, {
797
+ isCancelled: () => !this._isCurrentSession(i),
798
+ chunkTimeoutMs: Oe,
799
+ accumulator: i.accumulator
800
+ }).then((t) => {
801
+ this._isCurrentSession(i) && Ae(() => {
802
+ if (!this._isCurrentSession(i)) return;
803
+ let a = f(), o = this._availableSessionSeries(i, e, n, r, t), s = this._mergeSeries(this.series, o);
804
+ Me(this.series, s) || (this.series = s);
805
+ for (let e of o) i.sourceStates.set(e.source.id, "ready");
806
+ this._completeLoad(i, c), this.host.requestUpdate(), this.debugPerformance && p(this.debugPerformance, "controller.add_sources_complete", {
807
+ sessionId: i.id,
808
+ sourceCount: t.length,
809
+ pointCount: t.reduce((e, t) => e + t.points.length, 0),
810
+ totalDurationMs: Math.round(f() - d),
811
+ mergeDurationMs: Math.round(f() - a)
812
+ });
813
+ });
814
+ }).catch((e) => {
815
+ if (this._isCurrentSession(i)) {
816
+ for (let e of c) i.sourceStates.set(e.id, "error");
817
+ this.error = je(e), this._completeLoad(i, c), this.host.requestUpdate(), this.debugPerformance && p(this.debugPerformance, "controller.add_sources_error", {
818
+ sessionId: i.id,
819
+ totalDurationMs: Math.round(f() - d),
820
+ error: this.error
821
+ });
822
+ }
823
+ });
824
+ }
825
+ updateLivePoints(e, t, n, r) {
826
+ if (!e || t.length === 0 || this.series.length === 0) return;
827
+ let i = n.getTime(), a = r.getTime(), o = !1, s = new Map(t.map((e) => [e.id, e])), c = this.series.map((t) => {
828
+ let n = s.get(t.source.id);
829
+ if (!n) return t;
830
+ let r = ge(e, n, a);
831
+ if (!r) return t;
832
+ let c = {
833
+ ...r,
834
+ time: Math.min(Math.max(r.time, i), a)
835
+ }, l = Ne(t.points, c);
836
+ return l === t.points ? t : (o = !0, {
837
+ ...t,
838
+ points: l
839
+ });
840
+ });
841
+ o && (this.series = c, this.host.requestUpdate());
842
+ }
843
+ _mergeSeries(e, t) {
844
+ let n = [...e];
845
+ for (let e of t) {
846
+ let t = n.findIndex((t) => t.source.id === e.source.id);
847
+ t === -1 ? n.push(e) : n[t] = e;
848
+ }
849
+ return n;
850
+ }
851
+ _mergePartial(e) {
852
+ this.series = this._mergeSeries(this.series, e);
853
+ }
854
+ removeSources(e) {
855
+ if (e.length === 0) return;
856
+ let t = new Set(e);
857
+ this.series = this.series.filter((e) => !t.has(e.source.id));
858
+ for (let t of e) this._session?.sourceStates.delete(t);
859
+ this._prevKey = this.series.map((e) => e.source.id).join("|") + "|", this.host.requestUpdate();
860
+ }
861
+ }, F = [
862
+ "#ff9800",
863
+ "#42a5f5",
864
+ "#66bb6a",
865
+ "#ec407a",
866
+ "#ab47bc",
867
+ "#26a69a"
868
+ ], Fe = {
869
+ current_temperature: "#42a5f5",
870
+ temperature: "#ff9800"
871
+ }, Ie = new Set(Object.values(Fe)), I = F.filter((e) => !Ie.has(e));
872
+ function L(e) {
873
+ return I[e % I.length];
874
+ }
875
+ function R(e) {
876
+ return e.trim().toLowerCase();
877
+ }
878
+ function Le(e) {
879
+ return `hsl(${(e * 137.508 % 360).toFixed(1)} 68% 52%)`;
880
+ }
881
+ function Re(e, t, n) {
882
+ if (!t.has(R(e))) return e;
883
+ let r = [
884
+ ...I.slice(n % I.length),
885
+ ...I.slice(0, n % I.length),
886
+ ...F
887
+ ];
888
+ for (let e of r) if (!t.has(R(e))) return e;
889
+ let i = n, a = Le(i);
890
+ for (; t.has(R(a));) i += 1, a = Le(i);
891
+ return a;
892
+ }
893
+ function ze(e) {
894
+ return R(e);
895
+ }
896
+ var Be = 214;
897
+ function Ve(e) {
898
+ if (!Number.isFinite(e) || Number.isInteger(e)) return 0;
899
+ let t = e.toString().toLowerCase();
900
+ if (t.includes("e-")) {
901
+ let [e, n] = t.split("e-"), r = e.split(".")[1]?.length ?? 0;
902
+ return Math.min(r + Number(n), 4);
903
+ }
904
+ return Math.min(t.split(".")[1]?.length ?? 0, 4);
905
+ }
906
+ function z(e, t) {
907
+ let n = 10 ** t;
908
+ return Math.round(e * n) / n;
909
+ }
910
+ function He(e, t, n = 5) {
911
+ if (!Number.isFinite(e) || !Number.isFinite(t)) return [e, t];
912
+ let r = Math.abs(t - e);
913
+ if (r < 1e-10) return [e];
914
+ let i = Ue(r / (Math.max(n, 2) - 1)), a = Math.floor(e / i) * i, o = Math.ceil(t / i) * i, s = i * 1e-8, c = [];
915
+ for (let e = a; e <= o + s; e += i) c.push(We(e, i));
916
+ return c;
917
+ }
918
+ function Ue(e) {
919
+ if (e <= 0) return 1;
920
+ let t = Math.floor(Math.log10(Math.abs(e))), n = e / 10 ** t, r;
921
+ return r = n < 1.5 ? 1 : n < 3 ? 2 : n < 7 ? 5 : 10, r * 10 ** t;
922
+ }
923
+ function We(e, t) {
924
+ let n = Math.max(0, -Math.floor(Math.log10(Math.abs(t) || 1)) + 1);
925
+ return parseFloat(e.toFixed(n));
926
+ }
927
+ function Ge(e) {
928
+ let t = 0;
929
+ for (let n of e) {
930
+ let e = String(n), r = e.indexOf(".");
931
+ r !== -1 && (t = Math.max(t, e.length - r - 1));
932
+ }
933
+ return t;
934
+ }
935
+ function Ke(e, t, n) {
936
+ let r = t - e;
937
+ if (r < 1e-6) {
938
+ let r = Math.max(Math.abs(t) * .05, 1);
939
+ return {
940
+ min: z(e - r, n),
941
+ max: z(t + r, n)
942
+ };
943
+ }
944
+ let i = Math.max(r * .08, 10 ** -n), a = 10 ** n, o = Math.ceil(i * a) / a;
945
+ return {
946
+ min: z(e - o, n),
947
+ max: z(t + o, n)
948
+ };
949
+ }
950
+ function qe(e) {
951
+ return 28 + (Math.max(e, 1) - 1) * Be + 180 + 18;
952
+ }
953
+ var Je = .15, Ye = 8;
954
+ function Xe(e) {
955
+ return Math.max(e.max - e.min, 1e-9);
956
+ }
957
+ function Ze(e) {
958
+ return (e.min + e.max) / 2;
959
+ }
960
+ function B(e) {
961
+ return Math.log10(Math.max(Math.abs(e), 1e-9));
962
+ }
963
+ function V(e, t) {
964
+ let n = Math.abs(B(Xe(e)) - B(Xe(t))), r = Math.abs(B(Ze(e)) - B(Ze(t))), i = e.unit && t.unit && e.unit !== t.unit ? .4 : 0;
965
+ return n + r * .6 + i;
966
+ }
967
+ function Qe(e) {
968
+ if (e.length < 2) return !1;
969
+ let t = Math.min(...e.map((e) => e.min)), n = Math.max(...e.map((e) => e.max)), r = Math.max(n - t, 1e-9), i = e.map((e) => e.max - e.min).filter((e) => e > 1e-6);
970
+ if (i.length < 2) return !1;
971
+ let a = Math.min(...i), o = Math.max(...i);
972
+ return Math.min(...i.map((e) => e / r)) <= Je && (o / Math.max(a, 1e-9) >= Ye || r / a >= Ye);
973
+ }
974
+ function $e(e) {
975
+ let t = e[0], n = e[1], r = -Infinity;
976
+ for (let i = 0; i < e.length; i++) for (let a = i + 1; a < e.length; a++) {
977
+ let o = V(e[i], e[a]);
978
+ o > r && (r = o, t = e[i], n = e[a]);
979
+ }
980
+ return t.order <= n.order ? [t, n] : [n, t];
981
+ }
982
+ function et(e) {
983
+ if (!Qe(e)) return [e, []];
984
+ let [t, n] = $e(e), r = [], i = [];
985
+ for (let a of e) a.id === t.id ? r.push(a) : a.id === n.id ? i.push(a) : V(a, t) <= V(a, n) ? r.push(a) : i.push(a);
986
+ return [r, i];
987
+ }
988
+ function tt(e, t, n, r) {
989
+ let i = Math.min(...n.map((e) => e.min)), a = Math.max(...n.map((e) => e.max)), o = Math.max(...n.map((e) => e.precision)), s = Ke(i, a, o), c = He(s.min, s.max);
990
+ return {
991
+ ids: new Set(n.map((e) => e.id)),
992
+ graphKey: e,
993
+ axis: t,
994
+ min: s.min,
995
+ max: s.max,
996
+ precision: Math.max(o, Ge(c)),
997
+ ticks: c,
998
+ top: r,
999
+ height: 180
1000
+ };
1001
+ }
1002
+ function nt(e) {
1003
+ let t = [];
1004
+ for (let [n, r] of e.entries()) {
1005
+ if (r.valueType !== "number" && r.valueType !== "boolean") continue;
1006
+ let e = r.points.map((e) => Number(e.value)).filter((e) => Number.isFinite(e)), i = r.scaleMode === "manual" && r.scaleMin !== void 0 ? r.scaleMin : 0, a = r.scaleMode === "manual" && r.scaleMax !== void 0 ? r.scaleMax : 1, o = r.valueType === "boolean" ? 0 : e.length > 0 ? Math.min(...e) : Math.min(i, a), s = r.valueType === "boolean" ? 1 : e.length > 0 ? Math.max(...e) : Math.max(i, a), c = r.valueType === "boolean" || e.length === 0 ? 0 : Math.max(...e.map((e) => Ve(e))), l = r.valueType === "boolean" ? "group:boolean" : r.scaleGroupKey, u = t.find((e) => e.key === l);
1007
+ u || (u = {
1008
+ key: l,
1009
+ series: []
1010
+ }, t.push(u)), r.scaleMode === "manual" && (r.scaleMin !== void 0 && (o = Math.min(o, r.scaleMin)), r.scaleMax !== void 0 && (s = Math.max(s, r.scaleMax))), u.series.push({
1011
+ id: r.id,
1012
+ unit: r.unit,
1013
+ min: o,
1014
+ max: s,
1015
+ precision: c,
1016
+ order: n
1017
+ });
1018
+ }
1019
+ return t.flatMap((e, t) => {
1020
+ let [n, r] = e.key === "group:boolean" ? [e.series, []] : et(e.series), i = 28 + t * Be, a = tt(e.key, "left", n, i);
1021
+ return r.length > 0 ? [a, tt(e.key, "right", r, i)] : [a];
1022
+ });
1023
+ }
1024
+ //#endregion
1025
+ //#region src/render/downsample.ts
1026
+ function rt(e, t, n, r) {
1027
+ let i = e.map((e) => ({
1028
+ time: e.time,
1029
+ value: Number(e.value)
1030
+ })).filter((e) => Number.isFinite(e.value)), a = r * 4;
1031
+ if (i.length <= a) return i;
1032
+ let o = /* @__PURE__ */ new Map();
1033
+ i.forEach((e, i) => {
1034
+ let a = Math.floor(n + (e.time - t.start) / (t.end - t.start) * r), s = o.get(a);
1035
+ s ? s.push({
1036
+ ...e,
1037
+ index: i
1038
+ }) : o.set(a, [{
1039
+ ...e,
1040
+ index: i
1041
+ }]);
1042
+ });
1043
+ let s = /* @__PURE__ */ new Map();
1044
+ for (let e of o.values()) {
1045
+ let t = e[0], n = e[e.length - 1], r = e.reduce((e, t) => t.value < e.value ? t : e, t), i = e.reduce((e, t) => t.value > e.value ? t : e, t);
1046
+ for (let e of [
1047
+ t,
1048
+ r,
1049
+ i,
1050
+ n
1051
+ ]) s.set(e.index, e);
1052
+ }
1053
+ return [...s.values()].sort((e, t) => e.index - t.index).map(({ time: e, value: t }) => ({
1054
+ time: e,
1055
+ value: t
1056
+ }));
1057
+ }
1058
+ //#endregion
1059
+ //#region src/render/climate-overlay.ts
1060
+ function it(e, t) {
1061
+ return 40 + (e - t.start) / (t.end - t.start) * 640;
1062
+ }
1063
+ function at(e, t) {
1064
+ let n = t.max - t.min;
1065
+ if (n < 1e-6) return t.top + t.height / 2;
1066
+ let r = t.height - 10;
1067
+ return t.top + 5 + r - (e - t.min) / n * r;
1068
+ }
1069
+ function ot(e, t) {
1070
+ if (e.length === 0) return;
1071
+ if (t <= e[0].time) return e[0].value;
1072
+ let n = e[e.length - 1];
1073
+ if (t >= n.time) return n.value;
1074
+ for (let n = 0; n < e.length - 1; n++) {
1075
+ let r = e[n], i = e[n + 1];
1076
+ if (t >= r.time && t <= i.time) {
1077
+ let e = i.time - r.time;
1078
+ return e <= 0 ? r.value : r.value + (t - r.time) / e * (i.value - r.value);
1079
+ }
1080
+ }
1081
+ }
1082
+ function st(e) {
1083
+ let t = /* @__PURE__ */ new Map();
1084
+ for (let n of e) {
1085
+ if (!n.id.startsWith("attr:")) continue;
1086
+ let e = n.id.split(":");
1087
+ if (e.length < 3) continue;
1088
+ let r = e[1], i = e.slice(2).join(":");
1089
+ t.has(r) || t.set(r, {});
1090
+ let a = t.get(r);
1091
+ i === "current_temperature" || i === "temperature" ? a.temp = n.id : i === "hvac_action" && (a.hvac = n.id);
1092
+ }
1093
+ for (let [, e] of t) if (e.temp && e.hvac) return {
1094
+ tempId: e.temp,
1095
+ hvacId: e.hvac
1096
+ };
1097
+ }
1098
+ function ct(e, t, n) {
1099
+ let r = st(e);
1100
+ if (!r) return [];
1101
+ let i = e.find((e) => e.id === r.tempId), a = e.find((e) => e.id === r.hvacId);
1102
+ if (!i || !a) return [];
1103
+ let o = t.find((e) => e.ids.has(i.id));
1104
+ if (!o) return [];
1105
+ let s = i.points.map((e) => ({
1106
+ time: e.time,
1107
+ value: Number(e.value)
1108
+ })).filter((e) => Number.isFinite(e.value)).sort((e, t) => e.time - t.time);
1109
+ return s.length === 0 ? [] : lt(a.points, n).filter((e) => e.value === "heating").reduce((e, t) => {
1110
+ let n = e[e.length - 1];
1111
+ return n && Math.abs(n.end - t.start) < 1 ? n.end = t.end : e.push({
1112
+ start: t.start,
1113
+ end: t.end
1114
+ }), e;
1115
+ }, []).flatMap(({ start: e, end: t }, r) => {
1116
+ let i = [
1117
+ {
1118
+ time: e,
1119
+ value: ot(s, e)
1120
+ },
1121
+ ...s.filter((n) => n.time > e && n.time < t),
1122
+ {
1123
+ time: t,
1124
+ value: ot(s, t)
1125
+ }
1126
+ ].filter((e) => e.value !== void 0);
1127
+ if (i.length === 0) return [];
1128
+ let c = o.top + o.height, l = [
1129
+ `${it(e, n).toFixed(1)},${c.toFixed(1)}`,
1130
+ ...i.map((e) => `${it(e.time, n).toFixed(1)},${at(e.value, o).toFixed(1)}`),
1131
+ `${it(t, n).toFixed(1)},${c.toFixed(1)}`
1132
+ ].join(" ");
1133
+ return [{
1134
+ id: `${a.id}:heat:${r}`,
1135
+ points: l
1136
+ }];
1137
+ });
1138
+ }
1139
+ function lt(e, t) {
1140
+ let n = Date.now();
1141
+ return e.flatMap((r, i) => {
1142
+ let a = Math.max(r.time, t.start), o = Math.min(e[i + 1]?.time ?? t.end, t.end, n);
1143
+ return o > a ? [{
1144
+ start: a,
1145
+ end: o,
1146
+ value: r.value
1147
+ }] : [];
1148
+ });
1149
+ }
1150
+ var ut = 16;
1151
+ function H(e, t) {
1152
+ return 40 + (e - t.start) / (t.end - t.start) * 640;
1153
+ }
1154
+ function U(e, t) {
1155
+ let n = t.max - t.min;
1156
+ if (n < 1e-6) return t.top + t.height / 2;
1157
+ let r = t.height - 10;
1158
+ return t.top + 5 + r - (e - t.min) / n * r;
1159
+ }
1160
+ function W(e, t) {
1161
+ return t.find((t) => t.ids.has(e.id));
1162
+ }
1163
+ function G(e, t) {
1164
+ let n = Date.now();
1165
+ return e.points.flatMap((r, i) => {
1166
+ let a = Math.max(r.time, t.start), o = Math.min(e.points[i + 1]?.time ?? t.end, t.end, n);
1167
+ return o > a ? [{
1168
+ start: a,
1169
+ end: o,
1170
+ value: r.value
1171
+ }] : [];
1172
+ });
1173
+ }
1174
+ var dt = new Set([
1175
+ "off",
1176
+ "idle",
1177
+ "none",
1178
+ "false"
1179
+ ]);
1180
+ function ft(e, t, n, r) {
1181
+ if (typeof e == "boolean") return e ? t : "var(--better-history-muted-color, var(--secondary-text-color, #888))";
1182
+ let i = String(e);
1183
+ return dt.has(i.toLowerCase()) ? "var(--better-history-muted-color, var(--secondary-text-color, #888))" : (n.has(i) || n.set(i, F[(r + n.size) % F.length]), n.get(i));
1184
+ }
1185
+ function pt(e, t) {
1186
+ return e + 34 + Math.max(t - 1, 0) * 14;
1187
+ }
1188
+ function mt(e, t, n, r) {
1189
+ return e.flatMap((e) => {
1190
+ if (e.valueType !== "number" && e.valueType !== "boolean" || e.lineMode === "column") return [];
1191
+ let i = W(e, t);
1192
+ if (!i) return [];
1193
+ let a = rt(ht(e.points, n, e.lineMode, r), n, 40, 640), { points: o, pathLength: s } = e.lineMode === "line" ? xt(a, n, i) : bt(a, n, i);
1194
+ return [{
1195
+ id: e.id,
1196
+ color: e.color,
1197
+ points: o,
1198
+ pathLength: s,
1199
+ lineWidth: e.lineWidth
1200
+ }];
1201
+ });
1202
+ }
1203
+ function ht(e, t, n, r) {
1204
+ let i = e.map((e) => ({
1205
+ time: e.time,
1206
+ value: Number(e.value)
1207
+ })).filter((e) => Number.isFinite(e.value)).sort((e, t) => e.time - t.time), a = i.filter((e) => e.time >= t.start && e.time <= t.end);
1208
+ if (n === "line") return a;
1209
+ let o = [...i].reverse().find((e) => e.time < t.start), s = o && (a.length === 0 || a[0].time > t.start) ? [{
1210
+ time: t.start,
1211
+ value: o.value
1212
+ }, ...a] : a, c = s[s.length - 1];
1213
+ return r.extendStairToEnd && c && c.time < t.end ? [...s, {
1214
+ time: t.end,
1215
+ value: c.value
1216
+ }] : s;
1217
+ }
1218
+ function gt(e) {
1219
+ return e.min <= 0 && e.max >= 0 ? 0 : e.min > 0 ? e.min : e.max;
1220
+ }
1221
+ function _t(e, t, n) {
1222
+ return e.flatMap((e) => {
1223
+ if (e.valueType !== "number" && e.valueType !== "boolean" || e.lineMode !== "column") return [];
1224
+ let r = W(e, t);
1225
+ if (!r) return [];
1226
+ let i = U(gt(r), r);
1227
+ return G(e, n).flatMap((t, a) => {
1228
+ let o = Number(t.value);
1229
+ if (!Number.isFinite(o)) return [];
1230
+ let s = H(t.start, n), c = H(t.end, n), l = U(o, r), u = Math.max(c - s, 1);
1231
+ return [{
1232
+ id: `${e.id}:${a}`,
1233
+ x: s,
1234
+ y: Math.min(l, i),
1235
+ width: u,
1236
+ height: Math.max(Math.abs(i - l), 1),
1237
+ fill: e.color
1238
+ }];
1239
+ });
1240
+ });
1241
+ }
1242
+ function vt(e, t, n) {
1243
+ let r = t + 10, i = 0;
1244
+ return e.flatMap((e, t) => {
1245
+ if (e.valueType === "number" || e.valueType === "boolean") return [];
1246
+ let a = r + i * 14;
1247
+ i += 1;
1248
+ let o = /* @__PURE__ */ new Map();
1249
+ return G(e, n).reduce((n, r) => {
1250
+ let i = ft(r.value, e.color, o, t), a = n[n.length - 1];
1251
+ return a && a.fill === i && Math.abs(a.end - r.start) < 1 ? a.end = r.end : n.push({
1252
+ start: r.start,
1253
+ end: r.end,
1254
+ fill: i
1255
+ }), n;
1256
+ }, []).map((t, r) => {
1257
+ let i = H(t.start, n), o = Math.max(H(t.end, n) - i, 1);
1258
+ return {
1259
+ id: `${e.id}:${r}`,
1260
+ x: i,
1261
+ y: a,
1262
+ width: o,
1263
+ fill: t.fill
1264
+ };
1265
+ });
1266
+ });
1267
+ }
1268
+ function yt(e) {
1269
+ return e.flatMap((e) => {
1270
+ let t = e.height - 10;
1271
+ return e.ticks.map((n) => ({
1272
+ y: e.top + 5 + t - (n - e.min) / (e.max - e.min) * t,
1273
+ value: St(n, e.precision)
1274
+ }));
1275
+ });
1276
+ }
1277
+ function bt(e, t, n) {
1278
+ if (e.length === 0) return {
1279
+ points: "",
1280
+ pathLength: 0
1281
+ };
1282
+ if (e.length === 1) return {
1283
+ points: `${H(e[0].time, t).toFixed(1)},${U(e[0].value, n).toFixed(1)}`,
1284
+ pathLength: 0
1285
+ };
1286
+ let r = [], i = 0;
1287
+ for (let a = 0; a < e.length - 1; a++) {
1288
+ let o = e[a], s = e[a + 1], c = H(o.time, t), l = U(o.value, n), u = H(s.time, t), d = U(s.value, n);
1289
+ a === 0 && r.push(`${c.toFixed(1)},${l.toFixed(1)}`), r.push(`${u.toFixed(1)},${l.toFixed(1)}`), r.push(`${u.toFixed(1)},${d.toFixed(1)}`), i += Math.abs(u - c) + Math.abs(d - l);
1290
+ }
1291
+ return {
1292
+ points: r.join(" "),
1293
+ pathLength: i
1294
+ };
1295
+ }
1296
+ function xt(e, t, n) {
1297
+ if (e.length === 0) return {
1298
+ points: "",
1299
+ pathLength: 0
1300
+ };
1301
+ let r = 0, i;
1302
+ return {
1303
+ points: e.map((e) => {
1304
+ let a = H(e.time, t), o = U(e.value, n);
1305
+ return i && (r += Math.hypot(a - i.x, o - i.y)), i = {
1306
+ x: a,
1307
+ y: o
1308
+ }, `${a.toFixed(1)},${o.toFixed(1)}`;
1309
+ }).join(" "),
1310
+ pathLength: r
1311
+ };
1312
+ }
1313
+ function St(e, t) {
1314
+ if (t <= 0 && Number.isInteger(e)) return String(e);
1315
+ let n = e.toFixed(t);
1316
+ return n = n.replace(/\.?0+$/, ""), n;
1317
+ }
1318
+ var K = 60 * 1e3, q = 60 * K, J = 24 * q, Ct = [
1319
+ 10 * K,
1320
+ 15 * K,
1321
+ 20 * K,
1322
+ 30 * K,
1323
+ q,
1324
+ 2 * q,
1325
+ 3 * q,
1326
+ 4 * q,
1327
+ 6 * q,
1328
+ 8 * q,
1329
+ 12 * q,
1330
+ J,
1331
+ 2 * J,
1332
+ 3 * J,
1333
+ 7 * J,
1334
+ 14 * J,
1335
+ 30 * J,
1336
+ 60 * J,
1337
+ 90 * J
1338
+ ];
1339
+ function wt(e, t) {
1340
+ for (let n of Ct) if (e / n <= t) return n;
1341
+ return Ct[Ct.length - 1];
1342
+ }
1343
+ function Tt(e, t, n = 12) {
1344
+ let r = t - e;
1345
+ if (r <= 0) return [];
1346
+ let i = wt(r, n), a = [], o = Math.ceil(e / i) * i;
1347
+ for (let e = o; e < t; e += i) {
1348
+ let t = new Date(e);
1349
+ a.push({
1350
+ time: e,
1351
+ bold: t.getHours() === 0 && t.getMinutes() === 0
1352
+ });
1353
+ }
1354
+ return a;
1355
+ }
1356
+ function Et(e, t) {
1357
+ let n = new Date(e), r = t / J;
1358
+ if (r > 88) {
1359
+ let e = n.toLocaleString("default", { month: "short" }), t = n.getFullYear();
1360
+ return n.getMonth() === 0 ? `${e} ${t}` : e;
1361
+ }
1362
+ if (r > 35) {
1363
+ let e = n.toLocaleString("default", { month: "short" });
1364
+ return `${n.getDate()} ${e}`;
1365
+ }
1366
+ if (r > 7) return `${n.getDate()}/${n.getMonth() + 1}`;
1367
+ if (r > 2) return n.toLocaleString("default", { weekday: "short" });
1368
+ let i = String(n.getHours()).padStart(2, "0"), a = String(n.getMinutes()).padStart(2, "0");
1369
+ return r > .5 ? `${i}:${a}` : `${i}:${a}:${String(n.getSeconds()).padStart(2, "0")}`;
1370
+ }
1371
+ function Dt(e, t) {
1372
+ let n = [], r;
1373
+ for (let i of e) {
1374
+ if (i.time < t.start) {
1375
+ r = i;
1376
+ continue;
1377
+ }
1378
+ if (i.time > t.end) break;
1379
+ n.push(i);
1380
+ }
1381
+ return r ? [r, ...n] : n;
1382
+ }
1383
+ function Ot(e, t) {
1384
+ return e.map((e) => ({
1385
+ ...e,
1386
+ points: Dt(e.points, t)
1387
+ }));
1388
+ }
1389
+ function kt(e) {
1390
+ return e.valueType === "boolean" ? "group:boolean" : e.scaleGroupKey;
1391
+ }
1392
+ function At(e, t, n) {
1393
+ let r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
1394
+ for (let t of e) {
1395
+ if (t.valueType !== "number" && t.valueType !== "boolean") continue;
1396
+ let e = kt(t);
1397
+ i.set(e, [...i.get(e) ?? [], t]);
1398
+ }
1399
+ for (let e of t) {
1400
+ if (e.valueType !== "number" && e.valueType !== "boolean") continue;
1401
+ let t = kt(e);
1402
+ r.set(t, [...r.get(t) ?? [], e]);
1403
+ }
1404
+ return [...i.entries()].flatMap(([e, t]) => Ot(r.get(e) ?? t, n));
1405
+ }
1406
+ function jt(e, t, n, r = !1, i = 12, a = !0) {
1407
+ let o = { extendStairToEnd: a }, s = nt(At(e, t, n)), c = new Set(s.map((e) => e.graphKey)).size, l = qe(c), u = e.filter((e) => e.valueType !== "number" && e.valueType !== "boolean").length, d = Tt(n.start, n.end, i), f = n.end - n.start;
1408
+ return {
1409
+ allSeries: e,
1410
+ visibleSeries: t,
1411
+ timeBounds: n,
1412
+ extendStairToEnd: a,
1413
+ numericScales: s,
1414
+ plotBottom: l,
1415
+ chartHeight: pt(l, u),
1416
+ numericLines: mt(t, s, n, o),
1417
+ numericColumns: _t(t, s, n),
1418
+ segments: vt(t, l, n),
1419
+ heatingAreas: r ? [] : ct(t, s, n),
1420
+ yAxisLabels: yt(s),
1421
+ xAxisLabels: d.map((e) => ({
1422
+ x: H(e.time, n),
1423
+ label: Et(e.time, f),
1424
+ bold: e.bold
1425
+ }))
1426
+ };
1427
+ }
1428
+ function Mt(e, t, n, r) {
1429
+ return e.filter((e) => (e.valueType === "number" || e.valueType === "boolean") && e.lineMode !== "column").flatMap((e) => {
1430
+ let i = W(e, t);
1431
+ if (!i) return [];
1432
+ let a = {
1433
+ ...i,
1434
+ top: 28
1435
+ }, o = rt(ht(e.points, n, e.lineMode, r), n, 40, 640), { points: s, pathLength: c } = e.lineMode === "line" ? xt(o, n, a) : bt(o, n, a);
1436
+ return {
1437
+ id: e.id,
1438
+ color: e.color,
1439
+ points: s,
1440
+ pathLength: c,
1441
+ lineWidth: e.lineWidth
1442
+ };
1443
+ });
1444
+ }
1445
+ function Nt(e, t, n) {
1446
+ return e.filter((e) => (e.valueType === "number" || e.valueType === "boolean") && e.lineMode === "column").flatMap((e) => {
1447
+ let r = W(e, t);
1448
+ if (!r) return [];
1449
+ let i = {
1450
+ ...r,
1451
+ top: 28
1452
+ }, a = U(gt(i), i);
1453
+ return G(e, n).flatMap((t, r) => {
1454
+ let o = Number(t.value);
1455
+ if (!Number.isFinite(o)) return [];
1456
+ let s = H(t.start, n), c = H(t.end, n), l = U(o, i);
1457
+ return [{
1458
+ id: `${e.id}:${r}`,
1459
+ x: s,
1460
+ y: Math.min(l, a),
1461
+ width: Math.max(c - s, 1),
1462
+ height: Math.max(Math.abs(a - l), 1),
1463
+ fill: e.color
1464
+ }];
1465
+ });
1466
+ });
1467
+ }
1468
+ function Pt(e, t, n) {
1469
+ return e.filter((e) => e.valueType !== "number" && e.valueType !== "boolean").flatMap((e, r) => {
1470
+ let i = t + r * 14, a = /* @__PURE__ */ new Map();
1471
+ return G(e, n).reduce((t, n) => {
1472
+ let i = ft(n.value, e.color, a, r), o = t[t.length - 1];
1473
+ return o && o.fill === i && Math.abs(o.end - n.start) < 1 ? o.end = n.end : t.push({
1474
+ start: n.start,
1475
+ end: n.end,
1476
+ fill: i
1477
+ }), t;
1478
+ }, []).map((t, r) => {
1479
+ let a = H(t.start, n), o = Math.max(H(t.end, n) - a, 1);
1480
+ return {
1481
+ id: `${e.id}:${r}`,
1482
+ x: a,
1483
+ y: i,
1484
+ width: o,
1485
+ fill: t.fill
1486
+ };
1487
+ });
1488
+ });
1489
+ }
1490
+ function Ft(e) {
1491
+ let t = e.height - 10;
1492
+ return e.ticks.map((n) => ({
1493
+ y: 33 + t - (n - e.min) / (e.max - e.min) * t,
1494
+ value: St(n, e.precision)
1495
+ }));
1496
+ }
1497
+ function It(e, t) {
1498
+ return t === 0 ? e : e.split(" ").map((e) => {
1499
+ let [n, r] = e.split(",");
1500
+ return `${n},${(parseFloat(r) + t).toFixed(1)}`;
1501
+ }).join(" ");
1502
+ }
1503
+ function Lt(e, t, n) {
1504
+ let r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Map();
1505
+ return {
1506
+ allSeries: e.map((e, t) => {
1507
+ let a = Re(e.color, r, n * F.length + t);
1508
+ return r.add(ze(a)), i.set(e.id, a), a === e.color ? e : {
1509
+ ...e,
1510
+ color: a
1511
+ };
1512
+ }),
1513
+ visibleSeries: t.map((e) => {
1514
+ let t = i.get(e.id);
1515
+ return t && t !== e.color ? {
1516
+ ...e,
1517
+ color: t
1518
+ } : e;
1519
+ })
1520
+ };
1521
+ }
1522
+ function Rt(e, t = 12) {
1523
+ let n = [], r = e.timeBounds, i = e.allSeries.filter((e) => e.valueType !== "number" && e.valueType !== "boolean"), a = e.visibleSeries.filter((e) => e.valueType !== "number" && e.valueType !== "boolean"), o = r.end - r.start, s = Tt(r.start, r.end, t).map((e) => ({
1524
+ x: H(e.time, r),
1525
+ label: Et(e.time, o),
1526
+ bold: e.bold
1527
+ }));
1528
+ if (e.numericScales.length === 0 && i.length > 0) {
1529
+ let e = a.length, t = 208 + (e > 0 ? 10 + e * 14 : 0) + 18, o = t + ut, c = Lt(i, a, 0);
1530
+ n.push({
1531
+ series: c.visibleSeries,
1532
+ allSeries: c.allSeries,
1533
+ scales: [],
1534
+ svgHeight: t,
1535
+ canvasHeight: o,
1536
+ lines: [],
1537
+ columns: [],
1538
+ segments: Pt(c.visibleSeries, 218, r),
1539
+ yLabels: [],
1540
+ rightYLabels: [],
1541
+ xLabels: s,
1542
+ heatingAreas: []
1543
+ });
1544
+ }
1545
+ let c = [...new Set(e.numericScales.map((e) => e.graphKey))];
1546
+ for (let t = 0; t < c.length; t++) {
1547
+ let o = c[t], l = e.numericScales.filter((e) => e.graphKey === o), u = l.find((e) => e.axis === "left") ?? l[0], d = l.find((e) => e.axis === "right"), f = new Set(l.flatMap((e) => [...e.ids])), p = e.allSeries.filter((e) => (e.valueType === "number" || e.valueType === "boolean") && kt(e) === o), m = e.visibleSeries.filter((e) => f.has(e.id)), h = t === 0 ? [...m, ...a] : m, g = Lt(t === 0 ? [...p, ...i] : p, h, t), _ = g.visibleSeries.filter((e) => e.valueType !== "number" && e.valueType !== "boolean"), v = _.length, y = 208 + (v > 0 ? 10 + v * 14 : 0) + 18, b = y + ut, x = 28 - u.top;
1548
+ n.push({
1549
+ series: g.visibleSeries,
1550
+ allSeries: g.allSeries,
1551
+ scale: u,
1552
+ scales: l,
1553
+ svgHeight: y,
1554
+ canvasHeight: b,
1555
+ lines: Mt(g.visibleSeries, l, r, { extendStairToEnd: e.extendStairToEnd }),
1556
+ columns: Nt(g.visibleSeries, l, r),
1557
+ segments: Pt(_, 218, r),
1558
+ yLabels: Ft(u),
1559
+ rightYLabels: d ? Ft(d) : [],
1560
+ xLabels: s,
1561
+ heatingAreas: t === 0 ? e.heatingAreas.map((e) => ({
1562
+ id: e.id,
1563
+ points: It(e.points, x)
1564
+ })) : []
1565
+ });
1566
+ }
1567
+ return n;
1568
+ }
1569
+ //#endregion
1570
+ //#region src/controllers/tooltip-controller.ts
1571
+ var zt = class {
1572
+ constructor(e) {
1573
+ this.tooltip = void 0, this._series = [], this._cacheKey = "", this._timeBounds = {
1574
+ start: 0,
1575
+ end: 1
1576
+ }, this._host = e, e.addController(this);
1577
+ }
1578
+ hostConnected() {}
1579
+ hostDisconnected() {
1580
+ this._frame !== void 0 && (cancelAnimationFrame(this._frame), this._frame = void 0);
1581
+ }
1582
+ sync(e, t, n, r, i) {
1583
+ this._timeBounds = i, this._rebuildCache(e, t, n);
1584
+ }
1585
+ _rebuildCache(e, t, n) {
1586
+ let r = `${t.map((e) => `${e.source.id}:${e.points.length}`).join("|")}::${n.join("|")}`;
1587
+ this._fetchedRef === t && this._cacheKey === r || (this._fetchedRef = t, this._cacheKey = r, this._series = e.filter((e) => !n.includes(e.id)).flatMap((e) => {
1588
+ let n = t.find((t) => t.source.id === e.id);
1589
+ return !n || n.points.length === 0 ? [] : [{
1590
+ id: e.id,
1591
+ label: e.label,
1592
+ color: e.color,
1593
+ points: n.points
1594
+ }];
1595
+ }));
1596
+ }
1597
+ handlePointerMove(e) {
1598
+ let t = this._svgPoint(e);
1599
+ if (!t) {
1600
+ this._clear();
1601
+ return;
1602
+ }
1603
+ this._pendingPoint = t, this._frame === void 0 && (this._frame = requestAnimationFrame(() => {
1604
+ this._frame = void 0, this._apply();
1605
+ }));
1606
+ }
1607
+ handlePointerLeave() {
1608
+ this._pendingPoint = void 0, this._clear();
1609
+ }
1610
+ _clear() {
1611
+ this.tooltip !== void 0 && (this.tooltip = void 0, this._host.requestUpdate(), this._emit());
1612
+ }
1613
+ _apply() {
1614
+ let e = this._pendingPoint;
1615
+ if (!e) return;
1616
+ let t = this._timeAt(e.x), n = this._series.filter((t) => e.activeIds.has(t.id)), r = this._nearestPoint(n, t);
1617
+ if (!r) {
1618
+ this.tooltip !== void 0 && (this.tooltip = void 0, this._host.requestUpdate(), this._emit());
1619
+ return;
1620
+ }
1621
+ let i = r.time, a = n.flatMap((e) => {
1622
+ let t = this._pointAtOrBefore(e.points, i);
1623
+ return t ? [{
1624
+ label: e.label,
1625
+ color: e.color,
1626
+ value: String(t.value)
1627
+ }] : [];
1628
+ });
1629
+ if (a.length === 0) {
1630
+ this.tooltip !== void 0 && (this.tooltip = void 0, this._host.requestUpdate(), this._emit());
1631
+ return;
1632
+ }
1633
+ if (this.tooltip?.time === i && this.tooltip.activeTop === e.activeTop && this.tooltip.activeHeight === e.activeHeight && this.tooltip.activeKey === e.activeKey && Math.abs(this.tooltip.tooltipX - Math.min(Math.max(e.x, 120), 600)) < 1 && Math.abs(this.tooltip.y - this._tooltipY(e)) < 1 && this.tooltip.placement === this._placement(e)) return;
1634
+ let o = Math.min(Math.max(e.x, 120), 600), s = this._tooltipY(e);
1635
+ this.tooltip = {
1636
+ x: H(i, this._timeBounds),
1637
+ tooltipX: o,
1638
+ y: s,
1639
+ placement: this._placement(e),
1640
+ activeTop: e.activeTop,
1641
+ activeHeight: e.activeHeight,
1642
+ activeKey: e.activeKey,
1643
+ time: i,
1644
+ values: a
1645
+ }, this._host.requestUpdate(), this._emit();
1646
+ }
1647
+ _placement(e) {
1648
+ let t = e.activeTop + e.activeHeight;
1649
+ return e.touchLike ? e.y < e.activeTop + e.activeHeight / 2 ? "above" : "below" : t - e.y < 120 ? "above" : "below";
1650
+ }
1651
+ _tooltipY(e) {
1652
+ let t = e.activeTop + e.activeHeight;
1653
+ return e.touchLike ? this._placement(e) === "above" ? t - 10 : e.activeTop + 10 : Math.min(Math.max(e.y, e.activeTop + 28), t - 28);
1654
+ }
1655
+ _emit() {
1656
+ this._host.dispatchEvent(new CustomEvent("tooltip-changed", {
1657
+ detail: this.tooltip ? {
1658
+ time: this.tooltip.time,
1659
+ values: this.tooltip.values
1660
+ } : null,
1661
+ bubbles: !0,
1662
+ composed: !0
1663
+ }));
1664
+ }
1665
+ _nearest(e, t) {
1666
+ if (e.length === 0) return;
1667
+ let n = 0, r = e.length - 1;
1668
+ for (; n < r;) {
1669
+ let i = Math.floor((n + r) / 2);
1670
+ e[i].time < t ? n = i + 1 : r = i;
1671
+ }
1672
+ let i = e[n], a = n > 0 ? e[n - 1] : void 0;
1673
+ return a && Math.abs(a.time - t) < Math.abs(i.time - t) ? a : i;
1674
+ }
1675
+ _nearestPoint(e, t) {
1676
+ let n, r = Infinity;
1677
+ for (let i of e) {
1678
+ let e = this._nearest(i.points, t);
1679
+ if (!e) continue;
1680
+ let a = Math.abs(e.time - t);
1681
+ a < r && (n = e, r = a);
1682
+ }
1683
+ return n;
1684
+ }
1685
+ _pointAtOrBefore(e, t) {
1686
+ if (e.length === 0) return;
1687
+ let n = 0, r = e.length - 1;
1688
+ for (; n < r;) {
1689
+ let i = Math.ceil((n + r) / 2);
1690
+ e[i].time <= t ? n = i : r = i - 1;
1691
+ }
1692
+ return e[n].time <= t ? e[n] : void 0;
1693
+ }
1694
+ _timeAt(e) {
1695
+ let t = Math.min(Math.max((e - 40) / 640, 0), 1);
1696
+ return this._timeBounds.start + t * (this._timeBounds.end - this._timeBounds.start);
1697
+ }
1698
+ _svgPoint(e) {
1699
+ let t = e.currentTarget;
1700
+ if (!(t instanceof Element)) return;
1701
+ let n = e.composedPath().find((e) => e instanceof HTMLElement && e.classList.contains("graph-canvas"));
1702
+ if (!n) return;
1703
+ let r = new Set((n.dataset.seriesIds ?? "").split("|").filter((e) => e !== ""));
1704
+ if (r.size === 0) return;
1705
+ let i = t.getBoundingClientRect(), a = n.getBoundingClientRect();
1706
+ if (e.clientX < a.left || e.clientX > a.right || e.clientY < a.top || e.clientY > a.bottom) return;
1707
+ let o = a.top - i.top;
1708
+ return {
1709
+ x: (e.clientX - a.left) / a.width * 720,
1710
+ y: e.clientY - i.top,
1711
+ activeTop: o,
1712
+ activeHeight: a.height,
1713
+ activeIds: r,
1714
+ activeKey: [...r].join("|"),
1715
+ touchLike: e.pointerType === "touch" || window.matchMedia("(hover: none) and (pointer: coarse)").matches
1716
+ };
1717
+ }
1718
+ renderTooltip() {
1719
+ if (!this.tooltip) return r;
1720
+ let e = this.tooltip.x / 720 * 100, t = this.tooltip.tooltipX / 720 * 100, i = this.tooltip.placement === "above" ? "translate(-50%, calc(-100% - 10px))" : "translate(-50%, 10px)";
1721
+ return n`
1722
+ <div class="tooltip-axis-pointer" style=${`left:${e}%;top:${this.tooltip.activeTop.toFixed(1)}px;height:${this.tooltip.activeHeight.toFixed(1)}px;`}></div>
1723
+ <div
1724
+ class="tooltip"
1725
+ style=${`left:clamp(150px,${t}%,calc(100% - 150px));top:${this.tooltip.y.toFixed(1)}px;transform:${i};`}
1726
+ >
1727
+ <div class="tooltip-time">${new Date(this.tooltip.time).toLocaleString()}</div>
1728
+ ${this.tooltip.values.map((e) => n`
1729
+ <div class="tooltip-row">
1730
+ <span class="tooltip-dot" style=${`background:${e.color}`}></span>
1731
+ <span class="tooltip-label">${e.label}</span>
1732
+ <span>${e.value}</span>
1733
+ </div>
1734
+ `)}
1735
+ </div>
1736
+ `;
1737
+ }
1738
+ }, Bt = "temperature";
1739
+ function Vt(e) {
1740
+ return e.join(".");
1741
+ }
1742
+ function Ht(e) {
1743
+ return e?.toLowerCase() === Bt;
1744
+ }
1745
+ function Ut(e, t) {
1746
+ if (!e || !t) return;
1747
+ let n = t[Vt(e)];
1748
+ return typeof n == "string" && n !== "" ? n : void 0;
1749
+ }
1750
+ //#endregion
1751
+ //#region src/data/resolve-config.ts
1752
+ function Wt(e) {
1753
+ return {
1754
+ id: e.id,
1755
+ kind: e.attribute ? "entity_attribute" : "entity_state",
1756
+ entityId: e.entity,
1757
+ label: e.label,
1758
+ path: e.attribute,
1759
+ valueType: e.valueType,
1760
+ unit: e.unit
1761
+ };
1762
+ }
1763
+ var Gt = 24, Kt = "2.5", qt = [
1764
+ "current_temperature",
1765
+ "temperature",
1766
+ "hvac_action"
1767
+ ], Jt = /°[CF]|[CFK]$/;
1768
+ function Yt(e) {
1769
+ return Jt.test(e);
1770
+ }
1771
+ function Xt(e) {
1772
+ return e.scaleMode === "manual" && (e.scaleMin !== void 0 || e.scaleMax !== void 0);
1773
+ }
1774
+ function Zt(e) {
1775
+ return /* @__PURE__ */ new Date(Math.floor(e.getTime() / 1e3) * 1e3);
1776
+ }
1777
+ function Qt(e) {
1778
+ if (e !== void 0) return Array.isArray(e) ? e : e.split(".");
1779
+ }
1780
+ function $t(e) {
1781
+ return e === "line" || e === "column" ? e : "stair";
1782
+ }
1783
+ function en(e) {
1784
+ return typeof e == "number" ? Number.isFinite(e) && e >= 0 ? String(e) : Kt : typeof e == "string" && e.trim() !== "" ? e.trim() : Kt;
1785
+ }
1786
+ function tn(e, t) {
1787
+ return t ? `attr:${e}:${t.join(".")}` : `state:${e}`;
1788
+ }
1789
+ function nn(e) {
1790
+ return e[e.length - 1] ?? "";
1791
+ }
1792
+ function rn(e, t, n) {
1793
+ let r = e?.states[t];
1794
+ return r ? n ? M(r, n)?.valueType ?? "string" : ue(r)?.valueType ?? "string" : "number";
1795
+ }
1796
+ function an(e, t, n, r) {
1797
+ if (r) return r;
1798
+ if (n) return nn(n);
1799
+ let i = e?.states[t]?.attributes.friendly_name;
1800
+ return typeof i == "string" && i !== "" ? i : t;
1801
+ }
1802
+ function on(e, t, n, r, i) {
1803
+ if (r !== void 0) return r || void 0;
1804
+ if (n) return Ut(n, i);
1805
+ let a = e?.states[t]?.attributes.unit_of_measurement;
1806
+ return typeof a == "string" && a !== "" ? a : void 0;
1807
+ }
1808
+ function Y(e, t, n, r) {
1809
+ return n ? `group:${n}` : r === "number" && t ? `unit:${t}` : `series:${e}`;
1810
+ }
1811
+ function sn(e, t, n, r, i, a) {
1812
+ let o = Qt(e.attribute), s = tn(e.entity, o), c = rn(n, e.entity, o), l = on(n, e.entity, o, e.unit, r);
1813
+ return {
1814
+ id: s,
1815
+ entity: e.entity,
1816
+ attribute: o,
1817
+ label: an(n, e.entity, o, e.label),
1818
+ color: e.color ?? L(t),
1819
+ unit: l,
1820
+ scaleGroupKey: Y(s, l, e.scaleGroup, c),
1821
+ scaleMode: e.scaleMode ?? "auto",
1822
+ scaleMin: e.scaleMin,
1823
+ scaleMax: e.scaleMax,
1824
+ lineMode: $t(e.lineMode ?? i),
1825
+ lineWidth: en(e.lineWidth ?? a),
1826
+ valueType: c
1827
+ };
1828
+ }
1829
+ function cn(e, t, n, r, i) {
1830
+ let a = n?.states[e];
1831
+ if (!a) {
1832
+ let n = `state:${e}`;
1833
+ return {
1834
+ id: n,
1835
+ entity: e,
1836
+ label: e,
1837
+ color: L(t),
1838
+ scaleGroupKey: `series:${n}`,
1839
+ scaleMode: "auto",
1840
+ lineMode: $t(r),
1841
+ lineWidth: en(i),
1842
+ valueType: "number"
1843
+ };
1844
+ }
1845
+ let o = ue(a);
1846
+ if (o) return {
1847
+ id: o.id,
1848
+ entity: e,
1849
+ label: o.label,
1850
+ color: L(t),
1851
+ unit: o.unit,
1852
+ scaleGroupKey: Y(o.id, o.unit, void 0, o.valueType),
1853
+ scaleMode: "auto",
1854
+ lineMode: $t(r),
1855
+ lineWidth: en(i),
1856
+ valueType: o.valueType
1857
+ };
1858
+ }
1859
+ function ln(e, t) {
1860
+ let n = t?.states[e];
1861
+ if (!n) return;
1862
+ let r = n.attributes, i = r.temperature_unit;
1863
+ if (typeof i == "string" && i !== "") return i;
1864
+ let a = r.unit_of_measurement;
1865
+ if (typeof a == "string" && a !== "") return a;
1866
+ }
1867
+ function un(e, t, n) {
1868
+ if (e.attribute || !e.entity.startsWith("climate.") || !n?.states[e.entity]) return [e];
1869
+ let r = ln(e.entity, n);
1870
+ return [e, ...qt.map((i) => {
1871
+ let a = [i], o = tn(e.entity, a), s = rn(n, e.entity, a), c = Fe[i] ?? L(t()), l = i === "current_temperature" || i === "temperature" ? r : void 0, u = i === "hvac_action" ? void 0 : "temperature";
1872
+ return {
1873
+ id: o,
1874
+ entity: e.entity,
1875
+ attribute: a,
1876
+ label: i,
1877
+ color: c,
1878
+ unit: l,
1879
+ scaleGroupKey: Y(o, l, u, s),
1880
+ scaleMode: "auto",
1881
+ lineMode: e.lineMode,
1882
+ lineWidth: e.lineWidth,
1883
+ valueType: s
1884
+ };
1885
+ })];
1886
+ }
1887
+ function dn(e) {
1888
+ return e.find((e) => e.scaleGroupKey === "group:temperature" && e.unit && Yt(e.unit))?.unit ?? e.find((e) => e.unit && Yt(e.unit))?.unit;
1889
+ }
1890
+ function fn(e) {
1891
+ let t = dn(e), n = e.some((e) => e.scaleGroupKey === "group:temperature");
1892
+ return e.map((e) => {
1893
+ let r = Ht(e.unit), i = r && t ? t : e.unit, a = r && i && e.scaleGroupKey === "unit:temperature" ? `unit:${i}` : e.scaleGroupKey;
1894
+ return n && e.valueType === "number" && i && Yt(i) && a.startsWith("unit:") && !Xt(e) && (a = "group:temperature"), i !== e.unit || a !== e.scaleGroupKey ? {
1895
+ ...e,
1896
+ unit: i,
1897
+ scaleGroupKey: a
1898
+ } : e;
1899
+ });
1900
+ }
1901
+ function pn(e) {
1902
+ let { config: t, hass: n } = e, r = e.attributeUnits ?? t?.attributeUnits, i = t?.endDate ?? e.endDate ?? /* @__PURE__ */ new Date(), a = t?.hours ?? e.hours ?? Gt, o = t?.startDate ?? e.startDate ?? /* @__PURE__ */ new Date(i.getTime() - a * 36e5), s = t?.lineMode ?? e.lineMode, c = t?.lineWidth ?? e.lineWidth, l;
1903
+ l = t?.series && t.series.length > 0 ? t.series.map((e, t) => sn(e, t, n, r, s, c)) : (t?.defaultEntities ?? e.entities ?? []).map((e, t) => cn(e, t, n, s, c)).filter((e) => e !== void 0);
1904
+ let u = l.length;
1905
+ return l = l.flatMap((e) => un(e, () => u++, n)), l = fn(l), {
1906
+ startDate: Zt(o),
1907
+ endDate: Zt(i),
1908
+ showDatePicker: t?.showDatePicker ?? e.showDatePicker ?? !1,
1909
+ showEntityPicker: t?.showEntityPicker ?? e.showEntityPicker ?? !1,
1910
+ showLegend: t?.showLegend ?? e.showLegend ?? !0,
1911
+ showTooltip: t?.showTooltip ?? e.showTooltip ?? !0,
1912
+ width: t?.width ?? e.width ?? "100%",
1913
+ height: t?.height ?? e.height,
1914
+ backgroundColor: t?.backgroundColor ?? e.backgroundColor,
1915
+ title: t?.title ?? e.title,
1916
+ titleFontFamily: t?.titleFontFamily ?? e.titleFontFamily,
1917
+ titleFontSize: t?.titleFontSize ?? e.titleFontSize,
1918
+ titleColor: t?.titleColor ?? e.titleColor,
1919
+ language: e.language ?? n?.locale?.language ?? n?.language,
1920
+ series: l,
1921
+ disableClimateOverlay: t?.disableClimateOverlay ?? !1
1922
+ };
1923
+ }
1924
+ //#endregion
1925
+ //#region src/localize/localize.ts
1926
+ var mn = {
1927
+ loading: "ui.common.loading",
1928
+ empty: "ui.components.history_charts.no_history_found",
1929
+ error: "ui.components.history_charts.error",
1930
+ add_target: "ui.components.target-picker.add_target",
1931
+ attributes: "ui.dialogs.more_info_control.attributes",
1932
+ back: "ui.common.back"
1933
+ }, hn = {
1934
+ en: {
1935
+ no_series: "No series configured",
1936
+ no_entity_selected: "No entity selected",
1937
+ error_timeout: "The request timed out. Please try again.",
1938
+ tools: "Tools",
1939
+ view_range: "View range",
1940
+ reset_zoom: "Reset zoom",
1941
+ line_mode: "Display mode",
1942
+ mode_stair: "Stair",
1943
+ mode_line: "Line",
1944
+ mode_column: "Columns",
1945
+ export_data: "Export",
1946
+ import_data: "Import",
1947
+ search_attributes: "Search attributes",
1948
+ no_matching_attributes: "No matching attributes",
1949
+ attribute_results_limited: "Showing first 50 matches"
1950
+ },
1951
+ fr: {
1952
+ no_series: "Aucune série configurée",
1953
+ no_entity_selected: "Aucune entité sélectionnée",
1954
+ error_timeout: "La requête a expiré. Veuillez réessayer.",
1955
+ tools: "Outils",
1956
+ view_range: "Plage affichée",
1957
+ reset_zoom: "Réinitialiser le zoom",
1958
+ line_mode: "Mode d'affichage",
1959
+ mode_stair: "Escalier",
1960
+ mode_line: "Ligne",
1961
+ mode_column: "Colonnes",
1962
+ export_data: "Exporter",
1963
+ import_data: "Importer",
1964
+ search_attributes: "Rechercher des attributs",
1965
+ no_matching_attributes: "Aucun attribut correspondant",
1966
+ attribute_results_limited: "50 premiers résultats affichés"
1967
+ },
1968
+ cs: {
1969
+ no_series: "Není nakonfigurována žádná série",
1970
+ no_entity_selected: "Nebyla vybrána žádná entita",
1971
+ error_timeout: "Požadavek vypršel. Zkuste to prosím znovu.",
1972
+ tools: "Nástroje",
1973
+ view_range: "Rozsah zobrazení",
1974
+ reset_zoom: "Obnovit přiblížení",
1975
+ line_mode: "Režim zobrazení",
1976
+ mode_stair: "Schody",
1977
+ mode_line: "Čára",
1978
+ mode_column: "Sloupce",
1979
+ export_data: "Exportovat",
1980
+ import_data: "Importovat",
1981
+ search_attributes: "Hledat atributy",
1982
+ no_matching_attributes: "Žádné odpovídající atributy",
1983
+ attribute_results_limited: "Zobrazuje se prvních 50 shod"
1984
+ },
1985
+ de: {
1986
+ no_series: "Keine Serie konfiguriert",
1987
+ no_entity_selected: "Keine Entität ausgewählt",
1988
+ error_timeout: "Die Anfrage ist abgelaufen. Bitte erneut versuchen.",
1989
+ tools: "Werkzeuge",
1990
+ view_range: "Anzeigebereich",
1991
+ reset_zoom: "Zoom zurücksetzen",
1992
+ line_mode: "Anzeigemodus",
1993
+ mode_stair: "Stufen",
1994
+ mode_line: "Linie",
1995
+ mode_column: "Spalten",
1996
+ export_data: "Exportieren",
1997
+ import_data: "Importieren",
1998
+ search_attributes: "Attribute suchen",
1999
+ no_matching_attributes: "Keine passenden Attribute",
2000
+ attribute_results_limited: "Die ersten 50 Treffer werden angezeigt"
2001
+ },
2002
+ el: {
2003
+ no_series: "Δεν έχει ρυθμιστεί σειρά",
2004
+ no_entity_selected: "Δεν έχει επιλεγεί οντότητα",
2005
+ error_timeout: "Το αίτημα έληξε χρονικά. Παρακαλώ δοκιμάστε ξανά.",
2006
+ tools: "Εργαλεία",
2007
+ view_range: "Εύρος προβολής",
2008
+ reset_zoom: "Επαναφορά ζουμ",
2009
+ line_mode: "Λειτουργία εμφάνισης",
2010
+ mode_stair: "Σκάλα",
2011
+ mode_line: "Γραμμή",
2012
+ mode_column: "Στήλες",
2013
+ export_data: "Εξαγωγή",
2014
+ import_data: "Εισαγωγή",
2015
+ search_attributes: "Αναζήτηση χαρακτηριστικών",
2016
+ no_matching_attributes: "Δεν βρέθηκαν χαρακτηριστικά",
2017
+ attribute_results_limited: "Εμφανίζονται οι πρώτες 50 αντιστοιχίες"
2018
+ },
2019
+ it: {
2020
+ no_series: "Nessuna serie configurata",
2021
+ no_entity_selected: "Nessuna entità selezionata",
2022
+ error_timeout: "La richiesta è scaduta. Riprova.",
2023
+ tools: "Strumenti",
2024
+ view_range: "Intervallo visualizzato",
2025
+ reset_zoom: "Reimposta zoom",
2026
+ line_mode: "Modalità di visualizzazione",
2027
+ mode_stair: "Gradini",
2028
+ mode_line: "Linea",
2029
+ mode_column: "Colonne",
2030
+ export_data: "Esporta",
2031
+ import_data: "Importa",
2032
+ search_attributes: "Cerca attributi",
2033
+ no_matching_attributes: "Nessun attributo corrispondente",
2034
+ attribute_results_limited: "Mostrate le prime 50 corrispondenze"
2035
+ },
2036
+ pl: {
2037
+ no_series: "Nie skonfigurowano serii",
2038
+ no_entity_selected: "Nie wybrano encji",
2039
+ error_timeout: "Upłynął limit czasu żądania. Spróbuj ponownie.",
2040
+ tools: "Narzędzia",
2041
+ view_range: "Zakres widoku",
2042
+ reset_zoom: "Resetuj powiększenie",
2043
+ line_mode: "Tryb wyświetlania",
2044
+ mode_stair: "Schodkowy",
2045
+ mode_line: "Linia",
2046
+ mode_column: "Kolumny",
2047
+ export_data: "Eksportuj",
2048
+ import_data: "Importuj",
2049
+ search_attributes: "Szukaj atrybutów",
2050
+ no_matching_attributes: "Brak pasujących atrybutów",
2051
+ attribute_results_limited: "Pokazano pierwsze 50 wyników"
2052
+ },
2053
+ ru: {
2054
+ no_series: "Серии не настроены",
2055
+ no_entity_selected: "Сущность не выбрана",
2056
+ error_timeout: "Время ожидания запроса истекло. Повторите попытку.",
2057
+ tools: "Инструменты",
2058
+ view_range: "Диапазон просмотра",
2059
+ reset_zoom: "Сбросить масштаб",
2060
+ line_mode: "Режим отображения",
2061
+ mode_stair: "Ступени",
2062
+ mode_line: "Линия",
2063
+ mode_column: "Столбцы",
2064
+ export_data: "Экспорт",
2065
+ import_data: "Импорт",
2066
+ search_attributes: "Поиск атрибутов",
2067
+ no_matching_attributes: "Подходящие атрибуты не найдены",
2068
+ attribute_results_limited: "Показаны первые 50 совпадений"
2069
+ },
2070
+ sk: {
2071
+ no_series: "Nie je nakonfigurovaná žiadna séria",
2072
+ no_entity_selected: "Nie je vybraná žiadna entita",
2073
+ error_timeout: "Časový limit požiadavky vypršal. Skúste to znova.",
2074
+ tools: "Nástroje",
2075
+ view_range: "Rozsah zobrazenia",
2076
+ reset_zoom: "Obnoviť priblíženie",
2077
+ line_mode: "Režim zobrazenia",
2078
+ mode_stair: "Schody",
2079
+ mode_line: "Čiara",
2080
+ mode_column: "Stĺpce",
2081
+ export_data: "Exportovať",
2082
+ import_data: "Importovať",
2083
+ search_attributes: "Hľadať atribúty",
2084
+ no_matching_attributes: "Žiadne zodpovedajúce atribúty",
2085
+ attribute_results_limited: "Zobrazuje sa prvých 50 zhôd"
2086
+ }
2087
+ };
2088
+ function X(e, t) {
2089
+ let n = mn[t];
2090
+ return n && e?.localize ? e.localize(n) : hn[e?.locale?.language?.split("-")[0] ?? e?.language?.split("-")[0] ?? "en"]?.[t] ?? hn.en?.[t] ?? t;
2091
+ }
2092
+ //#endregion
2093
+ //#region src/styles/chart.css.ts
2094
+ var gn = t`
2095
+ :host {
2096
+ display: flex;
2097
+ flex-direction: column;
2098
+ min-height: 360px;
2099
+ font-family: var(--better-history-font-family, inherit);
2100
+ user-select: none;
2101
+ -webkit-user-select: none;
2102
+ }
2103
+
2104
+ .root {
2105
+ flex: 1;
2106
+ min-height: 0;
2107
+ display: flex;
2108
+ flex-direction: column;
2109
+ user-select: none;
2110
+ -webkit-user-select: none;
2111
+ }
2112
+
2113
+ .graph-title {
2114
+ margin: 0 0 8px;
2115
+ color: var(--better-history-title-color, var(--primary-text-color, inherit));
2116
+ font-family: var(--better-history-title-font-family, inherit);
2117
+ font-size: var(--better-history-title-font-size, var(--ha-font-size-xl, 20px));
2118
+ font-weight: 500;
2119
+ line-height: 1.25;
2120
+ }
2121
+
2122
+ .chart-area {
2123
+ flex: 1;
2124
+ min-height: 0;
2125
+ display: flex;
2126
+ flex-direction: column;
2127
+ }
2128
+
2129
+ .chart-surface {
2130
+ position: relative;
2131
+ overflow-y: auto;
2132
+ flex: 1;
2133
+ min-height: 0;
2134
+ }
2135
+
2136
+ svg {
2137
+ width: 100%;
2138
+ display: block;
2139
+ touch-action: pan-y;
2140
+ }
2141
+
2142
+ .axis {
2143
+ stroke: var(--better-history-border-color, var(--divider-color, #444));
2144
+ stroke-width: 1;
2145
+ }
2146
+
2147
+ .axis.tick {
2148
+ stroke-width: 1;
2149
+ }
2150
+
2151
+ .grid-line {
2152
+ stroke: var(--better-history-border-color, var(--divider-color, #444));
2153
+ stroke-width: 1;
2154
+ opacity: 0.45;
2155
+ vector-effect: non-scaling-stroke;
2156
+ }
2157
+
2158
+ .x-axis-label {
2159
+ position: absolute;
2160
+ font-size: 11px;
2161
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2162
+ font-family: inherit;
2163
+ white-space: nowrap;
2164
+ pointer-events: none;
2165
+ transform: translateX(-50%);
2166
+ box-sizing: border-box;
2167
+ line-height: 1;
2168
+ }
2169
+
2170
+ .x-axis-label--bold {
2171
+ font-weight: 600;
2172
+ color: var(--better-history-text-color, var(--primary-text-color, #fff));
2173
+ }
2174
+
2175
+ .graph-separator {
2176
+ stroke: var(--better-history-border-color, var(--divider-color, #444));
2177
+ stroke-width: 1.2;
2178
+ stroke-dasharray: 3 5;
2179
+ opacity: 0.64;
2180
+ }
2181
+
2182
+ .line {
2183
+ fill: none;
2184
+ stroke-width: 2.5;
2185
+ stroke-linecap: round;
2186
+ stroke-linejoin: round;
2187
+ vector-effect: non-scaling-stroke;
2188
+ }
2189
+
2190
+ .column {
2191
+ opacity: 0.62;
2192
+ vector-effect: non-scaling-stroke;
2193
+ }
2194
+
2195
+ .segment {
2196
+ opacity: 0.7;
2197
+ }
2198
+
2199
+ .segment-border {
2200
+ stroke-width: 1.5;
2201
+ stroke-opacity: 0.6;
2202
+ vector-effect: non-scaling-stroke;
2203
+ }
2204
+
2205
+ .climate-heating-area {
2206
+ fill: var(--better-history-accent-color, var(--accent-color, #ff9800));
2207
+ opacity: 0.22;
2208
+ }
2209
+
2210
+ .y-axis-label {
2211
+ position: absolute;
2212
+ font-size: 11px;
2213
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2214
+ transform: translateY(-50%);
2215
+ white-space: nowrap;
2216
+ pointer-events: none;
2217
+ box-sizing: border-box;
2218
+ line-height: 1;
2219
+ z-index: 1;
2220
+ }
2221
+
2222
+ .tooltip-axis-pointer {
2223
+ position: absolute;
2224
+ top: 0;
2225
+ width: 0;
2226
+ border-left: 1px dashed var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4)));
2227
+ opacity: 0.9;
2228
+ pointer-events: none;
2229
+ transform: translateX(-0.5px);
2230
+ z-index: 2;
2231
+ }
2232
+
2233
+ .chart-graphs {
2234
+ display: flex;
2235
+ flex-direction: column;
2236
+ gap: 0;
2237
+ position: relative;
2238
+ contain: layout;
2239
+ }
2240
+
2241
+ .graph-section {
2242
+ display: flex;
2243
+ flex-direction: column;
2244
+ }
2245
+
2246
+ .graph-canvas {
2247
+ position: relative;
2248
+ overflow: hidden;
2249
+ }
2250
+
2251
+ .graph-legend {
2252
+ display: flex;
2253
+ flex-wrap: wrap;
2254
+ gap: 6px;
2255
+ margin-top: 6px;
2256
+ padding: 0 6px 8px 6px;
2257
+ font-size: 12px;
2258
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2259
+ }
2260
+
2261
+ .legend-item {
2262
+ display: inline-flex;
2263
+ align-items: center;
2264
+ gap: 5px;
2265
+ min-width: 0;
2266
+ border: 0;
2267
+ background: transparent;
2268
+ color: inherit;
2269
+ padding: 0;
2270
+ font: inherit;
2271
+ cursor: pointer;
2272
+ }
2273
+
2274
+ .legend-item[hidden-series] {
2275
+ opacity: 0.38;
2276
+ }
2277
+
2278
+ .swatch {
2279
+ width: 9px;
2280
+ height: 9px;
2281
+ border-radius: 50%;
2282
+ flex: 0 0 auto;
2283
+ box-sizing: border-box;
2284
+ }
2285
+
2286
+ .legend-label {
2287
+ overflow: hidden;
2288
+ text-overflow: ellipsis;
2289
+ white-space: nowrap;
2290
+ max-width: 160px;
2291
+ }
2292
+
2293
+ .tooltip {
2294
+ position: absolute;
2295
+ z-index: 2;
2296
+ min-width: 170px;
2297
+ width: max-content;
2298
+ max-width: min(300px, calc(100% - 16px));
2299
+ padding: 8px;
2300
+ border-radius: var(--better-history-radius, 8px);
2301
+ background: color-mix(in srgb, var(--better-history-bg, var(--card-background-color, #1e1e2e)) 88%, #000 12%);
2302
+ border: 1px solid var(--better-history-border-color, var(--divider-color, #444));
2303
+ box-shadow: 0 8px 20px rgb(0 0 0 / 28%);
2304
+ color: var(--better-history-text-color, var(--primary-text-color, #fff));
2305
+ font-size: 12px;
2306
+ pointer-events: none;
2307
+ box-sizing: border-box;
2308
+ transition: left 90ms ease-out, top 90ms ease-out;
2309
+ will-change: left, top, transform;
2310
+ }
2311
+
2312
+ .tooltip-time {
2313
+ margin-bottom: 6px;
2314
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2315
+ font-size: 11px;
2316
+ }
2317
+
2318
+ .tooltip-row {
2319
+ display: grid;
2320
+ grid-template-columns: 8px minmax(0, 1fr) auto;
2321
+ align-items: center;
2322
+ gap: 6px;
2323
+ margin-top: 3px;
2324
+ }
2325
+
2326
+ .tooltip-label {
2327
+ min-width: 0;
2328
+ overflow: hidden;
2329
+ text-overflow: ellipsis;
2330
+ white-space: nowrap;
2331
+ }
2332
+
2333
+ .tooltip-dot {
2334
+ width: 8px;
2335
+ height: 8px;
2336
+ border-radius: 50%;
2337
+ }
2338
+
2339
+ .empty,
2340
+ .error {
2341
+ display: flex;
2342
+ align-items: center;
2343
+ justify-content: center;
2344
+ height: 100%;
2345
+ padding: 20px;
2346
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2347
+ text-align: center;
2348
+ font-size: 13px;
2349
+ box-sizing: border-box;
2350
+ }
2351
+
2352
+ .error {
2353
+ color: #ff6b6b;
2354
+ }
2355
+
2356
+ .controls-bar {
2357
+ display: flex;
2358
+ flex-direction: row;
2359
+ align-items: center;
2360
+ gap: 8px;
2361
+ margin-bottom: 8px;
2362
+ width: 100%;
2363
+ }
2364
+
2365
+ .tool-icon-button,
2366
+ .mode-button,
2367
+ .tool-action-button {
2368
+ display: inline-flex;
2369
+ align-items: center;
2370
+ justify-content: center;
2371
+ border: 1px solid var(--better-history-border-color, var(--divider-color, #444));
2372
+ background: transparent;
2373
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2374
+ cursor: pointer;
2375
+ box-sizing: border-box;
2376
+ font: inherit;
2377
+ }
2378
+
2379
+ .mode-button[active] {
2380
+ color: var(--better-history-text-color, var(--primary-text-color, #fff));
2381
+ background: color-mix(in srgb, var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4))) 18%, transparent);
2382
+ border-color: var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4)));
2383
+ }
2384
+
2385
+ .tool-icon-button ha-icon,
2386
+ .mode-button ha-icon,
2387
+ .tool-action-button ha-icon {
2388
+ --mdc-icon-size: 18px;
2389
+ }
2390
+
2391
+ .tools-panel {
2392
+ display: grid;
2393
+ grid-template-columns: minmax(0, 1fr) auto;
2394
+ gap: 10px;
2395
+ align-items: end;
2396
+ margin-bottom: 8px;
2397
+ padding: 7px 8px;
2398
+ border: 1px solid var(--better-history-border-color, var(--divider-color, #444));
2399
+ border-radius: var(--better-history-radius, 8px);
2400
+ background: color-mix(in srgb, var(--better-history-bg, var(--card-background-color, #1e1e2e)) 96%, var(--primary-text-color, #fff) 4%);
2401
+ }
2402
+
2403
+ .tool-range {
2404
+ min-width: 0;
2405
+ }
2406
+
2407
+ .tool-range-head,
2408
+ .tool-actions,
2409
+ .range-values {
2410
+ display: flex;
2411
+ align-items: center;
2412
+ }
2413
+
2414
+ .tool-range-head {
2415
+ justify-content: space-between;
2416
+ gap: 8px;
2417
+ margin-bottom: 2px;
2418
+ }
2419
+
2420
+ .tool-label {
2421
+ display: inline-flex;
2422
+ align-items: center;
2423
+ gap: 6px;
2424
+ min-width: 0;
2425
+ color: var(--better-history-text-color, var(--primary-text-color, #fff));
2426
+ font-size: 11px;
2427
+ }
2428
+
2429
+ .tool-label ha-icon {
2430
+ --mdc-icon-size: 16px;
2431
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2432
+ }
2433
+
2434
+ .tool-icon-button {
2435
+ width: 24px;
2436
+ height: 24px;
2437
+ border-radius: var(--better-history-radius, 8px);
2438
+ }
2439
+
2440
+ .range-values {
2441
+ justify-content: space-between;
2442
+ gap: 8px;
2443
+ margin-bottom: 0;
2444
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2445
+ font-size: 10px;
2446
+ }
2447
+
2448
+ .range-slider-stack {
2449
+ position: relative;
2450
+ height: 24px;
2451
+ display: flex;
2452
+ align-items: center;
2453
+ padding: 0 2px;
2454
+ }
2455
+
2456
+ .range-slider-stack::before {
2457
+ content: "";
2458
+ position: absolute;
2459
+ left: 2px;
2460
+ right: 2px;
2461
+ top: 50%;
2462
+ height: 3px;
2463
+ border-radius: 999px;
2464
+ background:
2465
+ linear-gradient(
2466
+ 90deg,
2467
+ transparent,
2468
+ color-mix(in srgb, var(--better-history-border-color, var(--divider-color, #444)) 72%, transparent) 12%,
2469
+ color-mix(in srgb, var(--better-history-border-color, var(--divider-color, #444)) 72%, transparent) 88%,
2470
+ transparent
2471
+ );
2472
+ transform: translateY(-50%);
2473
+ box-shadow: inset 0 1px 1px rgb(0 0 0 / 14%);
2474
+ }
2475
+
2476
+ .range-selection {
2477
+ position: absolute;
2478
+ top: 50%;
2479
+ height: 12px;
2480
+ border-radius: 999px;
2481
+ background: color-mix(in srgb, var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4))) 16%, transparent);
2482
+ transform: translateY(-50%);
2483
+ pointer-events: none;
2484
+ box-shadow: 0 0 8px color-mix(in srgb, var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4))) 26%, transparent);
2485
+ z-index: 1;
2486
+ }
2487
+
2488
+ .range-selection,
2489
+ .range-selection-hit {
2490
+ min-width: 0;
2491
+ }
2492
+
2493
+ .range-selection::before,
2494
+ .range-selection-hit::before {
2495
+ content: "";
2496
+ position: absolute;
2497
+ left: 6px;
2498
+ right: 6px;
2499
+ top: 50%;
2500
+ height: 2px;
2501
+ border-radius: inherit;
2502
+ background: color-mix(in srgb, var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4))) 82%, var(--primary-text-color, #fff) 18%);
2503
+ transform: translateY(-50%);
2504
+ }
2505
+
2506
+ .range-selection-hit {
2507
+ position: absolute;
2508
+ top: 50%;
2509
+ width: max(36px, 8%);
2510
+ height: 22px;
2511
+ border-radius: 999px;
2512
+ background: color-mix(in srgb, var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4))) 8%, transparent);
2513
+ cursor: grab;
2514
+ pointer-events: auto;
2515
+ touch-action: none;
2516
+ transform: translate(-50%, -50%);
2517
+ z-index: 1;
2518
+ }
2519
+
2520
+ .range-selection-hit::before {
2521
+ left: 12px;
2522
+ right: 12px;
2523
+ opacity: 0.42;
2524
+ }
2525
+
2526
+ .range-selection-hit[dragging] {
2527
+ cursor: grabbing;
2528
+ }
2529
+
2530
+ .range-slider {
2531
+ position: absolute;
2532
+ inset: 0;
2533
+ width: 100%;
2534
+ margin: 0;
2535
+ appearance: none;
2536
+ background: transparent;
2537
+ cursor: ew-resize;
2538
+ pointer-events: none;
2539
+ z-index: 2;
2540
+ }
2541
+
2542
+ .range-slider::-webkit-slider-runnable-track {
2543
+ height: 24px;
2544
+ background: transparent;
2545
+ }
2546
+
2547
+ .range-slider::-moz-range-track {
2548
+ height: 24px;
2549
+ background: transparent;
2550
+ }
2551
+
2552
+ .range-slider::-webkit-slider-thumb {
2553
+ appearance: none;
2554
+ width: 12px;
2555
+ height: 18px;
2556
+ margin-top: 3px;
2557
+ border: 0;
2558
+ border-radius: 999px;
2559
+ background: color-mix(in srgb, var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4))) 86%, var(--primary-text-color, #fff) 14%);
2560
+ box-shadow:
2561
+ 0 0 0 2px color-mix(in srgb, var(--better-history-bg, var(--card-background-color, #1e1e2e)) 88%, transparent),
2562
+ 0 1px 4px rgb(0 0 0 / 18%);
2563
+ pointer-events: auto;
2564
+ }
2565
+
2566
+ .range-slider::-moz-range-thumb {
2567
+ width: 12px;
2568
+ height: 18px;
2569
+ border: 0;
2570
+ border-radius: 999px;
2571
+ background: color-mix(in srgb, var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4))) 86%, var(--primary-text-color, #fff) 14%);
2572
+ box-shadow:
2573
+ 0 0 0 2px color-mix(in srgb, var(--better-history-bg, var(--card-background-color, #1e1e2e)) 88%, transparent),
2574
+ 0 1px 4px rgb(0 0 0 / 18%);
2575
+ pointer-events: auto;
2576
+ }
2577
+
2578
+ .range-slider:focus-visible::-webkit-slider-thumb {
2579
+ outline: 1px solid var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4)));
2580
+ outline-offset: 3px;
2581
+ }
2582
+
2583
+ .range-slider:focus-visible::-moz-range-thumb {
2584
+ outline: 1px solid var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4)));
2585
+ outline-offset: 3px;
2586
+ }
2587
+
2588
+ .tool-actions {
2589
+ justify-content: flex-end;
2590
+ gap: 6px;
2591
+ }
2592
+
2593
+ .mode-switch {
2594
+ display: inline-flex;
2595
+ overflow: hidden;
2596
+ border-radius: var(--better-history-radius, 8px);
2597
+ border: 1px solid var(--better-history-border-color, var(--divider-color, #444));
2598
+ }
2599
+
2600
+ .mode-button {
2601
+ width: 30px;
2602
+ height: 30px;
2603
+ border: 0;
2604
+ border-right: 1px solid var(--better-history-border-color, var(--divider-color, #444));
2605
+ }
2606
+
2607
+ .mode-button:last-child {
2608
+ border-right: 0;
2609
+ }
2610
+
2611
+ .tool-action-button {
2612
+ gap: 5px;
2613
+ height: 32px;
2614
+ padding: 0 8px;
2615
+ border-radius: var(--better-history-radius, 8px);
2616
+ color: var(--better-history-text-color, var(--primary-text-color, #fff));
2617
+ font-size: 12px;
2618
+ }
2619
+
2620
+ @media (hover: none) and (pointer: coarse) {
2621
+ .controls-bar {
2622
+ flex-direction: column;
2623
+ align-items: stretch;
2624
+ }
2625
+
2626
+ .tools-panel {
2627
+ grid-template-columns: 1fr;
2628
+ padding: 8px;
2629
+ }
2630
+
2631
+ .tool-actions {
2632
+ justify-content: space-between;
2633
+ }
2634
+
2635
+ .range-slider-stack,
2636
+ .range-slider::-webkit-slider-runnable-track,
2637
+ .range-slider::-moz-range-track {
2638
+ height: 30px;
2639
+ }
2640
+
2641
+ .range-selection {
2642
+ height: 18px;
2643
+ }
2644
+
2645
+ .range-selection-hit {
2646
+ width: max(44px, 12%);
2647
+ height: 30px;
2648
+ }
2649
+
2650
+ .range-slider::-webkit-slider-thumb {
2651
+ width: 14px;
2652
+ height: 22px;
2653
+ margin-top: 4px;
2654
+ }
2655
+
2656
+ .range-slider::-moz-range-thumb {
2657
+ width: 14px;
2658
+ height: 22px;
2659
+ }
2660
+
2661
+ .date-picker-wrapper {
2662
+ width: 100%;
2663
+ max-width: 100%;
2664
+ }
2665
+
2666
+ .date-picker-wrapper ha-date-range-picker {
2667
+ width: 100%;
2668
+ }
2669
+ }
2670
+
2671
+ .date-picker-wrapper {
2672
+ flex: 0 0 auto;
2673
+ width: fit-content;
2674
+ max-width: 100%;
2675
+ min-width: 0;
2676
+ overflow: visible;
2677
+ }
2678
+
2679
+ .date-picker-wrapper ha-date-range-picker {
2680
+ display: block;
2681
+ }
2682
+
2683
+ .entity-picker {
2684
+ position: relative;
2685
+ flex: 1 1 0;
2686
+ min-width: 0;
2687
+ width: 100%;
2688
+ display: flex;
2689
+ flex-direction: column;
2690
+ align-items: flex-start;
2691
+ gap: 4px;
2692
+ }
2693
+
2694
+ .entity-trigger {
2695
+ width: 100%;
2696
+ }
2697
+
2698
+ .history-loading-indicator {
2699
+ position: absolute;
2700
+ top: 16px;
2701
+ right: 8px;
2702
+ display: inline-flex;
2703
+ align-items: center;
2704
+ gap: 6px;
2705
+ height: 24px;
2706
+ padding: 0 8px;
2707
+ border: 1px solid var(--better-history-border-color, var(--divider-color, #444));
2708
+ border-radius: 999px;
2709
+ background: color-mix(in srgb, var(--better-history-bg, var(--card-background-color, #1e1e2e)) 88%, var(--primary-color, #03a9f4) 12%);
2710
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2711
+ box-shadow: 0 2px 8px rgb(0 0 0 / 14%);
2712
+ font-size: 11px;
2713
+ line-height: 1;
2714
+ pointer-events: none;
2715
+ transform: translateY(-50%);
2716
+ z-index: 1;
2717
+ }
2718
+
2719
+ .history-loading-spinner {
2720
+ width: 12px;
2721
+ height: 12px;
2722
+ border-radius: 50%;
2723
+ border: 2px solid color-mix(in srgb, var(--better-history-muted-color, var(--secondary-text-color, #888)) 28%, transparent);
2724
+ border-top-color: var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4)));
2725
+ box-sizing: border-box;
2726
+ animation: better-history-spin 850ms linear infinite;
2727
+ }
2728
+
2729
+ @keyframes better-history-spin {
2730
+ to {
2731
+ transform: rotate(360deg);
2732
+ }
2733
+ }
2734
+
2735
+ .entity-row {
2736
+ display: flex;
2737
+ flex-wrap: wrap;
2738
+ gap: 6px;
2739
+ align-items: center;
2740
+ width: 100%;
2741
+ }
2742
+
2743
+ .entity-menu {
2744
+ position: fixed;
2745
+ top: -9999px;
2746
+ left: -9999px;
2747
+ display: none;
2748
+ width: min(420px, calc(100vw - 48px));
2749
+ max-height: 420px;
2750
+ padding: 12px;
2751
+ overflow: hidden;
2752
+ border: var(--wa-panel-border-width, 1px) var(--wa-panel-border-style, solid) var(--wa-color-surface-border, var(--divider-color, rgba(0, 0, 0, 0.12)));
2753
+ border-radius: var(--wa-panel-border-radius, var(--ha-dialog-border-radius, var(--ha-border-radius-3xl, 24px)));
2754
+ background: var(--wa-color-surface-raised, var(--card-background-color, #fff));
2755
+ box-shadow: var(--wa-shadow-m, var(--ha-box-shadow-m, 0 4px 8px rgba(0, 0, 0, 0.08)));
2756
+ box-sizing: border-box;
2757
+ z-index: 100;
2758
+ }
2759
+
2760
+ .entity-menu[open] {
2761
+ display: grid;
2762
+ grid-template-rows: auto minmax(0, 1fr);
2763
+ gap: 8px;
2764
+ }
2765
+
2766
+ .entity-menu-top {
2767
+ display: flex;
2768
+ justify-content: space-between;
2769
+ align-items: center;
2770
+ }
2771
+
2772
+ .entity-menu-title {
2773
+ font-size: var(--wa-font-size-m, var(--ha-font-size-m, 14px));
2774
+ font-weight: var(--wa-font-weight-body, var(--ha-font-weight-normal, 400));
2775
+ color: var(--wa-color-text-normal, var(--primary-text-color));
2776
+ overflow: hidden;
2777
+ text-overflow: ellipsis;
2778
+ white-space: nowrap;
2779
+ margin-right: 8px;
2780
+ }
2781
+
2782
+ .entity-menu-close {
2783
+ display: inline-flex;
2784
+ align-items: center;
2785
+ justify-content: center;
2786
+ width: 30px;
2787
+ height: 30px;
2788
+ border: 0;
2789
+ background: transparent;
2790
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2791
+ cursor: pointer;
2792
+ font-size: 14px;
2793
+ flex-shrink: 0;
2794
+ }
2795
+
2796
+ .entity-list {
2797
+ display: flex;
2798
+ flex-wrap: wrap;
2799
+ gap: 6px;
2800
+ min-width: 0;
2801
+ }
2802
+
2803
+ .source-chip {
2804
+ display: inline-flex;
2805
+ align-items: center;
2806
+ gap: 4px;
2807
+ height: 32px;
2808
+ padding: 0 8px 0 3px;
2809
+ border-radius: 20px;
2810
+ border: var(--wa-border-width-md, 1.5px) solid;
2811
+ background: var(--card-background-color, var(--better-history-bg, #1e1e2e));
2812
+ box-sizing: border-box;
2813
+ max-width: 100%;
2814
+ overflow: hidden;
2815
+ flex-shrink: 0;
2816
+ user-select: none;
2817
+ -webkit-user-select: none;
2818
+ }
2819
+
2820
+ .source-chip[draggable="true"] {
2821
+ cursor: grab;
2822
+ }
2823
+
2824
+ .source-chip[draggable="true"]:active {
2825
+ cursor: grabbing;
2826
+ }
2827
+
2828
+ .source-chip[dragging] {
2829
+ opacity: 0.48;
2830
+ outline: 2px solid var(--better-history-info-color, var(--info-color, var(--primary-color, #03a9f4)));
2831
+ outline-offset: 2px;
2832
+ }
2833
+
2834
+ .source-chip.entity-source-chip {
2835
+ border-color: var(--ha-color-green-80, #81c784);
2836
+ }
2837
+
2838
+ .source-chip.attr-source-chip {
2839
+ border-color: var(--ha-color-amber-80, #ffb74d);
2840
+ }
2841
+
2842
+ .source-chip-icon {
2843
+ display: flex;
2844
+ align-items: center;
2845
+ justify-content: center;
2846
+ width: 24px;
2847
+ height: 24px;
2848
+ border-radius: 50%;
2849
+ flex-shrink: 0;
2850
+ }
2851
+
2852
+ .entity-source-chip .source-chip-icon {
2853
+ background: color-mix(in srgb, var(--ha-color-green-80, #81c784) 20%, transparent);
2854
+ }
2855
+
2856
+ .attr-source-chip .source-chip-icon {
2857
+ background: color-mix(in srgb, var(--ha-color-amber-80, #ffb74d) 20%, transparent);
2858
+ }
2859
+
2860
+ .source-chip-icon ha-icon {
2861
+ --mdc-icon-size: 16px;
2862
+ }
2863
+
2864
+ .source-chip-label {
2865
+ overflow: hidden;
2866
+ text-overflow: ellipsis;
2867
+ white-space: nowrap;
2868
+ font-size: var(--wa-font-size-s, 13px);
2869
+ color: var(--primary-text-color, var(--better-history-text-color, #fff));
2870
+ }
2871
+
2872
+ .source-chip-remove {
2873
+ display: inline-flex;
2874
+ align-items: center;
2875
+ justify-content: center;
2876
+ width: 18px;
2877
+ height: 18px;
2878
+ border: 0;
2879
+ border-radius: 50%;
2880
+ background: transparent;
2881
+ color: var(--secondary-text-color, #888);
2882
+ cursor: pointer;
2883
+ flex-shrink: 0;
2884
+ padding: 0;
2885
+ font-size: 12px;
2886
+ line-height: 1;
2887
+ }
2888
+
2889
+ .source-chip-remove:hover {
2890
+ background: color-mix(in srgb, var(--secondary-text-color, #888) 15%, transparent);
2891
+ color: var(--primary-text-color, #fff);
2892
+ }
2893
+
2894
+ .entity-browser {
2895
+ min-height: 0;
2896
+ display: flex;
2897
+ flex-direction: column;
2898
+ overflow: hidden;
2899
+ }
2900
+
2901
+ .entity-browser-title {
2902
+ font-size: var(--wa-font-size-m, var(--ha-font-size-m, 14px));
2903
+ font-weight: var(--wa-font-weight-body, var(--ha-font-weight-normal, 400));
2904
+ color: var(--wa-color-text-normal, var(--primary-text-color));
2905
+ margin-bottom: 2px;
2906
+ }
2907
+
2908
+ .entity-breadcrumb {
2909
+ display: flex;
2910
+ align-items: center;
2911
+ gap: 5px;
2912
+ min-width: 0;
2913
+ margin-bottom: 6px;
2914
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
2915
+ font-size: 12px;
2916
+ }
2917
+
2918
+ .entity-crumb {
2919
+ border: 0;
2920
+ background: transparent;
2921
+ color: inherit;
2922
+ padding: 0;
2923
+ font: inherit;
2924
+ cursor: pointer;
2925
+ }
2926
+
2927
+ .entity-breadcrumb-sep {
2928
+ opacity: 0.5;
2929
+ }
2930
+
2931
+ .entity-browser-list {
2932
+ overflow-y: auto;
2933
+ flex: 1;
2934
+ min-height: 0;
2935
+ }
2936
+
2937
+ .entity-browser-entries {
2938
+ display: flex;
2939
+ flex-direction: column;
2940
+ gap: 2px;
2941
+ }
2942
+
2943
+ .entity-browser-entry {
2944
+ display: flex;
2945
+ align-items: center;
2946
+ justify-content: space-between;
2947
+ gap: 10px;
2948
+ padding: 6px 8px;
2949
+ border-radius: var(--better-history-radius, 8px);
2950
+ cursor: pointer;
2951
+ font-size: 13px;
2952
+ color: var(--better-history-text-color, var(--primary-text-color, #fff));
2953
+ }
2954
+
2955
+ .entity-browser-entry:hover {
2956
+ background: rgba(var(--rgb-primary-text-color, 255, 255, 255), 0.08);
2957
+ }
2958
+
2959
+ .entity-browser-entry--disabled {
2960
+ cursor: default;
2961
+ opacity: 0.45;
2962
+ }
2963
+
2964
+ .entity-browser-entry--disabled:hover {
2965
+ background: transparent;
2966
+ }
2967
+
2968
+ .entity-browser-entry--present {
2969
+ cursor: default;
2970
+ color: var(--ha-color-amber-80, #ffb74d);
2971
+ background: color-mix(in srgb, var(--ha-color-amber-80, #ffb74d) 14%, transparent);
2972
+ box-shadow: inset 3px 0 0 var(--ha-color-amber-80, #ffb74d);
2973
+ }
2974
+
2975
+ .entity-browser-entry--present:hover {
2976
+ background: color-mix(in srgb, var(--ha-color-amber-80, #ffb74d) 14%, transparent);
2977
+ }
2978
+
2979
+ .entity-browser-entry--removable {
2980
+ cursor: pointer;
2981
+ }
2982
+
2983
+ .entity-browser-entry--removable:hover {
2984
+ background: color-mix(in srgb, var(--ha-color-amber-80, #ffb74d) 22%, transparent);
2985
+ }
2986
+
2987
+ .entity-browser-entry--present .entity-browser-entry-type,
2988
+ .entity-browser-entry--present .entity-browser-entry-arrow {
2989
+ color: inherit;
2990
+ opacity: 0.78;
2991
+ }
2992
+
2993
+ .entity-browser-entry-text {
2994
+ display: flex;
2995
+ flex-direction: column;
2996
+ min-width: 0;
2997
+ gap: 1px;
2998
+ }
2999
+
3000
+ .entity-browser-entry-label {
3001
+ overflow: hidden;
3002
+ text-overflow: ellipsis;
3003
+ white-space: nowrap;
3004
+ }
3005
+
3006
+ .entity-browser-entry-secondary {
3007
+ overflow: hidden;
3008
+ text-overflow: ellipsis;
3009
+ white-space: nowrap;
3010
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
3011
+ font-size: 11px;
3012
+ line-height: 1.25;
3013
+ }
3014
+
3015
+ .entity-browser-entry-type,
3016
+ .entity-browser-entry-arrow {
3017
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
3018
+ font-size: 11px;
3019
+ flex-shrink: 0;
3020
+ }
3021
+
3022
+ .entity-browser-search {
3023
+ width: 100%;
3024
+ margin: 4px 0 6px;
3025
+ }
3026
+
3027
+ .entity-browser-search-input {
3028
+ width: 100%;
3029
+ height: 32px;
3030
+ padding: 0 10px;
3031
+ border: 1px solid var(--better-history-border-color, var(--divider-color, #444));
3032
+ border-radius: var(--better-history-radius, 8px);
3033
+ background: color-mix(in srgb, var(--wa-color-surface-raised, var(--card-background-color, #fff)) 92%, var(--primary-text-color, #000) 8%);
3034
+ box-sizing: border-box;
3035
+ color: var(--better-history-text-color, var(--primary-text-color, #fff));
3036
+ font: inherit;
3037
+ font-size: 13px;
3038
+ outline: none;
3039
+ }
3040
+
3041
+ .entity-browser-search-input::placeholder {
3042
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
3043
+ }
3044
+
3045
+ .entity-browser-search-input:focus {
3046
+ border-color: var(--better-history-accent-color, var(--primary-color, #03a9f4));
3047
+ box-shadow: 0 0 0 1px var(--better-history-accent-color, var(--primary-color, #03a9f4));
3048
+ }
3049
+
3050
+ .entity-browser-search-results {
3051
+ display: flex;
3052
+ flex-direction: column;
3053
+ gap: 2px;
3054
+ }
3055
+
3056
+ .entity-browser-search-empty,
3057
+ .entity-browser-search-count {
3058
+ padding: 8px;
3059
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
3060
+ font-size: 12px;
3061
+ }
3062
+
3063
+ .entity-browser-back {
3064
+ padding: 6px 8px;
3065
+ cursor: pointer;
3066
+ font-size: 12px;
3067
+ color: var(--better-history-accent-color, var(--primary-color, #03a9f4));
3068
+ border-radius: var(--ha-card-border-radius, 12px);
3069
+ }
3070
+
3071
+ .entity-browser-back:hover {
3072
+ background: rgba(var(--rgb-primary-text-color, 255, 255, 255), 0.08);
3073
+ }
3074
+
3075
+ .entity-browser-entity {
3076
+ display: flex;
3077
+ align-items: center;
3078
+ justify-content: space-between;
3079
+ gap: 8px;
3080
+ padding: 8px 10px;
3081
+ border-radius: var(--better-history-radius, 8px);
3082
+ border: 1px solid var(--better-history-border-color, var(--divider-color, #444));
3083
+ color: var(--better-history-text-color, var(--primary-text-color, #fff));
3084
+ cursor: pointer;
3085
+ margin-bottom: 6px;
3086
+ }
3087
+
3088
+ .entity-browser-entity:hover {
3089
+ background: color-mix(in srgb, var(--better-history-accent-color, var(--primary-color, #03a9f4)) 10%, transparent);
3090
+ }
3091
+
3092
+ .entity-browser-entity--disabled {
3093
+ cursor: default;
3094
+ opacity: 0.45;
3095
+ border-style: dashed;
3096
+ }
3097
+
3098
+ .entity-browser-entity--disabled:hover {
3099
+ background: transparent;
3100
+ }
3101
+
3102
+ .entity-browser-entity--present {
3103
+ cursor: default;
3104
+ color: var(--ha-color-green-80, #81c784);
3105
+ border-color: color-mix(in srgb, var(--ha-color-green-80, #81c784) 62%, var(--better-history-border-color, var(--divider-color, #444)));
3106
+ background: color-mix(in srgb, var(--ha-color-green-80, #81c784) 14%, transparent);
3107
+ box-shadow: inset 3px 0 0 var(--ha-color-green-80, #81c784);
3108
+ }
3109
+
3110
+ .entity-browser-entity--present:hover {
3111
+ background: color-mix(in srgb, var(--ha-color-green-80, #81c784) 14%, transparent);
3112
+ }
3113
+
3114
+ .entity-browser-entity--removable {
3115
+ cursor: pointer;
3116
+ }
3117
+
3118
+ .entity-browser-entity--removable:hover {
3119
+ background: color-mix(in srgb, var(--ha-color-green-80, #81c784) 22%, transparent);
3120
+ }
3121
+
3122
+ .entity-browser-entity-id {
3123
+ font-size: 13px;
3124
+ font-weight: 500;
3125
+ color: var(--better-history-text-color, var(--primary-text-color, #fff));
3126
+ overflow: hidden;
3127
+ text-overflow: ellipsis;
3128
+ white-space: nowrap;
3129
+ }
3130
+
3131
+ .entity-browser-section-title {
3132
+ font-size: 10px;
3133
+ font-weight: 600;
3134
+ letter-spacing: 0.06em;
3135
+ text-transform: uppercase;
3136
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
3137
+ padding: 2px 8px 4px;
3138
+ margin-top: 2px;
3139
+ }
3140
+
3141
+ .entity-browser-empty {
3142
+ padding: 12px;
3143
+ color: var(--better-history-muted-color, var(--secondary-text-color, #888));
3144
+ font-size: 13px;
3145
+ text-align: center;
3146
+ }
3147
+ `, _n = [
3148
+ "ha-icon",
3149
+ "ha-icon-button",
3150
+ "ha-svg-icon",
3151
+ "ha-entity-picker",
3152
+ "ha-md-list",
3153
+ "ha-md-list-item",
3154
+ "ha-input-chip",
3155
+ "ha-assist-chip",
3156
+ "ha-generic-picker"
3157
+ ], vn;
3158
+ function yn() {
3159
+ return vn ??= s(_n), vn;
3160
+ }
3161
+ var bn;
3162
+ function xn() {
3163
+ return bn ??= Sn(), bn;
3164
+ }
3165
+ async function Sn() {
3166
+ if (!customElements.get("ha-date-range-picker")) try {
3167
+ await Promise.race([customElements.whenDefined("partial-panel-resolver"), new Promise((e, t) => setTimeout(() => t(/* @__PURE__ */ Error("timeout")), 1e4))]);
3168
+ let e = document.createElement("partial-panel-resolver");
3169
+ e.hass = { panels: [{
3170
+ url_path: "history",
3171
+ component_name: "history"
3172
+ }] }, e._updateRoutes(), await e.routerOptions?.routes?.history?.load(), await customElements.whenDefined("ha-date-range-picker");
3173
+ } catch (e) {
3174
+ console.warn("[ha-better-history] Failed to load ha-date-range-picker:", e);
3175
+ }
3176
+ }
3177
+ //#endregion
3178
+ //#region src/ui/date-picker.ts
3179
+ function Cn() {
3180
+ return customElements.get("ha-date-range-picker") !== void 0;
3181
+ }
3182
+ async function wn() {
3183
+ await xn();
3184
+ }
3185
+ function Tn(e, t, r, i) {
3186
+ return n`
3187
+ <div class="date-picker-wrapper">
3188
+ <ha-date-range-picker
3189
+ .hass=${e}
3190
+ .startDate=${t}
3191
+ .endDate=${r}
3192
+ time-picker
3193
+ extended-presets
3194
+ @value-changed=${(e) => {
3195
+ let t = e.detail, n = t.value?.startDate ?? t.startDate, r = t.value?.endDate ?? t.endDate;
3196
+ n instanceof Date && r instanceof Date && i(n, r);
3197
+ }}
3198
+ ></ha-date-range-picker>
3199
+ </div>
3200
+ `;
3201
+ }
3202
+ //#endregion
3203
+ //#region src/ui/entity-picker.ts
3204
+ var En = 8, Dn = 50;
3205
+ function Z(e) {
3206
+ return typeof e == "object" && !!e && !Array.isArray(e);
3207
+ }
3208
+ function On(e) {
3209
+ return typeof e.attributes.friendly_name == "string" ? e.attributes.friendly_name : e.entity_id;
3210
+ }
3211
+ var kn = !1;
3212
+ async function An() {
3213
+ kn || (kn = !0, await yn());
3214
+ }
3215
+ function jn() {
3216
+ return customElements.get("ha-generic-picker") !== void 0;
3217
+ }
3218
+ function Mn(e) {
3219
+ let t = e.selectedEntityId && e.hass ? e.hass.states[e.selectedEntityId] : void 0;
3220
+ return n`
3221
+ <div class="entity-picker"
3222
+ @picker-opened=${e.onEntityPickerOpened}
3223
+ @picker-closed=${e.onEntityPickerClosed}
3224
+ >
3225
+ <div class="entity-menu" ?open=${e.menuOpen} @click=${(e) => e.stopPropagation()}>
3226
+ <div class="entity-menu-top">
3227
+ <span class="entity-menu-title">${t ? On(t) : ""}</span>
3228
+ <button class="entity-menu-close" @click=${e.onCloseMenu}>&#x2715;</button>
3229
+ </div>
3230
+ ${In(e)}
3231
+ </div>
3232
+ <ha-generic-picker
3233
+ class="entity-trigger"
3234
+ .hass=${e.hass}
3235
+ .addButtonLabel=${X(e.hass, "add_target")}
3236
+ .value=${""}
3237
+ .getItems=${e.getItems}
3238
+ .getAdditionalItems=${e.getAdditionalItems}
3239
+ @value-changed=${(t) => {
3240
+ let n = t.detail.value;
3241
+ n && e.onEntitySelected(n);
3242
+ }}
3243
+ ></ha-generic-picker>
3244
+ ${e.loading ? n`
3245
+ <div class="history-loading-indicator" role="status" aria-label=${X(e.hass, "loading")}>
3246
+ <span class="history-loading-spinner"></span>
3247
+ <span class="history-loading-text">${X(e.hass, "loading")}</span>
3248
+ </div>
3249
+ ` : r}
3250
+ ${e.selectedSources.length > 0 ? n`
3251
+ <div
3252
+ class="entity-row"
3253
+ @dragover=${(t) => e.onSourceDragOver(void 0, t)}
3254
+ @drop=${(t) => e.onSourceDrop(void 0, t)}
3255
+ >
3256
+ ${e.selectedSources.map((t) => Pn(t, e))}
3257
+ </div>
3258
+ ` : r}
3259
+ </div>
3260
+ `;
3261
+ }
3262
+ function Nn(e) {
3263
+ let t = e.attributes.icon;
3264
+ return typeof t == "string" && t ? t : {
3265
+ climate: "mdi:thermostat",
3266
+ sensor: "mdi:eye",
3267
+ binary_sensor: "mdi:radiobox-marked",
3268
+ light: "mdi:lightbulb",
3269
+ switch: "mdi:toggle-switch",
3270
+ input_boolean: "mdi:toggle-switch",
3271
+ fan: "mdi:fan",
3272
+ cover: "mdi:window-shutter",
3273
+ lock: "mdi:lock",
3274
+ media_player: "mdi:cast",
3275
+ vacuum: "mdi:robot-vacuum",
3276
+ camera: "mdi:camera",
3277
+ weather: "mdi:weather-partly-cloudy",
3278
+ device_tracker: "mdi:map-marker",
3279
+ person: "mdi:account",
3280
+ sun: "mdi:white-balance-sunny",
3281
+ alarm_control_panel: "mdi:shield",
3282
+ automation: "mdi:robot",
3283
+ script: "mdi:script-text",
3284
+ scene: "mdi:palette",
3285
+ timer: "mdi:timer"
3286
+ }[e.entity_id.split(".")[0]] ?? "mdi:bookmark";
3287
+ }
3288
+ function Pn(e, t) {
3289
+ let i = t.resolved?.series.some((t) => t.id === e.id) ?? !1, a = e.kind === "entity_state", o = t.hass?.states[e.entityId], s = a ? "entity-source-chip" : "attr-source-chip", c = t.draggingSourceId === e.id;
3290
+ return n`
3291
+ <div
3292
+ class="source-chip ${s}"
3293
+ draggable=${!i}
3294
+ ?dragging=${c}
3295
+ @dragstart=${(n) => t.onSourceDragStart(e.id, n)}
3296
+ @dragend=${() => t.onSourceDragEnd()}
3297
+ @dragover=${(n) => {
3298
+ i || t.onSourceDragOver(e.id, n);
3299
+ }}
3300
+ @drop=${(n) => t.onSourceDrop(e.id, n)}
3301
+ >
3302
+ <span class="source-chip-icon">
3303
+ ${a && o ? n`<ha-icon .icon=${Nn(o)}></ha-icon>` : n`<ha-icon .icon=${Fn(e.valueType)}></ha-icon>`}
3304
+ </span>
3305
+ <span class="source-chip-label">${e.label}</span>
3306
+ ${i ? r : n`<button
3307
+ class="source-chip-remove"
3308
+ @click=${(n) => {
3309
+ n.preventDefault(), t.onSourceRemoved(e.id);
3310
+ }}
3311
+ >&#x2715;</button>`}
3312
+ </div>
3313
+ `;
3314
+ }
3315
+ function Fn(e) {
3316
+ switch (e) {
3317
+ case "number": return "mdi:chart-line";
3318
+ case "string": return "mdi:text";
3319
+ case "boolean": return "mdi:toggle-switch";
3320
+ default: return "mdi:code-tags";
3321
+ }
3322
+ }
3323
+ function In(e) {
3324
+ let t = e.selectedEntityId && e.hass ? e.hass.states[e.selectedEntityId] : void 0, r = e.path, i = t ? (() => {
3325
+ if (r.length === 0) return t.attributes;
3326
+ let e = t.attributes;
3327
+ for (let t of r) {
3328
+ if (!Z(e)) return;
3329
+ e = e[t];
3330
+ }
3331
+ return e;
3332
+ })() : void 0;
3333
+ return n`
3334
+ <div class="entity-browser">
3335
+ ${Ln(t, e)}
3336
+ <div class="entity-browser-list">
3337
+ ${t ? Rn(t, r, Z(i) ? i : {}, e) : n`<div class="entity-browser-empty">${X(e.hass, "no_entity_selected")}</div>`}
3338
+ </div>
3339
+ </div>
3340
+ `;
3341
+ }
3342
+ function Ln(e, t) {
3343
+ return !e || t.path.length === 0 ? n`` : n`
3344
+ <div class="entity-breadcrumb">
3345
+ ${t.path.map((e, i) => n`
3346
+ ${i > 0 ? n`<span class="entity-breadcrumb-sep">/</span>` : r}
3347
+ <button class="entity-crumb" @click=${() => t.onBreadcrumbClick(t.path.slice(0, i + 1))}>${e}</button>
3348
+ `)}
3349
+ </div>
3350
+ `;
3351
+ }
3352
+ function Rn(e, t, i, a) {
3353
+ let o = Object.entries(i).sort(([e], [t]) => e.localeCompare(t)), s = o.some(([n, r]) => Z(r) ? !0 : j(r) !== void 0 && !!M(e, [...t, n]));
3354
+ return n`
3355
+ <div class="entity-browser-entries">
3356
+ ${t.length > 0 ? n`
3357
+ <div class="entity-browser-back" @click=${() => a.onBreadcrumbClick(t.slice(0, -1))}>
3358
+ &#x2190; ${X(a.hass, "back")}
3359
+ </div>
3360
+ ` : n`
3361
+ ${Wn(e, a)}
3362
+ ${s ? n`
3363
+ <div class="entity-browser-section-title">${X(a.hass, "attributes")}</div>
3364
+ ${zn(a)}
3365
+ ` : r}
3366
+ `}
3367
+ ${t.length === 0 && a.attributeSearch.trim() ? qn(e, a) : o.map(([n, r]) => Gn(e, n, r, t, a))}
3368
+ </div>
3369
+ `;
3370
+ }
3371
+ function zn(e) {
3372
+ let t = X(e.hass, "search_attributes");
3373
+ return n`
3374
+ <div class="entity-browser-search">
3375
+ <input
3376
+ class="entity-browser-search-input"
3377
+ type="search"
3378
+ .value=${e.attributeSearch}
3379
+ placeholder=${t}
3380
+ aria-label=${t}
3381
+ @input=${(t) => e.onAttributeSearchChanged(t.target.value)}
3382
+ @click=${(e) => e.stopPropagation()}
3383
+ @keydown=${(e) => e.stopPropagation()}
3384
+ />
3385
+ </div>
3386
+ `;
3387
+ }
3388
+ function Bn(e, t) {
3389
+ return t.selectedSources.some((t) => t.id === e);
3390
+ }
3391
+ function Vn(e, t) {
3392
+ return (t.resolved?.series ?? []).some((t) => t.id === e);
3393
+ }
3394
+ function Hn(e, t) {
3395
+ let n = t.selectedSources.some((t) => t.entityId === e), r = (t.resolved?.series ?? []).some((t) => t.entity === e);
3396
+ return n || r;
3397
+ }
3398
+ function Un(e, t) {
3399
+ if (!e.entity_id.startsWith("climate.")) return !1;
3400
+ let n = t.selectedSources.some((t) => t.entityId.startsWith("climate.") && t.entityId !== e.entity_id), r = (t.resolved?.series ?? []).some((t) => t.entity.startsWith("climate.") && t.entity !== e.entity_id);
3401
+ return n || r;
3402
+ }
3403
+ function Wn(e, t) {
3404
+ let i = ue(e);
3405
+ return i ? Un(e, t) ? n`
3406
+ <div class="entity-browser-entity entity-browser-entity--disabled">
3407
+ <span class="entity-browser-entry-label">${e.entity_id}</span>
3408
+ </div>
3409
+ ` : Bn(i.id, t) ? n`
3410
+ <div class="entity-browser-entity entity-browser-entity--present entity-browser-entity--removable" @click=${() => t.onSourceRemoved(i.id)}>
3411
+ <span class="entity-browser-entry-label">${e.entity_id}</span>
3412
+ </div>
3413
+ ` : Vn(i.id, t) ? n`
3414
+ <div class="entity-browser-entity entity-browser-entity--present entity-browser-entity--forced">
3415
+ <span class="entity-browser-entry-label">${e.entity_id}</span>
3416
+ </div>
3417
+ ` : Hn(e.entity_id, t) ? n`
3418
+ <div class="entity-browser-entity entity-browser-entity--disabled">
3419
+ <span class="entity-browser-entry-label">${e.entity_id}</span>
3420
+ </div>
3421
+ ` : n`
3422
+ <div class="entity-browser-entity" @click=${() => t.onSourceAdded(i)}>
3423
+ <span class="entity-browser-entry-label">${e.entity_id}</span>
3424
+ </div>
3425
+ ` : r;
3426
+ }
3427
+ function Gn(e, t, i, a, o) {
3428
+ if (Z(i)) return n`
3429
+ <div class="entity-browser-entry" @click=${() => o.onBreadcrumbClick([...a, t])}>
3430
+ <span class="entity-browser-entry-label">${t}</span>
3431
+ <span class="entity-browser-entry-arrow">&#x203A;</span>
3432
+ </div>
3433
+ `;
3434
+ let s = j(i), c = [...a, t];
3435
+ if (!s) return r;
3436
+ let l = M(e, c);
3437
+ return l ? Kn({
3438
+ label: t,
3439
+ source: l,
3440
+ type: s,
3441
+ opts: o
3442
+ }) : r;
3443
+ }
3444
+ function Kn(e) {
3445
+ let { label: t, source: i, type: a, opts: o, secondary: s } = e, c = n`
3446
+ <span class="entity-browser-entry-text">
3447
+ <span class="entity-browser-entry-label">${t}</span>
3448
+ ${s ? n`<span class="entity-browser-entry-secondary">${s}</span>` : r}
3449
+ </span>
3450
+ <span class="entity-browser-entry-type">${a}</span>
3451
+ `;
3452
+ return Bn(i.id, o) ? n`
3453
+ <div class="entity-browser-entry entity-browser-entry--present entity-browser-entry--removable" @click=${() => o.onSourceRemoved(i.id)}>
3454
+ ${c}
3455
+ </div>
3456
+ ` : Vn(i.id, o) ? n`
3457
+ <div class="entity-browser-entry entity-browser-entry--present entity-browser-entry--forced">
3458
+ ${c}
3459
+ </div>
3460
+ ` : n`
3461
+ <div class="entity-browser-entry" @click=${() => o.onSourceAdded(i)}>
3462
+ ${c}
3463
+ </div>
3464
+ `;
3465
+ }
3466
+ function qn(e, t) {
3467
+ let i = Jn(e, e.attributes, t.attributeSearch), a = i.slice(0, Dn);
3468
+ return a.length === 0 ? n`<div class="entity-browser-search-empty">${X(t.hass, "no_matching_attributes")}</div>` : n`
3469
+ <div class="entity-browser-search-results">
3470
+ ${a.map((e) => Kn({
3471
+ label: e.key,
3472
+ source: e.source,
3473
+ type: e.valueType,
3474
+ opts: t,
3475
+ secondary: e.dottedPath
3476
+ }))}
3477
+ ${i.length > a.length ? n`<div class="entity-browser-search-count">${X(t.hass, "attribute_results_limited")}</div>` : r}
3478
+ </div>
3479
+ `;
3480
+ }
3481
+ function Jn(e, t, n) {
3482
+ let r = n.trim().toLocaleLowerCase();
3483
+ if (!r) return [];
3484
+ let i = [], a = (t, n, o) => {
3485
+ if (!(o > En)) for (let [s, c] of Object.entries(t)) {
3486
+ let t = [...n, s];
3487
+ if (Z(c)) {
3488
+ a(c, t, o + 1);
3489
+ continue;
3490
+ }
3491
+ let l = j(c), u = l ? M(e, t) : void 0;
3492
+ !l || !u || Yn(t, c).includes(r) && i.push({
3493
+ key: s,
3494
+ dottedPath: t.join("."),
3495
+ valueType: l,
3496
+ source: u
3497
+ });
3498
+ }
3499
+ };
3500
+ return a(t, [], 0), i.sort((e, t) => {
3501
+ let n = Xn(e, r), i = Xn(t, r);
3502
+ return n === i ? e.dottedPath.length === t.dottedPath.length ? e.dottedPath.localeCompare(t.dottedPath) : e.dottedPath.length - t.dottedPath.length : n - i;
3503
+ });
3504
+ }
3505
+ function Yn(e, t) {
3506
+ let n = typeof t == "string" || typeof t == "number" || typeof t == "boolean" ? String(t) : "";
3507
+ return [
3508
+ ...e,
3509
+ e.join("."),
3510
+ n
3511
+ ].join(" ").toLocaleLowerCase();
3512
+ }
3513
+ function Xn(e, t) {
3514
+ let n = e.key.toLocaleLowerCase(), r = e.dottedPath.toLocaleLowerCase();
3515
+ return n.startsWith(t) ? 0 : r.startsWith(t) ? 1 : n.includes(t) ? 2 : r.includes(t) ? 3 : 4;
3516
+ }
3517
+ //#endregion
3518
+ //#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
3519
+ function Q(e, t, n, r) {
3520
+ var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
3521
+ if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
3522
+ else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a);
3523
+ return i > 3 && a && Object.defineProperty(t, n, a), a;
3524
+ }
3525
+ //#endregion
3526
+ //#region src/ha-better-history.ts
3527
+ var Zn = /°[CF]|[CFK]$/, Qn = 60, $n = 1e3;
3528
+ function er(e) {
3529
+ return Zn.test(e);
3530
+ }
3531
+ var $ = class extends e {
3532
+ constructor(...e) {
3533
+ super(...e), this.hours = 24, this.showDatePicker = !1, this.showEntityPicker = !1, this.showImportButton = !1, this.showLegend = !0, this.showTooltip = !0, this.showControls = !0, this.debugPerformance = !1, this.toolsOpen = !1, this._hiddenSeriesIds = [], this._liveNow = Date.now(), this._datePickerReady = !1, this._entityComponentsReady = !1, this._attributeMenuOpen = !1, this._attributeSearch = "", this._path = [], this._selectedSources = [], this._customEntityIds = [], this._entityPickerOpen = !1, this._data = new Pe(this), this._tooltip = new zt(this), this._prevClipX = /* @__PURE__ */ new Map(), this._prevStartTime = 0, this._prevEndTime = 0, this._prevContainerWidth = 0, this._wasLoading = !1, this._suppressLineAnimation = !1, this._pendingAddedSources = [], this._dragDropCommitted = !1, this._lastPickerOverlayOpen = !1, this._importedSeriesMeta = /* @__PURE__ */ new Map(), this._importedDataActive = !1, this._containerWidth = 0, this._lastFetchKey = "", this._lastFetchSources = [], this._lastHassResolveTime = 0, this._getEntityPickerItems = () => this._pickerEntities().map((e) => ({
3534
+ id: e.entity_id,
3535
+ primary: On(e),
3536
+ secondary: e.entity_id
3537
+ })), this._getAdditionalEntityPickerItems = (e) => {
3538
+ if (!this.hass || !e?.trim()) return [];
3539
+ let t = e.toLowerCase(), n = new Set(this._pickerEntities().map((e) => e.entity_id));
3540
+ return Object.values(this.hass.states).filter((e) => e !== void 0).filter((e) => !n.has(e.entity_id)).filter((e) => e.entity_id.toLowerCase().includes(t) || typeof e.attributes.friendly_name == "string" && e.attributes.friendly_name.toLowerCase().includes(t)).slice(0, 20).map((e) => ({
3541
+ id: e.entity_id,
3542
+ primary: On(e),
3543
+ secondary: e.entity_id
3544
+ }));
3545
+ }, this._handleDocumentPointerDown = (e) => {
3546
+ this._attributeMenuOpen && (this._isEventInsideAttributeOverlay(e) || (e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation()));
3547
+ }, this._handleDocumentClick = (e) => {
3548
+ this._attributeMenuOpen && (this._isEventInsideAttributeOverlay(e) || (e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), this._closeAttributeMenu()));
3549
+ };
3550
+ }
3551
+ static {
3552
+ this.styles = gn;
3553
+ }
3554
+ connectedCallback() {
3555
+ super.connectedCallback(), yn(), document.addEventListener("pointerdown", this._handleDocumentPointerDown, !0), document.addEventListener("mousedown", this._handleDocumentPointerDown, !0), document.addEventListener("click", this._handleDocumentClick, !0), this._resizeObserver = new ResizeObserver((e) => {
3556
+ let t = e[0]?.contentRect.width ?? 0;
3557
+ t !== this._containerWidth && (this._containerWidth = t);
3558
+ }), this._resizeObserver.observe(this);
3559
+ }
3560
+ disconnectedCallback() {
3561
+ super.disconnectedCallback(), document.removeEventListener("pointerdown", this._handleDocumentPointerDown, !0), document.removeEventListener("mousedown", this._handleDocumentPointerDown, !0), document.removeEventListener("click", this._handleDocumentClick, !0), this._resizeObserver?.disconnect(), this._resizeObserver = void 0, this._sourceAddBatchTimer !== void 0 && (clearTimeout(this._sourceAddBatchTimer), this._sourceAddBatchTimer = void 0), this._stopLiveClock();
3562
+ }
3563
+ _maxXTicks() {
3564
+ return this._containerWidth <= 0 ? 12 : Math.max(3, Math.floor(this._containerWidth * 640 / (720 * 50)));
3565
+ }
3566
+ _effectiveStartDate() {
3567
+ return this._rangeStart ?? this.startDate ?? this.config?.startDate ?? /* @__PURE__ */ new Date(Date.now() - (this.config?.hours ?? this.hours ?? 24) * 36e5);
3568
+ }
3569
+ _effectiveEndDate() {
3570
+ let e = this._requestedEndDate(), t = this._liveNow || Date.now();
3571
+ return e.getTime() > t ? new Date(t) : e;
3572
+ }
3573
+ _requestedEndDate() {
3574
+ return this._rangeEnd ?? this.endDate ?? this.config?.endDate ?? /* @__PURE__ */ new Date();
3575
+ }
3576
+ _rangeExtendsFuture() {
3577
+ return this._requestedEndDate().getTime() > Date.now();
3578
+ }
3579
+ _syncLiveClock() {
3580
+ if (!this._rangeExtendsFuture()) {
3581
+ this._stopLiveClock();
3582
+ return;
3583
+ }
3584
+ if (this._liveNowTimer !== void 0) return;
3585
+ let e = Date.now();
3586
+ this._viewEnd &&= new Date(e), this._liveNow = e, this._liveNowTimer = setInterval(() => {
3587
+ if (!this._rangeExtendsFuture()) {
3588
+ this._stopLiveClock();
3589
+ return;
3590
+ }
3591
+ let e = this._effectiveEndDate().getTime(), t = !this._viewEnd || Math.abs(this._viewEnd.getTime() - e) <= $n * 2, n = Date.now();
3592
+ this._liveNow = n, t && (this._viewEnd = new Date(n));
3593
+ }, $n);
3594
+ }
3595
+ _stopLiveClock() {
3596
+ this._liveNowTimer !== void 0 && (clearInterval(this._liveNowTimer), this._liveNowTimer = void 0);
3597
+ }
3598
+ _effectiveLineMode() {
3599
+ return this._runtimeLineMode ?? this.config?.lineMode ?? this.lineMode;
3600
+ }
3601
+ _effectiveViewRange() {
3602
+ let e = this._resolved?.startDate ?? this._effectiveStartDate(), t = this._rangeExtendsFuture() ? this._effectiveEndDate() : this._resolved?.endDate ?? this._effectiveEndDate(), n = this._viewStart && this._viewStart.getTime() >= e.getTime() ? this._viewStart : e, r = this._viewEnd && this._viewEnd.getTime() <= t.getTime() ? this._viewEnd : t;
3603
+ return r.getTime() > n.getTime() ? {
3604
+ start: n,
3605
+ end: r
3606
+ } : {
3607
+ start: e,
3608
+ end: t
3609
+ };
3610
+ }
3611
+ _pickerEntities() {
3612
+ return this.hass ? [...this.config?.defaultEntities ?? [], ...this._customEntityIds].filter((e) => typeof e == "string" && e !== "").filter((e, t, n) => n.indexOf(e) === t).map((e) => this.hass?.states[e]).filter((e) => e !== void 0) : [];
3613
+ }
3614
+ _fetchSources() {
3615
+ let e = [], t = /* @__PURE__ */ new Set();
3616
+ if (this._resolved && !this._importedDataActive) for (let n of this._resolved.series) t.has(n.id) || (t.add(n.id), e.push(Wt(n)));
3617
+ for (let n of this._selectedSources) {
3618
+ let r = this._sourceWithAttributeUnit(n);
3619
+ t.has(r.id) || (t.add(r.id), e.push(r));
3620
+ }
3621
+ return e;
3622
+ }
3623
+ _isDefaultSource(e) {
3624
+ return (this._resolved?.series ?? []).some((t) => t.id === e.id);
3625
+ }
3626
+ _resolvedTemperatureUnit() {
3627
+ return this._resolved?.series.find((e) => e.scaleGroupKey === "group:temperature" && e.unit && er(e.unit))?.unit;
3628
+ }
3629
+ willUpdate(e) {
3630
+ this._data.debugPerformance = this.debugPerformance || this.config?.debugPerformance === !0;
3631
+ let t = this._effectiveStartDate().getTime(), n = this._effectiveEndDate().getTime(), r = this._rangeExtendsFuture();
3632
+ this._syncLiveClock(), e.has("hass") && r && this._data.updateLivePoints(this.hass, this._lastFetchSources, new Date(t), new Date(n));
3633
+ let i = t !== this._prevStartTime || n !== this._prevEndTime, a = this._containerWidth !== this._prevContainerWidth, o = e.has("_rangeStart") || e.has("_rangeEnd") || e.has("startDate") || e.has("endDate") || e.has("config") || e.has("hours");
3634
+ (i || a) && (r && i && !a && !o || this._prevClipX.clear(), this._prevStartTime = t, this._prevEndTime = n, this._prevContainerWidth = this._containerWidth), this._data.loading && this._data.series.length === 0 && this._prevClipX.clear();
3635
+ let s = /* @__PURE__ */ "_rangeStart._rangeEnd._selectedSources.hass.config.entities.hours.startDate.endDate.showDatePicker.showEntityPicker.showLegend.showTooltip.width.height.lineMode.lineWidth.backgroundColor.graphTitle.titleFontFamily.titleFontSize.titleColor.language.debugPerformance.attributeUnits._runtimeLineMode".split(".");
3636
+ if (s.some((t) => e.has(t))) {
3637
+ let t = !s.some((t) => t !== "hass" && e.has(t));
3638
+ if ((e.has("config") || e.has("entities")) && (this._importedDataActive = !1, this._importedSeriesMeta.clear()), t) {
3639
+ let e = Math.floor(Date.now() / 1e3) * 1e3;
3640
+ if (r && this._lastFetchKey) {
3641
+ this._lastHassResolveTime = e;
3642
+ return;
3643
+ }
3644
+ if (e === this._lastHassResolveTime && this._lastFetchKey) return;
3645
+ this._lastHassResolveTime = e;
3646
+ }
3647
+ let n = pn({
3648
+ config: this.config,
3649
+ entities: this.entities,
3650
+ hours: this.hours,
3651
+ startDate: this._effectiveStartDate(),
3652
+ endDate: this._effectiveEndDate(),
3653
+ showDatePicker: this.showDatePicker,
3654
+ showEntityPicker: this.showEntityPicker,
3655
+ showLegend: this.showLegend,
3656
+ showTooltip: this.showTooltip,
3657
+ width: this.width,
3658
+ height: this.height,
3659
+ lineMode: this._effectiveLineMode(),
3660
+ lineWidth: this.lineWidth,
3661
+ backgroundColor: this.backgroundColor,
3662
+ title: this.graphTitle,
3663
+ titleFontFamily: this.titleFontFamily,
3664
+ titleFontSize: this.titleFontSize,
3665
+ titleColor: this.titleColor,
3666
+ language: this.language,
3667
+ hass: this.hass,
3668
+ attributeUnits: this.attributeUnits
3669
+ });
3670
+ this._resolved = n, !this._rangeStart && !this._rangeEnd && (this._rangeStart = n.startDate, this._rangeEnd = n.endDate), !this._viewStart && !this._viewEnd && (this._viewStart = n.startDate, this._viewEnd = n.endDate);
3671
+ let i = this._fetchSources(), a = i.map((e) => e.id).sort().join("|"), o = `${a}|${n.startDate.getTime()}|${n.endDate.getTime()}`;
3672
+ if (o !== this._lastFetchKey) {
3673
+ let e = a === this._lastFetchKey.split("|").slice(0, -2).join("|") && this._lastFetchKey !== "";
3674
+ if (this._lastFetchSources.length > 0 && !e) {
3675
+ let e = new Set(this._lastFetchSources.map((e) => e.id)), t = new Set(i.map((e) => e.id)), r = i.filter((t) => !e.has(t.id)), a = this._lastFetchSources.filter((e) => !t.has(e.id)).map((e) => e.id);
3676
+ r.length > 0 && a.length === 0 ? (this._lastFetchKey = o, this._lastFetchSources = i, this._data.addSources(this.hass, r, n.startDate, n.endDate)) : a.length > 0 && r.length === 0 ? (this._lastFetchKey = o, this._lastFetchSources = i, this._data.removeSources(a)) : (this._lastFetchKey = o, this._lastFetchSources = i, this._data.fetch(this.hass, i, n.startDate, n.endDate));
3677
+ } else this._lastFetchKey = o, this._lastFetchSources = i, this._data.fetch(this.hass, i, n.startDate, n.endDate);
3678
+ }
3679
+ n.showDatePicker && !this._datePickerReady && wn().then(() => {
3680
+ this._datePickerReady = Cn(), this.requestUpdate();
3681
+ }), n.showEntityPicker && !this._entityComponentsReady && An().then(() => {
3682
+ this._entityComponentsReady = jn(), this.requestUpdate();
3683
+ });
3684
+ }
3685
+ }
3686
+ updated(e) {
3687
+ e.has("_attributeMenuOpen") && this._attributeMenuOpen && this._positionEntityMenu(), (e.has("_attributeMenuOpen") || e.has("_entityPickerOpen")) && this._emitPickerOverlayState(), this._animateClipPaths(), this._wasLoading = this._data.loading;
3688
+ }
3689
+ _emitPickerOverlayState() {
3690
+ let e = this._attributeMenuOpen || this._entityPickerOpen;
3691
+ e !== this._lastPickerOverlayOpen && (this._lastPickerOverlayOpen = e, this.dispatchEvent(new CustomEvent("picker-overlay-changed", {
3692
+ detail: { open: e },
3693
+ bubbles: !0,
3694
+ composed: !0
3695
+ })));
3696
+ }
3697
+ _onDateRangeChanged(e, t) {
3698
+ this._rangeStart = e, this._rangeEnd = t, this._viewStart = e, this._viewEnd = t, this.dispatchEvent(new CustomEvent("range-changed", {
3699
+ detail: {
3700
+ startDate: e,
3701
+ endDate: t
3702
+ },
3703
+ bubbles: !0,
3704
+ composed: !0
3705
+ })), this.requestUpdate();
3706
+ }
3707
+ _pickScaleGroup(e, t) {
3708
+ if (e.valueType !== "number") return `series:${e.id}`;
3709
+ if (e.unit) {
3710
+ let t = this._resolved?.series.find((t) => t.unit === e.unit && t.valueType === "number");
3711
+ if (t) return t.scaleGroupKey;
3712
+ let n = this._resolved?.series.find((e) => e.scaleGroupKey === "group:temperature");
3713
+ if (n && er(e.unit)) return n.scaleGroupKey;
3714
+ }
3715
+ return e.unit ? `unit:${e.unit}` : `series:${e.id}`;
3716
+ }
3717
+ _defaultLineMode() {
3718
+ let e = this._effectiveLineMode();
3719
+ return e === "line" || e === "column" ? e : "stair";
3720
+ }
3721
+ _defaultLineWidth() {
3722
+ let e = this.config?.lineWidth ?? this.lineWidth;
3723
+ return typeof e == "number" ? Number.isFinite(e) && e >= 0 ? String(e) : "2.5" : typeof e == "string" && e.trim() !== "" ? e.trim() : "2.5";
3724
+ }
3725
+ _showImportButton() {
3726
+ return this.showImportButton || this.config?.showImportButton === !0;
3727
+ }
3728
+ _buildRenderSeries() {
3729
+ if (!this._resolved) return [];
3730
+ let e = this._importedDataActive ? [] : this._resolved.series.flatMap((e) => {
3731
+ let t = this._data.series.find((t) => t.source.id === e.id);
3732
+ return [{
3733
+ id: e.id,
3734
+ label: e.label,
3735
+ color: e.color,
3736
+ unit: e.unit,
3737
+ scaleGroupKey: e.scaleGroupKey,
3738
+ scaleMode: e.scaleMode,
3739
+ scaleMin: e.scaleMin,
3740
+ scaleMax: e.scaleMax,
3741
+ lineMode: this._runtimeLineMode ?? e.lineMode,
3742
+ lineWidth: e.lineWidth,
3743
+ valueType: e.valueType,
3744
+ points: t?.points ?? []
3745
+ }];
3746
+ });
3747
+ for (let t of this._selectedSources) {
3748
+ let n = this._sourceWithAttributeUnit(t);
3749
+ if (e.some((e) => e.id === n.id)) continue;
3750
+ let r = this._data.series.find((e) => e.source.id === n.id);
3751
+ if (!r) continue;
3752
+ let i = e.length, a = this._pickScaleGroup(n, e);
3753
+ e.push({
3754
+ id: n.id,
3755
+ label: n.label,
3756
+ color: this._importedSeriesMeta.get(n.id)?.color ?? L(i),
3757
+ unit: n.unit,
3758
+ scaleGroupKey: a,
3759
+ scaleMode: "auto",
3760
+ lineMode: this._runtimeLineMode ?? this._importedSeriesMeta.get(n.id)?.lineMode ?? this._defaultLineMode(),
3761
+ lineWidth: this._defaultLineWidth(),
3762
+ valueType: n.valueType,
3763
+ points: r?.points ?? []
3764
+ });
3765
+ }
3766
+ return e;
3767
+ }
3768
+ _chartSourceKey() {
3769
+ return [...this._resolved?.series.map((e) => [
3770
+ e.id,
3771
+ e.label,
3772
+ e.color,
3773
+ e.unit ?? "",
3774
+ e.scaleGroupKey,
3775
+ e.scaleMode,
3776
+ e.scaleMin ?? "",
3777
+ e.scaleMax ?? "",
3778
+ e.lineMode,
3779
+ e.lineWidth,
3780
+ e.valueType
3781
+ ].join("~")) ?? [], ...this._selectedSources.map((e) => {
3782
+ let t = this._sourceWithAttributeUnit(e);
3783
+ return [
3784
+ t.id,
3785
+ t.label,
3786
+ t.kind,
3787
+ t.unit ?? "",
3788
+ t.valueType,
3789
+ this._defaultLineMode(),
3790
+ this._defaultLineWidth()
3791
+ ].join("~");
3792
+ })].join("|");
3793
+ }
3794
+ _chartData() {
3795
+ let e = this._hiddenSeriesIds.join("|"), t = this._chartSourceKey(), n = this._chartRenderCache, r = this._effectiveViewRange(), i = r.start.getTime(), a = r.end.getTime(), o = this._containerWidth, s = !this._data.loading;
3796
+ if (n && n.seriesRef === this._data.series && n.sourceKey === t && n.hiddenKey === e && n.startTime === i && n.endTime === a && n.extendStairToEnd === s && n.containerWidth === o) return n.data;
3797
+ let c = this._maxXTicks(), l = this._buildRenderSeries(), u = l.filter((e) => !this._hiddenSeriesIds.includes(e.id)), d = {
3798
+ start: i,
3799
+ end: Math.max(a, i + 1)
3800
+ }, m = this._data.debugPerformance, h = m ? f() : 0, g = jt(l, u, d, this._resolved?.disableClimateOverlay ?? !1, c, s), _ = m ? f() - h : 0;
3801
+ return m && p(m, "chart.build_data", {
3802
+ allSeriesCount: l.length,
3803
+ visibleSeriesCount: u.length,
3804
+ pointCount: u.reduce((e, t) => e + t.points.length, 0),
3805
+ groupCount: g.numericScales.length,
3806
+ segmentCount: g.segments.length,
3807
+ lineCount: g.numericLines.length,
3808
+ buildDurationMs: Math.round(_)
3809
+ }), this._chartRenderCache = {
3810
+ seriesRef: this._data.series,
3811
+ sourceKey: t,
3812
+ hiddenKey: e,
3813
+ startTime: i,
3814
+ endTime: a,
3815
+ extendStairToEnd: s,
3816
+ containerWidth: o,
3817
+ data: g
3818
+ }, g;
3819
+ }
3820
+ _graphGroups(e) {
3821
+ let t = this._maxXTicks(), n = this._graphGroupRenderCache;
3822
+ if (n && n.dataRef === e && n.maxXTicks === t) return n.groups;
3823
+ let r = Rt(e, t);
3824
+ return this._graphGroupRenderCache = {
3825
+ dataRef: e,
3826
+ maxXTicks: t,
3827
+ groups: r
3828
+ }, r;
3829
+ }
3830
+ _renderGraphGroup(e) {
3831
+ let t = this._resolved?.showLegend ?? !0;
3832
+ return n`
3833
+ <div class="graph-section">
3834
+ <div class="graph-canvas" data-series-ids=${e.series.map((e) => e.id).join("|")} style="height:${e.canvasHeight}px">
3835
+ <svg
3836
+ viewBox="0 0 ${720} ${e.svgHeight}"
3837
+ height="${e.svgHeight}"
3838
+ preserveAspectRatio="none"
3839
+ >
3840
+ ${e.xLabels.map((t) => i`
3841
+ <line class="grid-line grid-line--vertical" x1=${t.x.toFixed(1)} y1=${18} x2=${t.x.toFixed(1)} y2=${e.svgHeight - 18}></line>
3842
+ `)}
3843
+ ${e.yLabels.map((e) => i`
3844
+ <line class="grid-line grid-line--horizontal" x1=${40} y1=${e.y.toFixed(1)} x2=${680} y2=${e.y.toFixed(1)}></line>
3845
+ `)}
3846
+ <defs>
3847
+ ${e.lines.map((t) => {
3848
+ let n = t.id.replace(/[^a-zA-Z0-9]/g, "_");
3849
+ return i`
3850
+ <clipPath id=${`clip-${n}`}>
3851
+ <rect id=${`rect-${n}`} x="0" y="0" width="0" height=${e.svgHeight}></rect>
3852
+ </clipPath>
3853
+ `;
3854
+ })}
3855
+ </defs>
3856
+ ${e.heatingAreas.map((e) => i`<polygon class="climate-heating-area" points=${e.points}></polygon>`)}
3857
+ ${e.columns.map((e) => i`<rect class="column" x=${e.x.toFixed(1)} y=${e.y.toFixed(1)} width=${e.width.toFixed(1)} height=${e.height.toFixed(1)} fill=${e.fill}></rect>`)}
3858
+ ${e.lines.map((e) => {
3859
+ let t = `clip-${e.id.replace(/[^a-zA-Z0-9]/g, "_")}`, n = e.points.split(" "), a = n[n.length - 1], o = a ? parseFloat(a.split(",")[0]) : 0, s = this._prevClipX.get(e.id) ?? 0, c = !this._suppressLineAnimation && o > s;
3860
+ return i`<polyline class="line" clip-path="url(#${t})" data-line-id=${e.id} data-animate-clip=${c ? "true" : r} data-target-x=${o} points=${e.points} stroke=${e.color} stroke-width=${e.lineWidth}></polyline>`;
3861
+ })}
3862
+ ${e.segments.map((e) => i`<rect class="segment" x=${e.x} y=${e.y} width=${e.width} height="9" fill=${e.fill}></rect>`)}
3863
+ ${e.series.filter((e) => e.valueType !== "number" && e.valueType !== "boolean").map((e, t) => i`<rect class="segment-border" x=${40} y=${218 + t * 14} width=${640} height="9" fill="none" stroke=${e.color}></rect>`)}
3864
+ <line class="axis" x1=${40} y1=${18} x2=${40} y2=${e.svgHeight - 18}></line>
3865
+ ${e.rightYLabels.length > 0 ? i`<line class="axis" x1=${680} y1=${18} x2=${680} y2=${e.svgHeight - 18}></line>` : r}
3866
+ <line class="axis" x1=${40} y1=${e.svgHeight - 18} x2=${680} y2=${e.svgHeight - 18}></line>
3867
+ ${e.scale ? e.yLabels.map((e) => i`
3868
+ <line class="axis tick" x1=${36} y1=${e.y.toFixed(1)} x2=${40} y2=${e.y.toFixed(1)}></line>
3869
+ `) : r}
3870
+ ${e.rightYLabels.map((e) => i`
3871
+ <line class="axis tick" x1=${680} y1=${e.y.toFixed(1)} x2=${684} y2=${e.y.toFixed(1)}></line>
3872
+ `)}
3873
+ </svg>
3874
+ ${e.yLabels.map((e) => {
3875
+ let t = (40 / 720 * 100).toFixed(2);
3876
+ return n`<span class="y-axis-label" style="top:${e.y.toFixed(1)}px;left:0;width:${t}%;text-align:right;padding-right:6px;">${e.value}</span>`;
3877
+ })}
3878
+ ${e.rightYLabels.map((e) => {
3879
+ let t = (680 / 720 * 100).toFixed(2), r = (100 - Number(t)).toFixed(2);
3880
+ return n`<span class="y-axis-label" style="top:${e.y.toFixed(1)}px;left:${t}%;width:${r}%;text-align:left;padding-left:6px;">${e.value}</span>`;
3881
+ })}
3882
+ ${e.xLabels.map((t) => {
3883
+ let r = (t.x / 720 * 100).toFixed(2);
3884
+ return n`<span class="x-axis-label ${t.bold ? "x-axis-label--bold" : ""}" style="left:${r}%;top:${e.svgHeight + 3}px;">${t.label}</span>`;
3885
+ })}
3886
+ </div>
3887
+ ${t && e.allSeries.length > 0 ? n`
3888
+ <div class="graph-legend">
3889
+ ${e.allSeries.map((e) => n`
3890
+ <button class="legend-item" ?hidden-series=${this._hiddenSeriesIds.includes(e.id)} @click=${() => this._toggleSeries(e.id)}>
3891
+ <span class="swatch" style=${e.valueType === "string" ? `background:color-mix(in srgb,${e.color} 30%,transparent);border:1px solid ${e.color};` : `background:${e.color};`}></span>
3892
+ <span class="legend-label">${e.label}</span>
3893
+ </button>
3894
+ `)}
3895
+ </div>
3896
+ ` : r}
3897
+ </div>
3898
+ `;
3899
+ }
3900
+ _animateClipPaths() {
3901
+ let e = this.renderRoot;
3902
+ if (!e) return;
3903
+ let t = e.querySelectorAll("polyline[data-animate-clip=\"true\"]");
3904
+ if (t.length === 0) {
3905
+ e.querySelectorAll("polyline[data-line-id]").forEach((t) => {
3906
+ let n = t.getAttribute("data-line-id");
3907
+ if (!n) return;
3908
+ let r = Number(t.getAttribute("data-target-x")), i = n.replace(/[^a-zA-Z0-9]/g, "_"), a = e.querySelector(`#rect-${i}`);
3909
+ a instanceof SVGRectElement && (a.setAttribute("width", r.toString()), this._prevClipX.set(n, r));
3910
+ });
3911
+ return;
3912
+ }
3913
+ t.forEach((t) => {
3914
+ let n = t.getAttribute("data-line-id"), r = Number(t.getAttribute("data-target-x"));
3915
+ if (!n || !Number.isFinite(r)) return;
3916
+ let i = this._prevClipX.get(n) ?? 0, a = n.replace(/[^a-zA-Z0-9]/g, "_"), o = e.querySelector(`#rect-${a}`);
3917
+ o instanceof SVGRectElement && (o.style.setProperty("transition", "none"), o.setAttribute("width", i.toString()), o.getBoundingClientRect(), o.style.setProperty("transition", "width 0.9s cubic-bezier(0.25, 0.1, 0.25, 1)"), o.setAttribute("width", r.toString()), this._prevClipX.set(n, r)), t.removeAttribute("data-animate-clip");
3918
+ });
3919
+ }
3920
+ _renderChartBody() {
3921
+ if (this._data.error) {
3922
+ let e = /timed?\s*out/i.test(this._data.error);
3923
+ return n`<div class="error">${X(this.hass, e ? "error_timeout" : "error")}</div>`;
3924
+ }
3925
+ if (!this._resolved || this._resolved.series.length === 0 && this._selectedSources.length === 0) return n`<div class="empty">${X(this.hass, "no_series")}</div>`;
3926
+ let e = this._chartData(), t = e.visibleSeries.some((e) => e.points.length > 0), i = this._resolved.showTooltip, a = this._graphGroups(e), o = a.length > 0 && (t || this._data.loading);
3927
+ this._suppressLineAnimation = this._wasLoading && !this._data.loading;
3928
+ let s = a.reduce((e, t) => e + t.canvasHeight, 0);
3929
+ if (t && i) {
3930
+ let t = a.flatMap((e) => e.allSeries.map((e) => ({
3931
+ id: e.id,
3932
+ label: e.label,
3933
+ color: e.color
3934
+ })));
3935
+ this._tooltip.sync(t, this._data.series, this._hiddenSeriesIds, s, e.timeBounds);
3936
+ }
3937
+ return n`
3938
+ <div class="chart-surface">
3939
+ ${o ? n`
3940
+ <div class="chart-graphs"
3941
+ @pointermove=${i ? (e) => this._tooltip.handlePointerMove(e) : r}
3942
+ @pointerleave=${i ? () => this._tooltip.handlePointerLeave() : r}
3943
+ >
3944
+ ${a.map((e) => this._renderGraphGroup(e))}
3945
+ ${i ? this._tooltip.renderTooltip() : r}
3946
+ </div>` : this._data.loading ? r : n`<div class="empty">${X(this.hass, "empty")}</div>`}
3947
+ </div>
3948
+ `;
3949
+ }
3950
+ _renderEntityPickerUI() {
3951
+ return !this._resolved?.showEntityPicker || !this._entityComponentsReady ? r : Mn({
3952
+ hass: this.hass,
3953
+ menuOpen: this._attributeMenuOpen,
3954
+ entityPickerOpen: this._entityPickerOpen,
3955
+ selectedEntityId: this._selectedEntityId,
3956
+ path: this._path,
3957
+ selectedSources: this._selectedSources,
3958
+ draggingSourceId: this._draggingSourceId,
3959
+ resolved: this._resolved,
3960
+ loading: this._data.loading,
3961
+ attributeSearch: this._attributeSearch,
3962
+ getItems: this._getEntityPickerItems,
3963
+ getAdditionalItems: this._getAdditionalEntityPickerItems,
3964
+ onEntityPickerOpened: () => this._onEntityPickerOpened(),
3965
+ onEntityPickerClosed: () => this._onEntityPickerClosed(),
3966
+ onEntitySelected: (e) => this._onEntitySelected(e),
3967
+ onAttributeSearchChanged: (e) => {
3968
+ this._attributeSearch = e;
3969
+ },
3970
+ onSourceAdded: (e) => this._addSource(e),
3971
+ onSourceRemoved: (e) => this._removeSource(e),
3972
+ onSourceDragStart: (e, t) => this._onSourceDragStart(e, t),
3973
+ onSourceDragOver: (e, t) => this._onSourceDragOver(e, t),
3974
+ onSourceDragEnd: () => this._onSourceDragEnd(),
3975
+ onSourceDrop: (e, t) => this._onSourceDrop(e, t),
3976
+ onBreadcrumbClick: (e) => {
3977
+ this._path = e;
3978
+ },
3979
+ onCloseMenu: () => this._closeAttributeMenu()
3980
+ });
3981
+ }
3982
+ _rangePercent(e, t) {
3983
+ let n = this._resolved?.startDate.getTime() ?? this._effectiveStartDate().getTime(), r = this._rangeExtendsFuture() ? this._effectiveEndDate().getTime() : this._resolved?.endDate.getTime() ?? this._effectiveEndDate().getTime(), i = Math.max(r - n, 1), a = e?.getTime() ?? t.getTime();
3984
+ return Math.round((a - n) / i * 1e3);
3985
+ }
3986
+ _loadedRangeMs() {
3987
+ let e = this._resolved?.startDate.getTime() ?? this._effectiveStartDate().getTime(), t = this._rangeExtendsFuture() ? this._effectiveEndDate().getTime() : this._resolved?.endDate.getTime() ?? this._effectiveEndDate().getTime(), n = Math.max(t, e + 1);
3988
+ return {
3989
+ start: e,
3990
+ end: n,
3991
+ span: n - e
3992
+ };
3993
+ }
3994
+ _minViewSpanMs() {
3995
+ let { span: e } = this._loadedRangeMs();
3996
+ return Math.min(6e4, Math.max(1, Math.floor(e / 1e3)));
3997
+ }
3998
+ _setViewRangeMs(e, t) {
3999
+ let n = this._loadedRangeMs(), r = this._minViewSpanMs(), i = Math.max(t - e, r), a = Math.min(i, n.span), o = Math.min(Math.max(e, n.start), n.end - a), s = o + a;
4000
+ this._viewStart = new Date(o), this._viewEnd = new Date(s), this.dispatchEvent(new CustomEvent("view-range-changed", {
4001
+ detail: {
4002
+ start: this._viewStart,
4003
+ end: this._viewEnd
4004
+ },
4005
+ bubbles: !0,
4006
+ composed: !0
4007
+ }));
4008
+ }
4009
+ _dateFromRangePercent(e) {
4010
+ let t = this._resolved?.startDate.getTime() ?? this._effectiveStartDate().getTime(), n = this._rangeExtendsFuture() ? this._effectiveEndDate().getTime() : this._resolved?.endDate.getTime() ?? this._effectiveEndDate().getTime();
4011
+ return new Date(t + Math.max(0, Math.min(1e3, e)) / 1e3 * (n - t));
4012
+ }
4013
+ _formatRangeDate(e) {
4014
+ return e.toLocaleString(this._resolved?.language ?? void 0, {
4015
+ month: "short",
4016
+ day: "numeric",
4017
+ hour: "2-digit",
4018
+ minute: "2-digit"
4019
+ });
4020
+ }
4021
+ _setViewRangePart(e, t) {
4022
+ let n = t.currentTarget, r = this._dateFromRangePercent(Number(n.value)), i = this._effectiveViewRange(), a = this._loadedRangeMs(), o = this._minViewSpanMs();
4023
+ if (e === "start") {
4024
+ let e = i.end.getTime();
4025
+ this._setViewRangeMs(Math.min(Math.max(r.getTime(), a.start), e - o), e);
4026
+ } else {
4027
+ let e = i.start.getTime();
4028
+ this._setViewRangeMs(e, Math.max(Math.min(r.getTime(), a.end), e + o));
4029
+ }
4030
+ }
4031
+ _onRangeSelectionPointerDown(e) {
4032
+ if (e.button !== 0) return;
4033
+ let t = e.currentTarget, n = t.closest(".range-slider-stack");
4034
+ if (!(n instanceof HTMLElement)) return;
4035
+ let r = n.getBoundingClientRect();
4036
+ if (r.width <= 0) return;
4037
+ let i = this._loadedRangeMs(), a = this._effectiveViewRange(), o = a.start.getTime(), s = a.end.getTime() - o;
4038
+ if (s >= i.span - 1) return;
4039
+ e.preventDefault(), e.stopPropagation();
4040
+ let c = e.clientX;
4041
+ t.setPointerCapture(e.pointerId), t.toggleAttribute("dragging", !0);
4042
+ let l = (e) => {
4043
+ e.preventDefault();
4044
+ let t = (e.clientX - c) / r.width * i.span, n = Math.min(Math.max(o + t, i.start), i.end - s);
4045
+ this._setViewRangeMs(n, n + s);
4046
+ }, u = () => {
4047
+ t.toggleAttribute("dragging", !1), t.removeEventListener("pointermove", l), t.removeEventListener("pointerup", u), t.removeEventListener("pointercancel", u);
4048
+ };
4049
+ t.addEventListener("pointermove", l), t.addEventListener("pointerup", u), t.addEventListener("pointercancel", u);
4050
+ }
4051
+ _resetViewRange() {
4052
+ this._resolved && (this._viewStart = this._resolved.startDate, this._viewEnd = this._rangeExtendsFuture() ? this._effectiveEndDate() : this._resolved.endDate, this.dispatchEvent(new CustomEvent("view-range-changed", {
4053
+ detail: {
4054
+ start: this._viewStart,
4055
+ end: this._viewEnd
4056
+ },
4057
+ bubbles: !0,
4058
+ composed: !0
4059
+ })));
4060
+ }
4061
+ _setRuntimeLineMode(e) {
4062
+ this._runtimeLineMode = e;
4063
+ }
4064
+ _exportData() {
4065
+ let e = this._effectiveViewRange(), t = this._buildRenderSeries().filter((e) => !this._hiddenSeriesIds.includes(e.id)).map((t) => ({
4066
+ id: t.id,
4067
+ entityId: t.id.startsWith("attr:") ? t.id.slice(5).split(":")[0] : t.id.replace(/^state:/, ""),
4068
+ attribute: t.id.startsWith("attr:") ? t.id.slice(5).split(":").slice(1).join(":") : void 0,
4069
+ label: t.label,
4070
+ unit: t.unit,
4071
+ valueType: t.valueType,
4072
+ lineMode: t.lineMode,
4073
+ color: t.color,
4074
+ points: t.points.filter((t) => t.time >= e.start.getTime() && t.time <= e.end.getTime()).map((e) => ({
4075
+ timestamp: new Date(e.time).toISOString(),
4076
+ value: e.value
4077
+ }))
4078
+ })), n = {
4079
+ format: "ha-better-history-series-v1",
4080
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
4081
+ loadedRange: {
4082
+ start: this._resolved?.startDate.toISOString(),
4083
+ end: (this._rangeExtendsFuture() ? this._effectiveEndDate() : this._resolved?.endDate)?.toISOString()
4084
+ },
4085
+ viewRange: {
4086
+ start: e.start.toISOString(),
4087
+ end: e.end.toISOString()
4088
+ },
4089
+ series: t
4090
+ }, r = new Blob([JSON.stringify(n, null, 2)], { type: "application/json" }), i = URL.createObjectURL(r), a = document.createElement("a"), o = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4091
+ a.href = i, a.download = `ha-better-history-${o}.json`, a.click(), URL.revokeObjectURL(i);
4092
+ }
4093
+ _importData() {
4094
+ let e = document.createElement("input");
4095
+ e.type = "file", e.accept = "application/json,.json", e.addEventListener("change", () => {
4096
+ let t = e.files?.[0];
4097
+ t && t.text().then((e) => this._applyImportedData(JSON.parse(e))).catch(() => this._data.setError("Invalid import file"));
4098
+ }, { once: !0 }), e.click();
4099
+ }
4100
+ _applyImportedData(e) {
4101
+ if (!this._isExportPayload(e)) {
4102
+ this._data.setError("Unsupported import format");
4103
+ return;
4104
+ }
4105
+ let t = this._parseImportedSeries(e.series ?? []);
4106
+ if (!t) {
4107
+ this._data.setError("Invalid import data");
4108
+ return;
4109
+ }
4110
+ let n = this._parseDate(e.viewRange?.start), r = this._parseDate(e.viewRange?.end), i = this._parseDate(e.loadedRange?.start) ?? n, a = this._parseDate(e.loadedRange?.end) ?? r;
4111
+ if (!n || !r || !i || !a || i.getTime() >= a.getTime()) {
4112
+ this._data.setError("Invalid import range");
4113
+ return;
4114
+ }
4115
+ this._importedSeriesMeta = t.meta, this._importedDataActive = !0, this._selectedSources = t.series.map((e) => e.source), this._rangeStart = i, this._rangeEnd = a, this._viewStart = n, this._viewEnd = r, this._hiddenSeriesIds = [], this._chartRenderCache = void 0, this._graphGroupRenderCache = void 0;
4116
+ let o = this._selectedSources.map((e) => e.id).sort().join("|");
4117
+ this._lastFetchKey = `${o}|${i.getTime()}|${a.getTime()}`, this._lastFetchSources = [...this._selectedSources], this._data.setImportedSeries(t.series, i, a), this.dispatchEvent(new CustomEvent("data-imported", {
4118
+ detail: {
4119
+ start: i,
4120
+ end: a,
4121
+ seriesCount: t.series.length
4122
+ },
4123
+ bubbles: !0,
4124
+ composed: !0
4125
+ }));
4126
+ }
4127
+ _isExportPayload(e) {
4128
+ return typeof e == "object" && !!e && e.format === "ha-better-history-series-v1";
4129
+ }
4130
+ _parseImportedSeries(e) {
4131
+ let t = [], n = /* @__PURE__ */ new Map();
4132
+ for (let r of e) {
4133
+ if (typeof r != "object" || !r) return;
4134
+ let e = r, i = typeof e.id == "string" && e.id.trim() !== "" ? e.id : void 0, a = typeof e.entityId == "string" && e.entityId.trim() !== "" ? e.entityId : void 0, o = typeof e.label == "string" && e.label.trim() !== "" ? e.label : i, s = e.valueType === "number" || e.valueType === "boolean" || e.valueType === "string" ? e.valueType : void 0, c = Array.isArray(e.points) ? e.points : void 0;
4135
+ if (!i || !a || !o || !s || !c) return;
4136
+ let l = typeof e.attribute == "string" && e.attribute.trim() !== "" ? e.attribute : void 0, u = {
4137
+ id: i,
4138
+ kind: l ? "entity_attribute" : "entity_state",
4139
+ entityId: a,
4140
+ label: o,
4141
+ path: l?.split("."),
4142
+ valueType: s,
4143
+ unit: typeof e.unit == "string" ? e.unit : void 0
4144
+ }, d = c.map((e) => this._parseImportedPoint(e, s)).filter((e) => e !== void 0).sort((e, t) => e.time - t.time);
4145
+ t.push({
4146
+ source: u,
4147
+ points: d
4148
+ }), n.set(i, {
4149
+ color: typeof e.color == "string" && e.color.trim() !== "" ? e.color : void 0,
4150
+ lineMode: e.lineMode === "line" || e.lineMode === "column" || e.lineMode === "stair" ? e.lineMode : void 0
4151
+ });
4152
+ }
4153
+ return {
4154
+ series: t,
4155
+ meta: n
4156
+ };
4157
+ }
4158
+ _parseImportedPoint(e, t) {
4159
+ if (typeof e != "object" || !e) return;
4160
+ let n = e, r = Date.parse(String(n.timestamp ?? "")), i = n.value;
4161
+ if (Number.isFinite(r) && (t === "number" && typeof i == "number" && Number.isFinite(i) || t === "boolean" && typeof i == "boolean" || t === "string" && typeof i == "string")) return {
4162
+ time: r,
4163
+ value: i
4164
+ };
4165
+ }
4166
+ _parseDate(e) {
4167
+ if (typeof e != "string") return;
4168
+ let t = Date.parse(e);
4169
+ return Number.isFinite(t) ? new Date(t) : void 0;
4170
+ }
4171
+ _renderToolsPanel() {
4172
+ if (!this.toolsOpen || !this._resolved) return r;
4173
+ let e = this._effectiveViewRange(), t = this._rangePercent(this._viewStart, this._resolved.startDate), i = this._rangePercent(this._viewEnd, this._resolved.endDate), a = (t + i) / 20, o = this._defaultLineMode();
4174
+ return n`
4175
+ <div class="tools-panel">
4176
+ <div class="tool-range">
4177
+ <div class="tool-range-head">
4178
+ <span class="tool-label"><ha-icon .icon=${"mdi:timeline-clock-outline"}></ha-icon>${X(this.hass, "view_range")}</span>
4179
+ <button class="tool-icon-button" title=${X(this.hass, "reset_zoom")} @click=${() => this._resetViewRange()}>
4180
+ <ha-icon .icon=${"mdi:restore"}></ha-icon>
4181
+ </button>
4182
+ </div>
4183
+ <div class="range-values">
4184
+ <span>${this._formatRangeDate(e.start)}</span>
4185
+ <span>${this._formatRangeDate(e.end)}</span>
4186
+ </div>
4187
+ <div class="range-slider-stack">
4188
+ <div
4189
+ class="range-selection"
4190
+ style="left:${t / 10}%;right:${100 - i / 10}%;"
4191
+ ></div>
4192
+ <div
4193
+ class="range-selection-hit"
4194
+ style="left:clamp(18px, ${a}%, calc(100% - 18px));"
4195
+ @pointerdown=${(e) => this._onRangeSelectionPointerDown(e)}
4196
+ ></div>
4197
+ <input class="range-slider" type="range" min="0" max="1000" .value=${String(t)} @input=${(e) => this._setViewRangePart("start", e)} />
4198
+ <input class="range-slider" type="range" min="0" max="1000" .value=${String(i)} @input=${(e) => this._setViewRangePart("end", e)} />
4199
+ </div>
4200
+ </div>
4201
+ <div class="tool-actions">
4202
+ <div class="mode-switch" role="group" aria-label=${X(this.hass, "line_mode")}>
4203
+ ${[
4204
+ [
4205
+ "stair",
4206
+ "mdi:stairs",
4207
+ "mode_stair"
4208
+ ],
4209
+ [
4210
+ "line",
4211
+ "mdi:chart-line",
4212
+ "mode_line"
4213
+ ],
4214
+ [
4215
+ "column",
4216
+ "mdi:chart-bar",
4217
+ "mode_column"
4218
+ ]
4219
+ ].map(([e, t, r]) => n`
4220
+ <button
4221
+ class="mode-button"
4222
+ ?active=${o === e}
4223
+ title=${X(this.hass, r)}
4224
+ @click=${() => this._setRuntimeLineMode(e)}
4225
+ >
4226
+ <ha-icon .icon=${t}></ha-icon>
4227
+ </button>
4228
+ `)}
4229
+ </div>
4230
+ <button class="tool-action-button" @click=${() => this._exportData()}>
4231
+ <ha-icon .icon=${"mdi:download"}></ha-icon>
4232
+ <span>${X(this.hass, "export_data")}</span>
4233
+ </button>
4234
+ ${this._showImportButton() ? n`
4235
+ <button class="tool-action-button" @click=${() => this._importData()}>
4236
+ <ha-icon .icon=${"mdi:upload"}></ha-icon>
4237
+ <span>${X(this.hass, "import_data")}</span>
4238
+ </button>
4239
+ ` : r}
4240
+ </div>
4241
+ </div>
4242
+ `;
4243
+ }
4244
+ render() {
4245
+ let e = this._resolved?.width ?? "100%", t = this._resolved?.backgroundColor ?? "transparent", i = this._resolved?.title?.trim(), a = [
4246
+ this._resolved?.titleFontFamily ? `font-family:${this._resolved.titleFontFamily};` : "",
4247
+ this._resolved?.titleFontSize ? `font-size:${this._resolved.titleFontSize};` : "",
4248
+ this._resolved?.titleColor ? `color:${this._resolved.titleColor};` : ""
4249
+ ].join("");
4250
+ return n`
4251
+ <div class="root" style="width:${e};background:${t};">
4252
+ ${i ? n`<div class="graph-title" style=${a}>${i}</div>` : r}
4253
+ ${this.showControls ? n`<div class="controls-bar">
4254
+ ${this._renderDatePicker()}
4255
+ ${this._renderEntityPickerUI()}
4256
+ </div>` : r}
4257
+ ${this._renderToolsPanel()}
4258
+ <div class="chart-area">
4259
+ ${this._renderChartBody()}
4260
+ </div>
4261
+ </div>
4262
+ `;
4263
+ }
4264
+ _toggleSeries(e) {
4265
+ let t = !this._hiddenSeriesIds.includes(e);
4266
+ this._hiddenSeriesIds = t ? [...this._hiddenSeriesIds, e] : this._hiddenSeriesIds.filter((t) => t !== e), this.dispatchEvent(new CustomEvent("series-toggled", {
4267
+ detail: {
4268
+ id: e,
4269
+ hidden: t
4270
+ },
4271
+ bubbles: !0,
4272
+ composed: !0
4273
+ }));
4274
+ }
4275
+ _renderDatePicker() {
4276
+ return !this._resolved?.showDatePicker || !this._datePickerReady ? r : Tn(this.hass, this._resolved.startDate, this._resolved.endDate, (e, t) => this._onDateRangeChanged(e, t));
4277
+ }
4278
+ _positionEntityMenu() {
4279
+ let e = this.renderRoot?.querySelector(".entity-trigger"), t = this.renderRoot?.querySelector(".entity-menu");
4280
+ if (!e || !t) return;
4281
+ t.style.top = "0", t.style.left = "0", t.style.right = "", t.style.width = "";
4282
+ let n = t.getBoundingClientRect(), r = e.getBoundingClientRect(), i = this.getBoundingClientRect(), a = i.bottom - 8 - r.bottom - 8;
4283
+ t.style.maxHeight = `${Math.min(Math.max(a, 120), 420)}px`, t.style.top = `${r.bottom - n.top + 6}px`;
4284
+ let o = i.left + 8, s = i.right - 8, c = s - o, l = Math.min(420, c);
4285
+ t.style.width = `${l}px`;
4286
+ let u;
4287
+ window.matchMedia("(hover: hover) and (pointer: fine)").matches ? (u = r.left, u = Math.min(u, s - l), u = Math.max(u, o)) : (u = o, t.style.width = `${c}px`), t.style.left = `${u - n.left}px`, t.style.right = "";
4288
+ }
4289
+ _closeAttributeMenu() {
4290
+ this._attributeMenuOpen = !1, this._entityPickerOpen = !1, this._attributeSearch = "";
4291
+ }
4292
+ _onEntitySelected(e) {
4293
+ new Set(this._pickerEntities().map((e) => e.entity_id)).has(e) || (this._customEntityIds = [...this._customEntityIds, e]), this._selectedEntityId = e, this._path = [], this._attributeSearch = "", this._entityPickerOpen = !1, this._attributeMenuOpen = !0;
4294
+ }
4295
+ _onEntityPickerOpened() {
4296
+ this._entityPickerOpen = !0, this._attributeMenuOpen = !1;
4297
+ }
4298
+ _onEntityPickerClosed() {
4299
+ this._entityPickerOpen = !1;
4300
+ }
4301
+ _isEventInsideAttributeOverlay(e) {
4302
+ let t = e.composedPath(), n = this.renderRoot?.querySelector(".entity-menu");
4303
+ if (n && t.includes(n)) return !0;
4304
+ for (let e of t) {
4305
+ if (!(e instanceof HTMLElement)) continue;
4306
+ let t = e.localName;
4307
+ if (t === "ha-generic-picker" || t === "ha-combo-box" || t === "vaadin-combo-box-overlay" || t === "mwc-menu-surface" || t === "ha-md-list" || t === "md-menu") return !0;
4308
+ }
4309
+ return !1;
4310
+ }
4311
+ _sourceWithAttributeUnit(e) {
4312
+ if (e.kind !== "entity_attribute" || !e.path) return e;
4313
+ let t = Ut(e.path, this.attributeUnits ?? this.config?.attributeUnits), n = Ht(t) ? this._resolvedTemperatureUnit() ?? t : t;
4314
+ return !n || e.unit === n ? e : {
4315
+ ...e,
4316
+ unit: n
4317
+ };
4318
+ }
4319
+ _addSource(e) {
4320
+ if (this._selectedSources.some((t) => t.id === e.id) || this._pendingAddedSources.some((t) => t.id === e.id) || (this._resolved?.series ?? []).some((t) => t.id === e.id)) return;
4321
+ let t = this._sourceWithAttributeUnit(e);
4322
+ this._pendingAddedSources = [...this._pendingAddedSources, t], this.dispatchEvent(new CustomEvent("series-added", {
4323
+ detail: { source: t },
4324
+ bubbles: !0,
4325
+ composed: !0
4326
+ })), this._sourceAddBatchTimer !== void 0 && clearTimeout(this._sourceAddBatchTimer), this._sourceAddBatchTimer = setTimeout(() => this._flushPendingAddedSources(), Qn);
4327
+ }
4328
+ _flushPendingAddedSources() {
4329
+ if (this._sourceAddBatchTimer = void 0, this._pendingAddedSources.length === 0) return;
4330
+ let e = new Set(this._selectedSources.map((e) => e.id)), t = this._pendingAddedSources.filter((t) => !e.has(t.id));
4331
+ this._pendingAddedSources = [], t.length !== 0 && (this._selectedSources = [...this._selectedSources, ...t], this.requestUpdate());
4332
+ }
4333
+ _removeSource(e) {
4334
+ let t = this._selectedSources.find((t) => t.id === e);
4335
+ this._pendingAddedSources = this._pendingAddedSources.filter((t) => t.id !== e), !(!t || this._isDefaultSource(t)) && (this._selectedSources = this._selectedSources.filter((t) => t.id !== e), this._hiddenSeriesIds = this._hiddenSeriesIds.filter((t) => t !== e), this.dispatchEvent(new CustomEvent("series-removed", {
4336
+ detail: { sourceId: e },
4337
+ bubbles: !0,
4338
+ composed: !0
4339
+ })), this.requestUpdate());
4340
+ }
4341
+ _onSourceDragStart(e, t) {
4342
+ let n = this._selectedSources.find((t) => t.id === e);
4343
+ if (!n || this._isDefaultSource(n)) {
4344
+ t.preventDefault();
4345
+ return;
4346
+ }
4347
+ this._draggingSourceId = e, this._dragStartSourceIds = this._selectedSources.map((e) => e.id), this._dragDropCommitted = !1, t.dataTransfer?.setData("text/plain", e), t.dataTransfer && (t.dataTransfer.effectAllowed = "move");
4348
+ }
4349
+ _onSourceDragEnd() {
4350
+ if (!this._dragDropCommitted && this._dragStartSourceIds) {
4351
+ let e = new Map(this._dragStartSourceIds.map((e, t) => [e, t]));
4352
+ this._selectedSources = [...this._selectedSources].sort((t, n) => (e.get(t.id) ?? 2 ** 53 - 1) - (e.get(n.id) ?? 2 ** 53 - 1));
4353
+ }
4354
+ this._draggingSourceId = void 0, this._dragStartSourceIds = void 0, this._dragDropCommitted = !1;
4355
+ }
4356
+ _onSourceDragOver(e, t) {
4357
+ t.preventDefault(), t.stopPropagation(), t.dataTransfer && (t.dataTransfer.dropEffect = "move");
4358
+ let n = this._draggingSourceId ?? t.dataTransfer?.getData("text/plain");
4359
+ n && this._previewSourceOrder(n, e);
4360
+ }
4361
+ _onSourceDrop(e, t) {
4362
+ t.preventDefault(), t.stopPropagation();
4363
+ let n = this._draggingSourceId ?? t.dataTransfer?.getData("text/plain");
4364
+ n && (this._previewSourceOrder(n, e), this._dragDropCommitted = !0, this.dispatchEvent(new CustomEvent("series-reordered", {
4365
+ detail: { sourceIds: this._selectedSources.map((e) => e.id) },
4366
+ bubbles: !0,
4367
+ composed: !0
4368
+ })), this.requestUpdate());
4369
+ }
4370
+ _previewSourceOrder(e, t) {
4371
+ if (e === t) return;
4372
+ let n = this._selectedSources.find((t) => t.id === e);
4373
+ if (!n || this._isDefaultSource(n)) return;
4374
+ let r = this._selectedSources.filter((t) => t.id !== e), i = t ? r.findIndex((e) => e.id === t) : r.length;
4375
+ if (i < 0) return;
4376
+ let a = [
4377
+ ...r.slice(0, i),
4378
+ n,
4379
+ ...r.slice(i)
4380
+ ];
4381
+ a.map((e) => e.id).join("|") !== this._selectedSources.map((e) => e.id).join("|") && (this._selectedSources = a, this.requestUpdate());
4382
+ }
4383
+ };
4384
+ Q([a({ attribute: !1 })], $.prototype, "hass", void 0), Q([a({ attribute: !1 })], $.prototype, "config", void 0), Q([a({ attribute: !1 })], $.prototype, "entities", void 0), Q([a({ attribute: !1 })], $.prototype, "attributeUnits", void 0), Q([a({ type: Number })], $.prototype, "hours", void 0), Q([a({ attribute: !1 })], $.prototype, "startDate", void 0), Q([a({ attribute: !1 })], $.prototype, "endDate", void 0), Q([a({
4385
+ type: Boolean,
4386
+ attribute: "show-date-picker"
4387
+ })], $.prototype, "showDatePicker", void 0), Q([a({
4388
+ type: Boolean,
4389
+ attribute: "show-entity-picker"
4390
+ })], $.prototype, "showEntityPicker", void 0), Q([a({
4391
+ type: Boolean,
4392
+ attribute: "show-import-button"
4393
+ })], $.prototype, "showImportButton", void 0), Q([a({
4394
+ type: Boolean,
4395
+ attribute: "show-legend"
4396
+ })], $.prototype, "showLegend", void 0), Q([a({
4397
+ type: Boolean,
4398
+ attribute: "show-tooltip"
4399
+ })], $.prototype, "showTooltip", void 0), Q([a({
4400
+ type: Boolean,
4401
+ attribute: "show-controls"
4402
+ })], $.prototype, "showControls", void 0), Q([a()], $.prototype, "width", void 0), Q([a()], $.prototype, "height", void 0), Q([a({ attribute: "line-mode" })], $.prototype, "lineMode", void 0), Q([a({ attribute: "line-width" })], $.prototype, "lineWidth", void 0), Q([a({ attribute: "background-color" })], $.prototype, "backgroundColor", void 0), Q([a({ attribute: "graph-title" })], $.prototype, "graphTitle", void 0), Q([a({ attribute: "title-font-family" })], $.prototype, "titleFontFamily", void 0), Q([a({ attribute: "title-font-size" })], $.prototype, "titleFontSize", void 0), Q([a({ attribute: "title-color" })], $.prototype, "titleColor", void 0), Q([a()], $.prototype, "language", void 0), Q([a({
4403
+ type: Boolean,
4404
+ attribute: "debug-performance"
4405
+ })], $.prototype, "debugPerformance", void 0), Q([a({
4406
+ type: Boolean,
4407
+ attribute: "tools-open"
4408
+ })], $.prototype, "toolsOpen", void 0), Q([o()], $.prototype, "_resolved", void 0), Q([o()], $.prototype, "_hiddenSeriesIds", void 0), Q([o()], $.prototype, "_rangeStart", void 0), Q([o()], $.prototype, "_rangeEnd", void 0), Q([o()], $.prototype, "_viewStart", void 0), Q([o()], $.prototype, "_viewEnd", void 0), Q([o()], $.prototype, "_liveNow", void 0), Q([o()], $.prototype, "_datePickerReady", void 0), Q([o()], $.prototype, "_entityComponentsReady", void 0), Q([o()], $.prototype, "_runtimeLineMode", void 0), Q([o()], $.prototype, "_attributeMenuOpen", void 0), Q([o()], $.prototype, "_attributeSearch", void 0), Q([o()], $.prototype, "_selectedEntityId", void 0), Q([o()], $.prototype, "_path", void 0), Q([o()], $.prototype, "_selectedSources", void 0), Q([o()], $.prototype, "_customEntityIds", void 0), Q([o()], $.prototype, "_entityPickerOpen", void 0), Q([o()], $.prototype, "_draggingSourceId", void 0), Q([o()], $.prototype, "_containerWidth", void 0);
4409
+ //#endregion
4410
+ export { $ as t };