@machhub-dev/node-red-nodes 1.0.9-test → 1.0.10-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.
@@ -15,11 +15,13 @@ 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
+ }
18
21
  static getInstance(settings, statusCallback) {
19
22
  return __awaiter(this, void 0, void 0, function* () {
20
23
  // If we already have an instance, return it
21
24
  if (this.instance && this.instance.client && this.instance.client.connected) {
22
- console.log("Returning existing MQTT instance");
23
25
  if (statusCallback) {
24
26
  statusCallback({ fill: "green", shape: "dot", text: "MQTT Connected" });
25
27
  }
@@ -39,6 +41,7 @@ class MQTTService {
39
41
  this.instancePromise = this.createInstance(settings, statusCallback);
40
42
  try {
41
43
  this.instance = yield this.instancePromise;
44
+ this.instancePromise = null; // Clear so future calls re-evaluate fresh state
42
45
  return this.instance;
43
46
  }
44
47
  catch (error) {
@@ -106,17 +109,8 @@ class MQTTService {
106
109
  clientId: settings.clientId,
107
110
  username: settings.clientId,
108
111
  password: settings.clientSecret,
109
- reconnectPeriod: 0, // disable MQTT.js auto-reconnect; we manage reconnection via getInstance()
110
112
  });
111
113
  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
- }
120
114
  // console.log("MQTT connection established successfully");
121
115
  if (statusCallback) {
122
116
  statusCallback({ fill: "green", shape: "dot", text: "MQTT Connected" });
@@ -134,22 +128,19 @@ class MQTTService {
134
128
  return instance;
135
129
  });
136
130
  }
137
- // Method to reset the instance and all state (full shutdown / redeploy)
131
+ // Method to reset the instance
138
132
  static resetInstance() {
139
- const inst = this.instance;
140
- // Clear references first so the 'close' event handler guard doesn't re-null unnecessarily
133
+ if (this.instance && this.instance.client) {
134
+ this.instance.client.end();
135
+ }
141
136
  this.instance = null;
142
137
  this.instancePromise = null;
143
138
  this.stopConnecting = true;
144
- this.subscribedTopics = [];
145
- if (inst && inst.client) {
146
- inst.client.end();
147
- }
148
139
  }
149
140
  // Adds a topic and handler to the subscribed list
150
141
  addTopicHandler(topic, handler) {
151
142
  try {
152
- MQTTService.subscribedTopics.push({ topic, handler });
143
+ this.subscribedTopics.push({ topic, handler });
153
144
  // console.log("New Subscription Handler : ", topic)
154
145
  this.client.subscribe(topic, { qos: 2 }, (err) => {
155
146
  if (err) {
@@ -161,23 +152,6 @@ class MQTTService {
161
152
  console.error(`Failed to subscribe to topic ${topic}:`, e);
162
153
  }
163
154
  }
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
- }
181
155
  // Publishes a message to a specific topic
182
156
  publish(topic, message) {
183
157
  try {
@@ -202,7 +176,7 @@ class MQTTService {
202
176
  }
203
177
  attachMessageListener() {
204
178
  this.client.on('message', (topic, message, packet) => {
205
- for (const subscribedTopic of MQTTService.subscribedTopics) {
179
+ for (const subscribedTopic of this.subscribedTopics) {
206
180
  if (this.matchesTopic(subscribedTopic.topic, topic)) {
207
181
  const parsedMessage = this.parseMessage(message, topic);
208
182
  subscribedTopic.handler(parsedMessage, packet.retain);
@@ -210,14 +184,6 @@ class MQTTService {
210
184
  }
211
185
  }
212
186
  });
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
- });
221
187
  }
222
188
  // Matches MQTT topic patterns with wildcards (+ for single level, # for multi-level)
223
189
  matchesTopic(pattern, topic) {
@@ -266,4 +232,3 @@ class MQTTService {
266
232
  exports.MQTTService = MQTTService;
267
233
  MQTTService.instancePromise = null;
268
234
  MQTTService.stopConnecting = false;
269
- MQTTService.subscribedTopics = [];
@@ -56,6 +56,7 @@ function default_1(RED) {
56
56
  }
57
57
  }
58
58
  node.on('close', () => {
59
+ mqtt_service_1.MQTTService.resetInstance();
59
60
  mqttService = null;
60
61
  initializationPromise = null;
61
62
  });
@@ -63,13 +64,18 @@ function default_1(RED) {
63
64
  return __awaiter(this, void 0, void 0, function* () {
64
65
  try {
65
66
  node.status({ fill: "orange", shape: "dot", text: "Sending Payload" });
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
- });
67
+ // Wait for initialization if still in progress
68
+ if (isInitializing && initializationPromise) {
69
+ yield initializationPromise;
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
+ }
70
76
  Object.keys(msg.payload).forEach(function (key) {
71
77
  try {
72
- service.publish(key, msg.payload[key]);
78
+ mqttService.publish(key, msg.payload[key]);
73
79
  }
74
80
  catch (error) {
75
81
  node.status({ fill: "red", shape: "dot", text: "Failed to publish to '" + key + "': " + error.toString() });
@@ -88,16 +94,9 @@ function default_1(RED) {
88
94
  isInitializing = true;
89
95
  node.status({ fill: "yellow", shape: "dot", text: "Connecting..." });
90
96
  // Use MACHHUB config to get MQTT service with status callback
91
- while (true) {
92
- // console.log("Getting MQTT instance");
93
- mqttService = yield mqtt_service_1.MQTTService.getInstance(machhubConfig, (status) => {
94
- node.status(status);
95
- });
96
- // console.log("MQTT Service obtained client:", mqttService.client);
97
- if (mqttService.client) {
98
- break;
99
- }
100
- }
97
+ mqttService = yield mqtt_service_1.MQTTService.getInstance(machhubConfig, (status) => {
98
+ node.status(status);
99
+ });
101
100
  node.status({ fill: "green", shape: "dot", text: "Ready to publish" });
102
101
  }
103
102
  catch (error) {
@@ -61,36 +61,22 @@ function default_1(RED) {
61
61
  isRetained: false,
62
62
  payload: undefined
63
63
  };
64
- let mqttService = null;
65
- let topicHandler = null;
66
64
  node.on('close', function () {
67
- if (topicHandler && mqttService) {
68
- mqttService.removeTopicHandler(node.selectedTag, topicHandler);
69
- }
70
- mqttService = null;
71
- topicHandler = null;
65
+ mqtt_service_1.MQTTService.resetInstance();
72
66
  });
73
67
  (() => __awaiter(this, void 0, void 0, function* () {
74
68
  try {
75
69
  node.status({ fill: "yellow", shape: "dot", text: "Connecting..." });
76
70
  // Use MACHHUB config from machhub.env.json to get MQTT service with status callback
77
- while (true) {
78
- // console.log("Getting MQTT instance");
79
- mqttService = yield mqtt_service_1.MQTTService.getInstance(machhubConfig, (status) => {
80
- node.status(status);
81
- });
82
- // console.log("MQTT Service obtained client:", mqttService.client);
83
- if (mqttService.client) {
84
- break;
85
- }
86
- }
87
- topicHandler = (message, isRetained) => {
71
+ const mqttService = yield mqtt_service_1.MQTTService.getInstance(machhubConfig, (status) => {
72
+ node.status(status);
73
+ });
74
+ mqttService.addTopicHandler(node.selectedTag, (message, isRetained) => {
88
75
  msg.payload = message;
89
76
  msg.isRetained = isRetained;
90
77
  node.send(msg);
91
78
  node.status({ fill: "green", shape: "dot", text: "Payload Received" });
92
- };
93
- mqttService.addTopicHandler(node.selectedTag, topicHandler);
79
+ });
94
80
  node.status({ fill: "green", shape: "dot", text: "MQTT Listening" });
95
81
  }
96
82
  catch (error) {
@@ -58,17 +58,23 @@ function default_1(RED) {
58
58
  }
59
59
  }
60
60
  node.on('close', () => {
61
+ mqtt_service_1.MQTTService.resetInstance();
61
62
  mqttService = null;
62
63
  initializationPromise = null;
63
64
  });
64
65
  node.on('input', (msg) => __awaiter(this, void 0, void 0, function* () {
65
66
  try {
66
67
  node.status({ fill: "orange", shape: "dot", text: "Sending 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);
68
+ // Wait for initialization if still in progress
69
+ if (isInitializing && initializationPromise) {
70
+ yield initializationPromise;
71
+ }
72
+ // Check if mqttService is initialized
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);
72
78
  node.status({ fill: "green", shape: "dot", text: "Payload Sent" });
73
79
  }
74
80
  catch (err) {
@@ -80,16 +86,9 @@ function default_1(RED) {
80
86
  try {
81
87
  isInitializing = true;
82
88
  node.status({ fill: "yellow", shape: "dot", text: "Connecting..." });
83
- while (true) {
84
- // console.log("Getting MQTT instance");
85
- mqttService = yield mqtt_service_1.MQTTService.getInstance(machhubConfig, (status) => {
86
- node.status(status);
87
- });
88
- // console.log("MQTT Service obtained client:", mqttService.client);
89
- if (mqttService.client) {
90
- break;
91
- }
92
- }
89
+ mqttService = yield mqtt_service_1.MQTTService.getInstance(machhubConfig, (status) => {
90
+ node.status(status);
91
+ });
93
92
  node.status({ fill: "green", shape: "dot", text: "Ready to publish" });
94
93
  }
95
94
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@machhub-dev/node-red-nodes",
3
- "version": "1.0.9-test",
3
+ "version": "1.0.10-test",
4
4
  "description": "Node-RED API for MACHHUB EDGE",
5
5
  "scripts": {
6
6
  "build": "yarn clean && npx tsc && yarn copy-files",