homebridge-melcloud-control 4.3.2-beta.7 → 4.3.2-beta.9
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 +5 -5
- package/package.json +1 -1
- package/src/functions.js +8 -0
- package/src/melcloudata.js +102 -94
- package/src/melcloudhome.js +1 -5
package/index.js
CHANGED
|
@@ -57,7 +57,7 @@ class MelCloudPlatform {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
if (logLevel.debug) {
|
|
60
|
-
log.
|
|
60
|
+
log.info(`${accountName}, debug: did finish launching.`);
|
|
61
61
|
const safeConfig = {
|
|
62
62
|
...account,
|
|
63
63
|
passwd: 'removed',
|
|
@@ -88,7 +88,7 @@ class MelCloudPlatform {
|
|
|
88
88
|
const melCloud = account.type === 'melcloud' ? new MelCloud(account, accountFile, buildingsFile, devicesFile, true) : new MelCloudHome(account, accountFile, buildingsFile, devicesFile, true)
|
|
89
89
|
.on('success', (msg) => log.success(`${accountName}, ${msg}`))
|
|
90
90
|
.on('info', (msg) => log.info(`${accountName}, ${msg}`))
|
|
91
|
-
.on('debug', (msg) => log.
|
|
91
|
+
.on('debug', (msg) => log.info(`${accountName}, debug: ${msg}`))
|
|
92
92
|
.on('warn', (msg) => log.warn(`${accountName}, ${msg}`))
|
|
93
93
|
.on('error', (msg) => log.error(`${accountName}, ${msg}`));
|
|
94
94
|
|
|
@@ -125,7 +125,7 @@ class MelCloudPlatform {
|
|
|
125
125
|
const atwDevices = (account.atwDevices || []).filter(device => device.id != null && String(device.id) !== '0');
|
|
126
126
|
const ervDevices = (account.ervDevices || []).filter(device => device.id != null && String(device.id) !== '0');
|
|
127
127
|
const devices = [...ataDevices, ...atwDevices, ...ervDevices];
|
|
128
|
-
if (logLevel.debug) log.
|
|
128
|
+
if (logLevel.debug) log.info(`Found configured devices ATA: ${ataDevices.length}, ATW: ${atwDevices.length}, ERV: ${ervDevices.length}.`);
|
|
129
129
|
|
|
130
130
|
for (const [index, device] of devices.entries()) {
|
|
131
131
|
//chack device from config exist on melcloud
|
|
@@ -183,7 +183,7 @@ class MelCloudPlatform {
|
|
|
183
183
|
configuredDevice.on('devInfo', (info) => logLevel.devInfo && log.info(info))
|
|
184
184
|
.on('success', (msg) => log.success(`${accountName}, ${deviceTypeString}, ${deviceName}, ${msg}`))
|
|
185
185
|
.on('info', (msg) => log.info(`${accountName}, ${deviceTypeString}, ${deviceName}, ${msg}`))
|
|
186
|
-
.on('debug', (msg) => log.
|
|
186
|
+
.on('debug', (msg) => log.info(`${accountName}, ${deviceTypeString}, ${deviceName}, debug: ${msg}`))
|
|
187
187
|
.on('warn', (msg) => log.warn(`${accountName}, ${deviceTypeString}, ${deviceName}, ${msg}`))
|
|
188
188
|
.on('error', (msg) => log.error(`${accountName}, ${deviceTypeString}, ${deviceName}, ${msg}`));
|
|
189
189
|
|
|
@@ -205,7 +205,7 @@ class MelCloudPlatform {
|
|
|
205
205
|
if (logLevel.error) log.error(`${accountName}, Start impulse generator error, ${error.message ?? error}, trying again.`);
|
|
206
206
|
}
|
|
207
207
|
}).on('state', (state) => {
|
|
208
|
-
if (logLevel.debug) log.
|
|
208
|
+
if (logLevel.debug) log.info(`${accountName}, Start impulse generator ${state ? 'started' : 'stopped'}.`);
|
|
209
209
|
});
|
|
210
210
|
|
|
211
211
|
//start impulse generator
|
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.2-beta.
|
|
4
|
+
"version": "4.3.2-beta.9",
|
|
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
|
@@ -160,5 +160,13 @@ class Functions extends EventEmitter {
|
|
|
160
160
|
return v !== undefined && v !== null && !(typeof v === 'number' && Number.isNaN(v));
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
convertValue(value, name) {
|
|
164
|
+
let parsedValue = value;
|
|
165
|
+
if (value === "True") parsedValue = true;
|
|
166
|
+
else if (value === "False") parsedValue = false;
|
|
167
|
+
else if (!isNaN(value) && value !== "") parsedValue = Number(value);
|
|
168
|
+
return [name, parsedValue];
|
|
169
|
+
}
|
|
170
|
+
|
|
163
171
|
}
|
|
164
172
|
export default Functions
|
package/src/melcloudata.js
CHANGED
|
@@ -70,17 +70,8 @@ class MelCloudAta extends EventEmitter {
|
|
|
70
70
|
this.socketConnected = false;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
async
|
|
73
|
+
async updateState(deviceData) {
|
|
74
74
|
try {
|
|
75
|
-
|
|
76
|
-
//read device info from file
|
|
77
|
-
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
78
|
-
if (!devicesData) return;
|
|
79
|
-
|
|
80
|
-
this.headers = devicesData.Headers;
|
|
81
|
-
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
82
|
-
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
83
|
-
|
|
84
75
|
if (this.accountType === 'melcloudhome') {
|
|
85
76
|
deviceData.Device.OperationMode = AirConditioner.OperationModeMapStringToEnum[deviceData.Device.OperationMode] ?? deviceData.Device.OperationMode;
|
|
86
77
|
deviceData.Device.ActualFanSpeed = AirConditioner.FanSpeedMapStringToEnum[deviceData.Device.ActualFanSpeed] ?? deviceData.Device.ActualFanSpeed;
|
|
@@ -93,87 +84,6 @@ class MelCloudAta extends EventEmitter {
|
|
|
93
84
|
deviceData.Device.DefaultHeatingSetTemperature = temps?.defaultHeatingSetTemperature ?? 20;
|
|
94
85
|
deviceData.Device.DefaultCoolingSetTemperature = temps?.defaultCoolingSetTemperature ?? 24;
|
|
95
86
|
|
|
96
|
-
//web cocket connection
|
|
97
|
-
if (!this.connecting && !this.socketConnected) {
|
|
98
|
-
this.connecting = true;
|
|
99
|
-
|
|
100
|
-
const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
|
|
101
|
-
try {
|
|
102
|
-
const socket = new WebSocket(url, { headers: devicesData.WebSocketOptions.Headers })
|
|
103
|
-
.on('error', (error) => {
|
|
104
|
-
if (this.logError) this.emit('error', `Socket error: ${error}`);
|
|
105
|
-
socket.close();
|
|
106
|
-
})
|
|
107
|
-
.on('close', () => {
|
|
108
|
-
if (this.logDebug) this.emit('debug', `Socket closed`);
|
|
109
|
-
this.cleanupSocket();
|
|
110
|
-
})
|
|
111
|
-
.on('open', () => {
|
|
112
|
-
this.socket = socket;
|
|
113
|
-
this.socketConnected = true;
|
|
114
|
-
this.connecting = false;
|
|
115
|
-
if (this.logSuccess) this.emit('success', `Socket Connect Success`);
|
|
116
|
-
|
|
117
|
-
// heartbeat
|
|
118
|
-
this.heartbeat = setInterval(() => {
|
|
119
|
-
if (socket.readyState === socket.OPEN) {
|
|
120
|
-
if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
|
|
121
|
-
socket.ping();
|
|
122
|
-
}
|
|
123
|
-
}, 30000);
|
|
124
|
-
})
|
|
125
|
-
.on('pong', () => {
|
|
126
|
-
if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
|
|
127
|
-
})
|
|
128
|
-
.on('message', (message) => {
|
|
129
|
-
const parsedMessage = JSON.parse(message);
|
|
130
|
-
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
131
|
-
if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
132
|
-
if (parsedMessage.message === 'Forbidden') return;
|
|
133
|
-
|
|
134
|
-
const messageData = parsedMessage?.[0]?.Data;
|
|
135
|
-
if (!messageData) return;
|
|
136
|
-
|
|
137
|
-
let updateDeviceState = false;
|
|
138
|
-
const unitId = messageData?.id;
|
|
139
|
-
switch (unitId) {
|
|
140
|
-
case this.deviceId:
|
|
141
|
-
const messageType = parsedMessage[0].messageType;
|
|
142
|
-
switch (messageType) {
|
|
143
|
-
case 'unitStateChanged':
|
|
144
|
-
const settings = Object.fromEntries(
|
|
145
|
-
messageData.settings.map(({ name, value }) => {
|
|
146
|
-
let parsedValue = value;
|
|
147
|
-
if (value === "True") parsedValue = true;
|
|
148
|
-
else if (value === "False") parsedValue = false;
|
|
149
|
-
else if (!isNaN(value) && value !== "") parsedValue = Number(value);
|
|
150
|
-
return [name, parsedValue];
|
|
151
|
-
})
|
|
152
|
-
);
|
|
153
|
-
Object.assign(deviceData.Device, settings);
|
|
154
|
-
updateDeviceState = true;
|
|
155
|
-
break;
|
|
156
|
-
case 'unitWifiSignalChanged':
|
|
157
|
-
deviceData.Rssi = messageData.rssi;
|
|
158
|
-
updateDeviceState = true;
|
|
159
|
-
break;
|
|
160
|
-
default:
|
|
161
|
-
if (!this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
break;
|
|
165
|
-
default:
|
|
166
|
-
if (!this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (updateDeviceState) this.emit('deviceState', deviceData);
|
|
171
|
-
});
|
|
172
|
-
} catch (error) {
|
|
173
|
-
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
174
|
-
this.cleanupSocket();
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
87
|
}
|
|
178
88
|
if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
|
|
179
89
|
|
|
@@ -233,6 +143,104 @@ class MelCloudAta extends EventEmitter {
|
|
|
233
143
|
};
|
|
234
144
|
};
|
|
235
145
|
|
|
146
|
+
async checkState() {
|
|
147
|
+
try {
|
|
148
|
+
|
|
149
|
+
//read device info from file
|
|
150
|
+
const devicesData = await this.functions.readData(this.devicesFile, true);
|
|
151
|
+
if (!devicesData) return;
|
|
152
|
+
|
|
153
|
+
this.headers = devicesData.Headers;
|
|
154
|
+
const deviceData = devicesData.Devices.find(device => device.DeviceID === this.deviceId);
|
|
155
|
+
deviceData.Scenes = devicesData.Scenes ?? [];
|
|
156
|
+
|
|
157
|
+
//web cocket connection
|
|
158
|
+
if (this.accountType === 'melcloudhome' && !this.connecting && !this.socketConnected) {
|
|
159
|
+
this.connecting = true;
|
|
160
|
+
|
|
161
|
+
const url = `${ApiUrlsHome.WebSocketURL}${devicesData.WebSocketOptions.Hash}`;
|
|
162
|
+
try {
|
|
163
|
+
const socket = new WebSocket(url, { headers: devicesData.WebSocketOptions.Headers })
|
|
164
|
+
.on('error', (error) => {
|
|
165
|
+
if (this.logError) this.emit('error', `Socket error: ${error}`);
|
|
166
|
+
socket.close();
|
|
167
|
+
})
|
|
168
|
+
.on('close', () => {
|
|
169
|
+
if (this.logDebug) this.emit('debug', `Socket closed`);
|
|
170
|
+
this.cleanupSocket();
|
|
171
|
+
})
|
|
172
|
+
.on('open', () => {
|
|
173
|
+
this.socket = socket;
|
|
174
|
+
this.socketConnected = true;
|
|
175
|
+
this.connecting = false;
|
|
176
|
+
if (this.logSuccess) this.emit('success', `Socket Connect Success`);
|
|
177
|
+
|
|
178
|
+
// heartbeat
|
|
179
|
+
this.heartbeat = setInterval(() => {
|
|
180
|
+
if (socket.readyState === socket.OPEN) {
|
|
181
|
+
if (this.logDebug) this.emit('debug', `Socket send heartbeat`);
|
|
182
|
+
socket.ping();
|
|
183
|
+
}
|
|
184
|
+
}, 30000);
|
|
185
|
+
})
|
|
186
|
+
.on('pong', () => {
|
|
187
|
+
if (this.logDebug) this.emit('debug', `Socket received heartbeat`);
|
|
188
|
+
})
|
|
189
|
+
.on('message', async (message) => {
|
|
190
|
+
const parsedMessage = JSON.parse(message);
|
|
191
|
+
const stringifyMessage = JSON.stringify(parsedMessage, null, 2);
|
|
192
|
+
if (this.logDebug) this.emit('debug', `Incoming message: ${stringifyMessage}`);
|
|
193
|
+
if (parsedMessage.message === 'Forbidden') return;
|
|
194
|
+
|
|
195
|
+
const messageData = parsedMessage?.[0]?.Data;
|
|
196
|
+
if (!messageData) return;
|
|
197
|
+
|
|
198
|
+
let updateDeviceState = false;
|
|
199
|
+
const unitId = messageData?.id;
|
|
200
|
+
switch (unitId) {
|
|
201
|
+
case this.deviceId:
|
|
202
|
+
const messageType = parsedMessage[0].messageType;
|
|
203
|
+
switch (messageType) {
|
|
204
|
+
case 'unitStateChanged':
|
|
205
|
+
const settings = Object.fromEntries(
|
|
206
|
+
messageData.settings.map(({ name, value }) => {
|
|
207
|
+
let parsedValue = this.functions.convertValue(value, name);
|
|
208
|
+
return parsedValue;
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
Object.assign(deviceData.Device, settings);
|
|
212
|
+
updateDeviceState = true;
|
|
213
|
+
break;
|
|
214
|
+
case 'unitWifiSignalChanged':
|
|
215
|
+
deviceData.Rssi = messageData.rssi;
|
|
216
|
+
updateDeviceState = true;
|
|
217
|
+
break;
|
|
218
|
+
default:
|
|
219
|
+
if (!this.logDebug) this.emit('debug', `Unit ${unitId}, received unknown message type: ${stringifyMessage}`);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
default:
|
|
224
|
+
if (!this.logDebug) this.emit('debug', `Incoming unknown unit id: ${stringifyMessage}`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (updateDeviceState) await this.updateState(deviceData);
|
|
229
|
+
});
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (this.logError) this.emit('error', `Socket connection failed: ${error}`);
|
|
232
|
+
this.cleanupSocket();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (this.logDebug) this.emit('debug', `Device Data: ${JSON.stringify(deviceData, null, 2)}`);
|
|
236
|
+
await this.updateState(deviceData);
|
|
237
|
+
|
|
238
|
+
return true;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
throw new Error(`Check state error: ${error.message}`);
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
|
|
236
244
|
async send(accountType, displayType, deviceData, flag, flagData) {
|
|
237
245
|
try {
|
|
238
246
|
let method = null
|
|
@@ -374,7 +382,7 @@ class MelCloudAta extends EventEmitter {
|
|
|
374
382
|
data: payload
|
|
375
383
|
});
|
|
376
384
|
|
|
377
|
-
this.updateData(deviceData, updateState);
|
|
385
|
+
await this.updateData(deviceData, updateState);
|
|
378
386
|
return true;
|
|
379
387
|
default:
|
|
380
388
|
return;
|
|
@@ -385,9 +393,9 @@ class MelCloudAta extends EventEmitter {
|
|
|
385
393
|
}
|
|
386
394
|
}
|
|
387
395
|
|
|
388
|
-
updateData(deviceData, updateState = true) {
|
|
396
|
+
async updateData(deviceData, updateState = true) {
|
|
389
397
|
this.locks = true;
|
|
390
|
-
if (updateState) this.
|
|
398
|
+
if (updateState) await this.updateState(deviceData);
|
|
391
399
|
|
|
392
400
|
setTimeout(() => {
|
|
393
401
|
this.locks = false
|
package/src/melcloudhome.js
CHANGED
|
@@ -145,11 +145,7 @@ class MelCloudHome extends EventEmitter {
|
|
|
145
145
|
|
|
146
146
|
const settingsObject = Object.fromEntries(
|
|
147
147
|
settingsArray.map(({ name, value }) => {
|
|
148
|
-
let parsedValue = value;
|
|
149
|
-
if (value === "True") parsedValue = true;
|
|
150
|
-
else if (value === "False") parsedValue = false;
|
|
151
|
-
else if (!isNaN(value) && value !== "") parsedValue = Number(value);
|
|
152
|
-
|
|
148
|
+
let parsedValue = this.functions.convertValue(value, name);
|
|
153
149
|
const key = name.charAt(0).toUpperCase() + name.slice(1);
|
|
154
150
|
return [key, parsedValue];
|
|
155
151
|
})
|