@digital-alchemy/hass 24.9.3 → 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.
Files changed (276) hide show
  1. package/README.md +3 -0
  2. package/dist/dynamic.d.ts +1 -1
  3. package/dist/dynamic.js +7 -2
  4. package/dist/dynamic.js.map +1 -1
  5. package/dist/extensions/area.extension.d.ts +2 -12
  6. package/dist/extensions/area.extension.js +20 -26
  7. package/dist/extensions/area.extension.js.map +1 -1
  8. package/dist/extensions/backup.extension.d.ts +2 -7
  9. package/dist/extensions/backup.extension.js +5 -8
  10. package/dist/extensions/backup.extension.js.map +1 -1
  11. package/dist/extensions/call-proxy.extension.d.ts +1 -1
  12. package/dist/extensions/call-proxy.extension.js +3 -22
  13. package/dist/extensions/call-proxy.extension.js.map +1 -1
  14. package/dist/extensions/config.extension.d.ts +2 -6
  15. package/dist/extensions/config.extension.js +21 -25
  16. package/dist/extensions/config.extension.js.map +1 -1
  17. package/dist/extensions/conversation.extension.d.ts +2 -6
  18. package/dist/extensions/conversation.extension.js +5 -8
  19. package/dist/extensions/conversation.extension.js.map +1 -1
  20. package/dist/extensions/device.extension.d.ts +2 -5
  21. package/dist/extensions/device.extension.js +16 -22
  22. package/dist/extensions/device.extension.js.map +1 -1
  23. package/dist/extensions/entity.extension.d.ts +2 -61
  24. package/dist/extensions/entity.extension.js +42 -83
  25. package/dist/extensions/entity.extension.js.map +1 -1
  26. package/dist/extensions/events.extension.d.ts +3 -11
  27. package/dist/extensions/events.extension.js +8 -11
  28. package/dist/extensions/events.extension.js.map +1 -1
  29. package/dist/extensions/fetch-api.extension.d.ts +12 -4
  30. package/dist/extensions/fetch-api.extension.js +23 -35
  31. package/dist/extensions/fetch-api.extension.js.map +1 -1
  32. package/dist/extensions/floor.extension.d.ts +2 -9
  33. package/dist/extensions/floor.extension.js +17 -23
  34. package/dist/extensions/floor.extension.js.map +1 -1
  35. package/dist/extensions/id-by.extension.js +15 -20
  36. package/dist/extensions/id-by.extension.js.map +1 -1
  37. package/dist/extensions/index.d.ts +1 -0
  38. package/dist/extensions/index.js +16 -18
  39. package/dist/extensions/index.js.map +1 -1
  40. package/dist/extensions/internal.extension.d.ts +18 -0
  41. package/dist/extensions/internal.extension.js +102 -0
  42. package/dist/extensions/internal.extension.js.map +1 -0
  43. package/dist/extensions/label.extension.d.ts +2 -9
  44. package/dist/extensions/label.extension.js +17 -23
  45. package/dist/extensions/label.extension.js.map +1 -1
  46. package/dist/extensions/reference.extension.d.ts +2 -12
  47. package/dist/extensions/reference.extension.js +19 -25
  48. package/dist/extensions/reference.extension.js.map +1 -1
  49. package/dist/extensions/registry.extension.d.ts +2 -7
  50. package/dist/extensions/registry.extension.js +1 -4
  51. package/dist/extensions/registry.extension.js.map +1 -1
  52. package/dist/extensions/websocket-api.extension.d.ts +3 -78
  53. package/dist/extensions/websocket-api.extension.js +82 -165
  54. package/dist/extensions/websocket-api.extension.js.map +1 -1
  55. package/dist/extensions/zone.extension.d.ts +2 -7
  56. package/dist/extensions/zone.extension.js +15 -21
  57. package/dist/extensions/zone.extension.js.map +1 -1
  58. package/dist/hass.module.d.ts +47 -36
  59. package/dist/hass.module.js +70 -70
  60. package/dist/hass.module.js.map +1 -1
  61. package/dist/helpers/backup.helper.js +1 -2
  62. package/dist/helpers/constants.helper.js +15 -18
  63. package/dist/helpers/constants.helper.js.map +1 -1
  64. package/dist/helpers/device.helper.js +2 -5
  65. package/dist/helpers/device.helper.js.map +1 -1
  66. package/dist/helpers/entity-state.helper.d.ts +3 -8
  67. package/dist/helpers/entity-state.helper.js +1 -8
  68. package/dist/helpers/entity-state.helper.js.map +1 -1
  69. package/dist/helpers/features.helper.js +79 -85
  70. package/dist/helpers/features.helper.js.map +1 -1
  71. package/dist/helpers/fetch/calendar.js +1 -2
  72. package/dist/helpers/fetch/configuration.js +2 -5
  73. package/dist/helpers/fetch/configuration.js.map +1 -1
  74. package/dist/helpers/fetch/index.js +5 -8
  75. package/dist/helpers/fetch/index.js.map +1 -1
  76. package/dist/helpers/fetch/server-log.js +1 -2
  77. package/dist/helpers/fetch/server-log.js.map +1 -1
  78. package/dist/helpers/fetch/service-list.js +1 -2
  79. package/dist/helpers/fetch/weather-forecasts.js +1 -2
  80. package/dist/helpers/fetch/weather-forecasts.js.map +1 -1
  81. package/dist/helpers/fetch.helper.d.ts +162 -0
  82. package/dist/helpers/fetch.helper.js +161 -0
  83. package/dist/helpers/fetch.helper.js.map +1 -0
  84. package/dist/helpers/id-by.helper.js +1 -2
  85. package/dist/helpers/index.d.ts +2 -1
  86. package/dist/helpers/index.js +13 -15
  87. package/dist/helpers/index.js.map +1 -1
  88. package/dist/helpers/interfaces.helper.d.ts +228 -0
  89. package/dist/helpers/interfaces.helper.js +10 -0
  90. package/dist/helpers/interfaces.helper.js.map +1 -0
  91. package/dist/helpers/manifest.helper.d.ts +0 -1
  92. package/dist/helpers/manifest.helper.js +0 -1
  93. package/dist/helpers/notify.helper.d.ts +13 -5
  94. package/dist/helpers/notify.helper.js +1 -2
  95. package/dist/helpers/registry.js +7 -10
  96. package/dist/helpers/registry.js.map +1 -1
  97. package/dist/helpers/utility.helper.d.ts +6 -1
  98. package/dist/helpers/utility.helper.js +9 -13
  99. package/dist/helpers/utility.helper.js.map +1 -1
  100. package/dist/helpers/websocket.helper.d.ts +1 -2
  101. package/dist/helpers/websocket.helper.js +1 -2
  102. package/dist/index.js +5 -8
  103. package/dist/index.js.map +1 -1
  104. package/dist/mock_assistant/extensions/area.extension.d.ts +8 -0
  105. package/dist/mock_assistant/extensions/area.extension.js +51 -0
  106. package/dist/mock_assistant/extensions/area.extension.js.map +1 -0
  107. package/dist/mock_assistant/extensions/config.extension.d.ts +14 -0
  108. package/dist/mock_assistant/extensions/config.extension.js +29 -0
  109. package/dist/mock_assistant/extensions/config.extension.js.map +1 -0
  110. package/dist/mock_assistant/extensions/device.extension.d.ts +8 -0
  111. package/dist/mock_assistant/extensions/device.extension.js +33 -0
  112. package/dist/mock_assistant/extensions/device.extension.js.map +1 -0
  113. package/dist/mock_assistant/extensions/entity-registry.extension.d.ts +13 -0
  114. package/dist/mock_assistant/extensions/entity-registry.extension.js +28 -0
  115. package/dist/mock_assistant/extensions/entity-registry.extension.js.map +1 -0
  116. package/dist/mock_assistant/extensions/entity.extension.d.ts +30 -0
  117. package/dist/mock_assistant/extensions/entity.extension.js +77 -0
  118. package/dist/mock_assistant/extensions/entity.extension.js.map +1 -0
  119. package/dist/mock_assistant/extensions/events.extension.d.ts +1 -1
  120. package/dist/mock_assistant/extensions/events.extension.js +3 -6
  121. package/dist/mock_assistant/extensions/events.extension.js.map +1 -1
  122. package/dist/mock_assistant/extensions/fetch.extension.d.ts +1 -0
  123. package/dist/mock_assistant/extensions/fetch.extension.js +4 -0
  124. package/dist/mock_assistant/extensions/fetch.extension.js.map +1 -0
  125. package/dist/mock_assistant/extensions/fixtures.extension.d.ts +1 -1
  126. package/dist/mock_assistant/extensions/fixtures.extension.js +29 -33
  127. package/dist/mock_assistant/extensions/fixtures.extension.js.map +1 -1
  128. package/dist/mock_assistant/extensions/floor.extension.d.ts +8 -0
  129. package/dist/mock_assistant/extensions/floor.extension.js +51 -0
  130. package/dist/mock_assistant/extensions/floor.extension.js.map +1 -0
  131. package/dist/mock_assistant/extensions/index.d.ts +10 -0
  132. package/dist/mock_assistant/extensions/index.js +12 -5
  133. package/dist/mock_assistant/extensions/index.js.map +1 -1
  134. package/dist/mock_assistant/extensions/label.extension.d.ts +8 -0
  135. package/dist/mock_assistant/extensions/label.extension.js +51 -0
  136. package/dist/mock_assistant/extensions/label.extension.js.map +1 -0
  137. package/dist/mock_assistant/extensions/services.extension.d.ts +12 -0
  138. package/dist/mock_assistant/extensions/services.extension.js +20 -0
  139. package/dist/mock_assistant/extensions/services.extension.js.map +1 -0
  140. package/dist/mock_assistant/extensions/websocket-api.extension.d.ts +15 -0
  141. package/dist/mock_assistant/extensions/websocket-api.extension.js +68 -0
  142. package/dist/mock_assistant/extensions/websocket-api.extension.js.map +1 -0
  143. package/dist/mock_assistant/extensions/zone.extension.d.ts +8 -0
  144. package/dist/mock_assistant/extensions/zone.extension.js +51 -0
  145. package/dist/mock_assistant/extensions/zone.extension.js.map +1 -0
  146. package/dist/mock_assistant/helpers/fixtures.js +1 -2
  147. package/dist/mock_assistant/helpers/index.d.ts +0 -1
  148. package/dist/mock_assistant/helpers/index.js +1 -5
  149. package/dist/mock_assistant/helpers/index.js.map +1 -1
  150. package/dist/mock_assistant/index.js +3 -6
  151. package/dist/mock_assistant/index.js.map +1 -1
  152. package/dist/mock_assistant/main.js +11 -15
  153. package/dist/mock_assistant/main.js.map +1 -1
  154. package/dist/mock_assistant/mock-assistant.module.d.ts +156 -3
  155. package/dist/mock_assistant/mock-assistant.module.js +56 -11
  156. package/dist/mock_assistant/mock-assistant.module.js.map +1 -1
  157. package/dist/quickboot.module.js +5 -8
  158. package/dist/quickboot.module.js.map +1 -1
  159. package/dist/testing/area.spec.js +106 -194
  160. package/dist/testing/area.spec.js.map +1 -1
  161. package/dist/testing/backup.spec.js +97 -139
  162. package/dist/testing/backup.spec.js.map +1 -1
  163. package/dist/testing/config.spec.js +79 -153
  164. package/dist/testing/config.spec.js.map +1 -1
  165. package/dist/testing/device.spec.js +35 -69
  166. package/dist/testing/device.spec.js.map +1 -1
  167. package/dist/testing/entity.spec.js +94 -149
  168. package/dist/testing/entity.spec.js.map +1 -1
  169. package/dist/testing/events.spec.js +33 -57
  170. package/dist/testing/events.spec.js.map +1 -1
  171. package/dist/testing/fetch-api.spec.js +242 -427
  172. package/dist/testing/fetch-api.spec.js.map +1 -1
  173. package/dist/testing/fixtures.spec.d.ts +1 -0
  174. package/dist/testing/fixtures.spec.js +150 -0
  175. package/dist/testing/fixtures.spec.js.map +1 -0
  176. package/dist/testing/floor.spec.js +106 -194
  177. package/dist/testing/floor.spec.js.map +1 -1
  178. package/dist/testing/id-by.spec.js +68 -107
  179. package/dist/testing/id-by.spec.js.map +1 -1
  180. package/dist/testing/label.spec.js +106 -194
  181. package/dist/testing/label.spec.js.map +1 -1
  182. package/dist/testing/ref-by.spec.js +155 -219
  183. package/dist/testing/ref-by.spec.js.map +1 -1
  184. package/dist/testing/websocket.spec.d.ts +1 -8
  185. package/dist/testing/websocket.spec.js +35 -50
  186. package/dist/testing/websocket.spec.js.map +1 -1
  187. package/dist/testing/workflow.spec.js +82 -81
  188. package/dist/testing/workflow.spec.js.map +1 -1
  189. package/dist/testing/zone.spec.js +61 -113
  190. package/dist/testing/zone.spec.js.map +1 -1
  191. package/package.json +62 -44
  192. package/scripts/mock-assistant.sh +5 -0
  193. package/scripts/run-e2e.sh +7 -0
  194. package/scripts/test.sh +2 -0
  195. package/src/dynamic.ts +4254 -0
  196. package/src/extensions/area.extension.ts +118 -0
  197. package/src/extensions/backup.extension.ts +63 -0
  198. package/src/extensions/call-proxy.extension.ts +113 -0
  199. package/src/extensions/config.extension.ts +119 -0
  200. package/src/extensions/conversation.extension.ts +46 -0
  201. package/src/extensions/device.extension.ts +56 -0
  202. package/src/extensions/entity.extension.ts +344 -0
  203. package/src/extensions/events.extension.ts +25 -0
  204. package/src/extensions/fetch-api.extension.ts +269 -0
  205. package/src/extensions/floor.extension.ts +76 -0
  206. package/src/extensions/id-by.extension.ts +157 -0
  207. package/src/extensions/index.ts +16 -0
  208. package/src/extensions/internal.extension.ts +145 -0
  209. package/src/extensions/label.extension.ts +83 -0
  210. package/src/extensions/reference.extension.ts +330 -0
  211. package/src/extensions/registry.extension.ts +44 -0
  212. package/src/extensions/websocket-api.extension.ts +554 -0
  213. package/src/extensions/zone.extension.ts +69 -0
  214. package/src/hass.module.ts +217 -0
  215. package/src/helpers/backup.helper.ts +11 -0
  216. package/src/helpers/constants.helper.ts +30 -0
  217. package/src/helpers/device.helper.ts +25 -0
  218. package/src/helpers/entity-state.helper.ts +171 -0
  219. package/src/helpers/features.helper.ts +580 -0
  220. package/src/helpers/fetch/calendar.ts +54 -0
  221. package/src/helpers/fetch/configuration.ts +75 -0
  222. package/src/helpers/fetch/index.ts +5 -0
  223. package/src/helpers/fetch/server-log.ts +28 -0
  224. package/src/helpers/fetch/service-list.ts +64 -0
  225. package/src/helpers/fetch/weather-forecasts.ts +86 -0
  226. package/src/helpers/fetch.helper.ts +328 -0
  227. package/src/helpers/id-by.helper.ts +53 -0
  228. package/src/helpers/index.ts +13 -0
  229. package/src/helpers/interfaces.helper.ts +340 -0
  230. package/src/helpers/manifest.helper.ts +0 -0
  231. package/src/helpers/notify.helper.ts +302 -0
  232. package/src/helpers/registry.ts +281 -0
  233. package/src/helpers/utility.helper.ts +147 -0
  234. package/src/helpers/websocket.helper.ts +117 -0
  235. package/src/index.ts +5 -0
  236. package/src/mock_assistant/extensions/area.extension.ts +62 -0
  237. package/src/mock_assistant/extensions/config.extension.ts +33 -0
  238. package/src/mock_assistant/extensions/device.extension.ts +44 -0
  239. package/src/mock_assistant/extensions/entity-registry.extension.ts +41 -0
  240. package/src/mock_assistant/extensions/entity.extension.ts +114 -0
  241. package/src/mock_assistant/extensions/events.extension.ts +37 -0
  242. package/src/mock_assistant/extensions/fetch.extension.ts +3 -0
  243. package/src/mock_assistant/extensions/fixtures.extension.ts +79 -0
  244. package/src/mock_assistant/extensions/floor.extension.ts +64 -0
  245. package/src/mock_assistant/extensions/index.ts +12 -0
  246. package/src/mock_assistant/extensions/label.extension.ts +64 -0
  247. package/src/mock_assistant/extensions/services.extension.ts +25 -0
  248. package/src/mock_assistant/extensions/websocket-api.extension.ts +84 -0
  249. package/src/mock_assistant/extensions/zone.extension.ts +65 -0
  250. package/src/mock_assistant/helpers/fixtures.ts +22 -0
  251. package/src/mock_assistant/helpers/index.ts +1 -0
  252. package/src/mock_assistant/index.ts +3 -0
  253. package/src/mock_assistant/main.ts +46 -0
  254. package/src/mock_assistant/mock-assistant.module.ts +90 -0
  255. package/src/quickboot.module.ts +23 -0
  256. package/src/testing/area.spec.ts +189 -0
  257. package/src/testing/backup.spec.ts +157 -0
  258. package/src/testing/config.spec.ts +188 -0
  259. package/src/testing/device.spec.ts +89 -0
  260. package/src/testing/entity.spec.ts +171 -0
  261. package/src/testing/events.spec.ts +78 -0
  262. package/src/testing/fetch-api.spec.ts +410 -0
  263. package/src/testing/fixtures.spec.ts +158 -0
  264. package/src/testing/floor.spec.ts +186 -0
  265. package/src/testing/id-by.spec.ts +140 -0
  266. package/src/testing/label.spec.ts +186 -0
  267. package/src/testing/ref-by.spec.ts +300 -0
  268. package/src/testing/websocket.spec.ts +63 -0
  269. package/src/testing/workflow.spec.ts +195 -0
  270. package/src/testing/zone.spec.ts +109 -0
  271. package/dist/helpers/metrics.helper.d.ts +0 -29
  272. package/dist/helpers/metrics.helper.js +0 -62
  273. package/dist/helpers/metrics.helper.js.map +0 -1
  274. package/dist/mock_assistant/helpers/utils.d.ts +0 -4
  275. package/dist/mock_assistant/helpers/utils.js +0 -57
  276. package/dist/mock_assistant/helpers/utils.js.map +0 -1
@@ -0,0 +1,330 @@
1
+ import { DOWN, is, NONE, sleep, TAnyFunction, TServiceParams, UP } from "@digital-alchemy/core";
2
+ import dayjs, { Dayjs } from "dayjs";
3
+ import { Get } from "type-fest";
4
+
5
+ import { SERVICE_LIST_UPDATED } from "..";
6
+ import {
7
+ TAreaId,
8
+ TDeviceId,
9
+ TFloorId,
10
+ TLabelId,
11
+ TPlatformId,
12
+ TRawDomains,
13
+ TUniqueId,
14
+ TUniqueIDMapping,
15
+ } from "../dynamic";
16
+ import {
17
+ ALL_SERVICE_DOMAINS,
18
+ ANY_ENTITY,
19
+ ByIdProxy,
20
+ domain,
21
+ ENTITY_STATE,
22
+ HassReferenceService,
23
+ PICK_ENTITY,
24
+ PICK_FROM_AREA,
25
+ PICK_FROM_DEVICE,
26
+ PICK_FROM_FLOOR,
27
+ PICK_FROM_LABEL,
28
+ PICK_FROM_PLATFORM,
29
+ } from "../helpers";
30
+
31
+ export function ReferenceExtension({
32
+ hass,
33
+ logger,
34
+ internal,
35
+ event,
36
+ }: TServiceParams): HassReferenceService {
37
+ const ENTITY_PROXIES = new Map<ANY_ENTITY, ByIdProxy<ANY_ENTITY>>();
38
+ // #MARK:proxyGetLogic
39
+ function proxyGetLogic<ENTITY extends ANY_ENTITY = ANY_ENTITY, PROPERTY extends string = string>(
40
+ entity: ENTITY,
41
+ property: PROPERTY,
42
+ ): Get<ENTITY_STATE<ENTITY>, PROPERTY> {
43
+ const valid = ["state", "attributes", "last"].some(i => property.startsWith(i));
44
+ if (!valid) {
45
+ logger.error({ entity, name: proxyGetLogic, property }, `invalid property lookup`);
46
+ return undefined;
47
+ }
48
+ const current = hass.entity.getCurrentState(entity);
49
+ if (!current) {
50
+ logger.error({ name: entity, property }, `proxyGetLogic cannot find entity`);
51
+ }
52
+ if (property.startsWith("last")) {
53
+ const value = internal.utils.object.get(current, property) as string;
54
+ return dayjs(value) as Get<ENTITY_STATE<ENTITY>, PROPERTY>;
55
+ }
56
+ if (property === "state") {
57
+ if (domain(entity) === "sensor" && is.number(Number(current.state))) {
58
+ return Number(current.state) as Get<ENTITY_STATE<ENTITY>, PROPERTY>;
59
+ }
60
+ return current.state as Get<ENTITY_STATE<ENTITY>, PROPERTY>;
61
+ }
62
+
63
+ return (current.attributes || {}) as Get<ENTITY_STATE<ENTITY>, PROPERTY>;
64
+ }
65
+
66
+ // #MARK: byId
67
+ function byId<ENTITY_ID extends ANY_ENTITY>(entity_id: ENTITY_ID): ByIdProxy<ENTITY_ID> {
68
+ const entity_domain = domain(entity_id) as ALL_SERVICE_DOMAINS;
69
+ if (!ENTITY_PROXIES.has(entity_id)) {
70
+ const { ...thing } = hass.entity.getCurrentState(entity_id) as ByIdProxy<ENTITY_ID>;
71
+ let loaded = false;
72
+
73
+ function keys() {
74
+ const entityDomain = domain(entity_id);
75
+ return [
76
+ "attributes",
77
+ "entity_id",
78
+ "history",
79
+ "last",
80
+ "nextState",
81
+ "once",
82
+ "onUpdate",
83
+ "previous",
84
+ "removeAllListeners",
85
+ "state",
86
+ "waitForState",
87
+ ...hass.configure
88
+ .getServices()
89
+ .filter(({ domain }) => domain === entityDomain)
90
+ .flatMap(i => Object.keys(i.services))
91
+ .sort((a, b) => (a > b ? UP : DOWN)),
92
+ ];
93
+ }
94
+ function appendKeys(force = false) {
95
+ if (loaded && !force) {
96
+ return;
97
+ }
98
+ // Not gonna build types for this, and ts-expect-error fails in jest
99
+ // This is a weird hack for an obscure feature, so sue me
100
+ //
101
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
102
+ // @ts-ignore
103
+ keys().forEach(i => (thing[i] ??= () => {}));
104
+ if (!is.empty(hass.configure.getServices())) {
105
+ loaded = true;
106
+ }
107
+ }
108
+ event.on(SERVICE_LIST_UPDATED, () => appendKeys(true));
109
+ ENTITY_PROXIES.set(
110
+ entity_id,
111
+ // just because you can't do generics properly....
112
+ new Proxy(thing, {
113
+ // things that shouldn't be needed: this extract
114
+ // eslint-disable-next-line sonarjs/function-return-type
115
+ get: (_, property: Extract<keyof ByIdProxy<ENTITY_ID>, string>) => {
116
+ switch (property) {
117
+ // * onUpdate
118
+ case "onUpdate": {
119
+ return (callback: TAnyFunction) => {
120
+ const removableCallback = async (
121
+ a: ENTITY_STATE<ENTITY_ID>,
122
+ b: ENTITY_STATE<ENTITY_ID>,
123
+ ) => await internal.safeExec(async () => callback(a, b, remove));
124
+ function remove() {
125
+ event.removeListener(entity_id, removableCallback);
126
+ }
127
+
128
+ event.on(entity_id, removableCallback);
129
+ return { remove };
130
+ };
131
+ }
132
+
133
+ // * removeAllListeners
134
+ case "removeAllListeners": {
135
+ return function () {
136
+ event.removeAllListeners(entity_id);
137
+ };
138
+ }
139
+
140
+ // * history
141
+ case "history": {
142
+ return async function (from: Dayjs | Date, to: Dayjs | Date) {
143
+ return await hass.fetch.fetchEntityHistory(entity_id, from, to);
144
+ };
145
+ }
146
+
147
+ // * once
148
+ case "once": {
149
+ return (callback: TAnyFunction) =>
150
+ event.once(entity_id, async (a, b) => callback(a, b));
151
+ }
152
+
153
+ // * entity_id
154
+ case "entity_id": {
155
+ return entity_id;
156
+ }
157
+
158
+ // * previous
159
+ case "previous": {
160
+ return hass.entity.previousState(entity_id);
161
+ }
162
+
163
+ // * nextState
164
+ case "nextState": {
165
+ return async (timeout?: number) =>
166
+ await new Promise<ENTITY_STATE<ENTITY_ID>>(async done => {
167
+ const complete = (entity: ENTITY_STATE<ENTITY_ID>) => {
168
+ if (done) {
169
+ done(entity satisfies ENTITY_STATE<ENTITY_ID>);
170
+ done = undefined;
171
+ }
172
+ };
173
+ event.once(entity_id, complete);
174
+ if (is.number(timeout) && timeout > NONE) {
175
+ await sleep(timeout);
176
+ if (done) {
177
+ logger.debug({ entity_id, name: "nextState", timeout }, "timed out");
178
+ done(undefined);
179
+ done = undefined;
180
+ event.removeListener(entity_id, complete);
181
+ }
182
+ }
183
+ });
184
+ }
185
+
186
+ // * waitForState
187
+ case "waitForState": {
188
+ return async (state: string | number, timeout?: number) =>
189
+ await new Promise<ENTITY_STATE<ENTITY_ID>>(async done => {
190
+ const complete = (entity: ENTITY_STATE<ENTITY_ID>) => {
191
+ if (entity.state !== state) {
192
+ logger.trace(
193
+ {
194
+ expected: state,
195
+ incoming: entity.state,
196
+ name: "waitForState",
197
+ },
198
+ `state did not match`,
199
+ );
200
+ return;
201
+ }
202
+ if (done) {
203
+ done(entity satisfies ENTITY_STATE<ENTITY_ID>);
204
+ done = undefined;
205
+ event.removeListener(entity_id, complete);
206
+ }
207
+ };
208
+ event.on(entity_id, complete);
209
+ if (is.number(timeout) && timeout > NONE) {
210
+ await sleep(timeout);
211
+ if (done) {
212
+ logger.debug({ entity_id, name: "waitForState", timeout }, "timed out");
213
+ done(undefined);
214
+ done = undefined;
215
+ event.removeListener(entity_id, complete);
216
+ }
217
+ }
218
+ });
219
+ }
220
+ }
221
+ if (hass.configure.isService(entity_domain, property)) {
222
+ return async function (data = {}) {
223
+ // @ts-expect-error it's fine
224
+ return await hass.call[entity_domain][property]({
225
+ entity_id,
226
+ ...data,
227
+ });
228
+ };
229
+ }
230
+ return proxyGetLogic(entity_id, property);
231
+ },
232
+ has(_, property: string) {
233
+ appendKeys();
234
+ return property in thing;
235
+ },
236
+ ownKeys() {
237
+ appendKeys();
238
+ return Object.keys(thing);
239
+ },
240
+ set(_, property: Extract<keyof ByIdProxy<ENTITY_ID>, string>, value: unknown) {
241
+ // * state
242
+ if (property === "state") {
243
+ setImmediate(async () => {
244
+ logger.debug({ entity_id, state: value }, `emitting set state via rest`);
245
+ await hass.fetch.updateEntity(entity_id, {
246
+ state: value as string | number,
247
+ });
248
+ });
249
+ return true;
250
+ }
251
+ // * attributes
252
+ if (property === "attributes") {
253
+ if (!is.object(value)) {
254
+ logger.error(`can only provide objects as attributes`);
255
+ return false;
256
+ }
257
+ setImmediate(async () => {
258
+ logger.debug(
259
+ { attributes: Object.keys(value), entity_id },
260
+ `updating attributes via rest`,
261
+ );
262
+ await hass.fetch.updateEntity(entity_id, {
263
+ attributes: value,
264
+ });
265
+ });
266
+ return true;
267
+ }
268
+ logger.error({ entity_id, property }, `cannot set property on entity`);
269
+ return false;
270
+ },
271
+ }),
272
+ );
273
+ }
274
+ return ENTITY_PROXIES.get(entity_id) as ByIdProxy<ENTITY_ID>;
275
+ }
276
+
277
+ return {
278
+ area: <AREA extends TAreaId, DOMAINS extends TRawDomains = TRawDomains>(
279
+ area: AREA,
280
+ ...domains: DOMAINS[]
281
+ ): ByIdProxy<PICK_FROM_AREA<AREA, DOMAINS>>[] =>
282
+ hass.idBy.area<AREA, DOMAINS>(area, ...domains).map(id => byId(id)),
283
+
284
+ device: <DEVICE extends TDeviceId, DOMAINS extends TRawDomains = TRawDomains>(
285
+ device: DEVICE,
286
+ ...domains: DOMAINS[]
287
+ ): ByIdProxy<PICK_FROM_DEVICE<DEVICE, DOMAINS>>[] =>
288
+ hass.idBy.device<DEVICE, DOMAINS>(device, ...domains).map(id => byId(id)),
289
+
290
+ domain: <DOMAIN extends TRawDomains = TRawDomains>(
291
+ domain: DOMAIN,
292
+ ): ByIdProxy<PICK_ENTITY<DOMAIN>>[] => hass.idBy.domain<DOMAIN>(domain).map(id => byId(id)),
293
+
294
+ floor: <FLOOR extends TFloorId, DOMAINS extends TRawDomains = TRawDomains>(
295
+ floor: FLOOR,
296
+ ...domains: DOMAINS[]
297
+ ): ByIdProxy<PICK_FROM_FLOOR<FLOOR, DOMAINS>>[] =>
298
+ hass.idBy.floor<FLOOR, DOMAINS>(floor, ...domains).map(id => byId(id)),
299
+
300
+ id: byId,
301
+
302
+ label: <LABEL extends TLabelId, DOMAINS extends TRawDomains = TRawDomains>(
303
+ label: LABEL,
304
+ ...domains: DOMAINS[]
305
+ ): ByIdProxy<PICK_FROM_LABEL<LABEL, DOMAINS>>[] =>
306
+ hass.idBy.label<LABEL, DOMAINS>(label, ...domains).map(id => byId(id)),
307
+
308
+ platform: <PLATFORM extends TPlatformId, DOMAINS extends TRawDomains = TRawDomains>(
309
+ platform: PLATFORM,
310
+ ...domains: DOMAINS[]
311
+ ): ByIdProxy<PICK_FROM_PLATFORM<PLATFORM, DOMAINS>>[] =>
312
+ hass.idBy.platform<PLATFORM, DOMAINS>(platform, ...domains).map(id => byId(id)),
313
+
314
+ unique_id: <
315
+ UNIQUE_ID extends TUniqueId,
316
+ ENTITY_ID extends Extract<TUniqueIDMapping[UNIQUE_ID], ANY_ENTITY> = Extract<
317
+ TUniqueIDMapping[UNIQUE_ID],
318
+ ANY_ENTITY
319
+ >,
320
+ >(
321
+ unique_id: UNIQUE_ID,
322
+ ): ByIdProxy<ENTITY_ID> => {
323
+ const id = hass.idBy.unique_id<UNIQUE_ID, ENTITY_ID>(unique_id);
324
+ if (!id) {
325
+ return undefined;
326
+ }
327
+ return byId(id);
328
+ },
329
+ };
330
+ }
@@ -0,0 +1,44 @@
1
+ import { TServiceParams } from "@digital-alchemy/core";
2
+
3
+ import {
4
+ ConfigEntry,
5
+ HassConfig,
6
+ HassRegistryService,
7
+ ManifestItem,
8
+ UpdateCoreOptions,
9
+ ZoneDetails,
10
+ } from "../helpers";
11
+
12
+ export function Registry({ hass }: TServiceParams): HassRegistryService {
13
+ async function ManifestList() {
14
+ return await hass.socket.sendMessage<ManifestItem[]>({
15
+ type: "manifest/list",
16
+ });
17
+ }
18
+
19
+ async function UpdateCore(options: UpdateCoreOptions) {
20
+ await hass.socket.sendMessage<ZoneDetails[]>({
21
+ ...options,
22
+ type: "config/core/update",
23
+ });
24
+ }
25
+
26
+ async function GetConfig() {
27
+ return await hass.socket.sendMessage<HassConfig>({
28
+ type: "get_config",
29
+ });
30
+ }
31
+
32
+ async function GetConfigEntries() {
33
+ return await hass.socket.sendMessage<ConfigEntry[]>({
34
+ type: "config_entries/get",
35
+ });
36
+ }
37
+
38
+ return {
39
+ getConfig: GetConfig,
40
+ getConfigEntries: GetConfigEntries,
41
+ manifestList: ManifestList,
42
+ updateCore: UpdateCore,
43
+ };
44
+ }