homebridge-midea-platform 1.2.6-beta.2 → 1.2.6-beta.21
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/.luarc.json +5 -0
- package/CHANGELOG.md +3 -0
- package/README.md +1 -1
- package/ac_00000Q11.lua +5148 -0
- package/biome.json +1 -1
- package/c3_HeatPump_152832116442666.lua +4681 -0
- package/cd_RSJ000CB.lua +4983 -0
- package/config.schema.json +66 -2
- package/dist/accessory/AccessoryFactory.d.ts +4 -3
- package/dist/accessory/AccessoryFactory.js +3 -0
- package/dist/accessory/AccessoryFactory.js.map +1 -1
- package/dist/accessory/AirConditionerAccessory.js +64 -63
- package/dist/accessory/AirConditionerAccessory.js.map +1 -1
- package/dist/accessory/BaseAccessory.d.ts +2 -0
- package/dist/accessory/BaseAccessory.js +14 -0
- package/dist/accessory/BaseAccessory.js.map +1 -1
- package/dist/accessory/DehumidifierAccessory.js +23 -18
- package/dist/accessory/DehumidifierAccessory.js.map +1 -1
- package/dist/accessory/ElectricWaterHeaterAccessory.js +9 -8
- package/dist/accessory/ElectricWaterHeaterAccessory.js.map +1 -1
- package/dist/accessory/GasWaterHeaterAccessory.js +20 -20
- package/dist/accessory/GasWaterHeaterAccessory.js.map +1 -1
- package/dist/accessory/HeatPumpWaterHeaterAccessory.d.ts +45 -0
- package/dist/accessory/HeatPumpWaterHeaterAccessory.js +167 -0
- package/dist/accessory/HeatPumpWaterHeaterAccessory.js.map +1 -0
- package/dist/core/MideaCloud.js +2 -2
- package/dist/core/MideaCloud.js.map +1 -1
- package/dist/core/MideaConstants.d.ts +1 -0
- package/dist/core/MideaConstants.js +2 -0
- package/dist/core/MideaConstants.js.map +1 -1
- package/dist/devices/DeviceFactory.d.ts +2 -1
- package/dist/devices/DeviceFactory.js +5 -2
- package/dist/devices/DeviceFactory.js.map +1 -1
- package/dist/devices/ac/MideaACMessage.js +1 -1
- package/dist/devices/ac/MideaACMessage.js.map +1 -1
- package/dist/devices/c3/MideaC3Device.d.ts +3 -2
- package/dist/devices/c3/MideaC3Device.js +20 -7
- package/dist/devices/c3/MideaC3Device.js.map +1 -1
- package/dist/devices/c3/MideaC3Message.d.ts +128 -12
- package/dist/devices/c3/MideaC3Message.js +283 -35
- package/dist/devices/c3/MideaC3Message.js.map +1 -1
- package/dist/devices/cd/MideaCDDevice.d.ts +42 -0
- package/dist/devices/cd/MideaCDDevice.js +105 -0
- package/dist/devices/cd/MideaCDDevice.js.map +1 -0
- package/dist/devices/cd/MideaCDMessage.d.ts +101 -0
- package/dist/devices/cd/MideaCDMessage.js +237 -0
- package/dist/devices/cd/MideaCDMessage.js.map +1 -0
- package/dist/devices/ce/MideaCEDevice.js +1 -1
- package/dist/platform.d.ts +3 -0
- package/dist/platform.js +0 -11
- package/dist/platform.js.map +1 -1
- package/dist/platformUtils.d.ts +11 -0
- package/dist/platformUtils.js +10 -0
- package/dist/platformUtils.js.map +1 -1
- package/docs/download_lua.md +3 -2
- package/homebridge-ui/public/index.html +57 -7
- package/homebridge-ui/server.js +94 -146
- package/package.json +1 -1
package/dist/platformUtils.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export type DeviceConfig = {
|
|
|
21
21
|
AC_options: ACOptions;
|
|
22
22
|
A1_options: A1Options;
|
|
23
23
|
C3_options: C3Options;
|
|
24
|
+
CD_options: CDOptions;
|
|
24
25
|
CE_options: CEOptions;
|
|
25
26
|
DB_options: DBOptions;
|
|
26
27
|
E1_options: E1Options;
|
|
@@ -93,6 +94,16 @@ type C3Options = {
|
|
|
93
94
|
silentSwitch: boolean;
|
|
94
95
|
tbhSwitch: boolean;
|
|
95
96
|
};
|
|
97
|
+
type CDOptions = {
|
|
98
|
+
minTemp: number;
|
|
99
|
+
maxTemp: number;
|
|
100
|
+
tempStep: number;
|
|
101
|
+
energySaveModeSwitch: boolean;
|
|
102
|
+
standardModeSwitch: boolean;
|
|
103
|
+
eHeaterModeSwitch: boolean;
|
|
104
|
+
smartModeSwitch: boolean;
|
|
105
|
+
disinfectionSwitch: boolean;
|
|
106
|
+
};
|
|
96
107
|
type CEOptions = {
|
|
97
108
|
silentMode: boolean;
|
|
98
109
|
autoSetModeSwitch: boolean;
|
package/dist/platformUtils.js
CHANGED
|
@@ -83,6 +83,16 @@ export const defaultDeviceConfig = {
|
|
|
83
83
|
silentSwitch: false,
|
|
84
84
|
tbhSwitch: false,
|
|
85
85
|
},
|
|
86
|
+
CD_options: {
|
|
87
|
+
minTemp: 38,
|
|
88
|
+
maxTemp: 70,
|
|
89
|
+
tempStep: 0.5,
|
|
90
|
+
energySaveModeSwitch: false,
|
|
91
|
+
standardModeSwitch: false,
|
|
92
|
+
eHeaterModeSwitch: false,
|
|
93
|
+
smartModeSwitch: false,
|
|
94
|
+
disinfectionSwitch: false,
|
|
95
|
+
},
|
|
86
96
|
CE_options: {
|
|
87
97
|
autoSetModeSwitch: false,
|
|
88
98
|
minTemp: 16,
|
|
@@ -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/download_lua.md
CHANGED
|
@@ -5,5 +5,6 @@ If you need support for a device, or a new feature for an existing one, please d
|
|
|
5
5
|
2. Do the discovery process through the UI
|
|
6
6
|
- Here you must your or the default NetHome Plus account (if you don't have your own account in these apps)
|
|
7
7
|
3. After the discovery ends, there will be table of discovered devices
|
|
8
|
-
4. In the `Model` column, the model of your device will be a button, which you can click
|
|
9
|
-
5.
|
|
8
|
+
4. In the `Model` column, the model of your device will be a button, which you can click top initiate fetching the lua file which will be shown in a modal.
|
|
9
|
+
5. Save the content of the file in a text file with a `.lua` extension. (there is a suggestion for the file name as well, but you can choose any name you like)
|
|
10
|
+
6. Open an issue about the feature you need and attach the lua script to it (or open an issue and send me the script in email, it's `kovapatrik@gmail.com`)
|
|
@@ -199,6 +199,7 @@
|
|
|
199
199
|
createForm(configSchema, configuration);
|
|
200
200
|
homebridge.hideSpinner();
|
|
201
201
|
|
|
202
|
+
const main = document.getElementById('main');
|
|
202
203
|
const loginSection = document.getElementById('login')
|
|
203
204
|
const userPass = document.getElementById('userPass');
|
|
204
205
|
|
|
@@ -286,15 +287,64 @@
|
|
|
286
287
|
download_btn.className = 'btn btn-outline-secondary btn-sm';
|
|
287
288
|
download_btn.addEventListener('click', async () => {
|
|
288
289
|
try {
|
|
290
|
+
homebridge.showSpinner();
|
|
289
291
|
const file_data_str = await homebridge.request('/downloadLua', { deviceType: device.type, deviceSn: device.sn });
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
292
|
+
|
|
293
|
+
const modal = document.createElement("div");
|
|
294
|
+
modal.className = 'modal fade';
|
|
295
|
+
modal.id = 'luaCodeModal';
|
|
296
|
+
modal.setAttribute('tabindex', '-1');
|
|
297
|
+
modal.setAttribute('aria-labelledby', 'luaCodeModalLabel');
|
|
298
|
+
modal.setAttribute('aria-hidden', 'true');
|
|
299
|
+
|
|
300
|
+
modal.innerHTML = `
|
|
301
|
+
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
|
302
|
+
<div class="modal-content">
|
|
303
|
+
<div class="modal-header">
|
|
304
|
+
<h5 class="modal-title" id="luaCodeModalLabel">Lua Code for ${device.model}</h5>
|
|
305
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
306
|
+
</div>
|
|
307
|
+
<div class="modal-body">
|
|
308
|
+
<div class="mb-3 btn-toolbar">
|
|
309
|
+
<div class="btn-group me-2">
|
|
310
|
+
<button id="selectAllLuaBtn" class="btn btn-secondary">Select All</button>
|
|
311
|
+
</div>
|
|
312
|
+
<small class="text-warning ms-2 align-self-center">Save this as ${device.type.toString(16)}_${device.model}.lua</small>
|
|
313
|
+
</div>
|
|
314
|
+
<pre id="luaCodePre"><code style="color: var(--bs-body-color);">${file_data_str}</code></pre>
|
|
315
|
+
</div>
|
|
316
|
+
<div class="modal-footer">
|
|
317
|
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
`;
|
|
322
|
+
|
|
323
|
+
main.appendChild(modal);
|
|
324
|
+
|
|
325
|
+
// Initialize the Bootstrap modal
|
|
326
|
+
const bootstrapModal = new bootstrap.Modal(modal);
|
|
327
|
+
bootstrapModal.show();
|
|
328
|
+
|
|
329
|
+
document.getElementById('selectAllLuaBtn').addEventListener('click', () => {
|
|
330
|
+
const codeElement = document.getElementById('luaCodePre');
|
|
331
|
+
const selection = window.getSelection();
|
|
332
|
+
const range = document.createRange();
|
|
333
|
+
|
|
334
|
+
range.selectNodeContents(codeElement);
|
|
335
|
+
selection.removeAllRanges();
|
|
336
|
+
selection.addRange(range);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Clean up when modal is hidden
|
|
340
|
+
modal.addEventListener('hidden.bs.modal', () => {
|
|
341
|
+
main.removeChild(modal);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
homebridge.hideSpinner();
|
|
345
|
+
|
|
297
346
|
} catch (e) {
|
|
347
|
+
homebridge.hideSpinner();
|
|
298
348
|
homebridge.toast.error(e.message);
|
|
299
349
|
}
|
|
300
350
|
});
|
package/homebridge-ui/server.js
CHANGED
|
@@ -7,36 +7,28 @@
|
|
|
7
7
|
* Based on https://github.com/homebridge/plugin-ui-utils
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from
|
|
14
|
-
import Discover from
|
|
15
|
-
import
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
TCPMessageType,
|
|
19
|
-
ProtocolVersion,
|
|
20
|
-
Endianness,
|
|
21
|
-
} from "../dist/core/MideaConstants.js";
|
|
22
|
-
import { LocalSecurity } 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";
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
|
+
import { HomebridgePluginUiServer, RequestError } from '@homebridge/plugin-ui-utils';
|
|
12
|
+
import CloudFactory from '../dist/core/MideaCloud.js';
|
|
13
|
+
import { DeviceTypeToName, Endianness, ProtocolVersion, TCPMessageType } from '../dist/core/MideaConstants.js';
|
|
14
|
+
import Discover from '../dist/core/MideaDiscover.js';
|
|
15
|
+
import { LocalSecurity } from '../dist/core/MideaSecurity.js';
|
|
16
|
+
import { PromiseSocket } from '../dist/core/MideaUtils.js';
|
|
17
|
+
import { defaultConfig, defaultDeviceConfig } from '../dist/platformUtils.js';
|
|
26
18
|
const require = createRequire(import.meta.url);
|
|
27
19
|
|
|
28
|
-
import _ from
|
|
20
|
+
import _ from 'lodash';
|
|
29
21
|
|
|
30
22
|
const DEFAULT_ACCOUNT = [
|
|
31
|
-
BigInt(
|
|
32
|
-
|
|
33
|
-
),
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
BigInt(
|
|
38
|
-
|
|
39
|
-
),
|
|
23
|
+
BigInt('41136566961777418441619689108052131385308997994436615360276316597550126349990'),
|
|
24
|
+
BigInt('41136566961777418205521495345904086238221761646585049169700858993146668339659'),
|
|
25
|
+
BigInt('41136566961777418441619689108052131385308997994436615362339979365072738212503'),
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const DEFAULT_SMARTHOME_ACCOUNT = [
|
|
29
|
+
BigInt('4270685954756226103292057380984602745528999549492916172461797725852782444008'),
|
|
30
|
+
BigInt('4270685954756226103289246300068351442953216461967596801287597164299361014405'),
|
|
31
|
+
BigInt('4270685954756226103292057380984602757738380835485182656166614575373678037465'),
|
|
40
32
|
];
|
|
41
33
|
|
|
42
34
|
/*********************************************************************
|
|
@@ -45,19 +37,19 @@ const DEFAULT_ACCOUNT = [
|
|
|
45
37
|
*/
|
|
46
38
|
class Logger {
|
|
47
39
|
_debug;
|
|
48
|
-
_Reset =
|
|
49
|
-
_Bright =
|
|
50
|
-
_Dim =
|
|
40
|
+
_Reset = '\x1b[0m';
|
|
41
|
+
_Bright = '\x1b[1m';
|
|
42
|
+
_Dim = '\x1b[2m';
|
|
51
43
|
|
|
52
|
-
_FgBlack =
|
|
53
|
-
_FgRed =
|
|
54
|
-
_FgGreen =
|
|
55
|
-
_FgYellow =
|
|
56
|
-
_FgBlue =
|
|
57
|
-
_FgMagenta =
|
|
58
|
-
_FgCyan =
|
|
59
|
-
_FgWhite =
|
|
60
|
-
_FgGray =
|
|
44
|
+
_FgBlack = '\x1b[30m';
|
|
45
|
+
_FgRed = '\x1b[31m';
|
|
46
|
+
_FgGreen = '\x1b[32m';
|
|
47
|
+
_FgYellow = '\x1b[33m';
|
|
48
|
+
_FgBlue = '\x1b[34m';
|
|
49
|
+
_FgMagenta = '\x1b[35m';
|
|
50
|
+
_FgCyan = '\x1b[36m';
|
|
51
|
+
_FgWhite = '\x1b[37m';
|
|
52
|
+
_FgGray = '\x1b[90m';
|
|
61
53
|
|
|
62
54
|
constructor(uiDebug = false) {
|
|
63
55
|
this._debug = uiDebug;
|
|
@@ -87,6 +79,7 @@ class Logger {
|
|
|
87
79
|
*/
|
|
88
80
|
class UiServer extends HomebridgePluginUiServer {
|
|
89
81
|
cloud;
|
|
82
|
+
smartHomeCloud;
|
|
90
83
|
promiseSocket;
|
|
91
84
|
security;
|
|
92
85
|
logger;
|
|
@@ -95,113 +88,79 @@ class UiServer extends HomebridgePluginUiServer {
|
|
|
95
88
|
constructor() {
|
|
96
89
|
super();
|
|
97
90
|
// Obtain the plugin configuration from homebridge config JSON file.
|
|
98
|
-
const config = require(this.homebridgeConfigPath).platforms.find(
|
|
99
|
-
(obj) => obj.platform === "midea-platform",
|
|
100
|
-
);
|
|
91
|
+
const config = require(this.homebridgeConfigPath).platforms.find((obj) => obj.platform === 'midea-platform');
|
|
101
92
|
this.logger = new Logger(config?.uiDebug ?? false);
|
|
102
|
-
this.logger.info(
|
|
93
|
+
this.logger.info('Custom UI created.');
|
|
103
94
|
this.logger.debug(`ENV:\n${JSON.stringify(process.env, null, 2)}`);
|
|
104
95
|
this.security = new LocalSecurity();
|
|
105
|
-
this.promiseSocket = new PromiseSocket(
|
|
106
|
-
this.logger,
|
|
107
|
-
config?.uiDebug ?? false,
|
|
108
|
-
);
|
|
96
|
+
this.promiseSocket = new PromiseSocket(this.logger, config?.uiDebug ?? false);
|
|
109
97
|
|
|
110
|
-
this.onRequest(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
password = Buffer.from(
|
|
121
|
-
(DEFAULT_ACCOUNT[0] ^ DEFAULT_ACCOUNT[2]).toString(16),
|
|
122
|
-
"hex",
|
|
123
|
-
).toString("ascii");
|
|
124
|
-
}
|
|
125
|
-
this.cloud = CloudFactory.createCloud(
|
|
126
|
-
username,
|
|
127
|
-
password,
|
|
128
|
-
"NetHome Plus",
|
|
129
|
-
);
|
|
130
|
-
if (username && password) {
|
|
131
|
-
await this.cloud.login();
|
|
132
|
-
}
|
|
133
|
-
} catch (e) {
|
|
134
|
-
const msg = e instanceof Error ? e.stack : e;
|
|
135
|
-
this.logger.warn(`Login failed:\n${msg}`);
|
|
136
|
-
throw new RequestError(
|
|
137
|
-
"Login failed! Check the logs for more information.",
|
|
138
|
-
);
|
|
98
|
+
this.onRequest('/login', async ({ username, password, useDefaultProfile }) => {
|
|
99
|
+
try {
|
|
100
|
+
if (useDefaultProfile) {
|
|
101
|
+
this.logger.debug('Using default profile.');
|
|
102
|
+
username = Buffer.from((DEFAULT_ACCOUNT[0] ^ DEFAULT_ACCOUNT[1]).toString(16), 'hex').toString('ascii');
|
|
103
|
+
password = Buffer.from((DEFAULT_ACCOUNT[0] ^ DEFAULT_ACCOUNT[2]).toString(16), 'hex').toString('ascii');
|
|
104
|
+
}
|
|
105
|
+
this.cloud = CloudFactory.createCloud(username, password, 'NetHome Plus');
|
|
106
|
+
if (username && password) {
|
|
107
|
+
await this.cloud.login();
|
|
139
108
|
}
|
|
140
|
-
}
|
|
141
|
-
|
|
109
|
+
} catch (e) {
|
|
110
|
+
const msg = e instanceof Error ? e.stack : e;
|
|
111
|
+
this.logger.warn(`Login failed:\n${msg}`);
|
|
112
|
+
throw new RequestError('Login failed! Check the logs for more information.');
|
|
113
|
+
}
|
|
114
|
+
});
|
|
142
115
|
|
|
143
|
-
this.onRequest(
|
|
116
|
+
this.onRequest('/mergeToDefault', async ({ config }) => {
|
|
144
117
|
_.defaultsDeep(config, defaultConfig);
|
|
145
|
-
config.devices
|
|
118
|
+
for (const device of config.devices) {
|
|
146
119
|
_.defaultsDeep(device, defaultDeviceConfig);
|
|
147
|
-
}
|
|
120
|
+
}
|
|
148
121
|
this.config = config;
|
|
149
122
|
this.logger.setDebugEnabled(config.uiDebug ? config.uiDebug : false);
|
|
150
123
|
this.logger.debug(`Merged config:\n${JSON.stringify(config, null, 2)}`);
|
|
151
124
|
return config;
|
|
152
125
|
});
|
|
153
126
|
|
|
154
|
-
this.onRequest(
|
|
127
|
+
this.onRequest('/getDefaults', async () => {
|
|
155
128
|
return {
|
|
156
129
|
defaultConfig,
|
|
157
130
|
defaultDeviceConfig,
|
|
158
131
|
};
|
|
159
132
|
});
|
|
160
133
|
|
|
161
|
-
this.onRequest(
|
|
134
|
+
this.onRequest('/discover', async ({ ip }) => {
|
|
162
135
|
try {
|
|
163
136
|
const devices = await this.blockingDiscover(ip);
|
|
164
137
|
for (const device of devices) {
|
|
165
138
|
if (device.version === ProtocolVersion.V3 && this.cloud.loggedIn) {
|
|
166
139
|
await this.getNewCredentials(device);
|
|
167
140
|
} else {
|
|
168
|
-
device.token =
|
|
169
|
-
device.key =
|
|
141
|
+
device.token = '';
|
|
142
|
+
device.key = '';
|
|
170
143
|
}
|
|
171
144
|
}
|
|
172
145
|
this.logger.debug(`All devices:\n${JSON.stringify(devices, null, 2)}`);
|
|
173
|
-
return devices
|
|
174
|
-
.filter((a) => Object.keys(a).length > 0)
|
|
175
|
-
.sort((a, b) => a.ip.localeCompare(b.ip));
|
|
146
|
+
return devices.filter((a) => Object.keys(a).length > 0).sort((a, b) => a.ip.localeCompare(b.ip));
|
|
176
147
|
} catch (e) {
|
|
177
148
|
const msg = e instanceof Error ? e.stack : e;
|
|
178
149
|
throw new RequestError(`Device discovery failed:\n${msg}`);
|
|
179
150
|
}
|
|
180
151
|
});
|
|
181
152
|
|
|
182
|
-
this.onRequest(
|
|
153
|
+
this.onRequest('/downloadLua', async ({ deviceType, deviceSn }) => {
|
|
183
154
|
try {
|
|
184
|
-
if (!this.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
});
|
|
189
|
-
const username = Buffer.from(
|
|
190
|
-
(DEFAULT_ACCOUNT[0] ^ DEFAULT_ACCOUNT[1]).toString(16),
|
|
191
|
-
"hex",
|
|
192
|
-
).toString("ascii");
|
|
193
|
-
const password = Buffer.from(
|
|
194
|
-
(DEFAULT_ACCOUNT[0] ^ DEFAULT_ACCOUNT[2]).toString(16),
|
|
195
|
-
"hex",
|
|
196
|
-
).toString("ascii");
|
|
197
|
-
this.cloud = CloudFactory.createCloud(
|
|
198
|
-
username,
|
|
199
|
-
password,
|
|
200
|
-
"NetHome Plus",
|
|
201
|
-
);
|
|
202
|
-
await this.cloud.login();
|
|
155
|
+
if (!this.smartHomeCloud) {
|
|
156
|
+
const username = Buffer.from((DEFAULT_SMARTHOME_ACCOUNT[0] ^ DEFAULT_SMARTHOME_ACCOUNT[1]).toString(16), 'hex').toString('ascii');
|
|
157
|
+
const password = Buffer.from((DEFAULT_SMARTHOME_ACCOUNT[0] ^ DEFAULT_SMARTHOME_ACCOUNT[2]).toString(16), 'hex').toString('ascii');
|
|
158
|
+
this.smartHomeCloud = CloudFactory.createCloud(username, password, 'Midea SmartHome (MSmartHome)');
|
|
203
159
|
}
|
|
204
|
-
|
|
160
|
+
if (!this.smartHomeCloud.loggedIn) {
|
|
161
|
+
await this.smartHomeCloud.login();
|
|
162
|
+
}
|
|
163
|
+
const lua = await this.smartHomeCloud.getProtocolLua(deviceType, deviceSn);
|
|
205
164
|
return lua;
|
|
206
165
|
} catch (e) {
|
|
207
166
|
const msg = e instanceof Error ? e.stack : e;
|
|
@@ -229,24 +188,20 @@ class UiServer extends HomebridgePluginUiServer {
|
|
|
229
188
|
const endianess = i === 0 ? Endianness.Little : Endianness.Big;
|
|
230
189
|
try {
|
|
231
190
|
const [token, key] = await this.cloud.getTokenKey(device.id, endianess);
|
|
232
|
-
device.token = token ? token.toString(
|
|
233
|
-
device.key = key ? key.toString(
|
|
191
|
+
device.token = token ? token.toString('hex') : undefined;
|
|
192
|
+
device.key = key ? key.toString('hex') : undefined;
|
|
234
193
|
await this.authenticate(device);
|
|
235
194
|
connected = true;
|
|
236
195
|
} catch (e) {
|
|
237
196
|
//const msg = e instanceof Error ? e.stack : e;
|
|
238
|
-
this.logger.debug(
|
|
239
|
-
`[${device.name}] Getting token and key with ${endianess}-endian is not successful.\n${e}`,
|
|
240
|
-
);
|
|
197
|
+
this.logger.debug(`[${device.name}] Getting token and key with ${endianess}-endian is not successful.\n${e}`);
|
|
241
198
|
// if failed then reset token/key
|
|
242
199
|
device.token = undefined;
|
|
243
200
|
device.key = undefined;
|
|
244
201
|
}
|
|
245
202
|
i++;
|
|
246
203
|
}
|
|
247
|
-
this.logger.debug(
|
|
248
|
-
`[${device.name}] Token: ${device.token}, Key: ${device.key}`,
|
|
249
|
-
);
|
|
204
|
+
this.logger.debug(`[${device.name}] Token: ${device.token}, Key: ${device.key}`);
|
|
250
205
|
return;
|
|
251
206
|
}
|
|
252
207
|
|
|
@@ -262,10 +217,7 @@ class UiServer extends HomebridgePluginUiServer {
|
|
|
262
217
|
// Wrap next block in try/finally so we can destroy the socket if error occurs
|
|
263
218
|
// let thrown errors cascade up.
|
|
264
219
|
try {
|
|
265
|
-
const request = this.security.encode_8370(
|
|
266
|
-
Buffer.from(device.token, "hex"),
|
|
267
|
-
TCPMessageType.HANDSHAKE_REQUEST,
|
|
268
|
-
);
|
|
220
|
+
const request = this.security.encode_8370(Buffer.from(device.token, 'hex'), TCPMessageType.HANDSHAKE_REQUEST);
|
|
269
221
|
await this.promiseSocket.write(request);
|
|
270
222
|
const response = await this.promiseSocket.read();
|
|
271
223
|
if (response) {
|
|
@@ -275,16 +227,12 @@ class UiServer extends HomebridgePluginUiServer {
|
|
|
275
227
|
response.length
|
|
276
228
|
})\n${JSON.stringify(response)}`,
|
|
277
229
|
);
|
|
278
|
-
throw Error(
|
|
279
|
-
`[${device.name}] Authenticate error when receiving data from ${device.ip}:${device.port}. (Data length mismatch)`,
|
|
280
|
-
);
|
|
230
|
+
throw Error(`[${device.name}] Authenticate error when receiving data from ${device.ip}:${device.port}. (Data length mismatch)`);
|
|
281
231
|
}
|
|
282
232
|
const resp = response.subarray(8, 72);
|
|
283
|
-
this.security.tcp_key_from_resp(resp, Buffer.from(device.key,
|
|
233
|
+
this.security.tcp_key_from_resp(resp, Buffer.from(device.key, 'hex'));
|
|
284
234
|
} else {
|
|
285
|
-
throw Error(
|
|
286
|
-
`[${device.name}] Authenticate error when receiving data from ${device.ip}:${device.port}.`,
|
|
287
|
-
);
|
|
235
|
+
throw Error(`[${device.name}] Authenticate error when receiving data from ${device.ip}:${device.port}.`);
|
|
288
236
|
}
|
|
289
237
|
} finally {
|
|
290
238
|
this.promiseSocket.destroy();
|
|
@@ -297,44 +245,44 @@ class UiServer extends HomebridgePluginUiServer {
|
|
|
297
245
|
* for each as discovered.
|
|
298
246
|
*/
|
|
299
247
|
async blockingDiscover(ipAddrs = undefined) {
|
|
300
|
-
|
|
301
|
-
this.logger.debug(
|
|
302
|
-
`[blockingDiscover] IP addresses: ${JSON.stringify(ipAddrs)}`,
|
|
303
|
-
);
|
|
248
|
+
const devices = [];
|
|
249
|
+
this.logger.debug(`[blockingDiscover] IP addresses: ${JSON.stringify(ipAddrs)}`);
|
|
304
250
|
const discover = new Discover(this.logger);
|
|
305
251
|
return new Promise((resolve, reject) => {
|
|
306
|
-
this.logger.info(
|
|
307
|
-
this.pushEvent(
|
|
252
|
+
this.logger.info('Start device discovery...');
|
|
253
|
+
this.pushEvent('showToast', {
|
|
308
254
|
success: true,
|
|
309
|
-
msg:
|
|
255
|
+
msg: 'Start device discovery',
|
|
310
256
|
});
|
|
311
257
|
// If IP addresses provided then probe them directly
|
|
312
|
-
ipAddrs
|
|
313
|
-
|
|
314
|
-
|
|
258
|
+
if (ipAddrs && ipAddrs.length > 0) {
|
|
259
|
+
for (const ip of ipAddrs) {
|
|
260
|
+
discover.discoverDeviceByIP(ip);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
315
263
|
// And then send broadcast to network(s)
|
|
316
264
|
discover.startDiscover();
|
|
317
265
|
|
|
318
|
-
discover.on(
|
|
319
|
-
device.displayName = DeviceTypeToName[device.type] ??
|
|
266
|
+
discover.on('device', async (device) => {
|
|
267
|
+
device.displayName = DeviceTypeToName[device.type] ?? 'Unknown';
|
|
320
268
|
devices.push(device);
|
|
321
269
|
// too verbose to post every device as found...
|
|
322
270
|
// this.pushEvent('showToast', { success: true, msg: `Discovered ${device.name} at ${device.ip}`, device: device });
|
|
323
271
|
});
|
|
324
272
|
|
|
325
|
-
discover.on(
|
|
326
|
-
this.logger.info(
|
|
327
|
-
this.pushEvent(
|
|
273
|
+
discover.on('retry', (nTry, nDevices) => {
|
|
274
|
+
this.logger.info('Device discovery complete.');
|
|
275
|
+
this.pushEvent('showToast', {
|
|
328
276
|
success: true,
|
|
329
277
|
msg: `Continuing to search for devices (${nDevices} found)`,
|
|
330
278
|
});
|
|
331
279
|
});
|
|
332
280
|
|
|
333
|
-
discover.on(
|
|
334
|
-
this.logger.info(
|
|
335
|
-
this.pushEvent(
|
|
281
|
+
discover.on('complete', () => {
|
|
282
|
+
this.logger.info('Device discovery complete.');
|
|
283
|
+
this.pushEvent('showToast', {
|
|
336
284
|
success: true,
|
|
337
|
-
msg:
|
|
285
|
+
msg: 'Discovery complete',
|
|
338
286
|
});
|
|
339
287
|
resolve(devices);
|
|
340
288
|
});
|
package/package.json
CHANGED