homebridge-melcloud-control 4.4.1-beta.4 → 4.4.1-beta.41
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 +10 -0
- package/README.md +55 -55
- package/config.schema.json +54 -54
- package/homebridge-ui/public/index.html +144 -119
- package/index.js +18 -6
- package/package.json +2 -2
- package/src/constants.js +62 -50
- package/src/deviceata.js +25 -18
- package/src/deviceatw.js +6 -10
- package/src/deviceerv.js +61 -60
- package/src/functions.js +105 -111
- package/src/melcloud.js +10 -8
- package/src/melcloudata.js +41 -10
- package/src/melcloudatw.js +25 -9
- package/src/melclouderv.js +24 -8
- package/src/melcloudhome.js +40 -36
package/src/functions.js
CHANGED
|
@@ -56,162 +56,157 @@ class Functions extends EventEmitter {
|
|
|
56
56
|
|
|
57
57
|
async ensureChromiumInstalled() {
|
|
58
58
|
try {
|
|
59
|
-
|
|
60
|
-
const { stdout: osOut } = await execPromise("uname -s");
|
|
59
|
+
const { stdout: osOut } = await execPromise('uname -s');
|
|
61
60
|
const osName = osOut.trim();
|
|
62
|
-
const { stdout: archOut } = await execPromise("uname -m");
|
|
63
|
-
const arch = archOut.trim();
|
|
64
61
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
const { stdout: archOut } = await execPromise('uname -m');
|
|
63
|
+
let arch = archOut.trim() || 'unknown';
|
|
64
|
+
|
|
65
|
+
// Normalizacja architektury
|
|
66
|
+
if (arch.startsWith('arm') || arch.startsWith('aarch')) arch = 'arm';
|
|
67
|
+
else if (arch.includes('64')) arch = 'x64';
|
|
68
|
+
else arch = 'x86';
|
|
69
|
+
|
|
70
|
+
const isARM = arch === 'arm';
|
|
71
|
+
const isMac = osName === 'Darwin';
|
|
72
|
+
const isLinux = osName === 'Linux';
|
|
73
|
+
const isQnap = fs.existsSync('/etc/config/uLinux.conf') || fs.existsSync('/etc/config/qpkg.conf');
|
|
68
74
|
|
|
69
75
|
// Detect Docker
|
|
70
76
|
let isDocker = false;
|
|
71
77
|
try {
|
|
72
|
-
await access(
|
|
78
|
+
await access('/.dockerenv');
|
|
73
79
|
isDocker = true;
|
|
74
80
|
} catch { }
|
|
81
|
+
|
|
75
82
|
try {
|
|
76
|
-
const { stdout } = await execPromise(
|
|
77
|
-
if (stdout.includes(
|
|
83
|
+
const { stdout } = await execPromise('cat /proc/1/cgroup');
|
|
84
|
+
if (stdout.includes('docker') || stdout.includes('containerd')) isDocker = true;
|
|
78
85
|
} catch { }
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
const result = { path: null, arch, system: 'unknown' };
|
|
88
|
+
|
|
89
|
+
/* ===================== macOS ===================== */
|
|
81
90
|
if (isMac) {
|
|
82
|
-
const macCandidates = [
|
|
83
|
-
|
|
84
|
-
"/Applications/Chromium.app/Contents/MacOS/Chromium"
|
|
85
|
-
];
|
|
86
|
-
for (const p of macCandidates) {
|
|
91
|
+
const macCandidates = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', '/Applications/Chromium.app/Contents/MacOS/Chromium'];
|
|
92
|
+
for (const path of macCandidates) {
|
|
87
93
|
try {
|
|
88
|
-
await access(
|
|
89
|
-
|
|
94
|
+
await access(path, fs.constants.X_OK);
|
|
95
|
+
result.path = path;
|
|
96
|
+
result.system = 'macOS';
|
|
97
|
+
return result;
|
|
90
98
|
} catch { }
|
|
91
99
|
}
|
|
92
|
-
return
|
|
100
|
+
return result;
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
"/usr/bin/chromium",
|
|
100
|
-
"/snap/bin/chromium"
|
|
101
|
-
];
|
|
102
|
-
|
|
103
|
-
// Try existing
|
|
104
|
-
for (const p of armCandidates) {
|
|
103
|
+
/* ===================== QNAP ===================== */
|
|
104
|
+
if (isQnap) {
|
|
105
|
+
const qnapCandidates = ['/opt/bin/chromium', '/opt/bin/chromium-browser'];
|
|
106
|
+
for (const path of qnapCandidates) {
|
|
105
107
|
try {
|
|
106
|
-
await access(
|
|
107
|
-
|
|
108
|
+
await access(path, fs.constants.X_OK);
|
|
109
|
+
result.path = path;
|
|
110
|
+
result.system = 'Qnap';
|
|
111
|
+
return result;
|
|
108
112
|
} catch { }
|
|
109
113
|
}
|
|
110
114
|
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
try {
|
|
116
|
+
await access('/opt/bin/opkg', fs.constants.X_OK);
|
|
117
|
+
await execPromise('/opt/bin/opkg update');
|
|
118
|
+
await execPromise('opkg install chromium nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib');
|
|
119
|
+
process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ''}`;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (this.logError) this.emit('error', `Install package for Qnap error: ${error}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (const path of qnapCandidates) {
|
|
113
125
|
try {
|
|
114
|
-
await
|
|
126
|
+
await access(path, fs.constants.X_OK);
|
|
127
|
+
result.path = path;
|
|
128
|
+
result.system = 'Qnap';
|
|
129
|
+
return result;
|
|
115
130
|
} catch { }
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* ===================== Linux ARM ===================== */
|
|
136
|
+
if (isLinux && isARM) {
|
|
137
|
+
const armCandidates = ['/usr/bin/chromium-browser', '/usr/bin/chromium', '/snap/bin/chromium'];
|
|
138
|
+
for (const path of armCandidates) {
|
|
116
139
|
try {
|
|
117
|
-
await
|
|
140
|
+
await access(path, fs.constants.X_OK);
|
|
141
|
+
result.path = path;
|
|
142
|
+
result.system = 'Linux';
|
|
143
|
+
return result;
|
|
118
144
|
} catch { }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!isDocker) {
|
|
119
148
|
try {
|
|
120
|
-
await execPromise(
|
|
121
|
-
|
|
149
|
+
await execPromise('sudo apt update -y');
|
|
150
|
+
await execPromise('sudo apt install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2');
|
|
151
|
+
await execPromise('sudo apt install -y chromium chromium-browser chromium-codecs-ffmpeg');
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (this.logError) this.emit('error', `Install package for Linux ARM error: ${error}`);
|
|
154
|
+
}
|
|
122
155
|
}
|
|
123
156
|
|
|
124
|
-
|
|
125
|
-
for (const p of armCandidates) {
|
|
157
|
+
for (const path of armCandidates) {
|
|
126
158
|
try {
|
|
127
|
-
await access(
|
|
128
|
-
|
|
159
|
+
await access(path, fs.constants.X_OK);
|
|
160
|
+
result.path = path;
|
|
161
|
+
result.system = 'Linux';
|
|
162
|
+
return result;
|
|
129
163
|
} catch { }
|
|
130
164
|
}
|
|
131
|
-
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// QNAP / Entware
|
|
136
|
-
let entwareExists = false;
|
|
137
|
-
try {
|
|
138
|
-
await access("/opt/bin/opkg", fs.constants.X_OK);
|
|
139
|
-
entwareExists = true;
|
|
140
|
-
} catch { }
|
|
141
|
-
|
|
142
|
-
if (entwareExists) {
|
|
143
|
-
try {
|
|
144
|
-
await execPromise("/opt/bin/opkg update");
|
|
145
|
-
await execPromise("/opt/bin/opkg install nspr nss libx11 libxcomposite libxdamage libxrandr atk libcups libdrm libgbm alsa-lib");
|
|
146
|
-
process.env.LD_LIBRARY_PATH = `/opt/lib:${process.env.LD_LIBRARY_PATH || ""}`;
|
|
147
|
-
} catch { }
|
|
165
|
+
return result;
|
|
148
166
|
}
|
|
149
167
|
|
|
150
|
-
|
|
151
|
-
const synoCandidates = [
|
|
152
|
-
"/var/packages/Chromium/target/usr/bin/chromium",
|
|
153
|
-
"/usr/local/chromium/bin/chromium"
|
|
154
|
-
];
|
|
155
|
-
for (const p of synoCandidates) {
|
|
156
|
-
try {
|
|
157
|
-
await access(p, fs.constants.X_OK);
|
|
158
|
-
return p;
|
|
159
|
-
} catch { }
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Linux x64
|
|
168
|
+
/* ===================== Linux x64 ===================== */
|
|
163
169
|
if (isLinux) {
|
|
164
|
-
const linuxCandidates = [
|
|
165
|
-
"/usr/bin/chromium",
|
|
166
|
-
"/usr/bin/chromium-browser",
|
|
167
|
-
"/usr/bin/google-chrome",
|
|
168
|
-
"/snap/bin/chromium",
|
|
169
|
-
"/usr/local/bin/chromium"
|
|
170
|
-
];
|
|
171
|
-
|
|
170
|
+
const linuxCandidates = ['/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/google-chrome', '/snap/bin/chromium', '/usr/local/bin/chromium'];
|
|
172
171
|
try {
|
|
173
|
-
const { stdout } = await execPromise(
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
const { stdout } = await execPromise('which chromium || which chromium-browser || which google-chrome');
|
|
173
|
+
if (stdout.trim()) {
|
|
174
|
+
result.path = stdout.trim();
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
176
177
|
} catch { }
|
|
177
178
|
|
|
178
|
-
for (const
|
|
179
|
+
for (const path of linuxCandidates) {
|
|
179
180
|
try {
|
|
180
|
-
await access(
|
|
181
|
-
|
|
181
|
+
await access(path, fs.constants.X_OK);
|
|
182
|
+
result.path = path;
|
|
183
|
+
result.system = 'Linux';
|
|
184
|
+
return result;
|
|
182
185
|
} catch { }
|
|
183
186
|
}
|
|
184
187
|
|
|
185
|
-
// Docker: try installing chromium inside container (if allowed)
|
|
186
188
|
if (isDocker) {
|
|
187
189
|
try {
|
|
188
|
-
await execPromise(
|
|
189
|
-
} catch {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
];
|
|
202
|
-
for (const cmd of depCommands) {
|
|
203
|
-
try {
|
|
204
|
-
await execPromise(`sudo ${cmd}`);
|
|
205
|
-
} catch { }
|
|
190
|
+
await execPromise('apt update -y && apt install -y chromium');
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (this.logError) this.emit('error', `Install package for Linux Docker error: ${error}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const path of linuxCandidates) {
|
|
196
|
+
try {
|
|
197
|
+
await access(path, fs.constants.X_OK);
|
|
198
|
+
result.path = path;
|
|
199
|
+
result.system = 'Linux Docker';
|
|
200
|
+
return result;
|
|
201
|
+
} catch { }
|
|
202
|
+
}
|
|
206
203
|
}
|
|
207
|
-
|
|
208
|
-
return null;
|
|
209
204
|
}
|
|
210
205
|
|
|
211
|
-
return
|
|
212
|
-
} catch (
|
|
213
|
-
if (this.logError) this.emit(
|
|
214
|
-
return null;
|
|
206
|
+
return result;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (this.logError) this.emit('error', `Chromium detection error: ${error.message}`);
|
|
209
|
+
return { path: null, arch: 'unknown', system: 'unknown' };
|
|
215
210
|
}
|
|
216
211
|
}
|
|
217
212
|
|
|
@@ -280,7 +275,6 @@ class Functions extends EventEmitter {
|
|
|
280
275
|
|
|
281
276
|
return { min, max };
|
|
282
277
|
}
|
|
283
|
-
|
|
284
278
|
}
|
|
285
279
|
|
|
286
280
|
export default Functions
|
package/src/melcloud.js
CHANGED
|
@@ -54,9 +54,9 @@ class MelCloud extends EventEmitter {
|
|
|
54
54
|
|
|
55
55
|
async checkDevicesList() {
|
|
56
56
|
try {
|
|
57
|
-
const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
|
|
57
|
+
const devicesList = { State: false, Info: null, Buildings: [], Devices: [], Scenes: [] }
|
|
58
58
|
if (this.logDebug) this.emit('debug', `Scanning for devices...`);
|
|
59
|
-
const listDevicesData = await this.client(ApiUrls.ListDevices, { method: 'GET', });
|
|
59
|
+
const listDevicesData = await this.client(ApiUrls.Get.ListDevices, { method: 'GET', });
|
|
60
60
|
|
|
61
61
|
if (!listDevicesData || !listDevicesData.data) {
|
|
62
62
|
devicesList.Info = 'Invalid or empty response from MELCloud API'
|
|
@@ -71,9 +71,6 @@ class MelCloud extends EventEmitter {
|
|
|
71
71
|
return devicesList;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
await this.functions.saveData(this.buildingsFile, buildingsList);
|
|
75
|
-
if (this.logDebug) this.emit('debug', `Buildings list saved`);
|
|
76
|
-
|
|
77
74
|
const devices = [];
|
|
78
75
|
for (const building of buildingsList) {
|
|
79
76
|
if (!building.Structure) {
|
|
@@ -108,7 +105,12 @@ class MelCloud extends EventEmitter {
|
|
|
108
105
|
|
|
109
106
|
devicesList.State = true;
|
|
110
107
|
devicesList.Info = `Found ${devicesCount} devices`;
|
|
108
|
+
devicesList.Buildings = buildingsList;
|
|
111
109
|
devicesList.Devices = devices;
|
|
110
|
+
|
|
111
|
+
await this.functions.saveData(this.buildingsFile, devicesList);
|
|
112
|
+
if (this.logDebug) this.emit('debug', `Buildings list saved`);
|
|
113
|
+
|
|
112
114
|
this.emit('devicesList', devicesList);
|
|
113
115
|
|
|
114
116
|
return devicesList;
|
|
@@ -132,9 +134,9 @@ class MelCloud extends EventEmitter {
|
|
|
132
134
|
CaptchaResponse: '',
|
|
133
135
|
Persist: true
|
|
134
136
|
};
|
|
135
|
-
const accountData = await axios(ApiUrls.ClientLogin, {
|
|
137
|
+
const accountData = await axios(ApiUrls.Post.ClientLogin, {
|
|
136
138
|
method: 'POST',
|
|
137
|
-
baseURL: ApiUrls.
|
|
139
|
+
baseURL: ApiUrls.Base,
|
|
138
140
|
timeout: 15000,
|
|
139
141
|
data: payload
|
|
140
142
|
});
|
|
@@ -164,7 +166,7 @@ class MelCloud extends EventEmitter {
|
|
|
164
166
|
};
|
|
165
167
|
|
|
166
168
|
this.client = axios.create({
|
|
167
|
-
baseURL: ApiUrls.
|
|
169
|
+
baseURL: ApiUrls.Base,
|
|
168
170
|
timeout: 30000,
|
|
169
171
|
headers: headers
|
|
170
172
|
});
|
package/src/melcloudata.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import Functions from './functions.js';
|
|
3
|
-
import { ApiUrls,
|
|
3
|
+
import { ApiUrls, AirConditioner } from './constants.js';
|
|
4
4
|
|
|
5
5
|
class MelCloudAta extends EventEmitter {
|
|
6
6
|
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
@@ -72,7 +72,34 @@ class MelCloudAta extends EventEmitter {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
updateState = true;
|
|
76
|
+
break;
|
|
77
|
+
case 'ataUnitFrostProtectionTriggered':
|
|
78
|
+
deviceData.FrostProtection.Active = messageData.active;
|
|
79
|
+
|
|
80
|
+
//update device settings
|
|
81
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
82
|
+
if (!this.functions.isValidValue(value)) continue;
|
|
83
|
+
|
|
84
|
+
if (key in deviceData.Device) {
|
|
85
|
+
deviceData.Device[key] = value;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
updateState = true;
|
|
90
|
+
break;
|
|
91
|
+
case 'ataUnitOverheatProtectionTriggered':
|
|
92
|
+
deviceData.OverheatProtection.Active = messageData.active;
|
|
93
|
+
|
|
94
|
+
//update device settings
|
|
95
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
96
|
+
if (!this.functions.isValidValue(value)) continue;
|
|
97
|
+
|
|
98
|
+
if (key in deviceData.Device) {
|
|
99
|
+
deviceData.Device[key] = value;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
76
103
|
updateState = true;
|
|
77
104
|
break;
|
|
78
105
|
case 'unitHolidayModeTriggered':
|
|
@@ -85,6 +112,10 @@ class MelCloudAta extends EventEmitter {
|
|
|
85
112
|
deviceData.Rssi = messageData.rssi;
|
|
86
113
|
updateState = true;
|
|
87
114
|
break;
|
|
115
|
+
case 'unitCommunicationRestored':
|
|
116
|
+
timestamp = messageData.timestamp;
|
|
117
|
+
deviceData.Device.IsConnected = true;
|
|
118
|
+
break;
|
|
88
119
|
default:
|
|
89
120
|
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${parsedMessage}`);
|
|
90
121
|
return;
|
|
@@ -204,7 +235,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
204
235
|
case 'account':
|
|
205
236
|
flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
|
|
206
237
|
payload = { data: flagData.LoginData };
|
|
207
|
-
path = ApiUrls.UpdateApplicationOptions;
|
|
238
|
+
path = ApiUrls.Post.UpdateApplicationOptions;
|
|
208
239
|
await this.functions.saveData(this.accountFile, flagData);
|
|
209
240
|
break;
|
|
210
241
|
default:
|
|
@@ -230,7 +261,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
230
261
|
HideDryModeControl: deviceData.HideDryModeControl,
|
|
231
262
|
HasPendingCommand: true
|
|
232
263
|
};
|
|
233
|
-
path = ApiUrls.
|
|
264
|
+
path = ApiUrls.Post.Ata;
|
|
234
265
|
update = true;
|
|
235
266
|
break;
|
|
236
267
|
}
|
|
@@ -254,7 +285,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
254
285
|
units: { ATA: [deviceData.DeviceID] }
|
|
255
286
|
};
|
|
256
287
|
method = 'POST';
|
|
257
|
-
path =
|
|
288
|
+
path = ApiUrls.Home.Post.ProtectionFrost;
|
|
258
289
|
update = true;
|
|
259
290
|
break;
|
|
260
291
|
case 'overheatprotection':
|
|
@@ -265,7 +296,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
265
296
|
units: { ATA: [deviceData.DeviceID] }
|
|
266
297
|
};
|
|
267
298
|
method = 'POST';
|
|
268
|
-
path =
|
|
299
|
+
path = ApiUrls.Home.Post.ProtectionOverheat;
|
|
269
300
|
update = true;
|
|
270
301
|
break;
|
|
271
302
|
case 'holidaymode':
|
|
@@ -276,17 +307,17 @@ class MelCloudAta extends EventEmitter {
|
|
|
276
307
|
units: { ATA: [deviceData.DeviceID] }
|
|
277
308
|
};
|
|
278
309
|
method = 'POST';
|
|
279
|
-
path =
|
|
310
|
+
path = ApiUrls.Home.Post.HolidayMode;
|
|
280
311
|
break;
|
|
281
312
|
case 'schedule':
|
|
282
313
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
283
314
|
method = 'PUT';
|
|
284
|
-
path =
|
|
315
|
+
path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
|
|
285
316
|
update = true;
|
|
286
317
|
break;
|
|
287
318
|
case 'scene':
|
|
288
319
|
method = 'PUT';
|
|
289
|
-
path =
|
|
320
|
+
path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
|
|
290
321
|
break;
|
|
291
322
|
default:
|
|
292
323
|
if (displayType === 1 && deviceData.Device.OperationMode === 8) {
|
|
@@ -312,7 +343,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
312
343
|
inStandbyMode: null
|
|
313
344
|
};
|
|
314
345
|
method = 'PUT';
|
|
315
|
-
path =
|
|
346
|
+
path = ApiUrls.Home.Put.Ata.replace('deviceid', deviceData.DeviceID);
|
|
316
347
|
break;
|
|
317
348
|
}
|
|
318
349
|
|
package/src/melcloudatw.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import Functions from './functions.js';
|
|
3
|
-
import { ApiUrls,
|
|
3
|
+
import { ApiUrls, HeatPump } from './constants.js';
|
|
4
4
|
|
|
5
5
|
class MelCloudAtw extends EventEmitter {
|
|
6
6
|
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
@@ -71,7 +71,19 @@ class MelCloudAtw extends EventEmitter {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
updateState = true;
|
|
75
|
+
break;
|
|
76
|
+
case 'atwUnitFrostProtectionTriggered':
|
|
77
|
+
deviceData.FrostProtection.Active = messageData.active;
|
|
78
|
+
|
|
79
|
+
//update device settings
|
|
80
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
81
|
+
if (!this.functions.isValidValue(value)) continue;
|
|
82
|
+
|
|
83
|
+
if (key in deviceData.Device) {
|
|
84
|
+
deviceData.Device[key] = value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
75
87
|
updateState = true;
|
|
76
88
|
break;
|
|
77
89
|
case 'unitHolidayModeTriggered':
|
|
@@ -84,6 +96,10 @@ class MelCloudAtw extends EventEmitter {
|
|
|
84
96
|
deviceData.Rssi = messageData.rssi;
|
|
85
97
|
updateState = true;
|
|
86
98
|
break;
|
|
99
|
+
case 'unitCommunicationRestored':
|
|
100
|
+
timestamp = messageData.timestamp;
|
|
101
|
+
deviceData.Device.IsConnected = true;
|
|
102
|
+
break;
|
|
87
103
|
default:
|
|
88
104
|
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${parsedMessage}`);
|
|
89
105
|
return;
|
|
@@ -204,7 +220,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
204
220
|
case 'account':
|
|
205
221
|
flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
|
|
206
222
|
payload = { data: flagData.LoginData };
|
|
207
|
-
path = ApiUrls.UpdateApplicationOptions;
|
|
223
|
+
path = ApiUrls.Post.UpdateApplicationOptions;
|
|
208
224
|
await this.functions.saveData(this.accountFile, flagData);
|
|
209
225
|
break;
|
|
210
226
|
default:
|
|
@@ -231,7 +247,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
231
247
|
ProhibitHotWater: deviceData.Device.ProhibitHotWater,
|
|
232
248
|
HasPendingCommand: true
|
|
233
249
|
}
|
|
234
|
-
path = ApiUrls.
|
|
250
|
+
path = ApiUrls.Post.Atw;
|
|
235
251
|
update = true;
|
|
236
252
|
break;
|
|
237
253
|
}
|
|
@@ -255,7 +271,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
255
271
|
units: { ATA: [deviceData.DeviceID] }
|
|
256
272
|
};
|
|
257
273
|
method = 'POST';
|
|
258
|
-
path =
|
|
274
|
+
path = ApiUrls.Home.Post.ProtectionFrost;
|
|
259
275
|
update = true;
|
|
260
276
|
break;
|
|
261
277
|
case 'holidaymode':
|
|
@@ -266,17 +282,17 @@ class MelCloudAtw extends EventEmitter {
|
|
|
266
282
|
units: { ATW: [deviceData.DeviceID] }
|
|
267
283
|
};
|
|
268
284
|
method = 'POST';
|
|
269
|
-
path =
|
|
285
|
+
path = ApiUrls.Home.Post.HolidayMode;
|
|
270
286
|
break;
|
|
271
287
|
case 'schedule':
|
|
272
288
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
273
289
|
method = 'PUT';
|
|
274
|
-
path =
|
|
290
|
+
path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
|
|
275
291
|
update = true;
|
|
276
292
|
break;
|
|
277
293
|
case 'scene':
|
|
278
294
|
method = 'PUT';
|
|
279
|
-
path =
|
|
295
|
+
path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
|
|
280
296
|
break;
|
|
281
297
|
default:
|
|
282
298
|
payload = {
|
|
@@ -295,7 +311,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
295
311
|
ecoHotWater: deviceData.Device.EcoHotWater,
|
|
296
312
|
};
|
|
297
313
|
method = 'PUT';
|
|
298
|
-
path =
|
|
314
|
+
path = ApiUrls.Home.Put.Atw.replace('deviceid', deviceData.DeviceID);
|
|
299
315
|
break
|
|
300
316
|
}
|
|
301
317
|
|
package/src/melclouderv.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import Functions from './functions.js';
|
|
3
|
-
import { ApiUrls,
|
|
3
|
+
import { ApiUrls, Ventilation } from './constants.js';
|
|
4
4
|
|
|
5
5
|
class MelCloudErv extends EventEmitter {
|
|
6
6
|
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
@@ -71,7 +71,19 @@ class MelCloudErv extends EventEmitter {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
updateState = true;
|
|
75
|
+
break;
|
|
76
|
+
case 'ervUnitFrostProtectionTriggered':
|
|
77
|
+
deviceData.FrostProtection.Active = messageData.active;
|
|
78
|
+
|
|
79
|
+
//update device settings
|
|
80
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
81
|
+
if (!this.functions.isValidValue(value)) continue;
|
|
82
|
+
|
|
83
|
+
if (key in deviceData.Device) {
|
|
84
|
+
deviceData.Device[key] = value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
75
87
|
updateState = true;
|
|
76
88
|
break;
|
|
77
89
|
case 'unitHolidayModeTriggered':
|
|
@@ -84,6 +96,10 @@ class MelCloudErv extends EventEmitter {
|
|
|
84
96
|
deviceData.Rssi = messageData.rssi;
|
|
85
97
|
updateState = true;
|
|
86
98
|
break;
|
|
99
|
+
case 'unitCommunicationRestored':
|
|
100
|
+
timestamp = messageData.timestamp;
|
|
101
|
+
deviceData.Device.IsConnected = true;
|
|
102
|
+
break;
|
|
87
103
|
default:
|
|
88
104
|
if (this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${parsedMessage}`);
|
|
89
105
|
return;
|
|
@@ -189,7 +205,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
189
205
|
case 'account':
|
|
190
206
|
flagData.Account.LoginData.UseFahrenheit = flagData.UseFahrenheit;
|
|
191
207
|
payload = { data: flagData.LoginData };
|
|
192
|
-
path = ApiUrls.UpdateApplicationOptions;
|
|
208
|
+
path = ApiUrls.Post.UpdateApplicationOptions;
|
|
193
209
|
await this.functions.saveData(this.accountFile, flagData);
|
|
194
210
|
break;
|
|
195
211
|
default:
|
|
@@ -231,7 +247,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
231
247
|
NightPurgeMode: deviceData.Device.NightPurgeMode,
|
|
232
248
|
HasPendingCommand: true
|
|
233
249
|
}
|
|
234
|
-
path = ApiUrls.
|
|
250
|
+
path = ApiUrls.Post.Erv;
|
|
235
251
|
update = true;
|
|
236
252
|
break;
|
|
237
253
|
}
|
|
@@ -255,17 +271,17 @@ class MelCloudErv extends EventEmitter {
|
|
|
255
271
|
units: { ERV: [deviceData.DeviceID] }
|
|
256
272
|
};
|
|
257
273
|
method = 'POST';
|
|
258
|
-
path =
|
|
274
|
+
path = ApiUrls.Home.Post.HolidayMode;
|
|
259
275
|
break;
|
|
260
276
|
case 'schedule':
|
|
261
277
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
262
278
|
method = 'PUT';
|
|
263
|
-
path =
|
|
279
|
+
path = ApiUrls.Home.Put.ScheduleEnableDisable.Home.replace('deviceid', deviceData.DeviceID);
|
|
264
280
|
update = true;
|
|
265
281
|
break;
|
|
266
282
|
case 'scene':
|
|
267
283
|
method = 'PUT';
|
|
268
|
-
path =
|
|
284
|
+
path = `${ApiUrls.Home.Put.SceneEnableDisable.replace('sceneid', flagData.Id)}${flagData.Enabled ? 'enable' : 'disable'}`;
|
|
269
285
|
break;
|
|
270
286
|
default:
|
|
271
287
|
if (displayType === 1 && deviceData.Device.VentilationMode === 2) {
|
|
@@ -288,7 +304,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
288
304
|
ventilationMode: Ventilation.VentilationModeMapEnumToString[deviceData.Device.VentilationMode],
|
|
289
305
|
};
|
|
290
306
|
method = 'PUT';
|
|
291
|
-
path =
|
|
307
|
+
path = ApiUrls.Home.Put.Erv.replace('deviceid', deviceData.DeviceID);
|
|
292
308
|
break
|
|
293
309
|
}
|
|
294
310
|
|