@turquoisebay/mqtt 0.1.13 → 0.1.14
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/CHANGELOG.md +6 -0
- package/README.md +1 -0
- package/dist/src/__mocks__/mqtt.d.ts +1 -0
- package/dist/src/__mocks__/mqtt.d.ts.map +1 -1
- package/dist/src/__mocks__/mqtt.js +5 -0
- package/dist/src/channel.js +2 -3
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +138 -57
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.14] - 2026-02-03
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Robust reconnection logic (handles broker down at startup and restarts)
|
|
12
|
+
- Clean reconnect scheduling + shutdown cleanup
|
|
13
|
+
|
|
8
14
|
## [0.1.13] - 2026-02-03
|
|
9
15
|
|
|
10
16
|
### Fixed
|
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ MQTT channel plugin for [OpenClaw](https://github.com/openclaw/openclaw) — bid
|
|
|
10
10
|
|
|
11
11
|
- 🔌 **Bidirectional messaging** — subscribe and publish to MQTT topics
|
|
12
12
|
- 🏠 **Home automation ready** — integrates with Home Assistant, Mosquitto, EMQX
|
|
13
|
+
- 🔁 **Robust reconnection** — recovers from broker restarts and cold starts
|
|
13
14
|
- 🔒 **TLS support** — secure connections to cloud brokers
|
|
14
15
|
- 📊 **Service monitoring** — receive alerts from Uptime Kuma, healthchecks, etc.
|
|
15
16
|
- ⚡ **QoS levels** — configurable delivery guarantees (0, 1, 2)
|
|
@@ -21,6 +21,7 @@ export declare class MockMqttClient extends EventEmitter {
|
|
|
21
21
|
simulateMessage(topic: string, payload: Buffer | string): void;
|
|
22
22
|
simulateError(err: Error): void;
|
|
23
23
|
simulateDisconnect(): void;
|
|
24
|
+
reconnect(): this;
|
|
24
25
|
simulateReconnect(): void;
|
|
25
26
|
}
|
|
26
27
|
export declare function connect(url: string, opts?: unknown): MockMqttClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mqtt.d.ts","sourceRoot":"","sources":["../../../src/__mocks__/mqtt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC;;GAEG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,SAAS,UAAS;IAClB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAa;IACxD,SAAS,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAM;IAEzE,SAAS,CACP,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EACrB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI;IAOxC,OAAO,CACL,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,EACb,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI;IAOxC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,IAAI;IAQxD,eAAe;IAKf,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAKvD,aAAa,CAAC,GAAG,EAAE,KAAK;IAIxB,kBAAkB;IAKlB,iBAAiB;CAGlB;AAKD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,cAAc,CAKnE;AAGD,wBAAgB,aAAa,IAAI,cAAc,GAAG,IAAI,CAErD;AAGD,wBAAgB,SAAS,SAExB;;;;;;AAED,wBAAqD"}
|
|
1
|
+
{"version":3,"file":"mqtt.d.ts","sourceRoot":"","sources":["../../../src/__mocks__/mqtt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC;;GAEG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,SAAS,UAAS;IAClB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAa;IACxD,SAAS,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAM;IAEzE,SAAS,CACP,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EACrB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI;IAOxC,OAAO,CACL,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,EACb,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI;IAOxC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,IAAI;IAQxD,eAAe;IAKf,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAKvD,aAAa,CAAC,GAAG,EAAE,KAAK;IAIxB,kBAAkB;IAKlB,SAAS;IAMT,iBAAiB;CAGlB;AAKD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,cAAc,CAKnE;AAGD,wBAAgB,aAAa,IAAI,cAAc,GAAG,IAAI,CAErD;AAGD,wBAAgB,SAAS,SAExB;;;;;;AAED,wBAAqD"}
|
|
@@ -38,6 +38,11 @@ export class MockMqttClient extends EventEmitter {
|
|
|
38
38
|
this.connected = false;
|
|
39
39
|
this.emit("close");
|
|
40
40
|
}
|
|
41
|
+
reconnect() {
|
|
42
|
+
this.emit("reconnect");
|
|
43
|
+
setTimeout(() => this.simulateConnect(), 10);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
41
46
|
simulateReconnect() {
|
|
42
47
|
this.emit("reconnect");
|
|
43
48
|
}
|
package/dist/src/channel.js
CHANGED
|
@@ -67,12 +67,12 @@ export const mqttPlugin = {
|
|
|
67
67
|
gateway: {
|
|
68
68
|
startAccount: async (ctx) => {
|
|
69
69
|
const { cfg, account, accountId, abortSignal, log } = ctx;
|
|
70
|
-
const runtime = getMqttRuntime();
|
|
71
70
|
const mqtt = cfg.channels?.mqtt;
|
|
72
71
|
if (!mqtt?.brokerUrl) {
|
|
73
72
|
log?.debug?.("MQTT channel not configured, skipping");
|
|
74
73
|
return;
|
|
75
74
|
}
|
|
75
|
+
const runtime = getMqttRuntime();
|
|
76
76
|
log?.info?.(`[${accountId}] starting MQTT provider (${mqtt.brokerUrl})`);
|
|
77
77
|
// Create and connect client
|
|
78
78
|
mqttClient = createMqttClient(mqtt, {
|
|
@@ -85,8 +85,7 @@ export const mqttPlugin = {
|
|
|
85
85
|
await mqttClient.connect();
|
|
86
86
|
}
|
|
87
87
|
catch (err) {
|
|
88
|
-
log?.error?.(`MQTT connection failed: ${err}`);
|
|
89
|
-
throw err;
|
|
88
|
+
log?.error?.(`MQTT connection failed (will keep retrying): ${err}`);
|
|
90
89
|
}
|
|
91
90
|
// Subscribe to inbound topic
|
|
92
91
|
const inboundTopic = mqtt.topics?.inbound ?? "openclaw/inbound";
|
package/dist/src/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,MAAM,WAAW,iBAAiB;IAChC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACxD,WAAW,IAAI,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEtE,UAAU,MAAM;IACd,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,MAAM,WAAW,iBAAiB;IAChC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IACxD,WAAW,IAAI,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEtE,UAAU,MAAM;IACd,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAOD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,EAC9B,MAAM,EAAE,MAAM,GACb,iBAAiB,CA4QnB"}
|
package/dist/src/client.js
CHANGED
|
@@ -2,6 +2,8 @@ import mqtt from "mqtt";
|
|
|
2
2
|
import { mergeWithEnv } from "./env.js";
|
|
3
3
|
const DEFAULT_RECONNECT_MS = 5000;
|
|
4
4
|
const MAX_RECONNECT_MS = 60000;
|
|
5
|
+
const INITIAL_CONNECT_GRACE_MS = 5000;
|
|
6
|
+
const RECONNECT_JITTER = 0.2;
|
|
5
7
|
/**
|
|
6
8
|
* MQTT Client Manager
|
|
7
9
|
*
|
|
@@ -12,12 +14,15 @@ export function createMqttClient(rawConfig, logger) {
|
|
|
12
14
|
let client = null;
|
|
13
15
|
let messageHandlers = new Map();
|
|
14
16
|
let reconnectAttempts = 0;
|
|
17
|
+
let reconnectTimer = null;
|
|
18
|
+
let connectPromise = null;
|
|
19
|
+
let manualDisconnect = false;
|
|
15
20
|
function getClientOptions() {
|
|
16
21
|
const options = {
|
|
17
22
|
clientId: config.clientId ?? `openclaw-${Math.random().toString(36).slice(2, 10)}`,
|
|
18
23
|
clean: true,
|
|
19
24
|
connectTimeout: 10000,
|
|
20
|
-
reconnectPeriod:
|
|
25
|
+
reconnectPeriod: 0,
|
|
21
26
|
};
|
|
22
27
|
// Auth
|
|
23
28
|
if (config.username) {
|
|
@@ -36,80 +41,156 @@ export function createMqttClient(rawConfig, logger) {
|
|
|
36
41
|
}
|
|
37
42
|
return options;
|
|
38
43
|
}
|
|
44
|
+
function clearReconnectTimer() {
|
|
45
|
+
if (reconnectTimer) {
|
|
46
|
+
clearTimeout(reconnectTimer);
|
|
47
|
+
reconnectTimer = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function getBackoffDelay(attempt) {
|
|
51
|
+
const base = Math.min(DEFAULT_RECONNECT_MS * Math.pow(2, Math.max(0, attempt - 1)), MAX_RECONNECT_MS);
|
|
52
|
+
const jitter = base * RECONNECT_JITTER * Math.random();
|
|
53
|
+
return Math.round(base + jitter);
|
|
54
|
+
}
|
|
55
|
+
function scheduleReconnect(reason) {
|
|
56
|
+
if (manualDisconnect)
|
|
57
|
+
return;
|
|
58
|
+
if (reconnectTimer)
|
|
59
|
+
return;
|
|
60
|
+
reconnectAttempts += 1;
|
|
61
|
+
const delay = getBackoffDelay(reconnectAttempts);
|
|
62
|
+
logger.warn(`MQTT reconnect scheduled in ${delay}ms (${reason})`);
|
|
63
|
+
reconnectTimer = setTimeout(() => {
|
|
64
|
+
reconnectTimer = null;
|
|
65
|
+
if (manualDisconnect)
|
|
66
|
+
return;
|
|
67
|
+
if (!client) {
|
|
68
|
+
connect().catch((err) => logger.error(`MQTT reconnect failed: ${err}`));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
logger.info("MQTT reconnecting...");
|
|
73
|
+
client.reconnect();
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
logger.error(`MQTT reconnect error: ${err}`);
|
|
77
|
+
scheduleReconnect("reconnect error");
|
|
78
|
+
}
|
|
79
|
+
}, delay);
|
|
80
|
+
}
|
|
81
|
+
function attachClientHandlers(activeClient) {
|
|
82
|
+
activeClient.on("connect", () => {
|
|
83
|
+
logger.info("MQTT connected");
|
|
84
|
+
reconnectAttempts = 0;
|
|
85
|
+
clearReconnectTimer();
|
|
86
|
+
// Resubscribe to all topics
|
|
87
|
+
for (const topic of messageHandlers.keys()) {
|
|
88
|
+
activeClient.subscribe(topic, { qos: config.qos }, (err) => {
|
|
89
|
+
if (err) {
|
|
90
|
+
logger.error(`Failed to subscribe to ${topic}: ${err.message}`);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
logger.debug(`Subscribed to ${topic}`);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
activeClient.on("message", (topic, payload) => {
|
|
99
|
+
logger.debug(`Received message on ${topic}: ${payload.length} bytes`);
|
|
100
|
+
const handlers = [...(messageHandlers.get(topic) ?? [])];
|
|
101
|
+
// Also check wildcard subscriptions (skip exact match to avoid duplicates)
|
|
102
|
+
for (const [pattern, patternHandlers] of messageHandlers) {
|
|
103
|
+
if (pattern === topic)
|
|
104
|
+
continue;
|
|
105
|
+
if (topicMatches(pattern, topic)) {
|
|
106
|
+
handlers.push(...patternHandlers);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const handler of handlers) {
|
|
110
|
+
try {
|
|
111
|
+
handler(topic, payload);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
logger.error(`Message handler error: ${err}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
activeClient.on("error", (err) => {
|
|
119
|
+
logger.error(`MQTT error: ${err.message}`);
|
|
120
|
+
scheduleReconnect("error");
|
|
121
|
+
});
|
|
122
|
+
activeClient.on("close", () => {
|
|
123
|
+
logger.warn("MQTT connection closed");
|
|
124
|
+
scheduleReconnect("close");
|
|
125
|
+
});
|
|
126
|
+
activeClient.on("reconnect", () => {
|
|
127
|
+
logger.info("MQTT reconnect event");
|
|
128
|
+
});
|
|
129
|
+
activeClient.on("offline", () => {
|
|
130
|
+
logger.warn("MQTT client offline");
|
|
131
|
+
scheduleReconnect("offline");
|
|
132
|
+
});
|
|
133
|
+
}
|
|
39
134
|
async function connect() {
|
|
40
135
|
if (client?.connected) {
|
|
41
136
|
logger.debug("MQTT already connected");
|
|
42
137
|
return;
|
|
43
138
|
}
|
|
44
|
-
|
|
139
|
+
if (connectPromise) {
|
|
140
|
+
return connectPromise;
|
|
141
|
+
}
|
|
142
|
+
manualDisconnect = false;
|
|
143
|
+
if (!client) {
|
|
45
144
|
logger.info(`Connecting to MQTT broker: ${config.brokerUrl}`);
|
|
46
145
|
const options = getClientOptions();
|
|
47
146
|
client = mqtt.connect(config.brokerUrl, options);
|
|
48
|
-
client
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
147
|
+
attachClientHandlers(client);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
logger.info("MQTT connect requested; reconnecting existing client");
|
|
151
|
+
try {
|
|
152
|
+
client.reconnect();
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
logger.error(`MQTT reconnect error: ${err}`);
|
|
156
|
+
scheduleReconnect("reconnect error");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
connectPromise = new Promise((resolve) => {
|
|
160
|
+
let settled = false;
|
|
161
|
+
const settle = () => {
|
|
162
|
+
if (settled)
|
|
163
|
+
return;
|
|
164
|
+
settled = true;
|
|
165
|
+
connectPromise = null;
|
|
62
166
|
resolve();
|
|
167
|
+
};
|
|
168
|
+
if (client?.connected) {
|
|
169
|
+
settle();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const timer = setTimeout(() => {
|
|
173
|
+
logger.warn(`MQTT initial connect not ready after ${INITIAL_CONNECT_GRACE_MS}ms; continuing retries in background`);
|
|
174
|
+
settle();
|
|
175
|
+
}, INITIAL_CONNECT_GRACE_MS);
|
|
176
|
+
client?.once("connect", () => {
|
|
177
|
+
clearTimeout(timer);
|
|
178
|
+
settle();
|
|
63
179
|
});
|
|
64
|
-
client.on("message", (topic, payload) => {
|
|
65
|
-
logger.debug(`Received message on ${topic}: ${payload.length} bytes`);
|
|
66
|
-
const handlers = [...(messageHandlers.get(topic) ?? [])];
|
|
67
|
-
// Also check wildcard subscriptions (skip exact match to avoid duplicates)
|
|
68
|
-
for (const [pattern, patternHandlers] of messageHandlers) {
|
|
69
|
-
if (pattern === topic)
|
|
70
|
-
continue;
|
|
71
|
-
if (topicMatches(pattern, topic)) {
|
|
72
|
-
handlers.push(...patternHandlers);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
for (const handler of handlers) {
|
|
76
|
-
try {
|
|
77
|
-
handler(topic, payload);
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
logger.error(`Message handler error: ${err}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
client.on("error", (err) => {
|
|
85
|
-
logger.error(`MQTT error: ${err.message}`);
|
|
86
|
-
reject(err);
|
|
87
|
-
});
|
|
88
|
-
client.on("close", () => {
|
|
89
|
-
logger.warn("MQTT connection closed");
|
|
90
|
-
});
|
|
91
|
-
client.on("reconnect", () => {
|
|
92
|
-
reconnectAttempts++;
|
|
93
|
-
const backoff = Math.min(DEFAULT_RECONNECT_MS * Math.pow(2, reconnectAttempts), MAX_RECONNECT_MS);
|
|
94
|
-
logger.info(`MQTT reconnecting (attempt ${reconnectAttempts}, backoff ${backoff}ms)`);
|
|
95
|
-
});
|
|
96
|
-
client.on("offline", () => {
|
|
97
|
-
logger.warn("MQTT client offline");
|
|
98
|
-
});
|
|
99
|
-
// Timeout for initial connection
|
|
100
|
-
setTimeout(() => {
|
|
101
|
-
if (!client?.connected) {
|
|
102
|
-
reject(new Error("MQTT connection timeout"));
|
|
103
|
-
}
|
|
104
|
-
}, 15000);
|
|
105
180
|
});
|
|
181
|
+
return connectPromise;
|
|
106
182
|
}
|
|
107
183
|
async function disconnect() {
|
|
108
184
|
if (!client)
|
|
109
185
|
return;
|
|
186
|
+
manualDisconnect = true;
|
|
187
|
+
clearReconnectTimer();
|
|
188
|
+
reconnectAttempts = 0;
|
|
189
|
+
connectPromise = null;
|
|
110
190
|
return new Promise((resolve) => {
|
|
111
191
|
logger.info("Disconnecting from MQTT broker");
|
|
112
192
|
client?.end(false, {}, () => {
|
|
193
|
+
client?.removeAllListeners();
|
|
113
194
|
client = null;
|
|
114
195
|
messageHandlers.clear();
|
|
115
196
|
logger.info("MQTT disconnected");
|