@wiotp/sdk 0.4.2 → 0.6.2
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/LICENSE +203 -203
- package/README.md +68 -68
- package/dist/BaseClient.js +259 -0
- package/dist/BaseConfig.js +194 -0
- package/dist/api/ApiClient.js +508 -0
- package/dist/api/ApiErrors.js +118 -0
- package/dist/api/DscClient.js +332 -0
- package/dist/api/LecClient.js +48 -0
- package/dist/api/MgmtClient.js +77 -0
- package/dist/api/RegistryClient.js +234 -0
- package/dist/api/RulesClient.js +105 -0
- package/dist/api/StateClient.js +738 -0
- package/dist/application/ApplicationClient.js +436 -0
- package/dist/application/ApplicationConfig.js +233 -0
- package/dist/application/index.js +23 -0
- package/dist/bundled/wiotp-bundle.js +35592 -0
- package/dist/bundled/wiotp-bundle.min.js +47 -0
- package/dist/device/DeviceClient.js +125 -0
- package/dist/device/DeviceConfig.js +216 -0
- package/dist/device/index.js +23 -0
- package/dist/gateway/GatewayClient.js +159 -0
- package/dist/gateway/GatewayConfig.js +52 -0
- package/dist/gateway/index.js +23 -0
- package/dist/index.js +55 -0
- package/dist/util.js +50 -0
- package/package.json +92 -84
- package/src/BaseClient.js +215 -215
- package/src/BaseConfig.js +157 -157
- package/src/api/ApiClient.js +454 -454
- package/src/api/ApiErrors.js +33 -33
- package/src/api/DscClient.js +164 -145
- package/src/api/LecClient.js +32 -32
- package/src/api/MgmtClient.js +57 -57
- package/src/api/RegistryClient.js +194 -194
- package/src/api/RulesClient.js +84 -84
- package/src/api/StateClient.js +650 -650
- package/src/application/ApplicationClient.js +348 -348
- package/src/application/ApplicationConfig.js +191 -191
- package/src/application/index.js +12 -12
- package/src/device/DeviceClient.js +78 -78
- package/src/device/DeviceConfig.js +175 -175
- package/src/device/index.js +14 -14
- package/src/gateway/GatewayClient.js +114 -114
- package/src/gateway/GatewayConfig.js +21 -21
- package/src/gateway/index.js +13 -13
- package/{index.js → src/index.js} +19 -19
- package/src/util.js +38 -38
- package/src/util/IoTFoundation.pem +0 -82
package/dist/util.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isString = isString;
|
|
7
|
+
exports.isNumber = isNumber;
|
|
8
|
+
exports.isBoolean = isBoolean;
|
|
9
|
+
exports.isDefined = isDefined;
|
|
10
|
+
exports.generateUUID = generateUUID;
|
|
11
|
+
exports.isNode = exports.isBrowser = void 0;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
*****************************************************************************
|
|
15
|
+
Copyright (c) 2014, 2019 IBM Corporation and other Contributors.
|
|
16
|
+
All rights reserved. This program and the accompanying materials
|
|
17
|
+
are made available under the terms of the Eclipse Public License v1.0
|
|
18
|
+
which accompanies this distribution, and is available at
|
|
19
|
+
http://www.eclipse.org/legal/epl-v10.html
|
|
20
|
+
*****************************************************************************
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
function isString(value) {
|
|
24
|
+
return typeof value === 'string';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isNumber(value) {
|
|
28
|
+
return typeof value === 'number';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isBoolean(value) {
|
|
32
|
+
return typeof value === 'boolean';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isDefined(value) {
|
|
36
|
+
return value !== undefined && value !== null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var isBrowser = new Function("try {return this===window;}catch(e){ return false;}");
|
|
40
|
+
exports.isBrowser = isBrowser;
|
|
41
|
+
var isNode = new Function("try {return this===global;}catch(e){return false;}");
|
|
42
|
+
exports.isNode = isNode;
|
|
43
|
+
|
|
44
|
+
function generateUUID() {
|
|
45
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
46
|
+
var r = Math.random() * 16 | 0,
|
|
47
|
+
v = c == 'x' ? r : r & 0x3 | 0x8;
|
|
48
|
+
return v.toString(16);
|
|
49
|
+
});
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,84 +1,92 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@wiotp/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "SDK for developing device, gateway, and application clients for IBM Watson IoT Platform",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"files": [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"package.json",
|
|
10
|
-
"README.md",
|
|
11
|
-
"LICENCE"
|
|
12
|
-
],
|
|
13
|
-
"publishConfig": {
|
|
14
|
-
"access": "public"
|
|
15
|
-
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"axios": "^0.19.0",
|
|
18
|
-
"bluebird": "^3.5.4",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"@babel/
|
|
33
|
-
"@babel/
|
|
34
|
-
"@babel/
|
|
35
|
-
"@
|
|
36
|
-
"@
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"chai
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"mocha
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
},
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
"
|
|
80
|
-
],
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@wiotp/sdk",
|
|
3
|
+
"version": "0.6.2",
|
|
4
|
+
"description": "SDK for developing device, gateway, and application clients for IBM Watson IoT Platform",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"src",
|
|
9
|
+
"package.json",
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENCE"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"axios": "^0.19.0",
|
|
18
|
+
"bluebird": "^3.5.4",
|
|
19
|
+
"browserify": "^16.5.0",
|
|
20
|
+
"btoa": "^1.2.1",
|
|
21
|
+
"concat-stream": "^2.0.0",
|
|
22
|
+
"events": "^3.0.0",
|
|
23
|
+
"form-data": "^2.3.3",
|
|
24
|
+
"format": "^0.2.2",
|
|
25
|
+
"loglevel": "^1.6.1",
|
|
26
|
+
"mqtt": "^3.0.0",
|
|
27
|
+
"tinycache": "^1.1.2",
|
|
28
|
+
"uuid": "^3.3.2",
|
|
29
|
+
"yaml": "^1.7.2"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@babel/cli": "7.7.7",
|
|
33
|
+
"@babel/core": "7.7.7",
|
|
34
|
+
"@babel/preset-env": "7.7.7",
|
|
35
|
+
"@babel/register": "7.7.7",
|
|
36
|
+
"@cloudant/cloudant": "^2.1.0",
|
|
37
|
+
"@istanbuljs/nyc-config-babel": "^3.0.0",
|
|
38
|
+
"babel-plugin-istanbul": "^6.0.0",
|
|
39
|
+
"chai": "^4.2.0",
|
|
40
|
+
"chai-as-promised": "^7.1.1",
|
|
41
|
+
"coveralls": "3.0.9",
|
|
42
|
+
"mocha": "6.2.2",
|
|
43
|
+
"mocha-steps": "^1.3.0",
|
|
44
|
+
"nyc": "15.0.0",
|
|
45
|
+
"rimraf": "2.6.3",
|
|
46
|
+
"terser": "^4.4.3"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"clean": "rimraf dist/*",
|
|
50
|
+
"build": "npm run clean && npm run build:commonjs && npm run build:bundle && npm run build:min",
|
|
51
|
+
"build:commonjs": "babel src --out-dir dist",
|
|
52
|
+
"build:bundle": "browserify dist/index.js --outfile dist/bundled/wiotp-bundle.js",
|
|
53
|
+
"build:min": "terser dist/bundled/wiotp-bundle.js -o dist/bundled/wiotp-bundle.min.js",
|
|
54
|
+
"test": "mocha --require @babel/register --require mocha-steps",
|
|
55
|
+
"test-cov": "nyc --reporter=lcov --reporter=text-summary mocha --require mocha-steps",
|
|
56
|
+
"test:watch": "mocha --require @babel/register --require mocha-steps --watch"
|
|
57
|
+
},
|
|
58
|
+
"author": {
|
|
59
|
+
"name": "David Parker",
|
|
60
|
+
"email": "parkerda@uk.ibm.com"
|
|
61
|
+
},
|
|
62
|
+
"contributors": [
|
|
63
|
+
{
|
|
64
|
+
"name": "Tom Klapiscak",
|
|
65
|
+
"email": "klapitom@uk.ibm.com"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "Bryan Boyd",
|
|
69
|
+
"email": "bboyd@us.ibm.com"
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"repository": {
|
|
73
|
+
"type": "git",
|
|
74
|
+
"url": "git+https://github.com/ibm-watson-iot/iot-nodejs.git"
|
|
75
|
+
},
|
|
76
|
+
"license": "EPL-1.0",
|
|
77
|
+
"nyc": {
|
|
78
|
+
"require": [
|
|
79
|
+
"@babel/register"
|
|
80
|
+
],
|
|
81
|
+
"all": true,
|
|
82
|
+
"include": [
|
|
83
|
+
"src/**/*.js"
|
|
84
|
+
],
|
|
85
|
+
"reporter": [
|
|
86
|
+
"lcov",
|
|
87
|
+
"text"
|
|
88
|
+
],
|
|
89
|
+
"sourceMap": false,
|
|
90
|
+
"instrument": true
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/BaseClient.js
CHANGED
|
@@ -1,215 +1,215 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*****************************************************************************
|
|
3
|
-
Copyright (c) 2014, 2019 IBM Corporation and other Contributors.
|
|
4
|
-
All rights reserved. This program and the accompanying materials
|
|
5
|
-
are made available under the terms of the Eclipse Public License v1.0
|
|
6
|
-
which accompanies this distribution, and is available at
|
|
7
|
-
http://www.eclipse.org/legal/epl-v10.html
|
|
8
|
-
*****************************************************************************
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
import events from 'events';
|
|
12
|
-
import mqtt from 'mqtt';
|
|
13
|
-
import log from 'loglevel';
|
|
14
|
-
|
|
15
|
-
import TinyCache from 'tinycache';
|
|
16
|
-
|
|
17
|
-
const uuidv4 = require('uuid/v4');
|
|
18
|
-
|
|
19
|
-
export default class BaseClient extends events.EventEmitter {
|
|
20
|
-
constructor(config){
|
|
21
|
-
super();
|
|
22
|
-
this.log = log;
|
|
23
|
-
this.log.setDefaultLevel(config.options.logLevel);
|
|
24
|
-
|
|
25
|
-
this.config = config;
|
|
26
|
-
|
|
27
|
-
this.reconnectLog = 0;
|
|
28
|
-
this.mqtt = null;
|
|
29
|
-
|
|
30
|
-
this.lostConnectionLog = new TinyCache();
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
isConnected() {
|
|
36
|
-
if (this.mqtt == null) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
return this.mqtt.connected;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
connect(){
|
|
44
|
-
if(this.mqtt != null) {
|
|
45
|
-
this.log.info("[BaseClient:connect] Reconnecting to " + this.config.getMqttHost() + " as " + this.config.getClientId());
|
|
46
|
-
this.mqtt.reconnect();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
this.log.info("[BaseClient:connect] Connecting to " + this.config.getMqttHost() + " as " + this.config.getClientId());
|
|
51
|
-
|
|
52
|
-
this.mqtt = mqtt.connect(this.config.getMqttHost(), this.config.getMqttConfig());
|
|
53
|
-
|
|
54
|
-
/* Events coming from mqtt
|
|
55
|
-
* Event 'connect' - Emitted on successful (re)connection (i.e. connack rc=0).
|
|
56
|
-
* Event 'reconnect' - Emitted when a reconnect starts.
|
|
57
|
-
* Event 'close' - Emitted after a disconnection.
|
|
58
|
-
* Event 'offline' - Emitted when the client goes offline.
|
|
59
|
-
* Event 'error' - Emitted when the client cannot connect (i.e. connack rc != 0) or when a parsing error occurs.
|
|
60
|
-
* Event 'end' - Emitted when mqtt.Client#end() is called. If a callback was passed to mqtt.Client#end(), this event is emitted once the callback returns.
|
|
61
|
-
* Event 'message' - Emitted when the client receives a publish packet
|
|
62
|
-
* Event 'packetsend' - Emitted when the client sends any packet. This includes .published() packets as well as packets used by MQTT for managing subscriptions and connections
|
|
63
|
-
* Event 'packetreceive' - Emitted when the client receives any packet. This includes packets from subscribed topics as well as packets used by MQTT for managing subscriptions and connections
|
|
64
|
-
*/
|
|
65
|
-
|
|
66
|
-
this.mqtt.on('connect', () => {
|
|
67
|
-
this.log.info("[BaseClient:onConnect] MQTT client is connected.");
|
|
68
|
-
this.emit('connect');
|
|
69
|
-
|
|
70
|
-
// less than 3 connect attempts you get put to a connect delay of 1 second
|
|
71
|
-
// after 3 connect attempts you get put to a connect delay of 2 seconds (3 seconds elapsed - 3 attempts @ 1 second intervals)
|
|
72
|
-
// after 6 connect attempts you get put to a connect delay of 5 seconds (3 + 6 seconds elapsed - 3 attempts @ 2 second intervals)
|
|
73
|
-
// after 9 connect attempts you get put to a connect delay of 20 seconds (3 + 6 + 15 seconds elapsed - 3 attempts @ 5 second intervals)
|
|
74
|
-
let connectionLostCount = this.lostConnectionLog.size;
|
|
75
|
-
|
|
76
|
-
// Default is 1 second reconnect period
|
|
77
|
-
let reconnectPeriod = 1000;
|
|
78
|
-
if (connectionLostCount >= 9) {
|
|
79
|
-
reconnectPeriod = 20000;
|
|
80
|
-
|
|
81
|
-
// Log this and raise the error EVERY time we reconnect under these conditions.
|
|
82
|
-
this.log.warn("[BaseClient:onOffline] This client is likely suffering from clientId stealing (where two connections try to use the same client Id).");
|
|
83
|
-
this.emit("error", "Exceeded 9 connection losses in a 5 minute period. Check for clientId conflict with another connection.")
|
|
84
|
-
}
|
|
85
|
-
else if (connectionLostCount >= 6) {
|
|
86
|
-
reconnectPeriod = 5000;
|
|
87
|
-
}
|
|
88
|
-
else if (connectionLostCount >= 3) {
|
|
89
|
-
reconnectPeriod = 2000;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (reconnectPeriod != this.mqtt.options.reconnectPeriod) {
|
|
93
|
-
this.log.info("[BaseClient:onOffline] Client has lost connection " + connectionLostCount + " times during the last 5 minutes, reconnect delay adjusted to " + reconnectPeriod + " ms");
|
|
94
|
-
this.mqtt.options.reconnectPeriod = reconnectPeriod;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
this.mqtt.on('reconnect', () => {
|
|
100
|
-
this.log.info("[BaseClient:onReconnect] MQTT client is reconnecting.");
|
|
101
|
-
// this.log.debug("[BaseClient:onReconnect] Resubscribe topics:");
|
|
102
|
-
// this.log.debug(this.mqtt._resubscribeTopics);
|
|
103
|
-
this.emit('reconnect');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
this.mqtt.on('close', () => {
|
|
107
|
-
this.log.info("[BaseClient:onClose] MQTT client connection was closed.");
|
|
108
|
-
this.emit('close');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
this.mqtt.on('offline', () => {
|
|
112
|
-
let newId = uuidv4();
|
|
113
|
-
this.log.info("[BaseClient:onOffline] MQTT client connection is offline. [" + newId + "]");
|
|
114
|
-
this.emit('offline');
|
|
115
|
-
// Record the disconnect event for 5 minutes
|
|
116
|
-
this.lostConnectionLog.put( newId, '1', 300000 );
|
|
117
|
-
|
|
118
|
-
let connectionLostCount = this.lostConnectionLog.size;
|
|
119
|
-
this.log.info("[BaseClient:onOffline] Connection losses in the last 5 minutes: " + connectionLostCount);
|
|
120
|
-
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
this.mqtt.on('error', (error) => {
|
|
124
|
-
this.log.error("[BaseClient:onError] " + error);
|
|
125
|
-
|
|
126
|
-
let errorMsg = '' + error;
|
|
127
|
-
if (errorMsg.indexOf('Not authorized') > -1) {
|
|
128
|
-
this.log.error("[BaseClient:onError] One or more configuration parameters are wrong. Modify the configuration before trying to reconnect.");
|
|
129
|
-
this.mqtt.end(false, () => {
|
|
130
|
-
this.log.info("[BaseClient:onError] Closed the MQTT connection due to client misconfiguration");
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
this.emit('error', error);
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
disconnect(){
|
|
139
|
-
if(this.mqtt == null) {
|
|
140
|
-
this.log.info("[BaseClient:disconnect] Client was never connected");
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this.mqtt.end(false, () => {
|
|
145
|
-
this.log.info("[BaseClient:disconnect] Closed the MQTT connection due to disconnect() call");
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
_subscribe(topic, QoS, callback) {
|
|
151
|
-
if (this.mqtt == null) {
|
|
152
|
-
this.emit('error', "[BaseClient:_subscribe] MQTT Client is not initialized - call connect() first");
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
if (!this.mqtt.connected) {
|
|
156
|
-
this.emit('error', "[BaseClient:_subscribe] MQTT Client is not connected - call connect() first");
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
QoS = QoS || 0;
|
|
161
|
-
callback = callback || function (err, granted) {
|
|
162
|
-
if (err == null) {
|
|
163
|
-
for (var index in granted) {
|
|
164
|
-
let grant = granted[index];
|
|
165
|
-
this.log.debug("[BaseClient:_subscribe] Subscribed to " + grant.topic + " at QoS " + grant.qos);
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
this.log.error("[BaseClient:_subscribe] " + err);
|
|
169
|
-
this.emit("error", err);
|
|
170
|
-
}
|
|
171
|
-
}.bind(this);
|
|
172
|
-
|
|
173
|
-
this.log.debug("[BaseClient:_subscribe] Subscribing to topic " + topic + " with QoS " + QoS);
|
|
174
|
-
this.mqtt.subscribe(topic, { qos: parseInt(QoS) }, callback);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
_unsubscribe(topic, callback) {
|
|
179
|
-
if (this.mqtt == null) {
|
|
180
|
-
this.emit('error', "[BaseClient:_unsubscribe] MQTT Client is not initialized - call connect() first");
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
if (!this.mqtt.connected) {
|
|
184
|
-
this.emit('error', "[BaseClient:_unsubscribe] MQTT Client is not connected - call connect() first");
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
callback = callback || function (err) {
|
|
189
|
-
if (err == null) {
|
|
190
|
-
this.log.debug("[BaseClient:_unsubscribe] Unsubscribed from: " + topic);
|
|
191
|
-
} else {
|
|
192
|
-
this.log.error("[BaseClient:_unsubscribe] " + err);
|
|
193
|
-
this.emit("error", err);
|
|
194
|
-
}
|
|
195
|
-
}.bind(this);
|
|
196
|
-
|
|
197
|
-
this.log.debug("[BaseClient:_unsubscribe] Unsubscribe: " + topic);
|
|
198
|
-
this.mqtt.unsubscribe(topic, callback);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
_publish(topic, msg, QoS, callback) {
|
|
203
|
-
QoS = QoS || 0;
|
|
204
|
-
|
|
205
|
-
if ((typeof msg === 'object' || typeof msg === 'boolean' || typeof msg === 'number') && !Buffer.isBuffer(msg)) {
|
|
206
|
-
// mqtt library does not support sending JSON/Boolean/Number data. So stringifying it.
|
|
207
|
-
// All JSON object, array will be encoded.
|
|
208
|
-
msg = JSON.stringify(msg);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
this.log.debug("[BaseClient:_publish] Publish: " + topic + ", " + msg + ", QoS : " + QoS);
|
|
212
|
-
this.mqtt.publish(topic, msg, { qos: parseInt(QoS) }, callback);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
*****************************************************************************
|
|
3
|
+
Copyright (c) 2014, 2019 IBM Corporation and other Contributors.
|
|
4
|
+
All rights reserved. This program and the accompanying materials
|
|
5
|
+
are made available under the terms of the Eclipse Public License v1.0
|
|
6
|
+
which accompanies this distribution, and is available at
|
|
7
|
+
http://www.eclipse.org/legal/epl-v10.html
|
|
8
|
+
*****************************************************************************
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
import events from 'events';
|
|
12
|
+
import mqtt from 'mqtt';
|
|
13
|
+
import log from 'loglevel';
|
|
14
|
+
|
|
15
|
+
import TinyCache from 'tinycache';
|
|
16
|
+
|
|
17
|
+
const uuidv4 = require('uuid/v4');
|
|
18
|
+
|
|
19
|
+
export default class BaseClient extends events.EventEmitter {
|
|
20
|
+
constructor(config){
|
|
21
|
+
super();
|
|
22
|
+
this.log = log;
|
|
23
|
+
this.log.setDefaultLevel(config.options.logLevel);
|
|
24
|
+
|
|
25
|
+
this.config = config;
|
|
26
|
+
|
|
27
|
+
this.reconnectLog = 0;
|
|
28
|
+
this.mqtt = null;
|
|
29
|
+
|
|
30
|
+
this.lostConnectionLog = new TinyCache();
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
isConnected() {
|
|
36
|
+
if (this.mqtt == null) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return this.mqtt.connected;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
connect(){
|
|
44
|
+
if(this.mqtt != null) {
|
|
45
|
+
this.log.info("[BaseClient:connect] Reconnecting to " + this.config.getMqttHost() + " as " + this.config.getClientId());
|
|
46
|
+
this.mqtt.reconnect();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.log.info("[BaseClient:connect] Connecting to " + this.config.getMqttHost() + " as " + this.config.getClientId());
|
|
51
|
+
|
|
52
|
+
this.mqtt = mqtt.connect(this.config.getMqttHost(), this.config.getMqttConfig());
|
|
53
|
+
|
|
54
|
+
/* Events coming from mqtt
|
|
55
|
+
* Event 'connect' - Emitted on successful (re)connection (i.e. connack rc=0).
|
|
56
|
+
* Event 'reconnect' - Emitted when a reconnect starts.
|
|
57
|
+
* Event 'close' - Emitted after a disconnection.
|
|
58
|
+
* Event 'offline' - Emitted when the client goes offline.
|
|
59
|
+
* Event 'error' - Emitted when the client cannot connect (i.e. connack rc != 0) or when a parsing error occurs.
|
|
60
|
+
* Event 'end' - Emitted when mqtt.Client#end() is called. If a callback was passed to mqtt.Client#end(), this event is emitted once the callback returns.
|
|
61
|
+
* Event 'message' - Emitted when the client receives a publish packet
|
|
62
|
+
* Event 'packetsend' - Emitted when the client sends any packet. This includes .published() packets as well as packets used by MQTT for managing subscriptions and connections
|
|
63
|
+
* Event 'packetreceive' - Emitted when the client receives any packet. This includes packets from subscribed topics as well as packets used by MQTT for managing subscriptions and connections
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
this.mqtt.on('connect', () => {
|
|
67
|
+
this.log.info("[BaseClient:onConnect] MQTT client is connected.");
|
|
68
|
+
this.emit('connect');
|
|
69
|
+
|
|
70
|
+
// less than 3 connect attempts you get put to a connect delay of 1 second
|
|
71
|
+
// after 3 connect attempts you get put to a connect delay of 2 seconds (3 seconds elapsed - 3 attempts @ 1 second intervals)
|
|
72
|
+
// after 6 connect attempts you get put to a connect delay of 5 seconds (3 + 6 seconds elapsed - 3 attempts @ 2 second intervals)
|
|
73
|
+
// after 9 connect attempts you get put to a connect delay of 20 seconds (3 + 6 + 15 seconds elapsed - 3 attempts @ 5 second intervals)
|
|
74
|
+
let connectionLostCount = this.lostConnectionLog.size;
|
|
75
|
+
|
|
76
|
+
// Default is 1 second reconnect period
|
|
77
|
+
let reconnectPeriod = 1000;
|
|
78
|
+
if (connectionLostCount >= 9) {
|
|
79
|
+
reconnectPeriod = 20000;
|
|
80
|
+
|
|
81
|
+
// Log this and raise the error EVERY time we reconnect under these conditions.
|
|
82
|
+
this.log.warn("[BaseClient:onOffline] This client is likely suffering from clientId stealing (where two connections try to use the same client Id).");
|
|
83
|
+
this.emit("error", "Exceeded 9 connection losses in a 5 minute period. Check for clientId conflict with another connection.")
|
|
84
|
+
}
|
|
85
|
+
else if (connectionLostCount >= 6) {
|
|
86
|
+
reconnectPeriod = 5000;
|
|
87
|
+
}
|
|
88
|
+
else if (connectionLostCount >= 3) {
|
|
89
|
+
reconnectPeriod = 2000;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (reconnectPeriod != this.mqtt.options.reconnectPeriod) {
|
|
93
|
+
this.log.info("[BaseClient:onOffline] Client has lost connection " + connectionLostCount + " times during the last 5 minutes, reconnect delay adjusted to " + reconnectPeriod + " ms");
|
|
94
|
+
this.mqtt.options.reconnectPeriod = reconnectPeriod;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
this.mqtt.on('reconnect', () => {
|
|
100
|
+
this.log.info("[BaseClient:onReconnect] MQTT client is reconnecting.");
|
|
101
|
+
// this.log.debug("[BaseClient:onReconnect] Resubscribe topics:");
|
|
102
|
+
// this.log.debug(this.mqtt._resubscribeTopics);
|
|
103
|
+
this.emit('reconnect');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
this.mqtt.on('close', () => {
|
|
107
|
+
this.log.info("[BaseClient:onClose] MQTT client connection was closed.");
|
|
108
|
+
this.emit('close');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
this.mqtt.on('offline', () => {
|
|
112
|
+
let newId = uuidv4();
|
|
113
|
+
this.log.info("[BaseClient:onOffline] MQTT client connection is offline. [" + newId + "]");
|
|
114
|
+
this.emit('offline');
|
|
115
|
+
// Record the disconnect event for 5 minutes
|
|
116
|
+
this.lostConnectionLog.put( newId, '1', 300000 );
|
|
117
|
+
|
|
118
|
+
let connectionLostCount = this.lostConnectionLog.size;
|
|
119
|
+
this.log.info("[BaseClient:onOffline] Connection losses in the last 5 minutes: " + connectionLostCount);
|
|
120
|
+
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
this.mqtt.on('error', (error) => {
|
|
124
|
+
this.log.error("[BaseClient:onError] " + error);
|
|
125
|
+
|
|
126
|
+
let errorMsg = '' + error;
|
|
127
|
+
if (errorMsg.indexOf('Not authorized') > -1) {
|
|
128
|
+
this.log.error("[BaseClient:onError] One or more configuration parameters are wrong. Modify the configuration before trying to reconnect.");
|
|
129
|
+
this.mqtt.end(false, () => {
|
|
130
|
+
this.log.info("[BaseClient:onError] Closed the MQTT connection due to client misconfiguration");
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
this.emit('error', error);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
disconnect(){
|
|
139
|
+
if(this.mqtt == null) {
|
|
140
|
+
this.log.info("[BaseClient:disconnect] Client was never connected");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.mqtt.end(false, () => {
|
|
145
|
+
this.log.info("[BaseClient:disconnect] Closed the MQTT connection due to disconnect() call");
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
_subscribe(topic, QoS, callback) {
|
|
151
|
+
if (this.mqtt == null) {
|
|
152
|
+
this.emit('error', "[BaseClient:_subscribe] MQTT Client is not initialized - call connect() first");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!this.mqtt.connected) {
|
|
156
|
+
this.emit('error', "[BaseClient:_subscribe] MQTT Client is not connected - call connect() first");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
QoS = QoS || 0;
|
|
161
|
+
callback = callback || function (err, granted) {
|
|
162
|
+
if (err == null) {
|
|
163
|
+
for (var index in granted) {
|
|
164
|
+
let grant = granted[index];
|
|
165
|
+
this.log.debug("[BaseClient:_subscribe] Subscribed to " + grant.topic + " at QoS " + grant.qos);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
this.log.error("[BaseClient:_subscribe] " + err);
|
|
169
|
+
this.emit("error", err);
|
|
170
|
+
}
|
|
171
|
+
}.bind(this);
|
|
172
|
+
|
|
173
|
+
this.log.debug("[BaseClient:_subscribe] Subscribing to topic " + topic + " with QoS " + QoS);
|
|
174
|
+
this.mqtt.subscribe(topic, { qos: parseInt(QoS) }, callback);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
_unsubscribe(topic, callback) {
|
|
179
|
+
if (this.mqtt == null) {
|
|
180
|
+
this.emit('error', "[BaseClient:_unsubscribe] MQTT Client is not initialized - call connect() first");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!this.mqtt.connected) {
|
|
184
|
+
this.emit('error', "[BaseClient:_unsubscribe] MQTT Client is not connected - call connect() first");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
callback = callback || function (err) {
|
|
189
|
+
if (err == null) {
|
|
190
|
+
this.log.debug("[BaseClient:_unsubscribe] Unsubscribed from: " + topic);
|
|
191
|
+
} else {
|
|
192
|
+
this.log.error("[BaseClient:_unsubscribe] " + err);
|
|
193
|
+
this.emit("error", err);
|
|
194
|
+
}
|
|
195
|
+
}.bind(this);
|
|
196
|
+
|
|
197
|
+
this.log.debug("[BaseClient:_unsubscribe] Unsubscribe: " + topic);
|
|
198
|
+
this.mqtt.unsubscribe(topic, callback);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
_publish(topic, msg, QoS, callback) {
|
|
203
|
+
QoS = QoS || 0;
|
|
204
|
+
|
|
205
|
+
if ((typeof msg === 'object' || typeof msg === 'boolean' || typeof msg === 'number') && !Buffer.isBuffer(msg)) {
|
|
206
|
+
// mqtt library does not support sending JSON/Boolean/Number data. So stringifying it.
|
|
207
|
+
// All JSON object, array will be encoded.
|
|
208
|
+
msg = JSON.stringify(msg);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.log.debug("[BaseClient:_publish] Publish: " + topic + ", " + msg + ", QoS : " + QoS);
|
|
212
|
+
this.mqtt.publish(topic, msg, { qos: parseInt(QoS) }, callback);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
}
|