homebridge-midea-platform 1.2.1 → 1.2.3-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/CHANGELOG.md +6 -0
- package/README.md +1 -0
- package/config.schema.json +16 -1
- package/dist/accessory/AccessoryFactory.d.ts +2 -1
- package/dist/accessory/AccessoryFactory.js +3 -0
- package/dist/accessory/AccessoryFactory.js.map +1 -1
- package/dist/accessory/HumidifierAccessory.d.ts +28 -0
- package/dist/accessory/HumidifierAccessory.js +58 -0
- package/dist/accessory/HumidifierAccessory.js.map +1 -0
- package/dist/core/MideaConstants.d.ts +2 -1
- package/dist/core/MideaConstants.js +1 -0
- package/dist/core/MideaConstants.js.map +1 -1
- package/dist/devices/DeviceFactory.d.ts +2 -1
- package/dist/devices/DeviceFactory.js +3 -0
- package/dist/devices/DeviceFactory.js.map +1 -1
- package/dist/devices/fa/MideaFAMessage.js +8 -0
- package/dist/devices/fa/MideaFAMessage.js.map +1 -1
- package/dist/devices/fd/MideaFDDevice.d.ts +35 -0
- package/dist/devices/fd/MideaFDDevice.js +101 -0
- package/dist/devices/fd/MideaFDDevice.js.map +1 -0
- package/dist/devices/fd/MideaFDMessage.d.ts +48 -0
- package/dist/devices/fd/MideaFDMessage.js +131 -0
- package/dist/devices/fd/MideaFDMessage.js.map +1 -0
- package/dist/platformUtils.d.ts +2 -0
- package/dist/platformUtils.js +1 -0
- package/dist/platformUtils.js.map +1 -1
- package/docs/fd.md +3 -0
- package/homebridge-ui/server.js +381 -0
- package/package.json +1 -1
- package/homebridge-ui/server.cjs +0 -305
package/dist/platformUtils.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export type DeviceConfig = {
|
|
|
26
26
|
E2_options: E2Options;
|
|
27
27
|
E3_options: E3Options;
|
|
28
28
|
FA_options: FAOptions;
|
|
29
|
+
FD_options: FDOptions;
|
|
29
30
|
};
|
|
30
31
|
export declare enum SwingMode {
|
|
31
32
|
NONE = "None",
|
|
@@ -112,5 +113,6 @@ type E3Options = {
|
|
|
112
113
|
smartVolumeSwitch: boolean;
|
|
113
114
|
};
|
|
114
115
|
type FAOptions = unknown;
|
|
116
|
+
type FDOptions = unknown;
|
|
115
117
|
export declare const defaultDeviceConfig: DeviceConfig;
|
|
116
118
|
export {};
|
package/dist/platformUtils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platformUtils.js","sourceRoot":"","sources":["../src/platformUtils.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,aAAa,GAAW;IACnC,eAAe,EAAE,EAAE;IACnB,iBAAiB,EAAE,EAAE;IACrB,OAAO,EAAE,KAAK;IACd,OAAO,EAAE,EAAE;CACZ,CAAC;
|
|
1
|
+
{"version":3,"file":"platformUtils.js","sourceRoot":"","sources":["../src/platformUtils.ts"],"names":[],"mappings":"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"}
|
package/docs/fd.md
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* Midea Homebridge platform Custom UI server-side script
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2023 Kovalovszky Patrik, https://github.com/kovapatrik
|
|
5
|
+
* Copyright (c) 2023 David Kerr, https://github.com/dkerr64
|
|
6
|
+
*
|
|
7
|
+
* Based on https://github.com/homebridge/plugin-ui-utils
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
import {
|
|
11
|
+
HomebridgePluginUiServer,
|
|
12
|
+
RequestError,
|
|
13
|
+
} from "@homebridge/plugin-ui-utils";
|
|
14
|
+
import Discover from "../dist/core/MideaDiscover.js";
|
|
15
|
+
import CloudFactory from "../dist/core/MideaCloud.js";
|
|
16
|
+
import {
|
|
17
|
+
DeviceType,
|
|
18
|
+
TCPMessageType,
|
|
19
|
+
ProtocolVersion,
|
|
20
|
+
Endianness,
|
|
21
|
+
} from "../dist/core/MideaConstants.js";
|
|
22
|
+
import { LocalSecurity, ProxiedSecurity } from "../dist/core/MideaSecurity.js";
|
|
23
|
+
import { PromiseSocket } from "../dist/core/MideaUtils.js";
|
|
24
|
+
import { defaultConfig, defaultDeviceConfig } from "../dist/platformUtils.js";
|
|
25
|
+
import { createRequire } from "node:module";
|
|
26
|
+
const require = createRequire(import.meta.url);
|
|
27
|
+
|
|
28
|
+
import _ from "lodash";
|
|
29
|
+
|
|
30
|
+
const DEFAULT_ACCOUNT = [
|
|
31
|
+
BigInt(
|
|
32
|
+
"39182118275972017797890111985649342047468653967530949796945843010512",
|
|
33
|
+
),
|
|
34
|
+
BigInt(
|
|
35
|
+
"29406100301096535908214728322278519471982973450672552249652548883645",
|
|
36
|
+
),
|
|
37
|
+
BigInt(
|
|
38
|
+
"39182118275972017797890111985649342050088014265865102175083010656997",
|
|
39
|
+
),
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/*********************************************************************
|
|
43
|
+
* Logger
|
|
44
|
+
* Lightweight log class to mimic the homebridge log capability
|
|
45
|
+
*/
|
|
46
|
+
class Logger {
|
|
47
|
+
_debug;
|
|
48
|
+
_Reset = "\x1b[0m";
|
|
49
|
+
_Bright = "\x1b[1m";
|
|
50
|
+
_Dim = "\x1b[2m";
|
|
51
|
+
|
|
52
|
+
_FgBlack = "\x1b[30m";
|
|
53
|
+
_FgRed = "\x1b[31m";
|
|
54
|
+
_FgGreen = "\x1b[32m";
|
|
55
|
+
_FgYellow = "\x1b[33m";
|
|
56
|
+
_FgBlue = "\x1b[34m";
|
|
57
|
+
_FgMagenta = "\x1b[35m";
|
|
58
|
+
_FgCyan = "\x1b[36m";
|
|
59
|
+
_FgWhite = "\x1b[37m";
|
|
60
|
+
_FgGray = "\x1b[90m";
|
|
61
|
+
|
|
62
|
+
constructor(uiDebug = false) {
|
|
63
|
+
this._debug = uiDebug;
|
|
64
|
+
}
|
|
65
|
+
info(str) {
|
|
66
|
+
console.info(this._FgWhite + str + this._Reset);
|
|
67
|
+
}
|
|
68
|
+
warn(str) {
|
|
69
|
+
console.warn(this._FgYellow + str + this._Reset);
|
|
70
|
+
}
|
|
71
|
+
error(str) {
|
|
72
|
+
console.error(this._FgRed + str + this._Reset);
|
|
73
|
+
}
|
|
74
|
+
debug(str) {
|
|
75
|
+
if (this._debug) {
|
|
76
|
+
console.debug(this._FgGray + str + this._Reset);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
setDebugEnabled(enabled = true) {
|
|
80
|
+
this._debug = enabled;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/*********************************************************************
|
|
85
|
+
* UIServer
|
|
86
|
+
* Main server-side script called when Custom UI client sends requests
|
|
87
|
+
*/
|
|
88
|
+
class UiServer extends HomebridgePluginUiServer {
|
|
89
|
+
cloud;
|
|
90
|
+
promiseSocket;
|
|
91
|
+
security;
|
|
92
|
+
logger;
|
|
93
|
+
config;
|
|
94
|
+
|
|
95
|
+
constructor() {
|
|
96
|
+
super();
|
|
97
|
+
// Obtain the plugin configuration from homebridge config JSON file.
|
|
98
|
+
const config = require(this.homebridgeConfigPath).platforms.find(
|
|
99
|
+
(obj) => obj.platform === "midea-platform",
|
|
100
|
+
);
|
|
101
|
+
this.logger = new Logger(config?.uiDebug ?? false);
|
|
102
|
+
this.logger.info("Custom UI created.");
|
|
103
|
+
this.logger.debug(`ENV:\n${JSON.stringify(process.env, null, 2)}`);
|
|
104
|
+
this.security = new LocalSecurity();
|
|
105
|
+
this.promiseSocket = new PromiseSocket(
|
|
106
|
+
this.logger,
|
|
107
|
+
config?.uiDebug ?? false,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
this.onRequest(
|
|
111
|
+
"/login",
|
|
112
|
+
async ({ username, password, registeredApp, useDefaultProfile }) => {
|
|
113
|
+
try {
|
|
114
|
+
if (useDefaultProfile) {
|
|
115
|
+
this.logger.debug("Using default profile.");
|
|
116
|
+
registeredApp = "Midea SmartHome (MSmartHome)";
|
|
117
|
+
username = Buffer.from(
|
|
118
|
+
(DEFAULT_ACCOUNT[0] ^ DEFAULT_ACCOUNT[1]).toString(16),
|
|
119
|
+
"hex",
|
|
120
|
+
).toString("ascii");
|
|
121
|
+
password = Buffer.from(
|
|
122
|
+
(DEFAULT_ACCOUNT[0] ^ DEFAULT_ACCOUNT[2]).toString(16),
|
|
123
|
+
"hex",
|
|
124
|
+
).toString("ascii");
|
|
125
|
+
}
|
|
126
|
+
this.cloud = CloudFactory.createCloud(
|
|
127
|
+
username,
|
|
128
|
+
password,
|
|
129
|
+
registeredApp,
|
|
130
|
+
);
|
|
131
|
+
if (username && password && registeredApp) {
|
|
132
|
+
await this.cloud.login();
|
|
133
|
+
}
|
|
134
|
+
} catch (e) {
|
|
135
|
+
const msg = e instanceof Error ? e.stack : e;
|
|
136
|
+
this.logger.warn(`Login failed:\n${msg}`);
|
|
137
|
+
throw new RequestError(
|
|
138
|
+
"Login failed! Check the logs for more information.",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
this.onRequest("/mergeToDefault", async ({ config }) => {
|
|
145
|
+
_.defaultsDeep(config, defaultConfig);
|
|
146
|
+
config.devices.forEach((device) => {
|
|
147
|
+
_.defaultsDeep(device, defaultDeviceConfig);
|
|
148
|
+
});
|
|
149
|
+
this.config = config;
|
|
150
|
+
this.logger.setDebugEnabled(config.uiDebug ? config.uiDebug : false);
|
|
151
|
+
this.logger.debug(`Merged config:\n${JSON.stringify(config, null, 2)}`);
|
|
152
|
+
return config;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
this.onRequest("/getDefaults", async () => {
|
|
156
|
+
return {
|
|
157
|
+
defaultConfig,
|
|
158
|
+
defaultDeviceConfig,
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
this.onRequest("/discover", async ({ ip }) => {
|
|
163
|
+
try {
|
|
164
|
+
const devices = await this.blockingDiscover(ip);
|
|
165
|
+
for (const device of devices) {
|
|
166
|
+
if (device.version === ProtocolVersion.V3 && this.cloud.loggedIn) {
|
|
167
|
+
await this.getNewCredentials(device);
|
|
168
|
+
} else {
|
|
169
|
+
device.token = "";
|
|
170
|
+
device.key = "";
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
this.logger.debug(`All devices:\n${JSON.stringify(devices, null, 2)}`);
|
|
174
|
+
return devices
|
|
175
|
+
.filter((a) => Object.keys(a).length > 0)
|
|
176
|
+
.sort((a, b) => a.ip.localeCompare(b.ip));
|
|
177
|
+
} catch (e) {
|
|
178
|
+
const msg = e instanceof Error ? e.stack : e;
|
|
179
|
+
throw new RequestError(`Device discovery failed:\n${msg}`);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
this.onRequest("/downloadLua", async ({ deviceType, deviceSn }) => {
|
|
184
|
+
try {
|
|
185
|
+
if (!this.cloud || !(this.cloud instanceof ProxiedSecurity)) {
|
|
186
|
+
this.pushEvent("showToast", {
|
|
187
|
+
success: true,
|
|
188
|
+
msg: "Currently used cloud provider doesn't support Lua downloading, using the default profile now...",
|
|
189
|
+
});
|
|
190
|
+
const registeredApp = "Midea SmartHome (MSmartHome)";
|
|
191
|
+
const username = Buffer.from(
|
|
192
|
+
(DEFAULT_ACCOUNT[0] ^ DEFAULT_ACCOUNT[1]).toString(16),
|
|
193
|
+
"hex",
|
|
194
|
+
).toString("ascii");
|
|
195
|
+
const password = Buffer.from(
|
|
196
|
+
(DEFAULT_ACCOUNT[0] ^ DEFAULT_ACCOUNT[2]).toString(16),
|
|
197
|
+
"hex",
|
|
198
|
+
).toString("ascii");
|
|
199
|
+
this.cloud = CloudFactory.createCloud(
|
|
200
|
+
username,
|
|
201
|
+
password,
|
|
202
|
+
registeredApp,
|
|
203
|
+
);
|
|
204
|
+
await this.cloud.login();
|
|
205
|
+
}
|
|
206
|
+
const lua = await this.cloud.getProtocolLua(deviceType, deviceSn);
|
|
207
|
+
return lua;
|
|
208
|
+
} catch (e) {
|
|
209
|
+
const msg = e instanceof Error ? e.stack : e;
|
|
210
|
+
throw new RequestError(`Download Lua failed:\n${msg}`);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// inform client-side script that we are ready to receive requests.
|
|
215
|
+
this.ready();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/*********************************************************************
|
|
219
|
+
* getNewCredentials
|
|
220
|
+
* Obtains token/key credentials and saves them in device object.
|
|
221
|
+
*/
|
|
222
|
+
async getNewCredentials(device) {
|
|
223
|
+
let connected = false;
|
|
224
|
+
let i = 0;
|
|
225
|
+
this.logger.info(`[${device.name}] Retrieve credentials.`);
|
|
226
|
+
// Need to make two passes to obtain token/key credentials as they may work or not
|
|
227
|
+
// depending on byte order (little or big-endian). Exit the loop as soon as one
|
|
228
|
+
// works or having tried both.
|
|
229
|
+
while (i <= 1 && !connected) {
|
|
230
|
+
// Start with big-endianess as it is more likely to succeed.
|
|
231
|
+
const endianess = i === 0 ? Endianness.Little : Endianness.Big;
|
|
232
|
+
try {
|
|
233
|
+
const [token, key] = await this.cloud.getTokenKey(device.id, endianess);
|
|
234
|
+
device.token = token ? token.toString("hex") : undefined;
|
|
235
|
+
device.key = key ? key.toString("hex") : undefined;
|
|
236
|
+
await this.authenticate(device);
|
|
237
|
+
connected = true;
|
|
238
|
+
} catch (e) {
|
|
239
|
+
//const msg = e instanceof Error ? e.stack : e;
|
|
240
|
+
this.logger.debug(
|
|
241
|
+
`[${device.name}] Getting token and key with ${endianess}-endian is not successful.\n${e}`,
|
|
242
|
+
);
|
|
243
|
+
// if failed then reset token/key
|
|
244
|
+
device.token = undefined;
|
|
245
|
+
device.key = undefined;
|
|
246
|
+
}
|
|
247
|
+
i++;
|
|
248
|
+
}
|
|
249
|
+
this.logger.debug(
|
|
250
|
+
`[${device.name}] Token: ${device.token}, Key: ${device.key}`,
|
|
251
|
+
);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/*********************************************************************
|
|
256
|
+
* authenticate
|
|
257
|
+
* authenticate the token/key pair with the device to check that it works.
|
|
258
|
+
*/
|
|
259
|
+
async authenticate(device) {
|
|
260
|
+
if (!(device.token && device.key)) {
|
|
261
|
+
throw new Error(`[${device.name}] Token or key is missing!`);
|
|
262
|
+
}
|
|
263
|
+
await this.promiseSocket.connect(device.port, device.ip);
|
|
264
|
+
// Wrap next block in try/finally so we can destroy the socket if error occurs
|
|
265
|
+
// let thrown errors cascade up.
|
|
266
|
+
try {
|
|
267
|
+
const request = this.security.encode_8370(
|
|
268
|
+
Buffer.from(device.token, "hex"),
|
|
269
|
+
TCPMessageType.HANDSHAKE_REQUEST,
|
|
270
|
+
);
|
|
271
|
+
await this.promiseSocket.write(request);
|
|
272
|
+
const response = await this.promiseSocket.read();
|
|
273
|
+
if (response) {
|
|
274
|
+
if (response.length < 20) {
|
|
275
|
+
this.logger.debug(
|
|
276
|
+
`[${device.name}] Authenticate error when receiving data from ${device.ip}:${device.port}. (Data length: ${
|
|
277
|
+
response.length
|
|
278
|
+
})\n${JSON.stringify(response)}`,
|
|
279
|
+
);
|
|
280
|
+
throw Error(
|
|
281
|
+
`[${device.name}] Authenticate error when receiving data from ${device.ip}:${device.port}. (Data length mismatch)`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
const resp = response.subarray(8, 72);
|
|
285
|
+
this.security.tcp_key_from_resp(resp, Buffer.from(device.key, "hex"));
|
|
286
|
+
} else {
|
|
287
|
+
throw Error(
|
|
288
|
+
`[${device.name}] Authenticate error when receiving data from ${device.ip}:${device.port}.`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
} finally {
|
|
292
|
+
this.promiseSocket.destroy();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/*********************************************************************
|
|
297
|
+
* blockingDiscover
|
|
298
|
+
* broadcast to network(s) to discover new devices, obtain credentials
|
|
299
|
+
* for each as discovered.
|
|
300
|
+
*/
|
|
301
|
+
async blockingDiscover(ipAddrs = undefined) {
|
|
302
|
+
let devices = [];
|
|
303
|
+
this.logger.debug(
|
|
304
|
+
`[blockingDiscover] IP addresses: ${JSON.stringify(ipAddrs)}`,
|
|
305
|
+
);
|
|
306
|
+
const discover = new Discover(this.logger);
|
|
307
|
+
return new Promise((resolve, reject) => {
|
|
308
|
+
this.logger.info("Start device discovery...");
|
|
309
|
+
this.pushEvent("showToast", {
|
|
310
|
+
success: true,
|
|
311
|
+
msg: "Start device discovery",
|
|
312
|
+
});
|
|
313
|
+
// If IP addresses provided then probe them directly
|
|
314
|
+
ipAddrs?.forEach((ip) => {
|
|
315
|
+
discover.discoverDeviceByIP(ip);
|
|
316
|
+
});
|
|
317
|
+
// And then send broadcast to network(s)
|
|
318
|
+
discover.startDiscover();
|
|
319
|
+
|
|
320
|
+
discover.on("device", async (device) => {
|
|
321
|
+
switch (device.type) {
|
|
322
|
+
case DeviceType.AIR_CONDITIONER:
|
|
323
|
+
device.displayName = "Air Conditioner";
|
|
324
|
+
break;
|
|
325
|
+
case DeviceType.DEHUMIDIFIER:
|
|
326
|
+
device.displayName = "Dehumidifier";
|
|
327
|
+
break;
|
|
328
|
+
case DeviceType.HEAT_PUMP_WIFI_CONTROLLER:
|
|
329
|
+
device.displayName = "Heat Pump WiFi Controller";
|
|
330
|
+
case DeviceType.FRONT_LOAD_WASHER:
|
|
331
|
+
device.displayName = "Front Load Washer";
|
|
332
|
+
break;
|
|
333
|
+
case DeviceType.DISHWASHER:
|
|
334
|
+
device.displayName = "Dishwasher";
|
|
335
|
+
break;
|
|
336
|
+
case DeviceType.ELECTRIC_WATER_HEATER:
|
|
337
|
+
device.displayName = "Electric Water Heater";
|
|
338
|
+
break;
|
|
339
|
+
case DeviceType.GAS_WATER_HEATER:
|
|
340
|
+
device.displayName = "Gas Water Heater";
|
|
341
|
+
break;
|
|
342
|
+
case DeviceType.FAN:
|
|
343
|
+
device.displayName = "Fan";
|
|
344
|
+
break;
|
|
345
|
+
case DeviceType.HUMIDIFIER:
|
|
346
|
+
device.displayName = "Humidifier";
|
|
347
|
+
break;
|
|
348
|
+
case DeviceType.UNKNOWN:
|
|
349
|
+
default:
|
|
350
|
+
device.displayName = "Unknown";
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
devices.push(device);
|
|
354
|
+
// too verbose to post every device as found...
|
|
355
|
+
// this.pushEvent('showToast', { success: true, msg: `Discovered ${device.name} at ${device.ip}`, device: device });
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
discover.on("retry", (nTry, nDevices) => {
|
|
359
|
+
this.logger.info("Device discovery complete.");
|
|
360
|
+
this.pushEvent("showToast", {
|
|
361
|
+
success: true,
|
|
362
|
+
msg: `Continuing to search for devices (${nDevices} found)`,
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
discover.on("complete", () => {
|
|
367
|
+
this.logger.info("Device discovery complete.");
|
|
368
|
+
this.pushEvent("showToast", {
|
|
369
|
+
success: true,
|
|
370
|
+
msg: "Discovery complete",
|
|
371
|
+
});
|
|
372
|
+
resolve(devices);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// start the instance of the class
|
|
379
|
+
(() => {
|
|
380
|
+
return new UiServer();
|
|
381
|
+
})();
|
package/package.json
CHANGED