homebridge-melcloud-control 4.0.0-beta.52 → 4.0.0-beta.521

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/src/functions.js CHANGED
@@ -1,7 +1,17 @@
1
+ import fs from 'fs';
1
2
  import { promises as fsPromises } from 'fs';
3
+ import { promisify } from 'util';
4
+ import { exec } from 'child_process';
5
+ import EventEmitter from 'events';
6
+ const execPromise = promisify(exec);
7
+ const access = fs.promises.access;
2
8
 
3
- class Functions {
4
- constructor() {
9
+ class Functions extends EventEmitter {
10
+ constructor(logWarn, logError, logDebug) {
11
+ super();
12
+ this.logWarn = logWarn;
13
+ this.logError = logError;
14
+ this.logDebug = logDebug;
5
15
  }
6
16
 
7
17
  async saveData(path, data, stringify = true) {
@@ -14,12 +24,152 @@ class Functions {
14
24
  }
15
25
  }
16
26
 
17
- async readData(path) {
27
+ async readData(path, parseJson = false) {
18
28
  try {
19
- const data = await fsPromises.readFile(path);
29
+ const data = await fsPromises.readFile(path, 'utf8');
30
+
31
+ if (parseJson) {
32
+ if (!data.trim()) {
33
+ // Empty file when expecting JSON
34
+ return null;
35
+ }
36
+ try {
37
+ return JSON.parse(data);
38
+ } catch (jsonError) {
39
+ throw new Error(`JSON parse error in file "${path}": ${jsonError.message}`);
40
+ }
41
+ }
42
+
43
+ // For non-JSON, just return file content (can be empty string)
20
44
  return data;
21
45
  } catch (error) {
22
- throw new Error(`Read data error: ${error}`);
46
+ if (error.code === 'ENOENT') {
47
+ // File does not exist
48
+ return null;
49
+ }
50
+ // Preserve original error details
51
+ const wrappedError = new Error(`Read data error for "${path}": ${error.message}`);
52
+ wrappedError.original = error;
53
+ throw wrappedError;
54
+ }
55
+ }
56
+
57
+ async ensureChromiumInstalled() {
58
+ let chromiumPath = '/usr/bin/chromium-browser';
59
+
60
+ try {
61
+ // --- Detect OS ---
62
+ const { stdout: osOut } = await execPromise('uname -s');
63
+ const osName = osOut.trim();
64
+ if (this.logDebug) this.emit('debug', `Detected OS: ${osName}`);
65
+
66
+ // --- Detect Architecture ---
67
+ const { stdout: archOut } = await execPromise('uname -m');
68
+ const arch = archOut.trim();
69
+ if (this.logDebug) this.emit('debug', `Detected architecture: ${arch}`);
70
+
71
+ // --- Detect Docker environment ---
72
+ let isDocker = false;
73
+ try {
74
+ await access('/.dockerenv', fs.constants.F_OK);
75
+ isDocker = true;
76
+ } catch {
77
+ try {
78
+ const { stdout } = await execPromise('cat /proc/1/cgroup || true');
79
+ if (stdout.includes('docker') || stdout.includes('containerd'))
80
+ isDocker = true;
81
+ } catch { }
82
+ }
83
+
84
+ if (isDocker && this.logDebug) this.emit('debug', 'Running inside Docker container.');
85
+
86
+ // === macOS ===
87
+ if (osName === 'Darwin') {
88
+ chromiumPath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
89
+ try {
90
+ await access(chromiumPath, fs.constants.X_OK);
91
+ if (this.logDebug) this.emit('debug', `Using system Chrome at ${chromiumPath}`);
92
+ return chromiumPath;
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+
98
+ // === ARM (e.g. Raspberry Pi) ===
99
+ if (arch.startsWith('arm')) {
100
+ try {
101
+ await access('/usr/bin/chromium-browser', fs.constants.X_OK);
102
+ if (this.logDebug) this.emit('debug', 'Using system Chromium on ARM platform.');
103
+ return '/usr/bin/chromium-browser';
104
+ } catch {
105
+ if (this.logWarn) this.emit('warn', 'System Chromium not found on ARM. Attempting installation...');
106
+ try {
107
+ await execPromise('sudo apt-get update -y && sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg');
108
+ if (this.logDebug) this.emit('debug', 'Chromium installed successfully on ARM.');
109
+ return '/usr/bin/chromium-browser';
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ }
115
+
116
+ // === Linux (x64, Docker, etc.) ===
117
+ if (osName === 'Linux') {
118
+ try {
119
+ // --- Try detect common Chromium binaries ---
120
+ const { stdout: checkOut } = await execPromise('which chromium || which chromium-browser || true');
121
+ chromiumPath = checkOut.trim();
122
+ if (chromiumPath) {
123
+ if (this.logDebug) this.emit('debug', `Found system Chromium: ${chromiumPath}`);
124
+ return chromiumPath;
125
+ }
126
+ } catch { }
127
+
128
+ if (this.logWarn) this.emit('warn', 'Chromium not found. Attempting installation...');
129
+
130
+ // --- Try install (Docker-optimized first) ---
131
+ const installCommands = [
132
+ 'apt-get update -y && apt-get install -y chromium chromium-browser chromium-codecs-ffmpeg',
133
+ 'apk add --no-cache chromium ffmpeg',
134
+ 'yum install -y chromium chromium-codecs-ffmpeg'
135
+ ];
136
+
137
+ for (const cmd of installCommands) {
138
+ try {
139
+ if (this.logDebug) this.emit('debug', `Trying installation: ${cmd}`);
140
+ await execPromise(`sudo ${cmd}`);
141
+ // Check for binary after install
142
+ const { stdout: checkOut } = await execPromise('which chromium || which chromium-browser || true');
143
+ chromiumPath = checkOut.trim() || '/usr/bin/chromium';
144
+ if (chromiumPath) {
145
+ if (this.logDebug) this.emit('debug', `Chromium installed successfully at ${chromiumPath}`);
146
+ return chromiumPath;
147
+ }
148
+ } catch (error) {
149
+ if (this.logDebug) this.emit('debug', `Install attempt failed: ${cmd} → ${error.message}`);
150
+ }
151
+ }
152
+
153
+ if (isDocker) {
154
+ // Docker fallback specific
155
+ try {
156
+ await execPromise('sudo apt-get update -y && sudo apt-get install -y chromium');
157
+ await access('/usr/bin/chromium', fs.constants.X_OK);
158
+ if (this.logDebug) this.emit('debug', 'Chromium installed successfully inside Docker at /usr/bin/chromium');
159
+ return '/usr/bin/chromium';
160
+ } catch {
161
+ return null;
162
+ }
163
+ }
164
+ return null;
165
+ }
166
+
167
+ // Unknown OS
168
+ if (this.logDebug) this.emit('debug', `Unsupported OS: ${osName}.`);
169
+ return null;
170
+ } catch (error) {
171
+ if (this.logError) this.emit('error', `Chromium detection/install error: ${error.message}`);
172
+ return null;
23
173
  }
24
174
  }
25
175
  }