@glasshome/sync-layer 0.1.8 → 0.1.9
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.
|
@@ -27,7 +27,7 @@ export declare function unloadDemoData(): void;
|
|
|
27
27
|
* When a service call is made in demo mode, this updates the entity
|
|
28
28
|
* state locally (no WebSocket message sent). State resets on page refresh.
|
|
29
29
|
*/
|
|
30
|
-
export declare function applyDemoServiceCall(
|
|
30
|
+
export declare function applyDemoServiceCall(_domain: string, service: string, _serviceData?: Record<string, any>, target?: {
|
|
31
31
|
entity_id?: string | string[];
|
|
32
32
|
}): void;
|
|
33
33
|
export { createDemoFixtures } from "./demo-data";
|
|
@@ -74,6 +74,202 @@ function makeEntity(entity_id, state$1, attributes = {}) {
|
|
|
74
74
|
context: ctx()
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* One entity per weather scene the widget can render. Entity IDs use the
|
|
79
|
+
* `weather.demo_<slug>` form so a demo dashboard can reference them directly.
|
|
80
|
+
*/
|
|
81
|
+
const WEATHER_FIXTURES = [
|
|
82
|
+
{
|
|
83
|
+
slug: "sunny",
|
|
84
|
+
state: "sunny",
|
|
85
|
+
temp: 28,
|
|
86
|
+
apparent: 31,
|
|
87
|
+
humidity: 35,
|
|
88
|
+
pressure: 1018,
|
|
89
|
+
wind: 8,
|
|
90
|
+
low: 18
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
slug: "clear_night",
|
|
94
|
+
state: "clear-night",
|
|
95
|
+
temp: 14,
|
|
96
|
+
apparent: 12,
|
|
97
|
+
humidity: 55,
|
|
98
|
+
pressure: 1016,
|
|
99
|
+
wind: 5,
|
|
100
|
+
low: 9
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
slug: "cloudy",
|
|
104
|
+
state: "cloudy",
|
|
105
|
+
temp: 17,
|
|
106
|
+
apparent: 16,
|
|
107
|
+
humidity: 72,
|
|
108
|
+
pressure: 1010,
|
|
109
|
+
wind: 14,
|
|
110
|
+
low: 11
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
slug: "partly_cloudy",
|
|
114
|
+
state: "partlycloudy",
|
|
115
|
+
temp: 23,
|
|
116
|
+
apparent: 24,
|
|
117
|
+
humidity: 50,
|
|
118
|
+
pressure: 1014,
|
|
119
|
+
wind: 12,
|
|
120
|
+
low: 15
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
slug: "rainy",
|
|
124
|
+
state: "rainy",
|
|
125
|
+
temp: 12,
|
|
126
|
+
apparent: 10,
|
|
127
|
+
humidity: 88,
|
|
128
|
+
pressure: 1004,
|
|
129
|
+
wind: 18,
|
|
130
|
+
low: 8
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
slug: "pouring",
|
|
134
|
+
state: "pouring",
|
|
135
|
+
temp: 11,
|
|
136
|
+
apparent: 8,
|
|
137
|
+
humidity: 95,
|
|
138
|
+
pressure: 998,
|
|
139
|
+
wind: 26,
|
|
140
|
+
low: 7
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
slug: "snowy",
|
|
144
|
+
state: "snowy",
|
|
145
|
+
temp: -2,
|
|
146
|
+
apparent: -6,
|
|
147
|
+
humidity: 80,
|
|
148
|
+
pressure: 1020,
|
|
149
|
+
wind: 10,
|
|
150
|
+
low: -7
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
slug: "snowy_rainy",
|
|
154
|
+
state: "snowy-rainy",
|
|
155
|
+
temp: 1,
|
|
156
|
+
apparent: -2,
|
|
157
|
+
humidity: 92,
|
|
158
|
+
pressure: 1006,
|
|
159
|
+
wind: 16,
|
|
160
|
+
low: -2
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
slug: "lightning",
|
|
164
|
+
state: "lightning",
|
|
165
|
+
temp: 22,
|
|
166
|
+
apparent: 24,
|
|
167
|
+
humidity: 78,
|
|
168
|
+
pressure: 1001,
|
|
169
|
+
wind: 22,
|
|
170
|
+
low: 17
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
slug: "lightning_rainy",
|
|
174
|
+
state: "lightning-rainy",
|
|
175
|
+
temp: 19,
|
|
176
|
+
apparent: 18,
|
|
177
|
+
humidity: 90,
|
|
178
|
+
pressure: 996,
|
|
179
|
+
wind: 28,
|
|
180
|
+
low: 14
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
slug: "fog",
|
|
184
|
+
state: "fog",
|
|
185
|
+
temp: 8,
|
|
186
|
+
apparent: 6,
|
|
187
|
+
humidity: 98,
|
|
188
|
+
pressure: 1015,
|
|
189
|
+
wind: 4,
|
|
190
|
+
low: 6
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
slug: "hail",
|
|
194
|
+
state: "hail",
|
|
195
|
+
temp: 6,
|
|
196
|
+
apparent: 3,
|
|
197
|
+
humidity: 84,
|
|
198
|
+
pressure: 1002,
|
|
199
|
+
wind: 20,
|
|
200
|
+
low: 1
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
slug: "windy",
|
|
204
|
+
state: "windy",
|
|
205
|
+
temp: 18,
|
|
206
|
+
apparent: 15,
|
|
207
|
+
humidity: 60,
|
|
208
|
+
pressure: 1009,
|
|
209
|
+
wind: 42,
|
|
210
|
+
low: 12
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
slug: "exceptional",
|
|
214
|
+
state: "exceptional",
|
|
215
|
+
temp: 38,
|
|
216
|
+
apparent: 44,
|
|
217
|
+
humidity: 22,
|
|
218
|
+
pressure: 1005,
|
|
219
|
+
wind: 30,
|
|
220
|
+
low: 28
|
|
221
|
+
}
|
|
222
|
+
];
|
|
223
|
+
function makeWeatherEntity(w) {
|
|
224
|
+
const today = /* @__PURE__ */ new Date();
|
|
225
|
+
const day = (n) => new Date(today.getTime() + n * 864e5).toISOString();
|
|
226
|
+
return makeEntity(`weather.demo_${w.slug}`, w.state, {
|
|
227
|
+
temperature: w.temp,
|
|
228
|
+
temperature_unit: "°C",
|
|
229
|
+
apparent_temperature: w.apparent ?? w.temp,
|
|
230
|
+
humidity: w.humidity,
|
|
231
|
+
pressure: w.pressure,
|
|
232
|
+
pressure_unit: "hPa",
|
|
233
|
+
wind_speed: w.wind,
|
|
234
|
+
wind_speed_unit: "km/h",
|
|
235
|
+
wind_bearing: w.bearing ?? 220,
|
|
236
|
+
visibility: 10,
|
|
237
|
+
visibility_unit: "km",
|
|
238
|
+
uv_index: w.state === "sunny" ? 8 : 2,
|
|
239
|
+
forecast: [
|
|
240
|
+
{
|
|
241
|
+
datetime: day(0),
|
|
242
|
+
condition: w.state,
|
|
243
|
+
temperature: w.temp,
|
|
244
|
+
templow: w.low
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
datetime: day(1),
|
|
248
|
+
condition: "partlycloudy",
|
|
249
|
+
temperature: w.temp + 2,
|
|
250
|
+
templow: w.low
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
datetime: day(2),
|
|
254
|
+
condition: "cloudy",
|
|
255
|
+
temperature: w.temp + 1,
|
|
256
|
+
templow: w.low - 1
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
datetime: day(3),
|
|
260
|
+
condition: "sunny",
|
|
261
|
+
temperature: w.temp + 4,
|
|
262
|
+
templow: w.low + 1
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
datetime: day(4),
|
|
266
|
+
condition: "rainy",
|
|
267
|
+
temperature: w.temp - 2,
|
|
268
|
+
templow: w.low - 2
|
|
269
|
+
}
|
|
270
|
+
]
|
|
271
|
+
});
|
|
272
|
+
}
|
|
77
273
|
function friendlyName(entityId) {
|
|
78
274
|
const objectId = entityId.split(".")[1] ?? entityId;
|
|
79
275
|
return objectId.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
@@ -372,48 +568,7 @@ function createDemoFixtures() {
|
|
|
372
568
|
area_id: "living_room",
|
|
373
569
|
supported_features: 152461
|
|
374
570
|
});
|
|
375
|
-
add(
|
|
376
|
-
temperature: 3,
|
|
377
|
-
temperature_unit: "°C",
|
|
378
|
-
humidity: 72,
|
|
379
|
-
pressure: 1013,
|
|
380
|
-
pressure_unit: "hPa",
|
|
381
|
-
wind_speed: 12,
|
|
382
|
-
wind_speed_unit: "km/h",
|
|
383
|
-
wind_bearing: 220,
|
|
384
|
-
forecast: [
|
|
385
|
-
{
|
|
386
|
-
datetime: "2026-03-07T00:00:00Z",
|
|
387
|
-
condition: "cloudy",
|
|
388
|
-
temperature: 4,
|
|
389
|
-
templow: -1
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
datetime: "2026-03-08T00:00:00Z",
|
|
393
|
-
condition: "sunny",
|
|
394
|
-
temperature: 7,
|
|
395
|
-
templow: 1
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
datetime: "2026-03-09T00:00:00Z",
|
|
399
|
-
condition: "rainy",
|
|
400
|
-
temperature: 5,
|
|
401
|
-
templow: 2
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
datetime: "2026-03-10T00:00:00Z",
|
|
405
|
-
condition: "partlycloudy",
|
|
406
|
-
temperature: 6,
|
|
407
|
-
templow: 0
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
datetime: "2026-03-11T00:00:00Z",
|
|
411
|
-
condition: "snowy",
|
|
412
|
-
temperature: 1,
|
|
413
|
-
templow: -3
|
|
414
|
-
}
|
|
415
|
-
]
|
|
416
|
-
}), { platform: "met" });
|
|
571
|
+
for (const w of WEATHER_FIXTURES) add(makeWeatherEntity(w), { platform: "met" });
|
|
417
572
|
add(makeEntity("camera.front_door_camera", "streaming", {
|
|
418
573
|
entity_picture: "/api/camera_proxy/camera.front_door_camera",
|
|
419
574
|
frontend_stream_type: "hls",
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createDemoFixtures, resetStore, setState, state } from "./demo-data-
|
|
1
|
+
import { createDemoFixtures, resetStore, setState, state } from "./demo-data-zwOczByr.js";
|
|
2
2
|
import { forceResubscribe, setManagerConnection, setResubscribeHandler } from "./subscription-manager-gR0qpDYZ.js";
|
|
3
3
|
import { callService as callService$1, createConnection, createLongLivedTokenAuth, getAuth } from "home-assistant-js-websocket";
|
|
4
4
|
import { produce, reconcile } from "solid-js/store";
|
|
@@ -447,7 +447,7 @@ function unloadDemoData() {
|
|
|
447
447
|
* When a service call is made in demo mode, this updates the entity
|
|
448
448
|
* state locally (no WebSocket message sent). State resets on page refresh.
|
|
449
449
|
*/
|
|
450
|
-
function applyDemoServiceCall(
|
|
450
|
+
function applyDemoServiceCall(_domain, service, _serviceData = {}, target = {}) {
|
|
451
451
|
if (!_isDemoMode) return;
|
|
452
452
|
const entityIds = Array.isArray(target.entity_id) ? target.entity_id : target.entity_id ? [target.entity_id] : [];
|
|
453
453
|
for (const entityId of entityIds) {
|
|
@@ -1538,7 +1538,7 @@ function buildQuerySignature(query) {
|
|
|
1538
1538
|
/**
|
|
1539
1539
|
* Execute query (no caching -- SolidJS createMemo handles this in Plan 03)
|
|
1540
1540
|
*/
|
|
1541
|
-
function memoizedExecute(
|
|
1541
|
+
function memoizedExecute(_signature, compute) {
|
|
1542
1542
|
return compute();
|
|
1543
1543
|
}
|
|
1544
1544
|
|
|
@@ -2061,9 +2061,50 @@ async function sendWebRtcCandidate(entityId, sessionId, candidate) {
|
|
|
2061
2061
|
//#endregion
|
|
2062
2062
|
//#region src/weather/fetch.ts
|
|
2063
2063
|
/**
|
|
2064
|
+
* In demo mode, derive forecasts from the entity's `forecast` attribute.
|
|
2065
|
+
* Hourly and daily are synthesised from the same source (daily entries
|
|
2066
|
+
* expanded into 12 hourly steps for the chart).
|
|
2067
|
+
*/
|
|
2068
|
+
function buildDemoForecast(entityId, type) {
|
|
2069
|
+
const entity$1 = state.entities[entityId];
|
|
2070
|
+
const raw = entity$1?.attributes?.forecast ?? [];
|
|
2071
|
+
if (raw.length === 0) return [];
|
|
2072
|
+
if (type === "daily" || type === "twice_daily") return raw.map((d) => {
|
|
2073
|
+
const templow = d.templow;
|
|
2074
|
+
return {
|
|
2075
|
+
...d,
|
|
2076
|
+
temp_high: d.temp_high ?? d.temperature,
|
|
2077
|
+
temp_low: d.temp_low ?? templow
|
|
2078
|
+
};
|
|
2079
|
+
});
|
|
2080
|
+
const start = /* @__PURE__ */ new Date();
|
|
2081
|
+
const today = raw[0];
|
|
2082
|
+
const tomorrow = raw[1] ?? today;
|
|
2083
|
+
const tHi = today?.temperature ?? 18;
|
|
2084
|
+
const tLo = today?.templow ?? tHi - 5;
|
|
2085
|
+
const tNext = tomorrow?.temperature ?? tHi;
|
|
2086
|
+
const out = [];
|
|
2087
|
+
for (let i = 0; i < 24; i++) {
|
|
2088
|
+
const dt = new Date(start.getTime() + i * 60 * 60 * 1e3);
|
|
2089
|
+
const hour = dt.getHours();
|
|
2090
|
+
const dayMix = i < 12 ? 0 : (i - 12) / 12;
|
|
2091
|
+
const baseHi = tHi * (1 - dayMix) + tNext * dayMix;
|
|
2092
|
+
const phase = (hour - 6) / 24 * Math.PI * 2;
|
|
2093
|
+
const swing = (baseHi - tLo) / 2;
|
|
2094
|
+
const mid = (baseHi + tLo) / 2;
|
|
2095
|
+
out.push({
|
|
2096
|
+
datetime: dt.toISOString(),
|
|
2097
|
+
temperature: Math.round((mid + Math.sin(phase) * swing) * 10) / 10,
|
|
2098
|
+
condition: today?.condition ?? entity$1?.state ?? "cloudy"
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
return out;
|
|
2102
|
+
}
|
|
2103
|
+
/**
|
|
2064
2104
|
* Fetch forecast for a single entity and type
|
|
2065
2105
|
*/
|
|
2066
2106
|
async function fetchForecast(entityId, type) {
|
|
2107
|
+
if (isDemoMode()) return buildDemoForecast(entityId, type);
|
|
2067
2108
|
const conn = state.conn;
|
|
2068
2109
|
if (!conn) throw new Error("Not connected");
|
|
2069
2110
|
return new Promise((resolve, reject) => {
|
|
@@ -11,7 +11,7 @@ export declare function buildQuerySignature(query: QueryState): string;
|
|
|
11
11
|
/**
|
|
12
12
|
* Execute query (no caching -- SolidJS createMemo handles this in Plan 03)
|
|
13
13
|
*/
|
|
14
|
-
export declare function memoizedExecute<T>(
|
|
14
|
+
export declare function memoizedExecute<T>(_signature: string, compute: () => T): T;
|
|
15
15
|
/**
|
|
16
16
|
* Check if cache should be invalidated
|
|
17
17
|
*/
|
package/dist/solid/index.js
CHANGED
|
@@ -107,7 +107,12 @@ function useAreas() {
|
|
|
107
107
|
}).filter((v) => v !== void 0);
|
|
108
108
|
}, void 0, { equals: (a, b) => {
|
|
109
109
|
if (a.length !== b.length) return false;
|
|
110
|
-
for (let i = 0; i < a.length; i++)
|
|
110
|
+
for (let i = 0; i < a.length; i++) {
|
|
111
|
+
const aItem = a[i];
|
|
112
|
+
const bItem = b[i];
|
|
113
|
+
if (!aItem || !bItem) return false;
|
|
114
|
+
if (aItem.id !== bItem.id || aItem.modifiedAt !== bItem.modifiedAt || aItem.entities.length !== bItem.entities.length) return false;
|
|
115
|
+
}
|
|
111
116
|
return true;
|
|
112
117
|
} });
|
|
113
118
|
}
|
package/dist/testing/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glasshome/sync-layer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Type-safe, reactive state synchronization layer for Home Assistant",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dist"
|
|
45
45
|
],
|
|
46
46
|
"scripts": {
|
|
47
|
-
"build": "tsdown",
|
|
47
|
+
"build": "tsdown && tsc --noEmit",
|
|
48
48
|
"dev": "tsdown --watch",
|
|
49
49
|
"test": "bun test",
|
|
50
50
|
"lint": "tsc --noEmit"
|
|
@@ -53,7 +53,9 @@
|
|
|
53
53
|
"home-assistant-js-websocket": "^9.6.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
+
"@glasshome/ha-types": "0.1.0",
|
|
56
57
|
"@types/bun": "latest",
|
|
58
|
+
"solid-js": "^1.9.0",
|
|
57
59
|
"tsdown": "^0.2.0",
|
|
58
60
|
"typescript": "^5"
|
|
59
61
|
},
|