homebridge-melcloud-control 4.3.16-beta.1 → 4.3.16-beta.3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.3.16-beta.1",
4
+ "version": "4.3.16-beta.3",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
@@ -36,10 +36,10 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@homebridge/plugin-ui-utils": "^2.1.2",
39
- "async-mqtt": "^2.6.3",
39
+ "mqtt": "^5.14.1",
40
40
  "axios": "^1.13.2",
41
41
  "express": "^5.2.1",
42
- "puppeteer": "^24.32.0",
42
+ "puppeteer": "^24.32.1",
43
43
  "ws": "^8.18.3"
44
44
  },
45
45
  "keywords": [
package/src/functions.js CHANGED
@@ -56,7 +56,7 @@ class Functions extends EventEmitter {
56
56
 
57
57
  async ensureChromiumInstalled() {
58
58
  try {
59
- // --- Detect OS ---
59
+ // Detect OS
60
60
  const { stdout: osOut } = await execPromise("uname -s");
61
61
  const osName = osOut.trim();
62
62
  const { stdout: archOut } = await execPromise("uname -m");
@@ -66,7 +66,7 @@ class Functions extends EventEmitter {
66
66
  const isMac = osName === "Darwin";
67
67
  const isLinux = osName === "Linux";
68
68
 
69
- // --- Detect Docker ---
69
+ // Detect Docker
70
70
  let isDocker = false;
71
71
  try {
72
72
  await access("/.dockerenv");
@@ -77,7 +77,7 @@ class Functions extends EventEmitter {
77
77
  if (stdout.includes("docker") || stdout.includes("containerd")) isDocker = true;
78
78
  } catch { }
79
79
 
80
- // --- macOS ---
80
+ // macOS
81
81
  if (isMac) {
82
82
  const macCandidates = [
83
83
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
@@ -92,7 +92,7 @@ class Functions extends EventEmitter {
92
92
  return null;
93
93
  }
94
94
 
95
- // --- ARM / Raspberry Pi ---
95
+ // ARM / Raspberry Pi
96
96
  if (isARM && isLinux) {
97
97
  const armCandidates = [
98
98
  "/usr/bin/chromium-browser",
@@ -132,7 +132,7 @@ class Functions extends EventEmitter {
132
132
  return null;
133
133
  }
134
134
 
135
- // --- QNAP / Entware ---
135
+ // QNAP / Entware
136
136
  let entwareExists = false;
137
137
  try {
138
138
  await access("/opt/bin/opkg", fs.constants.X_OK);
@@ -147,7 +147,7 @@ class Functions extends EventEmitter {
147
147
  } catch { }
148
148
  }
149
149
 
150
- // --- Synology DSM 7 ---
150
+ // Synology DSM 7
151
151
  const synoCandidates = [
152
152
  "/var/packages/Chromium/target/usr/bin/chromium",
153
153
  "/usr/local/chromium/bin/chromium"
@@ -159,7 +159,7 @@ class Functions extends EventEmitter {
159
159
  } catch { }
160
160
  }
161
161
 
162
- // --- Linux x64 ---
162
+ // Linux x64
163
163
  if (isLinux) {
164
164
  const linuxCandidates = [
165
165
  "/usr/bin/chromium",
@@ -228,7 +228,7 @@ class MelCloudHome extends EventEmitter {
228
228
  // Get Chromium path
229
229
  let chromiumPath = await this.functions.ensureChromiumInstalled();
230
230
 
231
- // === Fallback to Puppeteer's bundled Chromium ===
231
+ // Fallback to Puppeteer's bundled Chromium
232
232
  if (!chromiumPath) {
233
233
  try {
234
234
  const puppeteerPath = puppeteer.executablePath();
@@ -245,7 +245,7 @@ class MelCloudHome extends EventEmitter {
245
245
  }
246
246
  }
247
247
 
248
- // === Verify Chromium executable ===
248
+ // Verify Chromium executable
249
249
  try {
250
250
  const { stdout } = await execPromise(`"${chromiumPath}" --version`);
251
251
  if (this.logDebug) this.emit('debug', `Chromium detected: ${stdout.trim()}`);
@@ -277,7 +277,7 @@ class MelCloudHome extends EventEmitter {
277
277
  page.setDefaultTimeout(GLOBAL_TIMEOUT);
278
278
  page.setDefaultNavigationTimeout(GLOBAL_TIMEOUT);
279
279
 
280
- // === CDP session ===
280
+ // CDP session
281
281
  const client = await page.createCDPSession();
282
282
  await client.send('Network.enable')
283
283
  client.on('Network.webSocketCreated', ({ url }) => {
@@ -311,7 +311,7 @@ class MelCloudHome extends EventEmitter {
311
311
  .on('open', () => {
312
312
  this.socketConnected = true;
313
313
  this.connecting = false;
314
- if (this.logDebug) this.emit('debug', `Web Socket Connect Success`);
314
+ if (this.logDebug) this.emit('debug', `Web Socket Connected`);
315
315
 
316
316
  // heartbeat
317
317
  this.heartbeat = setInterval(() => {
@@ -400,7 +400,6 @@ class MelCloudHome extends EventEmitter {
400
400
  `__Secure-monitorandcontrolC2=${c2}`
401
401
  ].join('; ');
402
402
 
403
-
404
403
  const userAgent = await page.evaluate(() => navigator.userAgent);
405
404
  const headers = {
406
405
  'Accept': '*/*',
package/src/mqtt.js CHANGED
@@ -1,56 +1,123 @@
1
- import asyncMqtt from 'async-mqtt';
2
- const { connectAsync } = asyncMqtt;
1
+ import { connect } from 'mqtt';
3
2
  import EventEmitter from 'events';
4
3
 
5
4
  class Mqtt extends EventEmitter {
6
5
  constructor(config) {
7
6
  super();
7
+
8
+ const url = `mqtt://${config.host}:${config.port}`;
9
+ const subscribeTopic = `${config.prefix}/Set`;
10
+
8
11
  const options = {
9
12
  clientId: config.clientId,
10
13
  username: config.user,
11
- password: config.passwd
12
- }
13
- const url = `mqtt://${config.host}:${config.port}`;
14
- const subscribeTopic = `${config.prefix}/Set`;
14
+ password: config.passwd,
15
+ protocolVersion: 5,
16
+ clean: false,
17
+ properties: {
18
+ sessionExpiryInterval: 60 * 60, // 1 hour
19
+ userProperties: {
20
+ source: 'node-client'
21
+ }
22
+ }
23
+ };
24
+
25
+ this.mqttClient = connect(url, options);
26
+
27
+ // === CONNECTED ===
28
+ this.mqttClient.on('connect', async (packet) => {
29
+ this.emit('connected', 'MQTT v5 connected.');
15
30
 
16
- this.on('connect', async () => {
17
31
  try {
18
- //connect
19
- this.mqttClient = await connectAsync(url, options);
20
- this.emit('connected', 'MQTT Connected.');
32
+ const result = await this.mqttClient.subscribeAsync(subscribeTopic, {
33
+ qos: 1,
34
+ properties: {
35
+ userProperties: {
36
+ type: 'subscription'
37
+ }
38
+ }
39
+ });
40
+
41
+ // MQTT v5 subscription results contain reason codes
42
+ if (config.logDebug) {
43
+ this.emit(
44
+ 'debug',
45
+ `Subscribed to ${subscribeTopic}, reason codes: ${JSON.stringify(result)}`
46
+ );
47
+ }
21
48
 
22
- //subscribe
23
- await this.mqttClient.subscribe(subscribeTopic);
24
49
  this.emit('subscribed', `MQTT Subscribe topic: ${subscribeTopic}`);
25
50
 
26
- //subscribed message
27
- this.mqttClient.on('message', (topic, message) => {
28
- try {
29
- const obj = JSON.parse(message.toString());
30
- if (config.logDebug) this.emit('debug', `MQTT Received topic: ${topic}, message: ${JSON.stringify(obj, null, 2)}`);
31
- const key = Object.keys(obj)[0];
32
- const value = Object.values(obj)[0];
33
- this.emit('set', key, value);
34
- } catch (error) {
35
- if (config.logWarn) this.emit('warn', `MQTT Parse object error: ${error}`);
36
- };
37
- });
38
51
  } catch (error) {
39
- if (config.logWarn) this.emit('warn', `MQTT Connect error: ${error}`);
40
- };
41
- }).on('publish', async (topic, message) => {
52
+ if (config.logWarn) this.emit('warn', `MQTT Subscribe error: ${error}`);
53
+ }
54
+ });
55
+
56
+ // === MESSAGE ===
57
+ this.mqttClient.on('message', (topic, payload, packet) => {
58
+ try {
59
+ const obj = JSON.parse(payload.toString());
60
+
61
+ if (config.logDebug) {
62
+ this.emit(
63
+ 'debug',
64
+ `MQTT Received:\nTopic: ${topic}\nPayload: ${JSON.stringify(obj, null, 2)}\nProperties: ${JSON.stringify(packet.properties, null, 2)}`
65
+ );
66
+ }
67
+
68
+ const key = Object.keys(obj)[0];
69
+ const value = Object.values(obj)[0];
70
+ this.emit('set', key, value);
71
+
72
+ } catch (error) {
73
+ if (config.logWarn) {
74
+ this.emit('warn', `MQTT Parse error: ${error}`);
75
+ }
76
+ }
77
+ });
78
+
79
+ // === PUBLISH EVENT ===
80
+ this.on('publish', async (topic, message) => {
42
81
  try {
43
82
  const fullTopic = `${config.prefix}/${topic}`;
44
- const publishMessage = JSON.stringify(message, null, 2);
45
- await this.mqttClient.publish(fullTopic, publishMessage);
46
- if (config.logDebug) this.emit('debug', `MQTT Publish topic: ${fullTopic}, message: ${publishMessage}`);
83
+ const publishMessage = JSON.stringify(message);
84
+
85
+ await this.mqttClient.publishAsync(fullTopic, publishMessage, {
86
+ qos: 1,
87
+ properties: {
88
+ contentType: 'application/json',
89
+ userProperties: {
90
+ source: 'node',
91
+ action: 'set'
92
+ }
93
+ }
94
+ });
95
+
96
+ if (config.logDebug) {
97
+ this.emit(
98
+ 'debug',
99
+ `MQTT Publish:\nTopic: ${fullTopic}\nPayload: ${publishMessage}`
100
+ );
101
+ }
102
+
47
103
  } catch (error) {
48
104
  if (config.logWarn) this.emit('warn', `MQTT Publish error: ${error}`);
49
- };
105
+ }
106
+ });
107
+
108
+ // === ERRORS / STATE ===
109
+ this.mqttClient.on('error', (err) => {
110
+ this.emit('warn', `MQTT Error: ${err.message}`);
50
111
  });
51
112
 
52
- this.emit('connect');
113
+ this.mqttClient.on('reconnect', () => {
114
+ if (config.logDebug) this.emit('debug', 'MQTT Reconnecting...');
115
+ });
116
+
117
+ this.mqttClient.on('close', () => {
118
+ if (config.logDebug) this.emit('debug', 'MQTT Connection closed.');
119
+ });
53
120
  }
54
121
  }
55
122
 
56
- export default Mqtt;
123
+ export default Mqtt;