@digital-alchemy/hass 25.8.21 → 25.10.19-beta.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/dist/dev/services.d.mts +1 -1
- package/dist/helpers/device.d.mts +1 -1
- package/dist/helpers/entity-state.d.mts +6 -6
- package/dist/helpers/fetch/calendar.d.mts +2 -2
- package/dist/helpers/fetch/configuration.d.mts +1 -1
- package/dist/helpers/fetch/service-list.d.mts +3 -3
- package/dist/helpers/fetch.d.mts +2 -2
- package/dist/helpers/fetch.mjs.map +1 -1
- package/dist/helpers/id-by.d.mts +1 -1
- package/dist/helpers/interfaces.d.mts +22 -13
- package/dist/helpers/interfaces.mjs.map +1 -1
- package/dist/helpers/registry.d.mts +1 -1
- package/dist/helpers/utility.d.mts +5 -5
- package/dist/helpers/utility.mjs.map +1 -1
- package/dist/helpers/websocket.d.mts +9 -8
- package/dist/merge.d.mts +1 -1
- package/dist/mock_assistant/helpers/fixtures.d.mts +2 -2
- package/dist/mock_assistant/main.mjs.map +1 -1
- package/dist/mock_assistant/services/area.service.d.mts +2 -2
- package/dist/mock_assistant/services/config.service.d.mts +2 -2
- package/dist/mock_assistant/services/config.service.mjs.map +1 -1
- package/dist/mock_assistant/services/device.service.d.mts +2 -2
- package/dist/mock_assistant/services/device.service.mjs.map +1 -1
- package/dist/mock_assistant/services/entity-registry.service.d.mts +3 -3
- package/dist/mock_assistant/services/entity.service.d.mts +3 -3
- package/dist/mock_assistant/services/entity.service.mjs.map +1 -1
- package/dist/mock_assistant/services/events.service.d.mts +3 -3
- package/dist/mock_assistant/services/events.service.mjs.map +1 -1
- package/dist/mock_assistant/services/fixtures.service.d.mts +4 -4
- package/dist/mock_assistant/services/fixtures.service.mjs.map +1 -1
- package/dist/mock_assistant/services/floor.service.d.mts +2 -2
- package/dist/mock_assistant/services/label.service.d.mts +2 -2
- package/dist/mock_assistant/services/services.service.d.mts +2 -2
- package/dist/mock_assistant/services/websocket-api.service.d.mts +4 -4
- package/dist/mock_assistant/services/websocket-api.service.mjs.map +1 -1
- package/dist/mock_assistant/services/zone.service.d.mts +2 -2
- package/dist/quickboot.module.d.mts +1 -1
- package/dist/quickboot.module.mjs.map +1 -1
- package/dist/services/area.service.d.mts +2 -2
- package/dist/services/area.service.mjs +1 -1
- package/dist/services/area.service.mjs.map +1 -1
- package/dist/services/backup.service.d.mts +2 -2
- package/dist/services/backup.service.mjs.map +1 -1
- package/dist/services/call-proxy.service.d.mts +2 -2
- package/dist/services/call-proxy.service.mjs +1 -1
- package/dist/services/call-proxy.service.mjs.map +1 -1
- package/dist/services/config.service.d.mts +2 -2
- package/dist/services/config.service.mjs +1 -2
- package/dist/services/config.service.mjs.map +1 -1
- package/dist/services/conversation.service.d.mts +2 -2
- package/dist/services/conversation.service.mjs +1 -1
- package/dist/services/conversation.service.mjs.map +1 -1
- package/dist/services/device.service.d.mts +2 -2
- package/dist/services/device.service.mjs +1 -1
- package/dist/services/device.service.mjs.map +1 -1
- package/dist/services/diagnostics.service.d.mts +1 -1
- package/dist/services/entity.service.d.mts +2 -2
- package/dist/services/entity.service.mjs +2 -2
- package/dist/services/entity.service.mjs.map +1 -1
- package/dist/services/events.service.d.mts +2 -2
- package/dist/services/events.service.mjs.map +1 -1
- package/dist/services/fetch-api.service.d.mts +5 -5
- package/dist/services/fetch-api.service.mjs +1 -1
- package/dist/services/fetch-api.service.mjs.map +1 -1
- package/dist/services/floor.service.d.mts +2 -2
- package/dist/services/floor.service.mjs +1 -1
- package/dist/services/floor.service.mjs.map +1 -1
- package/dist/services/id-by.service.d.mts +2 -2
- package/dist/services/id-by.service.mjs.map +1 -1
- package/dist/services/internal.service.d.mts +2 -2
- package/dist/services/internal.service.mjs +1 -1
- package/dist/services/internal.service.mjs.map +1 -1
- package/dist/services/label.service.d.mts +2 -2
- package/dist/services/label.service.mjs +1 -1
- package/dist/services/label.service.mjs.map +1 -1
- package/dist/services/reference.service.d.mts +2 -2
- package/dist/services/reference.service.mjs +1 -1
- package/dist/services/reference.service.mjs.map +1 -1
- package/dist/services/registry.service.d.mts +2 -2
- package/dist/services/websocket-api.service.d.mts +2 -2
- package/dist/services/websocket-api.service.mjs +53 -39
- package/dist/services/websocket-api.service.mjs.map +1 -1
- package/dist/services/zone.service.d.mts +2 -2
- package/dist/services/zone.service.mjs +1 -1
- package/dist/services/zone.service.mjs.map +1 -1
- package/dist/testing/area.spec.mjs.map +1 -1
- package/dist/testing/floor.spec.mjs.map +1 -1
- package/dist/testing/label.spec.mjs.map +1 -1
- package/dist/testing/websocket.spec.mjs +125 -0
- package/dist/testing/websocket.spec.mjs.map +1 -1
- package/dist/testing/zone.spec.mjs.map +1 -1
- package/package.json +28 -28
- package/src/dev/services.mts +1 -1
- package/src/helpers/device.mts +1 -1
- package/src/helpers/entity-state.mts +6 -6
- package/src/helpers/fetch/calendar.mts +2 -2
- package/src/helpers/fetch/configuration.mts +1 -1
- package/src/helpers/fetch/service-list.mts +3 -3
- package/src/helpers/fetch.mts +3 -2
- package/src/helpers/id-by.mts +1 -1
- package/src/helpers/interfaces.mts +22 -15
- package/src/helpers/registry.mts +1 -1
- package/src/helpers/utility.mts +6 -5
- package/src/helpers/websocket.mts +12 -8
- package/src/merge.mts +1 -1
- package/src/mock_assistant/helpers/fixtures.mts +2 -2
- package/src/mock_assistant/main.mts +3 -2
- package/src/mock_assistant/services/area.service.mts +3 -3
- package/src/mock_assistant/services/config.service.mts +3 -2
- package/src/mock_assistant/services/device.service.mts +4 -3
- package/src/mock_assistant/services/entity-registry.service.mts +3 -3
- package/src/mock_assistant/services/entity.service.mts +4 -3
- package/src/mock_assistant/services/events.service.mts +4 -3
- package/src/mock_assistant/services/fixtures.service.mts +5 -4
- package/src/mock_assistant/services/floor.service.mts +3 -3
- package/src/mock_assistant/services/label.service.mts +3 -3
- package/src/mock_assistant/services/services.service.mts +2 -2
- package/src/mock_assistant/services/websocket-api.service.mts +5 -4
- package/src/mock_assistant/services/zone.service.mts +3 -3
- package/src/quickboot.module.mts +2 -1
- package/src/services/area.service.mts +5 -11
- package/src/services/backup.service.mts +3 -2
- package/src/services/call-proxy.service.mts +4 -4
- package/src/services/config.service.mts +5 -8
- package/src/services/conversation.service.mts +3 -7
- package/src/services/device.service.mts +4 -8
- package/src/services/diagnostics.service.mts +1 -1
- package/src/services/entity.service.mts +7 -15
- package/src/services/events.service.mts +2 -3
- package/src/services/fetch-api.service.mts +8 -7
- package/src/services/floor.service.mts +5 -10
- package/src/services/id-by.service.mts +5 -3
- package/src/services/internal.service.mts +4 -4
- package/src/services/label.service.mts +5 -10
- package/src/services/reference.service.mts +8 -7
- package/src/services/registry.service.mts +2 -2
- package/src/services/websocket-api.service.mts +74 -60
- package/src/services/zone.service.mts +4 -10
- package/src/testing/area.spec.mts +3 -2
- package/src/testing/backup.spec.mts +1 -1
- package/src/testing/config.spec.mts +1 -1
- package/src/testing/device.spec.mts +1 -1
- package/src/testing/entity.spec.mts +2 -2
- package/src/testing/fetch-api.spec.mts +2 -2
- package/src/testing/floor.spec.mts +3 -2
- package/src/testing/id-by.spec.mts +1 -1
- package/src/testing/label.spec.mts +3 -2
- package/src/testing/ref-by.spec.mts +2 -2
- package/src/testing/websocket.spec.mts +156 -0
- package/src/testing/zone.spec.mts +2 -1
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import type { TAnyFunction, TServiceParams } from "@digital-alchemy/core";
|
|
2
|
+
import { DOWN, NONE, sleep, UP } from "@digital-alchemy/core";
|
|
3
|
+
import type { Dayjs } from "dayjs";
|
|
4
|
+
import dayjs from "dayjs";
|
|
5
|
+
import type { Get } from "type-fest";
|
|
4
6
|
|
|
5
|
-
import {
|
|
7
|
+
import type {
|
|
6
8
|
ALL_SERVICE_DOMAINS,
|
|
7
9
|
ByIdProxy,
|
|
8
|
-
domain,
|
|
9
10
|
ENTITY_STATE,
|
|
10
11
|
HassReferenceService,
|
|
11
|
-
perf,
|
|
12
12
|
RemoveCallback,
|
|
13
13
|
} from "../helpers/index.mts";
|
|
14
|
-
import {
|
|
14
|
+
import { domain, perf } from "../helpers/index.mts";
|
|
15
|
+
import type {
|
|
15
16
|
ANY_ENTITY,
|
|
16
17
|
HassUniqueIdMapping,
|
|
17
18
|
PICK_ENTITY,
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
START,
|
|
6
|
-
TBlackHole,
|
|
7
|
-
TServiceParams,
|
|
8
|
-
} from "@digital-alchemy/core";
|
|
9
|
-
import dayjs, { Dayjs } from "dayjs";
|
|
1
|
+
import type { TBlackHole, TServiceParams } from "@digital-alchemy/core";
|
|
2
|
+
import { InternalError, SECOND, sleep, START } from "@digital-alchemy/core";
|
|
3
|
+
import type { Dayjs } from "dayjs";
|
|
4
|
+
import dayjs from "dayjs";
|
|
10
5
|
import EventEmitter from "events";
|
|
6
|
+
import type { EmptyObject } from "type-fest";
|
|
11
7
|
import WS from "ws";
|
|
12
8
|
|
|
13
|
-
import {
|
|
9
|
+
import type {
|
|
14
10
|
ConnectionState,
|
|
15
11
|
EntityUpdateEvent,
|
|
16
12
|
HassWebsocketAPI,
|
|
@@ -34,6 +30,8 @@ type WaitingMap = Map<
|
|
|
34
30
|
}
|
|
35
31
|
>;
|
|
36
32
|
|
|
33
|
+
type MessageHandlerMap = Map<string, Array<(message: { type: string }) => TBlackHole>>;
|
|
34
|
+
|
|
37
35
|
export function WebsocketAPI({
|
|
38
36
|
context,
|
|
39
37
|
event,
|
|
@@ -55,6 +53,7 @@ export function WebsocketAPI({
|
|
|
55
53
|
let MESSAGE_TIMESTAMPS: number[] = [];
|
|
56
54
|
let onSocketReady: () => void;
|
|
57
55
|
const waitingCallback: WaitingMap = new Map();
|
|
56
|
+
const messageHandlers: MessageHandlerMap = new Map();
|
|
58
57
|
const isOld = (date: Dayjs) =>
|
|
59
58
|
is.undefined(date) || date.diff(dayjs(), "s") >= config.hass.RETRY_INTERVAL;
|
|
60
59
|
|
|
@@ -293,6 +292,20 @@ export function WebsocketAPI({
|
|
|
293
292
|
);
|
|
294
293
|
}
|
|
295
294
|
|
|
295
|
+
// #MARK: registerMessageHandler
|
|
296
|
+
function registerMessageHandler<T extends { type: string }>(
|
|
297
|
+
type: string,
|
|
298
|
+
callback: (message: T) => TBlackHole,
|
|
299
|
+
) {
|
|
300
|
+
const handlers = messageHandlers.get(type) ?? [];
|
|
301
|
+
logger.trace({ type }, "register socket message handler");
|
|
302
|
+
if (!messageHandlers.has(type)) {
|
|
303
|
+
messageHandlers.set(type, []);
|
|
304
|
+
}
|
|
305
|
+
handlers.push(callback as (message: { type: string }) => TBlackHole);
|
|
306
|
+
messageHandlers.set(type, handlers);
|
|
307
|
+
}
|
|
308
|
+
|
|
296
309
|
// #MARK: countMessage
|
|
297
310
|
function countMessage(): void | never {
|
|
298
311
|
messageCount++;
|
|
@@ -403,54 +416,18 @@ export function WebsocketAPI({
|
|
|
403
416
|
* ## result
|
|
404
417
|
* Response to an outgoing emit
|
|
405
418
|
*/
|
|
406
|
-
async function onMessage(message:
|
|
407
|
-
const id = Number(message.id);
|
|
419
|
+
async function onMessage<T extends { type: string }>(message: T) {
|
|
408
420
|
setImmediate(() => hass.diagnostics.websocket?.message_received.publish({ message }));
|
|
409
|
-
switch (message.type) {
|
|
410
|
-
case "auth_required": {
|
|
411
|
-
logger.trace({ name: onMessage }, `sending authentication`);
|
|
412
|
-
void hass.socket.sendMessage({ access_token: config.hass.TOKEN, type: "auth" }, false);
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
case "auth_ok": {
|
|
417
|
-
// * Flag as valid connection
|
|
418
|
-
logger.trace({ name: onMessage }, `event subscriptions starting`);
|
|
419
|
-
await hass.socket.sendMessage({ type: "subscribe_events" }, false);
|
|
420
|
-
onSocketReady?.();
|
|
421
|
-
event.emit(SOCKET_CONNECTED);
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
421
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
case "pong": {
|
|
431
|
-
// nothing in particular needs to be done, just don't log an error (default)
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
case "result": {
|
|
436
|
-
return await onMessageResult(id, message);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
case "auth_invalid": {
|
|
440
|
-
hass.socket.setConnectionState("invalid");
|
|
441
|
-
logger.fatal(
|
|
442
|
-
{ message, name: onMessage },
|
|
443
|
-
"received auth invalid {connecting} => {invalid}",
|
|
444
|
-
);
|
|
445
|
-
// ? If you have a use case for making this exit configurable, open a ticket
|
|
446
|
-
process.exit();
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
422
|
+
const handlers = messageHandlers.get(message.type);
|
|
423
|
+
if (is.empty(handlers)) {
|
|
424
|
+
logger.error(`unknown websocket message type: ${message.type}`);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
449
427
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
428
|
+
// Execute all registered handlers for this message type
|
|
429
|
+
for (const handler of handlers) {
|
|
430
|
+
await handler(message as { type: string });
|
|
454
431
|
}
|
|
455
432
|
}
|
|
456
433
|
|
|
@@ -523,11 +500,10 @@ export function WebsocketAPI({
|
|
|
523
500
|
}
|
|
524
501
|
|
|
525
502
|
// #MARK: subscribe
|
|
526
|
-
async function subscribe<
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}: SocketSubscribeOptions<EVENT>) {
|
|
503
|
+
async function subscribe<
|
|
504
|
+
EVENT extends string,
|
|
505
|
+
PAYLOAD extends Record<string, unknown> = EmptyObject,
|
|
506
|
+
>({ event_type, context, exec }: SocketSubscribeOptions<EVENT, PAYLOAD>) {
|
|
531
507
|
await hass.socket.sendMessage({ event_type, type: "subscribe_events" });
|
|
532
508
|
return hass.socket.onEvent({
|
|
533
509
|
context,
|
|
@@ -553,6 +529,43 @@ export function WebsocketAPI({
|
|
|
553
529
|
return internal.removeFn(() => event.removeListener(SOCKET_CONNECTED, wrapped));
|
|
554
530
|
}
|
|
555
531
|
|
|
532
|
+
lifecycle.onPreInit(() => {
|
|
533
|
+
// Register all current message handlers
|
|
534
|
+
hass.socket.registerMessageHandler("auth_required", async () => {
|
|
535
|
+
logger.trace({ name: onMessage }, `sending authentication`);
|
|
536
|
+
void hass.socket.sendMessage({ access_token: config.hass.TOKEN, type: "auth" }, false);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
hass.socket.registerMessageHandler("auth_ok", async () => {
|
|
540
|
+
// * Flag as valid connection
|
|
541
|
+
logger.trace({ name: onMessage }, `event subscriptions starting`);
|
|
542
|
+
await hass.socket.sendMessage({ type: "subscribe_events" }, false);
|
|
543
|
+
onSocketReady?.();
|
|
544
|
+
event.emit(SOCKET_CONNECTED);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
hass.socket.registerMessageHandler("event", async (message: SocketMessageDTO) => {
|
|
548
|
+
const id = Number(message.id);
|
|
549
|
+
return await onMessageEvent(id, message);
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
hass.socket.registerMessageHandler("pong", async () => {
|
|
553
|
+
// nothing in particular needs to be done, just don't log an error (default)
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
hass.socket.registerMessageHandler("result", async (message: SocketMessageDTO) => {
|
|
557
|
+
const id = Number(message.id);
|
|
558
|
+
return await onMessageResult(id, message);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
hass.socket.registerMessageHandler("auth_invalid", async (message: SocketMessageDTO) => {
|
|
562
|
+
hass.socket.setConnectionState("invalid");
|
|
563
|
+
logger.fatal({ message, name: onMessage }, "received auth invalid {connecting} => {invalid}");
|
|
564
|
+
// ? If you have a use case for making this exit configurable, open a ticket
|
|
565
|
+
process.exit();
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
|
|
556
569
|
// #MARK: return object
|
|
557
570
|
return {
|
|
558
571
|
attachScheduledFunctions,
|
|
@@ -565,6 +578,7 @@ export function WebsocketAPI({
|
|
|
565
578
|
onEvent,
|
|
566
579
|
onMessage,
|
|
567
580
|
pauseMessages: false,
|
|
581
|
+
registerMessageHandler,
|
|
568
582
|
sendMessage,
|
|
569
583
|
setConnectionState,
|
|
570
584
|
socketEvents,
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { TServiceParams } from "@digital-alchemy/core";
|
|
2
|
+
import { debounce } from "@digital-alchemy/core";
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
HassZoneService,
|
|
6
|
-
ManifestItem,
|
|
7
|
-
perf,
|
|
8
|
-
ZONE_REGISTRY_UPDATED,
|
|
9
|
-
ZoneDetails,
|
|
10
|
-
ZoneOptions,
|
|
11
|
-
} from "../helpers/index.mts";
|
|
4
|
+
import type { HassZoneService, ManifestItem, ZoneDetails, ZoneOptions } from "../helpers/index.mts";
|
|
5
|
+
import { EARLY_ON_READY, perf, ZONE_REGISTRY_UPDATED } from "../helpers/index.mts";
|
|
12
6
|
|
|
13
7
|
export function Zone({
|
|
14
8
|
config,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { sleep } from "@digital-alchemy/core";
|
|
2
2
|
import { subscribe } from "diagnostics_channel";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import type { AreaDetails } from "../helpers/index.mts";
|
|
5
|
+
import { AREA_REGISTRY_UPDATED } from "../helpers/index.mts";
|
|
5
6
|
import { hassTestRunner, INTERNAL_MESSAGE } from "../mock_assistant/index.mts";
|
|
6
|
-
import { TAreaId } from "../user.mts";
|
|
7
|
+
import type { TAreaId } from "../user.mts";
|
|
7
8
|
|
|
8
9
|
const EXAMPLE_AREA = {
|
|
9
10
|
area_id: "empty_area" as TAreaId,
|
|
@@ -2,7 +2,7 @@ import { subscribe } from "node:diagnostics_channel";
|
|
|
2
2
|
|
|
3
3
|
import { sleep } from "@digital-alchemy/core";
|
|
4
4
|
|
|
5
|
-
import { DeviceDetails } from "../helpers/index.mts";
|
|
5
|
+
import type { DeviceDetails } from "../helpers/index.mts";
|
|
6
6
|
import { hassTestRunner } from "../mock_assistant/index.mts";
|
|
7
7
|
|
|
8
8
|
describe("Device", () => {
|
|
@@ -3,9 +3,9 @@ import { subscribe } from "node:diagnostics_channel";
|
|
|
3
3
|
import { sleep } from "@digital-alchemy/core";
|
|
4
4
|
import dayjs from "dayjs";
|
|
5
5
|
|
|
6
|
-
import { ENTITY_STATE } from "../index.mts";
|
|
6
|
+
import type { ENTITY_STATE } from "../index.mts";
|
|
7
7
|
import { hassTestRunner } from "../mock_assistant/index.mts";
|
|
8
|
-
import { ANY_ENTITY } from "../user.mts";
|
|
8
|
+
import type { ANY_ENTITY } from "../user.mts";
|
|
9
9
|
|
|
10
10
|
describe("Entity", () => {
|
|
11
11
|
afterEach(async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import dayjs from "dayjs";
|
|
2
|
-
import { MockInstance } from "vitest";
|
|
2
|
+
import type { MockInstance } from "vitest";
|
|
3
3
|
|
|
4
|
-
import { HassConfig } from "../helpers/index.mts";
|
|
4
|
+
import type { HassConfig } from "../helpers/index.mts";
|
|
5
5
|
import { hassTestRunner } from "../mock_assistant/index.mts";
|
|
6
6
|
|
|
7
7
|
describe("FetchAPI", () => {
|
|
@@ -2,9 +2,10 @@ import { subscribe } from "node:diagnostics_channel";
|
|
|
2
2
|
|
|
3
3
|
import { sleep } from "@digital-alchemy/core";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import type { FloorDetails } from "../helpers/index.mts";
|
|
6
|
+
import { FLOOR_REGISTRY_UPDATED } from "../helpers/index.mts";
|
|
6
7
|
import { hassTestRunner } from "../mock_assistant/index.mts";
|
|
7
|
-
import { TFloorId } from "../user.mts";
|
|
8
|
+
import type { TFloorId } from "../user.mts";
|
|
8
9
|
|
|
9
10
|
describe("Floor", () => {
|
|
10
11
|
const EXAMPLE_FLOOR = {
|
|
@@ -2,9 +2,10 @@ import { subscribe } from "node:diagnostics_channel";
|
|
|
2
2
|
|
|
3
3
|
import { sleep } from "@digital-alchemy/core";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import type { LabelDefinition } from "../helpers/index.mts";
|
|
6
|
+
import { LABEL_REGISTRY_UPDATED } from "../helpers/index.mts";
|
|
6
7
|
import { hassTestRunner } from "../mock_assistant/index.mts";
|
|
7
|
-
import { TLabelId } from "../user.mts";
|
|
8
|
+
import type { TLabelId } from "../user.mts";
|
|
8
9
|
|
|
9
10
|
describe("Label", () => {
|
|
10
11
|
const EXAMPLE_LABEL = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import dayjs from "dayjs";
|
|
2
2
|
|
|
3
|
-
import { ENTITY_STATE } from "../helpers/index.mts";
|
|
3
|
+
import type { ENTITY_STATE } from "../helpers/index.mts";
|
|
4
4
|
import { hassTestRunner } from "../mock_assistant/index.mts";
|
|
5
|
-
import { ANY_ENTITY } from "../user.mts";
|
|
5
|
+
import type { ANY_ENTITY } from "../user.mts";
|
|
6
6
|
|
|
7
7
|
describe("References", () => {
|
|
8
8
|
afterEach(async () => {
|
|
@@ -56,4 +56,160 @@ describe("Websocket", () => {
|
|
|
56
56
|
});
|
|
57
57
|
});
|
|
58
58
|
});
|
|
59
|
+
|
|
60
|
+
describe("Message Handler Registration", () => {
|
|
61
|
+
it("should register and execute message handlers", async () => {
|
|
62
|
+
expect.assertions(4);
|
|
63
|
+
await hassTestRunner.run(({ lifecycle, hass }) => {
|
|
64
|
+
let handler1Called = false;
|
|
65
|
+
let handler2Called = false;
|
|
66
|
+
|
|
67
|
+
hass.socket.registerMessageHandler("test_message", async message => {
|
|
68
|
+
handler1Called = true;
|
|
69
|
+
expect(message.type).toBe("test_message");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
hass.socket.registerMessageHandler("test_message", async message => {
|
|
73
|
+
handler2Called = true;
|
|
74
|
+
expect(message.type).toBe("test_message");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
lifecycle.onReady(async () => {
|
|
78
|
+
await hass.socket.onMessage({
|
|
79
|
+
data: { test: "data" },
|
|
80
|
+
id: 1,
|
|
81
|
+
type: "test_message",
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect(handler1Called).toBe(true);
|
|
85
|
+
expect(handler2Called).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should handle generic message types", async () => {
|
|
91
|
+
expect.assertions(2);
|
|
92
|
+
await hassTestRunner.run(({ lifecycle, hass }) => {
|
|
93
|
+
interface CustomMessage {
|
|
94
|
+
type: string;
|
|
95
|
+
customProperty: string;
|
|
96
|
+
id: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let handlerCalled = false;
|
|
100
|
+
|
|
101
|
+
hass.socket.registerMessageHandler<CustomMessage>("custom_type", async message => {
|
|
102
|
+
handlerCalled = true;
|
|
103
|
+
expect(message.customProperty).toBe("test_value");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
lifecycle.onReady(async () => {
|
|
107
|
+
await hass.socket.onMessage({
|
|
108
|
+
customProperty: "test_value",
|
|
109
|
+
id: 1,
|
|
110
|
+
type: "custom_type",
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
expect(handlerCalled).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should handle unknown message types gracefully", async () => {
|
|
119
|
+
expect.assertions(1);
|
|
120
|
+
await hassTestRunner.run(({ lifecycle, hass }) => {
|
|
121
|
+
let errorOccurred = false;
|
|
122
|
+
|
|
123
|
+
lifecycle.onReady(async () => {
|
|
124
|
+
try {
|
|
125
|
+
// Use a message type that definitely won't have handlers and won't trigger auth flow
|
|
126
|
+
await hass.socket.onMessage({
|
|
127
|
+
id: 1,
|
|
128
|
+
type: "completely_unknown_type_12345",
|
|
129
|
+
});
|
|
130
|
+
} catch {
|
|
131
|
+
errorOccurred = true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// The function should complete without throwing an error
|
|
135
|
+
expect(errorOccurred).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should execute multiple handlers for the same message type", async () => {
|
|
141
|
+
expect.assertions(4);
|
|
142
|
+
await hassTestRunner.run(({ lifecycle, hass }) => {
|
|
143
|
+
const executionOrder: string[] = [];
|
|
144
|
+
|
|
145
|
+
hass.socket.registerMessageHandler("multi_handler", async message => {
|
|
146
|
+
executionOrder.push("first");
|
|
147
|
+
expect(message.type).toBe("multi_handler");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
hass.socket.registerMessageHandler("multi_handler", async message => {
|
|
151
|
+
executionOrder.push("second");
|
|
152
|
+
expect(message.type).toBe("multi_handler");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
hass.socket.registerMessageHandler("multi_handler", async message => {
|
|
156
|
+
executionOrder.push("third");
|
|
157
|
+
expect(message.type).toBe("multi_handler");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
lifecycle.onReady(async () => {
|
|
161
|
+
await hass.socket.onMessage({
|
|
162
|
+
id: 1,
|
|
163
|
+
type: "multi_handler",
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
expect(executionOrder).toEqual(["first", "second", "third"]);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("should handle async handlers correctly", async () => {
|
|
172
|
+
expect.assertions(2);
|
|
173
|
+
await hassTestRunner.run(({ lifecycle, hass }) => {
|
|
174
|
+
let asyncHandlerCompleted = false;
|
|
175
|
+
|
|
176
|
+
hass.socket.registerMessageHandler("async_test", async message => {
|
|
177
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
178
|
+
asyncHandlerCompleted = true;
|
|
179
|
+
expect(message.type).toBe("async_test");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
lifecycle.onReady(async () => {
|
|
183
|
+
await hass.socket.onMessage({
|
|
184
|
+
id: 1,
|
|
185
|
+
type: "async_test",
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
expect(asyncHandlerCompleted).toBe(true);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("should work with existing message types", async () => {
|
|
194
|
+
expect.assertions(2);
|
|
195
|
+
await hassTestRunner.run(({ lifecycle, hass }) => {
|
|
196
|
+
let customAuthHandlerCalled = false;
|
|
197
|
+
|
|
198
|
+
// Register an additional handler for auth_required
|
|
199
|
+
hass.socket.registerMessageHandler("auth_required", async message => {
|
|
200
|
+
customAuthHandlerCalled = true;
|
|
201
|
+
expect(message.type).toBe("auth_required");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
lifecycle.onReady(async () => {
|
|
205
|
+
await hass.socket.onMessage({
|
|
206
|
+
id: 1,
|
|
207
|
+
type: "auth_required",
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(customAuthHandlerCalled).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
59
215
|
});
|
|
@@ -2,7 +2,8 @@ import { subscribe } from "node:diagnostics_channel";
|
|
|
2
2
|
|
|
3
3
|
import { sleep } from "@digital-alchemy/core";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import type { ZoneDetails } from "../helpers/index.mts";
|
|
6
|
+
import { ZONE_REGISTRY_UPDATED } from "../helpers/index.mts";
|
|
6
7
|
import { hassTestRunner } from "../mock_assistant/index.mts";
|
|
7
8
|
|
|
8
9
|
describe("Zone", () => {
|