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 +3 -3
- package/src/functions.js +7 -7
- package/src/melcloudhome.js +4 -5
- package/src/mqtt.js +100 -33
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.
|
|
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
|
-
"
|
|
39
|
+
"mqtt": "^5.14.1",
|
|
40
40
|
"axios": "^1.13.2",
|
|
41
41
|
"express": "^5.2.1",
|
|
42
|
-
"puppeteer": "^24.32.
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
162
|
+
// Linux x64
|
|
163
163
|
if (isLinux) {
|
|
164
164
|
const linuxCandidates = [
|
|
165
165
|
"/usr/bin/chromium",
|
package/src/melcloudhome.js
CHANGED
|
@@ -228,7 +228,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
228
228
|
// Get Chromium path
|
|
229
229
|
let chromiumPath = await this.functions.ensureChromiumInstalled();
|
|
230
230
|
|
|
231
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
40
|
-
}
|
|
41
|
-
})
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
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.
|
|
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;
|