homebridge-melcloud-control 4.3.11-beta.3 → 4.3.11-beta.30
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/index.js +2 -2
- package/package.json +1 -1
- package/src/constants.js +3 -2
- package/src/deviceata.js +4 -4
- package/src/deviceatw.js +3 -3
- package/src/deviceerv.js +3 -3
- package/src/functions.js +16 -17
- package/src/melcloud.js +6 -7
- package/src/melcloudata.js +7 -32
- package/src/melcloudatw.js +6 -29
- package/src/melclouderv.js +6 -31
- package/src/melcloudhome.js +80 -84
package/index.js
CHANGED
|
@@ -105,8 +105,8 @@ class MelCloudPlatform {
|
|
|
105
105
|
|
|
106
106
|
//connect
|
|
107
107
|
const accountInfo = await melcloud.connect();
|
|
108
|
-
if (!accountInfo
|
|
109
|
-
if (logLevel.warn) log.warn(`${accountName}, ${accountInfo
|
|
108
|
+
if (!accountInfo?.State) {
|
|
109
|
+
if (logLevel.warn) log.warn(`${accountName}, ${accountInfo?.Info}`);
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
if (logLevel.success) log.success(`${accountName}, ${accountInfo.Info}`);
|
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.11-beta.
|
|
4
|
+
"version": "4.3.11-beta.30",
|
|
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/constants.js
CHANGED
|
@@ -23,7 +23,7 @@ export const ApiUrlsHome = {
|
|
|
23
23
|
GetConfiguration: "https://melcloudhome.com/api/configuration",
|
|
24
24
|
GetUserContext: "/api/user/context",
|
|
25
25
|
GetUserScenes: "/api/user/scenes",
|
|
26
|
-
PostSchedule: "
|
|
26
|
+
PostSchedule: "/api/cloudschedule/deviceid", // POST {"days":[2],"time":"17:59:00","enabled":true,"id":"53c5e804-0663-47d0-85c2-2d8ccd2573de","power":false,"operationMode":null,"setPoint":null,"vaneVerticalDirection":null,"vaneHorizontalDirection":null,"setFanSpeed":null}
|
|
27
27
|
PostProtectionFrost: "/api/protection/frost", // POST {"enabled":true,"min":13,"max":16,"units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
|
|
28
28
|
PostProtectionOverheat: "/api/protection/overheat", // POST {"enabled":true,"min":32,"max":35,"units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
|
|
29
29
|
PostHolidayMode: " /api/holidaymode", // POST {"enabled":true,"startDate":"2025-11-11T17:42:24.913","endDate":"2026-06-01T09:18:00","units":{"ATA":["ef333525-2699-4290-af5a-2922566676da"]}}
|
|
@@ -35,6 +35,7 @@ export const ApiUrlsHome = {
|
|
|
35
35
|
Enable: "/api/scene/sceneid/enable",
|
|
36
36
|
Disable: "/api/scene/sceneid/disable",
|
|
37
37
|
},
|
|
38
|
+
DeleteSchedule: "/api/cloudschedule/deviceid/scheduleid",
|
|
38
39
|
Referers: {
|
|
39
40
|
GetPutScenes: "https://melcloudhome.com/scenes",
|
|
40
41
|
PostHolidayMode: "https://melcloudhome.com/ata/deviceid/holidaymode",
|
|
@@ -60,7 +61,7 @@ export const AirConditioner = {
|
|
|
60
61
|
SystemMapEnumToString: { 0: "Air Conditioner Off", 1: "Air Conditioner On", 2: "Air Conditioner Offline" },
|
|
61
62
|
OperationModeMapStringToEnum: { "0": 0, "Heat": 1, "Dry": 2, "Cool": 3, "4": 4, "5": 5, "6": 6, "Fan": 7, "Automatic": 8, "Heat Isee": 9, "Dry Isee": 10, "Cool Isee": 11 },
|
|
62
63
|
OperationModeMapEnumToString: { 0: "0", 1: "Heat", 2: "Dry", 3: "Cool", 4: "4", 5: "5", 6: "6", 7: "Fan", 8: "Automatic", 9: "Heat Isee", 10: "Dry Isee", 11: "Cool Isee" },
|
|
63
|
-
OperationModeMapEnumToEnumWs: { 0: 0, 1: 1, 2: 2, 3: 3, 4: 7, 5: 8
|
|
64
|
+
OperationModeMapEnumToEnumWs: { 0: 0, 1: 1, 2: 2, 3: 3, 4: 7, 5: 8 },
|
|
64
65
|
FanSpeedMapStringToEnum: { "Auto": 0, "One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5, "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5 },
|
|
65
66
|
FanSpeedMapEnumToString: { 0: "Auto", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five" },
|
|
66
67
|
SetFanSpeedMapStringToEnum: { "Auto": 0, "One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5, "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5 },
|
package/src/deviceata.js
CHANGED
|
@@ -142,7 +142,7 @@ class DeviceAta extends EventEmitter {
|
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
} catch (error) {
|
|
145
|
-
this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
145
|
+
if (this.logWarn) this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -186,7 +186,7 @@ class DeviceAta extends EventEmitter {
|
|
|
186
186
|
});
|
|
187
187
|
}
|
|
188
188
|
} catch (error) {
|
|
189
|
-
this.emit('warn', `MQTT integration start error: ${error}`);
|
|
189
|
+
if (this.logWarn) this.emit('warn', `MQTT integration start error: ${error}`);
|
|
190
190
|
};
|
|
191
191
|
}
|
|
192
192
|
|
|
@@ -1325,7 +1325,7 @@ class DeviceAta extends EventEmitter {
|
|
|
1325
1325
|
async start() {
|
|
1326
1326
|
try {
|
|
1327
1327
|
//melcloud device
|
|
1328
|
-
this.melCloudAta = new MelCloudAta(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud
|
|
1328
|
+
this.melCloudAta = new MelCloudAta(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1329
1329
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
|
|
1330
1330
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1331
1331
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
|
@@ -1861,7 +1861,7 @@ class DeviceAta extends EventEmitter {
|
|
|
1861
1861
|
if (supportsFanSpeed) this.emit('info', `Current fan speed: ${AirConditioner.AktualFanSpeedMapEnumToString[actualFanSpeed]}`);
|
|
1862
1862
|
if (vaneHorizontalDirection !== null) this.emit('info', `Vane horizontal: ${AirConditioner.VaneHorizontalDirectionMapEnumToString[vaneHorizontalDirection]}`);
|
|
1863
1863
|
if (vaneVerticalDirection !== null) this.emit('info', `Vane vertical: ${AirConditioner.VaneVerticalDirectionMapEnumToString[vaneVerticalDirection]}`);
|
|
1864
|
-
if (supportsSwingFunction) this.emit('info', `Air direction: ${AirConditioner.AirDirectionMapEnumToString[
|
|
1864
|
+
if (supportsSwingFunction) this.emit('info', `Air direction: ${AirConditioner.AirDirectionMapEnumToString[currentSwingMode]}`);
|
|
1865
1865
|
this.emit('info', `Temperature display unit: ${obj.temperatureUnit}`);
|
|
1866
1866
|
this.emit('info', `Lock physical controls: ${obj.lockPhysicalControl ? 'Locked' : 'Unlocked'}`);
|
|
1867
1867
|
if (this.accountType === 'melcloudhome') this.emit('info', `Signal strength: ${deviceData.Rssi}dBm`);
|
package/src/deviceatw.js
CHANGED
|
@@ -146,7 +146,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
} catch (error) {
|
|
149
|
-
this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
149
|
+
if (this.logWarn) this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
152
|
|
|
@@ -190,7 +190,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
190
190
|
});
|
|
191
191
|
}
|
|
192
192
|
} catch (error) {
|
|
193
|
-
this.emit('warn', `MQTT integration start error: ${error}`);
|
|
193
|
+
if (this.logWarn) this.emit('warn', `MQTT integration start error: ${error}`);
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
}
|
|
@@ -1567,7 +1567,7 @@ class DeviceAtw extends EventEmitter {
|
|
|
1567
1567
|
async start() {
|
|
1568
1568
|
try {
|
|
1569
1569
|
//melcloud device
|
|
1570
|
-
this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud
|
|
1570
|
+
this.melCloudAtw = new MelCloudAtw(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1571
1571
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion, supportsHotWaterTank, supportsZone2) => {
|
|
1572
1572
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1573
1573
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
package/src/deviceerv.js
CHANGED
|
@@ -138,7 +138,7 @@ class DeviceErv extends EventEmitter {
|
|
|
138
138
|
});
|
|
139
139
|
}
|
|
140
140
|
} catch (error) {
|
|
141
|
-
this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
141
|
+
if (this.logWarn) this.emit('warn', `RESTFul integration start error: ${error}`);
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -182,7 +182,7 @@ class DeviceErv extends EventEmitter {
|
|
|
182
182
|
});
|
|
183
183
|
}
|
|
184
184
|
} catch (error) {
|
|
185
|
-
this.emit('warn', `MQTT integration start error: ${error}`);
|
|
185
|
+
if (this.logWarn) this.emit('warn', `MQTT integration start error: ${error}`);
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
188
|
}
|
|
@@ -1121,7 +1121,7 @@ class DeviceErv extends EventEmitter {
|
|
|
1121
1121
|
async start() {
|
|
1122
1122
|
try {
|
|
1123
1123
|
//melcloud device
|
|
1124
|
-
this.melCloudErv = new MelCloudErv(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud
|
|
1124
|
+
this.melCloudErv = new MelCloudErv(this.account, this.device, this.defaultTempsFile, this.accountFile, this.melcloud)
|
|
1125
1125
|
.on('deviceInfo', (modelIndoor, modelOutdoor, serialNumber, firmwareAppVersion) => {
|
|
1126
1126
|
if (this.logDeviceInfo && this.displayDeviceInfo) {
|
|
1127
1127
|
this.emit('devInfo', `---- ${this.deviceTypeString}: ${this.deviceName} ----`);
|
package/src/functions.js
CHANGED
|
@@ -58,30 +58,30 @@ class Functions extends EventEmitter {
|
|
|
58
58
|
let chromiumPath = '/usr/bin/chromium-browser';
|
|
59
59
|
|
|
60
60
|
try {
|
|
61
|
-
//
|
|
61
|
+
// Detect OS
|
|
62
62
|
const { stdout: osOut } = await execPromise('uname -s');
|
|
63
63
|
const osName = osOut.trim();
|
|
64
64
|
if (this.logDebug) this.emit('debug', `Detected OS: ${osName}`);
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// Detect Architecture
|
|
67
67
|
const { stdout: archOut } = await execPromise('uname -m');
|
|
68
68
|
const arch = archOut.trim();
|
|
69
69
|
if (this.logDebug) this.emit('debug', `Detected architecture: ${arch}`);
|
|
70
70
|
|
|
71
|
-
//
|
|
71
|
+
// Docker detection
|
|
72
72
|
let isDocker = false;
|
|
73
73
|
try {
|
|
74
|
-
await access('/.dockerenv', fs.constants.F_OK);
|
|
74
|
+
await access('/.dockerenv', fs.constants.F_OK);
|
|
75
|
+
isDocker = true;
|
|
75
76
|
} catch { }
|
|
76
77
|
|
|
77
78
|
try {
|
|
78
79
|
const { stdout } = await execPromise('cat /proc/1/cgroup || true');
|
|
79
80
|
if (stdout.includes('docker') || stdout.includes('containerd')) isDocker = true;
|
|
80
81
|
} catch { }
|
|
82
|
+
if (isDocker && this.logDebug) this.emit('debug', 'Running inside Docker container');
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// === macOS ===
|
|
84
|
+
// macOS
|
|
85
85
|
if (osName === 'Darwin') {
|
|
86
86
|
chromiumPath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
|
87
87
|
try {
|
|
@@ -92,10 +92,9 @@ class Functions extends EventEmitter {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
//
|
|
96
|
-
if (arch.startsWith('arm')) {
|
|
95
|
+
// ARM
|
|
96
|
+
if (arch.startsWith('arm') || arch.startsWith('aarch')) {
|
|
97
97
|
try {
|
|
98
|
-
chromiumPath = '/usr/bin/chromium-browser';
|
|
99
98
|
await access(chromiumPath, fs.constants.X_OK);
|
|
100
99
|
return chromiumPath;
|
|
101
100
|
} catch {
|
|
@@ -108,7 +107,7 @@ class Functions extends EventEmitter {
|
|
|
108
107
|
}
|
|
109
108
|
}
|
|
110
109
|
|
|
111
|
-
//
|
|
110
|
+
// Linux x64
|
|
112
111
|
if (osName === 'Linux') {
|
|
113
112
|
let systemChromium = null;
|
|
114
113
|
try {
|
|
@@ -116,7 +115,7 @@ class Functions extends EventEmitter {
|
|
|
116
115
|
systemChromium = checkOut.trim() || null;
|
|
117
116
|
} catch { }
|
|
118
117
|
|
|
119
|
-
//
|
|
118
|
+
// Entware (QNAP)
|
|
120
119
|
let entwareExists = false;
|
|
121
120
|
try {
|
|
122
121
|
await access('/opt/bin/opkg', fs.constants.X_OK);
|
|
@@ -131,24 +130,24 @@ class Functions extends EventEmitter {
|
|
|
131
130
|
} catch { }
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
//
|
|
135
|
-
const
|
|
133
|
+
// Install missing libs
|
|
134
|
+
const depCommands = [
|
|
136
135
|
'apt-get update -y && apt-get install -y libnspr4 libnss3 libx11-6 libxcomposite1 libxdamage1 libxrandr2 libatk1.0-0 libcups2 libdrm2 libgbm1 libasound2',
|
|
137
136
|
'apk add --no-cache nspr nss libx11 libxcomposite libxdamage libxrandr atk cups libdrm libgbm alsa-lib',
|
|
138
137
|
'yum install -y nspr nss libX11 libXcomposite libXdamage libXrandr atk cups libdrm libgbm alsa-lib'
|
|
139
138
|
];
|
|
140
|
-
|
|
139
|
+
|
|
140
|
+
for (const cmd of depCommands) {
|
|
141
141
|
try {
|
|
142
142
|
await execPromise(`sudo ${cmd}`);
|
|
143
143
|
} catch { }
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
// Set LD_LIBRARY_PATH so Puppeteer's Chromium can find libs
|
|
147
146
|
process.env.LD_LIBRARY_PATH = `/usr/lib:/usr/lib64:${process.env.LD_LIBRARY_PATH || ''}`;
|
|
148
147
|
return systemChromium;
|
|
149
148
|
}
|
|
150
149
|
|
|
151
|
-
if (this.logDebug) this.emit('debug', `Unsupported OS: ${osName}
|
|
150
|
+
if (this.logDebug) this.emit('debug', `Unsupported OS: ${osName}`);
|
|
152
151
|
return null;
|
|
153
152
|
} catch (error) {
|
|
154
153
|
if (this.logError) this.emit('error', `Chromium detection/install error: ${error.message}`);
|
package/src/melcloud.js
CHANGED
|
@@ -17,8 +17,8 @@ class MelCloud extends EventEmitter {
|
|
|
17
17
|
|
|
18
18
|
this.accountFile = accountFile;
|
|
19
19
|
this.buildingsFile = buildingsFile;
|
|
20
|
-
this.headers = {};
|
|
21
20
|
|
|
21
|
+
this.client = null;
|
|
22
22
|
this.functions = new Functions(this.logWarn, this.logError, this.logDebug)
|
|
23
23
|
.on('warn', warn => this.emit('warn', warn))
|
|
24
24
|
.on('error', error => this.emit('error', error))
|
|
@@ -56,7 +56,7 @@ class MelCloud extends EventEmitter {
|
|
|
56
56
|
try {
|
|
57
57
|
const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
|
|
58
58
|
if (this.logDebug) this.emit('debug', `Scanning for devices...`);
|
|
59
|
-
const listDevicesData = await this.
|
|
59
|
+
const listDevicesData = await this.client(ApiUrls.ListDevices, { method: 'GET', });
|
|
60
60
|
|
|
61
61
|
if (!listDevicesData || !listDevicesData.data) {
|
|
62
62
|
devicesList.Info = 'Invalid or empty response from MELCloud API'
|
|
@@ -121,7 +121,7 @@ class MelCloud extends EventEmitter {
|
|
|
121
121
|
if (this.logDebug) this.emit('debug', `Connecting to MELCloud`);
|
|
122
122
|
|
|
123
123
|
try {
|
|
124
|
-
const accountInfo = { State: false, Info: '', Account: null, UseFahrenheit: false
|
|
124
|
+
const accountInfo = { State: false, Info: '', Account: null, UseFahrenheit: false }
|
|
125
125
|
|
|
126
126
|
const payload = {
|
|
127
127
|
Email: this.user,
|
|
@@ -162,19 +162,18 @@ class MelCloud extends EventEmitter {
|
|
|
162
162
|
'X-MitsContextKey': contextKey,
|
|
163
163
|
'Content-Type': 'application/json'
|
|
164
164
|
};
|
|
165
|
-
|
|
166
|
-
this.
|
|
167
|
-
this.axiosInstance = axios.create({
|
|
165
|
+
|
|
166
|
+
this.client = axios.create({
|
|
168
167
|
baseURL: ApiUrls.BaseURL,
|
|
169
168
|
timeout: 30000,
|
|
170
169
|
headers: headers
|
|
171
170
|
});
|
|
171
|
+
this.emit('client', this.client);
|
|
172
172
|
|
|
173
173
|
accountInfo.State = true;
|
|
174
174
|
accountInfo.Info = 'Connect Success';
|
|
175
175
|
accountInfo.UseFahrenheit = loginData.UseFahrenheit;
|
|
176
176
|
accountInfo.Account = account;
|
|
177
|
-
accountInfo.Headers = headers;
|
|
178
177
|
await this.functions.saveData(this.accountFile, accountInfo);
|
|
179
178
|
|
|
180
179
|
return accountInfo
|
package/src/melcloudata.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import EventEmitter from 'events';
|
|
3
2
|
import Functions from './functions.js';
|
|
4
3
|
import { ApiUrls, ApiUrlsHome, AirConditioner } from './constants.js';
|
|
5
4
|
|
|
6
5
|
class MelCloudAta extends EventEmitter {
|
|
7
|
-
constructor(account, device, defaultTempsFile, accountFile, melcloud
|
|
6
|
+
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
8
7
|
super();
|
|
9
8
|
this.accountType = account.type;
|
|
10
9
|
this.logSuccess = account.log?.success;
|
|
@@ -24,12 +23,12 @@ class MelCloudAta extends EventEmitter {
|
|
|
24
23
|
|
|
25
24
|
//set default values
|
|
26
25
|
this.deviceData = {};
|
|
27
|
-
this.
|
|
26
|
+
this.client = melcloud.client;
|
|
28
27
|
|
|
29
28
|
//handle melcloud events
|
|
30
29
|
let deviceData = null;
|
|
31
|
-
melcloud.on('
|
|
32
|
-
this.
|
|
30
|
+
melcloud.on('client', (client) => {
|
|
31
|
+
this.client = client;
|
|
33
32
|
}).on('devicesList', async (devicesData) => {
|
|
34
33
|
try {
|
|
35
34
|
deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
@@ -197,7 +196,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
197
196
|
let method = null
|
|
198
197
|
let payload = {};
|
|
199
198
|
let path = '';
|
|
200
|
-
let headers = this.headers;
|
|
201
199
|
switch (accountType) {
|
|
202
200
|
case "melcloud":
|
|
203
201
|
switch (flag) {
|
|
@@ -235,14 +233,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
235
233
|
}
|
|
236
234
|
|
|
237
235
|
if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
|
|
238
|
-
|
|
239
|
-
await axios(path, {
|
|
240
|
-
method: 'POST',
|
|
241
|
-
baseURL: ApiUrls.BaseURL,
|
|
242
|
-
timeout: 30000,
|
|
243
|
-
headers: headers,
|
|
244
|
-
data: payload
|
|
245
|
-
});
|
|
236
|
+
await this.client(path, { method: 'POST', data: payload });
|
|
246
237
|
|
|
247
238
|
this.emit('deviceState', deviceData);
|
|
248
239
|
return true;
|
|
@@ -257,7 +248,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
257
248
|
};
|
|
258
249
|
method = 'POST';
|
|
259
250
|
path = ApiUrlsHome.PostProtectionFrost;
|
|
260
|
-
headers.Referer = ApiUrlsHome.Referers.PostProtectionFrost.replace('deviceid', deviceData.DeviceID);
|
|
261
251
|
break;
|
|
262
252
|
case 'overheatprotection':
|
|
263
253
|
payload = {
|
|
@@ -268,7 +258,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
268
258
|
};
|
|
269
259
|
method = 'POST';
|
|
270
260
|
path = ApiUrlsHome.PostProtectionOverheat;
|
|
271
|
-
headers.Referer = ApiUrlsHome.Referers.PostProtectionOverheat.replace('deviceid', deviceData.DeviceID);
|
|
272
261
|
break;
|
|
273
262
|
case 'holidaymode':
|
|
274
263
|
payload = {
|
|
@@ -279,18 +268,15 @@ class MelCloudAta extends EventEmitter {
|
|
|
279
268
|
};
|
|
280
269
|
method = 'POST';
|
|
281
270
|
path = ApiUrlsHome.PostHolidayMode;
|
|
282
|
-
headers.Referer = ApiUrlsHome.Referers.PostHolidayMode.replace('deviceid', deviceData.DeviceID);
|
|
283
271
|
break;
|
|
284
272
|
case 'schedule':
|
|
285
273
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
286
274
|
method = 'PUT';
|
|
287
275
|
path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
|
|
288
|
-
this.headers.Referer = ApiUrlsHome.Referers.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
|
|
289
276
|
break;
|
|
290
277
|
case 'scene':
|
|
291
278
|
method = 'PUT';
|
|
292
279
|
path = ApiUrlsHome.PutScene[flagData.Enabled ? 'Enable' : 'Disable'].replace('sceneid', flagData.Id);
|
|
293
|
-
headers.Referer = ApiUrlsHome.Referers.GetPutScenes;
|
|
294
280
|
break;
|
|
295
281
|
default:
|
|
296
282
|
if (displayType === 1 && deviceData.Device.OperationMode === 8) {
|
|
@@ -317,29 +303,18 @@ class MelCloudAta extends EventEmitter {
|
|
|
317
303
|
};
|
|
318
304
|
method = 'PUT';
|
|
319
305
|
path = ApiUrlsHome.PutAta.replace('deviceid', deviceData.DeviceID);
|
|
320
|
-
headers.Referer = ApiUrlsHome.Referers.PutDeviceSettings;
|
|
321
306
|
break;
|
|
322
307
|
}
|
|
323
308
|
|
|
324
|
-
//
|
|
325
|
-
headers['Content-Type'] = 'application/json; charset=utf-8';
|
|
326
|
-
headers.Origin = ApiUrlsHome.Origin;
|
|
309
|
+
//send payload
|
|
327
310
|
if (!this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
|
|
328
|
-
|
|
329
|
-
await axios(path, {
|
|
330
|
-
method: method,
|
|
331
|
-
baseURL: ApiUrlsHome.BaseURL,
|
|
332
|
-
timeout: 30000,
|
|
333
|
-
headers: headers,
|
|
334
|
-
data: payload
|
|
335
|
-
});
|
|
311
|
+
await this.client(path, { method: method, data: payload });
|
|
336
312
|
|
|
337
313
|
return true;
|
|
338
314
|
default:
|
|
339
315
|
return;
|
|
340
316
|
}
|
|
341
317
|
} catch (error) {
|
|
342
|
-
if (error.response?.status === 500) return true; // Return 500 for schedule hovewer working correct
|
|
343
318
|
throw new Error(`Send data error: ${error.message}`);
|
|
344
319
|
}
|
|
345
320
|
}
|
package/src/melcloudatw.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import EventEmitter from 'events';
|
|
3
2
|
import Functions from './functions.js';
|
|
4
3
|
import { ApiUrls, ApiUrlsHome, HeatPump } from './constants.js';
|
|
5
4
|
|
|
6
5
|
class MelCloudAtw extends EventEmitter {
|
|
7
|
-
constructor(account, device, defaultTempsFile, accountFile, melcloud
|
|
6
|
+
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
8
7
|
super();
|
|
9
8
|
this.accountType = account.type;
|
|
10
9
|
this.logSuccess = account.log?.success;
|
|
@@ -24,12 +23,12 @@ class MelCloudAtw extends EventEmitter {
|
|
|
24
23
|
|
|
25
24
|
//set default values
|
|
26
25
|
this.deviceData = {};
|
|
27
|
-
this.
|
|
26
|
+
this.client = melcloud.client;
|
|
28
27
|
|
|
29
28
|
//handle melcloud events
|
|
30
29
|
let deviceData = null;
|
|
31
|
-
melcloud.on('
|
|
32
|
-
this.
|
|
30
|
+
melcloud.on('client', (client) => {
|
|
31
|
+
this.client = client;
|
|
33
32
|
}).on('devicesList', async (devicesData) => {
|
|
34
33
|
try {
|
|
35
34
|
deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
@@ -198,7 +197,6 @@ class MelCloudAtw extends EventEmitter {
|
|
|
198
197
|
let method = null
|
|
199
198
|
let payload = {};
|
|
200
199
|
let path = '';
|
|
201
|
-
let headers = this.headers;
|
|
202
200
|
switch (accountType) {
|
|
203
201
|
case "melcloud":
|
|
204
202
|
switch (flag) {
|
|
@@ -237,14 +235,7 @@ class MelCloudAtw extends EventEmitter {
|
|
|
237
235
|
}
|
|
238
236
|
|
|
239
237
|
if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
|
|
240
|
-
|
|
241
|
-
await axios(path, {
|
|
242
|
-
method: 'POST',
|
|
243
|
-
baseURL: ApiUrls.BaseURL,
|
|
244
|
-
timeout: 30000,
|
|
245
|
-
headers: headers,
|
|
246
|
-
data: payload
|
|
247
|
-
});
|
|
238
|
+
await this.client(path, { method: 'POST', data: payload });
|
|
248
239
|
|
|
249
240
|
this.emit('deviceState', deviceData);
|
|
250
241
|
return true;
|
|
@@ -259,18 +250,15 @@ class MelCloudAtw extends EventEmitter {
|
|
|
259
250
|
};
|
|
260
251
|
method = 'POST';
|
|
261
252
|
path = ApiUrlsHome.PostHolidayMode;
|
|
262
|
-
headers.Referer = ApiUrlsHome.Referers.PostHolidayMode.replace('deviceid', deviceData.DeviceID);
|
|
263
253
|
break;
|
|
264
254
|
case 'schedule':
|
|
265
255
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
266
256
|
method = 'PUT';
|
|
267
257
|
path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
|
|
268
|
-
headers.Referer = ApiUrlsHome.Referers.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
|
|
269
258
|
break;
|
|
270
259
|
case 'scene':
|
|
271
260
|
method = 'PUT';
|
|
272
261
|
path = ApiUrlsHome.PutScene[flagData.Enabled ? 'Enable' : 'Disable'].replace('sceneid', flagData.Id);
|
|
273
|
-
headers.Referer = ApiUrlsHome.Referers.GetPutScenes;
|
|
274
262
|
break;
|
|
275
263
|
default:
|
|
276
264
|
payload = {
|
|
@@ -290,28 +278,17 @@ class MelCloudAtw extends EventEmitter {
|
|
|
290
278
|
};
|
|
291
279
|
method = 'PUT';
|
|
292
280
|
path = ApiUrlsHome.PutAtw.replace('deviceid', deviceData.DeviceID);
|
|
293
|
-
headers.Referer = ApiUrlsHome.Referers.PutDeviceSettings;
|
|
294
281
|
break
|
|
295
282
|
}
|
|
296
283
|
|
|
297
|
-
headers['Content-Type'] = 'application/json; charset=utf-8';
|
|
298
|
-
headers.Origin = ApiUrlsHome.Origin;
|
|
299
284
|
if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
|
|
300
|
-
|
|
301
|
-
await axios(path, {
|
|
302
|
-
method: method,
|
|
303
|
-
baseURL: ApiUrlsHome.BaseURL,
|
|
304
|
-
timeout: 30000,
|
|
305
|
-
headers: headers,
|
|
306
|
-
data: payload
|
|
307
|
-
});
|
|
285
|
+
await this.client(path, { method: method, data: payload });
|
|
308
286
|
|
|
309
287
|
return true;
|
|
310
288
|
default:
|
|
311
289
|
return;
|
|
312
290
|
}
|
|
313
291
|
} catch (error) {
|
|
314
|
-
if (error.response?.status === 500) return true; // Return 500 for schedule hovewer working correct
|
|
315
292
|
throw new Error(`Send data error: ${error.message}`);
|
|
316
293
|
}
|
|
317
294
|
}
|
package/src/melclouderv.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import EventEmitter from 'events';
|
|
3
2
|
import Functions from './functions.js';
|
|
4
3
|
import { ApiUrls, ApiUrlsHome, Ventilation } from './constants.js';
|
|
5
4
|
|
|
6
5
|
class MelCloudErv extends EventEmitter {
|
|
7
|
-
constructor(account, device, defaultTempsFile, accountFile, melcloud
|
|
6
|
+
constructor(account, device, defaultTempsFile, accountFile, melcloud) {
|
|
8
7
|
super();
|
|
9
8
|
this.accountType = account.type;
|
|
10
9
|
this.logSuccess = account.log?.success;
|
|
@@ -24,14 +23,12 @@ class MelCloudErv extends EventEmitter {
|
|
|
24
23
|
|
|
25
24
|
//set default values
|
|
26
25
|
this.deviceData = {};
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
this.emit('debug', `Headers: ${JSON.stringify(melcloud.Headers, null, 2)}`);
|
|
26
|
+
this.client = melcloud.client;
|
|
30
27
|
|
|
31
28
|
//handle melcloud events
|
|
32
29
|
let deviceData = null;
|
|
33
|
-
melcloud.on('
|
|
34
|
-
this.
|
|
30
|
+
melcloud.on('client', (client) => {
|
|
31
|
+
this.client = client;
|
|
35
32
|
}).on('devicesList', async (devicesData) => {
|
|
36
33
|
try {
|
|
37
34
|
deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
@@ -185,7 +182,6 @@ class MelCloudErv extends EventEmitter {
|
|
|
185
182
|
let method = null
|
|
186
183
|
let payload = {};
|
|
187
184
|
let path = '';
|
|
188
|
-
let headers = this.headers;
|
|
189
185
|
switch (accountType) {
|
|
190
186
|
case "melcloud":
|
|
191
187
|
switch (flag) {
|
|
@@ -239,14 +235,7 @@ class MelCloudErv extends EventEmitter {
|
|
|
239
235
|
}
|
|
240
236
|
|
|
241
237
|
if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
|
|
242
|
-
|
|
243
|
-
await axios(path, {
|
|
244
|
-
method: 'POST',
|
|
245
|
-
baseURL: ApiUrls.BaseURL,
|
|
246
|
-
timeout: 30000,
|
|
247
|
-
headers: headers,
|
|
248
|
-
data: payload
|
|
249
|
-
});
|
|
238
|
+
await this.client(path, { method: 'POST', data: payload });
|
|
250
239
|
|
|
251
240
|
this.emit('deviceState', deviceData);
|
|
252
241
|
return true;
|
|
@@ -261,18 +250,15 @@ class MelCloudErv extends EventEmitter {
|
|
|
261
250
|
};
|
|
262
251
|
method = 'POST';
|
|
263
252
|
path = ApiUrlsHome.PostHolidayMode;
|
|
264
|
-
headers.Referer = ApiUrlsHome.Referers.PostHolidayMode.replace('deviceid', deviceData.DeviceID);
|
|
265
253
|
break;
|
|
266
254
|
case 'schedule':
|
|
267
255
|
payload = { enabled: deviceData.ScheduleEnabled };
|
|
268
256
|
method = 'PUT';
|
|
269
257
|
path = ApiUrlsHome.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
|
|
270
|
-
headers.Referer = ApiUrlsHome.Referers.PutScheduleEnabled.replace('deviceid', deviceData.DeviceID);
|
|
271
258
|
break;
|
|
272
259
|
case 'scene':
|
|
273
260
|
method = 'PUT';
|
|
274
261
|
path = ApiUrlsHome.PutScene[flagData.Enabled ? 'Enable' : 'Disable'].replace('sceneid', flagData.Id);
|
|
275
|
-
headers.Referer = ApiUrlsHome.Referers.GetPutScenes;
|
|
276
262
|
break;
|
|
277
263
|
default:
|
|
278
264
|
if (displayType === 1 && deviceData.Device.VentilationMode === 2) {
|
|
@@ -296,28 +282,17 @@ class MelCloudErv extends EventEmitter {
|
|
|
296
282
|
};
|
|
297
283
|
method = 'PUT';
|
|
298
284
|
path = ApiUrlsHome.PutErv.replace('deviceid', deviceData.DeviceID);
|
|
299
|
-
headers.Referer = ApiUrlsHome.Referers.PutDeviceSettings;
|
|
300
285
|
break
|
|
301
286
|
}
|
|
302
287
|
|
|
303
|
-
headers['Content-Type'] = 'application/json; charset=utf-8';
|
|
304
|
-
headers.Origin = ApiUrlsHome.Origin;
|
|
305
288
|
if (this.logDebug) this.emit('debug', `Send data: ${JSON.stringify(payload, null, 2)}`);
|
|
306
|
-
|
|
307
|
-
await axios(path, {
|
|
308
|
-
method: method,
|
|
309
|
-
baseURL: ApiUrlsHome.BaseURL,
|
|
310
|
-
timeout: 30000,
|
|
311
|
-
headers: headers,
|
|
312
|
-
data: payload
|
|
313
|
-
});
|
|
289
|
+
await this.client(path, { method: method, data: payload });
|
|
314
290
|
|
|
315
291
|
return true;
|
|
316
292
|
default:
|
|
317
293
|
return;
|
|
318
294
|
}
|
|
319
295
|
} catch (error) {
|
|
320
|
-
if (error.response?.status === 500) return true; // Return 500 for schedule hovewer working correct
|
|
321
296
|
throw new Error(`Send data error: ${error.message}`);
|
|
322
297
|
}
|
|
323
298
|
}
|
package/src/melcloudhome.js
CHANGED
|
@@ -21,12 +21,11 @@ class MelCloudHome extends EventEmitter {
|
|
|
21
21
|
this.logWarn = account.log?.warn;
|
|
22
22
|
this.logError = account.log?.error;
|
|
23
23
|
this.logDebug = account.log?.debug;
|
|
24
|
+
|
|
24
25
|
this.accountFile = accountFile;
|
|
25
26
|
this.buildingsFile = buildingsFile;
|
|
26
27
|
|
|
27
|
-
this.
|
|
28
|
-
this.webSocketOptions = {};
|
|
29
|
-
this.socket = null;
|
|
28
|
+
this.client = null;
|
|
30
29
|
this.connecting = false;
|
|
31
30
|
this.socketConnected = false;
|
|
32
31
|
this.heartbeat = null;
|
|
@@ -74,18 +73,13 @@ class MelCloudHome extends EventEmitter {
|
|
|
74
73
|
this.heartbeat = null;
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
if (this.socket) {
|
|
78
|
-
try { this.socket.close(); } catch { }
|
|
79
|
-
this.socket = null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
76
|
this.socketConnected = false;
|
|
83
77
|
}
|
|
84
78
|
|
|
85
79
|
async checkScenesList() {
|
|
86
80
|
try {
|
|
87
81
|
if (this.logDebug) this.emit('debug', `Scanning for scenes`);
|
|
88
|
-
const listScenesData = await this.
|
|
82
|
+
const listScenesData = await this.client(ApiUrlsHome.GetUserScenes, { method: 'GET', });
|
|
89
83
|
|
|
90
84
|
const scenesList = listScenesData.data;
|
|
91
85
|
if (this.logDebug) this.emit('debug', `Scenes: ${JSON.stringify(scenesList, null, 2)}`);
|
|
@@ -117,7 +111,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
117
111
|
try {
|
|
118
112
|
const devicesList = { State: false, Info: null, Devices: [], Scenes: [] }
|
|
119
113
|
if (this.logDebug) this.emit('debug', `Scanning for devices`);
|
|
120
|
-
const listDevicesData = await this.
|
|
114
|
+
const listDevicesData = await this.client(ApiUrlsHome.GetUserContext, { method: 'GET' });
|
|
121
115
|
|
|
122
116
|
const userContext = listDevicesData.data;
|
|
123
117
|
const buildings = userContext.buildings ?? [];
|
|
@@ -202,56 +196,15 @@ class MelCloudHome extends EventEmitter {
|
|
|
202
196
|
// Get scenes
|
|
203
197
|
let scenes = [];
|
|
204
198
|
try {
|
|
205
|
-
|
|
206
|
-
if (this.logDebug) this.emit('debug', `Found ${
|
|
199
|
+
const scenesList = await this.checkScenesList();
|
|
200
|
+
if (this.logDebug) this.emit('debug', `Found ${scenesList.length} scenes`);
|
|
201
|
+
if (scenesList.length > 0) {
|
|
202
|
+
scenes = scenesList;
|
|
203
|
+
}
|
|
207
204
|
} catch (error) {
|
|
208
205
|
if (this.logError) this.emit('error', `Get scenes error: ${error}`);
|
|
209
206
|
}
|
|
210
207
|
|
|
211
|
-
//web cocket connection
|
|
212
|
-
if (!this.connecting && !this.socketConnected) {
|
|
213
|
-
this.connecting = true;
|
|
214
|
-
|
|
215
|
-
try {
|
|
216
|
-
const url = `${ApiUrlsHome.WebSocketURL}${this.webSocketOptions.Hash}`;
|
|
217
|
-
this.socket = new WebSocket(url, { headers: this.webSocketOptions.Headers })
|
|
218
|
-
.on('error', (error) => {
|
|
219
|
-
if (this.logError) this.emit('error', `Socket error: ${error}`);
|
|
220
|
-
this.socket.close();
|
|
221
|
-
})
|
|
222
|
-
.on('close', () => {
|
|
223
|
-
if (this.logDebug) this.emit('debug', `Socket closed`);
|
|
224
|
-
this.cleanupSocket();
|
|
225
|
-
})
|
|
226
|
-
.on('open', () => {
|
|
227
|
-
this.socketConnected = true;
|
|
228
|
-
this.connecting = false;
|
|
229
|
-
if (this.logSuccess) this.emit('success', `Socket Connect Success`);
|
|
230
|
-
|
|
231
|
-
// heartbeat
|
|
232
|
-
this.heartbeat = setInterval(() => {
|
|
233
|
-
if (this.socket.readyState === this.socket.OPEN) {
|
|
234
|
-
if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
|
|
235
|
-
this.socket.ping();
|
|
236
|
-
}
|
|
237
|
-
}, 30000);
|
|
238
|
-
})
|
|
239
|
-
.on('pong', () => {
|
|
240
|
-
if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
|
|
241
|
-
})
|
|
242
|
-
.on('message', (message) => {
|
|
243
|
-
const parsedMessage = JSON.parse(message);
|
|
244
|
-
if (this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
|
|
245
|
-
if (parsedMessage.message === 'Forbidden') return;
|
|
246
|
-
|
|
247
|
-
this.emit('webSocket', parsedMessage);
|
|
248
|
-
});
|
|
249
|
-
} catch (error) {
|
|
250
|
-
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
251
|
-
this.cleanupSocket();
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
208
|
devicesList.State = true;
|
|
256
209
|
devicesList.Info = `Found ${devicesCount} devices ${scenes.length > 0 ? `and ${scenes.length} scenes` : ''}`;
|
|
257
210
|
devicesList.Devices = devices;
|
|
@@ -270,20 +223,24 @@ class MelCloudHome extends EventEmitter {
|
|
|
270
223
|
|
|
271
224
|
let browser;
|
|
272
225
|
try {
|
|
273
|
-
const accountInfo = { State: false, Info: '', Account: {}, UseFahrenheit: false
|
|
226
|
+
const accountInfo = { State: false, Info: '', Account: {}, UseFahrenheit: false };
|
|
227
|
+
|
|
228
|
+
// Get Chromium path
|
|
274
229
|
let chromiumPath = await this.functions.ensureChromiumInstalled();
|
|
275
230
|
|
|
276
231
|
// === Fallback to Puppeteer's built-in Chromium ===
|
|
277
232
|
if (!chromiumPath) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
233
|
+
if (!arch.startsWith('arm')) {
|
|
234
|
+
try {
|
|
235
|
+
const puppeteerPath = puppeteer.executablePath();
|
|
236
|
+
if (puppeteerPath && fs.existsSync(puppeteerPath)) {
|
|
237
|
+
chromiumPath = puppeteerPath;
|
|
238
|
+
if (this.logDebug) this.emit('debug', `Using Puppeteer Chromium at ${chromiumPath}`);
|
|
239
|
+
}
|
|
240
|
+
} catch { }
|
|
241
|
+
} else {
|
|
242
|
+
if (this.logDebug) this.emit('debug', 'Skipping Puppeteer Chromium on ARM (incompatible)');
|
|
243
|
+
}
|
|
287
244
|
}
|
|
288
245
|
|
|
289
246
|
if (!chromiumPath) {
|
|
@@ -300,6 +257,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
300
257
|
return accountInfo;
|
|
301
258
|
}
|
|
302
259
|
|
|
260
|
+
// Launch Chromium
|
|
303
261
|
if (this.logDebug) this.emit('debug', `Launching Chromium...`);
|
|
304
262
|
browser = await puppeteer.launch({
|
|
305
263
|
headless: true,
|
|
@@ -323,18 +281,67 @@ class MelCloudHome extends EventEmitter {
|
|
|
323
281
|
page.setDefaultNavigationTimeout(GLOBAL_TIMEOUT);
|
|
324
282
|
|
|
325
283
|
// === CDP session ===
|
|
326
|
-
let hash = null;
|
|
327
284
|
const client = await page.createCDPSession();
|
|
328
285
|
await client.send('Network.enable')
|
|
329
286
|
client.on('Network.webSocketCreated', ({ url }) => {
|
|
330
287
|
try {
|
|
331
|
-
if (url.startsWith(
|
|
288
|
+
if (url.startsWith(`${ApiUrlsHome.WebSocketURL}`)) {
|
|
332
289
|
const params = new URL(url).searchParams;
|
|
333
|
-
hash = params.get('hash');
|
|
290
|
+
const hash = params.get('hash');
|
|
334
291
|
if (this.logDebug) this.emit('debug', `MelCloudHome WS hash detected: ${hash}`);
|
|
292
|
+
|
|
293
|
+
//web socket connection
|
|
294
|
+
if (!this.connecting && !this.socketConnected) {
|
|
295
|
+
this.connecting = true;
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const headers = {
|
|
299
|
+
'Origin': ApiUrlsHome.BaseURL,
|
|
300
|
+
'Pragma': 'no-cache',
|
|
301
|
+
'Cache-Control': 'no-cache'
|
|
302
|
+
};
|
|
303
|
+
const webSocket = new WebSocket(`${ApiUrlsHome.WebSocketURL}${hash}`, { headers: headers })
|
|
304
|
+
.on('error', (error) => {
|
|
305
|
+
if (this.logError) this.emit('error', `Socket error: ${error}`);
|
|
306
|
+
try {
|
|
307
|
+
webSocket.close();
|
|
308
|
+
} catch { }
|
|
309
|
+
})
|
|
310
|
+
.on('close', () => {
|
|
311
|
+
if (this.logDebug) this.emit('debug', `Socket closed`);
|
|
312
|
+
this.cleanupSocket();
|
|
313
|
+
})
|
|
314
|
+
.on('open', () => {
|
|
315
|
+
this.socketConnected = true;
|
|
316
|
+
this.connecting = false;
|
|
317
|
+
if (this.logSuccess) this.emit('success', `Socket Connect Success`);
|
|
318
|
+
|
|
319
|
+
// heartbeat
|
|
320
|
+
this.heartbeat = setInterval(() => {
|
|
321
|
+
if (webSocket.readyState === webSocket.OPEN) {
|
|
322
|
+
if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
|
|
323
|
+
webSocket.ping();
|
|
324
|
+
}
|
|
325
|
+
}, 30000);
|
|
326
|
+
})
|
|
327
|
+
.on('pong', () => {
|
|
328
|
+
if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
|
|
329
|
+
})
|
|
330
|
+
.on('message', (message) => {
|
|
331
|
+
const parsedMessage = JSON.parse(message);
|
|
332
|
+
if (this.logDebug) this.emit('debug', `Incoming message: ${JSON.stringify(parsedMessage, null, 2)}`);
|
|
333
|
+
if (parsedMessage.message === 'Forbidden') return;
|
|
334
|
+
|
|
335
|
+
this.emit('webSocket', parsedMessage);
|
|
336
|
+
});
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
339
|
+
this.cleanupSocket();
|
|
340
|
+
}
|
|
341
|
+
}
|
|
335
342
|
}
|
|
336
|
-
} catch (
|
|
337
|
-
this.emit('error', `CDP WebSocketCreated handler error: ${
|
|
343
|
+
} catch (error) {
|
|
344
|
+
if (this.logError) this.emit('error', `CDP WebSocketCreated handler error: ${error.message}`);
|
|
338
345
|
}
|
|
339
346
|
});
|
|
340
347
|
|
|
@@ -411,27 +418,16 @@ class MelCloudHome extends EventEmitter {
|
|
|
411
418
|
'User-Agent': userAgent,
|
|
412
419
|
'x-csrf': '1'
|
|
413
420
|
};
|
|
414
|
-
this.emit('headers', headers);
|
|
415
421
|
|
|
416
|
-
this.
|
|
417
|
-
this.axiosInstance = axios.create({
|
|
422
|
+
this.client = axios.create({
|
|
418
423
|
baseURL: ApiUrlsHome.BaseURL,
|
|
419
424
|
timeout: 30000,
|
|
420
425
|
headers: headers
|
|
421
426
|
})
|
|
422
|
-
|
|
423
|
-
this.webSocketOptions = {
|
|
424
|
-
Hash: hash,
|
|
425
|
-
Headers: {
|
|
426
|
-
'Origin': ApiUrlsHome.BaseURL,
|
|
427
|
-
'Pragma': 'no-cache',
|
|
428
|
-
'Cache-Control': 'no-cache'
|
|
429
|
-
}
|
|
430
|
-
};
|
|
427
|
+
this.emit('client', this.client);
|
|
431
428
|
|
|
432
429
|
accountInfo.State = true;
|
|
433
430
|
accountInfo.Info = 'Connect Success';
|
|
434
|
-
accountInfo.Headers = headers;
|
|
435
431
|
await this.functions.saveData(this.accountFile, accountInfo);
|
|
436
432
|
|
|
437
433
|
return accountInfo;
|