node-red-contrib-3dm-space 0.0.7 → 1.0.0
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/3dm-cloud-config.html +80 -16
- package/3dm-cloud-config.js +197 -192
- package/3dm-cloud-in.html +27 -14
- package/3dm-cloud-in.js +115 -115
- package/3dm-cloud-out.html +23 -10
- package/3dm-cloud-out.js +736 -669
- package/README.md +29 -102
- package/package.json +6 -2
package/3dm-cloud-config.html
CHANGED
|
@@ -10,35 +10,99 @@
|
|
|
10
10
|
password: { type: "password" }
|
|
11
11
|
},
|
|
12
12
|
label: function () {
|
|
13
|
-
return this.name || "3DM Cloud Login";
|
|
13
|
+
return this.name || this.clientid || "3DM Cloud Login";
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
18
|
<script type="text/html" data-template-name="3dm-cloud-config">
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
<style>
|
|
20
|
+
.three-dm-login-panel {
|
|
21
|
+
background: #2f3544;
|
|
22
|
+
border-radius: 10px;
|
|
23
|
+
padding: 14px;
|
|
24
|
+
margin-bottom: 12px;
|
|
25
|
+
}
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
.three-dm-login-row {
|
|
28
|
+
background: #3a4152;
|
|
29
|
+
border-radius: 8px;
|
|
30
|
+
padding: 10px 12px;
|
|
31
|
+
margin-bottom: 12px;
|
|
32
|
+
}
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
.three-dm-login-row label {
|
|
35
|
+
display: block;
|
|
36
|
+
width: 100%;
|
|
37
|
+
color: #69d36f;
|
|
38
|
+
font-weight: bold;
|
|
39
|
+
margin-bottom: 6px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.three-dm-login-row input {
|
|
43
|
+
width: 100%;
|
|
44
|
+
box-sizing: border-box;
|
|
45
|
+
background: #3a4152;
|
|
46
|
+
color: #ffffff;
|
|
47
|
+
border: none;
|
|
48
|
+
font-size: 15px;
|
|
49
|
+
padding: 4px 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.three-dm-login-row input::placeholder {
|
|
53
|
+
color: #b8beca;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.three-dm-login-row input:focus {
|
|
57
|
+
outline: none;
|
|
58
|
+
box-shadow: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.three-dm-login-note {
|
|
62
|
+
color: #c8ccd6;
|
|
63
|
+
font-size: 12px;
|
|
64
|
+
margin-top: 4px;
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
67
|
+
|
|
68
|
+
<div class="three-dm-login-panel">
|
|
69
|
+
<div class="three-dm-login-row">
|
|
70
|
+
<label for="node-config-input-clientid">Client ID</label>
|
|
71
|
+
<input type="text" id="node-config-input-clientid" placeholder="Paste Client ID from 3DM.space">
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div class="three-dm-login-row">
|
|
75
|
+
<label for="node-config-input-username">User Name*</label>
|
|
76
|
+
<input type="text" id="node-config-input-username" placeholder="Paste User Name from 3DM.space">
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="three-dm-login-row">
|
|
80
|
+
<label for="node-config-input-password">Password</label>
|
|
81
|
+
<input type="password" id="node-config-input-password" placeholder="Paste Password from 3DM.space">
|
|
82
|
+
</div>
|
|
32
83
|
</div>
|
|
33
84
|
|
|
34
85
|
<div class="form-row">
|
|
35
|
-
<label for="node-config-input-
|
|
36
|
-
|
|
86
|
+
<label for="node-config-input-name">
|
|
87
|
+
<i class="fa fa-tag"></i> Node Name
|
|
88
|
+
</label>
|
|
89
|
+
<input type="text" id="node-config-input-name" placeholder="Optional, e.g. Main PassPort">
|
|
90
|
+
<div class="three-dm-login-note">
|
|
91
|
+
This is only the name shown inside Node-RED.
|
|
92
|
+
</div>
|
|
37
93
|
</div>
|
|
38
94
|
</script>
|
|
39
95
|
|
|
40
96
|
<script type="text/html" data-help-name="3dm-cloud-config">
|
|
41
97
|
<p>Login details for 3DM.space SCADA Cloud.</p>
|
|
42
|
-
|
|
43
|
-
<p>
|
|
98
|
+
|
|
99
|
+
<p>Copy the details from 3DM.space in this order:</p>
|
|
100
|
+
|
|
101
|
+
<ol>
|
|
102
|
+
<li>Client ID</li>
|
|
103
|
+
<li>User Name*</li>
|
|
104
|
+
<li>Password</li>
|
|
105
|
+
</ol>
|
|
106
|
+
|
|
107
|
+
<p>Node Name is optional and is only used inside Node-RED.</p>
|
|
44
108
|
</script>
|
package/3dm-cloud-config.js
CHANGED
|
@@ -1,192 +1,197 @@
|
|
|
1
|
-
module.exports = function (RED) {
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
var mqtt = require("mqtt");
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
* 3DM.space Cloud connection settings
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
var
|
|
15
|
-
var
|
|
16
|
-
var
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
node.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
node.
|
|
49
|
-
node.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
node.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
node.
|
|
62
|
-
node.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
node.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
node.
|
|
95
|
-
node.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
node.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
node.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
node.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
1
|
+
module.exports = function (RED) {
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
var mqtt = require("mqtt");
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* Hidden 3DM.space Cloud connection settings.
|
|
8
|
+
*/
|
|
9
|
+
var CLOUD_HOST = "3dm.space";
|
|
10
|
+
var CLOUD_PORT = 1883;
|
|
11
|
+
var CLOUD_URL = "mqtt://" + CLOUD_HOST + ":" + CLOUD_PORT;
|
|
12
|
+
|
|
13
|
+
var MQTT_KEEPALIVE = 60;
|
|
14
|
+
var MQTT_CLEAN_SESSION = true;
|
|
15
|
+
var MQTT_RECONNECT_MS = 5000;
|
|
16
|
+
var MQTT_CONNECT_TIMEOUT_MS = 15000;
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* Prevent the same connection error being logged every few seconds.
|
|
20
|
+
*/
|
|
21
|
+
var REPEAT_ERROR_LOG_MS = 60000;
|
|
22
|
+
|
|
23
|
+
function cleanText(value) {
|
|
24
|
+
if (value === null || value === undefined) {
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return String(value).trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ThreeDMCloudConfigNode(config) {
|
|
32
|
+
RED.nodes.createNode(this, config);
|
|
33
|
+
|
|
34
|
+
var node = this;
|
|
35
|
+
|
|
36
|
+
node.name = cleanText(config.name);
|
|
37
|
+
node.clientid = cleanText(config.clientid);
|
|
38
|
+
|
|
39
|
+
node.username = node.credentials && node.credentials.username
|
|
40
|
+
? cleanText(node.credentials.username)
|
|
41
|
+
: "";
|
|
42
|
+
|
|
43
|
+
node.password = node.credentials && node.credentials.password
|
|
44
|
+
? cleanText(node.credentials.password)
|
|
45
|
+
: "";
|
|
46
|
+
|
|
47
|
+
node.connected = false;
|
|
48
|
+
node.client = null;
|
|
49
|
+
node.subscriptions = {};
|
|
50
|
+
|
|
51
|
+
node.lastErrorText = "";
|
|
52
|
+
node.lastErrorTime = 0;
|
|
53
|
+
|
|
54
|
+
node.setMaxListeners(0);
|
|
55
|
+
|
|
56
|
+
function warnOncePerMinute(text) {
|
|
57
|
+
var now = Date.now();
|
|
58
|
+
|
|
59
|
+
if (text !== node.lastErrorText || (now - node.lastErrorTime) > REPEAT_ERROR_LOG_MS) {
|
|
60
|
+
node.lastErrorText = text;
|
|
61
|
+
node.lastErrorTime = now;
|
|
62
|
+
node.warn(text);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
node.connect = function () {
|
|
67
|
+
if (!node.username) {
|
|
68
|
+
node.connected = false;
|
|
69
|
+
node.emit("state", false);
|
|
70
|
+
warnOncePerMinute("3DM Cloud username is missing");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
var options = {
|
|
75
|
+
username: node.username,
|
|
76
|
+
password: node.password,
|
|
77
|
+
clean: MQTT_CLEAN_SESSION,
|
|
78
|
+
keepalive: MQTT_KEEPALIVE,
|
|
79
|
+
reconnectPeriod: MQTT_RECONNECT_MS,
|
|
80
|
+
connectTimeout: MQTT_CONNECT_TIMEOUT_MS
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/*
|
|
84
|
+
* Only send Client ID if the user entered one.
|
|
85
|
+
*/
|
|
86
|
+
if (node.clientid) {
|
|
87
|
+
options.clientId = node.clientid;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
node.client = mqtt.connect(CLOUD_URL, options);
|
|
91
|
+
|
|
92
|
+
node.client.on("connect", function () {
|
|
93
|
+
node.connected = true;
|
|
94
|
+
node.lastErrorText = "";
|
|
95
|
+
node.lastErrorTime = 0;
|
|
96
|
+
|
|
97
|
+
node.emit("state", true);
|
|
98
|
+
|
|
99
|
+
Object.keys(node.subscriptions).forEach(function (topic) {
|
|
100
|
+
node.client.subscribe(topic, node.subscriptions[topic], function (err) {
|
|
101
|
+
if (err) {
|
|
102
|
+
warnOncePerMinute("3DM Cloud subscribe failed: " + (err.message || err));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
node.client.on("reconnect", function () {
|
|
109
|
+
node.emit("reconnect");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
node.client.on("offline", function () {
|
|
113
|
+
node.connected = false;
|
|
114
|
+
node.emit("state", false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
node.client.on("close", function () {
|
|
118
|
+
node.connected = false;
|
|
119
|
+
node.emit("state", false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
node.client.on("error", function (err) {
|
|
123
|
+
node.connected = false;
|
|
124
|
+
node.emit("state", false);
|
|
125
|
+
|
|
126
|
+
var text = err && err.message ? err.message : String(err);
|
|
127
|
+
|
|
128
|
+
if (text.indexOf("Not authorized") !== -1 || text.indexOf("not authorized") !== -1) {
|
|
129
|
+
warnOncePerMinute("3DM Cloud login rejected. Check username, password, and Client ID.");
|
|
130
|
+
} else {
|
|
131
|
+
warnOncePerMinute("3DM Cloud connection error: " + text);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
node.client.on("message", function (topic, payload) {
|
|
136
|
+
node.emit("cloud-message", topic, payload);
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
node.publish = function (topic, payload, options, callback) {
|
|
141
|
+
if (!node.client || !node.connected) {
|
|
142
|
+
var err = new Error("3DM Cloud is not connected");
|
|
143
|
+
|
|
144
|
+
if (callback) {
|
|
145
|
+
callback(err);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
var data = payload;
|
|
152
|
+
|
|
153
|
+
if (typeof data !== "string" && !Buffer.isBuffer(data)) {
|
|
154
|
+
data = JSON.stringify(data);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
node.client.publish(topic, data, options || {}, callback);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
node.subscribe = function (topic, options, callback) {
|
|
161
|
+
node.subscriptions[topic] = options || {};
|
|
162
|
+
|
|
163
|
+
if (node.client && node.connected) {
|
|
164
|
+
node.client.subscribe(topic, options || {}, callback);
|
|
165
|
+
} else if (callback) {
|
|
166
|
+
callback();
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
node.on("close", function (removed, done) {
|
|
171
|
+
if (typeof removed === "function") {
|
|
172
|
+
done = removed;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
done = done || function () {};
|
|
176
|
+
|
|
177
|
+
node.connected = false;
|
|
178
|
+
|
|
179
|
+
if (node.client) {
|
|
180
|
+
node.client.end(true, function () {
|
|
181
|
+
done();
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
done();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
node.connect();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
RED.nodes.registerType("3dm-cloud-config", ThreeDMCloudConfigNode, {
|
|
192
|
+
credentials: {
|
|
193
|
+
username: { type: "text" },
|
|
194
|
+
password: { type: "password" }
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
};
|
package/3dm-cloud-in.html
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
2
|
RED.nodes.registerType("3dm-cloud-in", {
|
|
3
3
|
category: "3DM.space",
|
|
4
|
-
color: "#
|
|
4
|
+
color: "#6D6D6D",
|
|
5
5
|
defaults: {
|
|
6
6
|
name: { value: "" },
|
|
7
7
|
server: { value: "", type: "3dm-cloud-config", required: true },
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
inputs: 0,
|
|
12
12
|
outputs: 1,
|
|
13
|
-
icon: "
|
|
13
|
+
icon: "font-awesome/fa-cloud-download",
|
|
14
14
|
label: function () {
|
|
15
15
|
return this.name || "3DM attributes in";
|
|
16
16
|
},
|
|
@@ -20,22 +20,30 @@
|
|
|
20
20
|
|
|
21
21
|
<script type="text/html" data-template-name="3dm-cloud-in">
|
|
22
22
|
<div class="form-row">
|
|
23
|
-
<label for="node-input-name"
|
|
24
|
-
|
|
23
|
+
<label for="node-input-name">
|
|
24
|
+
<i class="fa fa-tag"></i> Node Name
|
|
25
|
+
</label>
|
|
26
|
+
<input type="text" id="node-input-name" placeholder="Optional, e.g. Pump Command In">
|
|
25
27
|
</div>
|
|
26
28
|
|
|
27
29
|
<div class="form-row">
|
|
28
|
-
<label for="node-input-server"
|
|
30
|
+
<label for="node-input-server">
|
|
31
|
+
<i class="fa fa-cloud"></i> 3DM Cloud Login
|
|
32
|
+
</label>
|
|
29
33
|
<input type="text" id="node-input-server">
|
|
30
34
|
</div>
|
|
31
35
|
|
|
32
36
|
<div class="form-row">
|
|
33
|
-
<label for="node-input-keyFilter"
|
|
34
|
-
|
|
37
|
+
<label for="node-input-keyFilter">
|
|
38
|
+
<i class="fa fa-filter"></i> Attribute Key
|
|
39
|
+
</label>
|
|
40
|
+
<input type="text" id="node-input-keyFilter" placeholder="Optional, e.g. pump_cmd">
|
|
35
41
|
</div>
|
|
36
42
|
|
|
37
43
|
<div class="form-row">
|
|
38
|
-
<label for="node-input-outputMode"
|
|
44
|
+
<label for="node-input-outputMode">
|
|
45
|
+
<i class="fa fa-sign-out"></i> Output Type
|
|
46
|
+
</label>
|
|
39
47
|
<select id="node-input-outputMode" style="width:70%;">
|
|
40
48
|
<option value="value">Value only</option>
|
|
41
49
|
<option value="object">Object with key</option>
|
|
@@ -44,15 +52,20 @@
|
|
|
44
52
|
</script>
|
|
45
53
|
|
|
46
54
|
<script type="text/html" data-help-name="3dm-cloud-in">
|
|
47
|
-
<p>Receives
|
|
55
|
+
<p>Receives attribute updates from 3DM.space SCADA Cloud.</p>
|
|
48
56
|
|
|
49
|
-
<p>
|
|
57
|
+
<p>Use this node when 3DM.space needs to send a value down to Node-RED, such as a pump command, mode change, reset command, or setpoint.</p>
|
|
50
58
|
|
|
51
|
-
<p>
|
|
59
|
+
<p><b>Attribute Key</b> is optional.</p>
|
|
60
|
+
|
|
61
|
+
<p>If Attribute Key is blank, all received attributes are passed through.</p>
|
|
62
|
+
|
|
63
|
+
<p>If Attribute Key is set to <code>pump_cmd</code>, only incoming messages containing <code>pump_cmd</code> are passed through.</p>
|
|
64
|
+
|
|
65
|
+
<p><b>Output Type</b> controls what the node sends:</p>
|
|
52
66
|
|
|
53
|
-
<p>Output modes:</p>
|
|
54
67
|
<ul>
|
|
55
|
-
<li><b>Value only</b>: outputs
|
|
56
|
-
<li><b>Object with key</b>: outputs a JSON object containing the selected key.</li>
|
|
68
|
+
<li><b>Value only</b>: outputs only the selected value.</li>
|
|
69
|
+
<li><b>Object with key</b>: outputs a JSON object containing the selected key and value.</li>
|
|
57
70
|
</ul>
|
|
58
71
|
</script>
|