@iobroker/dm-utils 2.0.1 → 3.0.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/README.md +204 -124
- package/build/ActionContext.d.ts +2 -6
- package/build/DeviceManagement.d.ts +25 -29
- package/build/DeviceManagement.js +190 -106
- package/build/ProgressDialog.d.ts +2 -6
- package/build/index.d.ts +1 -1
- package/build/index.js +0 -2
- package/build/types/adapter.d.ts +4 -3
- package/build/types/api.d.ts +65 -0
- package/build/types/base.d.ts +49 -34
- package/build/types/base.js +2 -2
- package/build/types/common.d.ts +108 -106
- package/build/types/index.d.ts +1 -1
- package/build/types/index.js +1 -1
- package/package.json +10 -6
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
import type { AdapterInstance } from '@iobroker/adapter-core';
|
|
2
2
|
import type { ActionContext } from './ActionContext';
|
|
3
3
|
import type { ProgressDialog } from './ProgressDialog';
|
|
4
|
-
import { type DeviceDetails, type
|
|
5
|
-
import type { BackendToGuiCommand,
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { type ActionButton, type DeviceDetails, type DeviceId, type DeviceInfo, type DeviceStatus, type ErrorResponse, type InstanceDetails, type JsonFormData, type JsonFormSchema, type RetVal } from './types';
|
|
5
|
+
import type { BackendToGuiCommand, DeviceRefreshResponse, InstanceRefreshResponse } from './types/base';
|
|
6
|
+
import type { ProgressOptions } from './types/common';
|
|
7
|
+
export type DeviceLoadContext<TId extends DeviceId> = {
|
|
8
|
+
addDevice(device: DeviceInfo<TId>): void;
|
|
9
|
+
setTotalDevices(count: number): void;
|
|
10
|
+
};
|
|
11
|
+
export declare abstract class DeviceManagement<TAdapter extends AdapterInstance = AdapterInstance, TId extends DeviceId = string> {
|
|
12
|
+
protected readonly adapter: TAdapter;
|
|
8
13
|
private instanceInfo?;
|
|
9
14
|
private devices?;
|
|
10
15
|
private readonly communicationStateId;
|
|
11
|
-
private readonly
|
|
12
|
-
|
|
16
|
+
private readonly deviceLoadContexts;
|
|
17
|
+
private readonly messageContexts;
|
|
18
|
+
constructor(adapter: TAdapter, communicationStateId?: string | boolean);
|
|
13
19
|
private ensureCommunicationState;
|
|
14
|
-
protected sendCommandToGui(command: BackendToGuiCommand): Promise<void>;
|
|
20
|
+
protected sendCommandToGui(command: BackendToGuiCommand<TId>): Promise<void>;
|
|
15
21
|
protected get log(): ioBroker.Log;
|
|
16
22
|
protected getInstanceInfo(): RetVal<InstanceDetails>;
|
|
17
|
-
protected abstract
|
|
18
|
-
protected
|
|
23
|
+
protected abstract loadDevices(context: DeviceLoadContext<TId>): RetVal<void>;
|
|
24
|
+
protected getDeviceInfo(_deviceId: TId): RetVal<DeviceInfo<TId>>;
|
|
25
|
+
protected getDeviceStatus(_deviceId: TId): RetVal<DeviceStatus | DeviceStatus[]>;
|
|
26
|
+
protected getDeviceDetails(id: TId): RetVal<DeviceDetails<TId> | null | {
|
|
19
27
|
error: string;
|
|
20
28
|
}>;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
protected handleDeviceAction(deviceId: string, actionId: string, context?: ActionContext, options?: {
|
|
26
|
-
value?: number | string | boolean;
|
|
27
|
-
[key: string]: any;
|
|
28
|
-
}): RetVal<ErrorResponse> | RetVal<RefreshResponse>;
|
|
29
|
-
protected handleDeviceControl(deviceId: string, controlId: string, newState: ControlState, context?: MessageContext): RetVal<ErrorResponse | ioBroker.State>;
|
|
30
|
-
protected handleDeviceControlState(deviceId: string, controlId: string, context?: MessageContext): RetVal<ErrorResponse | ioBroker.State>;
|
|
29
|
+
private handleInstanceAction;
|
|
30
|
+
private handleDeviceAction;
|
|
31
|
+
private handleDeviceControl;
|
|
32
|
+
private handleDeviceControlState;
|
|
31
33
|
private onMessage;
|
|
32
34
|
private handleMessage;
|
|
33
|
-
private convertActions;
|
|
34
|
-
private convertControls;
|
|
35
35
|
private sendReply;
|
|
36
36
|
}
|
|
37
|
-
export declare class MessageContext implements ActionContext {
|
|
37
|
+
export declare class MessageContext<TId extends DeviceId> implements ActionContext {
|
|
38
38
|
private readonly adapter;
|
|
39
39
|
private hasOpenProgressDialog;
|
|
40
40
|
private lastMessage?;
|
|
@@ -47,13 +47,9 @@ export declare class MessageContext implements ActionContext {
|
|
|
47
47
|
title?: ioBroker.StringOrTranslated;
|
|
48
48
|
buttons?: (ActionButton | 'apply' | 'cancel' | 'close')[];
|
|
49
49
|
}): Promise<JsonFormData | undefined>;
|
|
50
|
-
openProgress(title:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
label?: string;
|
|
54
|
-
}): Promise<ProgressDialog>;
|
|
55
|
-
sendFinalResult(result: ErrorResponse | RefreshResponse): void;
|
|
56
|
-
sendControlResult(deviceId: string, controlId: string, result: ErrorResponse | ioBroker.State): void;
|
|
50
|
+
openProgress(title: ioBroker.StringOrTranslated, options?: ProgressOptions): Promise<ProgressDialog>;
|
|
51
|
+
sendFinalResult(result: ErrorResponse | DeviceRefreshResponse<'api', TId> | InstanceRefreshResponse): void;
|
|
52
|
+
sendControlResult(deviceId: TId, controlId: string, result: ErrorResponse | ioBroker.State): void;
|
|
57
53
|
handleProgress(message: ioBroker.Message): void;
|
|
58
54
|
private checkPreconditions;
|
|
59
55
|
private send;
|
|
@@ -5,7 +5,8 @@ const types_1 = require("./types");
|
|
|
5
5
|
class DeviceManagement {
|
|
6
6
|
constructor(adapter, communicationStateId) {
|
|
7
7
|
this.adapter = adapter;
|
|
8
|
-
this.
|
|
8
|
+
this.deviceLoadContexts = new Map();
|
|
9
|
+
this.messageContexts = new Map();
|
|
9
10
|
adapter.on('message', this.onMessage.bind(this));
|
|
10
11
|
if (communicationStateId === true) {
|
|
11
12
|
// use standard ID `info.deviceManager`
|
|
@@ -40,7 +41,7 @@ class DeviceManagement {
|
|
|
40
41
|
}
|
|
41
42
|
async sendCommandToGui(command) {
|
|
42
43
|
if (this.communicationStateId) {
|
|
43
|
-
await this.adapter.
|
|
44
|
+
await this.adapter.setState(this.communicationStateId, JSON.stringify(command), true);
|
|
44
45
|
}
|
|
45
46
|
else {
|
|
46
47
|
throw new Error('Communication state not found');
|
|
@@ -51,7 +52,13 @@ class DeviceManagement {
|
|
|
51
52
|
}
|
|
52
53
|
getInstanceInfo() {
|
|
53
54
|
// Overload this method if your adapter does not use BackendToGui communication and States/Objects in DeviceInfo
|
|
54
|
-
return { apiVersion: '
|
|
55
|
+
return { apiVersion: 'v3', communicationStateId: this.communicationStateId || undefined };
|
|
56
|
+
}
|
|
57
|
+
getDeviceInfo(_deviceId) {
|
|
58
|
+
throw new Error('Do not send "infoUpdate" or "delete" command without implementing getDeviceInfo method!');
|
|
59
|
+
}
|
|
60
|
+
getDeviceStatus(_deviceId) {
|
|
61
|
+
throw new Error('Do not send "statusUpdate" command without implementing getDeviceStatus method!');
|
|
55
62
|
}
|
|
56
63
|
getDeviceDetails(id) {
|
|
57
64
|
return { id, schema: {} };
|
|
@@ -77,7 +84,7 @@ class DeviceManagement {
|
|
|
77
84
|
},
|
|
78
85
|
};
|
|
79
86
|
}
|
|
80
|
-
if (!action.handler) {
|
|
87
|
+
if (!('handler' in action) || !action.handler) {
|
|
81
88
|
this.log.warn(`Instance action ${actionId} is disabled because it has no handler`);
|
|
82
89
|
return {
|
|
83
90
|
error: {
|
|
@@ -91,40 +98,41 @@ class DeviceManagement {
|
|
|
91
98
|
handleDeviceAction(deviceId, actionId, context, options) {
|
|
92
99
|
var _a;
|
|
93
100
|
if (!this.devices) {
|
|
94
|
-
this.log.warn(`Device action ${actionId} was called before
|
|
101
|
+
this.log.warn(`Device action ${actionId} was called before loadDevices()`);
|
|
95
102
|
return {
|
|
96
103
|
error: {
|
|
97
104
|
code: types_1.ErrorCodes.E_DEVICE_ACTION_NOT_INITIALIZED,
|
|
98
|
-
message: `Device action ${actionId} was called before
|
|
105
|
+
message: `Device action ${actionId} was called before loadDevices()`,
|
|
99
106
|
},
|
|
100
107
|
};
|
|
101
108
|
}
|
|
102
|
-
const
|
|
109
|
+
const jsonId = JSON.stringify(deviceId);
|
|
110
|
+
const device = this.devices.get(jsonId);
|
|
103
111
|
if (!device) {
|
|
104
|
-
this.log.warn(`Device action ${actionId} was called on unknown device: ${
|
|
112
|
+
this.log.warn(`Device action ${actionId} was called on unknown device: ${jsonId}`);
|
|
105
113
|
return {
|
|
106
114
|
error: {
|
|
107
115
|
code: types_1.ErrorCodes.E_DEVICE_ACTION_DEVICE_UNKNOWN,
|
|
108
|
-
message: `Device action ${actionId} was called on unknown device: ${
|
|
116
|
+
message: `Device action ${actionId} was called on unknown device: ${jsonId}`,
|
|
109
117
|
},
|
|
110
118
|
};
|
|
111
119
|
}
|
|
112
120
|
const action = (_a = device.actions) === null || _a === void 0 ? void 0 : _a.find(a => a.id === actionId);
|
|
113
121
|
if (!action) {
|
|
114
|
-
this.log.warn(`Device action ${actionId} doesn't exist on device ${
|
|
122
|
+
this.log.warn(`Device action ${actionId} doesn't exist on device ${jsonId}`);
|
|
115
123
|
return {
|
|
116
124
|
error: {
|
|
117
125
|
code: types_1.ErrorCodes.E_DEVICE_ACTION_UNKNOWN,
|
|
118
|
-
message: `Device action ${actionId} doesn't exist on device ${
|
|
126
|
+
message: `Device action ${actionId} doesn't exist on device ${jsonId}`,
|
|
119
127
|
},
|
|
120
128
|
};
|
|
121
129
|
}
|
|
122
|
-
if (!action.handler) {
|
|
123
|
-
this.log.warn(`Device action ${actionId} on ${
|
|
130
|
+
if (!('handler' in action) || !action.handler) {
|
|
131
|
+
this.log.warn(`Device action ${actionId} on ${jsonId} is disabled because it has no handler`);
|
|
124
132
|
return {
|
|
125
133
|
error: {
|
|
126
134
|
code: types_1.ErrorCodes.E_DEVICE_ACTION_NO_HANDLER,
|
|
127
|
-
message: `Device action ${actionId} on ${
|
|
135
|
+
message: `Device action ${actionId} on ${jsonId} is disabled because it has no handler`,
|
|
128
136
|
},
|
|
129
137
|
};
|
|
130
138
|
}
|
|
@@ -133,40 +141,41 @@ class DeviceManagement {
|
|
|
133
141
|
handleDeviceControl(deviceId, controlId, newState, context) {
|
|
134
142
|
var _a;
|
|
135
143
|
if (!this.devices) {
|
|
136
|
-
this.log.warn(`Device control ${controlId} was called before
|
|
144
|
+
this.log.warn(`Device control ${controlId} was called before loadDevices()`);
|
|
137
145
|
return {
|
|
138
146
|
error: {
|
|
139
147
|
code: types_1.ErrorCodes.E_DEVICE_CONTROL_NOT_INITIALIZED,
|
|
140
|
-
message: `Device control ${controlId} was called before
|
|
148
|
+
message: `Device control ${controlId} was called before loadDevices()`,
|
|
141
149
|
},
|
|
142
150
|
};
|
|
143
151
|
}
|
|
144
|
-
const
|
|
152
|
+
const jsonId = JSON.stringify(deviceId);
|
|
153
|
+
const device = this.devices.get(jsonId);
|
|
145
154
|
if (!device) {
|
|
146
|
-
this.log.warn(`Device control ${controlId} was called on unknown device: ${
|
|
155
|
+
this.log.warn(`Device control ${controlId} was called on unknown device: ${jsonId}`);
|
|
147
156
|
return {
|
|
148
157
|
error: {
|
|
149
158
|
code: types_1.ErrorCodes.E_DEVICE_CONTROL_DEVICE_UNKNOWN,
|
|
150
|
-
message: `Device control ${controlId} was called on unknown device: ${
|
|
159
|
+
message: `Device control ${controlId} was called on unknown device: ${jsonId}`,
|
|
151
160
|
},
|
|
152
161
|
};
|
|
153
162
|
}
|
|
154
163
|
const control = (_a = device.controls) === null || _a === void 0 ? void 0 : _a.find(a => a.id === controlId);
|
|
155
164
|
if (!control) {
|
|
156
|
-
this.log.warn(`Device control ${controlId} doesn't exist on device ${
|
|
165
|
+
this.log.warn(`Device control ${controlId} doesn't exist on device ${jsonId}`);
|
|
157
166
|
return {
|
|
158
167
|
error: {
|
|
159
168
|
code: types_1.ErrorCodes.E_DEVICE_CONTROL_UNKNOWN,
|
|
160
|
-
message: `Device control ${controlId} doesn't exist on device ${
|
|
169
|
+
message: `Device control ${controlId} doesn't exist on device ${jsonId}`,
|
|
161
170
|
},
|
|
162
171
|
};
|
|
163
172
|
}
|
|
164
173
|
if (!control.handler) {
|
|
165
|
-
this.log.warn(`Device control ${controlId} on ${
|
|
174
|
+
this.log.warn(`Device control ${controlId} on ${jsonId} is disabled because it has no handler`);
|
|
166
175
|
return {
|
|
167
176
|
error: {
|
|
168
177
|
code: types_1.ErrorCodes.E_DEVICE_CONTROL_NO_HANDLER,
|
|
169
|
-
message: `Device control ${controlId} on ${
|
|
178
|
+
message: `Device control ${controlId} on ${jsonId} is disabled because it has no handler`,
|
|
170
179
|
},
|
|
171
180
|
};
|
|
172
181
|
}
|
|
@@ -176,40 +185,41 @@ class DeviceManagement {
|
|
|
176
185
|
handleDeviceControlState(deviceId, controlId, context) {
|
|
177
186
|
var _a;
|
|
178
187
|
if (!this.devices) {
|
|
179
|
-
this.log.warn(`Device get state ${controlId} was called before
|
|
188
|
+
this.log.warn(`Device get state ${controlId} was called before loadDevices()`);
|
|
180
189
|
return {
|
|
181
190
|
error: {
|
|
182
191
|
code: types_1.ErrorCodes.E_DEVICE_GET_STATE_NOT_INITIALIZED,
|
|
183
|
-
message: `Device control ${controlId} was called before
|
|
192
|
+
message: `Device control ${controlId} was called before loadDevices()`,
|
|
184
193
|
},
|
|
185
194
|
};
|
|
186
195
|
}
|
|
187
|
-
const
|
|
196
|
+
const jsonId = JSON.stringify(deviceId);
|
|
197
|
+
const device = this.devices.get(jsonId);
|
|
188
198
|
if (!device) {
|
|
189
|
-
this.log.warn(`Device get state ${controlId} was called on unknown device: ${
|
|
199
|
+
this.log.warn(`Device get state ${controlId} was called on unknown device: ${jsonId}`);
|
|
190
200
|
return {
|
|
191
201
|
error: {
|
|
192
202
|
code: types_1.ErrorCodes.E_DEVICE_GET_STATE_DEVICE_UNKNOWN,
|
|
193
|
-
message: `Device control ${controlId} was called on unknown device: ${
|
|
203
|
+
message: `Device control ${controlId} was called on unknown device: ${jsonId}`,
|
|
194
204
|
},
|
|
195
205
|
};
|
|
196
206
|
}
|
|
197
207
|
const control = (_a = device.controls) === null || _a === void 0 ? void 0 : _a.find(a => a.id === controlId);
|
|
198
208
|
if (!control) {
|
|
199
|
-
this.log.warn(`Device get state ${controlId} doesn't exist on device ${
|
|
209
|
+
this.log.warn(`Device get state ${controlId} doesn't exist on device ${jsonId}`);
|
|
200
210
|
return {
|
|
201
211
|
error: {
|
|
202
212
|
code: types_1.ErrorCodes.E_DEVICE_GET_STATE_UNKNOWN,
|
|
203
|
-
message: `Device control ${controlId} doesn't exist on device ${
|
|
213
|
+
message: `Device control ${controlId} doesn't exist on device ${jsonId}`,
|
|
204
214
|
},
|
|
205
215
|
};
|
|
206
216
|
}
|
|
207
217
|
if (!control.getStateHandler) {
|
|
208
|
-
this.log.warn(`Device get state ${controlId} on ${
|
|
218
|
+
this.log.warn(`Device get state ${controlId} on ${jsonId} is disabled because it has no handler`);
|
|
209
219
|
return {
|
|
210
220
|
error: {
|
|
211
221
|
code: types_1.ErrorCodes.E_DEVICE_GET_STATE_NO_HANDLER,
|
|
212
|
-
message: `Device get state ${controlId} on ${
|
|
222
|
+
message: `Device get state ${controlId} on ${jsonId} is disabled because it has no handler`,
|
|
213
223
|
},
|
|
214
224
|
};
|
|
215
225
|
}
|
|
@@ -222,73 +232,110 @@ class DeviceManagement {
|
|
|
222
232
|
void this.handleMessage(obj).catch(this.log.error);
|
|
223
233
|
}
|
|
224
234
|
async handleMessage(msg) {
|
|
235
|
+
var _a;
|
|
225
236
|
this.log.debug(`DeviceManagement received: ${JSON.stringify(msg)}`);
|
|
226
237
|
switch (msg.command) {
|
|
227
238
|
case 'dm:instanceInfo': {
|
|
228
239
|
this.instanceInfo = await this.getInstanceInfo();
|
|
229
|
-
this.sendReply(Object.assign(Object.assign({}, this.instanceInfo), { actions:
|
|
240
|
+
this.sendReply(Object.assign(Object.assign({}, this.instanceInfo), { actions: convertActions(this.instanceInfo.actions) }), msg);
|
|
230
241
|
return;
|
|
231
242
|
}
|
|
232
|
-
case 'dm:
|
|
233
|
-
const
|
|
234
|
-
this.
|
|
235
|
-
|
|
236
|
-
|
|
243
|
+
case 'dm:loadDevices': {
|
|
244
|
+
const context = new DeviceLoadContextImpl(msg, this.adapter);
|
|
245
|
+
this.deviceLoadContexts.set(msg._id, context);
|
|
246
|
+
await this.loadDevices(context);
|
|
247
|
+
if (context.complete()) {
|
|
248
|
+
this.deviceLoadContexts.delete(msg._id);
|
|
249
|
+
}
|
|
250
|
+
this.devices = context.devices.reduce((map, value) => {
|
|
251
|
+
const jsonId = JSON.stringify(value.id);
|
|
252
|
+
if (map.has(jsonId)) {
|
|
253
|
+
throw new Error(`Device ID ${jsonId} is not unique`);
|
|
237
254
|
}
|
|
238
|
-
map.set(
|
|
255
|
+
map.set(jsonId, value);
|
|
239
256
|
return map;
|
|
240
257
|
}, new Map());
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
case 'dm:deviceInfo': {
|
|
261
|
+
const deviceInfo = await this.getDeviceInfo(msg.message);
|
|
262
|
+
this.sendReply(Object.assign(Object.assign({}, deviceInfo), { actions: convertActions(deviceInfo.actions), controls: convertControls(deviceInfo.controls) }), msg);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
case 'dm:deviceStatus': {
|
|
266
|
+
const deviceStatus = await this.getDeviceStatus(msg.message);
|
|
267
|
+
this.sendReply(deviceStatus, msg);
|
|
244
268
|
return;
|
|
245
269
|
}
|
|
246
270
|
case 'dm:deviceDetails': {
|
|
247
271
|
const details = await this.getDeviceDetails(msg.message);
|
|
248
|
-
this.
|
|
272
|
+
this.sendReply(details, msg);
|
|
249
273
|
return;
|
|
250
274
|
}
|
|
251
275
|
case 'dm:instanceAction': {
|
|
252
276
|
const action = msg.message;
|
|
253
277
|
const context = new MessageContext(msg, this.adapter);
|
|
254
|
-
this.
|
|
278
|
+
this.messageContexts.set(msg._id, context);
|
|
255
279
|
const result = await this.handleInstanceAction(action.actionId, context, { value: action.value });
|
|
256
|
-
this.
|
|
280
|
+
this.messageContexts.delete(msg._id);
|
|
257
281
|
context.sendFinalResult(result);
|
|
258
282
|
return;
|
|
259
283
|
}
|
|
260
284
|
case 'dm:deviceAction': {
|
|
261
285
|
const action = msg.message;
|
|
262
286
|
const context = new MessageContext(msg, this.adapter);
|
|
263
|
-
this.
|
|
287
|
+
this.messageContexts.set(msg._id, context);
|
|
264
288
|
const result = await this.handleDeviceAction(action.deviceId, action.actionId, context, {
|
|
265
289
|
value: action.value,
|
|
266
290
|
});
|
|
267
|
-
this.
|
|
268
|
-
|
|
291
|
+
this.messageContexts.delete(msg._id);
|
|
292
|
+
if ('update' in result) {
|
|
293
|
+
// special handling for update responses (we need to update our cache and convert actions/controls before sending to GUI)
|
|
294
|
+
const update = result.update;
|
|
295
|
+
(_a = this.devices) === null || _a === void 0 ? void 0 : _a.set(JSON.stringify(update.id), update);
|
|
296
|
+
context.sendFinalResult({
|
|
297
|
+
update: Object.assign(Object.assign({}, update), { actions: convertActions(update.actions), controls: convertControls(update.controls) }),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
context.sendFinalResult(result);
|
|
302
|
+
}
|
|
269
303
|
return;
|
|
270
304
|
}
|
|
271
305
|
case 'dm:deviceControl': {
|
|
272
306
|
const control = msg.message;
|
|
273
307
|
const context = new MessageContext(msg, this.adapter);
|
|
274
|
-
this.
|
|
308
|
+
this.messageContexts.set(msg._id, context);
|
|
275
309
|
const result = await this.handleDeviceControl(control.deviceId, control.controlId, control.state, context);
|
|
276
|
-
this.
|
|
310
|
+
this.messageContexts.delete(msg._id);
|
|
277
311
|
context.sendControlResult(control.deviceId, control.controlId, result);
|
|
278
312
|
return;
|
|
279
313
|
}
|
|
280
314
|
case 'dm:deviceControlState': {
|
|
281
315
|
const control = msg.message;
|
|
282
316
|
const context = new MessageContext(msg, this.adapter);
|
|
283
|
-
this.
|
|
317
|
+
this.messageContexts.set(msg._id, context);
|
|
284
318
|
const result = await this.handleDeviceControlState(control.deviceId, control.controlId, context);
|
|
285
|
-
this.
|
|
319
|
+
this.messageContexts.delete(msg._id);
|
|
286
320
|
context.sendControlResult(control.deviceId, control.controlId, result);
|
|
287
321
|
return;
|
|
288
322
|
}
|
|
323
|
+
case 'dm:deviceLoadProgress': {
|
|
324
|
+
const { origin } = msg.message;
|
|
325
|
+
const context = this.deviceLoadContexts.get(origin);
|
|
326
|
+
if (!context) {
|
|
327
|
+
this.log.warn(`Unknown message origin: ${origin}`);
|
|
328
|
+
this.sendReply({ error: 'Unknown load progress origin' }, msg);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (context.handleProgress(msg)) {
|
|
332
|
+
this.deviceLoadContexts.delete(origin);
|
|
333
|
+
}
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
289
336
|
case 'dm:actionProgress': {
|
|
290
337
|
const { origin } = msg.message;
|
|
291
|
-
const context = this.
|
|
338
|
+
const context = this.messageContexts.get(origin);
|
|
292
339
|
if (!context) {
|
|
293
340
|
this.log.warn(`Unknown message origin: ${origin}`);
|
|
294
341
|
this.sendReply({ error: 'Unknown action origin' }, msg);
|
|
@@ -299,41 +346,57 @@ class DeviceManagement {
|
|
|
299
346
|
}
|
|
300
347
|
}
|
|
301
348
|
}
|
|
302
|
-
convertActions(actions) {
|
|
303
|
-
if (!actions) {
|
|
304
|
-
return undefined;
|
|
305
|
-
}
|
|
306
|
-
// detect duplicate IDs
|
|
307
|
-
const ids = new Set();
|
|
308
|
-
actions.forEach(a => {
|
|
309
|
-
if (ids.has(a.id)) {
|
|
310
|
-
throw new Error(`Action ID ${a.id} is used twice, this would lead to unexpected behavior`);
|
|
311
|
-
}
|
|
312
|
-
ids.add(a.id);
|
|
313
|
-
});
|
|
314
|
-
// remove handler function to send it as JSON
|
|
315
|
-
return actions.map((a) => (Object.assign(Object.assign({}, a), { handler: undefined, disabled: !a.handler })));
|
|
316
|
-
}
|
|
317
|
-
convertControls(controls) {
|
|
318
|
-
if (!controls) {
|
|
319
|
-
return undefined;
|
|
320
|
-
}
|
|
321
|
-
// detect duplicate IDs
|
|
322
|
-
const ids = new Set();
|
|
323
|
-
controls.forEach(a => {
|
|
324
|
-
if (ids.has(a.id)) {
|
|
325
|
-
throw new Error(`Control ID ${a.id} is used twice, this would lead to unexpected behavior`);
|
|
326
|
-
}
|
|
327
|
-
ids.add(a.id);
|
|
328
|
-
});
|
|
329
|
-
// remove handler function to send it as JSON
|
|
330
|
-
return controls.map((a) => (Object.assign(Object.assign({}, a), { handler: undefined, getStateHandler: undefined })));
|
|
331
|
-
}
|
|
332
349
|
sendReply(reply, msg) {
|
|
333
350
|
this.adapter.sendTo(msg.from, msg.command, reply, msg.callback);
|
|
334
351
|
}
|
|
335
352
|
}
|
|
336
353
|
exports.DeviceManagement = DeviceManagement;
|
|
354
|
+
class DeviceLoadContextImpl {
|
|
355
|
+
constructor(msg, adapter) {
|
|
356
|
+
this.adapter = adapter;
|
|
357
|
+
this.minBatchSize = 8;
|
|
358
|
+
this.devices = [];
|
|
359
|
+
this.sendNext = [];
|
|
360
|
+
this.completed = false;
|
|
361
|
+
this.respondTo = msg;
|
|
362
|
+
this.id = msg._id;
|
|
363
|
+
}
|
|
364
|
+
addDevice(device) {
|
|
365
|
+
this.devices.push(device);
|
|
366
|
+
this.sendNext.push(Object.assign(Object.assign({}, device), { actions: convertActions(device.actions), controls: convertControls(device.controls) }));
|
|
367
|
+
this.flush();
|
|
368
|
+
}
|
|
369
|
+
setTotalDevices(count) {
|
|
370
|
+
this.totalDevices = count;
|
|
371
|
+
this.flush();
|
|
372
|
+
}
|
|
373
|
+
complete() {
|
|
374
|
+
this.completed = true;
|
|
375
|
+
return this.flush();
|
|
376
|
+
}
|
|
377
|
+
handleProgress(message) {
|
|
378
|
+
this.respondTo = message;
|
|
379
|
+
return this.flush();
|
|
380
|
+
}
|
|
381
|
+
flush() {
|
|
382
|
+
if (this.sendNext.length <= this.minBatchSize && !this.completed) {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
if (!this.respondTo) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
const reply = {
|
|
389
|
+
add: this.sendNext,
|
|
390
|
+
total: this.totalDevices,
|
|
391
|
+
next: this.completed ? undefined : { origin: this.id },
|
|
392
|
+
};
|
|
393
|
+
this.sendNext = [];
|
|
394
|
+
const msg = this.respondTo;
|
|
395
|
+
this.respondTo = undefined;
|
|
396
|
+
this.adapter.sendTo(msg.from, msg.command, reply, msg.callback);
|
|
397
|
+
return this.completed;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
337
400
|
class MessageContext {
|
|
338
401
|
constructor(msg, adapter) {
|
|
339
402
|
this.adapter = adapter;
|
|
@@ -345,9 +408,7 @@ class MessageContext {
|
|
|
345
408
|
const promise = new Promise(resolve => {
|
|
346
409
|
this.progressHandler = () => resolve();
|
|
347
410
|
});
|
|
348
|
-
this.send('message',
|
|
349
|
-
message: text,
|
|
350
|
-
});
|
|
411
|
+
this.send({ type: 'message', message: text });
|
|
351
412
|
return promise;
|
|
352
413
|
}
|
|
353
414
|
showConfirmation(text) {
|
|
@@ -355,9 +416,7 @@ class MessageContext {
|
|
|
355
416
|
const promise = new Promise(resolve => {
|
|
356
417
|
this.progressHandler = msg => resolve(!!msg.confirm);
|
|
357
418
|
});
|
|
358
|
-
this.send('confirm',
|
|
359
|
-
confirm: text,
|
|
360
|
-
});
|
|
419
|
+
this.send({ type: 'confirm', confirm: text });
|
|
361
420
|
return promise;
|
|
362
421
|
}
|
|
363
422
|
showForm(schema, options) {
|
|
@@ -365,7 +424,8 @@ class MessageContext {
|
|
|
365
424
|
const promise = new Promise(resolve => {
|
|
366
425
|
this.progressHandler = msg => resolve(msg.data);
|
|
367
426
|
});
|
|
368
|
-
this.send(
|
|
427
|
+
this.send({
|
|
428
|
+
type: 'form',
|
|
369
429
|
form: Object.assign({ schema }, options),
|
|
370
430
|
});
|
|
371
431
|
return promise;
|
|
@@ -378,9 +438,7 @@ class MessageContext {
|
|
|
378
438
|
const promise = new Promise(resolve => {
|
|
379
439
|
this.progressHandler = () => resolve();
|
|
380
440
|
});
|
|
381
|
-
this.send('progress',
|
|
382
|
-
progress: Object.assign(Object.assign(Object.assign({ title }, options), update), { open: true }),
|
|
383
|
-
}, true);
|
|
441
|
+
this.send({ type: 'progress', progress: update }, true);
|
|
384
442
|
return promise;
|
|
385
443
|
},
|
|
386
444
|
close: () => {
|
|
@@ -390,28 +448,23 @@ class MessageContext {
|
|
|
390
448
|
resolve();
|
|
391
449
|
};
|
|
392
450
|
});
|
|
393
|
-
this.send('progress', {
|
|
394
|
-
progress: { open: false },
|
|
395
|
-
});
|
|
451
|
+
this.send({ type: 'progress', progress: { open: false } });
|
|
396
452
|
return promise;
|
|
397
453
|
},
|
|
398
454
|
};
|
|
399
455
|
const promise = new Promise(resolve => {
|
|
400
456
|
this.progressHandler = () => resolve(dialog);
|
|
401
457
|
});
|
|
402
|
-
this.send('progress', {
|
|
403
|
-
progress: Object.assign(Object.assign({ title }, options), { open: true }),
|
|
404
|
-
}, true);
|
|
458
|
+
this.send({ type: 'progress', progress: Object.assign(Object.assign({ title }, options), { open: true }) }, true);
|
|
405
459
|
return promise;
|
|
406
460
|
}
|
|
407
461
|
sendFinalResult(result) {
|
|
408
|
-
this.send('result',
|
|
409
|
-
result,
|
|
410
|
-
});
|
|
462
|
+
this.send({ type: 'result', result });
|
|
411
463
|
}
|
|
412
464
|
sendControlResult(deviceId, controlId, result) {
|
|
413
465
|
if (typeof result === 'object' && 'error' in result) {
|
|
414
|
-
this.send(
|
|
466
|
+
this.send({
|
|
467
|
+
type: 'result',
|
|
415
468
|
result: {
|
|
416
469
|
error: result.error,
|
|
417
470
|
deviceId,
|
|
@@ -420,7 +473,8 @@ class MessageContext {
|
|
|
420
473
|
});
|
|
421
474
|
}
|
|
422
475
|
else {
|
|
423
|
-
this.send(
|
|
476
|
+
this.send({
|
|
477
|
+
type: 'result',
|
|
424
478
|
result: {
|
|
425
479
|
state: result,
|
|
426
480
|
deviceId,
|
|
@@ -442,11 +496,11 @@ class MessageContext {
|
|
|
442
496
|
throw new Error("Can't show another dialog while a progress dialog is open. Please call 'close()' on the dialog before opening another dialog.");
|
|
443
497
|
}
|
|
444
498
|
}
|
|
445
|
-
send(
|
|
499
|
+
send(message, doNotClose) {
|
|
446
500
|
if (!this.lastMessage) {
|
|
447
501
|
throw new Error("No outstanding message, can't send a new one");
|
|
448
502
|
}
|
|
449
|
-
this.adapter.sendTo(this.lastMessage.from, this.lastMessage.command, Object.assign(Object.assign({}, message), {
|
|
503
|
+
this.adapter.sendTo(this.lastMessage.from, this.lastMessage.command, Object.assign(Object.assign({}, message), { origin: this.lastMessage.message.origin || this.lastMessage._id }), this.lastMessage.callback);
|
|
450
504
|
if (!doNotClose) {
|
|
451
505
|
// "progress" is exception. It will be closed with "close" flag
|
|
452
506
|
this.lastMessage = undefined;
|
|
@@ -454,3 +508,33 @@ class MessageContext {
|
|
|
454
508
|
}
|
|
455
509
|
}
|
|
456
510
|
exports.MessageContext = MessageContext;
|
|
511
|
+
function convertActions(actions) {
|
|
512
|
+
if (!actions) {
|
|
513
|
+
return undefined;
|
|
514
|
+
}
|
|
515
|
+
// detect duplicate IDs
|
|
516
|
+
const ids = new Set();
|
|
517
|
+
actions.forEach(a => {
|
|
518
|
+
if (ids.has(a.id)) {
|
|
519
|
+
throw new Error(`Action ID ${a.id} is used twice, this would lead to unexpected behavior`);
|
|
520
|
+
}
|
|
521
|
+
ids.add(a.id);
|
|
522
|
+
});
|
|
523
|
+
// remove a handler function to send it as JSON
|
|
524
|
+
return actions.map((a) => (Object.assign(Object.assign({}, a), { handler: undefined, disabled: !a.handler && !a.url })));
|
|
525
|
+
}
|
|
526
|
+
function convertControls(controls) {
|
|
527
|
+
if (!controls) {
|
|
528
|
+
return undefined;
|
|
529
|
+
}
|
|
530
|
+
// detect duplicate IDs
|
|
531
|
+
const ids = new Set();
|
|
532
|
+
controls.forEach(a => {
|
|
533
|
+
if (ids.has(a.id)) {
|
|
534
|
+
throw new Error(`Control ID ${a.id} is used twice, this would lead to unexpected behavior`);
|
|
535
|
+
}
|
|
536
|
+
ids.add(a.id);
|
|
537
|
+
});
|
|
538
|
+
// remove handler function to send it as JSON
|
|
539
|
+
return controls.map((a) => (Object.assign(Object.assign({}, a), { handler: undefined, getStateHandler: undefined })));
|
|
540
|
+
}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
+
import type { ProgressUpdate } from './types/common';
|
|
1
2
|
export interface ProgressDialog {
|
|
2
|
-
update(update:
|
|
3
|
-
title?: ioBroker.StringOrTranslated;
|
|
4
|
-
indeterminate?: boolean;
|
|
5
|
-
value?: number;
|
|
6
|
-
label?: ioBroker.StringOrTranslated;
|
|
7
|
-
}): Promise<void>;
|
|
3
|
+
update(update: ProgressUpdate): Promise<void>;
|
|
8
4
|
close(): Promise<void>;
|
|
9
5
|
}
|
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
|
@@ -15,5 +15,3 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./DeviceManagement"), exports);
|
|
18
|
-
// don't export * from "./MessageContext" as it is private
|
|
19
|
-
__exportStar(require("./types"), exports);
|
package/build/types/adapter.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type * as base from './base';
|
|
2
|
+
import type { DeviceId } from './common';
|
|
2
3
|
export type ActionBase = base.ActionBase<'adapter'>;
|
|
3
4
|
export type InstanceAction = base.InstanceAction<'adapter'>;
|
|
4
|
-
export type DeviceAction = base.DeviceAction<'adapter'>;
|
|
5
|
+
export type DeviceAction<TId extends DeviceId> = base.DeviceAction<'adapter', TId>;
|
|
5
6
|
export type InstanceDetails = base.InstanceDetails<'adapter'>;
|
|
6
|
-
export type DeviceInfo = base.DeviceInfo<'adapter'>;
|
|
7
|
-
export type DeviceControl = base.DeviceControl<'adapter'>;
|
|
7
|
+
export type DeviceInfo<TId extends DeviceId> = base.DeviceInfo<'adapter', TId>;
|
|
8
|
+
export type DeviceControl<TId extends DeviceId> = base.DeviceControl<'adapter', TId>;
|