@iobroker/socket-classes 1.1.5 → 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.
@@ -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] || [];
@@ -264,7 +279,13 @@ class SocketCommands {
264
279
  key = pattern;
265
280
  }
266
281
 
267
- p = pattern2RegEx(key);
282
+ try {
283
+ p = pattern2RegEx(key);
284
+ } catch (e) {
285
+ this.adapter.log.error(`Invalid pattern on subscribe: ${e.message}`)
286
+ return
287
+ }
288
+
268
289
  if (p === null) {
269
290
  return this.adapter.log.warn('Empty pattern on subscribe!');
270
291
  }
@@ -293,8 +314,8 @@ class SocketCommands {
293
314
  this.logEnabled = true;
294
315
  this.adapter.requireLog(true, options);
295
316
  }
296
- } else if (type === 'fileChange') {
297
- this.adapter.subscribeForeignFiles && this.adapter.subscribeForeignFiles(pattern, patternFile, options);
317
+ } else if (type === 'fileChange' && this.adapter.subscribeForeignFiles) {
318
+ this.adapter.subscribeForeignFiles(pattern, patternFile, options);
298
319
  }
299
320
  } else {
300
321
  this.subscribes[type][key]++;
@@ -358,7 +379,7 @@ class SocketCommands {
358
379
  }
359
380
  }
360
381
  } else if (key) {
361
- // Remove pattern from global list
382
+ // Remove a pattern from a global list
362
383
  if (this.subscribes[type][key] !== undefined) {
363
384
  this.subscribes[type][key]--;
364
385
  if (this.subscribes[type][key] <= 0) {
@@ -445,6 +466,8 @@ class SocketCommands {
445
466
  if (!socket || !socket.subscribe) {
446
467
  return;
447
468
  }
469
+ // inform all instances about disconnected socket
470
+ this._informAboutDisconnect(socket.id);
448
471
 
449
472
  if (!type) {
450
473
  // all
@@ -689,10 +712,10 @@ class SocketCommands {
689
712
 
690
713
  // commands
691
714
  this.commands['sendTo'] = (socket, adapterInstance, command, message, callback) => {
692
- // Send message to specific instance
715
+ // Send the message to specific instance
693
716
  // @param {string} adapterInstance - instance name, e.g. `history.0`
694
717
  // @param {string} command - command name
695
- // @param {object} message - message is instance dependent
718
+ // @param {object} message - the message is instance dependent
696
719
  // @param {function} callback - `function (result)`
697
720
  if (this._checkPermissions(socket, 'sendTo', callback, command)) {
698
721
  try {
@@ -709,11 +732,11 @@ class SocketCommands {
709
732
  const protectedCommands = ['cmdExec', 'getLocationOnDisk', 'getDiagData', 'getDevList', 'delLogs', 'writeDirAsZip', 'writeObjectsAsZip', 'readObjectsAsZip', 'checkLogging', 'updateMultihost', 'rebuildAdapter'];
710
733
 
711
734
  this.commands['sendToHost'] = (socket, host, command, message, callback) => {
712
- // Send message to specific host.
713
- // Host can answer following commands: `cmdExec, getRepository, getInstalled, getInstalledAdapter, getVersion, getDiagData, getLocationOnDisk, getDevList, getLogs, getHostInfo, delLogs, readDirAsZip, writeDirAsZip, readObjectsAsZip, writeObjectsAsZip, checkLogging, updateMultihost`.
735
+ // Send a message to the specific host.
736
+ // Host can answer to the following commands: `cmdExec, getRepository, getInstalled, getInstalledAdapter, getVersion, getDiagData, getLocationOnDisk, getDevList, getLogs, getHostInfo, delLogs, readDirAsZip, writeDirAsZip, readObjectsAsZip, writeObjectsAsZip, checkLogging, updateMultihost`.
714
737
  // @param {string} host - instance name, e.g. `history.0`
715
738
  // @param {string} command - command name
716
- // @param {object} message - message is command specific
739
+ // @param {object} message - the message is command-specific
717
740
  // @param {function} callback - `function (result)`
718
741
  if (this._checkPermissions(socket, protectedCommands.includes(command) ? 'cmdExec' : 'sendToHost', callback, command)) {
719
742
  // Try to decode this file locally as redis has a limitation for files bigger than 20MB
@@ -731,7 +754,7 @@ class SocketCommands {
731
754
  zipFiles
732
755
  .writeDirAsZip(
733
756
  this.adapter, // normally we have to pass here the internal "objects" object, but as
734
- // only writeFile is used, and it has the same name we can pass here the
757
+ // only writeFile is used, and it has the same name, we can pass here the
735
758
  // adapter, which has the function with the same name and arguments
736
759
  message.id,
737
760
  message.name,
@@ -757,7 +780,7 @@ class SocketCommands {
757
780
  };
758
781
 
759
782
  this.commands['authEnabled'] = (socket, callback) => {
760
- // Ask server is authentication enabled and if the user authenticated
783
+ // Ask server is authentication enabled, and if the user authenticated
761
784
  // @param {function} callback - `function (isAuthenticationUsed, userName)`
762
785
  if (this._checkPermissions(socket, 'authEnabled', callback)) {
763
786
  if (typeof callback === 'function') {
@@ -797,7 +820,7 @@ class SocketCommands {
797
820
  };
798
821
 
799
822
  this.commands['getVersion'] = (socket, callback) => {
800
- // Get adapter version. Not the socket-classes version!
823
+ // Get the adapter version. Not the socket-classes version!
801
824
  // @param {function} callback - `function (error, adapterVersion, adapterName)`
802
825
  if (this._checkPermissions(socket, 'getVersion', callback)) {
803
826
  if (typeof callback === 'function') {
@@ -826,7 +849,7 @@ class SocketCommands {
826
849
  this.commands['readFile'] = (socket, _adapter, fileName, callback) => {
827
850
  // Read file from ioBroker DB
828
851
  // @param {string} _adapter - instance name, e.g. `vis.0`
829
- // @param {string} fileName - file name, e.g `main/vis-views.json`
852
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
830
853
  // @param {function} callback - `function (error, data, mimeType)`
831
854
  if (this._checkPermissions(socket, 'readFile', callback, fileName)) {
832
855
  try {
@@ -842,7 +865,7 @@ class SocketCommands {
842
865
  this.commands['readFile64'] = (socket, _adapter, fileName, callback) => {
843
866
  // Read file from ioBroker DB as base64 string
844
867
  // @param {string} _adapter - instance name, e.g. `vis.0`
845
- // @param {string} fileName - file name, e.g `main/vis-views.json`
868
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
846
869
  // @param {function} callback - `function (error, base64, mimeType)`
847
870
  if (this._checkPermissions(socket, 'readFile64', callback, fileName)) {
848
871
  try {
@@ -881,7 +904,7 @@ class SocketCommands {
881
904
  this.commands['writeFile64'] = (socket, _adapter, fileName, data64, options, callback) => {
882
905
  // Write file into ioBroker DB as base64 string
883
906
  // @param {string} _adapter - instance name, e.g. `vis.0`
884
- // @param {string} fileName - file name, e.g `main/vis-views.json`
907
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
885
908
  // @param {string} data64 - file content as base64 string
886
909
  // @param {object} options - optional `{mode: 0x0644}`
887
910
  // @param {function} callback - `function (error)`
@@ -914,7 +937,7 @@ class SocketCommands {
914
937
  this.commands['writeFile'] = (socket, _adapter, fileName, data, options, callback) => {
915
938
  // Write file into ioBroker DB as text **DEPRECATED**
916
939
  // @param {string} _adapter - instance name, e.g. `vis.0`
917
- // @param {string} fileName - file name, e.g `main/vis-views.json`
940
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
918
941
  // @param {string} data64 - file content as base64 string
919
942
  // @param {object} options - optional `{mode: 0x644}`
920
943
  // @param {function} callback - `function (error)`
@@ -940,7 +963,7 @@ class SocketCommands {
940
963
  this.commands['unlink'] = (socket, _adapter, name, callback) => {
941
964
  // Delete file in ioBroker DB
942
965
  // @param {string} _adapter - instance name, e.g. `vis.0`
943
- // @param {string} name - file name, e.g `main/vis-views.json`
966
+ // @param {string} name - file name, e.g. `main/vis-views.json`
944
967
  // @param {function} callback - `function (error)`
945
968
  if (this._checkPermissions(socket, 'unlink', callback, name)) {
946
969
  try {
@@ -957,7 +980,7 @@ class SocketCommands {
957
980
  this.commands['deleteFile'] = (socket, _adapter, name, callback) => {
958
981
  // Delete file in ioBroker DB (same as unlink, but only for files)
959
982
  // @param {string} _adapter - instance name, e.g. `vis.0`
960
- // @param {string} name - file name, e.g `main/vis-views.json`
983
+ // @param {string} name - file name, e.g. `main/vis-views.json`
961
984
  // @param {function} callback - `function (error)`
962
985
  if (this._checkPermissions(socket, 'unlink', callback, name)) {
963
986
  try {
@@ -973,7 +996,7 @@ class SocketCommands {
973
996
  this.commands['deleteFolder'] = (socket, _adapter, name, callback) => {
974
997
  // Delete file in ioBroker DB (same as unlink, but only for folders)
975
998
  // @param {string} _adapter - instance name, e.g. `vis.0`
976
- // @param {string} name - folder name, e.g `main`
999
+ // @param {string} name - folder name, e.g. `main`
977
1000
  // @param {function} callback - `function (error)`
978
1001
  if (this._checkPermissions(socket, 'unlink', callback, name)) {
979
1002
  try {
@@ -990,8 +1013,8 @@ class SocketCommands {
990
1013
  this.commands['renameFile'] = (socket, _adapter, oldName, newName, callback) => {
991
1014
  // Rename file in ioBroker DB
992
1015
  // @param {string} _adapter - instance name, e.g. `vis.0`
993
- // @param {string} oldName - current file name, e.g `main/vis-views.json`
994
- // @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`
995
1018
  // @param {function} callback - `function (error)`
996
1019
  if (this._checkPermissions(socket, 'rename', callback, oldName)) {
997
1020
  try {
@@ -1007,8 +1030,8 @@ class SocketCommands {
1007
1030
  this.commands['rename'] = (socket, _adapter, oldName, newName, callback) => {
1008
1031
  // Rename file or folder in ioBroker DB
1009
1032
  // @param {string} _adapter - instance name, e.g. `vis.0`
1010
- // @param {string} oldName - current file name, e.g `main/vis-views.json`
1011
- // @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`
1012
1035
  // @param {function} callback - `function (error)`
1013
1036
  if (this._checkPermissions(socket, 'rename', callback, oldName)) {
1014
1037
  try {
@@ -1025,7 +1048,7 @@ class SocketCommands {
1025
1048
  this.commands['mkdir'] = (socket, _adapter, dirName, callback) => {
1026
1049
  // Create folder in ioBroker DB
1027
1050
  // @param {string} _adapter - instance name, e.g. `vis.0`
1028
- // @param {string} dirName - desired folder name, e.g `main`
1051
+ // @param {string} dirName - desired folder name, e.g. `main`
1029
1052
  // @param {function} callback - `function (error)`
1030
1053
  if (this._checkPermissions(socket, 'mkdir', callback, dirName)) {
1031
1054
  try {
@@ -1041,7 +1064,7 @@ class SocketCommands {
1041
1064
  this.commands['readDir'] = (socket, _adapter, dirName, options, callback) => {
1042
1065
  // Read content of folder in ioBroker DB
1043
1066
  // @param {string} _adapter - instance name, e.g. `vis.0`
1044
- // @param {string} dirName - folder name, e.g `main`
1067
+ // @param {string} dirName - folder name, e.g. `main`
1045
1068
  // @param {object} options - optional `{filter: '*'}` or `{filter: '*.json'}`
1046
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}`
1047
1070
  if (typeof options === 'function') {
@@ -1069,8 +1092,8 @@ class SocketCommands {
1069
1092
  this.commands['chmodFile'] = (socket, _adapter, fileName, options, callback) => {
1070
1093
  // Change file mode in ioBroker DB
1071
1094
  // @param {string} _adapter - instance name, e.g. `vis.0`
1072
- // @param {string} fileName - file name, e.g `main/vis-views.json`
1073
- // @param {object} options - `{mode: 0x644}` or 0x644. First digit is user, second group, third others. Bit 1 is `execute`, bit 2 is `write`, bit 3 is `read`
1095
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
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`
1074
1097
  // @param {function} callback - `function (error)`
1075
1098
  if (typeof options === 'function') {
1076
1099
  callback = options;
@@ -1097,7 +1120,7 @@ class SocketCommands {
1097
1120
  this.commands['chownFile'] = (socket, _adapter, fileName, options, callback) => {
1098
1121
  // Change file owner in ioBroker DB
1099
1122
  // @param {string} _adapter - instance name, e.g. `vis.0`
1100
- // @param {string} fileName - file name, e.g `main/vis-views.json`
1123
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
1101
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.
1102
1125
  // @param {function} callback - `function (error)`
1103
1126
  if (this._checkPermissions(socket, 'chownFile', callback, fileName)) {
@@ -1114,9 +1137,9 @@ class SocketCommands {
1114
1137
  };
1115
1138
 
1116
1139
  this.commands['fileExists'] = (socket, _adapter, fileName, callback) => {
1117
- // Checks if the file or folder exists in ioBroker DB
1140
+ // Check if the file or folder exists in ioBroker DB
1118
1141
  // @param {string} _adapter - instance name, e.g. `vis.0`
1119
- // @param {string} fileName - file name, e.g `main/vis-views.json`
1142
+ // @param {string} fileName - file name, e.g. `main/vis-views.json`
1120
1143
  // @param {function} callback - `function (error, isExist)`
1121
1144
  if (this._checkPermissions(socket, 'fileExists', callback, fileName)) {
1122
1145
  try {
@@ -1130,17 +1153,17 @@ class SocketCommands {
1130
1153
  };
1131
1154
 
1132
1155
  this.commands['subscribeFiles'] = (socket, id, pattern, callback) => {
1133
- // Subscribe on file changes in ioBroker DB
1156
+ // Subscribe to file changes in ioBroker DB
1134
1157
  // @param {string} id - instance name, e.g. `vis.0` or any object ID of type `meta`. `id` could have wildcards `*` too.
1135
- // @param {string} pattern - file name pattern, e.g `main/*.json`
1158
+ // @param {string} pattern - file name pattern, e.g. `main/*.json`
1136
1159
  // @param {function} callback - `function (error)`
1137
1160
  return this._subscribeFiles(socket, id, pattern, callback);
1138
1161
  }
1139
1162
 
1140
1163
  this.commands['unsubscribeFiles'] = (socket, id, pattern, callback) => {
1141
- // Unsubscribe on file changes in ioBroker DB
1164
+ // Unsubscribe from file changes in ioBroker DB
1142
1165
  // @param {string} id - instance name, e.g. `vis.0` or any object ID of type `meta`. `id` could have wildcards `*` too.
1143
- // @param {string} pattern - file name pattern, e.g `main/*.json`
1166
+ // @param {string} pattern - file name pattern, e.g. `main/*.json`
1144
1167
  // @param {function} callback - `function (error)`
1145
1168
 
1146
1169
  return this._unsubscribeFiles(socket, id, pattern, callback);
@@ -1148,7 +1171,7 @@ class SocketCommands {
1148
1171
 
1149
1172
  this.commands['getAdapterInstances'] = (socket, adapterName, callback) => {
1150
1173
  // Read all instances of the given adapter, or all instances of all adapters if adapterName is not defined
1151
- // @param {string} adapterName - optional adapter name, e.g `history`.
1174
+ // @param {string} adapterName - optional adapter name, e.g. `history`.
1152
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: {...}}`
1153
1176
  if (typeof callback === 'function') {
1154
1177
  if (this._checkPermissions(socket, 'getObject', callback)) {
@@ -1212,7 +1235,7 @@ class SocketCommands {
1212
1235
 
1213
1236
  this.commands['getForeignStates'] = (socket, pattern, callback) => {
1214
1237
  // Read all states (which might not belong to this adapter) which match the given pattern
1215
- // @param {string} pattern - pattern like
1238
+ // @param {string} pattern - pattern like `system.adapter.*` or array of state IDs
1216
1239
  // @param {function} callback - `function (error)`
1217
1240
  if (this._checkPermissions(socket, 'getStates', callback)) {
1218
1241
  if (typeof callback === 'function') {
@@ -1320,7 +1343,7 @@ class SocketCommands {
1320
1343
  this.commands['setBinaryState'] = (socket, id, base64, callback) => {
1321
1344
  // Write one binary state.
1322
1345
  // @param {string} id - State ID like, 'javascript.0.binary'
1323
- // @param {string} base64 - State value as base64 string. Binary states has no acknowledge flag.
1346
+ // @param {string} base64 - State value as base64 string. Binary states have no acknowledged flag.
1324
1347
  // @param {function} callback - `function (error)`
1325
1348
  if (this._checkPermissions(socket, 'setState', callback, id)) {
1326
1349
  if (typeof callback === 'function') {
@@ -1350,14 +1373,14 @@ class SocketCommands {
1350
1373
  };
1351
1374
 
1352
1375
  this.commands['subscribe'] = (socket, pattern, callback) => {
1353
- // Subscribe on state changes by pattern. The events will come as 'stateChange' events to the socket.
1376
+ // Subscribe to state changes by pattern. The events will come as 'stateChange' events to the socket.
1354
1377
  // @param {string} pattern - pattern like 'system.adapter.*' or array of states like ['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']
1355
1378
  // @param {function} callback - `function (error)`
1356
1379
  return this._subscribeStates(socket, pattern, callback);
1357
1380
  };
1358
1381
 
1359
1382
  this.commands['subscribeStates'] = (socket, pattern, callback) => {
1360
- // Subscribe on state changes by pattern. Same as `subscribe`. The events will come as 'stateChange' events to the socket.
1383
+ // Subscribe to state changes by pattern. Same as `subscribe`. The events will come as 'stateChange' events to the socket.
1361
1384
  // @param {string} pattern - pattern like 'system.adapter.*' or array of states like ['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']
1362
1385
  // @param {function} callback - `function (error)`
1363
1386
  return this._subscribeStates(socket, pattern, callback);
@@ -1401,11 +1424,30 @@ class SocketCommands {
1401
1424
 
1402
1425
  // not admin version of "all objects"
1403
1426
  // this function is overloaded in admin
1404
- this.commands['getObjects'] = (socket, callback) => {
1427
+ this.commands['getObjects'] = (socket, list, callback) => {
1405
1428
  // Get all objects that are relevant for web: all states and enums with rooms
1406
1429
  // @param {string} id - object ID.
1430
+ // @param {string[]} list - optional list of IDs.
1407
1431
  // @param {function} callback - `function (error, obj)`
1408
- if (this._checkPermissions(socket, 'getObjects', callback)) {
1432
+ if (typeof list === 'function') {
1433
+ callback = list;
1434
+ list = null;
1435
+ }
1436
+ if (list && list.length) {
1437
+ if (this._checkPermissions(socket, 'getObject', callback)) {
1438
+ if (typeof callback === 'function') {
1439
+ try {
1440
+ this.adapter.getForeignObjects(list, {user: socket._acl.user}, (error, objs) =>
1441
+ SocketCommands._fixCallback(callback, error, objs));
1442
+ } catch (error) {
1443
+ this.adapter.log.error(`[getObjects] ERROR: ${error.toString()}`);
1444
+ SocketCommands._fixCallback(callback, error);
1445
+ }
1446
+ } else {
1447
+ this.adapter.log.warn('[getObjects] Invalid callback');
1448
+ }
1449
+ }
1450
+ } else if (this._checkPermissions(socket, 'getObjects', callback)) {
1409
1451
  try {
1410
1452
  if (typeof callback === 'function') {
1411
1453
  this.adapter.getForeignObjects('*', 'state', 'rooms', {user: socket._acl.user}, async (error, objs) => {
@@ -1437,7 +1479,7 @@ class SocketCommands {
1437
1479
  };
1438
1480
 
1439
1481
  this.commands['subscribeObjects'] = (socket, pattern, callback) => {
1440
- // Subscribe on object changes by pattern. The events will come as 'objectChange' events to the socket.
1482
+ // Subscribe to object changes by pattern. The events will come as 'objectChange' events to the socket.
1441
1483
  // @param {string} pattern - pattern like 'system.adapter.*' or array of IDs like ['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']
1442
1484
  // @param {function} callback - `function (error)`
1443
1485
  if (this._checkPermissions(socket, 'subscribeObjects', callback, pattern)) {
@@ -1461,7 +1503,7 @@ class SocketCommands {
1461
1503
  };
1462
1504
 
1463
1505
  this.commands['unsubscribeObjects'] = (socket, pattern, callback) => {
1464
- // Unsubscribe on object changes by pattern.
1506
+ // Unsubscribe from object changes by pattern.
1465
1507
  // @param {string} pattern - pattern like 'system.adapter.*' or array of IDs like ['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']
1466
1508
  // @param {function} callback - `function (error)`
1467
1509
  if (this._checkPermissions(socket, 'unsubscribeObjects', callback, pattern)) {
@@ -1540,6 +1582,48 @@ class SocketCommands {
1540
1582
  SocketCommands._fixCallback(callback, SocketCommands.ERROR_PERMISSION);
1541
1583
  }
1542
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
+ };
1543
1627
  }
1544
1628
 
1545
1629
  _initCommands() {
@@ -1549,6 +1633,20 @@ class SocketCommands {
1549
1633
  this.__initCommandsFiles();
1550
1634
  }
1551
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
+
1552
1650
  applyCommands(socket) {
1553
1651
  Object.keys(this.commands)
1554
1652
  .forEach(command => socket.on(command, (...args) => {