node-red-contrib-mobius-flow-base 2.5.86-beta.1 → 2.5.86-beta.101

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.
Files changed (39) hide show
  1. package/dist/nodes/icons/mobius-inject.png +0 -0
  2. package/dist/nodes/icons/mobius.png +0 -0
  3. package/dist/nodes/mobius-clear-resource.html +178 -0
  4. package/dist/nodes/mobius-clear-resource.js +109 -0
  5. package/dist/nodes/mobius-connection.html +68 -0
  6. package/dist/nodes/mobius-connection.js +675 -0
  7. package/dist/nodes/mobius-discover-object-list.html +272 -0
  8. package/dist/nodes/mobius-discover-object-list.js +263 -0
  9. package/dist/nodes/mobius-discover.html +139 -0
  10. package/dist/nodes/mobius-discover.js +100 -0
  11. package/dist/nodes/mobius-filter-by-rid-value.html +206 -0
  12. package/dist/nodes/mobius-filter-by-rid-value.js +123 -0
  13. package/dist/nodes/mobius-flow-inject.html +162 -0
  14. package/dist/nodes/mobius-flow-inject.js +124 -0
  15. package/dist/nodes/mobius-get-multi-object.html +176 -0
  16. package/dist/nodes/mobius-get-multi-object.js +127 -0
  17. package/dist/nodes/mobius-get-multi-resource.html +195 -0
  18. package/dist/nodes/mobius-get-multi-resource.js +140 -0
  19. package/dist/nodes/mobius-get-object.html +168 -0
  20. package/dist/nodes/mobius-get-object.js +102 -0
  21. package/dist/nodes/mobius-get-resource.html +169 -0
  22. package/dist/nodes/mobius-get-resource.js +101 -0
  23. package/dist/nodes/mobius-object-cov.html +171 -0
  24. package/dist/nodes/mobius-object-cov.js +83 -0
  25. package/dist/nodes/mobius-resource-cov.html +155 -0
  26. package/dist/nodes/mobius-resource-cov.js +77 -0
  27. package/dist/nodes/mobius-send-bcmd.html +93 -0
  28. package/dist/nodes/mobius-send-bcmd.js +61 -0
  29. package/dist/nodes/mobius-send-dcmd.html +130 -0
  30. package/dist/nodes/mobius-send-dcmd.js +80 -0
  31. package/dist/nodes/mobius-set-multi-resource.html +150 -0
  32. package/dist/nodes/mobius-set-multi-resource.js +104 -0
  33. package/dist/nodes/mobius-set-resource.html +180 -0
  34. package/dist/nodes/mobius-set-resource.js +145 -0
  35. package/dist/nodes/mobius-sub-bcmd.html +94 -0
  36. package/dist/nodes/mobius-sub-bcmd.js +64 -0
  37. package/dist/nodes/node-red-ui-discover-devices.js +103 -0
  38. package/dist/nodes/validateURI.js +51 -0
  39. package/package.json +1 -1
@@ -0,0 +1,675 @@
1
+ /**
2
+ Copyright (C) 2016, IAConnects Technology Limited
3
+ All Rights Reserved
4
+
5
+ ALL INFORMATION CONTAINED HEREIN IS, AND REMAINS
6
+ THE PROPERTY OF IACONNECTS TECHNOLOGY LIMITED AND ITS SUPPLIERS,
7
+ IF ANY. THE INTELLECTUAL AND TECHNICAL CONCEPTS CONTAINED
8
+ HEREIN ARE PROPRIETARY TO IACONNECTS TECHNOLOGY LIMITED
9
+ AND ITS SUPPLIERS AND ARE PROTECTED BY TRADE SECRET OR COPYRIGHT LAW.
10
+ DISSEMINATION OF THIS INFORMATION OR REPRODUCTION OF THIS MATERIAL
11
+ IS STRICTLY FORBIDDEN UNLESS PRIOR WRITTEN PERMISSION IS OBTAINED
12
+ FROM IACONNECTS TECHNOLOGY LIMITED.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
18
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ POSSIBILITY OF SUCH DAMAGE.
25
+ **/
26
+
27
+ 'use strict';
28
+
29
+ var _ = require('lodash');
30
+ var tcpAPI = require('@ia/mobius-tcpip-service-api');
31
+ var uiDeviceDiscovery = require('./node-red-ui-discover-devices');
32
+
33
+ module.exports = function (RED) {
34
+ /**
35
+ * Helper function to match topic subscriptions
36
+ * @param {string} topic - The subscribed topic
37
+ * @param {string} messageTopic - The topic in an actual message
38
+ * @return {boolean} - True if the topics match, else false
39
+ */
40
+ function matchTopic(topic, messageTopic) {
41
+ if (topic === '#') {
42
+ return true;
43
+ }
44
+ // eslint-disable-next-line no-useless-escape
45
+ var regex = new RegExp(
46
+ '^' +
47
+ topic
48
+ .replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g, '\\$1')
49
+ // eslint-disable-next-line no-useless-escape
50
+ .replace(/\+/g, '[^/]+')
51
+ .replace(/\/#$/, '(/.*)?') +
52
+ '$'
53
+ );
54
+ return regex.test(messageTopic);
55
+ }
56
+
57
+ /**
58
+ * Constructor for a Mobius service configuration node
59
+ * @param {Object} n - The node being created
60
+ */
61
+ function MobiusConnectionNode(n) {
62
+ RED.nodes.createNode(this, n);
63
+ this.host = n.host;
64
+ this.port = n.port;
65
+ this.psk = n.psk;
66
+ this.service = new tcpAPI.TCPIPServiceAPI(n.host, n.port, n.psk);
67
+ this.service.srvcAPI.removeAllListeners('cov');
68
+ this.service.srvcAPI.removeAllListeners('obcov');
69
+ this.service.srvcAPI.removeAllListeners('bcmd');
70
+ this.service.srvcAPI.on('cov', (response) => {
71
+ this.handleCOVEvents(response);
72
+ });
73
+ this.service.srvcAPI.on('obcov', (response) => {
74
+ this.handleObjectCOVEvents(response);
75
+ });
76
+ this.service.srvcAPI.on('bcmd', (response) => {
77
+ this.handleBCMDEvents(response);
78
+ });
79
+ var node = this;
80
+ node.serviceList = [];
81
+ node.hasStarted = false;
82
+ node.users = {};
83
+ node.resourceSubscriptions = new Map();
84
+ node.objectSubscriptions = new Map();
85
+ node.bcmdSubscriptions = new Map();
86
+ node.connected = false;
87
+ node.unsubscribeTimeout = 0;
88
+
89
+ // Connected to the mobius TCPIP spoke
90
+ // Update the status indicators
91
+ node.service.on('connected', () => {
92
+ this.log(`Connected to ${n.host} : ${n.port}`);
93
+ node.connected = true;
94
+ _.forEach(node.users, (user, id) => {
95
+ if (node.users.hasOwnProperty(id)) {
96
+ node.users[id].status({
97
+ fill: 'green',
98
+ shape: 'dot',
99
+ text: 'Connected',
100
+ });
101
+ }
102
+ });
103
+ // Subscribe to all COVs
104
+ this.resourceSubscriptions.forEach((subs, key) => {
105
+ node.service.srvcAPI.subscribeToCOV(key);
106
+ });
107
+ // Subscribe to all object COVs
108
+ node.objectSubscriptions.forEach((subs, key) => {
109
+ node.service.srvcAPI.subscribeToObjectCOV(key);
110
+ });
111
+ // Subscribe to all BCMDs
112
+ node.bcmdSubscriptions.forEach((subs, key) => {
113
+ node.service.srvcAPI.subscribeToBCMD(key);
114
+ });
115
+ // Discover all service IDs
116
+ node.service.srvcAPI.discover(node.service.srvcAPI.status.hubId, (err, response) => {
117
+ if (err) {
118
+ node.error('Mobius error discovering service list' + JSON.stringify(err));
119
+ } else {
120
+ node.serviceList = response.value;
121
+ node.hasStarted = true;
122
+ }
123
+ });
124
+ });
125
+
126
+ // Disconnected from the mobius TCPIP spoke
127
+ // Update the status indicators
128
+ node.service.on('disconnected', () => {
129
+ this.log(`Disconnected from ${n.host} : ${n.port}`);
130
+ node.connected = false;
131
+ for (var id in node.users) {
132
+ if (node.users.hasOwnProperty(id)) {
133
+ node.users[id].status({
134
+ fill: 'red',
135
+ shape: 'ring',
136
+ text: 'Disconnected',
137
+ });
138
+ }
139
+ }
140
+ });
141
+
142
+ /**
143
+ * Handle all resource COVs
144
+ * @param {Object} response - The resource that has changed
145
+ */
146
+ node.handleCOVEvents = function (response) {
147
+ let covsFired = [];
148
+ node.resourceSubscriptions.forEach((subs, key) => {
149
+ if (matchTopic(key, response.uri)) {
150
+ _.forEach(subs, (sub) => {
151
+ if (!_.includes(covsFired, sub.node)) {
152
+ covsFired.push(sub.node);
153
+ sub.cb(response);
154
+ }
155
+ });
156
+ }
157
+ });
158
+ };
159
+
160
+ /**
161
+ * Handle all object COVs
162
+ * @param {Object} response - The object that has changed
163
+ */
164
+ node.handleObjectCOVEvents = function (response) {
165
+ let covsFired = [];
166
+ node.objectSubscriptions.forEach((subs, key) => {
167
+ if (matchTopic(key, response.uri)) {
168
+ _.forEach(subs, (sub) => {
169
+ if (!_.includes(covsFired, sub.node)) {
170
+ covsFired.push(sub.node);
171
+ sub.cb(response);
172
+ }
173
+ });
174
+ }
175
+ });
176
+ };
177
+
178
+ /**
179
+ * Handle all BCMDs
180
+ * @param {Object} response - The BCMD
181
+ */
182
+ node.handleBCMDEvents = function (response) {
183
+ let bcmdsFired = [];
184
+ node.bcmdSubscriptions.forEach((subs, key) => {
185
+ if (matchTopic(key, response.uri)) {
186
+ _.forEach(subs, (sub) => {
187
+ if (!_.includes(bcmdsFired, sub.node)) {
188
+ bcmdsFired.push(sub.node);
189
+ sub.cb(response);
190
+ }
191
+ });
192
+ }
193
+ });
194
+ };
195
+
196
+ /**
197
+ * Select status text and colour of node status based on last active time
198
+ * @param {number} lastActiveTime - The node's last active time
199
+ * @returns Object containing status text and status colour
200
+ */
201
+ node.buildLastActiveTimeStatus = function (lastActiveTime) {
202
+ let response = {
203
+ text: 'Unknown',
204
+ colour: 'green',
205
+ };
206
+ const timeSinceLastActive = Date.now() - lastActiveTime;
207
+ if (timeSinceLastActive < 15 * 60000) {
208
+ response.text = 'Active less than 15 minutes ago';
209
+ }
210
+ if (timeSinceLastActive >= 15 * 60000 && timeSinceLastActive < 3 * 60 * 60000) {
211
+ response.text = 'Active less than 3 hours ago';
212
+ }
213
+ if (timeSinceLastActive >= 3 * 60 * 60000 && timeSinceLastActive < 12 * 60 * 60000) {
214
+ response.text = 'Active less than 12 hours ago';
215
+ }
216
+ if (timeSinceLastActive >= 12 * 60 * 60000 && timeSinceLastActive < 24 * 60 * 60000) {
217
+ response.text = 'Active less than 24 hours ago';
218
+ }
219
+ if (timeSinceLastActive > 24 * 60 * 60000 && lastActiveTime > 0) {
220
+ response.colour = 'grey';
221
+ response.text = 'Active more than 24 hours ago';
222
+ }
223
+ if (lastActiveTime === 0) {
224
+ response.colour = 'grey';
225
+ response.text = 'Never Active';
226
+ }
227
+ return {
228
+ fill: response.colour,
229
+ shape: 'dot',
230
+ text: response.text,
231
+ };
232
+ };
233
+
234
+ /**
235
+ * Update a given node's node status based on last active time
236
+ * @param {Object} mobiusNode - The node whos status is to be updated
237
+ */
238
+ node.updateNodeStatus = function (mobiusNode) {
239
+ node.users[mobiusNode.id] = mobiusNode;
240
+ let newStatus = {
241
+ fill: 'green',
242
+ shape: 'dot',
243
+ text: 'Connected',
244
+ };
245
+ switch (node.users[mobiusNode.id].statusType) {
246
+ case 'timeSinceLastActive':
247
+ newStatus = node.buildLastActiveTimeStatus(node.users[mobiusNode.id].lastActiveTime);
248
+ case 'lastUri':
249
+ const uri = node.users[mobiusNode.id].latestUri;
250
+ if (uri !== '') {
251
+ newStatus = {
252
+ fill: 'green',
253
+ shape: 'dot',
254
+ text: uri,
255
+ };
256
+ }
257
+ }
258
+ node.users[mobiusNode.id].status(newStatus);
259
+ };
260
+
261
+ /**
262
+ * Update a given node's node latest URI record
263
+ * @param {Object} mobiusNode - The node whos last URI record is to be updated
264
+ */
265
+ node.updateLatestUri = function (mobiusNode, uri) {
266
+ node.users[mobiusNode.id] = mobiusNode;
267
+ node.users[mobiusNode.id].latestUri = uri;
268
+ node.updateNodeStatus(mobiusNode);
269
+ };
270
+
271
+ /**
272
+ * Update a given node's node last active time
273
+ * @param {Object} mobiusNode - The node whos last active time is to be updated
274
+ */
275
+ node.updateLastActiveTime = function (mobiusNode) {
276
+ node.users[mobiusNode.id] = mobiusNode;
277
+ node.users[mobiusNode.id].lastActiveTime = Date.now();
278
+ node.updateNodeStatus(mobiusNode);
279
+ };
280
+
281
+ /**
282
+ * Register a new Mobius node with this config node
283
+ * @param {Object} mobiusNode - The new node to register
284
+ */
285
+ node.register = function (mobiusNode) {
286
+ node.users[mobiusNode.id] = mobiusNode;
287
+ if (node.connected) {
288
+ node.users[mobiusNode.id].status({
289
+ fill: 'green',
290
+ shape: 'dot',
291
+ text: 'Connected',
292
+ });
293
+ }
294
+ node.users[mobiusNode.id].lastActiveTime = 0;
295
+ node.users[mobiusNode.id].statusCheckLoop = setInterval(() => {
296
+ if (node.connected) {
297
+ node.updateNodeStatus(mobiusNode);
298
+ }
299
+ }, 60000);
300
+ };
301
+
302
+ /**
303
+ * Deregister a previously registered Mobius node
304
+ * @param {[Object} mobiusNode - The node to deregister
305
+ * @param {Function} done - A callback to be run by the node after it has been deregistered
306
+ */
307
+ node.deregister = function (mobiusNode, done) {
308
+ clearInterval(node.users[mobiusNode.id].statusCheckLoop);
309
+ delete node.users[mobiusNode.id];
310
+ if (node.service) {
311
+ try {
312
+ node.resourceSubscriptions.forEach((subs, key) => {
313
+ subs.forEach((sub, index) => {
314
+ if (mobiusNode.id === sub.node) {
315
+ subs.splice(index, 1);
316
+ }
317
+ });
318
+ if (subs.length === 0) {
319
+ node.service.srvcAPI.unsubscribeToCOV(key);
320
+ node.resourceSubscriptions.delete(key);
321
+ }
322
+ });
323
+ node.objectSubscriptions.forEach((subs, key) => {
324
+ subs.forEach((sub, index) => {
325
+ if (mobiusNode.id === sub.node) {
326
+ subs.splice(index, 1);
327
+ }
328
+ });
329
+ if (subs.length === 0) {
330
+ node.service.srvcAPI.unsubscribeToObjectCOV(key);
331
+ node.objectSubscriptions.delete(key);
332
+ }
333
+ });
334
+ node.bcmdSubscriptions.forEach((subs, key) => {
335
+ subs.forEach((sub, index) => {
336
+ if (mobiusNode.id === sub.node) {
337
+ subs.splice(index, 1);
338
+ }
339
+ });
340
+ if (subs.length === 0) {
341
+ node.service.srvcAPI.unsubscribeToBCMD(key);
342
+ node.bcmdSubscriptions.delete(key);
343
+ }
344
+ });
345
+ } catch (err) {
346
+ console.log(`Failed to unsubscribe from event: ${err.message}`);
347
+ }
348
+ return done();
349
+ }
350
+ done();
351
+ };
352
+
353
+ /**
354
+ * Register the close connection handler
355
+ */
356
+ node.on('close', (done) => {
357
+ if (node.connected) {
358
+ // Wait for all nodes to unsubscribe before terminating the connection
359
+ node.unsubscribeTimeout = 0;
360
+ var waitForCleanup = setInterval(() => {
361
+ if (
362
+ (node.resourceSubscriptions.size === 0 &&
363
+ node.objectSubscriptions.size === 0 &&
364
+ node.bcmdSubscriptions.size === 0) ||
365
+ node.unsubscribeTimeout > 50
366
+ ) {
367
+ clearInterval(waitForCleanup);
368
+ node.service.end();
369
+ node.service.once('disconnected', () => {
370
+ node.connected = false;
371
+ done();
372
+ });
373
+ }
374
+ node.unsubscribeTimeout++;
375
+ }, 100);
376
+ } else {
377
+ node.service.end();
378
+ done();
379
+ }
380
+ });
381
+
382
+ /**
383
+ * Discover the services on a hub, the object profiles used by a service or the instances of
384
+ * objects of a profile type.
385
+ * @param {string} uri - The identifier of the hub, service or profile
386
+ * @param {Function} callback - function(err, [object]) {}
387
+ * @return {string} - A random unique request response ID. This is also returned in
388
+ * the response message event
389
+ */
390
+ node.discover = function (uri, callback) {
391
+ node.service.srvcAPI.discover(uri, callback);
392
+ };
393
+
394
+ /**
395
+ * Write a value to a resource
396
+ * @param {string} uri - The full URI of the resource
397
+ * @param {Object} value - The value to write
398
+ * @param {number} priority - The priority to write to
399
+ * @param {Function} callback - Callback when complete
400
+ */
401
+ node.writeResourceValue = function (uri, value, priority, callback) {
402
+ node.service.srvcAPI.writeResourceValue(uri, value, priority, callback);
403
+ };
404
+
405
+ /**
406
+ * Write a value to multiple resources of a single object in a microservice
407
+ * Values can be anything as long as its type matches the resource's value type
408
+ * @param {string} uri - The object identifier
409
+ * @param {Object} updates - An array containing all of the resources to be updated
410
+ * @param {Function} callback - function(err, [object]) {}
411
+ * @return {string} - A random unique request response ID. This is also returned in
412
+ * the response message event
413
+ */
414
+ node.multiWriteResourceValue = function (uri, updates, callback) {
415
+ node.service.srvcAPI.multiWriteResourceValue(uri, updates, callback);
416
+ };
417
+
418
+ /**
419
+ * Clear a resource's priority
420
+ * @param {string} uri - The full URI of the resource
421
+ * @param {number} priority - The priority to clear
422
+ * @param {Function} callback - Callback when complete
423
+ */
424
+ node.clearResourcePriority = function (uri, priority, callback) {
425
+ node.service.srvcAPI.clearResourcePriority(uri, priority, callback);
426
+ };
427
+
428
+ /**
429
+ * Read a resource's value
430
+ * @param {string} uri - The full URI of the resource
431
+ * @param {Function} callback - Callback when complete
432
+ */
433
+ node.readResource = function (uri, callback) {
434
+ node.service.srvcAPI.readResource(uri, callback);
435
+ };
436
+
437
+ /**
438
+ * Read a resource's value
439
+ * @param {string} uri - The full URI of the object
440
+ * @param {Function} callback - Callback when complete
441
+ */
442
+ node.readObject = function (uri, callback) {
443
+ node.service.srvcAPI.readObject(uri, callback);
444
+ };
445
+
446
+ /**
447
+ * Filter objects by resource value
448
+ * @param {string} uri - The resource identifier
449
+ * @param {array} pids - The profile ids to search (as an array of four digit hex string)
450
+ * @param {string} rid - The id of the resource to test values against (as a two digit hex string)
451
+ * @param {number} limit - The maximum number of objects to return
452
+ * @param {Function} func - The function used to test the resource values
453
+ * @param {Function} callback - function(err, [object]) {}
454
+ */
455
+ node.filterObjectsByRIDValue = function (uri, pids, rid, limit, func, callback) {
456
+ node.service.srvcAPI.filterObjectsByRIDValue(uri, pids, rid, limit, func, callback);
457
+ };
458
+
459
+ /**
460
+ * Subscribe to a resource COV
461
+ * @param {string} uri - The full URI of the resource
462
+ * @param {Object} ref - The ID of the node that is registering for this COV
463
+ * @param {Function} callback - Callback when complete
464
+ */
465
+ node.subscribeToCOV = function (uri, ref, callback) {
466
+ let sub = {
467
+ node: ref,
468
+ cb: callback,
469
+ };
470
+ if (!node.resourceSubscriptions.has(uri)) {
471
+ node.service.srvcAPI.subscribeToCOV(uri);
472
+ node.resourceSubscriptions.set(uri, [sub]);
473
+ } else {
474
+ // _.pullAllBy(node.resourceSubscriptions.get(uri), [{node: ref}], node);
475
+ node.resourceSubscriptions.get(uri).push(sub);
476
+ }
477
+ };
478
+
479
+ /**
480
+ * Unsubscribe to a resource COV
481
+ * @param {string} uri - The full URI of the resource
482
+ * @param {Object} ref - The ID of the node that is registering for this COV
483
+ */
484
+ node.unsubscribeToCOV = function (uri, ref) {
485
+ if (node.resourceSubscriptions.has(uri)) {
486
+ node.resourceSubscriptions.set(
487
+ uri,
488
+ node.resourceSubscriptions.get(uri).filter((sub) => sub.node !== ref)
489
+ );
490
+ }
491
+ if (node.resourceSubscriptions.get(uri).length === 0) {
492
+ node.service.srvcAPI.unsubscribeToCOV(uri);
493
+ node.resourceSubscriptions.delete(uri);
494
+ }
495
+ };
496
+
497
+ /**
498
+ * Subscribe to an object COV
499
+ * @param {string} uri - The full URI of the object
500
+ * @param {Object} ref - The ID of the node that is registering for this COV
501
+ * @param {Function} callback - Callback when complete
502
+ */
503
+ node.subscribeToObjectCOV = function (uri, ref, callback) {
504
+ let sub = {
505
+ node: ref,
506
+ cb: callback,
507
+ };
508
+ if (!node.objectSubscriptions.has(uri)) {
509
+ node.service.srvcAPI.subscribeToObjectCOV(uri);
510
+ node.objectSubscriptions.set(uri, [sub]);
511
+ } else {
512
+ // _.pullAllBy(node.objectSubscriptions.get(uri), [{node: ref}], node);
513
+ node.objectSubscriptions.get(uri).push(sub);
514
+ }
515
+ };
516
+
517
+ /**
518
+ * Unsubscribe to an object COV
519
+ * @param {string} uri - The full URI of the object
520
+ * @param {Object} ref - The ID of the node that is registering for this COV
521
+ */
522
+ node.unsubscribeToObjectCOV = function (uri, ref) {
523
+ if (node.objectSubscriptions.has(uri)) {
524
+ node.objectSubscriptions.set(
525
+ uri,
526
+ node.objectSubscriptions.get(uri).filter((sub) => sub.node !== ref)
527
+ );
528
+ }
529
+ if (node.objectSubscriptions.get(uri).length === 0) {
530
+ node.service.srvcAPI.unsubscribeToObjectCOV(uri);
531
+ node.objectSubscriptions.delete(uri);
532
+ }
533
+ };
534
+
535
+ /**
536
+ * Subscribe to a broadcast command
537
+ * @param {string} uri - The full URI of the broadcast command
538
+ * @param {Object} ref - The ID of the node that is registering for this broadcast command event
539
+ * @param {Function} callback - Callback when complete
540
+ */
541
+ node.subscribeToBCMD = function (uri, ref, callback) {
542
+ let sub = {
543
+ node: ref,
544
+ cb: callback,
545
+ };
546
+ if (!node.bcmdSubscriptions.has(uri)) {
547
+ node.service.srvcAPI.subscribeToBCMD(uri);
548
+ node.bcmdSubscriptions.set(uri, [sub]);
549
+ } else {
550
+ node.bcmdSubscriptions.get(uri).push(sub);
551
+ }
552
+ };
553
+
554
+ /**
555
+ * Unsubscribe to a broadcast command
556
+ * @param {string} uri - The full URI of the broadcast command
557
+ * @param {Object} ref - The ID of the node that is registering for this broadcast command event
558
+ */
559
+ node.unsubscribeToBCMD = function (uri, ref) {
560
+ if (node.bcmdSubscriptions.has(uri)) {
561
+ node.bcmdSubscriptions.set(
562
+ uri,
563
+ node.bcmdSubscriptions.get(uri).filter((sub) => sub.node !== ref)
564
+ );
565
+ }
566
+ if (node.bcmdSubscriptions.get(uri).length === 0) {
567
+ node.service.srvcAPI.unsubscribeToBCMD(uri);
568
+ node.bcmdSubscriptions.delete(uri);
569
+ }
570
+ };
571
+
572
+ /**
573
+ * Send broadcast command
574
+ * @param {string} cid - The command ID
575
+ * @param {Object} commandData - An object containing the command data
576
+ */
577
+ node.sendBroadcastCommand = function (cid, commandData) {
578
+ node.service.srvcAPI.sendBroadcastCommand(cid, commandData);
579
+ };
580
+
581
+ /**
582
+ * Send direct command
583
+ * @param {string} uri - The full URI of the service to send the command to
584
+ * @param {string} cid - The command ID
585
+ * @param {Object} commandData - An object containing the command data
586
+ * @param {number} timeout - Optional timeout in ms which overrides the default timeout if > 0
587
+ * @param {Function} callback - Callback when complete
588
+ */
589
+ node.sendDirectCommand = function (uri, cid, commandData, timeout, callback) {
590
+ node.service.srvcAPI.sendDirectCommand(uri, cid, commandData, timeout, callback);
591
+ };
592
+
593
+ /**
594
+ * Get service configuration
595
+ * @param {string} hid - The hub ID of the hub the desired service is a child to
596
+ * @param {string} sid - The service ID of the desired service
597
+ * @param {Function} callback - Callback when complete
598
+ */
599
+ node.getServiceConfig = function (hid, sid, callback) {
600
+ node.service.srvcAPI.getServiceConfig(hid, sid, callback);
601
+ };
602
+
603
+ /**
604
+ * Update configuration of a given object
605
+ * @param {string} hid - The hub ID of the hub the target object is a child of
606
+ * @param {string} sid - The serbice ID of the service the target object is a child of
607
+ * @param {string} pid - The profile ID of target object type
608
+ * @param {string} ins - The object instance
609
+ * @param {Object} config - The new object configuration, structure given by {ins: string, pid: string, uiname: string, rids: array}
610
+ * @param {Function} callback - Callback when complete
611
+ */
612
+ node.updateObjectInServiceConfig = function (hid, sid, pid, ins, config, callback) {
613
+ node.service.srvcAPI.updateObjectInServiceConfig(hid, sid, pid, ins, config, callback);
614
+ };
615
+ }
616
+
617
+ RED.nodes.registerType('mobius connection', MobiusConnectionNode);
618
+
619
+ // Register the endpoint for node-red mobius object discovery in node-red node config pages
620
+ RED.httpAdmin.get(
621
+ '/mobjectdiscover',
622
+ RED.auth.needsPermission('serial.read'),
623
+ function (req, res) {
624
+ let conn = RED.nodes.getNode(req.query.mobius);
625
+ let hid = req.query.hid;
626
+ let spids = [];
627
+ try {
628
+ if (req.query.hasOwnProperty('spids')) {
629
+ spids = JSON.parse(req.query.spids);
630
+ }
631
+ } catch (e) {
632
+ spids = [];
633
+ }
634
+
635
+ let pid = req.query.pid;
636
+ if (conn === null || !conn.service.connected) {
637
+ RED.log.error('Discover objects failed - could not to connect to MOBiUS hub.');
638
+ throw new Error('Discover objects failed - could not to connect to MOBiUS hub.');
639
+ } else {
640
+ uiDeviceDiscovery
641
+ .discoverDevicesByPID(RED, conn, hid, spids, pid)
642
+ .then((devices) => {
643
+ res.json(devices);
644
+ })
645
+ .catch((err) => {
646
+ throw err;
647
+ });
648
+ }
649
+ }
650
+ );
651
+
652
+ // Register the endpoint for node-red mobius service discovery in node-red node config pages
653
+ RED.httpAdmin.get(
654
+ '/mservicediscover',
655
+ RED.auth.needsPermission('serial.read'),
656
+ function (req, res) {
657
+ let conn = RED.nodes.getNode(req.query.mobius);
658
+ let hid = req.query.hid;
659
+ let spid = req.query.spid;
660
+ if (conn === null || !conn.service.connected) {
661
+ RED.log.error('Discover services failed - could not to connect to MOBiUS hub.');
662
+ throw new Error('Discover services failed - could not to connect to MOBiUS hub.');
663
+ } else {
664
+ uiDeviceDiscovery
665
+ .discoverServicesBySPID(RED, conn, hid, spid)
666
+ .then((services) => {
667
+ res.json(services);
668
+ })
669
+ .catch((err) => {
670
+ throw err;
671
+ });
672
+ }
673
+ }
674
+ );
675
+ };