iobroker.rest-api 0.3.2 → 0.3.3

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 CHANGED
@@ -46,7 +46,7 @@ This adapter can run as web-extension. In this case the path is available under
46
46
  -->
47
47
 
48
48
  ## Changelog
49
- ### 0.3.2 (2022-04-19)
49
+ ### 0.3.3 (2022-04-20)
50
50
  * (bluefox) Corrected subscription
51
51
 
52
52
  ### 0.3.1 (2022-04-15)
package/io-package.json CHANGED
@@ -1,8 +1,20 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "rest-api",
4
- "version": "0.3.2",
4
+ "version": "0.3.3",
5
5
  "news": {
6
+ "0.3.3": {
7
+ "en": "Corrected subscription",
8
+ "de": "Abonnement korrigiert",
9
+ "ru": "Исправлена подписка",
10
+ "pt": "Assinatura corrigida",
11
+ "nl": "Gecorrigeerd abonnement",
12
+ "fr": "Abonnement corrigé",
13
+ "it": "Abbonamento corretto",
14
+ "es": "Suscripción corregida",
15
+ "pl": "Poprawiona subskrypcja",
16
+ "zh-cn": "更正订阅"
17
+ },
6
18
  "0.3.2": {
7
19
  "en": "Corrected subscription",
8
20
  "de": "Abonnement korrigiert",
@@ -69,7 +69,11 @@ function subscribeState(req, res) {
69
69
  } else if (obj.type !== 'state') {
70
70
  res.status(500).json({error: 'Cannot subscribe on non-state', stateId: params.stateId, type: obj.type, url: req.body.url});
71
71
  } else {
72
- const error = await req._swaggerObject.registerSubscribe(url, params.stateId, 'state', req._user, (req.query && req.query.method) || (req.body && req.body.method));
72
+ const error = await req._swaggerObject.registerSubscribe(url, params.stateId, 'state', req._user, {
73
+ method: (req.query && req.query.method) || (req.body && req.body.method),
74
+ delta: (req.query && req.query.delta) || (req.body && req.body.delta),
75
+ onchange: (req.query && req.query.onchange) || (req.body && req.body.onchange),
76
+ });
73
77
  if (error) {
74
78
  res.status(500).json({error, stateId: params.stateId, url: req.body.url});
75
79
  return;
@@ -376,7 +380,11 @@ module.exports = {
376
380
  return;
377
381
  }
378
382
  try {
379
- await req._swaggerObject.registerSubscribe(url, req.body.pattern, 'state', req._user, req.body.method);
383
+ await req._swaggerObject.registerSubscribe(url, req.body.pattern, 'state', req._user, {
384
+ method: req.body.method,
385
+ onchange: req.body.onchange === 'true',
386
+ delta: req.body.delta !== undefined ? parseFloat(req.body.delta) : undefined,
387
+ });
380
388
  } catch (error) {
381
389
  res.status(500).json({error, pattern: req.body.pattern, url: req.body.url});
382
390
  }
@@ -410,4 +418,4 @@ module.exports = {
410
418
  }
411
419
  });
412
420
  },
413
- };
421
+ };
@@ -168,6 +168,14 @@ paths:
168
168
  description: "method=polling forces subscribe via long polling"
169
169
  enum: [polling]
170
170
  type: "string"
171
+ - name: "onchange"
172
+ type: "boolean"
173
+ description: "If the updates must be delivered if the value changed"
174
+ in: "query"
175
+ - name: "delta"
176
+ type: "number"
177
+ description: "Minimal delta to trigger the event"
178
+ in: "query"
171
179
  responses:
172
180
  200:
173
181
  description: "successful operation"
@@ -304,7 +312,7 @@ paths:
304
312
  description: "URL of web hook and the unsubscribe pattern"
305
313
  required: true
306
314
  schema:
307
- $ref: "#/definitions/UrlHookWithPattern"
315
+ $ref: "#/definitions/UrlHook"
308
316
  responses:
309
317
  200:
310
318
  description: "successful operation"
@@ -520,7 +528,7 @@ paths:
520
528
  description: "URL of web hook and the unsubscribe pattern"
521
529
  required: true
522
530
  schema:
523
- $ref: "#/definitions/UrlHookWithPattern"
531
+ $ref: "#/definitions/UrlHook"
524
532
  responses:
525
533
  200:
526
534
  description: "successful operation"
@@ -775,7 +783,7 @@ definitions:
775
783
  method:
776
784
  type: "string"
777
785
  description: "HTTP Method"
778
- enum: [ POST, GET, PUT, PATCH ]
786
+ enum: [ POST, GET, PUT, PATCH, polling ]
779
787
  UrlHookWithPattern:
780
788
  type: "object"
781
789
  required: [pattern]
@@ -783,13 +791,16 @@ definitions:
783
791
  url:
784
792
  type: "string"
785
793
  description: "URL"
794
+ onchange:
795
+ type: "boolean"
796
+ description: "If the updates must be delivered if the value changed"
786
797
  pattern:
787
798
  type: "string"
788
799
  description: "Subscribe pattern like: \"system.adapter.admin.0.*\""
789
800
  method:
790
801
  type: "string"
791
802
  description: "HTTP Method"
792
- enum: [ POST, GET, PUT, PATCH, POLLING ]
803
+ enum: [ POST, GET, PUT, PATCH, polling ]
793
804
  HistoryOptions:
794
805
  type: "object"
795
806
  #required: [pattern]
@@ -913,4 +924,4 @@ definitions:
913
924
  description: "Error description"
914
925
  externalDocs:
915
926
  description: "Find out more about ioBroker"
916
- url: "https://www.iobroker.net"
927
+ url: "https://www.iobroker.net"
package/lib/rest-api.js CHANGED
@@ -277,27 +277,57 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
277
277
  this.gcInterval = this.gcInterval || setInterval(() => this._executeGC(), 30000);
278
278
  }
279
279
 
280
- this.registerSubscribe = async (urlHook, id, type, user, method) => {
280
+ this.registerSubscribe = async (urlHook, id, type, user, options) => {
281
+ if (typeof options === 'string') {
282
+ options = {method: options};
283
+ }
284
+ if (options.delta) {
285
+ options.delta = parseFloat(options.delta);
286
+ } else {
287
+ delete options.delta;
288
+ }
289
+ if (options.onchange) {
290
+ options.onchange = options.onchange === true || options.onchange === 'true';
291
+ } else {
292
+ delete options.onchange;
293
+ }
294
+
281
295
  const urlHash = crypto.createHash('md5').update(urlHook).digest('hex');
282
296
  if (!this.subscribes[urlHash]) {
283
- if (method !== 'polling') {
297
+ if (options.method !== 'polling') {
284
298
  const error = await _validateUrlHook({urlHook});
285
299
  if (error) {
286
300
  return `No valid answer from URL hook: ${error}`;
287
301
  }
288
302
  }
289
- if (method === 'polling') {
303
+ if (options.method === 'polling') {
290
304
  this.adapter.log.debug(`[${urlHook}] Subscribe on connection`);
291
305
  this.startGC();
292
306
  }
293
307
 
294
- this.subscribes[urlHash] = {state: [], object: [], urlHook, polling: method === 'polling', ts: Date.now()};
308
+ this.subscribes[urlHash] = {
309
+ state: [],
310
+ object: [],
311
+ urlHook,
312
+ polling: options.method === 'polling',
313
+ ts: Date.now()
314
+ };
295
315
  }
296
- if (!this.subscribes[urlHash][type].find(item => item.id === id && (!item.method || item.method === method))) {
297
- this.subscribes[urlHash][type].push({id});
316
+
317
+ if (!this.subscribes[urlHash][type].find(item => item.id === id && (!item.method || item.method === options.method))) {
318
+ const item = {id, delta: options.delta, onchange: options.onchange};
319
+ this.subscribes[urlHash][type].push(item);
298
320
  if (type === 'state') {
299
321
  this.adapter.log.debug(`[${urlHook}] Subscribe on state "${id}"`);
300
322
  await this.adapter.subscribeForeignStatesAsync(id, {user});
323
+ if (options.onchange || options.delta) {
324
+ item.val = await this.adapter.getForeignStateAsync(id);
325
+ if (item.val) {
326
+ item.val = item.val.val;
327
+ } else {
328
+ item.val = null;
329
+ }
330
+ }
301
331
  } else {
302
332
  this.adapter.log.debug(`[${urlHook}] Subscribe on object "${id}"`);
303
333
  await this.adapter.subscribeForeignObjectsAsync(id, {user});
@@ -385,6 +415,21 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
385
415
  this.subscribes[urlHash].state.forEach(async item => {
386
416
  // check if id
387
417
  if (typeof item.id === 'string' && item.id === id) {
418
+ if (state && item.delta && item.val !== null && Math.abs(item.val - state.val) < item.delta) {
419
+ // ignore
420
+ this.adapter.log.debug(`State change for "${id}" ignored as delta (${item.val} - ${state.val}) is less than ${item.delta}`);
421
+ return;
422
+ }
423
+ if (state && item.onchange && !item.delta && item.val === state.val) {
424
+ // ignore
425
+ this.adapter.log.debug(`State change for "${id}" ignored as does not changed (${state.val})`);
426
+ return;
427
+ }
428
+ if (state && (item.delta || item.onchange)) {
429
+ // remember last value
430
+ item.val = state.val;
431
+ }
432
+
388
433
  await reportChange(this.subscribes[urlHash], {id, state});
389
434
  } else {
390
435
  // todo
@@ -495,15 +540,18 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
495
540
  new Promise(resolve => {
496
541
  item.promise = {
497
542
  resolve,
498
- ts: setTimeout(() => {
499
- item.promise.ts = null;
543
+ timer: setTimeout(() => {
544
+ if (item.promise) {
545
+ // could never happen
546
+ item.promise.timer = null;
547
+ }
500
548
  resolve();
501
549
  }, item ? item.timeout : 30000)};
502
550
  })
503
551
  .then(data => {
504
552
  if (item.promise) {
505
- item.promise.ts && clearTimeout(item.promise.ts);
506
- item.promise.ts = null;
553
+ item.promise.timer && clearTimeout(item.promise.timer);
554
+ item.promise.timer = null;
507
555
  item.promise.resolve = null;
508
556
  item.promise = null;
509
557
  } else {
@@ -517,8 +565,8 @@ function SwaggerUI(_ignore, webSettings, adapter, instanceSettings, app, callbac
517
565
  this.adapter.log.warn(`[${item && item.urlHook}]Error in polling connection: ${error}`);
518
566
  }
519
567
  if (item.promise) {
520
- item.promise.ts && clearTimeout(item.promise.ts);
521
- item.promise.ts = null;
568
+ item.promise.timer && clearTimeout(item.promise.timer);
569
+ item.promise.timer = null;
522
570
  item.promise.resolve = null;
523
571
  item.promise = null;
524
572
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.rest-api",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "RESTful interface for ioBroker with GUI.",
5
5
  "author": {
6
6
  "name": "bluefox",