@digital-alchemy/hass 25.5.1 → 25.5.2
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/hass.module.d.mts +13 -1
- package/dist/hass.module.mjs +16 -1
- package/dist/hass.module.mjs.map +1 -1
- package/dist/helpers/fetch.mjs +0 -1
- package/dist/helpers/fetch.mjs.map +1 -1
- package/dist/helpers/utility.d.mts +1 -0
- package/dist/helpers/utility.mjs +4 -0
- package/dist/helpers/utility.mjs.map +1 -1
- package/dist/mock_assistant/mock-assistant.module.d.mts +12 -0
- package/dist/services/area.service.mjs +9 -1
- package/dist/services/area.service.mjs.map +1 -1
- package/dist/services/call-proxy.service.mjs +10 -2
- package/dist/services/call-proxy.service.mjs.map +1 -1
- package/dist/services/config.service.mjs +15 -13
- package/dist/services/config.service.mjs.map +1 -1
- package/dist/services/device.service.mjs +3 -1
- package/dist/services/device.service.mjs.map +1 -1
- package/dist/services/diagnostics.service.d.mts +26 -0
- package/dist/services/diagnostics.service.mjs +41 -0
- package/dist/services/diagnostics.service.mjs.map +1 -0
- package/dist/services/entity.service.mjs +12 -1
- package/dist/services/entity.service.mjs.map +1 -1
- package/dist/services/fetch-api.service.mjs +8 -2
- package/dist/services/fetch-api.service.mjs.map +1 -1
- package/dist/services/floor.service.mjs +3 -1
- package/dist/services/floor.service.mjs.map +1 -1
- package/dist/services/id-by.service.mjs +2 -1
- package/dist/services/id-by.service.mjs.map +1 -1
- package/dist/services/index.d.mts +1 -0
- package/dist/services/index.mjs +1 -0
- package/dist/services/index.mjs.map +1 -1
- package/dist/services/label.service.mjs +3 -1
- package/dist/services/label.service.mjs.map +1 -1
- package/dist/services/reference.service.mjs +15 -4
- package/dist/services/reference.service.mjs.map +1 -1
- package/dist/services/registry.service.mjs +8 -8
- package/dist/services/registry.service.mjs.map +1 -1
- package/dist/services/websocket-api.service.mjs +10 -2
- package/dist/services/websocket-api.service.mjs.map +1 -1
- package/dist/services/zone.service.mjs +3 -1
- package/dist/services/zone.service.mjs.map +1 -1
- package/dist/testing/area.spec.mjs +141 -132
- package/dist/testing/area.spec.mjs.map +1 -1
- package/dist/testing/device.spec.mjs +17 -0
- package/dist/testing/device.spec.mjs.map +1 -1
- package/dist/testing/entity.spec.mjs +167 -0
- package/dist/testing/entity.spec.mjs.map +1 -1
- package/dist/testing/fetch.spec.d.mts +1 -0
- package/dist/testing/fetch.spec.mjs +45 -0
- package/dist/testing/fetch.spec.mjs.map +1 -0
- package/dist/testing/floor.spec.mjs +17 -0
- package/dist/testing/floor.spec.mjs.map +1 -1
- package/dist/testing/label.spec.mjs +17 -0
- package/dist/testing/label.spec.mjs.map +1 -1
- package/dist/testing/zone.spec.mjs +24 -5
- package/dist/testing/zone.spec.mjs.map +1 -1
- package/package.json +17 -17
- package/src/hass.module.mts +18 -0
- package/src/helpers/fetch.mts +1 -1
- package/src/helpers/utility.mts +5 -0
- package/src/services/area.service.mts +9 -0
- package/src/services/call-proxy.service.mts +16 -9
- package/src/services/config.service.mts +21 -16
- package/src/services/device.service.mts +3 -0
- package/src/services/diagnostics.service.mts +45 -0
- package/src/services/entity.service.mts +12 -0
- package/src/services/fetch-api.service.mts +11 -2
- package/src/services/floor.service.mts +3 -0
- package/src/services/id-by.service.mts +2 -1
- package/src/services/index.mts +1 -0
- package/src/services/label.service.mts +3 -0
- package/src/services/reference.service.mts +15 -3
- package/src/services/registry.service.mts +8 -8
- package/src/services/websocket-api.service.mts +10 -2
- package/src/services/zone.service.mts +3 -0
- package/src/testing/area.spec.mts +153 -140
- package/src/testing/device.spec.mts +22 -0
- package/src/testing/entity.spec.mts +201 -0
- package/src/testing/fetch.spec.mts +54 -0
- package/src/testing/floor.spec.mts +22 -0
- package/src/testing/label.spec.mts +22 -0
- package/src/testing/zone.spec.mts +29 -5
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
EntityHistoryResult,
|
|
20
20
|
EntityRegistryItem,
|
|
21
21
|
HassEntityManager,
|
|
22
|
+
perf,
|
|
22
23
|
TMasterState,
|
|
23
24
|
} from "../index.mts";
|
|
24
25
|
import { ALL_DOMAINS, ANY_ENTITY, PICK_ENTITY } from "../user.mts";
|
|
@@ -44,6 +45,7 @@ export function EntityManager({
|
|
|
44
45
|
const PREVIOUS_STATE = new Map<ANY_ENTITY, ENTITY_STATE<ANY_ENTITY>>();
|
|
45
46
|
let lastRefresh: Dayjs;
|
|
46
47
|
function warnEarly(method: string) {
|
|
48
|
+
hass.diagnostics.entity?.warn_ready.publish({ method });
|
|
47
49
|
if (!init) {
|
|
48
50
|
lifecycle.onReady(() => {
|
|
49
51
|
if (config.boilerplate.LOG_LEVEL !== "trace") {
|
|
@@ -76,12 +78,14 @@ export function EntityManager({
|
|
|
76
78
|
payload: Omit<EntityHistoryDTO<ENTITES>, "type">,
|
|
77
79
|
) {
|
|
78
80
|
logger.trace({ payload }, `looking up entity history`);
|
|
81
|
+
const ms = perf();
|
|
79
82
|
const result = (await hass.socket.sendMessage({
|
|
80
83
|
...payload,
|
|
81
84
|
end_time: dayjs(payload.end_time).toISOString(),
|
|
82
85
|
start_time: dayjs(payload.start_time).toISOString(),
|
|
83
86
|
type: "history/history_during_period",
|
|
84
87
|
})) as Record<ANY_ENTITY, EntityHistoryItem[]>;
|
|
88
|
+
hass.diagnostics.entity?.history_lookup.publish({ ms: ms(), payload, result });
|
|
85
89
|
|
|
86
90
|
const entities = Object.keys(result) as ANY_ENTITY[];
|
|
87
91
|
return Object.fromEntries(
|
|
@@ -191,6 +195,7 @@ export function EntityManager({
|
|
|
191
195
|
internal.utils.object.get(oldState, entity.entity_id),
|
|
192
196
|
),
|
|
193
197
|
);
|
|
198
|
+
hass.diagnostics.entity?.refresh_entities.publish({ emitUpdates });
|
|
194
199
|
});
|
|
195
200
|
init = true;
|
|
196
201
|
}
|
|
@@ -201,6 +206,7 @@ export function EntityManager({
|
|
|
201
206
|
new_state: ENTITY_STATE<ENTITY>,
|
|
202
207
|
old_state: ENTITY_STATE<ENTITY>,
|
|
203
208
|
) {
|
|
209
|
+
hass.diagnostics.entity?.entity_updated.publish({ entity_id, new_state, old_state });
|
|
204
210
|
PREVIOUS_STATE.set(entity_id, old_state);
|
|
205
211
|
if (new_state === null) {
|
|
206
212
|
logger.warn(
|
|
@@ -209,8 +215,12 @@ export function EntityManager({
|
|
|
209
215
|
entity_id,
|
|
210
216
|
);
|
|
211
217
|
internal.utils.object.del(MASTER_STATE, entity_id);
|
|
218
|
+
hass.diagnostics.entity?.entity_remove.publish({ entity_id });
|
|
212
219
|
return;
|
|
213
220
|
}
|
|
221
|
+
if (old_state === null) {
|
|
222
|
+
hass.diagnostics.entity?.entity_add.publish({ entity_id });
|
|
223
|
+
}
|
|
214
224
|
internal.utils.object.set(MASTER_STATE, entity_id, new_state);
|
|
215
225
|
if (!hass.socket.pauseMessages) {
|
|
216
226
|
event.emit(entity_id, new_state, old_state);
|
|
@@ -293,10 +303,12 @@ export function EntityManager({
|
|
|
293
303
|
context,
|
|
294
304
|
event_type: "entity_registry_updated",
|
|
295
305
|
async exec() {
|
|
306
|
+
const ms = perf();
|
|
296
307
|
await debounce(ENTITY_REGISTRY_UPDATED, config.hass.EVENT_DEBOUNCE_MS);
|
|
297
308
|
logger.debug("entity registry updated");
|
|
298
309
|
hass.entity.registry.current = await hass.entity.registry.list();
|
|
299
310
|
event.emit(ENTITY_REGISTRY_UPDATED);
|
|
311
|
+
hass.diagnostics.entity?.registry_updated.publish({ ms: ms() });
|
|
300
312
|
},
|
|
301
313
|
});
|
|
302
314
|
hass.entity.registry.current = await hass.entity.registry.list();
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
HassConfig,
|
|
14
14
|
HassServiceDTO,
|
|
15
15
|
HomeAssistantServerLogItem,
|
|
16
|
+
perf,
|
|
16
17
|
PICK_SERVICE,
|
|
17
18
|
PICK_SERVICE_PARAMETERS,
|
|
18
19
|
PostConfigPriorities,
|
|
@@ -253,6 +254,15 @@ export function FetchAPI({
|
|
|
253
254
|
});
|
|
254
255
|
}
|
|
255
256
|
|
|
257
|
+
async function fetch<T, BODY extends TFetchBody = undefined>(
|
|
258
|
+
options: Partial<FetchArguments<BODY>>,
|
|
259
|
+
) {
|
|
260
|
+
const ms = perf();
|
|
261
|
+
const out = await fetcher.exec<T, BODY>(options);
|
|
262
|
+
hass.diagnostics.fetch?.fetch.publish({ ms: ms(), options, out });
|
|
263
|
+
return out;
|
|
264
|
+
}
|
|
265
|
+
|
|
256
266
|
return {
|
|
257
267
|
_fetcher: fetcher,
|
|
258
268
|
calendarSearch,
|
|
@@ -260,8 +270,7 @@ export function FetchAPI({
|
|
|
260
270
|
checkConfig,
|
|
261
271
|
checkCredentials,
|
|
262
272
|
download,
|
|
263
|
-
fetch
|
|
264
|
-
await fetcher.exec<T, BODY>(options),
|
|
273
|
+
fetch,
|
|
265
274
|
fetchEntityCustomizations,
|
|
266
275
|
fetchEntityHistory,
|
|
267
276
|
fireEvent,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
FloorCreate,
|
|
7
7
|
FloorDetails,
|
|
8
8
|
HassFloorService,
|
|
9
|
+
perf,
|
|
9
10
|
} from "../helpers/index.mts";
|
|
10
11
|
import { TFloorId } from "../user.mts";
|
|
11
12
|
|
|
@@ -29,10 +30,12 @@ export function Floor({
|
|
|
29
30
|
context,
|
|
30
31
|
event_type: "floor_registry_updated",
|
|
31
32
|
async exec() {
|
|
33
|
+
const ms = perf();
|
|
32
34
|
await debounce(FLOOR_REGISTRY_UPDATED, config.hass.EVENT_DEBOUNCE_MS);
|
|
33
35
|
hass.floor.current = await hass.floor.list();
|
|
34
36
|
logger.debug(`floor registry updated`);
|
|
35
37
|
event.emit(FLOOR_REGISTRY_UPDATED);
|
|
38
|
+
hass.diagnostics.floor?.registry_update.publish({ ms: ms() });
|
|
36
39
|
},
|
|
37
40
|
});
|
|
38
41
|
});
|
|
@@ -78,8 +78,9 @@ export function IDByExtension({
|
|
|
78
78
|
domain(i.entity_id) !== "update",
|
|
79
79
|
);
|
|
80
80
|
if (trimmed.length > SINGLE) {
|
|
81
|
+
const available_entity_ids = trimmed.map(i => i.entity_id);
|
|
81
82
|
logger.warn(
|
|
82
|
-
{ available_entity_ids
|
|
83
|
+
{ available_entity_ids, unique_id },
|
|
83
84
|
`unique_id collision during lookup (chose first in list)`,
|
|
84
85
|
);
|
|
85
86
|
} else {
|
package/src/services/index.mts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./backup.service.mts";
|
|
|
3
3
|
export * from "./call-proxy.service.mts";
|
|
4
4
|
export * from "./config.service.mts";
|
|
5
5
|
export * from "./device.service.mts";
|
|
6
|
+
export * from "./diagnostics.service.mts";
|
|
6
7
|
export * from "./entity.service.mts";
|
|
7
8
|
export * from "./events.service.mts";
|
|
8
9
|
export * from "./fetch-api.service.mts";
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
LABEL_REGISTRY_UPDATED,
|
|
7
7
|
LabelDefinition,
|
|
8
8
|
LabelOptions,
|
|
9
|
+
perf,
|
|
9
10
|
} from "../helpers/index.mts";
|
|
10
11
|
import { TLabelId } from "../user.mts";
|
|
11
12
|
|
|
@@ -29,10 +30,12 @@ export function Label({
|
|
|
29
30
|
context,
|
|
30
31
|
event_type: "label_registry_updated",
|
|
31
32
|
async exec() {
|
|
33
|
+
const ms = perf();
|
|
32
34
|
await debounce(LABEL_REGISTRY_UPDATED, config.hass.EVENT_DEBOUNCE_MS);
|
|
33
35
|
hass.label.current = await hass.label.list();
|
|
34
36
|
logger.debug(`label registry updated`);
|
|
35
37
|
event.emit(LABEL_REGISTRY_UPDATED);
|
|
38
|
+
hass.diagnostics.label?.registry_update.publish({ ms: ms() });
|
|
36
39
|
},
|
|
37
40
|
});
|
|
38
41
|
});
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
domain,
|
|
9
9
|
ENTITY_STATE,
|
|
10
10
|
HassReferenceService,
|
|
11
|
+
perf,
|
|
11
12
|
RemoveCallback,
|
|
12
13
|
} from "../helpers/index.mts";
|
|
13
14
|
import {
|
|
@@ -164,10 +165,11 @@ export function ReferenceService({
|
|
|
164
165
|
const listeners = new Set<() => void>();
|
|
165
166
|
|
|
166
167
|
// just because you can't do generics properly....
|
|
167
|
-
|
|
168
|
+
const proxy = new Proxy(thing, {
|
|
168
169
|
// things that shouldn't be needed: this extract
|
|
169
170
|
// eslint-disable-next-line sonarjs/function-return-type
|
|
170
171
|
get: (_, property: Extract<keyof ByIdProxy<ENTITY_ID>, string>) => {
|
|
172
|
+
hass.diagnostics.reference?.get_property.publish({ entity_id, property });
|
|
171
173
|
switch (property) {
|
|
172
174
|
// #MARK: onUpdate
|
|
173
175
|
case "onUpdate": {
|
|
@@ -304,7 +306,6 @@ export function ReferenceService({
|
|
|
304
306
|
listeners.add(remove);
|
|
305
307
|
|
|
306
308
|
const complete = (entity: ENTITY_STATE<ENTITY_ID>) => {
|
|
307
|
-
// eslint-disable-next-line sonarjs/different-types-comparison
|
|
308
309
|
if (entity.state !== state) {
|
|
309
310
|
logger.trace(
|
|
310
311
|
{
|
|
@@ -340,11 +341,20 @@ export function ReferenceService({
|
|
|
340
341
|
// #MARK: service calls
|
|
341
342
|
if (hass.configure.isService(entity_domain, property)) {
|
|
342
343
|
return async function (data = {}) {
|
|
344
|
+
const ms = perf();
|
|
343
345
|
// @ts-expect-error i don't care, this is fine
|
|
344
|
-
|
|
346
|
+
const result = await hass.call[entity_domain][property]({
|
|
345
347
|
entity_id,
|
|
346
348
|
...data,
|
|
347
349
|
});
|
|
350
|
+
hass.diagnostics.reference?.call_service.publish({
|
|
351
|
+
data,
|
|
352
|
+
entity_domain,
|
|
353
|
+
entity_id,
|
|
354
|
+
ms: ms(),
|
|
355
|
+
property,
|
|
356
|
+
});
|
|
357
|
+
return result;
|
|
348
358
|
};
|
|
349
359
|
}
|
|
350
360
|
return proxyGetLogic(entity_id, property);
|
|
@@ -392,6 +402,8 @@ export function ReferenceService({
|
|
|
392
402
|
return false;
|
|
393
403
|
},
|
|
394
404
|
});
|
|
405
|
+
hass.diagnostics.reference?.create_proxy.publish({ entity_id, proxy });
|
|
406
|
+
return proxy;
|
|
395
407
|
}
|
|
396
408
|
|
|
397
409
|
// #MARK: <return>
|
|
@@ -10,35 +10,35 @@ import {
|
|
|
10
10
|
} from "../helpers/index.mts";
|
|
11
11
|
|
|
12
12
|
export function Registry({ hass }: TServiceParams): HassRegistryService {
|
|
13
|
-
async function
|
|
13
|
+
async function manifestList() {
|
|
14
14
|
return await hass.socket.sendMessage<ManifestItem[]>({
|
|
15
15
|
type: "manifest/list",
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
async function
|
|
19
|
+
async function updateCore(options: UpdateCoreOptions) {
|
|
20
20
|
await hass.socket.sendMessage<ZoneDetails[]>({
|
|
21
21
|
...options,
|
|
22
22
|
type: "config/core/update",
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
async function
|
|
26
|
+
async function getConfig() {
|
|
27
27
|
return await hass.socket.sendMessage<HassConfig>({
|
|
28
28
|
type: "get_config",
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async function
|
|
32
|
+
async function getConfigEntries() {
|
|
33
33
|
return await hass.socket.sendMessage<ConfigEntry[]>({
|
|
34
34
|
type: "config_entries/get",
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
return {
|
|
39
|
-
getConfig
|
|
40
|
-
getConfigEntries
|
|
41
|
-
manifestList
|
|
42
|
-
updateCore
|
|
39
|
+
getConfig,
|
|
40
|
+
getConfigEntries,
|
|
41
|
+
manifestList,
|
|
42
|
+
updateCore,
|
|
43
43
|
};
|
|
44
44
|
}
|
|
@@ -73,6 +73,7 @@ export function WebsocketAPI({
|
|
|
73
73
|
// #MARK: setConnectionState
|
|
74
74
|
function setConnectionState(state: ConnectionState) {
|
|
75
75
|
hass.socket.connectionState = state;
|
|
76
|
+
hass.diagnostics.websocket?.set_connection_state.publish({ state });
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
// #MARK: handleUnknownConnectionState
|
|
@@ -87,6 +88,7 @@ export function WebsocketAPI({
|
|
|
87
88
|
// send a ping message to force a pong
|
|
88
89
|
logger.trace({ name }, `emitting ping`);
|
|
89
90
|
lastPingAttempt = now;
|
|
91
|
+
hass.diagnostics.websocket?.send_ping.publish({});
|
|
90
92
|
|
|
91
93
|
// emit a ping, do not wait for reply (inline)
|
|
92
94
|
hass.socket.sendMessage({ type: "ping" }, false);
|
|
@@ -109,6 +111,7 @@ export function WebsocketAPI({
|
|
|
109
111
|
logger.warn({ name }, "failed to receive expected {pong}");
|
|
110
112
|
return;
|
|
111
113
|
}
|
|
114
|
+
hass.diagnostics.websocket?.failed_ping.publish({});
|
|
112
115
|
// 🪦 oof, get rid of the current connection and try again
|
|
113
116
|
await hass.socket.teardown();
|
|
114
117
|
logger.warn({ name }, "hass stopped replying {unknown} => {offline}");
|
|
@@ -215,11 +218,13 @@ export function WebsocketAPI({
|
|
|
215
218
|
|
|
216
219
|
// #MARK: fireEvent
|
|
217
220
|
async function fireEvent(event_type: string, event_data: object = {}) {
|
|
218
|
-
|
|
221
|
+
const result = await hass.socket.sendMessage({
|
|
219
222
|
event_data,
|
|
220
223
|
event_type,
|
|
221
224
|
type: "fire_event",
|
|
222
225
|
});
|
|
226
|
+
hass.diagnostics.websocket?.fire_event.publish({ event_data, event_type, result });
|
|
227
|
+
return result;
|
|
223
228
|
}
|
|
224
229
|
|
|
225
230
|
// #MARK: sendMessage
|
|
@@ -245,6 +250,7 @@ export function WebsocketAPI({
|
|
|
245
250
|
if (data.type !== "auth") {
|
|
246
251
|
data.id = id;
|
|
247
252
|
}
|
|
253
|
+
hass.diagnostics.websocket?.send_message.publish({ data, waitForResponse });
|
|
248
254
|
const json = JSON.stringify(data);
|
|
249
255
|
const sentAt = new Date();
|
|
250
256
|
|
|
@@ -275,6 +281,7 @@ export function WebsocketAPI({
|
|
|
275
281
|
//
|
|
276
282
|
// discard the promise so whatever flow is depending on this can get garbage collected
|
|
277
283
|
waitingCallback.delete(id);
|
|
284
|
+
hass.diagnostics.websocket?.missed_reply.publish({ data, sentAt });
|
|
278
285
|
logger.warn(
|
|
279
286
|
{
|
|
280
287
|
message: data,
|
|
@@ -398,10 +405,11 @@ export function WebsocketAPI({
|
|
|
398
405
|
*/
|
|
399
406
|
async function onMessage(message: SocketMessageDTO) {
|
|
400
407
|
const id = Number(message.id);
|
|
408
|
+
setImmediate(() => hass.diagnostics.websocket?.message_received.publish({ message }));
|
|
401
409
|
switch (message.type) {
|
|
402
410
|
case "auth_required": {
|
|
403
411
|
logger.trace({ name: onMessage }, `sending authentication`);
|
|
404
|
-
hass.socket.sendMessage({ access_token: config.hass.TOKEN, type: "auth" }, false);
|
|
412
|
+
void hass.socket.sendMessage({ access_token: config.hass.TOKEN, type: "auth" }, false);
|
|
405
413
|
return;
|
|
406
414
|
}
|
|
407
415
|
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
EARLY_ON_READY,
|
|
5
5
|
HassZoneService,
|
|
6
6
|
ManifestItem,
|
|
7
|
+
perf,
|
|
7
8
|
ZONE_REGISTRY_UPDATED,
|
|
8
9
|
ZoneDetails,
|
|
9
10
|
ZoneOptions,
|
|
@@ -29,10 +30,12 @@ export function Zone({
|
|
|
29
30
|
context,
|
|
30
31
|
event_type: "zone_registry_updated",
|
|
31
32
|
async exec() {
|
|
33
|
+
const ms = perf();
|
|
32
34
|
await debounce(ZONE_REGISTRY_UPDATED, config.hass.EVENT_DEBOUNCE_MS);
|
|
33
35
|
hass.zone.current = await hass.zone.list();
|
|
34
36
|
logger.debug(`zone registry updated`);
|
|
35
37
|
event.emit(ZONE_REGISTRY_UPDATED);
|
|
38
|
+
hass.diagnostics.zone?.registry_update.publish({ ms: ms() });
|
|
36
39
|
},
|
|
37
40
|
});
|
|
38
41
|
});
|