@digital-alchemy/hass 24.9.4 → 24.9.5
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/README.md +1 -1
- package/dist/helpers/notify.helper.d.ts +2 -2
- package/package.json +16 -14
- package/scripts/mock-assistant.sh +5 -0
- package/scripts/run-e2e.sh +7 -0
- package/scripts/test.sh +2 -0
- package/src/dynamic.ts +4254 -0
- package/src/extensions/area.extension.ts +118 -0
- package/src/extensions/backup.extension.ts +63 -0
- package/src/extensions/call-proxy.extension.ts +113 -0
- package/src/extensions/config.extension.ts +119 -0
- package/src/extensions/conversation.extension.ts +46 -0
- package/src/extensions/device.extension.ts +56 -0
- package/src/extensions/entity.extension.ts +344 -0
- package/src/extensions/events.extension.ts +25 -0
- package/src/extensions/fetch-api.extension.ts +269 -0
- package/src/extensions/floor.extension.ts +76 -0
- package/src/extensions/id-by.extension.ts +157 -0
- package/src/extensions/index.ts +16 -0
- package/src/extensions/internal.extension.ts +145 -0
- package/src/extensions/label.extension.ts +83 -0
- package/src/extensions/reference.extension.ts +330 -0
- package/src/extensions/registry.extension.ts +44 -0
- package/src/extensions/websocket-api.extension.ts +554 -0
- package/src/extensions/zone.extension.ts +69 -0
- package/src/hass.module.ts +217 -0
- package/src/helpers/backup.helper.ts +11 -0
- package/src/helpers/constants.helper.ts +30 -0
- package/src/helpers/device.helper.ts +25 -0
- package/src/helpers/entity-state.helper.ts +171 -0
- package/src/helpers/features.helper.ts +580 -0
- package/src/helpers/fetch/calendar.ts +54 -0
- package/src/helpers/fetch/configuration.ts +75 -0
- package/src/helpers/fetch/index.ts +5 -0
- package/src/helpers/fetch/server-log.ts +28 -0
- package/src/helpers/fetch/service-list.ts +64 -0
- package/src/helpers/fetch/weather-forecasts.ts +86 -0
- package/src/helpers/fetch.helper.ts +328 -0
- package/src/helpers/id-by.helper.ts +53 -0
- package/src/helpers/index.ts +13 -0
- package/src/helpers/interfaces.helper.ts +340 -0
- package/src/helpers/manifest.helper.ts +0 -0
- package/src/helpers/notify.helper.ts +302 -0
- package/src/helpers/registry.ts +281 -0
- package/src/helpers/utility.helper.ts +147 -0
- package/src/helpers/websocket.helper.ts +117 -0
- package/src/index.ts +5 -0
- package/src/mock_assistant/extensions/area.extension.ts +62 -0
- package/src/mock_assistant/extensions/config.extension.ts +33 -0
- package/src/mock_assistant/extensions/device.extension.ts +44 -0
- package/src/mock_assistant/extensions/entity-registry.extension.ts +41 -0
- package/src/mock_assistant/extensions/entity.extension.ts +114 -0
- package/src/mock_assistant/extensions/events.extension.ts +37 -0
- package/src/mock_assistant/extensions/fetch.extension.ts +3 -0
- package/src/mock_assistant/extensions/fixtures.extension.ts +79 -0
- package/src/mock_assistant/extensions/floor.extension.ts +64 -0
- package/src/mock_assistant/extensions/index.ts +12 -0
- package/src/mock_assistant/extensions/label.extension.ts +64 -0
- package/src/mock_assistant/extensions/services.extension.ts +25 -0
- package/src/mock_assistant/extensions/websocket-api.extension.ts +84 -0
- package/src/mock_assistant/extensions/zone.extension.ts +65 -0
- package/src/mock_assistant/helpers/fixtures.ts +22 -0
- package/src/mock_assistant/helpers/index.ts +1 -0
- package/src/mock_assistant/index.ts +3 -0
- package/src/mock_assistant/main.ts +46 -0
- package/src/mock_assistant/mock-assistant.module.ts +90 -0
- package/src/quickboot.module.ts +23 -0
- package/src/testing/area.spec.ts +189 -0
- package/src/testing/backup.spec.ts +157 -0
- package/src/testing/config.spec.ts +188 -0
- package/src/testing/device.spec.ts +89 -0
- package/src/testing/entity.spec.ts +171 -0
- package/src/testing/events.spec.ts +78 -0
- package/src/testing/fetch-api.spec.ts +410 -0
- package/src/testing/fixtures.spec.ts +158 -0
- package/src/testing/floor.spec.ts +186 -0
- package/src/testing/id-by.spec.ts +140 -0
- package/src/testing/label.spec.ts +186 -0
- package/src/testing/ref-by.spec.ts +300 -0
- package/src/testing/websocket.spec.ts +63 -0
- package/src/testing/workflow.spec.ts +195 -0
- package/src/testing/zone.spec.ts +109 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { TAreaId, TDeviceId, TFloorId, TLabelId, TPlatformId, TZoneId } from "../dynamic";
|
|
2
|
+
import { ANY_ENTITY } from "./utility.helper";
|
|
3
|
+
|
|
4
|
+
export type LabelOptions = {
|
|
5
|
+
name: string;
|
|
6
|
+
icon: string;
|
|
7
|
+
color: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type EditLabelOptions = {
|
|
11
|
+
entity: ANY_ENTITY;
|
|
12
|
+
label: TLabelId;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type EditAliasOptions = {
|
|
16
|
+
entity: ANY_ENTITY;
|
|
17
|
+
alias: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type LabelDefinition = {
|
|
21
|
+
color: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
icon: string;
|
|
24
|
+
label_id: TLabelId;
|
|
25
|
+
name: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type ToggleExpose = {
|
|
29
|
+
assistants: string | string[];
|
|
30
|
+
entity_ids: ANY_ENTITY | ANY_ENTITY[];
|
|
31
|
+
should_expose: boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type SensorUnitOfMeasurement =
|
|
35
|
+
| "h"
|
|
36
|
+
| "min"
|
|
37
|
+
| "s"
|
|
38
|
+
| "d"
|
|
39
|
+
| "K"
|
|
40
|
+
| "°C"
|
|
41
|
+
| "°F"
|
|
42
|
+
| "cm"
|
|
43
|
+
| "in"
|
|
44
|
+
| "mm"
|
|
45
|
+
| "VA"
|
|
46
|
+
| "L"
|
|
47
|
+
| "gal"
|
|
48
|
+
| "m³"
|
|
49
|
+
| "ft³"
|
|
50
|
+
| "CCF"
|
|
51
|
+
| "kg"
|
|
52
|
+
| "g"
|
|
53
|
+
| "mg"
|
|
54
|
+
| "µg"
|
|
55
|
+
| "oz"
|
|
56
|
+
| "lb"
|
|
57
|
+
| "st"
|
|
58
|
+
| "ft/s"
|
|
59
|
+
| "km/h"
|
|
60
|
+
| "kn"
|
|
61
|
+
| "m/s"
|
|
62
|
+
| "mph"
|
|
63
|
+
| "in/d"
|
|
64
|
+
| "in/h"
|
|
65
|
+
| "mm/d"
|
|
66
|
+
| "V"
|
|
67
|
+
| "mV"
|
|
68
|
+
| "dB"
|
|
69
|
+
| "dBm"
|
|
70
|
+
| "mL"
|
|
71
|
+
| "fl. oz."
|
|
72
|
+
| "dBA"
|
|
73
|
+
| "cbar"
|
|
74
|
+
| "bar"
|
|
75
|
+
| "hPa"
|
|
76
|
+
| "inHg"
|
|
77
|
+
| "kPa"
|
|
78
|
+
| "mbar"
|
|
79
|
+
| "Pa"
|
|
80
|
+
| "psi"
|
|
81
|
+
| "var"
|
|
82
|
+
| "%"
|
|
83
|
+
| "None"
|
|
84
|
+
| "W"
|
|
85
|
+
| "kW"
|
|
86
|
+
| "µg/m³"
|
|
87
|
+
| "lx"
|
|
88
|
+
| "W/m²"
|
|
89
|
+
| "BTU/(h⋅ft²)"
|
|
90
|
+
| "Hz"
|
|
91
|
+
| "kHz"
|
|
92
|
+
| "MHz"
|
|
93
|
+
| "GHz"
|
|
94
|
+
| "Wh"
|
|
95
|
+
| "kWh"
|
|
96
|
+
| "MWh"
|
|
97
|
+
| "MJ"
|
|
98
|
+
| "GJ"
|
|
99
|
+
| "km"
|
|
100
|
+
| "m"
|
|
101
|
+
| "mi"
|
|
102
|
+
| "yd"
|
|
103
|
+
| "bit/s"
|
|
104
|
+
| "kbit/s"
|
|
105
|
+
| "Mbit/s"
|
|
106
|
+
| "Gbit/s"
|
|
107
|
+
| "B/s"
|
|
108
|
+
| "kB/s"
|
|
109
|
+
| "MB/s"
|
|
110
|
+
| "GB/s"
|
|
111
|
+
| "KiB/s"
|
|
112
|
+
| "MiB/s"
|
|
113
|
+
| "GiB/s"
|
|
114
|
+
| "bit"
|
|
115
|
+
| "kbit"
|
|
116
|
+
| "Mbit"
|
|
117
|
+
| "Gbit"
|
|
118
|
+
| "B"
|
|
119
|
+
| "kB"
|
|
120
|
+
| "MB"
|
|
121
|
+
| "GB"
|
|
122
|
+
| "TB"
|
|
123
|
+
| "PB"
|
|
124
|
+
| "EB"
|
|
125
|
+
| "ZB"
|
|
126
|
+
| "YB"
|
|
127
|
+
| "KiB"
|
|
128
|
+
| "MiB"
|
|
129
|
+
| "GiB"
|
|
130
|
+
| "TiB"
|
|
131
|
+
| "PiB"
|
|
132
|
+
| "EiB"
|
|
133
|
+
| "ZiB"
|
|
134
|
+
| "YiB"
|
|
135
|
+
| "A"
|
|
136
|
+
| "mA"
|
|
137
|
+
| "ppm";
|
|
138
|
+
|
|
139
|
+
export type EntityRegistryItem<ENTITY extends ANY_ENTITY> = {
|
|
140
|
+
area_id?: TAreaId;
|
|
141
|
+
categories: object;
|
|
142
|
+
config_entry_id?: string;
|
|
143
|
+
device_id?: TDeviceId;
|
|
144
|
+
disabled_by?: string;
|
|
145
|
+
entity_category?: string;
|
|
146
|
+
entity_id: ENTITY;
|
|
147
|
+
has_entity_name: boolean;
|
|
148
|
+
hidden_by?: string;
|
|
149
|
+
icon?: string;
|
|
150
|
+
id: string;
|
|
151
|
+
labels: TLabelId[];
|
|
152
|
+
name?: string;
|
|
153
|
+
options: {
|
|
154
|
+
conversation: {
|
|
155
|
+
should_expose?: boolean;
|
|
156
|
+
};
|
|
157
|
+
sensor?: {
|
|
158
|
+
display_precision?: number;
|
|
159
|
+
unit_of_measurement?: SensorUnitOfMeasurement;
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
original_name: string;
|
|
163
|
+
platform: TPlatformId;
|
|
164
|
+
translation_key?: string;
|
|
165
|
+
unique_id: string;
|
|
166
|
+
aliases: string[];
|
|
167
|
+
capabilities?: string;
|
|
168
|
+
device_class?: string;
|
|
169
|
+
original_device_class?: string;
|
|
170
|
+
original_icon?: string;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const UPDATE_REGISTRY = "config/entity_registry/update";
|
|
174
|
+
|
|
175
|
+
export type UpdateCoreOptions = {
|
|
176
|
+
currency: string;
|
|
177
|
+
elevation: number;
|
|
178
|
+
unit_system: string;
|
|
179
|
+
update_units: boolean;
|
|
180
|
+
time_zone: string;
|
|
181
|
+
location_name: string;
|
|
182
|
+
language: string;
|
|
183
|
+
country: string;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export type ZoneOptions = {
|
|
187
|
+
latitude: number;
|
|
188
|
+
longitude: number;
|
|
189
|
+
name: string;
|
|
190
|
+
icon: string;
|
|
191
|
+
passive: boolean;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export type ZoneDetails = ZoneOptions & { id: TZoneId };
|
|
195
|
+
|
|
196
|
+
export interface ManifestItem {
|
|
197
|
+
domain: string;
|
|
198
|
+
name: string;
|
|
199
|
+
codeowners: string[];
|
|
200
|
+
config_flow?: boolean;
|
|
201
|
+
documentation?: string;
|
|
202
|
+
iot_class?: IotClass;
|
|
203
|
+
loggers?: string[];
|
|
204
|
+
requirements?: string[];
|
|
205
|
+
is_built_in: boolean;
|
|
206
|
+
integration_type?: IntegrationType;
|
|
207
|
+
quality_scale?: QualityScale;
|
|
208
|
+
dependencies?: string[];
|
|
209
|
+
dhcp?: DHCP[];
|
|
210
|
+
after_dependencies?: string[];
|
|
211
|
+
single_config_entry?: boolean;
|
|
212
|
+
bluetooth?: Bluetooth[];
|
|
213
|
+
version?: string;
|
|
214
|
+
homekit?: Homekit;
|
|
215
|
+
zeroconf?: Array<ZeroconfClass | string>;
|
|
216
|
+
ssdp?: SSDP[];
|
|
217
|
+
issue_tracker?: string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface Bluetooth {
|
|
221
|
+
manufacturer_id: number;
|
|
222
|
+
service_uuid?: string;
|
|
223
|
+
manufacturer_data_start?: number[];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export interface DHCP {
|
|
227
|
+
registered_devices?: boolean;
|
|
228
|
+
hostname?: string;
|
|
229
|
+
macaddress?: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface Homekit {
|
|
233
|
+
models: string[];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export enum IntegrationType {
|
|
237
|
+
Device = "device",
|
|
238
|
+
Entity = "entity",
|
|
239
|
+
Helper = "helper",
|
|
240
|
+
Hub = "hub",
|
|
241
|
+
Service = "service",
|
|
242
|
+
System = "system",
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export enum IotClass {
|
|
246
|
+
Calculated = "calculated",
|
|
247
|
+
CloudPolling = "cloud_polling",
|
|
248
|
+
CloudPush = "cloud_push",
|
|
249
|
+
LocalPolling = "local_polling",
|
|
250
|
+
LocalPush = "local_push",
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export enum QualityScale {
|
|
254
|
+
Internal = "internal",
|
|
255
|
+
Platinum = "platinum",
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface SSDP {
|
|
259
|
+
manufacturer: string;
|
|
260
|
+
modelDescription: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface ZeroconfClass {
|
|
264
|
+
type: string;
|
|
265
|
+
properties?: Properties;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export interface Properties {
|
|
269
|
+
mdl?: string;
|
|
270
|
+
SYSTYPE?: string;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export type FloorCreate = {
|
|
274
|
+
name: string;
|
|
275
|
+
level: number;
|
|
276
|
+
aliases?: string[];
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export type FloorDetails = FloorCreate & {
|
|
280
|
+
floor_id: TFloorId;
|
|
281
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { is, TBlackHole } from "@digital-alchemy/core";
|
|
2
|
+
import { Dayjs } from "dayjs";
|
|
3
|
+
import { Get } from "type-fest";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ENTITY_SETUP,
|
|
7
|
+
iCallService,
|
|
8
|
+
REGISTRY_SETUP,
|
|
9
|
+
TAreaId,
|
|
10
|
+
TDeviceId,
|
|
11
|
+
TFloorId,
|
|
12
|
+
TLabelId,
|
|
13
|
+
TPlatformId,
|
|
14
|
+
TRawDomains,
|
|
15
|
+
TRawEntityIds,
|
|
16
|
+
} from "../dynamic";
|
|
17
|
+
import { HassEntityContext } from ".";
|
|
18
|
+
|
|
19
|
+
export type ANY_ENTITY = TRawEntityIds;
|
|
20
|
+
|
|
21
|
+
// ? Casting by domain turns things from "equiv to ANY_ENTITY" to "scene.*" type generics
|
|
22
|
+
// These are no longer valid comparisons against ANY_ENTITY though
|
|
23
|
+
// ? The extract converts those generic types back into real entity ids to make types valid again
|
|
24
|
+
/**
|
|
25
|
+
* Pick any valid entity, optionally limiting by domain
|
|
26
|
+
*/
|
|
27
|
+
export type PICK_ENTITY<DOMAIN extends ALL_DOMAINS = ALL_DOMAINS> = Extract<
|
|
28
|
+
ANY_ENTITY,
|
|
29
|
+
`${DOMAIN}.${string}`
|
|
30
|
+
>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pick any valid service call, optionally limiting by domain
|
|
34
|
+
*/
|
|
35
|
+
export type PICK_SERVICE<DOMAIN extends ALL_SERVICE_DOMAINS = ALL_SERVICE_DOMAINS> = {
|
|
36
|
+
[key in DOMAIN]: `${key}.${keyof iCallService[key] & string}`;
|
|
37
|
+
}[DOMAIN];
|
|
38
|
+
|
|
39
|
+
export type PICK_SERVICE_PARAMETERS<
|
|
40
|
+
DOMAINS extends ALL_SERVICE_DOMAINS,
|
|
41
|
+
SERVICE extends PICK_SERVICE<DOMAINS>,
|
|
42
|
+
> =
|
|
43
|
+
Get<iCallService, SERVICE> extends (serviceParams: infer ServiceParams) => TBlackHole
|
|
44
|
+
? ServiceParams
|
|
45
|
+
: never;
|
|
46
|
+
|
|
47
|
+
export function entity_split(
|
|
48
|
+
entity: { entity_id: ANY_ENTITY } | ANY_ENTITY,
|
|
49
|
+
): [ALL_DOMAINS, string] {
|
|
50
|
+
if (is.object(entity)) {
|
|
51
|
+
entity = entity.entity_id;
|
|
52
|
+
}
|
|
53
|
+
return entity.split(".") as [ALL_DOMAINS, string];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Extract the domain from an entity with type safety
|
|
57
|
+
*/
|
|
58
|
+
export function domain(entity: { entity_id: ANY_ENTITY } | ANY_ENTITY): ALL_DOMAINS {
|
|
59
|
+
if (is.object(entity)) {
|
|
60
|
+
entity = entity.entity_id;
|
|
61
|
+
}
|
|
62
|
+
return entity_split(entity).shift() as ALL_DOMAINS;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type ENTITY_PROP<ENTITY_ID extends PICK_ENTITY, PROP extends "state" | "attributes"> = Get<
|
|
66
|
+
ENTITY_SETUP,
|
|
67
|
+
`${ENTITY_ID}.${PROP}`
|
|
68
|
+
>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Type definitions to match a specific entity.
|
|
72
|
+
*/
|
|
73
|
+
export type ENTITY_STATE<ENTITY_ID extends ANY_ENTITY> = Omit<
|
|
74
|
+
ENTITY_SETUP[ENTITY_ID],
|
|
75
|
+
"state" | "context" | "last_changed" | "last_updated" | "entity_id" | "attributes"
|
|
76
|
+
> & {
|
|
77
|
+
last_reported: Dayjs;
|
|
78
|
+
last_changed: Dayjs;
|
|
79
|
+
last_updated: Dayjs;
|
|
80
|
+
attributes: ENTITY_SETUP[ENTITY_ID]["attributes"];
|
|
81
|
+
entity_id: ENTITY_ID;
|
|
82
|
+
state: ENTITY_SETUP[ENTITY_ID]["state"];
|
|
83
|
+
context: HassEntityContext;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Union of all domains that contain entities
|
|
88
|
+
*/
|
|
89
|
+
export type ALL_DOMAINS = TRawDomains;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Union of all services with callable methods
|
|
93
|
+
*/
|
|
94
|
+
export type ALL_SERVICE_DOMAINS = keyof iCallService;
|
|
95
|
+
|
|
96
|
+
export type GetDomain<ENTITY extends ANY_ENTITY> = ENTITY extends `${infer domain}.${string}`
|
|
97
|
+
? domain
|
|
98
|
+
: never;
|
|
99
|
+
|
|
100
|
+
function isDomain<DOMAIN extends ALL_DOMAINS>(
|
|
101
|
+
entity: string,
|
|
102
|
+
domain: DOMAIN | DOMAIN[],
|
|
103
|
+
): entity is PICK_ENTITY<DOMAIN> {
|
|
104
|
+
const [test] = entity.split(".") as [DOMAIN, string];
|
|
105
|
+
return [domain].flat().includes(test);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
is.domain = isDomain;
|
|
109
|
+
|
|
110
|
+
declare module "@digital-alchemy/core" {
|
|
111
|
+
export interface IsIt {
|
|
112
|
+
/**
|
|
113
|
+
* Check to see if an entity matches
|
|
114
|
+
*/
|
|
115
|
+
domain: typeof isDomain;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const PostConfigPriorities = {
|
|
120
|
+
FETCH: 1,
|
|
121
|
+
VALIDATE: -1,
|
|
122
|
+
} as const;
|
|
123
|
+
|
|
124
|
+
export type PICK_FROM_AREA<ID extends TAreaId, DOMAIN extends ALL_DOMAINS = ALL_DOMAINS> = Extract<
|
|
125
|
+
REGISTRY_SETUP["area"][`_${ID}`],
|
|
126
|
+
PICK_ENTITY<DOMAIN>
|
|
127
|
+
>;
|
|
128
|
+
|
|
129
|
+
export type PICK_FROM_LABEL<
|
|
130
|
+
ID extends TLabelId,
|
|
131
|
+
DOMAIN extends ALL_DOMAINS = ALL_DOMAINS,
|
|
132
|
+
> = Extract<REGISTRY_SETUP["label"][`_${ID}`], PICK_ENTITY<DOMAIN>>;
|
|
133
|
+
|
|
134
|
+
export type PICK_FROM_FLOOR<
|
|
135
|
+
ID extends TFloorId,
|
|
136
|
+
DOMAIN extends ALL_DOMAINS = ALL_DOMAINS,
|
|
137
|
+
> = Extract<REGISTRY_SETUP["floor"][`_${ID}`], PICK_ENTITY<DOMAIN>>;
|
|
138
|
+
|
|
139
|
+
export type PICK_FROM_DEVICE<
|
|
140
|
+
ID extends TDeviceId,
|
|
141
|
+
DOMAIN extends ALL_DOMAINS = ALL_DOMAINS,
|
|
142
|
+
> = Extract<REGISTRY_SETUP["device"][`_${ID}`], PICK_ENTITY<DOMAIN>>;
|
|
143
|
+
|
|
144
|
+
export type PICK_FROM_PLATFORM<
|
|
145
|
+
ID extends TPlatformId,
|
|
146
|
+
DOMAIN extends ALL_DOMAINS = ALL_DOMAINS,
|
|
147
|
+
> = Extract<REGISTRY_SETUP["platform"][`_${ID}`], PICK_ENTITY<DOMAIN>>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { TBlackHole, TContext } from "@digital-alchemy/core";
|
|
2
|
+
import { Dayjs } from "dayjs";
|
|
3
|
+
|
|
4
|
+
import { HassSocketMessageTypes } from "./constants.helper";
|
|
5
|
+
import { EntityUpdateEvent } from "./entity-state.helper";
|
|
6
|
+
import { ALL_DOMAINS, ANY_ENTITY, ENTITY_STATE, PICK_ENTITY } from "./utility.helper";
|
|
7
|
+
|
|
8
|
+
export interface SignRequestResponse {
|
|
9
|
+
path: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SocketMessageDTO {
|
|
13
|
+
error?: Record<string, unknown>;
|
|
14
|
+
event?: EntityUpdateEvent;
|
|
15
|
+
id: string | number;
|
|
16
|
+
message?: string;
|
|
17
|
+
result?: Record<string, unknown> | Array<unknown>;
|
|
18
|
+
type: `${HassSocketMessageTypes}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type SocketSubscribeOptions<EVENT extends string> = {
|
|
22
|
+
event_type: EVENT;
|
|
23
|
+
context: TContext;
|
|
24
|
+
exec: (data: SocketSubscribeData<EVENT>) => TBlackHole;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type SocketSubscribeData<EVENT extends string> = {
|
|
28
|
+
event_type: EVENT;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export interface SendSocketMessageDTO {
|
|
32
|
+
access_token?: string;
|
|
33
|
+
disabled_by?: "user";
|
|
34
|
+
domain?: string;
|
|
35
|
+
hidden_by?: "user";
|
|
36
|
+
service?: string;
|
|
37
|
+
service_data?: unknown;
|
|
38
|
+
type: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface UpdateEntityMessageDTO<DOMAIN extends ALL_DOMAINS = ALL_DOMAINS> {
|
|
42
|
+
area_id?: string;
|
|
43
|
+
disabled_by?: "user";
|
|
44
|
+
entity_id: PICK_ENTITY<DOMAIN>;
|
|
45
|
+
hidden_by?: "user";
|
|
46
|
+
icon?: string;
|
|
47
|
+
name: string;
|
|
48
|
+
new_entity_id: PICK_ENTITY<DOMAIN>;
|
|
49
|
+
type: "config/entity_registry/update";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface RemoveEntityMessageDTO {
|
|
53
|
+
entity_id: ANY_ENTITY;
|
|
54
|
+
type: "config/entity_registry/remove";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface FindRelatedDTO {
|
|
58
|
+
item_id: string;
|
|
59
|
+
item_type: string;
|
|
60
|
+
type: "search/related";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface RegistryGetDTO {
|
|
64
|
+
entity_id: string;
|
|
65
|
+
type: "config/entity_registry/get";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface RenderTemplateDTO {
|
|
69
|
+
template: string;
|
|
70
|
+
timeout: number;
|
|
71
|
+
type: "render_template";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface SubscribeTriggerDTO {
|
|
75
|
+
trigger: Record<string, unknown>;
|
|
76
|
+
type: "subscribe_trigger";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface UnsubscribeEventsDTO {
|
|
80
|
+
subscription: number;
|
|
81
|
+
type: "unsubscribe_events";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface SignPathDTO {
|
|
85
|
+
path: string;
|
|
86
|
+
type: "auth/sign_path";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface RemoveBackupDTO {
|
|
90
|
+
slug: string;
|
|
91
|
+
type: "backup/remove";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface EntityHistoryDTO<ENTITIES extends ANY_ENTITY[] = ANY_ENTITY[]> {
|
|
95
|
+
end_time: Date | string | Dayjs;
|
|
96
|
+
entity_ids: ENTITIES;
|
|
97
|
+
minimal_response?: boolean;
|
|
98
|
+
no_attributes?: boolean;
|
|
99
|
+
start_time: Date | string | Dayjs;
|
|
100
|
+
type: "history/history_during_period";
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type EntityHistoryResult<
|
|
104
|
+
ENTITY extends ANY_ENTITY = ANY_ENTITY,
|
|
105
|
+
ATTRIBUTES extends object = object,
|
|
106
|
+
> = Pick<ENTITY_STATE<ENTITY> & { attributes: ATTRIBUTES }, "attributes" | "state"> & {
|
|
107
|
+
date: Date;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export type OnHassEventCallback<T = object> = (event: T) => TBlackHole;
|
|
111
|
+
|
|
112
|
+
export type OnHassEventOptions<T = object> = {
|
|
113
|
+
context: TContext;
|
|
114
|
+
exec: OnHassEventCallback<T>;
|
|
115
|
+
event: string;
|
|
116
|
+
once?: boolean;
|
|
117
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { TServiceParams } from "@digital-alchemy/core";
|
|
2
|
+
|
|
3
|
+
import { TAreaId } from "../../dynamic";
|
|
4
|
+
import { AreaDetails } from "../../helpers";
|
|
5
|
+
|
|
6
|
+
export function MockAreaExtension({ mock_assistant }: TServiceParams) {
|
|
7
|
+
let areas = new Map<TAreaId, AreaDetails>();
|
|
8
|
+
|
|
9
|
+
mock_assistant.socket.onMessage("config/area_registry/list", message => {
|
|
10
|
+
mock_assistant.socket.sendMessage({
|
|
11
|
+
id: message.id,
|
|
12
|
+
result: [...areas.values()],
|
|
13
|
+
type: "result",
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
mock_assistant.socket.onMessage<{ area_id: TAreaId }>("config/area_registry/delete", message => {
|
|
18
|
+
areas.delete(message.area_id);
|
|
19
|
+
sendUpdate();
|
|
20
|
+
mock_assistant.socket.sendMessage({
|
|
21
|
+
id: message.id,
|
|
22
|
+
result: null,
|
|
23
|
+
type: "result",
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
mock_assistant.socket.onMessage<AreaDetails>("config/area_registry/create", message => {
|
|
28
|
+
message.area_id = message.name as TAreaId;
|
|
29
|
+
areas.set(message.area_id as TAreaId, message);
|
|
30
|
+
sendUpdate();
|
|
31
|
+
mock_assistant.socket.sendMessage({
|
|
32
|
+
id: message.id,
|
|
33
|
+
result: null,
|
|
34
|
+
type: "result",
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
mock_assistant.socket.onMessage<AreaDetails>("config/area_registry/update", message => {
|
|
39
|
+
areas.set(message.area_id as TAreaId, message);
|
|
40
|
+
sendUpdate();
|
|
41
|
+
mock_assistant.socket.sendMessage({
|
|
42
|
+
id: message.id,
|
|
43
|
+
result: null,
|
|
44
|
+
type: "result",
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const sendUpdate = () =>
|
|
49
|
+
mock_assistant.socket.sendMessage({
|
|
50
|
+
event: { event_type: "area_registry_updated" },
|
|
51
|
+
type: "event",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
/**
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
loadFixtures(incoming: AreaDetails[]) {
|
|
59
|
+
areas = new Map(incoming.map(i => [i.area_id, i]));
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { deepExtend, TServiceParams } from "@digital-alchemy/core";
|
|
2
|
+
|
|
3
|
+
import { HassConfig } from "../../helpers";
|
|
4
|
+
|
|
5
|
+
export function MockConfig({ hass }: TServiceParams) {
|
|
6
|
+
let config = { components: ["synapse"], version: "2024.4.1" } as HassConfig;
|
|
7
|
+
|
|
8
|
+
const origConfig = hass.fetch.getConfig;
|
|
9
|
+
hass.fetch.getConfig = async () => config;
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
current() {
|
|
13
|
+
return config;
|
|
14
|
+
},
|
|
15
|
+
/**
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
loadFixtures(incoming: HassConfig) {
|
|
19
|
+
if (incoming) {
|
|
20
|
+
config = incoming;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
merge(incoming: Partial<HassConfig>) {
|
|
24
|
+
config = deepExtend(config, incoming);
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
monkeyReset() {
|
|
30
|
+
hass.fetch.getConfig = origConfig;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TServiceParams } from "@digital-alchemy/core";
|
|
2
|
+
|
|
3
|
+
import { TDeviceId } from "../../dynamic";
|
|
4
|
+
import { DEVICE_REGISTRY_UPDATED, DeviceDetails } from "../../helpers";
|
|
5
|
+
|
|
6
|
+
export function MockDeviceExtension({ mock_assistant }: TServiceParams) {
|
|
7
|
+
let devices = new Map<TDeviceId, DeviceDetails>();
|
|
8
|
+
|
|
9
|
+
mock_assistant.socket.onMessage("config/device_registry/list", message => {
|
|
10
|
+
mock_assistant.socket.sendMessage({
|
|
11
|
+
id: message.id,
|
|
12
|
+
result: [...devices.values()],
|
|
13
|
+
type: "result",
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
mock_assistant.socket.onMessage<{ device_id: TDeviceId }>(
|
|
18
|
+
"config/device_registry/delete",
|
|
19
|
+
message => {
|
|
20
|
+
devices.delete(message.device_id);
|
|
21
|
+
sendUpdate();
|
|
22
|
+
mock_assistant.socket.sendMessage({
|
|
23
|
+
id: message.id,
|
|
24
|
+
result: null,
|
|
25
|
+
type: "result",
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const sendUpdate = () =>
|
|
31
|
+
mock_assistant.socket.sendMessage({
|
|
32
|
+
event: { event_type: DEVICE_REGISTRY_UPDATED },
|
|
33
|
+
type: "event",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
/**
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
loadFixtures(incoming: DeviceDetails[]) {
|
|
41
|
+
devices = new Map(incoming.map(i => [i.id, i]));
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|