@glasshome/sync-layer 0.2.2 → 0.3.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.
@@ -7,7 +7,7 @@ import type { HaBridge } from "../worker/bridge-client";
7
7
  *
8
8
  * Call after bridge.connect() resolved. Returns when initial data is loaded.
9
9
  */
10
- export declare function attachBridgeToStore(bridge: HaBridge, hassUrl: string): Promise<void>;
10
+ export declare function attachBridgeToStore(bridge: HaBridge, hassUrl: string, mediaProxyBase?: string): Promise<void>;
11
11
  /** Map worker connection-state messages onto the store. */
12
12
  export declare function applyBridgeConnState(state: "connected" | "disconnected" | "reconnecting"): void;
13
13
  /** Re-sync after the worker reports a completed reconnect. */
@@ -32,6 +32,9 @@ export interface GlassHomeState {
32
32
  connectionError: Error | null;
33
33
  /** Home Assistant base URL */
34
34
  hassUrl: string | null;
35
+ /** Same-origin prefix for the HA media proxy (e.g. "/ha-proxy/media"), or null
36
+ * to resolve media directly against hassUrl. Set by the host at connect. */
37
+ mediaProxyBase: string | null;
35
38
  /** Home Assistant core config (unit system, locale, currency); null until loaded */
36
39
  config: import("@glasshome/ha-types").HassConfig | null;
37
40
  /** Live entity state (keyed by entity_id) */
@@ -6,6 +6,7 @@ const initialState = {
6
6
  connectionState: "disconnected",
7
7
  connectionError: null,
8
8
  hassUrl: null,
9
+ mediaProxyBase: null,
9
10
  config: null,
10
11
  entities: {},
11
12
  entityRegistry: {},
package/dist/index.d.ts CHANGED
@@ -27,6 +27,7 @@ export type { CallServiceRequest, CoreEventType, Domain, EntityCategory, EntityR
27
27
  export { fetchStream, fetchStreamData } from "./camera/fetch";
28
28
  export { getStream, refreshStream } from "./camera/query";
29
29
  export { fetchEnergyPreferences } from "./energy/prefs";
30
+ export { hassMediaUrl } from "./media/url";
30
31
  export type { EnergyDeviceConsumption, EnergyPreferences, EnergySource, } from "./energy/prefs";
31
32
  export type { CameraStream, CameraStreamData, CameraStreams, EntityStreamQueryOptions, StreamFormat, StreamQueryOptions, StreamResult, } from "./camera/types";
32
33
  export { getWebRtcClientConfig, sendWebRtcCandidate, startWebRtcSession, } from "./camera/webrtc";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ENERGY_ENTITY_IDS, createDemoFixtures, demoEnergyPreferences, energyEntityValue, formatEnergyState, isEnergyEntity, isSunUp, resetStore, setState, simulateEnergy, state, sunEvents, synthesizeEnergyHistory, synthesizeEnergyStatistics } from "./demo-data-CavpxBw3.js";
1
+ import { ENERGY_ENTITY_IDS, createDemoFixtures, demoEnergyPreferences, energyEntityValue, formatEnergyState, isEnergyEntity, isSunUp, resetStore, setState, simulateEnergy, state, sunEvents, synthesizeEnergyHistory, synthesizeEnergyStatistics } from "./demo-data-DiGSP4EH.js";
2
2
  import { runHaBridgeWorker } from "./worker-main-BYGgbA7k.js";
3
3
  import { createConnection, createLongLivedTokenAuth, getAuth } from "home-assistant-js-websocket";
4
4
  import { produce, reconcile } from "solid-js/store";
@@ -2298,6 +2298,30 @@ async function fetchEnergyPreferences(connection) {
2298
2298
  };
2299
2299
  }
2300
2300
 
2301
+ //#endregion
2302
+ //#region src/media/url.ts
2303
+ /**
2304
+ * Resolve an HA media reference (e.g. an entity's `entity_picture`, a camera
2305
+ * proxy path) to a URL the page can actually load.
2306
+ *
2307
+ * HA gives these as origin-relative paths (`/api/media_player_proxy/...?token=`)
2308
+ * carrying their own signed token. A bare `<img src>` would resolve them against
2309
+ * the dash origin and 404. When the host set `mediaProxyBase`, route through the
2310
+ * same-origin media proxy so the page CSP (`img-src 'self'`) covers it in every
2311
+ * mode, LAN and remote. Otherwise fall back to prefixing `hassUrl` directly
2312
+ * (the LAN-only path, used until a transport sets the proxy base).
2313
+ *
2314
+ * Absolute URLs and non-path values pass through untouched.
2315
+ */
2316
+ function hassMediaUrl(path) {
2317
+ if (!path) return void 0;
2318
+ if (/^https?:\/\//i.test(path)) return path;
2319
+ if (!path.startsWith("/")) return path;
2320
+ const base = state.mediaProxyBase;
2321
+ if (base) return `${base}?path=${encodeURIComponent(path)}`;
2322
+ return state.hassUrl ? `${state.hassUrl}${path}` : path;
2323
+ }
2324
+
2301
2325
  //#endregion
2302
2326
  //#region src/camera/webrtc.ts
2303
2327
  /**
@@ -2720,10 +2744,11 @@ async function refreshForecasts(entityId, types) {
2720
2744
  *
2721
2745
  * Call after bridge.connect() resolved. Returns when initial data is loaded.
2722
2746
  */
2723
- async function attachBridgeToStore(bridge, hassUrl) {
2747
+ async function attachBridgeToStore(bridge, hassUrl, mediaProxyBase) {
2724
2748
  setState(produce((s) => {
2725
2749
  s.conn = bridge.conn;
2726
2750
  s.hassUrl = hassUrl.replace(/\/$/, "");
2751
+ s.mediaProxyBase = mediaProxyBase ?? null;
2727
2752
  s.connectionState = "connected";
2728
2753
  s.connectionError = null;
2729
2754
  }));
@@ -2919,4 +2944,4 @@ function createHaBridge(worker, events = {}) {
2919
2944
  const VERSION = "0.2.1";
2920
2945
 
2921
2946
  //#endregion
2922
- export { BridgeNeedsAuthError, DOMAINS_USE_LAST_UPDATED, LINE_ATTRIBUTES_TO_KEEP, MAX_HISTORY_POINTS, NEED_ATTRIBUTE_DOMAINS, VERSION, addDebugIncomingMessageListener, addDebugOutgoingMessageListener, appendHistoryPoint, applyBridgeConnState, applyDemoServiceCall, area, areas, attachBridgeToStore, authenticateWithOAuth, authenticateWithToken, batchEntityUpdates, batchServiceCalls, buildAreaView, buildDeviceView, buildEntityView, bulkAppendHistoryPoints, callService, createDemoFixtures, createHaBridge, detachBridgeFromStore, disconnect, entities, entitiesByAreaAndDomain, entitiesWhere, entity, entityIdHistoryNeedsAttributes, entityViewEquals, extractDomain, fetchEnergyPreferences, fetchEntityHistory, fetchForecast, fetchForecastData, fetchForecasts, fetchHistory, fetchStatisticsDuringPeriod, fetchStream, fetchStreamData, getAllEntityViews, getAreaView, getAreaViews, getConnection, getConnectionState, getEntityView, getEntityViews, getForecast, getForecasts, getStream, getWebRtcClientConfig, historyStateToTimeline, initConnection, isAuthValid, isConnected, isDemoMode, isHistoryTracked, loadDemoData, normalizeStatisticTime, refreshAuth, refreshForecast, refreshForecasts, refreshStream, registerEntity, reloadAfterBridgeReconnect, resetStore, runHaBridgeWorker, searchEntities, sendCommand, sendWebRtcCandidate, setState, startDemoEnergyTicker, startWebRtcSession, state, stopDemoEnergyTicker, toggle, trackEntityHistory, turnOff, turnOn, unloadDemoData, untrackEntityHistory, updateEntity };
2947
+ export { BridgeNeedsAuthError, DOMAINS_USE_LAST_UPDATED, LINE_ATTRIBUTES_TO_KEEP, MAX_HISTORY_POINTS, NEED_ATTRIBUTE_DOMAINS, VERSION, addDebugIncomingMessageListener, addDebugOutgoingMessageListener, appendHistoryPoint, applyBridgeConnState, applyDemoServiceCall, area, areas, attachBridgeToStore, authenticateWithOAuth, authenticateWithToken, batchEntityUpdates, batchServiceCalls, buildAreaView, buildDeviceView, buildEntityView, bulkAppendHistoryPoints, callService, createDemoFixtures, createHaBridge, detachBridgeFromStore, disconnect, entities, entitiesByAreaAndDomain, entitiesWhere, entity, entityIdHistoryNeedsAttributes, entityViewEquals, extractDomain, fetchEnergyPreferences, fetchEntityHistory, fetchForecast, fetchForecastData, fetchForecasts, fetchHistory, fetchStatisticsDuringPeriod, fetchStream, fetchStreamData, getAllEntityViews, getAreaView, getAreaViews, getConnection, getConnectionState, getEntityView, getEntityViews, getForecast, getForecasts, getStream, getWebRtcClientConfig, hassMediaUrl, historyStateToTimeline, initConnection, isAuthValid, isConnected, isDemoMode, isHistoryTracked, loadDemoData, normalizeStatisticTime, refreshAuth, refreshForecast, refreshForecasts, refreshStream, registerEntity, reloadAfterBridgeReconnect, resetStore, runHaBridgeWorker, searchEntities, sendCommand, sendWebRtcCandidate, setState, startDemoEnergyTicker, startWebRtcSession, state, stopDemoEnergyTicker, toggle, trackEntityHistory, turnOff, turnOn, unloadDemoData, untrackEntityHistory, updateEntity };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve an HA media reference (e.g. an entity's `entity_picture`, a camera
3
+ * proxy path) to a URL the page can actually load.
4
+ *
5
+ * HA gives these as origin-relative paths (`/api/media_player_proxy/...?token=`)
6
+ * carrying their own signed token. A bare `<img src>` would resolve them against
7
+ * the dash origin and 404. When the host set `mediaProxyBase`, route through the
8
+ * same-origin media proxy so the page CSP (`img-src 'self'`) covers it in every
9
+ * mode, LAN and remote. Otherwise fall back to prefixing `hassUrl` directly
10
+ * (the LAN-only path, used until a transport sets the proxy base).
11
+ *
12
+ * Absolute URLs and non-path values pass through untouched.
13
+ */
14
+ export declare function hassMediaUrl(path: string | null | undefined): string | undefined;
@@ -1,4 +1,4 @@
1
- import { createDemoFixtures, state } from "../demo-data-CavpxBw3.js";
1
+ import { createDemoFixtures, state } from "../demo-data-DiGSP4EH.js";
2
2
 
3
3
  //#region src/testing/mock-behaviors/base.ts
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glasshome/sync-layer",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Type-safe, reactive state synchronization layer for Home Assistant",
5
5
  "repository": {
6
6
  "type": "git",