@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.
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/dist/area-views-B-m8vwce.js +321 -0
- package/dist/camera/fetch.d.ts +15 -0
- package/dist/camera/query.d.ts +15 -0
- package/dist/camera/types.d.ts +85 -0
- package/dist/camera/webrtc.d.ts +31 -0
- package/dist/commands/service.d.ts +53 -0
- package/dist/commands/types.d.ts +113 -0
- package/dist/connection/adapter.d.ts +48 -0
- package/dist/connection/auth.d.ts +126 -0
- package/dist/connection/manager.d.ts +42 -0
- package/dist/connection/subscriptions.d.ts +25 -0
- package/dist/connection/types.d.ts +161 -0
- package/dist/core/reducers.d.ts +99 -0
- package/dist/core/store.d.ts +64 -0
- package/dist/core/types.d.ts +336 -0
- package/dist/demo/demo-data.d.ts +24 -0
- package/dist/demo/demo-provider.d.ts +33 -0
- package/dist/demo/demo-provider.js +103 -0
- package/dist/demo-data-D8JXWRpw.js +419 -0
- package/dist/entities/area-views.d.ts +25 -0
- package/dist/entities/index.d.ts +54 -0
- package/dist/entities/views.d.ts +30 -0
- package/dist/history/constants.d.ts +16 -0
- package/dist/history/fetch.d.ts +19 -0
- package/dist/history/query.d.ts +13 -0
- package/dist/history/types.d.ts +146 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +1747 -0
- package/dist/query/area-builder.d.ts +25 -0
- package/dist/query/builder.d.ts +18 -0
- package/dist/query/helpers.d.ts +21 -0
- package/dist/query/memoization.d.ts +18 -0
- package/dist/query/projections.d.ts +74 -0
- package/dist/query/types.d.ts +260 -0
- package/dist/solid/hooks.d.ts +88 -0
- package/dist/solid/index.d.ts +10 -0
- package/dist/solid/index.js +176 -0
- package/dist/solid/indices.d.ts +14 -0
- package/dist/store-B1sbyexX.js +54 -0
- package/dist/testing/fixtures.d.ts +161 -0
- package/dist/testing/helpers.d.ts +160 -0
- package/dist/testing/index.d.ts +13 -0
- package/dist/testing/index.js +2047 -0
- package/dist/testing/mock-behaviors/base.d.ts +73 -0
- package/dist/testing/mock-behaviors/climate.d.ts +22 -0
- package/dist/testing/mock-behaviors/cover.d.ts +22 -0
- package/dist/testing/mock-behaviors/index.d.ts +11 -0
- package/dist/testing/mock-behaviors/light.d.ts +20 -0
- package/dist/testing/mock-behaviors/lock.d.ts +20 -0
- package/dist/testing/mock-behaviors/media_player.d.ts +42 -0
- package/dist/testing/mock-behaviors/registry.d.ts +55 -0
- package/dist/testing/mock-behaviors/switch.d.ts +17 -0
- package/dist/testing/mock.d.ts +191 -0
- package/dist/testing/types.d.ts +87 -0
- package/dist/types-Csog0nqk.js +10 -0
- package/dist/weather/fetch.d.ts +19 -0
- package/dist/weather/query.d.ts +23 -0
- package/dist/weather/types.d.ts +126 -0
- package/package.json +85 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for 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
|
+
/**
|
|
11
|
+
* Service call options
|
|
12
|
+
*/
|
|
13
|
+
export interface ServiceCallOptions {
|
|
14
|
+
/** Entity ID or array of entity IDs to target */
|
|
15
|
+
entity_id?: EntityId | EntityId[];
|
|
16
|
+
/** Area ID or array of area IDs to target */
|
|
17
|
+
area_id?: string | string[];
|
|
18
|
+
/** Device ID or array of device IDs to target */
|
|
19
|
+
device_id?: string | string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Service call target
|
|
23
|
+
*
|
|
24
|
+
* Defines what entities the service call should affect
|
|
25
|
+
*/
|
|
26
|
+
export interface ServiceTarget {
|
|
27
|
+
/** Entity IDs */
|
|
28
|
+
entity_id?: string | string[];
|
|
29
|
+
/** Device IDs */
|
|
30
|
+
device_id?: string | string[];
|
|
31
|
+
/** Area IDs */
|
|
32
|
+
area_id?: string | string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Service call with full context
|
|
36
|
+
*/
|
|
37
|
+
export interface ServiceCallContext {
|
|
38
|
+
/** Domain (e.g., "light", "switch") */
|
|
39
|
+
domain: Domain;
|
|
40
|
+
/** Service name (e.g., "turn_on", "toggle") */
|
|
41
|
+
service: ServiceName<Domain>;
|
|
42
|
+
/** Service data */
|
|
43
|
+
serviceData?: ServiceCall<Domain, ServiceName<Domain>>;
|
|
44
|
+
/** Target entities/devices/areas */
|
|
45
|
+
target?: ServiceTarget;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Command success result
|
|
49
|
+
*/
|
|
50
|
+
export interface CommandSuccess<T = unknown> {
|
|
51
|
+
success: true;
|
|
52
|
+
result: T;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Command error result
|
|
56
|
+
*/
|
|
57
|
+
export interface CommandError {
|
|
58
|
+
success: false;
|
|
59
|
+
error: {
|
|
60
|
+
code: string;
|
|
61
|
+
message: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Command result (success or error)
|
|
66
|
+
*/
|
|
67
|
+
export type CommandResult<T = unknown> = CommandSuccess<T> | CommandError;
|
|
68
|
+
/**
|
|
69
|
+
* Entity registry update fields
|
|
70
|
+
*
|
|
71
|
+
* Fields that can be updated in the entity registry
|
|
72
|
+
*/
|
|
73
|
+
export interface EntityUpdateFields {
|
|
74
|
+
/** Friendly name */
|
|
75
|
+
name?: string;
|
|
76
|
+
/** Icon */
|
|
77
|
+
icon?: string;
|
|
78
|
+
/** Area ID */
|
|
79
|
+
area_id?: string | null;
|
|
80
|
+
/** Disabled by (null to enable) */
|
|
81
|
+
disabled_by?: string | null;
|
|
82
|
+
/** Hidden by (null to unhide) */
|
|
83
|
+
hidden_by?: string | null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Entity registry update request
|
|
87
|
+
*/
|
|
88
|
+
export interface EntityUpdateRequest {
|
|
89
|
+
/** Entity ID to update */
|
|
90
|
+
entity_id: EntityId;
|
|
91
|
+
/** Fields to update */
|
|
92
|
+
updates: EntityUpdateFields;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Generic WebSocket command
|
|
96
|
+
*/
|
|
97
|
+
export interface GenericCommand {
|
|
98
|
+
/** Command type */
|
|
99
|
+
type: WsCommandType | string;
|
|
100
|
+
/** Command payload */
|
|
101
|
+
[key: string]: unknown;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Command execution options
|
|
105
|
+
*/
|
|
106
|
+
export interface CommandOptions {
|
|
107
|
+
/** Timeout in milliseconds */
|
|
108
|
+
timeout?: number;
|
|
109
|
+
/** Retry on failure */
|
|
110
|
+
retry?: boolean;
|
|
111
|
+
/** Number of retry attempts */
|
|
112
|
+
retries?: number;
|
|
113
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HA Connection Adapter
|
|
3
|
+
*
|
|
4
|
+
* Wraps home-assistant-js-websocket Connection to SyncLayerConnection interface.
|
|
5
|
+
* This allows seamless swapping between mock and real connections.
|
|
6
|
+
*
|
|
7
|
+
* Debug listeners intercept messages at the adapter boundary:
|
|
8
|
+
* - Outgoing: fired from sendMessagePromise, subscribeMessage, subscribeEvents
|
|
9
|
+
* - Incoming: fired from subscribeMessage/subscribeEvents callbacks and sendMessagePromise responses
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
import type { Connection } from "home-assistant-js-websocket";
|
|
14
|
+
import type { SyncLayerConnection } from "./types";
|
|
15
|
+
/**
|
|
16
|
+
* Register a listener for all outgoing WebSocket messages.
|
|
17
|
+
* Returns an unsubscribe function.
|
|
18
|
+
*
|
|
19
|
+
* @param listener - Function called with each outgoing message object
|
|
20
|
+
* @returns Unsubscribe function to remove the listener
|
|
21
|
+
*/
|
|
22
|
+
export declare function addDebugOutgoingMessageListener(listener: (message: any) => void): () => void;
|
|
23
|
+
/**
|
|
24
|
+
* Register a listener for all incoming WebSocket messages.
|
|
25
|
+
* Returns an unsubscribe function. Receives subscription updates,
|
|
26
|
+
* event callbacks, and sendMessagePromise responses.
|
|
27
|
+
*
|
|
28
|
+
* @param listener - Function called with each incoming message object
|
|
29
|
+
* @returns Unsubscribe function to remove the listener
|
|
30
|
+
*/
|
|
31
|
+
export declare function addDebugIncomingMessageListener(listener: (message: any) => void): () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Wrap HA Connection to SyncLayerConnection interface
|
|
34
|
+
*
|
|
35
|
+
* This adapter makes the real HA Connection compatible with our unified interface,
|
|
36
|
+
* allowing it to be used interchangeably with MockConnection.
|
|
37
|
+
*
|
|
38
|
+
* @param conn - Home Assistant WebSocket connection
|
|
39
|
+
* @returns Wrapped connection implementing SyncLayerConnection
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const haConn = await createConnection({ auth });
|
|
44
|
+
* const wrappedConn = wrapHAConnection(haConn);
|
|
45
|
+
* await syncLayer.connect({ connection: wrappedConn });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function wrapHAConnection(conn: Connection): SyncLayerConnection;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Helpers
|
|
3
|
+
*
|
|
4
|
+
* Provides OAuth and token-based authentication for Home Assistant connections.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
import { type Auth } from "home-assistant-js-websocket";
|
|
9
|
+
/**
|
|
10
|
+
* Options for OAuth authentication flow
|
|
11
|
+
*/
|
|
12
|
+
export interface OAuthOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Home Assistant instance URL
|
|
15
|
+
* (e.g., "https://homeassistant.local:8123")
|
|
16
|
+
*/
|
|
17
|
+
url: string;
|
|
18
|
+
/**
|
|
19
|
+
* Client ID for OAuth
|
|
20
|
+
* Typically the same as the Home Assistant URL
|
|
21
|
+
*/
|
|
22
|
+
clientId: string;
|
|
23
|
+
/**
|
|
24
|
+
* Redirect URI after OAuth authorization
|
|
25
|
+
* (e.g., "https://myapp.com/auth/callback")
|
|
26
|
+
*/
|
|
27
|
+
redirectUri?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Optional authorization code if already obtained
|
|
30
|
+
* If provided, will be used directly instead of starting OAuth flow
|
|
31
|
+
*/
|
|
32
|
+
authCode?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Optional function to get authorization code
|
|
35
|
+
* If provided, will be called to get the code before token exchange
|
|
36
|
+
*/
|
|
37
|
+
getAuthCode?: () => Promise<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Optional function to save auth data
|
|
40
|
+
* Useful for persisting auth tokens
|
|
41
|
+
*/
|
|
42
|
+
saveAuth?: (auth: Auth) => void | Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Optional function to load saved auth data
|
|
45
|
+
*/
|
|
46
|
+
loadAuth?: () => Promise<Auth | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Optional flag to limit to specific Home Assistant instance
|
|
49
|
+
*/
|
|
50
|
+
limitHassInstance?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Authenticate using OAuth flow
|
|
54
|
+
*
|
|
55
|
+
* This function uses `getAuth` from `home-assistant-js-websocket` which:
|
|
56
|
+
* 1. Opens Home Assistant authorization page in a popup
|
|
57
|
+
* 2. Waits for user authorization
|
|
58
|
+
* 3. Exchanges authorization code for tokens
|
|
59
|
+
* 4. Returns authenticated Auth object
|
|
60
|
+
*
|
|
61
|
+
* Note: For a complete OAuth flow, you typically need to:
|
|
62
|
+
* - Set up a redirect URI endpoint in your app
|
|
63
|
+
* - Handle the OAuth callback with the authorization code
|
|
64
|
+
* - Use `getAuthCode` callback to provide the code
|
|
65
|
+
*
|
|
66
|
+
* Alternatively, `getAuth` can automatically handle the flow in browser environments
|
|
67
|
+
* by opening a popup and listening for the callback.
|
|
68
|
+
*
|
|
69
|
+
* @param options - OAuth options
|
|
70
|
+
* @returns Authenticated Auth object
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // Basic OAuth flow (browser only)
|
|
75
|
+
* const auth = await authenticateWithOAuth({
|
|
76
|
+
* url: 'https://homeassistant.local:8123',
|
|
77
|
+
* clientId: 'https://homeassistant.local:8123',
|
|
78
|
+
* redirectUri: 'https://myapp.com/auth/callback',
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* // OAuth with custom code handling
|
|
85
|
+
* const auth = await authenticateWithOAuth({
|
|
86
|
+
* url: 'https://homeassistant.local:8123',
|
|
87
|
+
* clientId: 'https://homeassistant.local:8123',
|
|
88
|
+
* redirectUri: 'https://myapp.com/auth/callback',
|
|
89
|
+
* getAuthCode: async () => {
|
|
90
|
+
* // Wait for user to authorize and get code from callback
|
|
91
|
+
* return await waitForAuthCode();
|
|
92
|
+
* },
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export declare function authenticateWithOAuth(options: OAuthOptions): Promise<Auth>;
|
|
97
|
+
/**
|
|
98
|
+
* Authenticate using a long-lived token
|
|
99
|
+
*
|
|
100
|
+
* @param url - Home Assistant instance URL
|
|
101
|
+
* @param token - Long-lived access token
|
|
102
|
+
* @returns Authenticated Auth object
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const auth = authenticateWithToken(
|
|
107
|
+
* 'https://homeassistant.local:8123',
|
|
108
|
+
* 'your_long_lived_token'
|
|
109
|
+
* );
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export declare function authenticateWithToken(url: string, token: string): Auth;
|
|
113
|
+
/**
|
|
114
|
+
* Check if auth object is valid
|
|
115
|
+
*
|
|
116
|
+
* @param auth - Auth object to check
|
|
117
|
+
* @returns True if auth is valid
|
|
118
|
+
*/
|
|
119
|
+
export declare function isAuthValid(auth: Auth): Promise<boolean>;
|
|
120
|
+
/**
|
|
121
|
+
* Refresh auth tokens
|
|
122
|
+
*
|
|
123
|
+
* @param auth - Auth object to refresh
|
|
124
|
+
* @returns Refreshed Auth object
|
|
125
|
+
*/
|
|
126
|
+
export declare function refreshAuth(auth: Auth): Promise<Auth>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection Manager
|
|
3
|
+
*
|
|
4
|
+
* Handles WebSocket connection lifecycle, authentication,
|
|
5
|
+
* and initial data loading.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import { type Auth, type Connection, type ConnectionOptions as HAConnectionOptions } from "home-assistant-js-websocket";
|
|
10
|
+
import { type OAuthOptions } from "./auth";
|
|
11
|
+
/**
|
|
12
|
+
* Options for initializing a connection
|
|
13
|
+
*/
|
|
14
|
+
export interface ConnectionOptions {
|
|
15
|
+
url: string;
|
|
16
|
+
auth: Auth | string | OAuthOptions;
|
|
17
|
+
createSocket?: HAConnectionOptions["createSocket"];
|
|
18
|
+
onConnect?: () => void;
|
|
19
|
+
onDisconnect?: () => void;
|
|
20
|
+
onReconnect?: () => void;
|
|
21
|
+
onError?: (error: Error) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Initialize connection to Home Assistant
|
|
25
|
+
*/
|
|
26
|
+
export declare function initConnection(options: ConnectionOptions): Promise<Connection>;
|
|
27
|
+
/**
|
|
28
|
+
* Disconnect from Home Assistant
|
|
29
|
+
*/
|
|
30
|
+
export declare function disconnect(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Get current connection
|
|
33
|
+
*/
|
|
34
|
+
export declare function getConnection(): Connection | null;
|
|
35
|
+
/**
|
|
36
|
+
* Check if currently connected
|
|
37
|
+
*/
|
|
38
|
+
export declare function isConnected(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Refresh all data
|
|
41
|
+
*/
|
|
42
|
+
export declare function refreshData(): Promise<void>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Subscriptions
|
|
3
|
+
*
|
|
4
|
+
* Sets up WebSocket event subscriptions to keep state in sync
|
|
5
|
+
* with Home Assistant.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { SyncLayerConnection } from "./types";
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to all relevant events from Home Assistant
|
|
12
|
+
*/
|
|
13
|
+
export declare function subscribeToUpdates(conn: SyncLayerConnection): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Clean up all active subscriptions
|
|
16
|
+
*/
|
|
17
|
+
export declare function cleanupSubscriptions(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Check if subscriptions are active
|
|
20
|
+
*/
|
|
21
|
+
export declare function hasActiveSubscriptions(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Get number of active subscriptions
|
|
24
|
+
*/
|
|
25
|
+
export declare function getSubscriptionCount(): number;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection Interface Types
|
|
3
|
+
*
|
|
4
|
+
* Defines the unified interface for both mock and real Home Assistant connections.
|
|
5
|
+
* This allows seamless swapping between test and production connections.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { HAEvent } from "@glasshome/ha-types";
|
|
10
|
+
import type { Fixtures } from "../testing/types";
|
|
11
|
+
/**
|
|
12
|
+
* Unified connection interface for Home Assistant connections
|
|
13
|
+
*
|
|
14
|
+
* This interface is implemented by both:
|
|
15
|
+
* - MockConnection (for testing)
|
|
16
|
+
* - Wrapped HA Connection (for production)
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // In tests
|
|
21
|
+
* const mockConn: SyncLayerConnection = new MockConnection(fixtures);
|
|
22
|
+
* await syncLayer.connect({ connection: mockConn });
|
|
23
|
+
*
|
|
24
|
+
* // In production
|
|
25
|
+
* const haConn = await createConnection({ auth });
|
|
26
|
+
* const wrappedConn: SyncLayerConnection = wrapHAConnection(haConn);
|
|
27
|
+
* await syncLayer.connect({ connection: wrappedConn });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export interface SyncLayerConnection {
|
|
31
|
+
/**
|
|
32
|
+
* Connect to Home Assistant (or mock)
|
|
33
|
+
*
|
|
34
|
+
* Establishes the WebSocket connection. For real HA connections,
|
|
35
|
+
* this is called automatically by createConnection. For mock connections,
|
|
36
|
+
* this simulates the connection process.
|
|
37
|
+
*
|
|
38
|
+
* @returns Promise that resolves when connection is established
|
|
39
|
+
*/
|
|
40
|
+
connect(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Disconnect from Home Assistant
|
|
43
|
+
*
|
|
44
|
+
* Closes the WebSocket connection and cleans up resources.
|
|
45
|
+
*/
|
|
46
|
+
disconnect(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Check if connection is currently active
|
|
49
|
+
*
|
|
50
|
+
* @returns true if connected, false otherwise
|
|
51
|
+
*/
|
|
52
|
+
get connected$(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Authenticate with Home Assistant
|
|
55
|
+
*
|
|
56
|
+
* Sends authentication credentials. For real HA connections,
|
|
57
|
+
* authentication is handled during connection creation. For mock connections,
|
|
58
|
+
* this simulates the authentication process.
|
|
59
|
+
*
|
|
60
|
+
* @param accessToken - Access token for authentication
|
|
61
|
+
* @returns Promise that resolves when authentication completes
|
|
62
|
+
*/
|
|
63
|
+
authenticate(accessToken: string): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Get current authentication state
|
|
66
|
+
*
|
|
67
|
+
* @returns Authentication state: "pending", "authenticated", or "failed"
|
|
68
|
+
*/
|
|
69
|
+
get authState(): "pending" | "authenticated" | "failed";
|
|
70
|
+
/**
|
|
71
|
+
* Subscribe to Home Assistant events
|
|
72
|
+
*
|
|
73
|
+
* Registers a callback to receive events of a specific type (or all events).
|
|
74
|
+
*
|
|
75
|
+
* @param callback - Function called when events are received
|
|
76
|
+
* @param eventType - Optional event type filter (e.g., "entity_registry_updated")
|
|
77
|
+
* @returns Promise resolving to unsubscribe function
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const unsubscribe = await connection.subscribeEvents((event) => {
|
|
82
|
+
* console.log("Event received:", event);
|
|
83
|
+
* }, "entity_registry_updated");
|
|
84
|
+
*
|
|
85
|
+
* // Later, unsubscribe
|
|
86
|
+
* await unsubscribe();
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
subscribeEvents(callback: (event: HAEvent<any>) => void, eventType?: string): Promise<() => void>;
|
|
90
|
+
/**
|
|
91
|
+
* Subscribe to message stream
|
|
92
|
+
*
|
|
93
|
+
* Used for streaming subscriptions like history/stream.
|
|
94
|
+
* Sends a subscription message and receives streaming responses.
|
|
95
|
+
*
|
|
96
|
+
* @param callback - Function called when messages are received
|
|
97
|
+
* @param message - Subscription message (e.g., { type: "history/stream", ... })
|
|
98
|
+
* @returns Promise resolving to unsubscribe function
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const unsubscribe = await connection.subscribeMessage((message) => {
|
|
103
|
+
* console.log("Stream message:", message);
|
|
104
|
+
* }, {
|
|
105
|
+
* type: "history/stream",
|
|
106
|
+
* entity_ids: ["sensor.temperature"],
|
|
107
|
+
* start_time: startTime.toISOString(),
|
|
108
|
+
* });
|
|
109
|
+
*
|
|
110
|
+
* // Later, unsubscribe
|
|
111
|
+
* await unsubscribe();
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
subscribeMessage<T>(callback: (message: T) => void, message: any): Promise<() => Promise<void>>;
|
|
115
|
+
/**
|
|
116
|
+
* Send a message and wait for response
|
|
117
|
+
*
|
|
118
|
+
* Sends a WebSocket message and returns a promise that resolves
|
|
119
|
+
* with the response.
|
|
120
|
+
*
|
|
121
|
+
* @param message - Message to send
|
|
122
|
+
* @returns Promise resolving to the response data
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* const states = await connection.sendMessagePromise({
|
|
127
|
+
* type: "get_states",
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
sendMessagePromise<T>(message: any): Promise<T>;
|
|
132
|
+
/**
|
|
133
|
+
* Add event listener for WebSocket messages
|
|
134
|
+
*
|
|
135
|
+
* Registers a handler for all WebSocket messages (not just events).
|
|
136
|
+
* Useful for listening to auth messages, connection status, etc.
|
|
137
|
+
*
|
|
138
|
+
* @param handler - Function called when any message is received
|
|
139
|
+
* @returns Unsubscribe function
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const unsubscribe = connection.addEventListener((message) => {
|
|
144
|
+
* console.log("Any message:", message);
|
|
145
|
+
* });
|
|
146
|
+
*
|
|
147
|
+
* // Later, unsubscribe
|
|
148
|
+
* unsubscribe();
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
addEventListener(handler: (message: any) => void): () => void;
|
|
152
|
+
/**
|
|
153
|
+
* Get fixtures (MockConnection only)
|
|
154
|
+
*
|
|
155
|
+
* Returns the fixture data for mock connections. SyncLayer uses this
|
|
156
|
+
* to load fixtures into its store during connection setup.
|
|
157
|
+
*
|
|
158
|
+
* @returns Fixture data (entities, registries, etc.)
|
|
159
|
+
*/
|
|
160
|
+
getFixtures?(): Fixtures;
|
|
161
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State update reducers
|
|
3
|
+
*
|
|
4
|
+
* These functions handle state updates in response to events from
|
|
5
|
+
* Home Assistant. They use SolidJS store's produce() for safe mutations.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: All index maintenance code has been stripped.
|
|
8
|
+
* Indices are derived reactively in Plan 03.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import type { EntityRegistryEntry } from "@glasshome/ha-types";
|
|
13
|
+
import type { AreaRegistryEntry, DeviceRegistryEntry, EntityId, FloorRegistryEntry, HassEntity, LabelRegistryEntry, StatisticData, StatisticMetadata } from "./types";
|
|
14
|
+
/**
|
|
15
|
+
* Bulk update multiple entities
|
|
16
|
+
*
|
|
17
|
+
* Used during initial load or bulk sync operations.
|
|
18
|
+
*
|
|
19
|
+
* @param entities - Array of entity states
|
|
20
|
+
*/
|
|
21
|
+
export declare function bulkUpdateEntities(entities: HassEntity[]): void;
|
|
22
|
+
/**
|
|
23
|
+
* Update entity registry entry
|
|
24
|
+
*
|
|
25
|
+
* @param entry - Entity registry entry
|
|
26
|
+
*/
|
|
27
|
+
export declare function updateEntityRegistry(entry: EntityRegistryEntry): void;
|
|
28
|
+
/**
|
|
29
|
+
* Remove entity registry entry
|
|
30
|
+
*
|
|
31
|
+
* @param entityId - Entity ID to remove
|
|
32
|
+
*/
|
|
33
|
+
export declare function removeEntityRegistry(entityId: EntityId): void;
|
|
34
|
+
/**
|
|
35
|
+
* Bulk update entity registry
|
|
36
|
+
*
|
|
37
|
+
* @param entries - Array of registry entries
|
|
38
|
+
*/
|
|
39
|
+
export declare function bulkUpdateEntityRegistry(entries: EntityRegistryEntry[]): void;
|
|
40
|
+
/**
|
|
41
|
+
* Update device registry entry
|
|
42
|
+
*
|
|
43
|
+
* @param device - Device registry entry
|
|
44
|
+
*/
|
|
45
|
+
export declare function updateDevice(device: DeviceRegistryEntry): void;
|
|
46
|
+
/**
|
|
47
|
+
* Remove device registry entry
|
|
48
|
+
*
|
|
49
|
+
* @param deviceId - Device ID to remove
|
|
50
|
+
*/
|
|
51
|
+
export declare function removeDevice(deviceId: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Update area registry entry
|
|
54
|
+
*
|
|
55
|
+
* @param area - Area registry entry
|
|
56
|
+
*/
|
|
57
|
+
export declare function updateArea(area: AreaRegistryEntry): void;
|
|
58
|
+
/**
|
|
59
|
+
* Remove area registry entry
|
|
60
|
+
*
|
|
61
|
+
* @param areaId - Area ID to remove
|
|
62
|
+
*/
|
|
63
|
+
export declare function removeArea(areaId: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Update floor registry entry
|
|
66
|
+
*
|
|
67
|
+
* @param floor - Floor registry entry
|
|
68
|
+
*/
|
|
69
|
+
export declare function updateFloor(floor: FloorRegistryEntry): void;
|
|
70
|
+
/**
|
|
71
|
+
* Remove floor registry entry
|
|
72
|
+
*
|
|
73
|
+
* @param floorId - Floor ID to remove
|
|
74
|
+
*/
|
|
75
|
+
export declare function removeFloor(floorId: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Update label registry entry
|
|
78
|
+
*
|
|
79
|
+
* @param label - Label registry entry
|
|
80
|
+
*/
|
|
81
|
+
export declare function updateLabel(label: LabelRegistryEntry): void;
|
|
82
|
+
/**
|
|
83
|
+
* Remove label registry entry
|
|
84
|
+
*
|
|
85
|
+
* @param labelId - Label ID to remove
|
|
86
|
+
*/
|
|
87
|
+
export declare function removeLabel(labelId: string): void;
|
|
88
|
+
/**
|
|
89
|
+
* Update statistics metadata
|
|
90
|
+
*
|
|
91
|
+
* @param metadata - Statistics metadata
|
|
92
|
+
*/
|
|
93
|
+
export declare function updateStatisticsMetadata(metadata: StatisticMetadata[]): void;
|
|
94
|
+
/**
|
|
95
|
+
* Update statistics data
|
|
96
|
+
*
|
|
97
|
+
* @param data - Statistics data points
|
|
98
|
+
*/
|
|
99
|
+
export declare function updateStatisticsData(data: StatisticData[]): void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Central reactive state store for sync-layer using SolidJS createStore. */
|
|
2
|
+
import type { Connection } from "home-assistant-js-websocket";
|
|
3
|
+
import type { SetStoreFunction, Store } from "solid-js/store";
|
|
4
|
+
import type { CameraStreamData } from "../camera/types";
|
|
5
|
+
import type { EntityHistoryData } from "../history/types";
|
|
6
|
+
import type { WeatherForecastsData } from "../weather/types";
|
|
7
|
+
import type { AreaId, AreaRegistryEntry, ConnectionState, DeviceId, DeviceRegistryEntry, EntityId, FloorId, FloorRegistryEntry, HassEntity, LabelId, LabelRegistryEntry, StatisticData, StatisticMetadata } from "./types";
|
|
8
|
+
/**
|
|
9
|
+
* Complete state shape for the sync-layer store
|
|
10
|
+
*
|
|
11
|
+
* All updates use SolidJS store's produce() for mutations
|
|
12
|
+
* or reconcile() for bulk replacements.
|
|
13
|
+
*
|
|
14
|
+
* NOTE: No EntityIndices field -- indices are derived reactively in Plan 03.
|
|
15
|
+
* NOTE: No _activeSubscriptions -- managed externally.
|
|
16
|
+
*/
|
|
17
|
+
export interface GlassHomeState {
|
|
18
|
+
/** WebSocket connection instance */
|
|
19
|
+
conn: Connection | null;
|
|
20
|
+
/** Current connection state */
|
|
21
|
+
connectionState: ConnectionState;
|
|
22
|
+
/** Connection error (if any) */
|
|
23
|
+
connectionError: Error | null;
|
|
24
|
+
/** Home Assistant base URL */
|
|
25
|
+
hassUrl: string | null;
|
|
26
|
+
/** Live entity state (keyed by entity_id) */
|
|
27
|
+
entities: Record<EntityId, HassEntity>;
|
|
28
|
+
/** Entity registry entries (keyed by entity_id) */
|
|
29
|
+
entityRegistry: Record<EntityId, import("@glasshome/ha-types").EntityRegistryEntry>;
|
|
30
|
+
/** Device registry entries (keyed by device ID) */
|
|
31
|
+
devices: Record<DeviceId, DeviceRegistryEntry>;
|
|
32
|
+
/** Area registry entries (keyed by area ID) */
|
|
33
|
+
areas: Record<AreaId, AreaRegistryEntry>;
|
|
34
|
+
/** Floor registry entries (keyed by floor ID) */
|
|
35
|
+
floors: Record<FloorId, FloorRegistryEntry>;
|
|
36
|
+
/** Label registry entries (keyed by label ID) */
|
|
37
|
+
labels: Record<LabelId, LabelRegistryEntry>;
|
|
38
|
+
/** Statistics data (keyed by statistic_id) */
|
|
39
|
+
statistics: Record<string, StatisticData[]>;
|
|
40
|
+
/** Statistics metadata (keyed by statistic_id) */
|
|
41
|
+
statisticsMetadata: Record<string, StatisticMetadata>;
|
|
42
|
+
/** Entity history data (keyed by entity ID) */
|
|
43
|
+
history: Record<EntityId, EntityHistoryData>;
|
|
44
|
+
/** Weather forecasts data (keyed by entity ID) */
|
|
45
|
+
forecasts: Record<EntityId, WeatherForecastsData>;
|
|
46
|
+
/** Camera stream data (keyed by entity ID) */
|
|
47
|
+
streams: Record<EntityId, CameraStreamData>;
|
|
48
|
+
}
|
|
49
|
+
export declare const state: Store<GlassHomeState>;
|
|
50
|
+
export declare const setState: SetStoreFunction<GlassHomeState>;
|
|
51
|
+
/**
|
|
52
|
+
* Reset store to initial state
|
|
53
|
+
*/
|
|
54
|
+
export declare function resetStore(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Factory for testing isolation
|
|
57
|
+
*
|
|
58
|
+
* Creates a new, independent store instance for test isolation.
|
|
59
|
+
*/
|
|
60
|
+
export declare function createStoreInstance(): [GlassHomeState, SetStoreFunction<GlassHomeState>];
|
|
61
|
+
/**
|
|
62
|
+
* Store tuple type for dependency injection
|
|
63
|
+
*/
|
|
64
|
+
export type Store = [get: GlassHomeState, set: SetStoreFunction<GlassHomeState>];
|