@iobroker/dm-utils 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 DeviceInfo, type ErrorResponse, type InstanceDetails, type JsonFormData, type JsonFormSchema, type RefreshResponse, type RetVal, type ActionButton } from './types';
5
- import type { BackendToGuiCommand, ControlState } from './types/base';
6
- export declare abstract class DeviceManagement<T extends AdapterInstance = AdapterInstance> {
7
- protected readonly adapter: T;
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 contexts;
12
- constructor(adapter: T, communicationStateId?: string | boolean);
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 listDevices(): RetVal<DeviceInfo[]>;
18
- protected getDeviceDetails(id: string): RetVal<DeviceDetails | null | {
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
- protected handleInstanceAction(actionId: string, context?: ActionContext, options?: {
22
- value?: number | string | boolean;
23
- [key: string]: any;
24
- }): RetVal<ErrorResponse> | RetVal<RefreshResponse>;
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: string, options?: {
51
- indeterminate?: boolean;
52
- value?: number;
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.contexts = new Map();
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.setStateAsync(this.communicationStateId, JSON.stringify(command), true);
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: 'v2', communicationStateId: this.communicationStateId || undefined };
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 listDevices()`);
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 listDevices()`,
105
+ message: `Device action ${actionId} was called before loadDevices()`,
99
106
  },
100
107
  };
101
108
  }
102
- const device = this.devices.get(deviceId);
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: ${deviceId}`);
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: ${deviceId}`,
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 ${deviceId}`);
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 ${deviceId}`,
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 ${deviceId} is disabled because it has no handler`);
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 ${deviceId} is disabled because it has no handler`,
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 listDevices()`);
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 listDevices()`,
148
+ message: `Device control ${controlId} was called before loadDevices()`,
141
149
  },
142
150
  };
143
151
  }
144
- const device = this.devices.get(deviceId);
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: ${deviceId}`);
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: ${deviceId}`,
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 ${deviceId}`);
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 ${deviceId}`,
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 ${deviceId} is disabled because it has no handler`);
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 ${deviceId} is disabled because it has no handler`,
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 listDevices()`);
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 listDevices()`,
192
+ message: `Device control ${controlId} was called before loadDevices()`,
184
193
  },
185
194
  };
186
195
  }
187
- const device = this.devices.get(deviceId);
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: ${deviceId}`);
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: ${deviceId}`,
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 ${deviceId}`);
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 ${deviceId}`,
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 ${deviceId} is disabled because it has no handler`);
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 ${deviceId} is disabled because it has no handler`,
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: this.convertActions(this.instanceInfo.actions) }), msg);
240
+ this.sendReply(Object.assign(Object.assign({}, this.instanceInfo), { actions: convertActions(this.instanceInfo.actions) }), msg);
230
241
  return;
231
242
  }
232
- case 'dm:listDevices': {
233
- const deviceList = await this.listDevices();
234
- this.devices = deviceList.reduce((map, value) => {
235
- if (map.has(value.id)) {
236
- throw new Error(`Device ID ${value.id} is not unique`);
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(value.id, value);
255
+ map.set(jsonId, value);
239
256
  return map;
240
257
  }, new Map());
241
- const apiDeviceList = deviceList.map(d => (Object.assign(Object.assign({}, d), { actions: this.convertActions(d.actions), controls: this.convertControls(d.controls) })));
242
- this.sendReply(apiDeviceList, msg);
243
- this.adapter.sendTo(msg.from, msg.command, this.devices, msg.callback);
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.adapter.sendTo(msg.from, msg.command, details, msg.callback);
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.contexts.set(msg._id, context);
278
+ this.messageContexts.set(msg._id, context);
255
279
  const result = await this.handleInstanceAction(action.actionId, context, { value: action.value });
256
- this.contexts.delete(msg._id);
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.contexts.set(msg._id, context);
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.contexts.delete(msg._id);
268
- context.sendFinalResult(result);
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.contexts.set(msg._id, context);
308
+ this.messageContexts.set(msg._id, context);
275
309
  const result = await this.handleDeviceControl(control.deviceId, control.controlId, control.state, context);
276
- this.contexts.delete(msg._id);
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.contexts.set(msg._id, context);
317
+ this.messageContexts.set(msg._id, context);
284
318
  const result = await this.handleDeviceControlState(control.deviceId, control.controlId, context);
285
- this.contexts.delete(msg._id);
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.contexts.get(origin);
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('form', {
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('result', {
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('result', {
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(type, message, doNotClose) {
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), { type, origin: this.lastMessage.message.origin || this.lastMessage._id }), this.lastMessage.callback);
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 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
  }
@@ -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>;