@digital-alchemy/hass 25.11.16 → 25.11.23
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/dist/helpers/entity-state.d.mts +20 -3
- package/dist/helpers/fetch/service-list.d.mts +86 -5
- package/dist/helpers/id-by.d.mts +1 -1
- package/dist/helpers/utility.d.mts +1 -1
- package/dist/mock_assistant/mock-assistant.module.mjs.map +1 -1
- package/dist/mock_assistant/services/websocket-api.service.d.mts +3 -1
- package/dist/mock_assistant/services/websocket-api.service.mjs +43 -4
- package/dist/mock_assistant/services/websocket-api.service.mjs.map +1 -1
- package/dist/services/feature.service.d.mts +2 -2
- package/dist/services/feature.service.mjs.map +1 -1
- package/dist/services/id-by.service.mjs +4 -1
- package/dist/services/id-by.service.mjs.map +1 -1
- package/dist/services/reference.service.d.mts +1 -1
- package/dist/services/reference.service.mjs +62 -7
- package/dist/services/reference.service.mjs.map +1 -1
- package/dist/testing/area.spec.mjs +142 -1
- package/dist/testing/area.spec.mjs.map +1 -1
- package/dist/testing/call-proxy.spec.d.mts +1 -0
- package/dist/testing/call-proxy.spec.mjs +204 -0
- package/dist/testing/call-proxy.spec.mjs.map +1 -0
- package/dist/testing/conversation.spec.mjs +1 -1
- package/dist/testing/conversation.spec.mjs.map +1 -1
- package/dist/testing/entity.spec.mjs +14 -0
- package/dist/testing/entity.spec.mjs.map +1 -1
- package/dist/testing/id-by.spec.mjs +38 -0
- package/dist/testing/id-by.spec.mjs.map +1 -1
- package/dist/testing/ref-by.spec.mjs +805 -4
- package/dist/testing/ref-by.spec.mjs.map +1 -1
- package/dist/testing/scheduler.spec.d.mts +1 -0
- package/dist/testing/scheduler.spec.mjs +412 -0
- package/dist/testing/scheduler.spec.mjs.map +1 -0
- package/dist/testing/websocket.spec.mjs +25 -0
- package/dist/testing/websocket.spec.mjs.map +1 -1
- package/dist/testing/workflow.spec.mjs +1 -1
- package/dist/testing/workflow.spec.mjs.map +1 -1
- package/package.json +17 -16
- package/src/helpers/entity-state.mts +20 -3
- package/src/helpers/fetch/service-list.mts +89 -5
- package/src/helpers/id-by.mts +1 -0
- package/src/helpers/utility.mts +1 -1
- package/src/mock_assistant/mock-assistant.module.mts +0 -1
- package/src/mock_assistant/services/websocket-api.service.mts +46 -4
- package/src/services/feature.service.mts +9 -6
- package/src/services/id-by.service.mts +4 -1
- package/src/services/reference.service.mts +78 -9
- package/src/testing/area.spec.mts +166 -2
- package/src/testing/call-proxy.spec.mts +241 -0
- package/src/testing/conversation.spec.mts +1 -1
- package/src/testing/entity.spec.mts +15 -0
- package/src/testing/id-by.spec.mts +50 -0
- package/src/testing/ref-by.spec.mts +965 -4
- package/src/testing/scheduler.spec.mts +444 -0
- package/src/testing/websocket.spec.mts +33 -0
- package/src/testing/workflow.spec.mts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FIRST, RemoveCallback, TBlackHole } from "@digital-alchemy/core";
|
|
1
|
+
import type { FIRST, RemoveCallback, TBlackHole, TContext, TOffset } from "@digital-alchemy/core";
|
|
2
2
|
import type { Dayjs } from "dayjs";
|
|
3
3
|
import type { Except } from "type-fest";
|
|
4
4
|
import type { ALL_DOMAINS, ANY_ENTITY, GetDomain, iCallService, PICK_ENTITY, TAreaId, TDeviceId, TLabelId, TPlatformId, TRawDomains } from "../user.mts";
|
|
@@ -26,6 +26,17 @@ export type EntityHistoryItem = {
|
|
|
26
26
|
};
|
|
27
27
|
export type TEntityUpdateCallback<ENTITY_ID extends ANY_ENTITY> = (new_state: NonNullable<ENTITY_STATE<ENTITY_ID>>, old_state: NonNullable<ENTITY_STATE<ENTITY_ID>>, remove: () => TBlackHole) => TBlackHole;
|
|
28
28
|
export type RemovableCallback<ENTITY_ID extends ANY_ENTITY> = (callback: TEntityUpdateCallback<ENTITY_ID>) => RemoveCallback;
|
|
29
|
+
export type OnStateForOptions<ENTITY_ID extends ANY_ENTITY> = ((Pick<ENTITY_STATE<ENTITY_ID>, "state"> & {
|
|
30
|
+
matches?: never;
|
|
31
|
+
}) | ({
|
|
32
|
+
matches: (new_state: ENTITY_STATE<ENTITY_ID>, old_state: ENTITY_STATE<ENTITY_ID>) => boolean;
|
|
33
|
+
} & {
|
|
34
|
+
state?: never;
|
|
35
|
+
})) & {
|
|
36
|
+
for: TOffset;
|
|
37
|
+
context?: TContext;
|
|
38
|
+
exec: (state: ByIdProxy<ENTITY_ID>) => TBlackHole;
|
|
39
|
+
};
|
|
29
40
|
export type ByIdProxy<ENTITY_ID extends ANY_ENTITY> = ENTITY_STATE<ENTITY_ID> & {
|
|
30
41
|
entity_id: ENTITY_ID;
|
|
31
42
|
/**
|
|
@@ -43,11 +54,17 @@ export type ByIdProxy<ENTITY_ID extends ANY_ENTITY> = ENTITY_STATE<ENTITY_ID> &
|
|
|
43
54
|
/**
|
|
44
55
|
* Will resolve with the next state of the next value. No time limit
|
|
45
56
|
*/
|
|
46
|
-
nextState: (
|
|
57
|
+
nextState: (timeout?: TOffset) => Promise<ENTITY_STATE<ENTITY_ID>>;
|
|
58
|
+
/**
|
|
59
|
+
* Runs on state change -
|
|
60
|
+
* If state matches the indicated target, then a timer will be initiated
|
|
61
|
+
* As long as the condition holds true, the callback will be executed at end of the timer
|
|
62
|
+
*/
|
|
63
|
+
onStateFor: (options: OnStateForOptions<ENTITY_ID>) => RemoveCallback;
|
|
47
64
|
/**
|
|
48
65
|
* Will resolve when state
|
|
49
66
|
*/
|
|
50
|
-
waitForState: (state: string | number,
|
|
67
|
+
waitForState: (state: string | number, timeout?: TOffset) => Promise<ENTITY_STATE<ENTITY_ID>>;
|
|
51
68
|
/**
|
|
52
69
|
* Access the immediate previous entity state
|
|
53
70
|
*/
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { LiteralUnion } from "type-fest";
|
|
2
2
|
import type { ALL_DOMAINS, PICK_ENTITY, TPlatformId } from "../../user.mts";
|
|
3
|
+
import type { SupportedCountries } from "../countries.mts";
|
|
3
4
|
import type { ColorMode } from "../features.mts";
|
|
5
|
+
import type { SupportedLanguages } from "../languages.mts";
|
|
4
6
|
export type EntityFilterSelector = {
|
|
5
7
|
integration?: TPlatformId;
|
|
6
8
|
domain?: ALL_DOMAINS | ALL_DOMAINS[];
|
|
@@ -27,16 +29,23 @@ export type LegacyDeviceSelector = {
|
|
|
27
29
|
export interface ServiceListSelector {
|
|
28
30
|
action: null;
|
|
29
31
|
addon: {
|
|
32
|
+
/** The name of the addon */
|
|
30
33
|
name?: string;
|
|
34
|
+
/** The slug identifier of the addon */
|
|
31
35
|
slug?: string;
|
|
32
36
|
};
|
|
33
37
|
area: {
|
|
38
|
+
/** Device filter selector(s) to filter areas by device */
|
|
34
39
|
device?: DeviceFilterSelector | DeviceFilterSelector[];
|
|
40
|
+
/** Entity filter selector(s) to filter areas by entity */
|
|
35
41
|
entity?: EntityFilterSelector | EntityFilterSelector[];
|
|
42
|
+
/** Whether multiple areas can be selected */
|
|
36
43
|
multiple?: boolean;
|
|
37
44
|
};
|
|
38
45
|
attribute: {
|
|
46
|
+
/** The entity ID to get attributes from */
|
|
39
47
|
entity_id?: PICK_ENTITY;
|
|
48
|
+
/** List of attribute names to hide from the selector */
|
|
40
49
|
hide_attributes?: string[];
|
|
41
50
|
};
|
|
42
51
|
assist_pipeline: null;
|
|
@@ -44,130 +53,202 @@ export interface ServiceListSelector {
|
|
|
44
53
|
boolean: null;
|
|
45
54
|
color_rgb: null;
|
|
46
55
|
color_temp: {
|
|
56
|
+
/** The unit of measurement for color temperature */
|
|
47
57
|
unit?: "kelvin" | "mired";
|
|
58
|
+
/** Minimum color temperature value */
|
|
48
59
|
min?: number;
|
|
60
|
+
/** Maximum color temperature value */
|
|
49
61
|
max?: number;
|
|
62
|
+
/** Maximum color temperature in mireds */
|
|
50
63
|
max_mireds?: number;
|
|
64
|
+
/** Minimum color temperature in mireds */
|
|
51
65
|
min_mireds?: number;
|
|
52
66
|
};
|
|
53
67
|
condition: null;
|
|
54
68
|
config_entry: {
|
|
69
|
+
/** The integration platform ID */
|
|
55
70
|
integration: TPlatformId;
|
|
56
71
|
};
|
|
57
72
|
constant: {
|
|
73
|
+
/** Display label for the constant value */
|
|
58
74
|
label?: string;
|
|
75
|
+
/** The constant value */
|
|
59
76
|
value: string | number | boolean;
|
|
77
|
+
/** Translation key for the label */
|
|
60
78
|
translation_key?: string;
|
|
61
79
|
};
|
|
62
80
|
conversation_agent: {
|
|
81
|
+
/** Language code for the conversation agent */
|
|
63
82
|
language?: string;
|
|
64
83
|
};
|
|
65
84
|
country: {
|
|
66
|
-
|
|
85
|
+
/** Country code(s) to include in the selector */
|
|
86
|
+
countries?: SupportedCountries | SupportedCountries[];
|
|
87
|
+
/** Whether to disable sorting of countries */
|
|
67
88
|
no_sort?: boolean;
|
|
68
89
|
};
|
|
69
90
|
date: null;
|
|
70
91
|
datetime: null;
|
|
71
92
|
device: LegacyDeviceSelector & {
|
|
93
|
+
/** Whether multiple devices can be selected */
|
|
72
94
|
multiple?: boolean;
|
|
95
|
+
/** Device filter selector(s) to filter devices */
|
|
73
96
|
filter?: DeviceFilterSelector | DeviceFilterSelector[];
|
|
97
|
+
/** Entity filter selector(s) to filter devices by their entities */
|
|
74
98
|
entity?: EntityFilterSelector | EntityFilterSelector[];
|
|
75
99
|
};
|
|
76
100
|
duration: {
|
|
101
|
+
/** Whether to enable day selection in duration input */
|
|
77
102
|
enable_day?: boolean;
|
|
103
|
+
/** Whether to enable millisecond precision in duration input */
|
|
78
104
|
enable_millisecond?: boolean;
|
|
105
|
+
/** Whether to allow negative duration values */
|
|
79
106
|
allow_negative?: boolean;
|
|
80
107
|
};
|
|
81
108
|
entity: LegacyEntitySelector & {
|
|
82
|
-
|
|
83
|
-
include_entities?: PICK_ENTITY | PICK_ENTITY[];
|
|
109
|
+
/** Whether multiple entities can be selected */
|
|
84
110
|
multiple?: boolean;
|
|
111
|
+
/** Whether entities can be reordered */
|
|
85
112
|
reorder?: boolean;
|
|
113
|
+
/** Entity filter selector(s) to filter entities */
|
|
86
114
|
filter?: EntityFilterSelector | EntityFilterSelector[];
|
|
87
|
-
}
|
|
115
|
+
} & ({
|
|
116
|
+
/** Entity ID(s) to exclude from the selector */
|
|
117
|
+
exclude_entities?: PICK_ENTITY | PICK_ENTITY[];
|
|
118
|
+
include_entities: never;
|
|
119
|
+
} | {
|
|
120
|
+
/** Entity ID(s) to include in the selector */
|
|
121
|
+
include_entities?: PICK_ENTITY | PICK_ENTITY[];
|
|
122
|
+
exclude_entities: never;
|
|
123
|
+
});
|
|
88
124
|
file: {
|
|
125
|
+
/** MIME type(s) or file extension(s) to accept */
|
|
89
126
|
accept: string;
|
|
90
127
|
};
|
|
91
128
|
floor: {
|
|
129
|
+
/** Entity filter selector(s) to filter floors by entity */
|
|
92
130
|
entity?: EntityFilterSelector | EntityFilterSelector[];
|
|
131
|
+
/** Device filter selector(s) to filter floors by device */
|
|
93
132
|
device?: DeviceFilterSelector | DeviceFilterSelector[];
|
|
133
|
+
/** Whether multiple floors can be selected */
|
|
94
134
|
multiple?: boolean;
|
|
95
135
|
};
|
|
96
136
|
icon: {
|
|
137
|
+
/** Placeholder text for the icon selector */
|
|
97
138
|
placeholder?: string;
|
|
98
139
|
};
|
|
99
140
|
label: {
|
|
141
|
+
/** Whether multiple labels can be selected */
|
|
100
142
|
multiple?: boolean;
|
|
101
143
|
};
|
|
102
144
|
language: {
|
|
103
|
-
|
|
145
|
+
/** Language code(s) to include in the selector */
|
|
146
|
+
languages?: SupportedLanguages | SupportedLanguages[];
|
|
147
|
+
/** Whether to display native language names */
|
|
104
148
|
native_name?: boolean;
|
|
149
|
+
/** Whether to disable sorting of languages */
|
|
105
150
|
no_sort?: boolean;
|
|
106
151
|
};
|
|
107
152
|
location: {
|
|
153
|
+
/** Whether to include radius selection */
|
|
108
154
|
radius?: boolean;
|
|
155
|
+
/** Icon to display for the location selector */
|
|
109
156
|
icon?: string;
|
|
110
157
|
};
|
|
111
158
|
media: {
|
|
159
|
+
/** MIME type(s) or file extension(s) to accept */
|
|
112
160
|
accept?: string | string[];
|
|
113
161
|
};
|
|
114
162
|
number: {
|
|
163
|
+
/** Minimum value allowed */
|
|
115
164
|
min?: number;
|
|
165
|
+
/** Maximum value allowed */
|
|
116
166
|
max?: number;
|
|
167
|
+
/** Input mode: "box" for text input or "slider" for slider control */
|
|
117
168
|
mode?: "box" | "slider";
|
|
169
|
+
/** Translation key for the number field */
|
|
118
170
|
translation_key?: string;
|
|
171
|
+
/** Step value for increment/decrement, or "any" for no step */
|
|
119
172
|
step?: number | "any";
|
|
173
|
+
/** Unit of measurement to display */
|
|
120
174
|
unit_of_measurement?: string;
|
|
121
175
|
};
|
|
122
176
|
object: {
|
|
177
|
+
/** Field definitions for the object structure */
|
|
123
178
|
fields?: Record<string, {
|
|
124
179
|
selector: ServiceListSelector;
|
|
125
180
|
required?: boolean;
|
|
126
181
|
label?: string;
|
|
127
182
|
}>;
|
|
183
|
+
/** Whether multiple objects can be selected */
|
|
128
184
|
multiple?: boolean;
|
|
185
|
+
/** Field name to use as the label for each object */
|
|
129
186
|
label_field?: string;
|
|
187
|
+
/** Field name to use as the description for each object */
|
|
130
188
|
description_field?: string;
|
|
189
|
+
/** Translation key for the object selector */
|
|
131
190
|
translation_key?: string;
|
|
132
191
|
};
|
|
133
192
|
qr_code: {
|
|
193
|
+
/** Data to encode in the QR code */
|
|
134
194
|
data: string;
|
|
195
|
+
/** Scale factor for the QR code size */
|
|
135
196
|
scale?: number;
|
|
197
|
+
/** Error correction level: L (low), M (medium), Q (quartile), H (high) */
|
|
136
198
|
error_correction_level?: "L" | "M" | "Q" | "H";
|
|
137
199
|
};
|
|
138
200
|
select: {
|
|
201
|
+
/** Available options for selection */
|
|
139
202
|
options: (string | {
|
|
140
203
|
label: string;
|
|
141
204
|
value: string;
|
|
142
205
|
})[];
|
|
206
|
+
/** Whether multiple options can be selected */
|
|
143
207
|
multiple?: boolean;
|
|
208
|
+
/** Whether custom values can be entered */
|
|
144
209
|
custom_value?: boolean;
|
|
210
|
+
/** Display mode: "dropdown" or "list" */
|
|
145
211
|
mode?: "dropdown" | "list";
|
|
212
|
+
/** Translation key for the select field */
|
|
146
213
|
translation_key?: string;
|
|
214
|
+
/** Whether to sort the options */
|
|
147
215
|
sort?: boolean;
|
|
148
216
|
};
|
|
149
217
|
state: {
|
|
218
|
+
/** Entity ID to get states from */
|
|
150
219
|
entity_id?: PICK_ENTITY;
|
|
220
|
+
/** Whether multiple states can be selected */
|
|
151
221
|
multiple?: boolean;
|
|
222
|
+
/** List of state values to hide from the selector */
|
|
152
223
|
hide_states?: string[];
|
|
153
224
|
};
|
|
154
225
|
statistic: {
|
|
226
|
+
/** Whether multiple statistics can be selected */
|
|
155
227
|
multiple?: boolean;
|
|
156
228
|
};
|
|
157
229
|
target: {
|
|
230
|
+
/** Entity filter selector(s) to filter targets by entity */
|
|
158
231
|
entity?: EntityFilterSelector | EntityFilterSelector[];
|
|
232
|
+
/** Device filter selector(s) to filter targets by device */
|
|
159
233
|
device?: DeviceFilterSelector | DeviceFilterSelector[];
|
|
160
234
|
};
|
|
161
235
|
template: null;
|
|
162
236
|
text: {
|
|
237
|
+
/** HTML input type for the text field */
|
|
163
238
|
type?: "color" | "date" | "datetime-local" | "email" | "month" | "number" | "password" | "search" | "tel" | "text" | "time" | "url" | "week";
|
|
239
|
+
/** Autocomplete attribute value */
|
|
164
240
|
autocomplete?: string;
|
|
241
|
+
/** Whether the text input should be multiline */
|
|
165
242
|
multiline?: boolean;
|
|
243
|
+
/** Prefix text to display before the input */
|
|
166
244
|
prefix?: string;
|
|
245
|
+
/** Suffix text to display after the input */
|
|
167
246
|
suffix?: string;
|
|
247
|
+
/** Whether multiple text values can be entered */
|
|
168
248
|
multiple?: boolean;
|
|
169
249
|
};
|
|
170
250
|
theme: {
|
|
251
|
+
/** Whether to include default themes in the selector */
|
|
171
252
|
include_defaults?: boolean;
|
|
172
253
|
};
|
|
173
254
|
time: null;
|
package/dist/helpers/id-by.d.mts
CHANGED
|
@@ -6,5 +6,5 @@ export type IDByInterface = {
|
|
|
6
6
|
floor: <FLOOR extends TFloorId, DOMAIN extends ALL_DOMAINS>(floor: FLOOR, ...domains: DOMAIN[]) => PICK_FROM_FLOOR<FLOOR, DOMAIN>[];
|
|
7
7
|
label: <LABEL extends TLabelId, DOMAIN extends ALL_DOMAINS>(label: LABEL, ...domains: DOMAIN[]) => PICK_FROM_LABEL<LABEL, DOMAIN>[];
|
|
8
8
|
platform: <PLATFORM extends TPlatformId, DOMAIN extends ALL_DOMAINS>(platform: PLATFORM, ...domains: DOMAIN[]) => PICK_FROM_PLATFORM<PLATFORM, DOMAIN>[];
|
|
9
|
-
unique_id: <UNIQUE_ID extends TUniqueId, ENTITY_ID extends Extract<HassUniqueIdMapping[UNIQUE_ID], ANY_ENTITY> = Extract<HassUniqueIdMapping[UNIQUE_ID], TRawEntityIds>>(unique_id: UNIQUE_ID) => ENTITY_ID;
|
|
9
|
+
unique_id: <UNIQUE_ID extends TUniqueId, ENTITY_ID extends Extract<HassUniqueIdMapping[UNIQUE_ID], ANY_ENTITY> = Extract<HassUniqueIdMapping[UNIQUE_ID], TRawEntityIds>>(unique_id: UNIQUE_ID, platform?: TPlatformId) => ENTITY_ID;
|
|
10
10
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TBlackHole } from "@digital-alchemy/core";
|
|
2
|
-
import type
|
|
2
|
+
import { type Dayjs } from "dayjs";
|
|
3
3
|
import type { Get } from "type-fest";
|
|
4
4
|
import type { ALL_DOMAINS, ANY_ENTITY, HassEntitySetupMapping, iCallService, PICK_ENTITY, TRawEntityIds } from "../user.mts";
|
|
5
5
|
import type { HassEntityContext } from "./entity-state.mts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-assistant.module.mjs","sourceRoot":"","sources":["../../src/mock_assistant/mock-assistant.module.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,mBAAmB,EACnB,mBAAmB,EACnB,2BAA2B,EAC3B,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"mock-assistant.module.mjs","sourceRoot":"","sources":["../../src/mock_assistant/mock-assistant.module.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,mBAAmB,EACnB,mBAAmB,EACnB,2BAA2B,EAC3B,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAC;IAC9C,aAAa,EAAE;QACb,UAAU,EAAE;YACV,OAAO,EAAE,MAAM;YACf,WAAW,EAAE,wDAAwD;YACrE,IAAI,EAAE,QAAQ;SACf;QACD,aAAa,EAAE;YACb,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC;YACrC,WAAW,EAAE,EAAE;YACf,IAAI,EAAE,QAAQ;SACf;QACD,SAAS,EAAE;YACT,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,+BAA+B;YAC5C,IAAI,EAAE,SAAS;SAChB;KACF;IACD,OAAO,EAAE,CAAC,QAAQ,CAAC;IACnB,IAAI,EAAE,gBAAgB;IACtB,YAAY,EAAE;QACZ,QAAQ;QACR,OAAO;QACP,QAAQ;QACR,MAAM;QACN,OAAO;QACP,QAAQ;QACR,QAAQ;QACR,iBAAiB;QACjB,UAAU;QACV,UAAU;KACX;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,iBAAiB;QACvB,MAAM,EAAE,UAAU;QAClB,MAAM,EAAE,mBAAmB;QAC3B,MAAM,EAAE,mBAAmB;QAC3B,eAAe,EAAE,2BAA2B;QAC5C,MAAM,EAAE,UAAU;QAClB,QAAQ,EAAE,YAAY;QACtB,KAAK,EAAE,kBAAkB;QACzB,KAAK,EAAE,kBAAkB;QACzB,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,gBAAgB;QACxB,IAAI,EAAE,iBAAiB;KACxB;CACF,CAAC,CAAC;AAQH;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,EAAE,CACnC,YAAY;KACT,WAAW,CAAC,QAAQ,CAAC;KACrB,MAAM,EAAE;KACR,MAAM,EAAE;KACR,SAAS,CAAC;IACT,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;CAC/B,CAAC;KACD,UAAU,CAAC;IACV,aAAa,EAAE;QACb,IAAI,EAAE,KAAK;QACX,GAAG,EAAE,KAAK;QACV,IAAI,EAAE,KAAK;KACZ;CACF,CAAC;KACD,aAAa,CAAC,kBAAkB,CAAC,CAAC;AAEvC,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,EAAE,CAAC"}
|
|
@@ -3,8 +3,10 @@ import type { PartialDeep } from "type-fest";
|
|
|
3
3
|
import type WS from "ws";
|
|
4
4
|
import type { SocketMessageDTO } from "../../helpers/index.mts";
|
|
5
5
|
export declare const INTERNAL_MESSAGE = "INTERNAL_MESSAGE";
|
|
6
|
-
export declare function MockWebsocketAPI({ hass, config, lifecycle }: TServiceParams): {
|
|
6
|
+
export declare function MockWebsocketAPI({ hass, config, lifecycle, event }: TServiceParams): {
|
|
7
|
+
attachScheduledFunctions: () => void;
|
|
7
8
|
connection: WS;
|
|
9
|
+
init: () => Promise<void>;
|
|
8
10
|
onMessage<DATA extends object>(type: string, callback: (data: DATA & MessageData) => TBlackHole): void;
|
|
9
11
|
sendMessage: (data: PartialDeep<SocketMessageDTO>) => void;
|
|
10
12
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { START } from "@digital-alchemy/core";
|
|
2
2
|
import EventEmitter from "events";
|
|
3
|
+
import { SOCKET_CONNECTED } from "../../services/websocket-api.service.mjs";
|
|
3
4
|
const CONNECTION_CLOSED = 0;
|
|
4
5
|
// const CONNECTION_OPEN = 1;
|
|
5
6
|
// const CONNECTION_FAILED = 2;
|
|
6
7
|
const UNLIMITED = 0;
|
|
7
8
|
export const INTERNAL_MESSAGE = "INTERNAL_MESSAGE";
|
|
8
|
-
export function MockWebsocketAPI({ hass, config, lifecycle }) {
|
|
9
|
+
export function MockWebsocketAPI({ hass, config, lifecycle, event }) {
|
|
9
10
|
const connection = new EventEmitter();
|
|
10
11
|
connection.setMaxListeners(UNLIMITED);
|
|
11
12
|
lifecycle.onShutdownStart(() => {
|
|
@@ -18,7 +19,8 @@ export function MockWebsocketAPI({ hass, config, lifecycle }) {
|
|
|
18
19
|
};
|
|
19
20
|
// connection.send = (...data) =>
|
|
20
21
|
hass.socket.createConnection = () => {
|
|
21
|
-
|
|
22
|
+
// Use Promise.resolve().then() instead of setImmediate for better compatibility with fake timers
|
|
23
|
+
Promise.resolve().then(() => {
|
|
22
24
|
if (!config.mock_assistant.PASS_AUTH) {
|
|
23
25
|
return;
|
|
24
26
|
}
|
|
@@ -26,6 +28,37 @@ export function MockWebsocketAPI({ hass, config, lifecycle }) {
|
|
|
26
28
|
});
|
|
27
29
|
return connection;
|
|
28
30
|
};
|
|
31
|
+
// Mock init - resolves immediately for tests
|
|
32
|
+
// The real service's init() waits for onSocketReady, but in tests we don't need that
|
|
33
|
+
async function init() {
|
|
34
|
+
if (hass.socket.connection) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
hass.socket.connection = hass.socket.createConnection("");
|
|
38
|
+
hass.socket.setConnectionState("connecting");
|
|
39
|
+
// Set up message handler - the real service's handlers registered in onPreInit
|
|
40
|
+
// will process messages via registerMessageHandler
|
|
41
|
+
connection.on("message", async (message) => {
|
|
42
|
+
try {
|
|
43
|
+
JSON.parse(message.toString());
|
|
44
|
+
// Real service's handlers will process this via onMessage/registerMessageHandler
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Ignore parse errors
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// In tests, we can resolve immediately without waiting for real socket handshake
|
|
51
|
+
// The real service's onBootstrap calls this, and it needs to complete for lifecycle to progress
|
|
52
|
+
hass.socket.setConnectionState("connected");
|
|
53
|
+
// Emit auth_ok to trigger the real service's handlers (registered in onPreInit)
|
|
54
|
+
// This allows the real service's auth_ok handler to run and do its setup
|
|
55
|
+
if (config.mock_assistant.PASS_AUTH) {
|
|
56
|
+
// Use microtask to ensure handlers are registered first
|
|
57
|
+
await Promise.resolve();
|
|
58
|
+
sendMessage({ type: "auth_ok" });
|
|
59
|
+
event.emit(SOCKET_CONNECTED);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
29
62
|
connection.send = (data) => {
|
|
30
63
|
const payload = JSON.parse(data);
|
|
31
64
|
connection.emit(INTERNAL_MESSAGE, payload);
|
|
@@ -38,7 +71,7 @@ export function MockWebsocketAPI({ hass, config, lifecycle }) {
|
|
|
38
71
|
return;
|
|
39
72
|
}
|
|
40
73
|
default: {
|
|
41
|
-
|
|
74
|
+
Promise.resolve().then(() => {
|
|
42
75
|
sendMessage({
|
|
43
76
|
id: payload.id,
|
|
44
77
|
result: null,
|
|
@@ -49,12 +82,18 @@ export function MockWebsocketAPI({ hass, config, lifecycle }) {
|
|
|
49
82
|
}
|
|
50
83
|
};
|
|
51
84
|
function sendMessage(data) {
|
|
52
|
-
|
|
85
|
+
// Use Promise.resolve() instead of setImmediate for better fake timer compatibility
|
|
86
|
+
Promise.resolve().then(() => {
|
|
53
87
|
connection.emit("message", JSON.stringify(data));
|
|
54
88
|
});
|
|
55
89
|
}
|
|
56
90
|
return {
|
|
91
|
+
attachScheduledFunctions: () => {
|
|
92
|
+
// Mock implementation - no-op for tests
|
|
93
|
+
// The real websocket service sets up intervals here, but we don't need them in tests
|
|
94
|
+
},
|
|
57
95
|
connection: connection,
|
|
96
|
+
init,
|
|
58
97
|
onMessage(type, callback) {
|
|
59
98
|
connection.on(INTERNAL_MESSAGE, (data) => {
|
|
60
99
|
if (data.type === type) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-api.service.mjs","sourceRoot":"","sources":["../../../src/mock_assistant/services/websocket-api.service.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,YAAY,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"websocket-api.service.mjs","sourceRoot":"","sources":["../../../src/mock_assistant/services/websocket-api.service.mts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import type { SupportedEntityFeatures, UsedSupportedFeatureDomains } from "../in
|
|
|
4
4
|
import type { PICK_ENTITY } from "../user.mts";
|
|
5
5
|
export declare function HassFeatureService({ hass, logger, internal: { utils: { is }, }, }: TServiceParams): {
|
|
6
6
|
createSupportedFeatures: <T extends UsedSupportedFeatureDomains>(features: (number | SupportedEntityFeatures<T>)[]) => number;
|
|
7
|
-
getSupportedFeatures: (input: number | PICK_ENTITY | ByIdProxy<PICK_ENTITY
|
|
7
|
+
getSupportedFeatures: <T extends UsedSupportedFeatureDomains>(input: number | PICK_ENTITY | ByIdProxy<PICK_ENTITY<T>>) => number[];
|
|
8
8
|
hasFeature: <T extends UsedSupportedFeatureDomains>(input: number | PICK_ENTITY<T> | ByIdProxy<PICK_ENTITY<T>>, feature: number | SupportedEntityFeatures<T>) => boolean;
|
|
9
|
-
listEntityFeatures: <DOMAIN extends UsedSupportedFeatureDomains
|
|
9
|
+
listEntityFeatures: <DOMAIN extends UsedSupportedFeatureDomains, ENTITY extends PICK_ENTITY<DOMAIN> = PICK_ENTITY<DOMAIN>>(input: ENTITY | ByIdProxy<ENTITY>) => SupportedEntityFeatures<DOMAIN>[];
|
|
10
10
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature.service.mjs","sourceRoot":"","sources":["../../src/services/feature.service.mts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,OAAO,EAAE,KAAK,EAAuB,MAAM,uBAAuB,CAAC;AAQnE,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAG1D,MAAM,UAAU,kBAAkB,CAAC,EACjC,IAAI,EACJ,MAAM,EACN,QAAQ,EAAE,EACR,KAAK,EAAE,EAAE,EAAE,EAAE,GACd,GACc;IACf;;OAEG;IACH,SAAS,uBAAuB,CAC9B,QAAiD;QAEjD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAW,EAAE,OAAO,EAAE,EAAE;YAC9C,IAAI,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;gBACvB,MAAM,QAAQ,GAAG,OAAO,CAAC;gBACzB,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAsC,CAAC;gBACtF,MAAM,aAAa,GAAG,kBAAkB,CAAC,MAAM,CAA2B,CAAC;gBAC3E,OAAO,GAAG,aAAa,EAAE,CAAC,WAAW,CAAC,CAAC;gBACvC,IAAI,CAAC,aAAa,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC;oBAClC,OAAO,GAAG,CAAC,CAAC;oBACZ,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,wBAAwB,CAAC,CAAC;gBAChE,CAAC;YACH,CAAC;YACD,OAAO,GAAG,GAAG,OAAO,CAAC;QACvB,CAAC,EAAE,CAAC,CAAC,CAAC;IACR,CAAC;IAED,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"feature.service.mjs","sourceRoot":"","sources":["../../src/services/feature.service.mts"],"names":[],"mappings":"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"}
|
|
@@ -27,9 +27,12 @@ export function IDByExtension({ hass, logger, config, internal: { utils: { is },
|
|
|
27
27
|
* The logic here will look up ALL entities with the "unique"_id, then check to see if there are multiple results
|
|
28
28
|
* It is assumed by both this code and type-writer that the unique_id lookup will refer to the not update entity
|
|
29
29
|
*/
|
|
30
|
-
function unique_id(unique_id) {
|
|
30
|
+
function unique_id(unique_id, platform) {
|
|
31
31
|
hass.entity.warnEarly("byUniqueId");
|
|
32
32
|
let list = hass.entity.registry.current.filter(i => i.unique_id === unique_id);
|
|
33
|
+
if (!is.empty(platform)) {
|
|
34
|
+
list = list.filter(i => i.platform === platform);
|
|
35
|
+
}
|
|
33
36
|
if (is.empty(list)) {
|
|
34
37
|
logger.error({ name: unique_id, unique_id }, `could not find an entity`);
|
|
35
38
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id-by.service.mjs","sourceRoot":"","sources":["../../src/services/id-by.service.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAkBtC,MAAM,UAAU,aAAa,CAAC,EAC5B,IAAI,EACJ,MAAM,EACN,MAAM,EACN,QAAQ,EAAE,EACR,KAAK,EAAE,EAAE,EAAE,EAAE,GACd,GACc;IACf,MAAM,KAAK,GAAG,CAAyB,GAAU,EAAE,OAAsB,EAAE,EAAE;QAC3E,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACvB,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,GAAG,EAAE,CACvB,MAAM,CAAC,IAAI,CAAC,8BAA8B;QACxC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACnE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;IAEnC,aAAa;IACb,SAAS,QAAQ,CAA6B,MAAc;QAC1D,OAAO,WAAW,EAAE;aACjB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;aACrB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;;;;;OAUG;IACH,SAAS,SAAS,CAMhB,SAAoB;
|
|
1
|
+
{"version":3,"file":"id-by.service.mjs","sourceRoot":"","sources":["../../src/services/id-by.service.mts"],"names":[],"mappings":"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"}
|
|
@@ -50,4 +50,4 @@ import type { HassReferenceService } from "../helpers/index.mts";
|
|
|
50
50
|
* The reference may call the remove all active event listeners and timers at any time.
|
|
51
51
|
* It will interrupt any timers (nextState/waitForState), as well as detach any event listeners
|
|
52
52
|
*/
|
|
53
|
-
export declare function ReferenceService({ hass, logger, internal, event, }: TServiceParams): HassReferenceService;
|
|
53
|
+
export declare function ReferenceService({ hass, logger, internal, scheduler, event, }: TServiceParams): HassReferenceService;
|
|
@@ -53,10 +53,13 @@ import { SOCKET_CONNECTED } from "./websocket-api.service.mjs";
|
|
|
53
53
|
* The reference may call the remove all active event listeners and timers at any time.
|
|
54
54
|
* It will interrupt any timers (nextState/waitForState), as well as detach any event listeners
|
|
55
55
|
*/
|
|
56
|
-
export function ReferenceService({ hass, logger, internal, event, }) {
|
|
56
|
+
export function ReferenceService({ hass, logger, internal, scheduler, event, }) {
|
|
57
57
|
const { is } = internal.utils;
|
|
58
58
|
// #MARK:proxyGetLogic
|
|
59
59
|
function proxyGetLogic(entity, property) {
|
|
60
|
+
if (!is.string(property)) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
60
63
|
const valid = ["state", "attributes", "last"].some(i => property.startsWith(i));
|
|
61
64
|
if (!valid) {
|
|
62
65
|
logger.error({ entity, name: proxyGetLogic, property }, `invalid property lookup`);
|
|
@@ -91,9 +94,12 @@ export function ReferenceService({ hass, logger, internal, event, }) {
|
|
|
91
94
|
"attributes",
|
|
92
95
|
"entity_id",
|
|
93
96
|
"history",
|
|
94
|
-
"
|
|
97
|
+
"last_changed",
|
|
98
|
+
"last_reported",
|
|
99
|
+
"last_updated",
|
|
95
100
|
"nextState",
|
|
96
101
|
"once",
|
|
102
|
+
"onStateFor",
|
|
97
103
|
"onUpdate",
|
|
98
104
|
"previous",
|
|
99
105
|
"removeAllListeners",
|
|
@@ -127,8 +133,50 @@ export function ReferenceService({ hass, logger, internal, event, }) {
|
|
|
127
133
|
// things that shouldn't be needed: this extract
|
|
128
134
|
// eslint-disable-next-line sonarjs/function-return-type
|
|
129
135
|
get: (_, property) => {
|
|
136
|
+
// Handle Symbol properties (e.g., when vitest formats test output)
|
|
137
|
+
if (!is.string(property)) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
130
140
|
hass.diagnostics.reference?.get_property.publish({ entity_id, property });
|
|
131
141
|
switch (property) {
|
|
142
|
+
// #MARK: runAfter
|
|
143
|
+
case "onStateFor": {
|
|
144
|
+
return function ({ context, ...options }) {
|
|
145
|
+
let timerRemove;
|
|
146
|
+
const remove = proxy.onUpdate((new_state, old_state) => {
|
|
147
|
+
const matches = options.matches
|
|
148
|
+
? options.matches(new_state, old_state)
|
|
149
|
+
: options.state === new_state.state;
|
|
150
|
+
if (!matches) {
|
|
151
|
+
if (timerRemove) {
|
|
152
|
+
timerRemove();
|
|
153
|
+
timerRemove = undefined;
|
|
154
|
+
logger.trace({ context, entity_id }, "cleared timer - state no longer matches");
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (timerRemove) {
|
|
159
|
+
logger.trace({ context, entity_id }, "timer already running, skipping");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
timerRemove = scheduler.setTimeout(async () => {
|
|
163
|
+
logger.trace({ context, entity_id, for: options.for }, "timer fired - executing callback");
|
|
164
|
+
internal.safeExec({
|
|
165
|
+
context,
|
|
166
|
+
exec: async () => await options.exec(proxy),
|
|
167
|
+
});
|
|
168
|
+
}, options.for);
|
|
169
|
+
logger.trace({ context, entity_id, for: options.for }, "started timer for state condition");
|
|
170
|
+
});
|
|
171
|
+
return internal.removeFn(() => {
|
|
172
|
+
if (timerRemove) {
|
|
173
|
+
timerRemove();
|
|
174
|
+
}
|
|
175
|
+
remove();
|
|
176
|
+
logger.trace({ context, entity_id }, "removed [onStateFor] listener");
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
}
|
|
132
180
|
// #MARK: onUpdate
|
|
133
181
|
case "onUpdate": {
|
|
134
182
|
return (callback) => {
|
|
@@ -215,9 +263,13 @@ export function ReferenceService({ hass, logger, internal, event, }) {
|
|
|
215
263
|
event.once(entity_id, complete);
|
|
216
264
|
// - race!
|
|
217
265
|
let wait;
|
|
218
|
-
if (is.
|
|
266
|
+
if (is.undefined(timeout)) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const duration = internal.utils.getIntervalMs(timeout);
|
|
270
|
+
if (duration > NONE) {
|
|
219
271
|
// keep track of sleep so it can be cleaned up also
|
|
220
|
-
wait = sleep(
|
|
272
|
+
wait = sleep(duration);
|
|
221
273
|
await wait;
|
|
222
274
|
wait = undefined;
|
|
223
275
|
if (done) {
|
|
@@ -232,7 +284,6 @@ export function ReferenceService({ hass, logger, internal, event, }) {
|
|
|
232
284
|
case "waitForState": {
|
|
233
285
|
return async (state, timeout) => await new Promise(async (done) => {
|
|
234
286
|
const remove = () => {
|
|
235
|
-
done = undefined;
|
|
236
287
|
listeners.delete(remove);
|
|
237
288
|
done = undefined;
|
|
238
289
|
logger.trace({ entity_id }, "remove [waitForState] listener");
|
|
@@ -259,8 +310,12 @@ export function ReferenceService({ hass, logger, internal, event, }) {
|
|
|
259
310
|
};
|
|
260
311
|
event.on(entity_id, complete);
|
|
261
312
|
let wait;
|
|
262
|
-
if (is.
|
|
263
|
-
|
|
313
|
+
if (is.undefined(timeout)) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const duration = internal.utils.getIntervalMs(timeout);
|
|
317
|
+
if (duration > NONE) {
|
|
318
|
+
wait = sleep(duration);
|
|
264
319
|
await wait;
|
|
265
320
|
wait = undefined;
|
|
266
321
|
if (done) {
|