homebridge-melcloud-control 4.3.13 → 4.3.15-beta.0
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 +1 -1
- package/src/functions.js +94 -65
- package/src/melcloudhome.js +11 -16
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.
|
|
4
|
+
"version": "4.3.15-beta.0",
|
|
5
5
|
"description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "grzegorz914",
|
package/src/functions.js
CHANGED
|
@@ -55,106 +55,135 @@ class Functions extends EventEmitter {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
async ensureChromiumInstalled() {
|
|
58
|
-
let chromiumPath =
|
|
58
|
+
let chromiumPath = null;
|
|
59
59
|
|
|
60
60
|
try {
|
|
61
|
-
// Detect OS
|
|
62
|
-
const { stdout: osOut } = await execPromise(
|
|
61
|
+
// --- Detect OS ---
|
|
62
|
+
const { stdout: osOut } = await execPromise("uname -s");
|
|
63
63
|
const osName = osOut.trim();
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// Detect Architecture
|
|
67
|
-
const { stdout: archOut } = await execPromise('uname -m');
|
|
64
|
+
const { stdout: archOut } = await execPromise("uname -m");
|
|
68
65
|
const arch = archOut.trim();
|
|
69
|
-
if (this.logDebug) this.emit('debug', `Detected architecture: ${arch}`);
|
|
70
66
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
await access('/.dockerenv', fs.constants.F_OK);
|
|
75
|
-
isDocker = true;
|
|
76
|
-
} catch { }
|
|
67
|
+
const isARM = arch.startsWith("arm") || arch.startsWith("aarch64") || arch.startsWith("aarch");
|
|
68
|
+
const isMac = osName === "Darwin";
|
|
69
|
+
const isLinux = osName === "Linux";
|
|
77
70
|
|
|
71
|
+
// --- Detect Docker ---
|
|
72
|
+
let isDocker = false;
|
|
73
|
+
try { await access("/.dockerenv"); isDocker = true; } catch { }
|
|
78
74
|
try {
|
|
79
|
-
const { stdout } = await execPromise(
|
|
80
|
-
if (stdout.includes(
|
|
75
|
+
const { stdout } = await execPromise("cat /proc/1/cgroup || true");
|
|
76
|
+
if (stdout.includes("docker") || stdout.includes("containerd")) isDocker = true;
|
|
81
77
|
} catch { }
|
|
82
|
-
if (isDocker && this.logDebug) this.emit('debug', 'Running inside Docker container');
|
|
83
78
|
|
|
84
|
-
// macOS
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return
|
|
79
|
+
// --- macOS ---
|
|
80
|
+
if (isMac) {
|
|
81
|
+
const macCandidates = [
|
|
82
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
83
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium"
|
|
84
|
+
];
|
|
85
|
+
for (const p of macCandidates) {
|
|
86
|
+
try { await access(p, fs.constants.X_OK); return p; } catch { }
|
|
92
87
|
}
|
|
88
|
+
return null;
|
|
93
89
|
}
|
|
94
90
|
|
|
95
|
-
// ARM
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
91
|
+
// --- ARM / Raspberry Pi ---
|
|
92
|
+
if (isARM && isLinux) {
|
|
93
|
+
const armCandidates = [
|
|
94
|
+
"/usr/bin/chromium-browser",
|
|
95
|
+
"/usr/bin/chromium",
|
|
96
|
+
"/snap/bin/chromium"
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// Try existing
|
|
100
|
+
for (const p of armCandidates) {
|
|
101
|
+
try { await access(p, fs.constants.X_OK); return p; } catch { }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// If not in Docker, try apt installation
|
|
105
|
+
if (!isDocker) {
|
|
106
|
+
try { await execPromise("sudo apt-get update -y"); } catch { }
|
|
107
|
+
try { await execPromise("sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg || true"); } catch { }
|
|
108
|
+
try { await execPromise("sudo apt-get install -y chromium || true"); } catch { }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Retry after installation
|
|
112
|
+
for (const p of armCandidates) {
|
|
113
|
+
try { await access(p, fs.constants.X_OK); return p; } catch { }
|
|
107
114
|
}
|
|
115
|
+
|
|
116
|
+
return null;
|
|
108
117
|
}
|
|
109
118
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
// --- QNAP / Entware ---
|
|
120
|
+
let entwareExists = false;
|
|
121
|
+
try { await access("/opt/bin/opkg", fs.constants.X_OK); entwareExists = true; } catch { }
|
|
122
|
+
|
|
123
|
+
if (entwareExists) {
|
|
113
124
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
125
|
+
await execPromise("/opt/bin/opkg update");
|
|
126
|
+
await execPromise("/opt/bin/opkg install nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib");
|
|
127
|
+
process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ""}`;
|
|
116
128
|
} catch { }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// --- Synology DSM 7 ---
|
|
132
|
+
const synoCandidates = [
|
|
133
|
+
"/var/packages/Chromium/target/usr/bin/chromium",
|
|
134
|
+
"/usr/local/chromium/bin/chromium"
|
|
135
|
+
];
|
|
136
|
+
for (const p of synoCandidates) {
|
|
137
|
+
try { await access(p, fs.constants.X_OK); return p; } catch { }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// --- Linux x64 ---
|
|
141
|
+
if (isLinux) {
|
|
142
|
+
const linuxCandidates = [
|
|
143
|
+
"/usr/bin/chromium",
|
|
144
|
+
"/usr/bin/chromium-browser",
|
|
145
|
+
"/usr/bin/google-chrome",
|
|
146
|
+
"/snap/bin/chromium",
|
|
147
|
+
"/usr/local/bin/chromium"
|
|
148
|
+
];
|
|
117
149
|
|
|
118
|
-
// Entware (QNAP)
|
|
119
|
-
let entwareExists = false;
|
|
120
150
|
try {
|
|
121
|
-
await
|
|
122
|
-
|
|
151
|
+
const { stdout } = await execPromise("which chromium || which chromium-browser || which google-chrome || true");
|
|
152
|
+
const found = stdout.trim();
|
|
153
|
+
if (found) return found;
|
|
123
154
|
} catch { }
|
|
124
155
|
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
156
|
+
for (const p of linuxCandidates) {
|
|
157
|
+
try { await access(p, fs.constants.X_OK); return p; } catch { }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Docker: try installing chromium inside container (if allowed)
|
|
161
|
+
if (isDocker) {
|
|
162
|
+
try { await execPromise("apt-get update -y && apt-get install -y chromium || true"); } catch { }
|
|
163
|
+
try { await access("/usr/bin/chromium", fs.constants.X_OK); return "/usr/bin/chromium"; } catch { }
|
|
131
164
|
}
|
|
132
165
|
|
|
133
|
-
// Install missing
|
|
166
|
+
// Install missing libraries
|
|
134
167
|
const depCommands = [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
168
|
+
"apt-get update -y && apt-get install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2 || true",
|
|
169
|
+
"yum install -y nspr nss libX11 libXcomposite libXdamage libXrandr atk cups libdrm libgbm alsa-lib || true",
|
|
170
|
+
"apk add --no-cache nspr nss libx11 libxcomposite libxdamage libxrandr atk cups libdrm libgbm alsa-lib || true"
|
|
138
171
|
];
|
|
139
|
-
|
|
140
172
|
for (const cmd of depCommands) {
|
|
141
|
-
try {
|
|
142
|
-
await execPromise(`sudo ${cmd}`);
|
|
143
|
-
} catch { }
|
|
173
|
+
try { await execPromise(`sudo ${cmd}`); } catch { }
|
|
144
174
|
}
|
|
145
175
|
|
|
146
|
-
|
|
147
|
-
return systemChromium;
|
|
176
|
+
return null;
|
|
148
177
|
}
|
|
149
178
|
|
|
150
|
-
if (this.logDebug) this.emit('debug', `Unsupported OS: ${osName}`);
|
|
151
179
|
return null;
|
|
152
|
-
} catch (
|
|
153
|
-
if (this.logError) this.emit(
|
|
180
|
+
} catch (err) {
|
|
181
|
+
if (this.logError) this.emit("error", `Chromium detection error: ${err.message}`);
|
|
154
182
|
return null;
|
|
155
183
|
}
|
|
156
184
|
}
|
|
157
185
|
|
|
186
|
+
|
|
158
187
|
isValidValue(v) {
|
|
159
188
|
return v !== undefined && v !== null && !(typeof v === 'number' && Number.isNaN(v));
|
|
160
189
|
}
|
package/src/melcloudhome.js
CHANGED
|
@@ -228,18 +228,13 @@ class MelCloudHome extends EventEmitter {
|
|
|
228
228
|
// Get Chromium path
|
|
229
229
|
let chromiumPath = await this.functions.ensureChromiumInstalled();
|
|
230
230
|
|
|
231
|
-
// === Fallback to Puppeteer's
|
|
231
|
+
// === Fallback to Puppeteer's bundled Chromium ===
|
|
232
232
|
if (!chromiumPath) {
|
|
233
233
|
try {
|
|
234
234
|
const puppeteerPath = puppeteer.executablePath();
|
|
235
235
|
if (puppeteerPath && puppeteerPath.length > 0) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (this.logDebug) this.emit('debug', `Using Puppeteer bundled Chromium at ${chromiumPath}`);
|
|
239
|
-
} else {
|
|
240
|
-
accountInfo.Info = `Puppeteer returned path, but file does not exist: ${puppeteerPath}`;
|
|
241
|
-
return accountInfo;
|
|
242
|
-
}
|
|
236
|
+
chromiumPath = puppeteerPath;
|
|
237
|
+
if (this.logDebug) this.emit('debug', `Using Puppeteer bundled Chromium at ${chromiumPath}`);
|
|
243
238
|
} else {
|
|
244
239
|
accountInfo.Info = `Puppeteer returned empty Chromium path`;
|
|
245
240
|
return accountInfo;
|
|
@@ -250,7 +245,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
250
245
|
}
|
|
251
246
|
}
|
|
252
247
|
|
|
253
|
-
// Verify executable
|
|
248
|
+
// === Verify Chromium executable ===
|
|
254
249
|
try {
|
|
255
250
|
const { stdout } = await execPromise(`"${chromiumPath}" --version`);
|
|
256
251
|
if (this.logDebug) this.emit('debug', `Chromium detected: ${stdout.trim()}`);
|
|
@@ -290,7 +285,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
290
285
|
if (url.startsWith(`${ApiUrlsHome.WebSocketURL}`)) {
|
|
291
286
|
const params = new URL(url).searchParams;
|
|
292
287
|
const hash = params.get('hash');
|
|
293
|
-
if (this.logDebug) this.emit('debug', `
|
|
288
|
+
if (this.logDebug) this.emit('debug', `Web socket hash detected: ${hash}`);
|
|
294
289
|
|
|
295
290
|
//web socket connection
|
|
296
291
|
if (!this.connecting && !this.socketConnected) {
|
|
@@ -304,24 +299,24 @@ class MelCloudHome extends EventEmitter {
|
|
|
304
299
|
};
|
|
305
300
|
const webSocket = new WebSocket(`${ApiUrlsHome.WebSocketURL}${hash}`, { headers: headers })
|
|
306
301
|
.on('error', (error) => {
|
|
307
|
-
if (this.logError) this.emit('error', `
|
|
302
|
+
if (this.logError) this.emit('error', `Web socket error: ${error}`);
|
|
308
303
|
try {
|
|
309
304
|
webSocket.close();
|
|
310
305
|
} catch { }
|
|
311
306
|
})
|
|
312
307
|
.on('close', () => {
|
|
313
|
-
if (this.logDebug) this.emit('debug', `
|
|
308
|
+
if (this.logDebug) this.emit('debug', `Web socket closed`);
|
|
314
309
|
this.cleanupSocket();
|
|
315
310
|
})
|
|
316
311
|
.on('open', () => {
|
|
317
312
|
this.socketConnected = true;
|
|
318
313
|
this.connecting = false;
|
|
319
|
-
if (this.logSuccess) this.emit('success', `Socket Connect Success`);
|
|
314
|
+
if (this.logSuccess) this.emit('success', `Web Socket Connect Success`);
|
|
320
315
|
|
|
321
316
|
// heartbeat
|
|
322
317
|
this.heartbeat = setInterval(() => {
|
|
323
318
|
if (webSocket.readyState === webSocket.OPEN) {
|
|
324
|
-
if (this.logDebug) this.emit('debug', `
|
|
319
|
+
if (this.logDebug) this.emit('debug', `Web socket send heartbeat`);
|
|
325
320
|
webSocket.ping();
|
|
326
321
|
}
|
|
327
322
|
}, 30000);
|
|
@@ -337,13 +332,13 @@ class MelCloudHome extends EventEmitter {
|
|
|
337
332
|
this.emit('webSocket', parsedMessage);
|
|
338
333
|
});
|
|
339
334
|
} catch (error) {
|
|
340
|
-
if (this.logError) this.emit('error', `
|
|
335
|
+
if (this.logError) this.emit('error', `Web socket connection failed: ${error}`);
|
|
341
336
|
this.cleanupSocket();
|
|
342
337
|
}
|
|
343
338
|
}
|
|
344
339
|
}
|
|
345
340
|
} catch (error) {
|
|
346
|
-
if (this.logError) this.emit('error', `CDP
|
|
341
|
+
if (this.logError) this.emit('error', `CDP web socket created handler error: ${error.message}`);
|
|
347
342
|
}
|
|
348
343
|
});
|
|
349
344
|
|