@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,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
|
+
}
|