@glasshome/sync-layer 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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -0
  3. package/dist/area-views-B-m8vwce.js +321 -0
  4. package/dist/camera/fetch.d.ts +15 -0
  5. package/dist/camera/query.d.ts +15 -0
  6. package/dist/camera/types.d.ts +85 -0
  7. package/dist/camera/webrtc.d.ts +31 -0
  8. package/dist/commands/service.d.ts +53 -0
  9. package/dist/commands/types.d.ts +113 -0
  10. package/dist/connection/adapter.d.ts +48 -0
  11. package/dist/connection/auth.d.ts +126 -0
  12. package/dist/connection/manager.d.ts +42 -0
  13. package/dist/connection/subscriptions.d.ts +25 -0
  14. package/dist/connection/types.d.ts +161 -0
  15. package/dist/core/reducers.d.ts +99 -0
  16. package/dist/core/store.d.ts +64 -0
  17. package/dist/core/types.d.ts +336 -0
  18. package/dist/demo/demo-data.d.ts +24 -0
  19. package/dist/demo/demo-provider.d.ts +33 -0
  20. package/dist/demo/demo-provider.js +103 -0
  21. package/dist/demo-data-D8JXWRpw.js +419 -0
  22. package/dist/entities/area-views.d.ts +25 -0
  23. package/dist/entities/index.d.ts +54 -0
  24. package/dist/entities/views.d.ts +30 -0
  25. package/dist/history/constants.d.ts +16 -0
  26. package/dist/history/fetch.d.ts +19 -0
  27. package/dist/history/query.d.ts +13 -0
  28. package/dist/history/types.d.ts +146 -0
  29. package/dist/index.d.ts +38 -0
  30. package/dist/index.js +1747 -0
  31. package/dist/query/area-builder.d.ts +25 -0
  32. package/dist/query/builder.d.ts +18 -0
  33. package/dist/query/helpers.d.ts +21 -0
  34. package/dist/query/memoization.d.ts +18 -0
  35. package/dist/query/projections.d.ts +74 -0
  36. package/dist/query/types.d.ts +260 -0
  37. package/dist/solid/hooks.d.ts +88 -0
  38. package/dist/solid/index.d.ts +10 -0
  39. package/dist/solid/index.js +176 -0
  40. package/dist/solid/indices.d.ts +14 -0
  41. package/dist/store-B1sbyexX.js +54 -0
  42. package/dist/testing/fixtures.d.ts +161 -0
  43. package/dist/testing/helpers.d.ts +160 -0
  44. package/dist/testing/index.d.ts +13 -0
  45. package/dist/testing/index.js +2047 -0
  46. package/dist/testing/mock-behaviors/base.d.ts +73 -0
  47. package/dist/testing/mock-behaviors/climate.d.ts +22 -0
  48. package/dist/testing/mock-behaviors/cover.d.ts +22 -0
  49. package/dist/testing/mock-behaviors/index.d.ts +11 -0
  50. package/dist/testing/mock-behaviors/light.d.ts +20 -0
  51. package/dist/testing/mock-behaviors/lock.d.ts +20 -0
  52. package/dist/testing/mock-behaviors/media_player.d.ts +42 -0
  53. package/dist/testing/mock-behaviors/registry.d.ts +55 -0
  54. package/dist/testing/mock-behaviors/switch.d.ts +17 -0
  55. package/dist/testing/mock.d.ts +191 -0
  56. package/dist/testing/types.d.ts +87 -0
  57. package/dist/types-Csog0nqk.js +10 -0
  58. package/dist/weather/fetch.d.ts +19 -0
  59. package/dist/weather/query.d.ts +23 -0
  60. package/dist/weather/types.d.ts +126 -0
  61. package/package.json +85 -0
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Area Query Builder
3
+ *
4
+ * Fluent API for filtering and querying areas.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ import type { AreaId, AreaView, Callback, FloorId, LabelId, PredicateFunc, UnsubscribeFunc } from "../core/types";
9
+ export interface AreaQueryBuilder {
10
+ byFloor(floorId: FloorId): AreaQueryBuilder;
11
+ byLabel(labelId: LabelId): AreaQueryBuilder;
12
+ where(predicate: PredicateFunc<AreaView>): AreaQueryBuilder;
13
+ orderBy(key: keyof AreaView | string, direction?: "asc" | "desc"): AreaQueryBuilder;
14
+ get(): AreaView[];
15
+ first(): AreaView | undefined;
16
+ subscribe(callback: Callback<AreaView[]>): UnsubscribeFunc;
17
+ }
18
+ /**
19
+ * Create a query builder for areas
20
+ */
21
+ export declare function areas(): AreaQueryBuilder;
22
+ /**
23
+ * Create a query for a single area
24
+ */
25
+ export declare function area(areaId: AreaId): AreaView | undefined;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Query Builder
3
+ *
4
+ * Fluent API for filtering, sorting, projecting, and subscribing to entities.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ import type { EntityId, EntityView } from "../core/types";
9
+ import type { EntitiesQueryBuilder, EntityQueryBuilder } from "./types";
10
+ /**
11
+ * Create a query builder for multiple entities
12
+ */
13
+ export declare function entities(): EntitiesQueryBuilder<EntityView>;
14
+ /**
15
+ * Create a query builder for a single entity
16
+ */
17
+ export declare function entity(entityId: EntityId): EntityQueryBuilder;
18
+ export type { EntitiesQueryBuilder, EntityQueryBuilder, UnsubscribeFunc } from "./types";
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Query Engine Helpers
3
+ *
4
+ * Helper functions to make query engine usage easier and more intuitive.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ import type { AreaId, EntityDomain, EntityView } from "../core/types";
9
+ import type { EntitiesQueryBuilder } from "./types";
10
+ /**
11
+ * Create a search query builder
12
+ */
13
+ export declare function searchEntities(searchText: string): EntitiesQueryBuilder<EntityView>;
14
+ /**
15
+ * Create a query for entities in an area by domain
16
+ */
17
+ export declare function entitiesByAreaAndDomain(areaId: AreaId, domain: EntityDomain): EntitiesQueryBuilder<EntityView>;
18
+ /**
19
+ * Create a query for entities matching a predicate
20
+ */
21
+ export declare function entitiesWhere(predicate: (entity: EntityView) => boolean): EntitiesQueryBuilder<EntityView>;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Query memoization
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import type { QueryState } from "./types";
7
+ /**
8
+ * Build a unique signature for a query
9
+ */
10
+ export declare function buildQuerySignature(query: QueryState): string;
11
+ /**
12
+ * Execute query (no caching -- SolidJS createMemo handles this in Plan 03)
13
+ */
14
+ export declare function memoizedExecute<T>(signature: string, compute: () => T): T;
15
+ /**
16
+ * Check if cache should be invalidated
17
+ */
18
+ export declare function isCacheStale(lastCheck: number, maxAge: number): boolean;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Projection utilities
3
+ *
4
+ * Functions for transforming entity views into different shapes.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ import type { EntityId, EntityView, MapFunc } from "../core/types";
9
+ /**
10
+ * Project entities to IDs only
11
+ *
12
+ * @param entities - Array of entity views
13
+ * @returns Array of entity IDs
14
+ */
15
+ export declare function projectToIds(entities: EntityView[]): EntityId[];
16
+ /**
17
+ * Pick specific fields from entities
18
+ *
19
+ * @param entities - Array of entity views
20
+ * @param keys - Array of field names to include
21
+ * @returns Array of objects with only specified fields
22
+ */
23
+ export declare function projectPick<K extends keyof EntityView>(entities: EntityView[], keys: K[]): Pick<EntityView, K>[];
24
+ /**
25
+ * Pluck a single field from entities
26
+ *
27
+ * Supports dot-notation for nested fields (e.g., "attributes.brightness")
28
+ *
29
+ * @param entities - Array of entity views
30
+ * @param key - Field name or dot-path to extract
31
+ * @returns Array of field values
32
+ */
33
+ export declare function projectPluck(entities: EntityView[], key: string): any[];
34
+ /**
35
+ * Map entities using a custom function
36
+ *
37
+ * @param entities - Array of entity views
38
+ * @param mapper - Function to transform each entity
39
+ * @returns Array of mapped values
40
+ */
41
+ export declare function projectMap<R>(entities: EntityView[], mapper: MapFunc<EntityView, R>): R[];
42
+ /**
43
+ * Get nested value from object using dot-notation
44
+ *
45
+ * @param obj - Object to extract value from
46
+ * @param path - Dot-notation path (e.g., "attributes.brightness")
47
+ * @returns Value at path or undefined
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const entity = { attributes: { brightness: 255 } };
52
+ * getNestedValue(entity, "attributes.brightness"); // 255
53
+ * ```
54
+ */
55
+ export declare function getNestedValue(obj: any, path: string): any;
56
+ /**
57
+ * Set nested value in object using dot-notation
58
+ *
59
+ * @param obj - Object to set value in
60
+ * @param path - Dot-notation path
61
+ * @param value - Value to set
62
+ *
63
+ * @internal
64
+ */
65
+ export declare function setNestedValue(obj: any, path: string, value: any): void;
66
+ /**
67
+ * Check if a value is a primitive
68
+ *
69
+ * @param value - Value to check
70
+ * @returns True if primitive
71
+ *
72
+ * @internal
73
+ */
74
+ export declare function isPrimitive(value: any): boolean;
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Query-related types
3
+ *
4
+ * Type definitions for the query builder system.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ import type { AreaId, AreaView, Callback, DeviceId, EntityDomain, EntityId, EntityView, FloorId, LabelId, MapFunc, PredicateFunc, UnsubscribeFunc } from "../core/types";
9
+ export type { UnsubscribeFunc };
10
+ /**
11
+ * Main query builder interface for multiple entities
12
+ *
13
+ * Provides a fluent API for filtering, sorting, projecting, and subscribing.
14
+ *
15
+ * @template T - The result type (defaults to EntityView)
16
+ */
17
+ export interface EntitiesQueryBuilder<T = EntityView> {
18
+ /**
19
+ * Filter by area
20
+ *
21
+ * @param areaId - Area ID to filter by
22
+ * @returns Query builder for chaining
23
+ */
24
+ byArea(areaId: AreaId): EntitiesQueryBuilder<T>;
25
+ /**
26
+ * Filter by domain
27
+ *
28
+ * @param domain - Domain to filter by (e.g., "light", "sensor")
29
+ * @returns Query builder for chaining
30
+ */
31
+ byDomain(domain: EntityDomain): EntitiesQueryBuilder<T>;
32
+ /**
33
+ * Filter by device
34
+ *
35
+ * @param deviceId - Device ID to filter by
36
+ * @returns Query builder for chaining
37
+ */
38
+ byDevice(deviceId: DeviceId): EntitiesQueryBuilder<T>;
39
+ /**
40
+ * Filter by floor
41
+ *
42
+ * @param floorId - Floor ID to filter by
43
+ * @returns Query builder for chaining
44
+ */
45
+ byFloor(floorId: FloorId): EntitiesQueryBuilder<T>;
46
+ /**
47
+ * Filter by label
48
+ *
49
+ * @param labelId - Label ID to filter by
50
+ * @returns Query builder for chaining
51
+ */
52
+ byLabel(labelId: LabelId): EntitiesQueryBuilder<T>;
53
+ /**
54
+ * Filter with custom predicate
55
+ *
56
+ * @param predicate - Function to test each entity
57
+ * @returns Query builder for chaining
58
+ */
59
+ where(predicate: PredicateFunc<EntityView>): EntitiesQueryBuilder<T>;
60
+ /**
61
+ * Search entities by text (searches id, name, friendlyName)
62
+ *
63
+ * @param searchText - Text to search for
64
+ * @returns Query builder for chaining
65
+ */
66
+ search(searchText: string): EntitiesQueryBuilder<T>;
67
+ /**
68
+ * Sort results
69
+ *
70
+ * @param key - Property key or dot-path (e.g., "name", "attributes.brightness")
71
+ * @param direction - Sort direction ("asc" or "desc")
72
+ * @returns Query builder for chaining
73
+ */
74
+ orderBy(key: string, direction?: "asc" | "desc"): EntitiesQueryBuilder<T>;
75
+ /**
76
+ * Project to entity IDs only
77
+ *
78
+ * @returns Query builder for entity IDs
79
+ */
80
+ ids(): EntitiesQueryBuilder<EntityId>;
81
+ /**
82
+ * Pick specific fields
83
+ *
84
+ * @param keys - Array of field names to include
85
+ * @returns Query builder with picked fields
86
+ */
87
+ pick<K extends keyof EntityView>(keys: K[]): EntitiesQueryBuilder<Pick<EntityView, K>>;
88
+ /**
89
+ * Extract a single field from each entity
90
+ *
91
+ * @param key - Field name to extract
92
+ * @returns Query builder for field values
93
+ */
94
+ pluck<K extends keyof EntityView>(key: K): EntitiesQueryBuilder<EntityView[K]>;
95
+ /**
96
+ * Map entities to new values
97
+ *
98
+ * @param mapper - Function to transform each entity
99
+ * @returns Query builder for mapped values
100
+ */
101
+ map<R>(mapper: MapFunc<EntityView, R>): EntitiesQueryBuilder<R>;
102
+ /**
103
+ * Limit the number of results
104
+ *
105
+ * @param limit - Maximum number of results to return
106
+ * @returns Query builder for chaining
107
+ */
108
+ take(limit: number): EntitiesQueryBuilder<T>;
109
+ /**
110
+ * Skip a number of results (for pagination)
111
+ *
112
+ * @param count - Number of results to skip
113
+ * @returns Query builder for chaining
114
+ */
115
+ skip(count: number): EntitiesQueryBuilder<T>;
116
+ /**
117
+ * Get distinct values by a key
118
+ *
119
+ * @param key - Field name or dot-path to get distinct values for
120
+ * @returns Query builder for distinct values
121
+ */
122
+ distinct(key: string): EntitiesQueryBuilder<T>;
123
+ /**
124
+ * Execute query and return results
125
+ *
126
+ * @returns Array of results
127
+ */
128
+ get(): T[];
129
+ /**
130
+ * Get first result
131
+ *
132
+ * @returns First result or undefined
133
+ */
134
+ first(): T | undefined;
135
+ /**
136
+ * Subscribe to query results
137
+ *
138
+ * Receives updates when entities matching the query change.
139
+ *
140
+ * @param callback - Function called with results
141
+ * @returns Unsubscribe function
142
+ */
143
+ subscribe(callback: Callback<T[]>): UnsubscribeFunc;
144
+ }
145
+ /**
146
+ * Query builder interface for areas
147
+ */
148
+ export interface AreaQueryBuilder {
149
+ /**
150
+ * Filter areas by floor
151
+ */
152
+ byFloor(floorId: FloorId): AreaQueryBuilder;
153
+ /**
154
+ * Filter areas by label
155
+ */
156
+ byLabel(labelId: LabelId): AreaQueryBuilder;
157
+ /**
158
+ * Filter areas using a predicate
159
+ */
160
+ where(predicate: PredicateFunc<AreaView>): AreaQueryBuilder;
161
+ /**
162
+ * Order results by a key
163
+ */
164
+ orderBy(key: keyof AreaView | string, direction?: "asc" | "desc"): AreaQueryBuilder;
165
+ /**
166
+ * Execute query and get results
167
+ */
168
+ get(): AreaView[];
169
+ /**
170
+ * Get first result
171
+ */
172
+ first(): AreaView | undefined;
173
+ /**
174
+ * Subscribe to query results
175
+ */
176
+ subscribe(callback: Callback<AreaView[]>): UnsubscribeFunc;
177
+ }
178
+ /**
179
+ * Query builder interface for a single entity
180
+ */
181
+ export interface EntityQueryBuilder {
182
+ /**
183
+ * Get entity view
184
+ *
185
+ * @returns Entity view or undefined
186
+ */
187
+ get(): EntityView | undefined;
188
+ /**
189
+ * Extract a single field
190
+ *
191
+ * @param key - Field name to extract
192
+ * @returns Field value or undefined
193
+ */
194
+ pluck<K extends keyof EntityView>(key: K): EntityView[K] | undefined;
195
+ /**
196
+ * Subscribe to entity changes
197
+ *
198
+ * @param callback - Function called with entity view
199
+ * @returns Unsubscribe function
200
+ */
201
+ subscribe(callback: Callback<EntityView | undefined>): UnsubscribeFunc;
202
+ }
203
+ /**
204
+ * Internal query state (not exported)
205
+ *
206
+ * @internal
207
+ */
208
+ export interface QueryState {
209
+ /** Index-based filters (use indices for fast lookup) */
210
+ indexFilters: IndexFilter[];
211
+ /** Predicate-based filters (run on each entity) */
212
+ predicateFilters: PredicateFunc<EntityView>[];
213
+ /** Sort functions */
214
+ sorts: SortConfig[];
215
+ /** Projection configuration */
216
+ projection?: ProjectionConfig;
217
+ /** Search text (for text search functionality) */
218
+ searchText?: string;
219
+ /** Limit number of results */
220
+ take?: number;
221
+ /** Skip number of results (for pagination) */
222
+ skip?: number;
223
+ /** Distinct key for deduplication */
224
+ distinct?: string;
225
+ }
226
+ /**
227
+ * Index filter (uses store indices for fast lookup)
228
+ *
229
+ * @internal
230
+ */
231
+ export interface IndexFilter {
232
+ type: "domain" | "area" | "device" | "floor" | "label";
233
+ value: string;
234
+ }
235
+ /**
236
+ * Sort configuration
237
+ *
238
+ * @internal
239
+ */
240
+ export interface SortConfig {
241
+ key: string;
242
+ direction: "asc" | "desc";
243
+ }
244
+ /**
245
+ * Projection configuration
246
+ *
247
+ * @internal
248
+ */
249
+ export type ProjectionConfig = {
250
+ type: "ids";
251
+ } | {
252
+ type: "pick";
253
+ keys: string[];
254
+ } | {
255
+ type: "pluck";
256
+ key: string;
257
+ } | {
258
+ type: "map";
259
+ mapper: MapFunc<EntityView, any>;
260
+ };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * SolidJS Hooks for Sync-Layer
3
+ *
4
+ * Thin createMemo wrappers over the native SolidJS store.
5
+ * Since the store IS SolidJS (Plan 01 replaced Zustand with createStore),
6
+ * no subscription bridge is needed -- SolidJS auto-tracks fine-grained
7
+ * reactivity through the store proxy.
8
+ *
9
+ * Every hook is just `createMemo(() => state.something)`.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+ import type { Accessor } from "solid-js";
14
+ import type { CameraStreamData } from "../camera/types";
15
+ import { callService, toggle, turnOff, turnOn } from "../commands/service";
16
+ import type { GlassHomeState } from "../core/store";
17
+ import type { AreaView, ConnectionState, EntityView, HassEntity } from "../core/types";
18
+ import type { EntityHistoryData } from "../history/types";
19
+ import type { WeatherForecastsData } from "../weather/types";
20
+ /**
21
+ * Get a reactive EntityView for an entity ID.
22
+ *
23
+ * Direct createMemo over store -- zero overhead, fine-grained reactivity.
24
+ * Only re-runs when the specific entity's data changes.
25
+ */
26
+ export declare function useEntity(entityId: Accessor<string> | string): Accessor<EntityView | undefined>;
27
+ /**
28
+ * Get reactive EntityViews for multiple entity IDs.
29
+ */
30
+ export declare function useEntities(entityIds: Accessor<string[]>): Accessor<EntityView[]>;
31
+ /**
32
+ * Get raw HassEntity state (without EntityView transformation).
33
+ */
34
+ export declare function useEntityState(entityId: Accessor<string> | string): Accessor<HassEntity | undefined>;
35
+ /**
36
+ * Get reactive connection status.
37
+ */
38
+ export declare function useConnection(): {
39
+ status: Accessor<ConnectionState>;
40
+ isConnected: Accessor<boolean>;
41
+ };
42
+ /**
43
+ * Get all service call functions.
44
+ */
45
+ export declare function useService(): {
46
+ callService: typeof callService;
47
+ turnOn: typeof turnOn;
48
+ turnOff: typeof turnOff;
49
+ toggle: typeof toggle;
50
+ };
51
+ /** Get turnOn command function */
52
+ export declare function useTurnOn(): typeof turnOn;
53
+ /** Get turnOff command function */
54
+ export declare function useTurnOff(): typeof turnOff;
55
+ /** Get toggle command function */
56
+ export declare function useToggle(): typeof toggle;
57
+ /**
58
+ * Get reactive list of all area views.
59
+ */
60
+ export declare function useAreas(): Accessor<AreaView[]>;
61
+ /**
62
+ * Get a reactive AreaView for a specific area.
63
+ */
64
+ export declare function useArea(areaId: Accessor<string> | string): Accessor<AreaView | undefined>;
65
+ /**
66
+ * Get reactive entity history data.
67
+ *
68
+ * History data is populated into the store via subscriptions;
69
+ * this hook simply reads it reactively.
70
+ */
71
+ export declare function useEntityHistory(entityId: Accessor<string> | string): Accessor<EntityHistoryData | undefined>;
72
+ /**
73
+ * Get reactive camera stream data.
74
+ */
75
+ export declare function useCamera(entityId: Accessor<string> | string): {
76
+ stream: Accessor<CameraStreamData | null>;
77
+ refresh: () => void;
78
+ };
79
+ /**
80
+ * Get reactive weather forecast data.
81
+ */
82
+ export declare function useForecast(entityId: Accessor<string> | string): Accessor<WeatherForecastsData | undefined>;
83
+ /**
84
+ * Escape hatch for direct store access with a selector.
85
+ *
86
+ * Use when no specific hook exists for your data need.
87
+ */
88
+ export declare function useStore<T>(selector: (s: GlassHomeState) => T): Accessor<T>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * SolidJS Adapter for @glasshome/sync-layer
3
+ *
4
+ * Thin reactive layer over the native SolidJS store.
5
+ * Import from `@glasshome/sync-layer/solid`.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export { useArea, useAreas, useCamera, useConnection, useEntities, useEntity, useEntityHistory, useEntityState, useForecast, useService, useStore, useToggle, useTurnOff, useTurnOn, } from "./hooks";
10
+ export { allEntityIds, byArea, byDevice, byDomain, byFloor, byLabel, } from "./indices";
@@ -0,0 +1,176 @@
1
+ import { state } from "../store-B1sbyexX.js";
2
+ import { buildAreaView, callService, getEntityView, toggle, turnOff, turnOn } from "../area-views-B-m8vwce.js";
3
+ import { extractDomain } from "../types-Csog0nqk.js";
4
+ import { createMemo } from "solid-js";
5
+
6
+ //#region src/solid/hooks.ts
7
+ /**
8
+ * Get a reactive EntityView for an entity ID.
9
+ *
10
+ * Direct createMemo over store -- zero overhead, fine-grained reactivity.
11
+ * Only re-runs when the specific entity's data changes.
12
+ */
13
+ function useEntity(entityId) {
14
+ const getId = typeof entityId === "function" ? entityId : () => entityId;
15
+ return createMemo(() => {
16
+ const id = getId();
17
+ if (!id) return void 0;
18
+ return getEntityView(id);
19
+ });
20
+ }
21
+ /**
22
+ * Get reactive EntityViews for multiple entity IDs.
23
+ */
24
+ function useEntities(entityIds) {
25
+ return createMemo(() => {
26
+ return entityIds().map((id) => getEntityView(id)).filter((v) => v !== void 0);
27
+ });
28
+ }
29
+ /**
30
+ * Get raw HassEntity state (without EntityView transformation).
31
+ */
32
+ function useEntityState(entityId) {
33
+ const getId = typeof entityId === "function" ? entityId : () => entityId;
34
+ return createMemo(() => state.entities[getId()]);
35
+ }
36
+ /**
37
+ * Get reactive connection status.
38
+ */
39
+ function useConnection() {
40
+ const status = createMemo(() => state.connectionState);
41
+ const isConnected = createMemo(() => state.connectionState === "connected");
42
+ return {
43
+ status,
44
+ isConnected
45
+ };
46
+ }
47
+ /**
48
+ * Get all service call functions.
49
+ */
50
+ function useService() {
51
+ return {
52
+ callService,
53
+ turnOn,
54
+ turnOff,
55
+ toggle
56
+ };
57
+ }
58
+ /** Get turnOn command function */
59
+ function useTurnOn() {
60
+ return turnOn;
61
+ }
62
+ /** Get turnOff command function */
63
+ function useTurnOff() {
64
+ return turnOff;
65
+ }
66
+ /** Get toggle command function */
67
+ function useToggle() {
68
+ return toggle;
69
+ }
70
+ /**
71
+ * Get reactive list of all area views.
72
+ */
73
+ function useAreas() {
74
+ return createMemo(() => {
75
+ return Object.keys(state.areas).map((areaId) => {
76
+ try {
77
+ return buildAreaView(areaId);
78
+ } catch {
79
+ return void 0;
80
+ }
81
+ }).filter((v) => v !== void 0);
82
+ });
83
+ }
84
+ /**
85
+ * Get a reactive AreaView for a specific area.
86
+ */
87
+ function useArea(areaId) {
88
+ const getId = typeof areaId === "function" ? areaId : () => areaId;
89
+ return createMemo(() => {
90
+ const id = getId();
91
+ if (!id || !state.areas[id]) return void 0;
92
+ try {
93
+ return buildAreaView(id);
94
+ } catch {
95
+ return void 0;
96
+ }
97
+ });
98
+ }
99
+ /**
100
+ * Get reactive entity history data.
101
+ *
102
+ * History data is populated into the store via subscriptions;
103
+ * this hook simply reads it reactively.
104
+ */
105
+ function useEntityHistory(entityId) {
106
+ const getId = typeof entityId === "function" ? entityId : () => entityId;
107
+ return createMemo(() => state.history[getId()]);
108
+ }
109
+ /**
110
+ * Get reactive camera stream data.
111
+ */
112
+ function useCamera(entityId) {
113
+ const getId = typeof entityId === "function" ? entityId : () => entityId;
114
+ return {
115
+ stream: createMemo(() => state.streams[getId()] ?? null),
116
+ refresh: () => {}
117
+ };
118
+ }
119
+ /**
120
+ * Get reactive weather forecast data.
121
+ */
122
+ function useForecast(entityId) {
123
+ const getId = typeof entityId === "function" ? entityId : () => entityId;
124
+ return createMemo(() => state.forecasts[getId()]);
125
+ }
126
+ /**
127
+ * Escape hatch for direct store access with a selector.
128
+ *
129
+ * Use when no specific hook exists for your data need.
130
+ */
131
+ function useStore(selector) {
132
+ return createMemo(() => selector(state));
133
+ }
134
+
135
+ //#endregion
136
+ //#region src/solid/indices.ts
137
+ /**
138
+ * Group items by a key function
139
+ */
140
+ function groupBy(items, keyFn) {
141
+ const result = {};
142
+ for (const item of items) {
143
+ const key = keyFn(item);
144
+ if (key) (result[key] ??= []).push(item);
145
+ }
146
+ return result;
147
+ }
148
+ /** Entity IDs grouped by domain (e.g., "light" -> ["light.living_room", ...]) */
149
+ const byDomain = createMemo(() => groupBy(Object.keys(state.entities), (id) => extractDomain(id)));
150
+ /** Entity IDs grouped by area (via entity registry area_id) */
151
+ const byArea = createMemo(() => groupBy(Object.keys(state.entityRegistry), (id) => state.entityRegistry[id]?.area_id ?? null));
152
+ /** Entity IDs grouped by device (via entity registry device_id) */
153
+ const byDevice = createMemo(() => groupBy(Object.keys(state.entityRegistry), (id) => state.entityRegistry[id]?.device_id ?? null));
154
+ /** Entity IDs grouped by floor (device -> area -> floor chain) */
155
+ const byFloor = createMemo(() => {
156
+ const result = {};
157
+ for (const [entityId, reg] of Object.entries(state.entityRegistry)) if (reg.device_id) {
158
+ const device = state.devices[reg.device_id];
159
+ if (device?.area_id) {
160
+ const area = state.areas[device.area_id];
161
+ if (area?.floor_id) (result[area.floor_id] ??= []).push(entityId);
162
+ }
163
+ }
164
+ return result;
165
+ });
166
+ /** Entity IDs grouped by label */
167
+ const byLabel = createMemo(() => {
168
+ const result = {};
169
+ for (const [entityId, reg] of Object.entries(state.entityRegistry)) for (const label of reg.labels ?? []) (result[label] ??= []).push(entityId);
170
+ return result;
171
+ });
172
+ /** All entity IDs currently in the store */
173
+ const allEntityIds = createMemo(() => Object.keys(state.entities));
174
+
175
+ //#endregion
176
+ export { allEntityIds, byArea, byDevice, byDomain, byFloor, byLabel, useArea, useAreas, useCamera, useConnection, useEntities, useEntity, useEntityHistory, useEntityState, useForecast, useService, useStore, useToggle, useTurnOff, useTurnOn };