@digital-alchemy/hass 25.2.2 → 25.3.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.
Files changed (115) hide show
  1. package/dist/dev/index.d.mts +3 -0
  2. package/dist/dev/index.mjs +4 -0
  3. package/dist/dev/index.mjs.map +1 -0
  4. package/dist/dev/mappings.d.mts +70 -0
  5. package/dist/dev/mappings.mjs +2 -0
  6. package/dist/dev/mappings.mjs.map +1 -0
  7. package/dist/dev/registry.d.mts +328 -0
  8. package/dist/dev/registry.mjs +2 -0
  9. package/dist/dev/registry.mjs.map +1 -0
  10. package/dist/dev/services.d.mts +2431 -0
  11. package/dist/dev/services.mjs +5 -0
  12. package/dist/dev/services.mjs.map +1 -0
  13. package/dist/helpers/device.d.mts +1 -1
  14. package/dist/helpers/entity-state.d.mts +2 -2
  15. package/dist/helpers/fetch/calendar.d.mts +1 -1
  16. package/dist/helpers/fetch/configuration.d.mts +1 -1
  17. package/dist/helpers/fetch/service-list.d.mts +28 -3
  18. package/dist/helpers/id-by.d.mts +2 -3
  19. package/dist/helpers/interfaces.d.mts +4 -5
  20. package/dist/helpers/interfaces.mjs.map +1 -1
  21. package/dist/helpers/registry.d.mts +1 -2
  22. package/dist/helpers/registry.mjs.map +1 -1
  23. package/dist/helpers/utility.d.mts +5 -21
  24. package/dist/helpers/utility.mjs +0 -5
  25. package/dist/helpers/utility.mjs.map +1 -1
  26. package/dist/helpers/websocket.d.mts +2 -1
  27. package/dist/index.d.mts +2 -1
  28. package/dist/index.mjs +2 -1
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/mock_assistant/helpers/fixtures.d.mts +2 -1
  31. package/dist/mock_assistant/services/device.service.mjs.map +1 -1
  32. package/dist/mock_assistant/services/entity-registry.service.d.mts +1 -1
  33. package/dist/mock_assistant/services/entity.service.d.mts +2 -2
  34. package/dist/mock_assistant/services/events.service.d.mts +2 -1
  35. package/dist/mock_assistant/services/events.service.mjs.map +1 -1
  36. package/dist/mock_assistant/services/fixtures.service.d.mts +3 -2
  37. package/dist/mock_assistant/services/fixtures.service.mjs.map +1 -1
  38. package/dist/mock_assistant/services/zone.service.mjs +1 -1
  39. package/dist/mock_assistant/services/zone.service.mjs.map +1 -1
  40. package/dist/services/area.service.d.mts +1 -1
  41. package/dist/services/area.service.mjs +1 -1
  42. package/dist/services/area.service.mjs.map +1 -1
  43. package/dist/services/call-proxy.service.d.mts +1 -1
  44. package/dist/services/call-proxy.service.mjs.map +1 -1
  45. package/dist/services/config.service.mjs.map +1 -1
  46. package/dist/services/entity.service.d.mts +1 -1
  47. package/dist/services/entity.service.mjs +1 -1
  48. package/dist/services/entity.service.mjs.map +1 -1
  49. package/dist/services/fetch-api.service.d.mts +3 -2
  50. package/dist/services/fetch-api.service.mjs.map +1 -1
  51. package/dist/services/floor.service.mjs.map +1 -1
  52. package/dist/services/id-by.service.d.mts +1 -1
  53. package/dist/services/id-by.service.mjs +28 -3
  54. package/dist/services/id-by.service.mjs.map +1 -1
  55. package/dist/services/label.service.mjs.map +1 -1
  56. package/dist/services/reference.service.mjs +4 -3
  57. package/dist/services/reference.service.mjs.map +1 -1
  58. package/dist/services/websocket-api.service.mjs +2 -2
  59. package/dist/services/websocket-api.service.mjs.map +1 -1
  60. package/dist/testing/area.spec.mjs.map +1 -1
  61. package/dist/testing/entity.spec.mjs.map +1 -1
  62. package/dist/testing/floor.spec.mjs.map +1 -1
  63. package/dist/testing/id-by.spec.mjs.map +1 -1
  64. package/dist/testing/label.spec.mjs.map +1 -1
  65. package/dist/testing/ref-by.spec.mjs.map +1 -1
  66. package/dist/user.d.mts +43 -0
  67. package/dist/user.mjs +4 -0
  68. package/dist/user.mjs.map +1 -0
  69. package/package.json +18 -17
  70. package/src/dev/index.mts +3 -0
  71. package/src/dev/mappings.mts +121 -0
  72. package/src/dev/registry.mts +330 -0
  73. package/src/dev/services.mts +2759 -0
  74. package/src/helpers/device.mts +1 -1
  75. package/src/helpers/entity-state.mts +6 -9
  76. package/src/helpers/fetch/calendar.mts +1 -1
  77. package/src/helpers/fetch/configuration.mts +1 -1
  78. package/src/helpers/fetch/service-list.mts +23 -3
  79. package/src/helpers/id-by.mts +11 -13
  80. package/src/helpers/interfaces.mts +14 -18
  81. package/src/helpers/registry.mts +9 -2
  82. package/src/helpers/utility.mts +13 -67
  83. package/src/helpers/websocket.mts +2 -1
  84. package/src/index.mts +2 -1
  85. package/src/mock_assistant/helpers/fixtures.mts +1 -1
  86. package/src/mock_assistant/services/area.service.mts +1 -1
  87. package/src/mock_assistant/services/device.service.mts +1 -1
  88. package/src/mock_assistant/services/entity-registry.service.mts +1 -1
  89. package/src/mock_assistant/services/entity.service.mts +2 -2
  90. package/src/mock_assistant/services/events.service.mts +2 -1
  91. package/src/mock_assistant/services/fixtures.service.mts +2 -1
  92. package/src/mock_assistant/services/floor.service.mts +1 -1
  93. package/src/mock_assistant/services/label.service.mts +1 -1
  94. package/src/mock_assistant/services/zone.service.mts +13 -10
  95. package/src/services/area.service.mts +2 -3
  96. package/src/services/call-proxy.service.mts +2 -2
  97. package/src/services/config.service.mts +1 -1
  98. package/src/services/entity.service.mts +2 -4
  99. package/src/services/fetch-api.service.mts +1 -1
  100. package/src/services/floor.service.mts +1 -1
  101. package/src/services/id-by.service.mts +47 -19
  102. package/src/services/label.service.mts +1 -1
  103. package/src/services/reference.service.mts +19 -18
  104. package/src/services/websocket-api.service.mts +2 -2
  105. package/src/testing/area.spec.mts +1 -1
  106. package/src/testing/entity.spec.mts +2 -1
  107. package/src/testing/floor.spec.mts +1 -1
  108. package/src/testing/id-by.spec.mts +1 -1
  109. package/src/testing/label.spec.mts +1 -1
  110. package/src/testing/ref-by.spec.mts +2 -1
  111. package/src/user.mts +97 -0
  112. package/dist/dynamic.d.mts +0 -3764
  113. package/dist/dynamic.mjs +0 -7
  114. package/dist/dynamic.mjs.map +0 -1
  115. package/src/dynamic.mts +0 -4302
@@ -1,26 +1,23 @@
1
- import { TServiceParams } from "@digital-alchemy/core";
1
+ import { FIRST, SINGLE, TServiceParams } from "@digital-alchemy/core";
2
2
 
3
- import {
4
- TAreaId,
5
- TDeviceId,
6
- TFloorId,
7
- TLabelId,
8
- TPlatformId,
9
- TUniqueId,
10
- TUniqueIDMapping,
11
- } from "../dynamic.mts";
3
+ import { domain, EntityRegistryItem, IDByInterface } from "../index.mts";
12
4
  import {
13
5
  ALL_DOMAINS,
14
6
  ANY_ENTITY,
15
- EntityRegistryItem,
16
- IDByInterface,
7
+ HassUniqueIdMapping,
17
8
  PICK_ENTITY,
18
9
  PICK_FROM_AREA,
19
10
  PICK_FROM_DEVICE,
20
11
  PICK_FROM_FLOOR,
21
12
  PICK_FROM_LABEL,
22
13
  PICK_FROM_PLATFORM,
23
- } from "../helpers/index.mts";
14
+ TAreaId,
15
+ TDeviceId,
16
+ TFloorId,
17
+ TLabelId,
18
+ TPlatformId,
19
+ TUniqueId,
20
+ } from "../user.mts";
24
21
 
25
22
  export function IDByExtension({
26
23
  hass,
@@ -44,22 +41,53 @@ export function IDByExtension({
44
41
  );
45
42
  }
46
43
 
47
- // * unique_id
44
+ /**
45
+ * unique_id
46
+ *
47
+ * ## Dev note on logic here
48
+ *
49
+ * unique_ids are not ACTUALLY unique when iterating through the registry from here
50
+ * hacs does this a bunch, using a sensor with the same unique_id as an update entity
51
+ *
52
+ * The logic here will look up ALL entities with the "unique"_id, then check to see if there are multiple results
53
+ * It is assumed by both this code and type-writer that the unique_id lookup will refer to the not update entity
54
+ */
48
55
  function unique_id<
49
56
  UNIQUE_ID extends TUniqueId,
50
- ENTITY_ID extends Extract<TUniqueIDMapping[UNIQUE_ID], ANY_ENTITY> = Extract<
51
- TUniqueIDMapping[UNIQUE_ID],
57
+ ENTITY_ID extends Extract<HassUniqueIdMapping[UNIQUE_ID], ANY_ENTITY> = Extract<
58
+ HassUniqueIdMapping[UNIQUE_ID],
52
59
  ANY_ENTITY
53
60
  >,
54
61
  >(unique_id: UNIQUE_ID): ENTITY_ID {
55
62
  hass.entity.warnEarly("byUniqueId");
56
- const entity = hass.entity.registry.current.find(
63
+ let list = hass.entity.registry.current.filter(
57
64
  i => i.unique_id === unique_id,
58
- ) as EntityRegistryItem<ENTITY_ID>;
59
- if (!entity) {
65
+ ) as EntityRegistryItem<ENTITY_ID>[];
66
+ if (is.empty(list)) {
60
67
  logger.error({ name: unique_id, unique_id }, `could not find an entity`);
61
68
  return undefined;
62
69
  }
70
+ if (list.length > SINGLE) {
71
+ const trimmed = list.filter(
72
+ i =>
73
+ // @ts-expect-error issue in dev types
74
+ domain(i.entity_id) !== "update",
75
+ );
76
+ if (trimmed.length > SINGLE) {
77
+ logger.warn(
78
+ { available_entity_ids: trimmed.map(i => i.entity_id), unique_id },
79
+ `unique_id collision during lookup (chose first in list)`,
80
+ );
81
+ } else {
82
+ logger.trace(
83
+ { unique_id },
84
+ `chose {%s} over update entity during lookup`,
85
+ trimmed[FIRST].entity_id,
86
+ );
87
+ }
88
+ list = trimmed;
89
+ }
90
+ const [entity] = list;
63
91
  return entity?.entity_id;
64
92
  }
65
93
 
@@ -1,6 +1,5 @@
1
1
  import { debounce, TServiceParams } from "@digital-alchemy/core";
2
2
 
3
- import { TLabelId } from "../dynamic.mts";
4
3
  import {
5
4
  EARLY_ON_READY,
6
5
  HassLabelService,
@@ -8,6 +7,7 @@ import {
8
7
  LabelDefinition,
9
8
  LabelOptions,
10
9
  } from "../helpers/index.mts";
10
+ import { TLabelId } from "../user.mts";
11
11
 
12
12
  export function Label({
13
13
  hass,
@@ -2,31 +2,31 @@ import { DOWN, NONE, sleep, TAnyFunction, TServiceParams, UP } from "@digital-al
2
2
  import dayjs, { Dayjs } from "dayjs";
3
3
  import { Get } from "type-fest";
4
4
 
5
- import {
6
- TAreaId,
7
- TDeviceId,
8
- TFloorId,
9
- TLabelId,
10
- TPlatformId,
11
- TRawDomains,
12
- TUniqueId,
13
- TUniqueIDMapping,
14
- } from "../dynamic.mts";
15
5
  import {
16
6
  ALL_SERVICE_DOMAINS,
17
- ANY_ENTITY,
18
7
  ByIdProxy,
19
8
  domain,
20
9
  ENTITY_STATE,
21
10
  HassReferenceService,
11
+ RemoveCallback,
12
+ } from "../helpers/index.mts";
13
+ import {
14
+ ANY_ENTITY,
15
+ HassUniqueIdMapping,
22
16
  PICK_ENTITY,
23
17
  PICK_FROM_AREA,
24
18
  PICK_FROM_DEVICE,
25
19
  PICK_FROM_FLOOR,
26
20
  PICK_FROM_LABEL,
27
21
  PICK_FROM_PLATFORM,
28
- RemoveCallback,
29
- } from "../helpers/index.mts";
22
+ TAreaId,
23
+ TDeviceId,
24
+ TFloorId,
25
+ TLabelId,
26
+ TPlatformId,
27
+ TRawDomains,
28
+ TUniqueId,
29
+ } from "../user.mts";
30
30
  import { SERVICE_LIST_UPDATED } from "./config.service.mts";
31
31
  import { SOCKET_CONNECTED } from "./websocket-api.service.mts";
32
32
 
@@ -187,7 +187,7 @@ export function ReferenceService({
187
187
  event.on(SOCKET_CONNECTED, removableCallback);
188
188
  listeners.add(remove);
189
189
 
190
- return is.removeFn(remove);
190
+ return internal.removeFn(remove);
191
191
  };
192
192
  }
193
193
 
@@ -227,7 +227,7 @@ export function ReferenceService({
227
227
  };
228
228
  listeners.add(remove);
229
229
  event.once(entity_id, wrapped);
230
- return is.removeFn(remove);
230
+ return internal.removeFn(remove);
231
231
  };
232
232
  }
233
233
 
@@ -304,6 +304,7 @@ export function ReferenceService({
304
304
  listeners.add(remove);
305
305
 
306
306
  const complete = (entity: ENTITY_STATE<ENTITY_ID>) => {
307
+ // eslint-disable-next-line sonarjs/different-types-comparison
307
308
  if (entity.state !== state) {
308
309
  logger.trace(
309
310
  {
@@ -339,7 +340,7 @@ export function ReferenceService({
339
340
  // #MARK: service calls
340
341
  if (hass.configure.isService(entity_domain, property)) {
341
342
  return async function (data = {}) {
342
- // @ts-expect-error it's fine
343
+ // @ts-expect-error i don't care, this is fine
343
344
  return await hass.call[entity_domain][property]({
344
345
  entity_id,
345
346
  ...data,
@@ -434,8 +435,8 @@ export function ReferenceService({
434
435
 
435
436
  unique_id: <
436
437
  UNIQUE_ID extends TUniqueId,
437
- ENTITY_ID extends Extract<TUniqueIDMapping[UNIQUE_ID], ANY_ENTITY> = Extract<
438
- TUniqueIDMapping[UNIQUE_ID],
438
+ ENTITY_ID extends Extract<HassUniqueIdMapping[UNIQUE_ID], ANY_ENTITY> = Extract<
439
+ HassUniqueIdMapping[UNIQUE_ID],
439
440
  ANY_ENTITY
440
441
  >,
441
442
  >(
@@ -508,7 +508,7 @@ export function WebsocketAPI({
508
508
  } else {
509
509
  socketEvents.on(event, callback);
510
510
  }
511
- return is.removeFn(() => {
511
+ return internal.removeFn(() => {
512
512
  logger.trace({ context, event, name: onEvent }, `removing socket event listener`);
513
513
  socketEvents.removeListener(event, callback);
514
514
  });
@@ -542,7 +542,7 @@ export function WebsocketAPI({
542
542
  // attach anyways, for restarts or whatever
543
543
  }
544
544
  event.on(SOCKET_CONNECTED, wrapped);
545
- return is.removeFn(() => event.removeListener(SOCKET_CONNECTED, wrapped));
545
+ return internal.removeFn(() => event.removeListener(SOCKET_CONNECTED, wrapped));
546
546
  }
547
547
 
548
548
  // #MARK: return object
@@ -1,8 +1,8 @@
1
1
  import { sleep } from "@digital-alchemy/core";
2
2
 
3
- import { TAreaId } from "../dynamic.mts";
4
3
  import { AREA_REGISTRY_UPDATED, AreaDetails } from "../helpers/index.mts";
5
4
  import { hassTestRunner, INTERNAL_MESSAGE } from "../mock_assistant/index.mts";
5
+ import { TAreaId } from "../user.mts";
6
6
 
7
7
  describe("Area", () => {
8
8
  const EXAMPLE_AREA = {
@@ -1,7 +1,8 @@
1
1
  import { sleep } from "@digital-alchemy/core";
2
2
 
3
- import { ANY_ENTITY, ENTITY_STATE } from "../helpers/index.mts";
3
+ import { ENTITY_STATE } from "../index.mts";
4
4
  import { hassTestRunner } from "../mock_assistant/index.mts";
5
+ import { ANY_ENTITY } from "../user.mts";
5
6
 
6
7
  describe("Entity", () => {
7
8
  afterEach(async () => {
@@ -1,8 +1,8 @@
1
1
  import { sleep } from "@digital-alchemy/core";
2
2
 
3
- import { TFloorId } from "../dynamic.mts";
4
3
  import { FLOOR_REGISTRY_UPDATED, FloorDetails } from "../helpers/index.mts";
5
4
  import { hassTestRunner } from "../mock_assistant/index.mts";
5
+ import { TFloorId } from "../user.mts";
6
6
 
7
7
  describe("Floor", () => {
8
8
  const EXAMPLE_FLOOR = {
@@ -1,5 +1,5 @@
1
- import { PICK_ENTITY } from "../helpers/index.mts";
2
1
  import { hassTestRunner } from "../mock_assistant/index.mts";
2
+ import { PICK_ENTITY } from "../user.mts";
3
3
 
4
4
  describe("ID By", () => {
5
5
  afterEach(async () => {
@@ -1,8 +1,8 @@
1
1
  import { sleep } from "@digital-alchemy/core";
2
2
 
3
- import { TLabelId } from "../dynamic.mts";
4
3
  import { LABEL_REGISTRY_UPDATED, LabelDefinition } from "../helpers/index.mts";
5
4
  import { hassTestRunner } from "../mock_assistant/index.mts";
5
+ import { TLabelId } from "../user.mts";
6
6
 
7
7
  describe("Label", () => {
8
8
  const EXAMPLE_LABEL = {
@@ -1,7 +1,8 @@
1
1
  import dayjs from "dayjs";
2
2
 
3
- import { ANY_ENTITY, ENTITY_STATE } from "../helpers/index.mts";
3
+ import { ENTITY_STATE } from "../helpers/index.mts";
4
4
  import { hassTestRunner } from "../mock_assistant/index.mts";
5
+ import { ANY_ENTITY } from "../user.mts";
5
6
 
6
7
  describe("References", () => {
7
8
  afterEach(async () => {
package/src/user.mts ADDED
@@ -0,0 +1,97 @@
1
+ /* eslint-disable sonarjs/class-name */
2
+ /* eslint-disable @typescript-eslint/no-empty-object-type */
3
+
4
+ // #MARK: utility
5
+ type UnPrefix<T> = T extends `_${infer Platform}` ? Platform : never;
6
+
7
+ // #MARK: merging
8
+ export interface HassUniqueIdMapping {
9
+ // "{unique_id}": "{entity_id}"
10
+ }
11
+
12
+ export interface HassDomainMapping {
13
+ // "{domain}": "{entity_id}" | "{entity_id}" | ....
14
+ }
15
+
16
+ // ? prefix everything with "_" to ensure valid keys
17
+ export interface HassPlatformMapping {
18
+ // "_{platform}": "{entity_id}" | "{entity_id}" | ....
19
+ }
20
+
21
+ export interface HassDeviceMapping {
22
+ // "_{device}": "{entity_id}" | "{entity_id}" | ....
23
+ }
24
+
25
+ export interface HassAreaMapping {
26
+ // "_{area}": "{entity_id}" | "{entity_id}" | ....
27
+ }
28
+
29
+ export interface HassLabelMapping {
30
+ // "_{label}": "{entity_id}" | "{entity_id}" | ....
31
+ }
32
+
33
+ export interface HassFloorMapping {
34
+ // "_{label}": "{entity_id}" | "{entity_id}" | ....
35
+ }
36
+
37
+ export interface HassEntitySetupMapping {
38
+ // "{entity_id}": { attributes, context, entity_id, state }
39
+ }
40
+
41
+ export interface iCallService {
42
+ // "{service_domain}": { "{service}": fn }
43
+ }
44
+
45
+ export interface HassZoneMapping {
46
+ // "zone": true
47
+ }
48
+
49
+ // #MARK: extract
50
+ export type TAreaId = UnPrefix<keyof HassAreaMapping>;
51
+ export type TDeviceId = UnPrefix<keyof HassDeviceMapping>;
52
+ export type TFloorId = UnPrefix<keyof HassFloorMapping>;
53
+ export type TLabelId = UnPrefix<keyof HassLabelMapping>;
54
+ export type TPlatformId = UnPrefix<keyof HassPlatformMapping>;
55
+
56
+ export type TRawDomains = keyof HassDomainMapping;
57
+ export type TZoneId = keyof HassZoneMapping;
58
+ export type ALL_DOMAINS = keyof HassDomainMapping;
59
+ export type TUniqueId = keyof HassUniqueIdMapping;
60
+ export type ANY_ENTITY = keyof HassEntitySetupMapping;
61
+ export type TRawEntityIds = keyof HassEntitySetupMapping;
62
+
63
+ export type PICK_FROM_AREA<ID extends TAreaId, DOMAIN extends ALL_DOMAINS = ALL_DOMAINS> = Extract<
64
+ HassAreaMapping[`_${ID}`],
65
+ PICK_ENTITY<DOMAIN>
66
+ >;
67
+
68
+ export type PICK_FROM_LABEL<
69
+ ID extends TLabelId,
70
+ DOMAIN extends ALL_DOMAINS = ALL_DOMAINS,
71
+ > = Extract<HassLabelMapping[`_${ID}`], PICK_ENTITY<DOMAIN>>;
72
+
73
+ export type PICK_FROM_FLOOR<
74
+ ID extends TFloorId,
75
+ DOMAIN extends ALL_DOMAINS = ALL_DOMAINS,
76
+ > = Extract<HassFloorMapping[`_${ID}`], PICK_ENTITY<DOMAIN>>;
77
+
78
+ export type PICK_FROM_DEVICE<
79
+ ID extends TDeviceId,
80
+ DOMAIN extends ALL_DOMAINS = ALL_DOMAINS,
81
+ > = Extract<HassDeviceMapping[`_${ID}`], PICK_ENTITY<DOMAIN>>;
82
+
83
+ export type PICK_FROM_PLATFORM<
84
+ ID extends TPlatformId,
85
+ DOMAIN extends ALL_DOMAINS = ALL_DOMAINS,
86
+ > = Extract<HassPlatformMapping[`_${ID}`], PICK_ENTITY<DOMAIN>>;
87
+
88
+ export type GetDomain<ENTITY extends `${ALL_DOMAINS}.${string}`> =
89
+ ENTITY extends `${infer domain}.${string}` ? domain : never;
90
+
91
+ /**
92
+ * Pick any valid entity, optionally limiting by domain
93
+ */
94
+ export type PICK_ENTITY<DOMAIN extends ALL_DOMAINS = ALL_DOMAINS> = Extract<
95
+ TRawEntityIds,
96
+ `${DOMAIN}.${string}`
97
+ >;