@machhub-dev/node-red-nodes 1.0.7 → 1.0.9-test
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/dist/lib/services/mqtt.service.js +44 -9
- package/dist/nodes/bulk-tag-write.html +7 -1
- package/dist/nodes/bulk-tag-write.js +5 -11
- package/dist/nodes/collections.html +7 -1
- package/dist/nodes/dbquery.html +7 -1
- package/dist/nodes/tag-read.html +7 -1
- package/dist/nodes/tag-read.js +10 -4
- package/dist/nodes/tag-write.js +5 -11
- package/package.json +1 -1
|
@@ -15,9 +15,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.MQTTService = void 0;
|
|
16
16
|
const mqtt_1 = __importDefault(require("mqtt"));
|
|
17
17
|
class MQTTService {
|
|
18
|
-
constructor() {
|
|
19
|
-
this.subscribedTopics = [];
|
|
20
|
-
}
|
|
21
18
|
static getInstance(settings, statusCallback) {
|
|
22
19
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
20
|
// If we already have an instance, return it
|
|
@@ -109,8 +106,17 @@ class MQTTService {
|
|
|
109
106
|
clientId: settings.clientId,
|
|
110
107
|
username: settings.clientId,
|
|
111
108
|
password: settings.clientSecret,
|
|
109
|
+
reconnectPeriod: 0, // disable MQTT.js auto-reconnect; we manage reconnection via getInstance()
|
|
112
110
|
});
|
|
113
111
|
instance.attachMessageListener();
|
|
112
|
+
// Re-subscribe any topics registered before this connection was established
|
|
113
|
+
for (const sub of MQTTService.subscribedTopics) {
|
|
114
|
+
instance.client.subscribe(sub.topic, { qos: 2 }, (err) => {
|
|
115
|
+
if (err) {
|
|
116
|
+
console.error(`Failed to re-subscribe to topic ${sub.topic}:`, err);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
114
120
|
// console.log("MQTT connection established successfully");
|
|
115
121
|
if (statusCallback) {
|
|
116
122
|
statusCallback({ fill: "green", shape: "dot", text: "MQTT Connected" });
|
|
@@ -128,19 +134,22 @@ class MQTTService {
|
|
|
128
134
|
return instance;
|
|
129
135
|
});
|
|
130
136
|
}
|
|
131
|
-
// Method to reset the instance
|
|
137
|
+
// Method to reset the instance and all state (full shutdown / redeploy)
|
|
132
138
|
static resetInstance() {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
139
|
+
const inst = this.instance;
|
|
140
|
+
// Clear references first so the 'close' event handler guard doesn't re-null unnecessarily
|
|
136
141
|
this.instance = null;
|
|
137
142
|
this.instancePromise = null;
|
|
138
143
|
this.stopConnecting = true;
|
|
144
|
+
this.subscribedTopics = [];
|
|
145
|
+
if (inst && inst.client) {
|
|
146
|
+
inst.client.end();
|
|
147
|
+
}
|
|
139
148
|
}
|
|
140
149
|
// Adds a topic and handler to the subscribed list
|
|
141
150
|
addTopicHandler(topic, handler) {
|
|
142
151
|
try {
|
|
143
|
-
|
|
152
|
+
MQTTService.subscribedTopics.push({ topic, handler });
|
|
144
153
|
// console.log("New Subscription Handler : ", topic)
|
|
145
154
|
this.client.subscribe(topic, { qos: 2 }, (err) => {
|
|
146
155
|
if (err) {
|
|
@@ -152,6 +161,23 @@ class MQTTService {
|
|
|
152
161
|
console.error(`Failed to subscribe to topic ${topic}:`, e);
|
|
153
162
|
}
|
|
154
163
|
}
|
|
164
|
+
// Removes a topic handler and unsubscribes from the broker if no other handler covers the same topic
|
|
165
|
+
removeTopicHandler(topic, handler) {
|
|
166
|
+
MQTTService.subscribedTopics = MQTTService.subscribedTopics.filter(st => !(st.topic === topic && st.handler === handler));
|
|
167
|
+
const stillSubscribed = MQTTService.subscribedTopics.some(st => st.topic === topic);
|
|
168
|
+
if (!stillSubscribed) {
|
|
169
|
+
try {
|
|
170
|
+
this.client.unsubscribe(topic, (err) => {
|
|
171
|
+
if (err) {
|
|
172
|
+
console.error(`Failed to unsubscribe from topic ${topic}:`, err);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
console.error(`Failed to unsubscribe from topic ${topic}:`, e);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
155
181
|
// Publishes a message to a specific topic
|
|
156
182
|
publish(topic, message) {
|
|
157
183
|
try {
|
|
@@ -176,7 +202,7 @@ class MQTTService {
|
|
|
176
202
|
}
|
|
177
203
|
attachMessageListener() {
|
|
178
204
|
this.client.on('message', (topic, message, packet) => {
|
|
179
|
-
for (const subscribedTopic of
|
|
205
|
+
for (const subscribedTopic of MQTTService.subscribedTopics) {
|
|
180
206
|
if (this.matchesTopic(subscribedTopic.topic, topic)) {
|
|
181
207
|
const parsedMessage = this.parseMessage(message, topic);
|
|
182
208
|
subscribedTopic.handler(parsedMessage, packet.retain);
|
|
@@ -184,6 +210,14 @@ class MQTTService {
|
|
|
184
210
|
}
|
|
185
211
|
}
|
|
186
212
|
});
|
|
213
|
+
// When the broker closes the connection (e.g. session expired), self-reset so the
|
|
214
|
+
// next getInstance() call creates a fresh client with an empty in-flight store.
|
|
215
|
+
this.client.on('close', () => {
|
|
216
|
+
if (MQTTService.instance === this) {
|
|
217
|
+
MQTTService.instance = null;
|
|
218
|
+
MQTTService.instancePromise = null;
|
|
219
|
+
}
|
|
220
|
+
});
|
|
187
221
|
}
|
|
188
222
|
// Matches MQTT topic patterns with wildcards (+ for single level, # for multi-level)
|
|
189
223
|
matchesTopic(pattern, topic) {
|
|
@@ -232,3 +266,4 @@ class MQTTService {
|
|
|
232
266
|
exports.MQTTService = MQTTService;
|
|
233
267
|
MQTTService.instancePromise = null;
|
|
234
268
|
MQTTService.stopConnecting = false;
|
|
269
|
+
MQTTService.subscribedTopics = [];
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
defaults: {
|
|
6
6
|
name: { value: "" },
|
|
7
7
|
useConfigNode: { value: false }, // Toggle between JSON file and config node
|
|
8
|
-
machhub: {
|
|
8
|
+
machhub: {
|
|
9
|
+
value: "",
|
|
10
|
+
type: "machhub-config",
|
|
11
|
+
validate: function(v) {
|
|
12
|
+
return !this.useConfigNode || (this.useConfigNode && v !== "");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
9
15
|
},
|
|
10
16
|
paletteLabel: "Bulk Tag Write",
|
|
11
17
|
inputs: 1,
|
|
@@ -56,7 +56,6 @@ function default_1(RED) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
node.on('close', () => {
|
|
59
|
-
mqtt_service_1.MQTTService.resetInstance();
|
|
60
59
|
mqttService = null;
|
|
61
60
|
initializationPromise = null;
|
|
62
61
|
});
|
|
@@ -64,18 +63,13 @@ function default_1(RED) {
|
|
|
64
63
|
return __awaiter(this, void 0, void 0, function* () {
|
|
65
64
|
try {
|
|
66
65
|
node.status({ fill: "orange", shape: "dot", text: "Sending Payload" });
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
// Check if mqttService is initialized
|
|
72
|
-
if (!mqttService) {
|
|
73
|
-
node.status({ fill: "red", shape: "dot", text: "MQTT service not initialized" });
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
66
|
+
// Always fetch the current instance so we recover transparently after reconnects
|
|
67
|
+
const service = yield mqtt_service_1.MQTTService.getInstance(machhubConfig, (status) => {
|
|
68
|
+
node.status(status);
|
|
69
|
+
});
|
|
76
70
|
Object.keys(msg.payload).forEach(function (key) {
|
|
77
71
|
try {
|
|
78
|
-
|
|
72
|
+
service.publish(key, msg.payload[key]);
|
|
79
73
|
}
|
|
80
74
|
catch (error) {
|
|
81
75
|
node.status({ fill: "red", shape: "dot", text: "Failed to publish to '" + key + "': " + error.toString() });
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
defaults: {
|
|
6
6
|
name: { value: "" },
|
|
7
7
|
useConfigNode: { value: false },
|
|
8
|
-
machhub: {
|
|
8
|
+
machhub: {
|
|
9
|
+
value: "",
|
|
10
|
+
type: "machhub-config",
|
|
11
|
+
validate: function(v) {
|
|
12
|
+
return !this.useConfigNode || (this.useConfigNode && v !== "");
|
|
13
|
+
}
|
|
14
|
+
},
|
|
9
15
|
collectionID: { value: "" },
|
|
10
16
|
action: { value: "select" },
|
|
11
17
|
selectFields: { value: [] },
|
package/dist/nodes/dbquery.html
CHANGED
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
defaults: {
|
|
6
6
|
name: { value: "" },
|
|
7
7
|
useConfigNode: { value: false },
|
|
8
|
-
machhub: {
|
|
8
|
+
machhub: {
|
|
9
|
+
value: "",
|
|
10
|
+
type: "machhub-config",
|
|
11
|
+
validate: function(v) {
|
|
12
|
+
return !this.useConfigNode || (this.useConfigNode && v !== "");
|
|
13
|
+
}
|
|
14
|
+
},
|
|
9
15
|
query: { value: "" }
|
|
10
16
|
},
|
|
11
17
|
paletteLabel: "DB Query",
|
package/dist/nodes/tag-read.html
CHANGED
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
name: { value: "" },
|
|
7
7
|
selectedTag: { value: "", required: true },
|
|
8
8
|
useConfigNode: { value: false }, // Toggle between JSON file and config node
|
|
9
|
-
machhub: {
|
|
9
|
+
machhub: {
|
|
10
|
+
value: "",
|
|
11
|
+
type: "machhub-config",
|
|
12
|
+
validate: function(v) {
|
|
13
|
+
return !this.useConfigNode || (this.useConfigNode && v !== "");
|
|
14
|
+
}
|
|
15
|
+
} // Will fill in below if empty
|
|
10
16
|
},
|
|
11
17
|
paletteLabel: "Tag Read",
|
|
12
18
|
outputs: 1,
|
package/dist/nodes/tag-read.js
CHANGED
|
@@ -61,14 +61,19 @@ function default_1(RED) {
|
|
|
61
61
|
isRetained: false,
|
|
62
62
|
payload: undefined
|
|
63
63
|
};
|
|
64
|
+
let mqttService = null;
|
|
65
|
+
let topicHandler = null;
|
|
64
66
|
node.on('close', function () {
|
|
65
|
-
|
|
67
|
+
if (topicHandler && mqttService) {
|
|
68
|
+
mqttService.removeTopicHandler(node.selectedTag, topicHandler);
|
|
69
|
+
}
|
|
70
|
+
mqttService = null;
|
|
71
|
+
topicHandler = null;
|
|
66
72
|
});
|
|
67
73
|
(() => __awaiter(this, void 0, void 0, function* () {
|
|
68
74
|
try {
|
|
69
75
|
node.status({ fill: "yellow", shape: "dot", text: "Connecting..." });
|
|
70
76
|
// Use MACHHUB config from machhub.env.json to get MQTT service with status callback
|
|
71
|
-
let mqttService;
|
|
72
77
|
while (true) {
|
|
73
78
|
// console.log("Getting MQTT instance");
|
|
74
79
|
mqttService = yield mqtt_service_1.MQTTService.getInstance(machhubConfig, (status) => {
|
|
@@ -79,12 +84,13 @@ function default_1(RED) {
|
|
|
79
84
|
break;
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
|
-
|
|
87
|
+
topicHandler = (message, isRetained) => {
|
|
83
88
|
msg.payload = message;
|
|
84
89
|
msg.isRetained = isRetained;
|
|
85
90
|
node.send(msg);
|
|
86
91
|
node.status({ fill: "green", shape: "dot", text: "Payload Received" });
|
|
87
|
-
}
|
|
92
|
+
};
|
|
93
|
+
mqttService.addTopicHandler(node.selectedTag, topicHandler);
|
|
88
94
|
node.status({ fill: "green", shape: "dot", text: "MQTT Listening" });
|
|
89
95
|
}
|
|
90
96
|
catch (error) {
|
package/dist/nodes/tag-write.js
CHANGED
|
@@ -58,23 +58,17 @@ function default_1(RED) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
node.on('close', () => {
|
|
61
|
-
mqtt_service_1.MQTTService.resetInstance();
|
|
62
61
|
mqttService = null;
|
|
63
62
|
initializationPromise = null;
|
|
64
63
|
});
|
|
65
64
|
node.on('input', (msg) => __awaiter(this, void 0, void 0, function* () {
|
|
66
65
|
try {
|
|
67
66
|
node.status({ fill: "orange", shape: "dot", text: "Sending Payload" });
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (!mqttService) {
|
|
74
|
-
node.status({ fill: "red", shape: "dot", text: "MQTT service not initialized" });
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
yield mqttService.publish(node.selectedTag, msg.payload);
|
|
67
|
+
// Always fetch the current instance so we recover transparently after reconnects
|
|
68
|
+
const service = yield mqtt_service_1.MQTTService.getInstance(machhubConfig, (status) => {
|
|
69
|
+
node.status(status);
|
|
70
|
+
});
|
|
71
|
+
yield service.publish(node.selectedTag, msg.payload);
|
|
78
72
|
node.status({ fill: "green", shape: "dot", text: "Payload Sent" });
|
|
79
73
|
}
|
|
80
74
|
catch (err) {
|