@glasshome/sync-layer 0.7.2 → 0.8.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.
- package/dist/index.d.ts +1291 -54
- package/dist/index.js +203 -304
- package/dist/service-C8H42I-Q.d.ts +459 -0
- package/dist/solid/index.d.ts +119 -6
- package/dist/solid/index.js +20 -15
- package/dist/{statistics-CtRfIF6D.js → statistics-DyCCTwv2.js} +83 -95
- package/dist/worker/worker-main.d.ts +11 -8
- package/dist/worker/worker-main.js +2 -3
- package/dist/{worker-main-C-3QM4di.js → worker-main-D_wyGAIG.js} +47 -157
- package/package.json +3 -3
- package/dist/calendar/track.d.ts +0 -19
- package/dist/calendar/types.d.ts +0 -32
- package/dist/camera/fetch.d.ts +0 -16
- package/dist/camera/query.d.ts +0 -15
- package/dist/camera/types.d.ts +0 -85
- package/dist/camera/webrtc.d.ts +0 -31
- package/dist/commands/service.d.ts +0 -53
- package/dist/commands/types.d.ts +0 -113
- package/dist/connection/adapter.d.ts +0 -48
- package/dist/connection/auth.d.ts +0 -126
- package/dist/connection/bridged.d.ts +0 -16
- package/dist/connection/manager.d.ts +0 -51
- package/dist/connection/subscription-manager.d.ts +0 -48
- package/dist/connection/subscriptions.d.ts +0 -31
- package/dist/connection/types.d.ts +0 -151
- package/dist/core/privileged-conn.d.ts +0 -29
- package/dist/core/reducers.d.ts +0 -99
- package/dist/core/store.d.ts +0 -100
- package/dist/core/types.d.ts +0 -339
- package/dist/demo/demo-data.d.ts +0 -24
- package/dist/demo/demo-provider.d.ts +0 -40
- package/dist/demo/energy-sim.d.ts +0 -112
- package/dist/energy/prefs.d.ts +0 -56
- package/dist/entities/area-views.d.ts +0 -32
- package/dist/entities/index.d.ts +0 -54
- package/dist/entities/views.d.ts +0 -35
- package/dist/history/constants.d.ts +0 -16
- package/dist/history/fetch.d.ts +0 -19
- package/dist/history/query.d.ts +0 -27
- package/dist/history/statistics.d.ts +0 -55
- package/dist/history/types.d.ts +0 -146
- package/dist/host-api.d.ts +0 -50
- package/dist/media/url.d.ts +0 -14
- package/dist/query/area-builder.d.ts +0 -25
- package/dist/query/builder.d.ts +0 -18
- package/dist/query/helpers.d.ts +0 -21
- package/dist/query/memoization.d.ts +0 -18
- package/dist/query/projections.d.ts +0 -74
- package/dist/query/types.d.ts +0 -260
- package/dist/solid/hooks.d.ts +0 -119
- package/dist/solid/indices.d.ts +0 -15
- package/dist/weather/fetch.d.ts +0 -19
- package/dist/weather/query.d.ts +0 -23
- package/dist/weather/types.d.ts +0 -126
- package/dist/worker/bridge-client.d.ts +0 -47
- package/dist/worker/enforcement.d.ts +0 -53
- package/dist/worker/protocol.d.ts +0 -129
- package/dist/worker/token-store.d.ts +0 -3
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createStore, produce, reconcile } from "solid-js/store";
|
|
2
|
-
|
|
3
2
|
//#region src/core/store.ts
|
|
4
3
|
const initialState = {
|
|
5
4
|
connectionState: "disconnected",
|
|
@@ -25,7 +24,6 @@ globalThis.__GH_SYNC_LAYER_STORE__ = true;
|
|
|
25
24
|
const storeTuple = createStore(initialState);
|
|
26
25
|
const state = storeTuple[0];
|
|
27
26
|
const setState = storeTuple[1];
|
|
28
|
-
|
|
29
27
|
//#endregion
|
|
30
28
|
//#region src/core/privileged-conn.ts
|
|
31
29
|
let privileged = null;
|
|
@@ -34,10 +32,9 @@ function privilegedConn() {
|
|
|
34
32
|
return privileged;
|
|
35
33
|
}
|
|
36
34
|
/** Set by the connection layer on connect/disconnect. Host-side callers only. */
|
|
37
|
-
function setPrivilegedConn(conn
|
|
38
|
-
privileged = conn
|
|
35
|
+
function setPrivilegedConn(conn) {
|
|
36
|
+
privileged = conn;
|
|
39
37
|
}
|
|
40
|
-
|
|
41
38
|
//#endregion
|
|
42
39
|
//#region src/calendar/track.ts
|
|
43
40
|
/**
|
|
@@ -53,7 +50,7 @@ const DEFAULT_DAYS_BEHIND = 7;
|
|
|
53
50
|
const DEFAULT_DAYS_AHEAD = 35;
|
|
54
51
|
/** Debounce for untrack, so dashboard switches don't churn subscriptions. */
|
|
55
52
|
const UNTRACK_FLUSH_DELAY_MS = 5e3;
|
|
56
|
-
const DAY_MS =
|
|
53
|
+
const DAY_MS = 864e5;
|
|
57
54
|
const trackers = /* @__PURE__ */ new Map();
|
|
58
55
|
const active = /* @__PURE__ */ new Map();
|
|
59
56
|
/** Per-entity reconcile chain; serializes subscribe/unsubscribe per calendar. */
|
|
@@ -72,7 +69,7 @@ function windowFor(list) {
|
|
|
72
69
|
const dayStart = /* @__PURE__ */ new Date();
|
|
73
70
|
dayStart.setHours(0, 0, 0, 0);
|
|
74
71
|
return {
|
|
75
|
-
start: new Date(dayStart.getTime() - behind * DAY_MS).toISOString(),
|
|
72
|
+
start: (/* @__PURE__ */ new Date(dayStart.getTime() - behind * DAY_MS)).toISOString(),
|
|
76
73
|
end: new Date(dayStart.getTime() + (ahead + 1) * DAY_MS).toISOString()
|
|
77
74
|
};
|
|
78
75
|
}
|
|
@@ -132,7 +129,7 @@ function scheduleFlush$1() {
|
|
|
132
129
|
});
|
|
133
130
|
}
|
|
134
131
|
async function flushAll() {
|
|
135
|
-
const ids = new Set([...trackers.keys(), ...active.keys()]);
|
|
132
|
+
const ids = /* @__PURE__ */ new Set([...trackers.keys(), ...active.keys()]);
|
|
136
133
|
await Promise.all([...ids].map(reconcileEntity));
|
|
137
134
|
if (trackers.size === 0) stopRollTimer();
|
|
138
135
|
}
|
|
@@ -157,8 +154,8 @@ async function runReconcile(entityId) {
|
|
|
157
154
|
}));
|
|
158
155
|
return;
|
|
159
156
|
}
|
|
160
|
-
const conn
|
|
161
|
-
if (!conn
|
|
157
|
+
const conn = privilegedConn();
|
|
158
|
+
if (!conn) return;
|
|
162
159
|
const win = windowFor(list);
|
|
163
160
|
if (current?.unsub && current.start === win.start && current.end === win.end) return;
|
|
164
161
|
setState(produce((s) => {
|
|
@@ -179,7 +176,7 @@ async function runReconcile(entityId) {
|
|
|
179
176
|
};
|
|
180
177
|
active.set(entityId, entry);
|
|
181
178
|
try {
|
|
182
|
-
entry.unsub = await conn
|
|
179
|
+
entry.unsub = await conn.subscribeMessage((msg) => {
|
|
183
180
|
setState(produce((s) => {
|
|
184
181
|
const rec = s.calendars[entityId];
|
|
185
182
|
if (!rec) return;
|
|
@@ -211,13 +208,13 @@ windows shift, so a reconcile slides every subscription forward. Only
|
|
|
211
208
|
armed while something is tracked — an idle app holds no timer. */
|
|
212
209
|
function ensureRollTimer() {
|
|
213
210
|
if (rollTimer || trackers.size === 0) return;
|
|
214
|
-
const now
|
|
215
|
-
const next = new Date(now
|
|
211
|
+
const now = /* @__PURE__ */ new Date();
|
|
212
|
+
const next = new Date(now);
|
|
216
213
|
next.setHours(24, 0, 30, 0);
|
|
217
214
|
rollTimer = setTimeout(() => {
|
|
218
215
|
rollTimer = null;
|
|
219
216
|
flushAll().then(ensureRollTimer);
|
|
220
|
-
}, next.getTime() - now
|
|
217
|
+
}, next.getTime() - now.getTime());
|
|
221
218
|
}
|
|
222
219
|
function stopRollTimer() {
|
|
223
220
|
if (rollTimer) {
|
|
@@ -225,7 +222,6 @@ function stopRollTimer() {
|
|
|
225
222
|
rollTimer = null;
|
|
226
223
|
}
|
|
227
224
|
}
|
|
228
|
-
|
|
229
225
|
//#endregion
|
|
230
226
|
//#region src/connection/subscription-manager.ts
|
|
231
227
|
if (globalThis.__GH_SYNC_LAYER_SUB_MGR__) console.error("[@glasshome/sync-layer] DUPLICATE subscription-manager module instantiated. registerEntity() and the connection setter will hit different singletons → subscribe_entities will never be sent. Cause: relative import of subscription-manager from a separate entry point.");
|
|
@@ -322,7 +318,6 @@ function setsEqual(a, b) {
|
|
|
322
318
|
for (const id of a) if (!b.has(id)) return false;
|
|
323
319
|
return true;
|
|
324
320
|
}
|
|
325
|
-
|
|
326
321
|
//#endregion
|
|
327
322
|
//#region src/core/types.ts
|
|
328
323
|
/**
|
|
@@ -331,7 +326,6 @@ function setsEqual(a, b) {
|
|
|
331
326
|
function extractDomain(entityId) {
|
|
332
327
|
return entityId.split(".")[0] ?? "";
|
|
333
328
|
}
|
|
334
|
-
|
|
335
329
|
//#endregion
|
|
336
330
|
//#region src/demo/energy-sim.ts
|
|
337
331
|
const SOLAR_PEAK_W = 5200;
|
|
@@ -392,27 +386,24 @@ function cloudFactor(lt) {
|
|
|
392
386
|
const phase2 = rand() * Math.PI * 2;
|
|
393
387
|
const phase3 = rand() * Math.PI * 2;
|
|
394
388
|
const m = lt.minuteOfDay;
|
|
395
|
-
const
|
|
396
|
-
const f = .85 + wave;
|
|
389
|
+
const f = .85 + (.16 * Math.sin(m / 37 * Math.PI * 2 + phase1) + .14 * Math.sin(m / 73 * Math.PI * 2 + phase2) + .05 * Math.sin(m / 11 * Math.PI * 2 + phase3));
|
|
397
390
|
return Math.max(.55, Math.min(1, f));
|
|
398
391
|
}
|
|
399
392
|
function solarPower(lt) {
|
|
400
393
|
if (lt.hourFrac <= SUNRISE_HOUR || lt.hourFrac >= SUNSET_HOUR) return 0;
|
|
401
|
-
const frac = (lt.hourFrac - SUNRISE_HOUR) /
|
|
394
|
+
const frac = (lt.hourFrac - SUNRISE_HOUR) / 14;
|
|
402
395
|
const shape = Math.sin(frac * Math.PI) ** 1.3;
|
|
403
396
|
return SOLAR_PEAK_W * shape * cloudFactor(lt);
|
|
404
397
|
}
|
|
405
398
|
/** Coarse solar elevation in degrees for sun.sun attributes. */
|
|
406
399
|
function sunElevation(lt) {
|
|
407
400
|
if (lt.hourFrac <= SUNRISE_HOUR || lt.hourFrac >= SUNSET_HOUR) return -12;
|
|
408
|
-
const frac = (lt.hourFrac - SUNRISE_HOUR) /
|
|
401
|
+
const frac = (lt.hourFrac - SUNRISE_HOUR) / 14;
|
|
409
402
|
return 60 * Math.sin(frac * Math.PI);
|
|
410
403
|
}
|
|
411
404
|
function fridgePower(lt) {
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
const pos = (lt.minuteOfDay + phase) % FRIDGE_CYCLE_MIN;
|
|
415
|
-
return pos < FRIDGE_ON_MIN ? FRIDGE_W : 0;
|
|
405
|
+
const phase = mulberry32(hashInt(lt.dayKey * 40503 + 7))() * FRIDGE_CYCLE_MIN;
|
|
406
|
+
return (lt.minuteOfDay + phase) % FRIDGE_CYCLE_MIN < FRIDGE_ON_MIN ? FRIDGE_W : 0;
|
|
416
407
|
}
|
|
417
408
|
/** A square wave: `power` while inside [start, start+durationMin). */
|
|
418
409
|
function windowPower(minuteOfDay, startMin, durationMin, power) {
|
|
@@ -423,14 +414,12 @@ function kettlePower(lt) {
|
|
|
423
414
|
}
|
|
424
415
|
function ovenPower(lt) {
|
|
425
416
|
const start = 1125;
|
|
426
|
-
if (lt.minuteOfDay < start || lt.minuteOfDay >=
|
|
427
|
-
|
|
428
|
-
const cyclePos = into % 6.5;
|
|
429
|
-
return cyclePos < 4 ? 2200 : 0;
|
|
417
|
+
if (lt.minuteOfDay < start || lt.minuteOfDay >= 1165) return 0;
|
|
418
|
+
return (lt.minuteOfDay - start) % 6.5 < 4 ? 2200 : 0;
|
|
430
419
|
}
|
|
431
420
|
function dishwasherPower(lt) {
|
|
432
421
|
const start = 1270;
|
|
433
|
-
if (lt.minuteOfDay < start || lt.minuteOfDay >=
|
|
422
|
+
if (lt.minuteOfDay < start || lt.minuteOfDay >= 1335) return 0;
|
|
434
423
|
const into = lt.minuteOfDay - start;
|
|
435
424
|
if (into < 12) return 1900;
|
|
436
425
|
if (into >= 57) return 1900;
|
|
@@ -443,9 +432,8 @@ function washerPower(lt) {
|
|
|
443
432
|
6
|
|
444
433
|
].includes(lt.dayOfWeek)) return 0;
|
|
445
434
|
const start = 630;
|
|
446
|
-
if (lt.minuteOfDay < start || lt.minuteOfDay >=
|
|
447
|
-
|
|
448
|
-
return into < 14 ? 2100 : 250;
|
|
435
|
+
if (lt.minuteOfDay < start || lt.minuteOfDay >= 680) return 0;
|
|
436
|
+
return lt.minuteOfDay - start < 14 ? 2100 : 250;
|
|
449
437
|
}
|
|
450
438
|
function evChargerPower(lt) {
|
|
451
439
|
if (![
|
|
@@ -454,11 +442,9 @@ function evChargerPower(lt) {
|
|
|
454
442
|
0
|
|
455
443
|
].includes(lt.dayOfWeek)) return 0;
|
|
456
444
|
const start = 1100;
|
|
457
|
-
|
|
458
|
-
if (lt.minuteOfDay < start || lt.minuteOfDay >= stop) return 0;
|
|
445
|
+
if (lt.minuteOfDay < start || lt.minuteOfDay >= 1300) return 0;
|
|
459
446
|
const into = lt.minuteOfDay - start;
|
|
460
|
-
|
|
461
|
-
return 7200 * ramp;
|
|
447
|
+
return 7200 * (into < 2 ? into / 2 : 1);
|
|
462
448
|
}
|
|
463
449
|
const HEAT_PUMP_PEAK_W = 3200;
|
|
464
450
|
/**
|
|
@@ -487,17 +473,14 @@ function occupancyEnvelope(lt) {
|
|
|
487
473
|
}
|
|
488
474
|
/** Low-amplitude smooth noise multiplier, ±4%, seeded per day. */
|
|
489
475
|
function homeNoise(lt) {
|
|
490
|
-
const
|
|
491
|
-
const phase = rand() * Math.PI * 2;
|
|
476
|
+
const phase = mulberry32(hashInt(lt.dayKey * 22699 + 13))() * Math.PI * 2;
|
|
492
477
|
return 1 + .04 * Math.sin(lt.minuteOfDay / 23 * Math.PI * 2 + phase);
|
|
493
478
|
}
|
|
494
479
|
const socCache = /* @__PURE__ */ new Map();
|
|
495
480
|
/** Net surplus (solar - home) at a given local-time sample, in watts. */
|
|
496
481
|
function netSurplusAt(timestampMs) {
|
|
497
482
|
const lt = localTime(timestampMs);
|
|
498
|
-
|
|
499
|
-
const home = homePower(lt);
|
|
500
|
-
return solar - home;
|
|
483
|
+
return solarPower(lt) - homePower(lt);
|
|
501
484
|
}
|
|
502
485
|
/**
|
|
503
486
|
* Battery SOC (%) at every {@link SOC_STEP_MIN}-minute step from local
|
|
@@ -507,28 +490,24 @@ function netSurplusAt(timestampMs) {
|
|
|
507
490
|
function socPrefix(dayKey, midnightMs) {
|
|
508
491
|
const cached = socCache.get(dayKey);
|
|
509
492
|
if (cached) return cached;
|
|
510
|
-
const steps = Math.ceil(
|
|
511
|
-
const arr = new Float64Array(
|
|
493
|
+
const steps = Math.ceil(1440 / SOC_STEP_MIN);
|
|
494
|
+
const arr = /* @__PURE__ */ new Float64Array(289);
|
|
512
495
|
arr[0] = MIDNIGHT_SOC;
|
|
513
496
|
const stepMs = SOC_STEP_MIN * 6e4;
|
|
514
497
|
const stepHours = SOC_STEP_MIN / 60;
|
|
515
498
|
for (let i = 0; i < steps; i++) {
|
|
516
499
|
const soc = arr[i] ?? MIDNIGHT_SOC;
|
|
517
|
-
const
|
|
518
|
-
const surplus = netSurplusAt(sampleMs);
|
|
500
|
+
const surplus = netSurplusAt(midnightMs + i * stepMs);
|
|
519
501
|
let chargeW = 0;
|
|
520
502
|
let dischargeW = 0;
|
|
521
503
|
if (surplus > 0) {
|
|
522
|
-
const
|
|
523
|
-
const headroomW = headroomWh / stepHours;
|
|
504
|
+
const headroomW = (BATTERY_MAX_SOC - soc) / 100 * BATTERY_CAPACITY_WH / stepHours;
|
|
524
505
|
chargeW = Math.min(surplus, BATTERY_POWER_CAP_W, Math.max(0, headroomW));
|
|
525
506
|
} else if (surplus < 0) {
|
|
526
|
-
const
|
|
527
|
-
const availW = availWh / stepHours;
|
|
507
|
+
const availW = (soc - BATTERY_MIN_SOC) / 100 * BATTERY_CAPACITY_WH / stepHours;
|
|
528
508
|
dischargeW = Math.min(-surplus, BATTERY_POWER_CAP_W, Math.max(0, availW));
|
|
529
509
|
}
|
|
530
|
-
const
|
|
531
|
-
const nextSoc = soc + deltaWh / BATTERY_CAPACITY_WH * 100;
|
|
510
|
+
const nextSoc = soc + (chargeW - dischargeW) * stepHours / BATTERY_CAPACITY_WH * 100;
|
|
532
511
|
arr[i + 1] = Math.max(BATTERY_MIN_SOC, Math.min(BATTERY_MAX_SOC, nextSoc));
|
|
533
512
|
}
|
|
534
513
|
socCache.set(dayKey, arr);
|
|
@@ -541,12 +520,10 @@ function batterySoc(lt, midnightMs) {
|
|
|
541
520
|
const i = Math.floor(stepFloat);
|
|
542
521
|
const frac = stepFloat - i;
|
|
543
522
|
const a = prefix[i] ?? MIDNIGHT_SOC;
|
|
544
|
-
|
|
545
|
-
return a + (b - a) * frac;
|
|
523
|
+
return a + ((prefix[i + 1] ?? a) - a) * frac;
|
|
546
524
|
}
|
|
547
525
|
function homePower(lt) {
|
|
548
|
-
|
|
549
|
-
return base * homeNoise(lt);
|
|
526
|
+
return (ALWAYS_ON_W + fridgePower(lt) + kettlePower(lt) + ovenPower(lt) + dishwasherPower(lt) + washerPower(lt) + evChargerPower(lt) + heatPumpPower(lt) + occupancyEnvelope(lt)) * homeNoise(lt);
|
|
550
527
|
}
|
|
551
528
|
/**
|
|
552
529
|
* Compute every instantaneous energy value at a timestamp.
|
|
@@ -571,10 +548,8 @@ function simulateEnergy(timestampMs) {
|
|
|
571
548
|
const surplus = solarW - homeW;
|
|
572
549
|
let batteryChargeW = 0;
|
|
573
550
|
let batteryDischargeW = 0;
|
|
574
|
-
if (surplus > 0)
|
|
575
|
-
|
|
576
|
-
batteryChargeW = Math.min(surplus, headroom);
|
|
577
|
-
} else if (surplus < 0) {
|
|
551
|
+
if (surplus > 0) batteryChargeW = Math.min(surplus, soc < BATTERY_MAX_SOC ? BATTERY_POWER_CAP_W : 0);
|
|
552
|
+
else if (surplus < 0) {
|
|
578
553
|
const avail = soc > BATTERY_MIN_SOC ? BATTERY_POWER_CAP_W : 0;
|
|
579
554
|
batteryDischargeW = Math.min(-surplus, avail);
|
|
580
555
|
}
|
|
@@ -648,7 +623,7 @@ function energyEntityValue(entityId, sample) {
|
|
|
648
623
|
case "sensor.oven_power": return sample.ovenW;
|
|
649
624
|
case "sensor.ev_charger_power": return sample.evChargerW;
|
|
650
625
|
case "sensor.always_on_power": return sample.alwaysOnW;
|
|
651
|
-
default: return
|
|
626
|
+
default: return;
|
|
652
627
|
}
|
|
653
628
|
}
|
|
654
629
|
/** Whether an entity id is one of the simulated energy sensors. */
|
|
@@ -705,7 +680,7 @@ function bucketMs(period) {
|
|
|
705
680
|
*/
|
|
706
681
|
function synthesizeEnergyStatistics(entityId, startMs, endMs, period) {
|
|
707
682
|
const span = bucketMs(period);
|
|
708
|
-
const sampleMs =
|
|
683
|
+
const sampleMs = 3e5;
|
|
709
684
|
const buckets = [];
|
|
710
685
|
let cumulativeWh = 0;
|
|
711
686
|
for (let bStart = startMs; bStart < endMs; bStart += span) {
|
|
@@ -786,7 +761,6 @@ function demoEnergyPreferences() {
|
|
|
786
761
|
]
|
|
787
762
|
};
|
|
788
763
|
}
|
|
789
|
-
|
|
790
764
|
//#endregion
|
|
791
765
|
//#region src/demo/demo-data.ts
|
|
792
766
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -798,10 +772,10 @@ function ctx() {
|
|
|
798
772
|
user_id: null
|
|
799
773
|
};
|
|
800
774
|
}
|
|
801
|
-
function makeEntity(entity_id, state
|
|
775
|
+
function makeEntity(entity_id, state, attributes = {}) {
|
|
802
776
|
return {
|
|
803
777
|
entity_id,
|
|
804
|
-
state
|
|
778
|
+
state,
|
|
805
779
|
attributes: {
|
|
806
780
|
friendly_name: friendlyName(entity_id),
|
|
807
781
|
...attributes
|
|
@@ -1008,8 +982,7 @@ function makeWeatherEntity(w) {
|
|
|
1008
982
|
});
|
|
1009
983
|
}
|
|
1010
984
|
function friendlyName(entityId) {
|
|
1011
|
-
|
|
1012
|
-
return objectId.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
985
|
+
return (entityId.split(".")[1] ?? entityId).split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
1013
986
|
}
|
|
1014
987
|
function makeRegistryEntry(entity_id, overrides = {}) {
|
|
1015
988
|
const domain = entity_id.split(".")[0] ?? "";
|
|
@@ -1487,7 +1460,7 @@ function createDemoFixtures() {
|
|
|
1487
1460
|
next_rising: sun.nextRising,
|
|
1488
1461
|
next_setting: sun.nextSetting
|
|
1489
1462
|
}), {});
|
|
1490
|
-
const
|
|
1463
|
+
for (const s of [
|
|
1491
1464
|
{
|
|
1492
1465
|
id: "sensor.solar_power",
|
|
1493
1466
|
name: "Solar Production",
|
|
@@ -1566,8 +1539,7 @@ function createDemoFixtures() {
|
|
|
1566
1539
|
deviceClass: "power",
|
|
1567
1540
|
unit: "W"
|
|
1568
1541
|
}
|
|
1569
|
-
]
|
|
1570
|
-
for (const s of powerSensors) {
|
|
1542
|
+
]) {
|
|
1571
1543
|
const value = energyEntityValue(s.id, sample) ?? 0;
|
|
1572
1544
|
add(makeEntity(s.id, formatEnergyState(s.id, value), {
|
|
1573
1545
|
friendly_name: s.name,
|
|
@@ -1587,9 +1559,16 @@ function createDemoFixtures() {
|
|
|
1587
1559
|
devices
|
|
1588
1560
|
};
|
|
1589
1561
|
}
|
|
1590
|
-
|
|
1591
1562
|
//#endregion
|
|
1592
1563
|
//#region src/demo/demo-provider.ts
|
|
1564
|
+
/**
|
|
1565
|
+
* Demo Mode Provider
|
|
1566
|
+
*
|
|
1567
|
+
* Loads demo fixtures into the SolidJS store and provides
|
|
1568
|
+
* optimistic local state updates for service calls (toggle, turnOn, turnOff).
|
|
1569
|
+
*
|
|
1570
|
+
* @packageDocumentation
|
|
1571
|
+
*/
|
|
1593
1572
|
let _isDemoMode = false;
|
|
1594
1573
|
/**
|
|
1595
1574
|
* Whether demo mode is currently active.
|
|
@@ -1685,21 +1664,21 @@ function applyDemoServiceCall(_domain, service, _serviceData = {}, target = {})
|
|
|
1685
1664
|
if (!_isDemoMode) return;
|
|
1686
1665
|
const entityIds = Array.isArray(target.entity_id) ? target.entity_id : target.entity_id ? [target.entity_id] : [];
|
|
1687
1666
|
for (const entityId of entityIds) {
|
|
1688
|
-
|
|
1689
|
-
if (!entity) continue;
|
|
1667
|
+
if (!state.entities[entityId]) continue;
|
|
1690
1668
|
const entityDomain = extractDomain(entityId);
|
|
1691
1669
|
setState(produce((s) => {
|
|
1692
1670
|
const e = s.entities[entityId];
|
|
1693
1671
|
if (!e) return;
|
|
1694
|
-
const now
|
|
1695
|
-
e.last_changed = now
|
|
1696
|
-
e.last_updated = now
|
|
1672
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1673
|
+
e.last_changed = now;
|
|
1674
|
+
e.last_updated = now;
|
|
1697
1675
|
if (service === "turn_on") applyTurnOn(e, entityDomain, _serviceData);
|
|
1698
|
-
else if (service === "press" && entityDomain === "button") e.state = now
|
|
1676
|
+
else if (service === "press" && entityDomain === "button") e.state = now;
|
|
1699
1677
|
else if (service === "turn_off") applyTurnOff(e, entityDomain);
|
|
1700
|
-
else if (service === "toggle")
|
|
1701
|
-
|
|
1702
|
-
|
|
1678
|
+
else if (service === "toggle") {
|
|
1679
|
+
if (e.state === "on" || e.state === "playing" || e.state === "open") applyTurnOff(e, entityDomain);
|
|
1680
|
+
else applyTurnOn(e, entityDomain, _serviceData);
|
|
1681
|
+
} else if (service === "lock") e.state = "locked";
|
|
1703
1682
|
else if (service === "unlock") e.state = "unlocked";
|
|
1704
1683
|
else if (service === "open_cover") {
|
|
1705
1684
|
e.state = "open";
|
|
@@ -1783,7 +1762,6 @@ function applyTurnOff(e, domain) {
|
|
|
1783
1762
|
if ("current_position" in e.attributes) e.attributes.current_position = 0;
|
|
1784
1763
|
} else e.state = "off";
|
|
1785
1764
|
}
|
|
1786
|
-
|
|
1787
1765
|
//#endregion
|
|
1788
1766
|
//#region src/commands/service.ts
|
|
1789
1767
|
/**
|
|
@@ -1882,7 +1860,6 @@ async function batchServiceCalls(calls) {
|
|
|
1882
1860
|
async function batchEntityUpdates(updates) {
|
|
1883
1861
|
return Promise.all(updates.map((u) => updateEntity(u.entityId, u.updates)));
|
|
1884
1862
|
}
|
|
1885
|
-
|
|
1886
1863
|
//#endregion
|
|
1887
1864
|
//#region src/entities/views.ts
|
|
1888
1865
|
/**
|
|
@@ -1905,10 +1882,10 @@ function resolveIcon(entity, registry) {
|
|
|
1905
1882
|
function buildEntityView(entity, registry) {
|
|
1906
1883
|
const domain = extractDomain(entity.entity_id);
|
|
1907
1884
|
const attributes = entity.attributes ?? {};
|
|
1908
|
-
const friendlyName
|
|
1909
|
-
const name = registry?.name ?? friendlyName
|
|
1885
|
+
const friendlyName = attributes.friendly_name ?? registry?.name ?? entity.entity_id;
|
|
1886
|
+
const name = registry?.name ?? friendlyName;
|
|
1910
1887
|
const iconResolution = resolveIcon(entity, registry);
|
|
1911
|
-
const { device_class: _dc, unit_of_measurement: _uom, friendly_name: _fn, icon: _icon
|
|
1888
|
+
const { device_class: _dc, unit_of_measurement: _uom, friendly_name: _fn, icon: _icon, ...restAttributes } = attributes;
|
|
1912
1889
|
let areaId = registry?.area_id ?? null;
|
|
1913
1890
|
if (areaId === null && registry?.device_id) {
|
|
1914
1891
|
const device = state.devices[registry.device_id];
|
|
@@ -1927,7 +1904,7 @@ function buildEntityView(entity, registry) {
|
|
|
1927
1904
|
userId: entity.context?.user_id ?? null
|
|
1928
1905
|
},
|
|
1929
1906
|
name,
|
|
1930
|
-
friendlyName
|
|
1907
|
+
friendlyName,
|
|
1931
1908
|
areaId,
|
|
1932
1909
|
deviceId: registry?.device_id ?? null,
|
|
1933
1910
|
platform: registry?.platform ?? "unknown",
|
|
@@ -1949,7 +1926,7 @@ function buildEntityView(entity, registry) {
|
|
|
1949
1926
|
*/
|
|
1950
1927
|
function getEntityView(entityId) {
|
|
1951
1928
|
const entity = state.entities[entityId];
|
|
1952
|
-
if (!entity) return
|
|
1929
|
+
if (!entity) return;
|
|
1953
1930
|
const registry = state.entityRegistry[entityId];
|
|
1954
1931
|
return buildEntityView(entity, registry);
|
|
1955
1932
|
}
|
|
@@ -1979,9 +1956,22 @@ function entityViewEquals(a, b) {
|
|
|
1979
1956
|
if (!a || !b) return false;
|
|
1980
1957
|
return a.id === b.id && a.state === b.state && a.lastChanged.getTime() === b.lastChanged.getTime() && a.name === b.name && a.friendlyName === b.friendlyName && a.areaId === b.areaId && a.deviceId === b.deviceId && a.icon === b.icon && a.isDisabled === b.isDisabled && a.isHidden === b.isHidden && a.attributes === b.attributes;
|
|
1981
1958
|
}
|
|
1982
|
-
|
|
1983
1959
|
//#endregion
|
|
1984
1960
|
//#region src/entities/area-views.ts
|
|
1961
|
+
/**
|
|
1962
|
+
* Area View Builder
|
|
1963
|
+
*
|
|
1964
|
+
* Constructs unified AreaView objects by combining registry data with computed
|
|
1965
|
+
* entities and devices.
|
|
1966
|
+
*
|
|
1967
|
+
* Membership (which entities/devices belong to an area) is registry-driven and
|
|
1968
|
+
* cheap; live entity *views* read entity state and are therefore exposed as a
|
|
1969
|
+
* lazy `entities` getter. Callers that only need names, ids, or counts never
|
|
1970
|
+
* touch entity state, so building the whole-area list does not recompute on
|
|
1971
|
+
* every entity state tick.
|
|
1972
|
+
*
|
|
1973
|
+
* @packageDocumentation
|
|
1974
|
+
*/
|
|
1985
1975
|
/** The area an entity belongs to: its own `area_id` wins; otherwise it inherits
|
|
1986
1976
|
* the area of its device. Null when neither resolves. */
|
|
1987
1977
|
function entityAreaId(entry) {
|
|
@@ -2096,10 +2086,9 @@ function getAreaView(areaId) {
|
|
|
2096
2086
|
try {
|
|
2097
2087
|
return buildAreaView(areaId);
|
|
2098
2088
|
} catch {
|
|
2099
|
-
return
|
|
2089
|
+
return;
|
|
2100
2090
|
}
|
|
2101
2091
|
}
|
|
2102
|
-
|
|
2103
2092
|
//#endregion
|
|
2104
2093
|
//#region src/history/statistics.ts
|
|
2105
2094
|
const STATISTIC_TYPES = [
|
|
@@ -2150,11 +2139,11 @@ function normalizeStatisticValue(raw) {
|
|
|
2150
2139
|
async function fetchStatisticsDuringPeriod(connection, statisticIds, options) {
|
|
2151
2140
|
const { startTime, endTime, period } = options;
|
|
2152
2141
|
if (isDemoMode()) {
|
|
2153
|
-
const result
|
|
2142
|
+
const result = {};
|
|
2154
2143
|
const statPeriod = period === "hour" || period === "day" ? period : "day";
|
|
2155
2144
|
const endMs = (endTime ?? /* @__PURE__ */ new Date()).getTime();
|
|
2156
|
-
for (const id of statisticIds) result
|
|
2157
|
-
return result
|
|
2145
|
+
for (const id of statisticIds) result[id] = isEnergyEntity(id) ? synthesizeEnergyStatistics(id, startTime.getTime(), endMs, statPeriod) : [];
|
|
2146
|
+
return result;
|
|
2158
2147
|
}
|
|
2159
2148
|
const params = {
|
|
2160
2149
|
type: "recorder/statistics_during_period",
|
|
@@ -2170,6 +2159,5 @@ async function fetchStatisticsDuringPeriod(connection, statisticIds, options) {
|
|
|
2170
2159
|
if (response && typeof response === "object") for (const [id, buckets] of Object.entries(response)) result[id] = Array.isArray(buckets) ? buckets.map(normalizeStatisticValue) : [];
|
|
2171
2160
|
return result;
|
|
2172
2161
|
}
|
|
2173
|
-
|
|
2174
2162
|
//#endregion
|
|
2175
|
-
export {
|
|
2163
|
+
export { extractDomain as A, state as B, startDemoEnergyTicker as C, demoEnergyPreferences as D, createDemoFixtures as E, forceResubscribeCalendars as F, trackCalendarEvents as I, privilegedConn as L, registerEntity as M, setManagerConnection as N, isEnergyEntity as O, setResubscribeHandler as P, setPrivilegedConn as R, loadDemoData as S, unloadDemoData as T, turnOff as _, getAreaView as a, applyDemoServiceCall as b, entityViewEquals as c, getEntityViews as d, batchEntityUpdates as f, toggle as g, sendCommand as h, buildDeviceView as i, forceResubscribe as j, synthesizeEnergyHistory as k, getAllEntityViews as l, callService as m, normalizeStatisticTime as n, getAreaViews as o, batchServiceCalls as p, buildAreaView as r, buildEntityView as s, fetchStatisticsDuringPeriod as t, getEntityView as u, turnOn as v, stopDemoEnergyTicker as w, isDemoMode as x, updateEntity as y, setState as z };
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
//#region src/worker/worker-main.d.ts
|
|
1
2
|
/** Structural worker-global surface; the package compiles against DOM libs. */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
interface WorkerScope {
|
|
4
|
+
postMessage(message: unknown): void;
|
|
5
|
+
onmessage: ((ev: MessageEvent) => void) | null;
|
|
6
|
+
location: {
|
|
7
|
+
protocol: string;
|
|
8
|
+
host: string;
|
|
9
|
+
};
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
+
declare function runHaBridgeWorker(scope: WorkerScope): void;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { WorkerScope, runHaBridgeWorker };
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { runHaBridgeWorker } from "../worker-main-
|
|
2
|
-
|
|
3
|
-
export { runHaBridgeWorker };
|
|
1
|
+
import { t as runHaBridgeWorker } from "../worker-main-D_wyGAIG.js";
|
|
2
|
+
export { runHaBridgeWorker };
|