@iobroker/socket-classes 1.2.0 → 1.3.1

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
@@ -63,6 +63,59 @@ io.start(server.server, ws, {userKey: 'connect.sid', checkUser, store, secret: a
63
63
  io.close();
64
64
  ```
65
65
 
66
+ ## GUI subscribes
67
+ GUI client can send to desired instance the `subscribe` message
68
+ ```
69
+ socket.emit('clientSubscribe', 'cameras.0', 'startCamera', {width: 640, height: 480}, result => console.log('Started: ' + result));
70
+ ```
71
+
72
+ The instance 'cameras.0' will receive message `clientSubscribe` with information who want to receive messages.
73
+ ```
74
+ adapter.on('message', obj => {
75
+ if (obj?.command === 'clientSubscribe') {
76
+ if (obj?.message.type && obj.message.type.startsWith('startCamera/')) {
77
+ const [, camera] = obj.message.type.split('/');
78
+ // start camera with obj.message.data
79
+ // ...
80
+
81
+ // inform GUI that camera is started
82
+ adapter.sendTo(obj.from, obj.command, {result: true}, obj.callback);
83
+ this.subscribes = this.subscribes || [];
84
+ this.subscribes.push({sid: obj.message.sid, from: obj.from, type: obj.message.type, camera});
85
+ }
86
+ } else
87
+ if (obj?.command === 'clientUnsubscribe' || obj?.command === 'clientSubscribeError') {
88
+ if (obj?.message.type && obj.message.type.startsWith('startCamera/')) {
89
+ const [, camera] = obj.message.type.split('/');
90
+ if (this.subscribes) {
91
+ const pos = this.subscribes.findIndex(s => s.sid === obj.message.sid && s.from === obj.from && s.type === obj.message.type);
92
+ if (pos !== -1) {
93
+ this.subscribes.splice(pos, 1);
94
+
95
+ // stop camera
96
+ // ...
97
+ }
98
+ }
99
+ }
100
+ }
101
+ });
102
+ ```
103
+
104
+ and after that client will receive messages from instance
105
+
106
+ ```
107
+ function sendImage(camera, data) {
108
+ this.subscribes.forEach(it => {
109
+ if (it.camera !== camera) {
110
+ return;
111
+ }
112
+ // send image to GUI
113
+ adapter.sendTo(it.from, 'im', {m: it.type, s: it.sid, d: data});
114
+ });
115
+ }
116
+ ```
117
+
118
+
66
119
  ## Web Methods
67
120
  <!-- WEB_METHODS_START -->
68
121
  ### List of commands
@@ -86,6 +139,8 @@ io.close();
86
139
  * [getObjectView](#getobjectview_w)
87
140
  * [setObject](#setobject_w)
88
141
  * [delObject](#delobject_w)
142
+ * [clientSubscribe](#clientsubscribe_w)
143
+ * [clientUnsubscribe](#clientunsubscribe_w)
89
144
  * [getStates](#getstates_w)
90
145
  * [getForeignStates](#getforeignstates_w)
91
146
  * [getState](#getstate_w)
@@ -218,6 +273,19 @@ Delete object. Only deletion of flot objects is allowed
218
273
  * options *(string)*: ignored
219
274
  * callback *(function)*: `function (error)`
220
275
 
276
+ ### <a name="clientsubscribe_w"></a>clientSubscribe(targetInstance, messageType, data, callback)
277
+ Client informs specific instance about subscription on its messages. After subscription the socket will receive "im" messages from desired instance
278
+ * targetInstance *(string)*: instance name, e.g. "cameras.0"
279
+ * messageType *(string)*: message type, e.g. "startRecording/cam1"
280
+ * data *(object)*: optional data object, e.g. {width: 640, height: 480}
281
+ * callback *(function)*: `function (error, result)`, target instance MUST acknowledge the subscription and return some object as result
282
+
283
+ ### <a name="clientunsubscribe_w"></a>clientUnsubscribe(targetInstance, messageType, callback)
284
+ Client unsubscribes from specific instance's messages
285
+ * targetInstance *(string)*: instance name, e.g. "cameras.0"
286
+ * messageType *(string)*: message type, e.g. "startRecording/cam1"
287
+ * callback *(function)*: `function (error, wasSubscribed)`, target instance MUST NOT acknowledge the un-subscription
288
+
221
289
  ### <a name="getstates_w"></a>getStates(pattern, callback)
222
290
  Read states by pattern
223
291
  * pattern *(string)*: optional pattern, like `system.adapter.*` or array of state IDs
@@ -273,19 +341,19 @@ Unsubscribe from state changes by pattern. Same as `unsubscribe`.
273
341
  ### <a name="readfile_w"></a>readFile(_adapter, fileName, callback)
274
342
  Read file from ioBroker DB
275
343
  * _adapter *(string)*: instance name, e.g. `vis.0`
276
- * fileName *(string)*: file name, e.g `main/vis-views.json`
344
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
277
345
  * callback *(function)*: `function (error, data, mimeType)`
278
346
 
279
347
  ### <a name="readfile64_w"></a>readFile64(_adapter, fileName, callback)
280
348
  Read file from ioBroker DB as base64 string
281
349
  * _adapter *(string)*: instance name, e.g. `vis.0`
282
- * fileName *(string)*: file name, e.g `main/vis-views.json`
350
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
283
351
  * callback *(function)*: `function (error, base64, mimeType)`
284
352
 
285
353
  ### <a name="writefile64_w"></a>writeFile64(_adapter, fileName, data64, options, callback)
286
354
  Write file into ioBroker DB as base64 string
287
355
  * _adapter *(string)*: instance name, e.g. `vis.0`
288
- * fileName *(string)*: file name, e.g `main/vis-views.json`
356
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
289
357
  * data64 *(string)*: file content as base64 string
290
358
  * options *(object)*: optional `{mode: 0x0644}`
291
359
  * callback *(function)*: `function (error)`
@@ -293,7 +361,7 @@ Write file into ioBroker DB as base64 string
293
361
  ### <a name="writefile_w"></a>writeFile(_adapter, fileName, data, options, callback)
294
362
  Write file into ioBroker DB as text **DEPRECATED**
295
363
  * _adapter *(string)*: instance name, e.g. `vis.0`
296
- * fileName *(string)*: file name, e.g `main/vis-views.json`
364
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
297
365
  * data64 *(string)*: file content as base64 string
298
366
  * options *(object)*: optional `{mode: 0x644}`
299
367
  * callback *(function)*: `function (error)`
@@ -301,83 +369,83 @@ Write file into ioBroker DB as text **DEPRECATED**
301
369
  ### <a name="unlink_w"></a>unlink(_adapter, name, callback)
302
370
  Delete file in ioBroker DB
303
371
  * _adapter *(string)*: instance name, e.g. `vis.0`
304
- * name *(string)*: file name, e.g `main/vis-views.json`
372
+ * name *(string)*: file name, e.g. `main/vis-views.json`
305
373
  * callback *(function)*: `function (error)`
306
374
 
307
375
  ### <a name="deletefile_w"></a>deleteFile(_adapter, name, callback)
308
376
  Delete file in ioBroker DB (same as unlink, but only for files)
309
377
  * _adapter *(string)*: instance name, e.g. `vis.0`
310
- * name *(string)*: file name, e.g `main/vis-views.json`
378
+ * name *(string)*: file name, e.g. `main/vis-views.json`
311
379
  * callback *(function)*: `function (error)`
312
380
 
313
381
  ### <a name="deletefolder_w"></a>deleteFolder(_adapter, name, callback)
314
382
  Delete file in ioBroker DB (same as unlink, but only for folders)
315
383
  * _adapter *(string)*: instance name, e.g. `vis.0`
316
- * name *(string)*: folder name, e.g `main`
384
+ * name *(string)*: folder name, e.g. `main`
317
385
  * callback *(function)*: `function (error)`
318
386
 
319
387
  ### <a name="renamefile_w"></a>renameFile(_adapter, oldName, newName, callback)
320
388
  Rename file in ioBroker DB
321
389
  * _adapter *(string)*: instance name, e.g. `vis.0`
322
- * oldName *(string)*: current file name, e.g `main/vis-views.json`
323
- * newName *(string)*: new file name, e.g `main/vis-views-new.json`
390
+ * oldName *(string)*: current file name, e.g. `main/vis-views.json`
391
+ * newName *(string)*: new file name, e.g. `main/vis-views-new.json`
324
392
  * callback *(function)*: `function (error)`
325
393
 
326
394
  ### <a name="rename_w"></a>rename(_adapter, oldName, newName, callback)
327
395
  Rename file or folder in ioBroker DB
328
396
  * _adapter *(string)*: instance name, e.g. `vis.0`
329
- * oldName *(string)*: current file name, e.g `main/vis-views.json`
330
- * newName *(string)*: new file name, e.g `main/vis-views-new.json`
397
+ * oldName *(string)*: current file name, e.g. `main/vis-views.json`
398
+ * newName *(string)*: new file name, e.g. `main/vis-views-new.json`
331
399
  * callback *(function)*: `function (error)`
332
400
 
333
401
  ### <a name="mkdir_w"></a>mkdir(_adapter, dirName, callback)
334
402
  Create folder in ioBroker DB
335
403
  * _adapter *(string)*: instance name, e.g. `vis.0`
336
- * dirName *(string)*: desired folder name, e.g `main`
404
+ * dirName *(string)*: desired folder name, e.g. `main`
337
405
  * callback *(function)*: `function (error)`
338
406
 
339
407
  ### <a name="readdir_w"></a>readDir(_adapter, dirName, options, callback)
340
408
  Read content of folder in ioBroker DB
341
409
  * _adapter *(string)*: instance name, e.g. `vis.0`
342
- * dirName *(string)*: folder name, e.g `main`
410
+ * dirName *(string)*: folder name, e.g. `main`
343
411
  * options *(object)*: optional `{filter: '*'}` or `{filter: '*.json'}`
344
412
  * callback *(function)*: `function (error, files)` where `files` is an array of objects, like `{file: 'vis-views.json', isDir: false, stats: {size: 123}, modifiedAt: 1661336290090, acl: {owner: 'system.user.admin', ownerGroup: 'system.group.administrator', permissions: 1632, read: true, write: true}`
345
413
 
346
414
  ### <a name="chmodfile_w"></a>chmodFile(_adapter, fileName, options, callback)
347
415
  Change file mode in ioBroker DB
348
416
  * _adapter *(string)*: instance name, e.g. `vis.0`
349
- * fileName *(string)*: file name, e.g `main/vis-views.json`
417
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
350
418
  * options *(object)*: `{mode: 0x644}` or 0x644. The first digit is user, second group, third others. Bit 1 is `execute`, bit 2 is `write`, bit 3 is `read`
351
419
  * callback *(function)*: `function (error)`
352
420
 
353
421
  ### <a name="chownfile_w"></a>chownFile(_adapter, fileName, options, callback)
354
422
  Change file owner in ioBroker DB
355
423
  * _adapter *(string)*: instance name, e.g. `vis.0`
356
- * fileName *(string)*: file name, e.g `main/vis-views.json`
424
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
357
425
  * options *(object)*: `{owner: 'system.user.user', ownerGroup: ''system.group.administrator'}` or 'system.user.user'. If ownerGroup is not defined, it will be taken from owner.
358
426
  * callback *(function)*: `function (error)`
359
427
 
360
428
  ### <a name="fileexists_w"></a>fileExists(_adapter, fileName, callback)
361
429
  Check if the file or folder exists in ioBroker DB
362
430
  * _adapter *(string)*: instance name, e.g. `vis.0`
363
- * fileName *(string)*: file name, e.g `main/vis-views.json`
431
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
364
432
  * callback *(function)*: `function (error, isExist)`
365
433
 
366
434
  ### <a name="subscribefiles_w"></a>subscribeFiles(id, pattern, callback)
367
435
  Subscribe to file changes in ioBroker DB
368
436
  * id *(string)*: instance name, e.g. `vis.0` or any object ID of type `meta`. `id` could have wildcards `*` too.
369
- * pattern *(string)*: file name pattern, e.g `main/*.json`
437
+ * pattern *(string)*: file name pattern, e.g. `main/*.json`
370
438
  * callback *(function)*: `function (error)`
371
439
 
372
440
  ### <a name="unsubscribefiles_w"></a>unsubscribeFiles(id, pattern, callback)
373
441
  Unsubscribe from file changes in ioBroker DB
374
442
  * id *(string)*: instance name, e.g. `vis.0` or any object ID of type `meta`. `id` could have wildcards `*` too.
375
- * pattern *(string)*: file name pattern, e.g `main/*.json`
443
+ * pattern *(string)*: file name pattern, e.g. `main/*.json`
376
444
  * callback *(function)*: `function (error)`
377
445
 
378
446
  ### <a name="getadapterinstances_w"></a>getAdapterInstances(adapterName, callback)
379
447
  Read all instances of the given adapter, or all instances of all adapters if adapterName is not defined
380
- * adapterName *(string)*: optional adapter name, e.g `history`.
448
+ * adapterName *(string)*: optional adapter name, e.g. `history`.
381
449
  * callback *(function)*: `function (error, instanceList)`, where instanceList is an array of instance objects, e.g. `{_id: 'system.adapter.history.0', common: {name: 'history', ...}, native: {...}}`
382
450
 
383
451
  <!-- WEB_METHODS_END -->
@@ -432,6 +500,8 @@ Read all instances of the given adapter, or all instances of all adapters if ada
432
500
  * [getObjectView](#getobjectview_a)
433
501
  * [setObject](#setobject_a)
434
502
  * [delObject](#delobject_a)
503
+ * [clientSubscribe](#clientsubscribe_a)
504
+ * [clientUnsubscribe](#clientunsubscribe_a)
435
505
  * [getAllObjects](#getallobjects_a)
436
506
  * [extendObject](#extendobject_a)
437
507
  * [getForeignObjects](#getforeignobjects_a)
@@ -697,6 +767,19 @@ Delete an object or objects recursively. Objects with `dontDelete` cannot be del
697
767
  * options *(string)*: `{recursive: true}`
698
768
  * callback *(function)*: `function (error)`
699
769
 
770
+ ### <a name="clientsubscribe_a"></a>clientSubscribe(targetInstance, messageType, data, callback)
771
+ Client informs specific instance about subscription on its messages. After subscription the socket will receive "im" messages from desired instance
772
+ * targetInstance *(string)*: instance name, e.g. "cameras.0"
773
+ * messageType *(string)*: message type, e.g. "startRecording/cam1"
774
+ * data *(object)*: optional data object, e.g. {width: 640, height: 480}
775
+ * callback *(function)*: `function (error, result)`, target instance MUST acknowledge the subscription and return some object as result
776
+
777
+ ### <a name="clientunsubscribe_a"></a>clientUnsubscribe(targetInstance, messageType, callback)
778
+ Client unsubscribes from specific instance's messages
779
+ * targetInstance *(string)*: instance name, e.g. "cameras.0"
780
+ * messageType *(string)*: message type, e.g. "startRecording/cam1"
781
+ * callback *(function)*: `function (error, wasSubscribed)`, target instance MUST NOT acknowledge the un-subscription
782
+
700
783
  ### <a name="getallobjects_a"></a>getAllObjects(callback)
701
784
  Read absolutely all objects
702
785
  * callback *(function)*: `function (error, objects)`, where `objects` is an object like `{'system.adapter.admin.0': {...}, 'system.adapter.web.0': {...}}`
@@ -774,19 +857,19 @@ Unsubscribe from state changes by pattern. Same as `unsubscribe`.
774
857
  ### <a name="readfile_a"></a>readFile(_adapter, fileName, callback)
775
858
  Read file from ioBroker DB
776
859
  * _adapter *(string)*: instance name, e.g. `vis.0`
777
- * fileName *(string)*: file name, e.g `main/vis-views.json`
860
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
778
861
  * callback *(function)*: `function (error, data, mimeType)`
779
862
 
780
863
  ### <a name="readfile64_a"></a>readFile64(_adapter, fileName, callback)
781
864
  Read file from ioBroker DB as base64 string
782
865
  * _adapter *(string)*: instance name, e.g. `vis.0`
783
- * fileName *(string)*: file name, e.g `main/vis-views.json`
866
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
784
867
  * callback *(function)*: `function (error, base64, mimeType)`
785
868
 
786
869
  ### <a name="writefile64_a"></a>writeFile64(_adapter, fileName, data64, options, callback)
787
870
  Write file into ioBroker DB as base64 string
788
871
  * _adapter *(string)*: instance name, e.g. `vis.0`
789
- * fileName *(string)*: file name, e.g `main/vis-views.json`
872
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
790
873
  * data64 *(string)*: file content as base64 string
791
874
  * options *(object)*: optional `{mode: 0x0644}`
792
875
  * callback *(function)*: `function (error)`
@@ -802,83 +885,83 @@ Write file into ioBroker DB as base64 string
802
885
  ### <a name="unlink_a"></a>unlink(_adapter, name, callback)
803
886
  Delete file in ioBroker DB
804
887
  * _adapter *(string)*: instance name, e.g. `vis.0`
805
- * name *(string)*: file name, e.g `main/vis-views.json`
888
+ * name *(string)*: file name, e.g. `main/vis-views.json`
806
889
  * callback *(function)*: `function (error)`
807
890
 
808
891
  ### <a name="deletefile_a"></a>deleteFile(_adapter, name, callback)
809
892
  Delete file in ioBroker DB (same as unlink, but only for files)
810
893
  * _adapter *(string)*: instance name, e.g. `vis.0`
811
- * name *(string)*: file name, e.g `main/vis-views.json`
894
+ * name *(string)*: file name, e.g. `main/vis-views.json`
812
895
  * callback *(function)*: `function (error)`
813
896
 
814
897
  ### <a name="deletefolder_a"></a>deleteFolder(_adapter, name, callback)
815
898
  Delete file in ioBroker DB (same as unlink, but only for folders)
816
899
  * _adapter *(string)*: instance name, e.g. `vis.0`
817
- * name *(string)*: folder name, e.g `main`
900
+ * name *(string)*: folder name, e.g. `main`
818
901
  * callback *(function)*: `function (error)`
819
902
 
820
903
  ### <a name="renamefile_a"></a>renameFile(_adapter, oldName, newName, callback)
821
904
  Rename file in ioBroker DB
822
905
  * _adapter *(string)*: instance name, e.g. `vis.0`
823
- * oldName *(string)*: current file name, e.g `main/vis-views.json`
824
- * newName *(string)*: new file name, e.g `main/vis-views-new.json`
906
+ * oldName *(string)*: current file name, e.g. `main/vis-views.json`
907
+ * newName *(string)*: new file name, e.g. `main/vis-views-new.json`
825
908
  * callback *(function)*: `function (error)`
826
909
 
827
910
  ### <a name="rename_a"></a>rename(_adapter, oldName, newName, callback)
828
911
  Rename file or folder in ioBroker DB
829
912
  * _adapter *(string)*: instance name, e.g. `vis.0`
830
- * oldName *(string)*: current file name, e.g `main/vis-views.json`
831
- * newName *(string)*: new file name, e.g `main/vis-views-new.json`
913
+ * oldName *(string)*: current file name, e.g. `main/vis-views.json`
914
+ * newName *(string)*: new file name, e.g. `main/vis-views-new.json`
832
915
  * callback *(function)*: `function (error)`
833
916
 
834
917
  ### <a name="mkdir_a"></a>mkdir(_adapter, dirName, callback)
835
918
  Create folder in ioBroker DB
836
919
  * _adapter *(string)*: instance name, e.g. `vis.0`
837
- * dirName *(string)*: desired folder name, e.g `main`
920
+ * dirName *(string)*: desired folder name, e.g. `main`
838
921
  * callback *(function)*: `function (error)`
839
922
 
840
923
  ### <a name="readdir_a"></a>readDir(_adapter, dirName, options, callback)
841
924
  Read content of folder in ioBroker DB
842
925
  * _adapter *(string)*: instance name, e.g. `vis.0`
843
- * dirName *(string)*: folder name, e.g `main`
926
+ * dirName *(string)*: folder name, e.g. `main`
844
927
  * options *(object)*: optional `{filter: '*'}` or `{filter: '*.json'}`
845
928
  * callback *(function)*: `function (error, files)` where `files` is an array of objects, like `{file: 'vis-views.json', isDir: false, stats: {size: 123}, modifiedAt: 1661336290090, acl: {owner: 'system.user.admin', ownerGroup: 'system.group.administrator', permissions: 1632, read: true, write: true}`
846
929
 
847
930
  ### <a name="chmodfile_a"></a>chmodFile(_adapter, fileName, options, callback)
848
931
  Change file mode in ioBroker DB
849
932
  * _adapter *(string)*: instance name, e.g. `vis.0`
850
- * fileName *(string)*: file name, e.g `main/vis-views.json`
933
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
851
934
  * options *(object)*: `{mode: 0x644}` or 0x644. The first digit is user, second group, third others. Bit 1 is `execute`, bit 2 is `write`, bit 3 is `read`
852
935
  * callback *(function)*: `function (error)`
853
936
 
854
937
  ### <a name="chownfile_a"></a>chownFile(_adapter, fileName, options, callback)
855
938
  Change file owner in ioBroker DB
856
939
  * _adapter *(string)*: instance name, e.g. `vis.0`
857
- * fileName *(string)*: file name, e.g `main/vis-views.json`
940
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
858
941
  * options *(object)*: `{owner: 'system.user.user', ownerGroup: ''system.group.administrator'}` or 'system.user.user'. If ownerGroup is not defined, it will be taken from owner.
859
942
  * callback *(function)*: `function (error)`
860
943
 
861
944
  ### <a name="fileexists_a"></a>fileExists(_adapter, fileName, callback)
862
945
  Check if the file or folder exists in ioBroker DB
863
946
  * _adapter *(string)*: instance name, e.g. `vis.0`
864
- * fileName *(string)*: file name, e.g `main/vis-views.json`
947
+ * fileName *(string)*: file name, e.g. `main/vis-views.json`
865
948
  * callback *(function)*: `function (error, isExist)`
866
949
 
867
950
  ### <a name="subscribefiles_a"></a>subscribeFiles(id, pattern, callback)
868
951
  Subscribe to file changes in ioBroker DB
869
952
  * id *(string)*: instance name, e.g. `vis.0` or any object ID of type `meta`. `id` could have wildcards `*` too.
870
- * pattern *(string)*: file name pattern, e.g `main/*.json`
953
+ * pattern *(string)*: file name pattern, e.g. `main/*.json`
871
954
  * callback *(function)*: `function (error)`
872
955
 
873
956
  ### <a name="unsubscribefiles_a"></a>unsubscribeFiles(id, pattern, callback)
874
957
  Unsubscribe from file changes in ioBroker DB
875
958
  * id *(string)*: instance name, e.g. `vis.0` or any object ID of type `meta`. `id` could have wildcards `*` too.
876
- * pattern *(string)*: file name pattern, e.g `main/*.json`
959
+ * pattern *(string)*: file name pattern, e.g. `main/*.json`
877
960
  * callback *(function)*: `function (error)`
878
961
 
879
962
  ### <a name="getadapterinstances_a"></a>getAdapterInstances(adapterName, callback)
880
963
  Read all instances of the given adapter, or all instances of all adapters if adapterName is not defined
881
- * adapterName *(string)*: optional adapter name, e.g `history`.
964
+ * adapterName *(string)*: optional adapter name, e.g. `history`.
882
965
  * callback *(function)*: `function (error, instanceList)`, where instanceList is an array of instance objects, e.g. `{_id: 'system.adapter.history.0', common: {name: 'history', ...}, native: {...}}`
883
966
 
884
967
  <!-- ADMIN_METHODS_END -->
@@ -889,6 +972,9 @@ Read all instances of the given adapter, or all instances of all adapters if ada
889
972
  -->
890
973
 
891
974
  ## Changelog
975
+ ### 1.3.1 (2023-07-31)
976
+ * (bluefox) Implemented subscribes of a client on messages from specific instance
977
+
892
978
  ### 1.2.0 (2023-07-07)
893
979
  * (foxriver76) fixed crash on invalid patterns with js-controller version 5
894
980
  * (bluefox) extended the getObjects function with the possibility to read the list of IDs
@@ -67,6 +67,7 @@ class SocketCommands {
67
67
  this.commands = {};
68
68
  this.subscribes = {};
69
69
  this.logEnabled = false;
70
+ this.clientSubscribes = {};
70
71
 
71
72
  this._updateSession = updateSession;
72
73
 
@@ -230,6 +231,20 @@ class SocketCommands {
230
231
  return false;
231
232
  }
232
233
 
234
+ publishInstanceMessage(socket, sourceInstance, messageType, data) {
235
+ if (this.clientSubscribes[socket.id] &&
236
+ this.clientSubscribes[socket.id][sourceInstance] &&
237
+ this.clientSubscribes[socket.id][sourceInstance].includes(messageType)
238
+ ) {
239
+ socket.emit('im', messageType, sourceInstance, data);
240
+ return true;
241
+ } else {
242
+ // inform instance about missing subscription
243
+ this.adapter.sendTo(sourceInstance, 'clientSubscribeError', {type: messageType, sid: socket.id});
244
+ return false;
245
+ }
246
+ }
247
+
233
248
  _showSubscribes(socket, type) {
234
249
  if (socket && socket.subscribe) {
235
250
  const s = socket.subscribe[type] || [];
@@ -364,7 +379,7 @@ class SocketCommands {
364
379
  }
365
380
  }
366
381
  } else if (key) {
367
- // Remove pattern from global list
382
+ // Remove a pattern from a global list
368
383
  if (this.subscribes[type][key] !== undefined) {
369
384
  this.subscribes[type][key]--;
370
385
  if (this.subscribes[type][key] <= 0) {
@@ -451,6 +466,8 @@ class SocketCommands {
451
466
  if (!socket || !socket.subscribe) {
452
467
  return;
453
468
  }
469
+ // inform all instances about disconnected socket
470
+ this._informAboutDisconnect(socket.id);
454
471
 
455
472
  if (!type) {
456
473
  // all
@@ -832,7 +849,7 @@ class SocketCommands {
832
849
  this.commands['readFile'] = (socket, _adapter, fileName, callback) => {
833
850
  // Read file from ioBroker DB
834
851
  // @param {string} _adapter - instance name, e.g. `vis.0`
835
- // @param {string} fileName - file name, e.g `main/vis-views.json`
852
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
836
853
  // @param {function} callback - `function (error, data, mimeType)`
837
854
  if (this._checkPermissions(socket, 'readFile', callback, fileName)) {
838
855
  try {
@@ -848,7 +865,7 @@ class SocketCommands {
848
865
  this.commands['readFile64'] = (socket, _adapter, fileName, callback) => {
849
866
  // Read file from ioBroker DB as base64 string
850
867
  // @param {string} _adapter - instance name, e.g. `vis.0`
851
- // @param {string} fileName - file name, e.g `main/vis-views.json`
868
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
852
869
  // @param {function} callback - `function (error, base64, mimeType)`
853
870
  if (this._checkPermissions(socket, 'readFile64', callback, fileName)) {
854
871
  try {
@@ -887,7 +904,7 @@ class SocketCommands {
887
904
  this.commands['writeFile64'] = (socket, _adapter, fileName, data64, options, callback) => {
888
905
  // Write file into ioBroker DB as base64 string
889
906
  // @param {string} _adapter - instance name, e.g. `vis.0`
890
- // @param {string} fileName - file name, e.g `main/vis-views.json`
907
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
891
908
  // @param {string} data64 - file content as base64 string
892
909
  // @param {object} options - optional `{mode: 0x0644}`
893
910
  // @param {function} callback - `function (error)`
@@ -920,7 +937,7 @@ class SocketCommands {
920
937
  this.commands['writeFile'] = (socket, _adapter, fileName, data, options, callback) => {
921
938
  // Write file into ioBroker DB as text **DEPRECATED**
922
939
  // @param {string} _adapter - instance name, e.g. `vis.0`
923
- // @param {string} fileName - file name, e.g `main/vis-views.json`
940
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
924
941
  // @param {string} data64 - file content as base64 string
925
942
  // @param {object} options - optional `{mode: 0x644}`
926
943
  // @param {function} callback - `function (error)`
@@ -946,7 +963,7 @@ class SocketCommands {
946
963
  this.commands['unlink'] = (socket, _adapter, name, callback) => {
947
964
  // Delete file in ioBroker DB
948
965
  // @param {string} _adapter - instance name, e.g. `vis.0`
949
- // @param {string} name - file name, e.g `main/vis-views.json`
966
+ // @param {string} name - file name, e.g. `main/vis-views.json`
950
967
  // @param {function} callback - `function (error)`
951
968
  if (this._checkPermissions(socket, 'unlink', callback, name)) {
952
969
  try {
@@ -963,7 +980,7 @@ class SocketCommands {
963
980
  this.commands['deleteFile'] = (socket, _adapter, name, callback) => {
964
981
  // Delete file in ioBroker DB (same as unlink, but only for files)
965
982
  // @param {string} _adapter - instance name, e.g. `vis.0`
966
- // @param {string} name - file name, e.g `main/vis-views.json`
983
+ // @param {string} name - file name, e.g. `main/vis-views.json`
967
984
  // @param {function} callback - `function (error)`
968
985
  if (this._checkPermissions(socket, 'unlink', callback, name)) {
969
986
  try {
@@ -979,7 +996,7 @@ class SocketCommands {
979
996
  this.commands['deleteFolder'] = (socket, _adapter, name, callback) => {
980
997
  // Delete file in ioBroker DB (same as unlink, but only for folders)
981
998
  // @param {string} _adapter - instance name, e.g. `vis.0`
982
- // @param {string} name - folder name, e.g `main`
999
+ // @param {string} name - folder name, e.g. `main`
983
1000
  // @param {function} callback - `function (error)`
984
1001
  if (this._checkPermissions(socket, 'unlink', callback, name)) {
985
1002
  try {
@@ -996,8 +1013,8 @@ class SocketCommands {
996
1013
  this.commands['renameFile'] = (socket, _adapter, oldName, newName, callback) => {
997
1014
  // Rename file in ioBroker DB
998
1015
  // @param {string} _adapter - instance name, e.g. `vis.0`
999
- // @param {string} oldName - current file name, e.g `main/vis-views.json`
1000
- // @param {string} newName - new file name, e.g `main/vis-views-new.json`
1016
+ // @param {string} oldName - current file name, e.g. `main/vis-views.json`
1017
+ // @param {string} newName - new file name, e.g. `main/vis-views-new.json`
1001
1018
  // @param {function} callback - `function (error)`
1002
1019
  if (this._checkPermissions(socket, 'rename', callback, oldName)) {
1003
1020
  try {
@@ -1013,8 +1030,8 @@ class SocketCommands {
1013
1030
  this.commands['rename'] = (socket, _adapter, oldName, newName, callback) => {
1014
1031
  // Rename file or folder in ioBroker DB
1015
1032
  // @param {string} _adapter - instance name, e.g. `vis.0`
1016
- // @param {string} oldName - current file name, e.g `main/vis-views.json`
1017
- // @param {string} newName - new file name, e.g `main/vis-views-new.json`
1033
+ // @param {string} oldName - current file name, e.g. `main/vis-views.json`
1034
+ // @param {string} newName - new file name, e.g. `main/vis-views-new.json`
1018
1035
  // @param {function} callback - `function (error)`
1019
1036
  if (this._checkPermissions(socket, 'rename', callback, oldName)) {
1020
1037
  try {
@@ -1031,7 +1048,7 @@ class SocketCommands {
1031
1048
  this.commands['mkdir'] = (socket, _adapter, dirName, callback) => {
1032
1049
  // Create folder in ioBroker DB
1033
1050
  // @param {string} _adapter - instance name, e.g. `vis.0`
1034
- // @param {string} dirName - desired folder name, e.g `main`
1051
+ // @param {string} dirName - desired folder name, e.g. `main`
1035
1052
  // @param {function} callback - `function (error)`
1036
1053
  if (this._checkPermissions(socket, 'mkdir', callback, dirName)) {
1037
1054
  try {
@@ -1047,7 +1064,7 @@ class SocketCommands {
1047
1064
  this.commands['readDir'] = (socket, _adapter, dirName, options, callback) => {
1048
1065
  // Read content of folder in ioBroker DB
1049
1066
  // @param {string} _adapter - instance name, e.g. `vis.0`
1050
- // @param {string} dirName - folder name, e.g `main`
1067
+ // @param {string} dirName - folder name, e.g. `main`
1051
1068
  // @param {object} options - optional `{filter: '*'}` or `{filter: '*.json'}`
1052
1069
  // @param {function} callback - `function (error, files)` where `files` is an array of objects, like `{file: 'vis-views.json', isDir: false, stats: {size: 123}, modifiedAt: 1661336290090, acl: {owner: 'system.user.admin', ownerGroup: 'system.group.administrator', permissions: 1632, read: true, write: true}`
1053
1070
  if (typeof options === 'function') {
@@ -1075,7 +1092,7 @@ class SocketCommands {
1075
1092
  this.commands['chmodFile'] = (socket, _adapter, fileName, options, callback) => {
1076
1093
  // Change file mode in ioBroker DB
1077
1094
  // @param {string} _adapter - instance name, e.g. `vis.0`
1078
- // @param {string} fileName - file name, e.g `main/vis-views.json`
1095
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
1079
1096
  // @param {object} options - `{mode: 0x644}` or 0x644. The first digit is user, second group, third others. Bit 1 is `execute`, bit 2 is `write`, bit 3 is `read`
1080
1097
  // @param {function} callback - `function (error)`
1081
1098
  if (typeof options === 'function') {
@@ -1103,7 +1120,7 @@ class SocketCommands {
1103
1120
  this.commands['chownFile'] = (socket, _adapter, fileName, options, callback) => {
1104
1121
  // Change file owner in ioBroker DB
1105
1122
  // @param {string} _adapter - instance name, e.g. `vis.0`
1106
- // @param {string} fileName - file name, e.g `main/vis-views.json`
1123
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
1107
1124
  // @param {object} options - `{owner: 'system.user.user', ownerGroup: ''system.group.administrator'}` or 'system.user.user'. If ownerGroup is not defined, it will be taken from owner.
1108
1125
  // @param {function} callback - `function (error)`
1109
1126
  if (this._checkPermissions(socket, 'chownFile', callback, fileName)) {
@@ -1122,7 +1139,7 @@ class SocketCommands {
1122
1139
  this.commands['fileExists'] = (socket, _adapter, fileName, callback) => {
1123
1140
  // Check if the file or folder exists in ioBroker DB
1124
1141
  // @param {string} _adapter - instance name, e.g. `vis.0`
1125
- // @param {string} fileName - file name, e.g `main/vis-views.json`
1142
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
1126
1143
  // @param {function} callback - `function (error, isExist)`
1127
1144
  if (this._checkPermissions(socket, 'fileExists', callback, fileName)) {
1128
1145
  try {
@@ -1138,7 +1155,7 @@ class SocketCommands {
1138
1155
  this.commands['subscribeFiles'] = (socket, id, pattern, callback) => {
1139
1156
  // Subscribe to file changes in ioBroker DB
1140
1157
  // @param {string} id - instance name, e.g. `vis.0` or any object ID of type `meta`. `id` could have wildcards `*` too.
1141
- // @param {string} pattern - file name pattern, e.g `main/*.json`
1158
+ // @param {string} pattern - file name pattern, e.g. `main/*.json`
1142
1159
  // @param {function} callback - `function (error)`
1143
1160
  return this._subscribeFiles(socket, id, pattern, callback);
1144
1161
  }
@@ -1146,7 +1163,7 @@ class SocketCommands {
1146
1163
  this.commands['unsubscribeFiles'] = (socket, id, pattern, callback) => {
1147
1164
  // Unsubscribe from file changes in ioBroker DB
1148
1165
  // @param {string} id - instance name, e.g. `vis.0` or any object ID of type `meta`. `id` could have wildcards `*` too.
1149
- // @param {string} pattern - file name pattern, e.g `main/*.json`
1166
+ // @param {string} pattern - file name pattern, e.g. `main/*.json`
1150
1167
  // @param {function} callback - `function (error)`
1151
1168
 
1152
1169
  return this._unsubscribeFiles(socket, id, pattern, callback);
@@ -1154,7 +1171,7 @@ class SocketCommands {
1154
1171
 
1155
1172
  this.commands['getAdapterInstances'] = (socket, adapterName, callback) => {
1156
1173
  // Read all instances of the given adapter, or all instances of all adapters if adapterName is not defined
1157
- // @param {string} adapterName - optional adapter name, e.g `history`.
1174
+ // @param {string} adapterName - optional adapter name, e.g. `history`.
1158
1175
  // @param {function} callback - `function (error, instanceList)`, where instanceList is an array of instance objects, e.g. `{_id: 'system.adapter.history.0', common: {name: 'history', ...}, native: {...}}`
1159
1176
  if (typeof callback === 'function') {
1160
1177
  if (this._checkPermissions(socket, 'getObject', callback)) {
@@ -1565,6 +1582,48 @@ class SocketCommands {
1565
1582
  SocketCommands._fixCallback(callback, SocketCommands.ERROR_PERMISSION);
1566
1583
  }
1567
1584
  };
1585
+
1586
+ this.commands['clientSubscribe'] = (socket, targetInstance, messageType, data, callback) => {
1587
+ // Client informs specific instance about subscription on its messages. After subscription the socket will receive "im" messages from desired instance
1588
+ // @param {string} targetInstance - instance name, e.g. "cameras.0"
1589
+ // @param {string} messageType - message type, e.g. "startRecording/cam1"
1590
+ // @param {object} data - optional data object, e.g. {width: 640, height: 480}
1591
+ // @param {function} callback - `function (error, result)`, target instance MUST acknowledge the subscription and return some object as result
1592
+ if (typeof data === 'function') {
1593
+ callback = data;
1594
+ data = null;
1595
+ }
1596
+ const sid = socket.id;
1597
+ // GUI subscribes for messages from targetInstance
1598
+ this.clientSubscribes[sid] = this.clientSubscribes[sid] || {};
1599
+ this.clientSubscribes[sid][targetInstance] = this.clientSubscribes[sid][targetInstance] || [];
1600
+ if (!this.clientSubscribes[sid][targetInstance].includes(messageType)) {
1601
+ this.clientSubscribes[sid][targetInstance].push(messageType);
1602
+ }
1603
+ // inform instance about new subscription
1604
+ this.adapter.sendTo(targetInstance, 'clientSubscribe', {type: messageType, sid, data}, result =>
1605
+ SocketCommands._fixCallback(callback, null, result));
1606
+ };
1607
+
1608
+ this.commands['clientUnsubscribe'] = (socket, targetInstance, messageType, callback) => {
1609
+ // Client unsubscribes from specific instance's messages
1610
+ // @param {string} targetInstance - instance name, e.g. "cameras.0"
1611
+ // @param {string} messageType - message type, e.g. "startRecording/cam1"
1612
+ // @param {function} callback - `function (error, wasSubscribed)`, target instance MUST NOT acknowledge the un-subscription
1613
+ const sid = socket.id;
1614
+ // GUI unsubscribes for messages from targetInstance
1615
+ if (this.clientSubscribes[sid] && this.clientSubscribes[sid][targetInstance]) {
1616
+ const pos = this.clientSubscribes[sid][targetInstance].indexOf(messageType);
1617
+ if (pos !== -1) {
1618
+ this.clientSubscribes[sid][targetInstance].splice(pos, 1);
1619
+ // inform instance about unsubscription
1620
+ this.adapter.sendTo(targetInstance, 'clientUnsubscribe', {type: [messageType], sid});
1621
+ SocketCommands._fixCallback(callback, null, true);
1622
+ return;
1623
+ }
1624
+ }
1625
+ SocketCommands._fixCallback(callback, null, false);
1626
+ };
1568
1627
  }
1569
1628
 
1570
1629
  _initCommands() {
@@ -1574,6 +1633,20 @@ class SocketCommands {
1574
1633
  this.__initCommandsFiles();
1575
1634
  }
1576
1635
 
1636
+ _informAboutDisconnect(socketId) {
1637
+ // say to all instances, that this socket was disconnected
1638
+ if (this.clientSubscribes[socketId]) {
1639
+ Object.keys(this.clientSubscribes[socketId]).forEach(targetInstance => {
1640
+ this.adapter.sendTo(targetInstance, 'clientUnsubscribe', {
1641
+ message: this.clientSubscribes[socketId][targetInstance],
1642
+ sid: socketId,
1643
+ provider: this.adapter.namespace
1644
+ });
1645
+ });
1646
+ delete this.clientSubscribes[socketId];
1647
+ }
1648
+ }
1649
+
1577
1650
  applyCommands(socket) {
1578
1651
  Object.keys(this.commands)
1579
1652
  .forEach(command => socket.on(command, (...args) => {
@@ -65,7 +65,7 @@ class SocketCommon {
65
65
 
66
66
  this.settings.ttl = parseInt(this.settings.ttl, 10) || 3600;
67
67
 
68
- // it can be used as client too for cloud
68
+ // it can be used as a client too for cloud
69
69
  if (socketClass) {
70
70
  if (!server.__inited) {
71
71
  if (typeof socketClass.listen === 'function') {
@@ -88,7 +88,7 @@ class SocketCommon {
88
88
  this.__initAuthentication(authOptions);
89
89
  }
90
90
 
91
- // Enable cross domain access
91
+ // Enable cross-domain access
92
92
  // deprecated, because no more used in socket.io@4 only(in @2)
93
93
  if (this.settings.crossDomain && this.server.set) {
94
94
  this.server.set('origins', '*:*');
@@ -111,9 +111,9 @@ class SocketCommon {
111
111
  if ((error && error.message && error.message.includes('authentication failed') ||
112
112
  (details && details.toString().includes('authentication failed')))
113
113
  ) {
114
- this.adapter.log.debug(`Error: ${(error && error.message) || JSON.stringify(error)}${details ? ' - ' + details : ''}`);
114
+ this.adapter.log.debug(`Error: ${(error && error.message) || JSON.stringify(error)}${details ? ` - ${details}` : ''}`);
115
115
  } else {
116
- this.adapter.log.error(`Error: ${(error && error.message) || JSON.stringify(error)}${details ? ' - ' + details : ''}`);
116
+ this.adapter.log.error(`Error: ${(error && error.message) || JSON.stringify(error)}${details ? ` - ${details}` : ''}`);
117
117
  }
118
118
  }
119
119
  });
@@ -123,9 +123,9 @@ class SocketCommon {
123
123
  // ignore "failed connection" as it already shown
124
124
  if (!error || !error.message || !error.message.includes('failed connection')) {
125
125
  if (error && error.message && error.message.includes('authentication failed')) {
126
- this.adapter.log.debug(`Error: ${(error && error.message) || JSON.stringify(error)}${details ? ' - ' + details : ''}`);
126
+ this.adapter.log.debug(`Error: ${(error && error.message) || JSON.stringify(error)}${details ? ` - ${details}` : ''}`);
127
127
  } else {
128
- this.adapter.log.error(`Error: ${(error && error.message) || JSON.stringify(error)}${details ? ' - ' + details : ''}`);
128
+ this.adapter.log.error(`Error: ${(error && error.message) || JSON.stringify(error)}${details ? ` - ${details}` : ''}`);
129
129
  }
130
130
  }
131
131
  });
@@ -201,17 +201,17 @@ class SocketCommon {
201
201
  return address;
202
202
  }
203
203
 
204
- // check if address is IPv4
204
+ // check if the address is IPv4
205
205
  const addressParts = address.split('.');
206
206
  if (addressParts.length !== 4) {
207
207
  return null;
208
208
  }
209
209
 
210
- // do we have settings for wild carded ips?
210
+ // do we have settings for wild-carded ips?
211
211
  const wildCardIps = Object.keys(whiteList).filter(key => key.includes('*'));
212
212
 
213
213
  if (!wildCardIps.length) {
214
- // no wild carded ips => no ip configured
214
+ // no wild-carded ips => no ip configured
215
215
  return null;
216
216
  }
217
217
 
@@ -253,7 +253,7 @@ class SocketCommon {
253
253
  });
254
254
 
255
255
  if (whiteListAcl.user !== 'auth') {
256
- acl.user = 'system.user.' + whiteListAcl.user;
256
+ acl.user = `system.user.${whiteListAcl.user}`;
257
257
  }
258
258
  }
259
259
  }
@@ -391,6 +391,10 @@ class SocketCommon {
391
391
  return this.commands.publish(socket, type, id, obj);
392
392
  }
393
393
 
394
+ publishInstanceMessage(socket, sourceInstance, messageType, data) {
395
+ return this.commands.publishInstanceMessage(socket, sourceInstance, messageType, data);
396
+ }
397
+
394
398
  publishFile(socket, id, fileName, size) {
395
399
  return this.commands.publishFile(socket, id, fileName, size);
396
400
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iobroker/socket-classes",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "ioBroker server-side web sockets",
5
5
  "author": {
6
6
  "name": "bluefox",
@@ -21,7 +21,7 @@
21
21
  "url": "https://github.com/ioBroker/ioBroker.socket-classes"
22
22
  },
23
23
  "dependencies": {
24
- "@iobroker/adapter-core": "^2.6.8",
24
+ "@iobroker/adapter-core": "^3.0.3",
25
25
  "passport": "^0.6.0",
26
26
  "cookie-parser": "^1.4.6",
27
27
  "axios": "^1.4.0"