@glasshome/sync-layer 0.7.1 → 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.
Files changed (58) hide show
  1. package/dist/index.d.ts +1291 -54
  2. package/dist/index.js +215 -308
  3. package/dist/service-C8H42I-Q.d.ts +459 -0
  4. package/dist/solid/index.d.ts +119 -6
  5. package/dist/solid/index.js +20 -15
  6. package/dist/{statistics-CtRfIF6D.js → statistics-DyCCTwv2.js} +83 -95
  7. package/dist/worker/worker-main.d.ts +11 -8
  8. package/dist/worker/worker-main.js +2 -3
  9. package/dist/{worker-main-C-3QM4di.js → worker-main-D_wyGAIG.js} +47 -157
  10. package/package.json +3 -3
  11. package/dist/calendar/track.d.ts +0 -19
  12. package/dist/calendar/types.d.ts +0 -32
  13. package/dist/camera/fetch.d.ts +0 -16
  14. package/dist/camera/query.d.ts +0 -15
  15. package/dist/camera/types.d.ts +0 -85
  16. package/dist/camera/webrtc.d.ts +0 -31
  17. package/dist/commands/service.d.ts +0 -53
  18. package/dist/commands/types.d.ts +0 -113
  19. package/dist/connection/adapter.d.ts +0 -48
  20. package/dist/connection/auth.d.ts +0 -126
  21. package/dist/connection/bridged.d.ts +0 -16
  22. package/dist/connection/manager.d.ts +0 -51
  23. package/dist/connection/subscription-manager.d.ts +0 -48
  24. package/dist/connection/subscriptions.d.ts +0 -31
  25. package/dist/connection/types.d.ts +0 -151
  26. package/dist/core/privileged-conn.d.ts +0 -29
  27. package/dist/core/reducers.d.ts +0 -99
  28. package/dist/core/store.d.ts +0 -100
  29. package/dist/core/types.d.ts +0 -339
  30. package/dist/demo/demo-data.d.ts +0 -24
  31. package/dist/demo/demo-provider.d.ts +0 -40
  32. package/dist/demo/energy-sim.d.ts +0 -112
  33. package/dist/energy/prefs.d.ts +0 -56
  34. package/dist/entities/area-views.d.ts +0 -32
  35. package/dist/entities/index.d.ts +0 -54
  36. package/dist/entities/views.d.ts +0 -35
  37. package/dist/history/constants.d.ts +0 -16
  38. package/dist/history/fetch.d.ts +0 -19
  39. package/dist/history/query.d.ts +0 -27
  40. package/dist/history/statistics.d.ts +0 -55
  41. package/dist/history/types.d.ts +0 -146
  42. package/dist/host-api.d.ts +0 -50
  43. package/dist/media/url.d.ts +0 -14
  44. package/dist/query/area-builder.d.ts +0 -25
  45. package/dist/query/builder.d.ts +0 -18
  46. package/dist/query/helpers.d.ts +0 -21
  47. package/dist/query/memoization.d.ts +0 -18
  48. package/dist/query/projections.d.ts +0 -74
  49. package/dist/query/types.d.ts +0 -260
  50. package/dist/solid/hooks.d.ts +0 -119
  51. package/dist/solid/indices.d.ts +0 -15
  52. package/dist/weather/fetch.d.ts +0 -19
  53. package/dist/weather/query.d.ts +0 -23
  54. package/dist/weather/types.d.ts +0 -126
  55. package/dist/worker/bridge-client.d.ts +0 -47
  56. package/dist/worker/enforcement.d.ts +0 -53
  57. package/dist/worker/protocol.d.ts +0 -129
  58. package/dist/worker/token-store.d.ts +0 -3
@@ -1,6 +1,5 @@
1
- import { Auth, createConnection, createSocket, genExpires } from "home-assistant-js-websocket";
1
+ import { Auth, ERR_INVALID_AUTH, createConnection, createSocket } from "home-assistant-js-websocket";
2
2
  import { matchesCapability } from "@glasshome/widget-contract";
3
-
4
3
  //#region src/worker/enforcement.ts
5
4
  var RegistryMirror = class {
6
5
  entities = /* @__PURE__ */ new Map();
@@ -65,66 +64,45 @@ function enforceServiceCall(caps, call, registry) {
65
64
  message: `Call to ${call.domain}.${call.service} is not covered by the widget's granted capabilities`
66
65
  };
67
66
  }
68
-
69
67
  //#endregion
70
- //#region src/worker/token-store.ts
71
- /**
72
- * OAuth token persistence inside the worker. IndexedDB is the only storage
73
- * a worker has; keeping tokens here (and deleting the old localStorage copy
74
- * on the main thread) is what makes "widgets never see the token" hold.
75
- */
76
- const DB_NAME = "glasshome-ha-bridge";
77
- const STORE = "auth";
78
- const KEY = "oauth";
79
- function openDb() {
80
- return new Promise((resolve, reject) => {
81
- const req = indexedDB.open(DB_NAME, 1);
82
- req.onupgradeneeded = () => {
83
- req.result.createObjectStore(STORE);
84
- };
85
- req.onsuccess = () => resolve(req.result);
86
- req.onerror = () => reject(req.error);
68
+ //#region src/worker/proxy-auth.ts
69
+ /** The relay authenticates upstream; this Auth carries no token and never expires. */
70
+ function proxyAuth(hassUrl) {
71
+ return new Auth({
72
+ hassUrl,
73
+ clientId: null,
74
+ access_token: "",
75
+ refresh_token: "",
76
+ expires: Number.MAX_SAFE_INTEGER,
77
+ expires_in: Number.MAX_SAFE_INTEGER
87
78
  });
88
79
  }
89
- function settle(req) {
90
- return new Promise((resolve, reject) => {
91
- req.onsuccess = () => resolve(req.result);
92
- req.onerror = () => reject(req.error);
93
- });
94
- }
95
- async function loadTokens() {
96
- try {
97
- const db = await openDb();
98
- const store = db.transaction(STORE, "readonly").objectStore(STORE);
99
- const data = await settle(store.get(KEY));
100
- db.close();
101
- return data ?? null;
102
- } catch {
103
- return null;
104
- }
80
+ function invalidAuthReason(err) {
81
+ return err === ERR_INVALID_AUTH ? { reason: "invalid_auth" } : {};
105
82
  }
106
- async function saveTokens(data) {
107
- try {
108
- const db = await openDb();
109
- const store = db.transaction(STORE, "readwrite").objectStore(STORE);
110
- await (data ? settle(store.put(data, KEY)) : settle(store.delete(KEY)));
111
- db.close();
112
- } catch {}
83
+ /** The lib fires reconnect-error only for ERR_INVALID_AUTH and then stops
84
+ * retrying, so that case is terminal, not a reconnect in progress. */
85
+ function reconnectErrorMessage(err) {
86
+ const reason = invalidAuthReason(err);
87
+ return {
88
+ k: "conn",
89
+ state: "reason" in reason ? "disconnected" : "reconnecting",
90
+ ...reason
91
+ };
113
92
  }
114
-
115
93
  //#endregion
116
94
  //#region src/worker/worker-main.ts
117
95
  /**
118
- * HA bridge worker. Owns the socket and the auth tokens; neither ever
119
- * reaches the main thread. The privileged channel (the worker's own port)
120
- * tunnels arbitrary HA traffic for the host. Widget MessagePorts get exactly
121
- * one verb — call_service — validated against the widget's granted
122
- * capabilities with the worker's own registry mirror, so enforcement does
123
- * not trust anything computed in the widget's realm.
96
+ * HA bridge worker. Owns the socket, which never reaches the main thread, and
97
+ * holds no HA token: the relay authenticates upstream. The privileged channel
98
+ * (the worker's own port) tunnels arbitrary HA traffic for the host. Widget
99
+ * MessagePorts get exactly one verb, call_service, validated against the
100
+ * widget's granted capabilities with the worker's own registry mirror, so
101
+ * enforcement does not trust anything computed in the widget's realm.
124
102
  */
125
103
  const HEARTBEAT_INTERVAL_MS = 3e4;
126
104
  const HEARTBEAT_TIMEOUT_MS = 15e3;
127
- const SUSPEND_DELAY_MS = 5 * 60 * 1e3;
105
+ const SUSPEND_DELAY_MS = 3e5;
128
106
  function isHaWireMessage(value) {
129
107
  return typeof value === "object" && value !== null && typeof value.type === "string";
130
108
  }
@@ -140,44 +118,13 @@ function errText(err) {
140
118
  }
141
119
  return String(err);
142
120
  }
143
- var BrokeredAuth = class extends Auth {
144
- constructor(mintUrl, data, ticket) {
145
- super(data);
146
- this.mintUrl = mintUrl;
147
- this.ticket = ticket;
148
- }
149
- async refreshAccessToken() {
150
- const res = await fetch(this.mintUrl, {
151
- credentials: "include",
152
- headers: {
153
- accept: "application/json",
154
- ...this.ticket ? { "x-proxy-ticket": this.ticket } : {}
155
- }
156
- });
157
- if (!res.ok) throw new Error(`Token broker responded ${res.status}`);
158
- if (!res.headers.get("content-type")?.includes("application/json")) throw new Error(`Token broker did not return JSON (is ${this.mintUrl} reachable?)`);
159
- const broker = await res.json();
160
- this.data = {
161
- ...this.data,
162
- access_token: broker.accessToken,
163
- expires_in: broker.expiresInSec,
164
- expires: genExpires(broker.expiresInSec),
165
- refresh_token: "",
166
- hassUrl: broker.haUrl,
167
- clientId: broker.haClientId
168
- };
169
- }
170
- };
171
121
  /** Resolve the proxy WS endpoint. Accepts a same-origin path (web) or an
172
122
  * absolute ws(s)/http(s) URL (native), and appends the auth ticket if given. */
173
123
  function buildProxyWsUrl(scope, proxyWsPath, ticket) {
174
124
  let base;
175
125
  if (/^wss?:\/\//i.test(proxyWsPath)) base = proxyWsPath;
176
126
  else if (/^https?:\/\//i.test(proxyWsPath)) base = proxyWsPath.replace(/^http/i, "ws");
177
- else {
178
- const proto = scope.location.protocol === "https:" ? "wss:" : "ws:";
179
- base = `${proto}//${scope.location.host}${proxyWsPath}`;
180
- }
127
+ else base = `${scope.location.protocol === "https:" ? "wss:" : "ws:"}//${scope.location.host}${proxyWsPath}`;
181
128
  if (ticket) base += `${base.includes("?") ? "&" : "?"}ticket=${encodeURIComponent(ticket)}`;
182
129
  return base;
183
130
  }
@@ -266,7 +213,7 @@ function runHaBridgeWorker(scope) {
266
213
  function startHeartbeat(c) {
267
214
  stopHeartbeat();
268
215
  heartbeat = setInterval(() => {
269
- const timer = new Promise((_, reject) => setTimeout(() => reject(new Error("ping timeout")), HEARTBEAT_TIMEOUT_MS));
216
+ const timer = new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("ping timeout")), HEARTBEAT_TIMEOUT_MS));
270
217
  Promise.race([c.ping(), timer]).catch(() => c.reconnect());
271
218
  }, HEARTBEAT_INTERVAL_MS);
272
219
  }
@@ -295,72 +242,25 @@ function runHaBridgeWorker(scope) {
295
242
  resumeVisible = null;
296
243
  }
297
244
  }
298
- async function buildAuth(url, mode, ticket) {
299
- switch (mode.kind) {
300
- case "token": return new Auth({
301
- hassUrl: url,
302
- clientId: null,
303
- access_token: mode.token,
304
- refresh_token: "",
305
- expires: Date.now() + 365 * 24 * 3600 * 1e3,
306
- expires_in: 365 * 24 * 3600
307
- });
308
- case "brokered": return new BrokeredAuth(mode.mintUrl, {
309
- hassUrl: url,
310
- clientId: null,
311
- access_token: "",
312
- refresh_token: "",
313
- expires: 0,
314
- expires_in: 0
315
- }, ticket);
316
- case "oauth": {
317
- const data = mode.data ?? await loadTokens();
318
- if (!data) return "needs-auth";
319
- if (mode.data) await saveTokens(mode.data);
320
- return new Auth(data, (updated) => {
321
- saveTokens(updated);
322
- });
323
- }
324
- }
325
- }
326
- async function connect(url, mode, proxyWsPath, proxyTicket) {
327
- const auth = await buildAuth(url, mode, proxyTicket);
328
- if (auth === "needs-auth") {
329
- post({
330
- k: "connect_result",
331
- ok: false,
332
- needsAuth: true
333
- });
334
- return;
335
- }
336
- if (mode.kind === "brokered") try {
337
- await auth.refreshAccessToken();
338
- } catch (err) {
339
- post({
340
- k: "connect_result",
341
- ok: false,
342
- error: errText(err)
343
- });
344
- return;
345
- }
346
- const proxyWsUrl = proxyWsPath ? buildProxyWsUrl(scope, proxyWsPath, proxyTicket) : null;
347
- const socketFactory = proxyWsUrl ? (options) => {
348
- const proxied = new Proxy(options.auth, { get: (target, prop) => prop === "wsUrl" ? proxyWsUrl : Reflect.get(target, prop) });
349
- return createSocket({
350
- ...options,
351
- auth: proxied
352
- });
353
- } : void 0;
245
+ async function connect(url, proxyWsPath, proxyTicket) {
246
+ const auth = proxyAuth(url);
247
+ const proxyWsUrl = buildProxyWsUrl(scope, proxyWsPath, proxyTicket);
248
+ const proxiedAuth = new Proxy(auth, { get: (target, prop) => prop === "wsUrl" ? proxyWsUrl : Reflect.get(target, prop) });
249
+ const socketFactory = (options) => createSocket({
250
+ ...options,
251
+ auth: proxiedAuth
252
+ });
354
253
  try {
355
254
  conn = await createConnection({
356
255
  auth,
357
- ...socketFactory ? { createSocket: socketFactory } : {}
256
+ createSocket: socketFactory
358
257
  });
359
258
  } catch (err) {
360
259
  post({
361
260
  k: "connect_result",
362
261
  ok: false,
363
- error: errText(err)
262
+ error: errText(err),
263
+ ...invalidAuthReason(err)
364
264
  });
365
265
  return;
366
266
  }
@@ -368,10 +268,7 @@ function runHaBridgeWorker(scope) {
368
268
  k: "conn",
369
269
  state: "disconnected"
370
270
  }));
371
- conn.addEventListener("reconnect-error", () => post({
372
- k: "conn",
373
- state: "reconnecting"
374
- }));
271
+ conn.addEventListener("reconnect-error", (_c, err) => post(reconnectErrorMessage(err)));
375
272
  let everReady = false;
376
273
  conn.addEventListener("ready", () => {
377
274
  post({
@@ -399,9 +296,9 @@ function runHaBridgeWorker(scope) {
399
296
  const msg = ev.data;
400
297
  switch (msg.k) {
401
298
  case "connect":
402
- await connect(msg.url, msg.mode, msg.proxyWsPath, msg.proxyTicket);
299
+ await connect(msg.url, msg.proxyWsPath, msg.proxyTicket);
403
300
  break;
404
- case "send": {
301
+ case "send":
405
302
  if (!conn || !isHaWireMessage(msg.message)) {
406
303
  post({
407
304
  k: "result",
@@ -428,8 +325,7 @@ function runHaBridgeWorker(scope) {
428
325
  });
429
326
  }
430
327
  break;
431
- }
432
- case "sub": {
328
+ case "sub":
433
329
  if (!conn) {
434
330
  post({
435
331
  k: "sub_end",
@@ -466,7 +362,6 @@ function runHaBridgeWorker(scope) {
466
362
  });
467
363
  }
468
364
  break;
469
- }
470
365
  case "unsub": {
471
366
  const unsub = subs.get(msg.id);
472
367
  subs.delete(msg.id);
@@ -493,9 +388,6 @@ function runHaBridgeWorker(scope) {
493
388
  state: "disconnected"
494
389
  });
495
390
  break;
496
- case "clear_tokens":
497
- await saveTokens(null);
498
- break;
499
391
  case "register_widget": {
500
392
  const port = ev.ports[0];
501
393
  if (port) attachWidgetPort(msg.widgetId, msg.caps, port);
@@ -504,10 +396,8 @@ function runHaBridgeWorker(scope) {
504
396
  case "unregister_widget":
505
397
  widgets.get(msg.widgetId)?.port.close();
506
398
  widgets.delete(msg.widgetId);
507
- break;
508
399
  }
509
400
  };
510
401
  }
511
-
512
402
  //#endregion
513
- export { runHaBridgeWorker };
403
+ export { runHaBridgeWorker as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glasshome/sync-layer",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Type-safe, reactive state synchronization layer for Home Assistant",
5
5
  "repository": {
6
6
  "type": "git",
@@ -57,8 +57,8 @@
57
57
  "@glasshome/ha-types": "0.1.2",
58
58
  "@types/bun": "latest",
59
59
  "solid-js": "^1.9.0",
60
- "tsdown": "^0.2.0",
61
- "typescript": "^5"
60
+ "tsdown": "^0.22.14",
61
+ "typescript": "^7.0.2"
62
62
  },
63
63
  "homepage": "https://github.com/glasshome/sync-layer#readme",
64
64
  "bugs": {
@@ -1,19 +0,0 @@
1
- import type { EntityId } from "../core/types";
2
- import type { CalendarWindowOptions } from "./types";
3
- /**
4
- * Start tracking live events for a calendar entity. Returns the untrack
5
- * function. Safe while disconnected: the subscription activates on the next
6
- * `forceResubscribeCalendars()` (called by the connection layer on connect
7
- * and reconnect).
8
- */
9
- export declare function trackCalendarEvents(entityId: EntityId, options?: CalendarWindowOptions): () => void;
10
- /**
11
- * Reconcile every tracked calendar, dropping current subscription handles
12
- * first. The connection layer calls this on connect and after reconnect:
13
- * an unchanged window is no proof the old subscription is alive, so each
14
- * tracked entity resubscribes (HA re-pushes the snapshot, refreshing the
15
- * store through any missed changes).
16
- */
17
- export declare function forceResubscribeCalendars(): Promise<void>;
18
- /** Test-only: drop all tracking state and timers. */
19
- export declare function resetCalendarTracking(): void;
@@ -1,32 +0,0 @@
1
- import type { EntityId } from "../core/types";
2
- /**
3
- * One calendar event as HA serializes it over `calendar/event/subscribe`:
4
- * dates/datetimes are ISO strings (date-only for all-day events), and
5
- * None-valued fields are omitted.
6
- */
7
- export interface CalendarEvent {
8
- start: string;
9
- end: string;
10
- summary: string;
11
- description?: string;
12
- location?: string;
13
- uid?: string;
14
- recurrence_id?: string;
15
- rrule?: string;
16
- }
17
- /** Live event window for one calendar entity, held in the store. */
18
- export interface CalendarEventsData {
19
- entityId: EntityId;
20
- events: CalendarEvent[];
21
- /** True until the first snapshot for the current subscription arrives. */
22
- loading: boolean;
23
- error: Error | null;
24
- /** Subscribed window bounds (ISO). */
25
- start: string;
26
- end: string;
27
- }
28
- /** Per-tracker window request, in whole days relative to now. */
29
- export interface CalendarWindowOptions {
30
- daysBehind?: number;
31
- daysAhead?: number;
32
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * Camera Stream Fetching
3
- *
4
- * @packageDocumentation
5
- */
6
- import type { EntityId } from "../core/types";
7
- import type { CameraStreamData, StreamFormat, StreamQueryOptions } from "./types";
8
- /**
9
- * Fetch stream for a camera entity.
10
- * Returns a public demo HLS stream when not connected.
11
- */
12
- export declare function fetchStream(entityId: EntityId, format?: StreamFormat): Promise<string | null>;
13
- /**
14
- * Fetch stream data for a camera entity
15
- */
16
- export declare function fetchStreamData(entityId: EntityId, options?: StreamQueryOptions): Promise<CameraStreamData>;
@@ -1,15 +0,0 @@
1
- /**
2
- * Camera Stream Query Builder
3
- *
4
- * @packageDocumentation
5
- */
6
- import type { EntityId } from "../core/types";
7
- import type { CameraStreamData, EntityStreamQueryOptions, StreamFormat } from "./types";
8
- /**
9
- * Get stream for a camera entity
10
- */
11
- export declare function getStream(entityId: EntityId, options?: EntityStreamQueryOptions): Promise<CameraStreamData>;
12
- /**
13
- * Refresh stream for a camera entity
14
- */
15
- export declare function refreshStream(entityId: EntityId, format?: StreamFormat): Promise<CameraStreamData>;
@@ -1,85 +0,0 @@
1
- /**
2
- * Camera Types
3
- *
4
- * Type definitions for camera stream data.
5
- *
6
- * @packageDocumentation
7
- */
8
- import type { EntityId } from "../core/types";
9
- /**
10
- * Stream format
11
- * Supported stream formats from Home Assistant Camera API
12
- */
13
- export type StreamFormat = "hls" | "webrtc" | "mjpeg";
14
- /**
15
- * Camera stream data
16
- */
17
- export interface CameraStream {
18
- /** Stream URL */
19
- url: string | null;
20
- /** Loading state */
21
- loading: boolean;
22
- /** Error if any */
23
- error: Error | null;
24
- /** Last fetched timestamp */
25
- lastFetched: number | null;
26
- /** Stream format */
27
- format: StreamFormat;
28
- /** Stream expiration time (if available) */
29
- expiresAt?: number | null;
30
- }
31
- /**
32
- * Camera stream data for an entity
33
- */
34
- export interface CameraStreamData {
35
- /** Entity ID */
36
- entityId: EntityId;
37
- /** Stream data */
38
- stream: CameraStream;
39
- /** Loading state */
40
- loading: boolean;
41
- /** Error if any */
42
- error: Error | null;
43
- /** Last fetched timestamp */
44
- lastFetched: number | null;
45
- }
46
- /**
47
- * Camera stream data for multiple entities
48
- */
49
- export interface CameraStreams {
50
- /** Entity ID -> Stream data */
51
- [entityId: EntityId]: CameraStreamData;
52
- }
53
- /**
54
- * Options for stream queries
55
- */
56
- export interface StreamQueryOptions {
57
- /** Stream format (default: "hls") */
58
- format?: StreamFormat;
59
- /** Auto-refresh stream before expiration */
60
- autoRefresh?: boolean;
61
- /** Stream refresh interval in milliseconds (default: 5 minutes) */
62
- refreshInterval?: number;
63
- }
64
- /**
65
- * Single entity stream query options
66
- */
67
- export interface EntityStreamQueryOptions {
68
- /** Stream format (default: "hls") */
69
- format?: StreamFormat;
70
- /** Auto-refresh stream before expiration */
71
- autoRefresh?: boolean;
72
- /** Stream refresh interval in milliseconds (default: 5 minutes) */
73
- refreshInterval?: number;
74
- }
75
- /**
76
- * Stream query result
77
- */
78
- export interface StreamResult {
79
- /** Stream data */
80
- data: CameraStreamData | null;
81
- /** Loading state */
82
- loading: boolean;
83
- /** Error if any */
84
- error: Error | null;
85
- }
@@ -1,31 +0,0 @@
1
- import type { EntityId } from "../core/types";
2
- /** Minimal RTC types for sync-layer (no DOM lib dependency) */
3
- type RtcConfiguration = Record<string, unknown>;
4
- type RtcIceCandidateInit = Record<string, unknown>;
5
- interface WebRtcClientConfig {
6
- configuration: RtcConfiguration;
7
- dataChannel?: string;
8
- }
9
- interface WebRtcSession {
10
- sessionId: string | null;
11
- unsubscribe: (() => Promise<void>) | null;
12
- }
13
- /**
14
- * Get WebRTC client configuration (STUN/TURN servers) from HA
15
- */
16
- export declare function getWebRtcClientConfig(entityId: EntityId): Promise<WebRtcClientConfig>;
17
- /**
18
- * Start WebRTC session with HA.
19
- * Returns a promise that resolves when the answer SDP is received.
20
- * ICE candidates from HA are forwarded to the provided callback.
21
- * Local ICE candidates should be sent via sendWebRtcCandidate().
22
- */
23
- export declare function startWebRtcSession(entityId: EntityId, sdpOffer: string, onCandidate: (candidate: RtcIceCandidateInit) => void): Promise<{
24
- answer: string;
25
- session: WebRtcSession;
26
- }>;
27
- /**
28
- * Send a local ICE candidate to HA
29
- */
30
- export declare function sendWebRtcCandidate(entityId: EntityId, sessionId: string, candidate: RtcIceCandidateInit): Promise<void>;
31
- export {};
@@ -1,53 +0,0 @@
1
- /**
2
- * Service calls and commands
3
- *
4
- * Type-safe wrappers for Home Assistant service calls and commands.
5
- *
6
- * @packageDocumentation
7
- */
8
- import type { Domain, ServiceCall, ServiceName, WsCommandType } from "@glasshome/ha-types";
9
- import type { EntityId } from "../core/types";
10
- import type { EntityUpdateFields, ServiceTarget } from "./types";
11
- /**
12
- * Call a Home Assistant service
13
- */
14
- export declare function callService<D extends Domain, S extends ServiceName<D>>(domain: D, service: S, serviceData?: ServiceCall<D, S>, target?: ServiceTarget): Promise<void>;
15
- /**
16
- * Turn on entity or entities
17
- */
18
- export declare function turnOn(entityId: EntityId | EntityId[], serviceData?: Record<string, any>): Promise<void>;
19
- /**
20
- * Turn off entity or entities
21
- */
22
- export declare function turnOff(entityId: EntityId | EntityId[], serviceData?: Record<string, any>): Promise<void>;
23
- /**
24
- * Toggle entity or entities
25
- */
26
- export declare function toggle(entityId: EntityId | EntityId[], serviceData?: Record<string, any>): Promise<void>;
27
- /**
28
- * Update entity registry entry
29
- */
30
- export declare function updateEntity(entityId: EntityId, updates: EntityUpdateFields): Promise<any>;
31
- /**
32
- * Send a generic WebSocket command
33
- */
34
- export declare function sendCommand<T = unknown>(command: {
35
- type: WsCommandType | string;
36
- [key: string]: unknown;
37
- }): Promise<T>;
38
- /**
39
- * Execute multiple service calls in parallel
40
- */
41
- export declare function batchServiceCalls(calls: Array<{
42
- domain: Domain;
43
- service: ServiceName<Domain>;
44
- serviceData?: ServiceCall<Domain, ServiceName<Domain>>;
45
- target?: ServiceTarget;
46
- }>): Promise<void>;
47
- /**
48
- * Execute multiple entity updates in parallel
49
- */
50
- export declare function batchEntityUpdates(updates: Array<{
51
- entityId: EntityId;
52
- updates: EntityUpdateFields;
53
- }>): Promise<any[]>;