homebridge-midea-platform 1.1.2-beta.2 → 1.1.2-beta.4

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.
Files changed (76) hide show
  1. package/.husky/pre-commit +0 -0
  2. package/CHANGELOG.md +6 -1
  3. package/config.schema.json +20 -56
  4. package/dist/accessory/AccessoryFactory.d.ts +12 -12
  5. package/dist/accessory/AccessoryFactory.js +31 -31
  6. package/dist/accessory/AirConditionerAccessory.d.ts +86 -86
  7. package/dist/accessory/AirConditionerAccessory.js +560 -560
  8. package/dist/accessory/AirConditionerAccessory.js.map +1 -1
  9. package/dist/accessory/BaseAccessory.d.ts +11 -11
  10. package/dist/accessory/BaseAccessory.js +21 -21
  11. package/dist/accessory/DehumidifierAccessory.d.ts +45 -45
  12. package/dist/accessory/DehumidifierAccessory.d.ts.map +1 -1
  13. package/dist/accessory/DehumidifierAccessory.js +345 -343
  14. package/dist/accessory/DehumidifierAccessory.js.map +1 -1
  15. package/dist/accessory/ElectricWaterHeaterAccessory.d.ts +44 -44
  16. package/dist/accessory/ElectricWaterHeaterAccessory.js +176 -176
  17. package/dist/accessory/FanAccessory.d.ts +39 -39
  18. package/dist/accessory/FanAccessory.js +123 -123
  19. package/dist/accessory/FrontLoadWasherAccessory.d.ts +30 -30
  20. package/dist/accessory/FrontLoadWasherAccessory.js +60 -60
  21. package/dist/accessory/GasWaterHeaterAccessory.d.ts +51 -51
  22. package/dist/accessory/GasWaterHeaterAccessory.js +216 -216
  23. package/dist/core/MideaCloud.d.ts +35 -35
  24. package/dist/core/MideaCloud.js +350 -350
  25. package/dist/core/MideaConstants.d.ts +49 -49
  26. package/dist/core/MideaConstants.js +57 -57
  27. package/dist/core/MideaDevice.d.ts +76 -76
  28. package/dist/core/MideaDevice.js +409 -409
  29. package/dist/core/MideaDiscover.d.ts +35 -35
  30. package/dist/core/MideaDiscover.js +212 -212
  31. package/dist/core/MideaMessage.d.ts +75 -75
  32. package/dist/core/MideaMessage.js +184 -184
  33. package/dist/core/MideaPacketBuilder.d.ts +10 -10
  34. package/dist/core/MideaPacketBuilder.js +60 -60
  35. package/dist/core/MideaSecurity.d.ts +63 -63
  36. package/dist/core/MideaSecurity.js +242 -242
  37. package/dist/core/MideaUtils.d.ts +32 -32
  38. package/dist/core/MideaUtils.js +181 -181
  39. package/dist/devices/DeviceFactory.d.ts +12 -12
  40. package/dist/devices/DeviceFactory.js +33 -33
  41. package/dist/devices/a1/MideaA1Device.d.ts +76 -76
  42. package/dist/devices/a1/MideaA1Device.js +136 -136
  43. package/dist/devices/a1/MideaA1Message.d.ts +40 -40
  44. package/dist/devices/a1/MideaA1Message.js +198 -198
  45. package/dist/devices/ac/MideaACDevice.d.ts +100 -100
  46. package/dist/devices/ac/MideaACDevice.js +370 -370
  47. package/dist/devices/ac/MideaACMessage.d.ts +92 -92
  48. package/dist/devices/ac/MideaACMessage.js +589 -589
  49. package/dist/devices/db/MideaDBDevice.d.ts +29 -29
  50. package/dist/devices/db/MideaDBDevice.js +88 -88
  51. package/dist/devices/db/MideaDBMessage.d.ts +32 -32
  52. package/dist/devices/db/MideaDBMessage.js +101 -101
  53. package/dist/devices/e2/MideaE2Device.d.ts +44 -44
  54. package/dist/devices/e2/MideaE2Device.js +119 -119
  55. package/dist/devices/e2/MideaE2Message.d.ts +33 -33
  56. package/dist/devices/e2/MideaE2Message.js +132 -132
  57. package/dist/devices/e3/MideaE3Device.d.ts +43 -43
  58. package/dist/devices/e3/MideaE3Device.js +125 -125
  59. package/dist/devices/e3/MideaE3Message.d.ts +51 -51
  60. package/dist/devices/e3/MideaE3Message.js +136 -136
  61. package/dist/devices/fa/MideaFADevice.d.ts +36 -36
  62. package/dist/devices/fa/MideaFADevice.js +92 -92
  63. package/dist/devices/fa/MideaFAMessage.d.ts +38 -38
  64. package/dist/devices/fa/MideaFAMessage.js +98 -98
  65. package/dist/index.d.ts +6 -6
  66. package/dist/index.js +6 -6
  67. package/dist/platform.d.ts +60 -60
  68. package/dist/platform.js +213 -213
  69. package/dist/platformUtils.d.ts +98 -97
  70. package/dist/platformUtils.d.ts.map +1 -1
  71. package/dist/platformUtils.js +96 -95
  72. package/dist/platformUtils.js.map +1 -1
  73. package/dist/settings.d.ts +8 -8
  74. package/dist/settings.js +11 -11
  75. package/docs/db.md +3 -0
  76. package/package.json +1 -1
@@ -1,243 +1,243 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LocalSecurity = exports.ArtisonClimaSecurity = exports.NetHomePlusSecurity = exports.SimpleSecurity = exports.MeijuCloudSecurity = exports.MSmartHomeCloudSecurity = exports.ProxiedSecurity = exports.CloudSecurity = void 0;
4
- const crypto_1 = require("crypto");
5
- const MideaConstants_1 = require("./MideaConstants");
6
- const MideaUtils_1 = require("./MideaUtils");
7
- const querystring_1 = require("querystring");
8
- function unescape_plus(str) {
9
- return (0, querystring_1.unescape)(str.replace(/\+/g, ' '));
10
- }
11
- class CloudSecurity {
12
- constructor(login_key, iot_key, hmac_key) {
13
- this.LOGIN_KEY = login_key;
14
- if (hmac_key) {
15
- this.HMAC_KEY = Buffer.from(hmac_key.toString(16), 'hex').toString();
16
- }
17
- if (iot_key) {
18
- this.IOT_KEY = Buffer.from(iot_key.toString(16), 'hex').toString();
19
- }
20
- }
21
- // Generate a HMAC signature for the provided data and random data.
22
- sign(data, random) {
23
- const message = `${this.IOT_KEY}${data}${random}`;
24
- return (0, crypto_1.createHmac)('sha256', this.HMAC_KEY).update(message).digest('hex');
25
- }
26
- // Encrypt the password for cloud API password.
27
- encrpytPassword(loginId, password) {
28
- const m1 = (0, crypto_1.createHash)('sha256').update(password);
29
- const login_hash = `${loginId}${m1.digest('hex')}${this.LOGIN_KEY}`;
30
- const m2 = (0, crypto_1.createHash)('sha256').update(login_hash);
31
- return m2.digest('hex');
32
- }
33
- static getUDPID(device_id_buf) {
34
- const data = (0, crypto_1.createHash)('sha256').update(device_id_buf).digest();
35
- const output = Buffer.alloc(16);
36
- for (let i = 0; i < 16; i++) {
37
- output[i] = data[i] ^ data[i + 16];
38
- }
39
- return output.toString('hex');
40
- }
41
- }
42
- exports.CloudSecurity = CloudSecurity;
43
- class ProxiedSecurity extends CloudSecurity {
44
- getAppKeyAndIv() {
45
- const hash = (0, crypto_1.createHash)('sha256').update(Buffer.from(this.APP_KEY, 'utf8')).digest('hex');
46
- return {
47
- appKey: Buffer.from(hash.substring(0, 16)),
48
- iv: Buffer.from(hash.substring(16, 32)),
49
- };
50
- }
51
- encryptAESAppKey(data) {
52
- const { appKey, iv } = this.getAppKeyAndIv();
53
- const cipher = (0, crypto_1.createCipheriv)('aes-128-cbc', appKey, iv);
54
- return Buffer.concat([cipher.update(data), cipher.final()]);
55
- }
56
- decryptAESAppKey(data) {
57
- const { appKey, iv } = this.getAppKeyAndIv();
58
- const decipher = (0, crypto_1.createDecipheriv)('aes-128-cbc', appKey, iv);
59
- return Buffer.concat([decipher.update(data), decipher.final()]);
60
- }
61
- }
62
- exports.ProxiedSecurity = ProxiedSecurity;
63
- class MSmartHomeCloudSecurity extends ProxiedSecurity {
64
- constructor() {
65
- super(MSmartHomeCloudSecurity._LOGIN_KEY, BigInt('7882822598523843940'), BigInt('117390035944627627450677220413733956185864939010425'));
66
- this.APP_KEY = 'ac21b9f9cbfe4ca5a88562ef25e2b768';
67
- }
68
- encrpytIAMPassword(loginId, password) {
69
- const m1 = (0, crypto_1.createHash)('md5').update(password);
70
- const m2 = (0, crypto_1.createHash)('md5').update(m1.digest('hex'));
71
- const login_hash = `${loginId}${m2.digest('hex')}${this.LOGIN_KEY}`;
72
- const sha = (0, crypto_1.createHash)('sha256').update(login_hash);
73
- return sha.digest('hex');
74
- }
75
- }
76
- exports.MSmartHomeCloudSecurity = MSmartHomeCloudSecurity;
77
- MSmartHomeCloudSecurity._LOGIN_KEY = 'ac21b9f9cbfe4ca5a88562ef25e2b768';
78
- class MeijuCloudSecurity extends ProxiedSecurity {
79
- constructor() {
80
- super(MeijuCloudSecurity._LOGIN_KEY, BigInt('9795516279659324117647275084689641883661667'), BigInt('117390035944627627450677220413733956185864939010425'));
81
- this.APP_KEY = 'ac21b9f9cbfe4ca5a88562ef25e2b768';
82
- }
83
- encrpytIAMPassword(_loginId, password) {
84
- const m1 = (0, crypto_1.createHash)('md5').update(password);
85
- const m2 = (0, crypto_1.createHash)('md5').update(m1.digest('hex'));
86
- return m2.digest('hex');
87
- }
88
- }
89
- exports.MeijuCloudSecurity = MeijuCloudSecurity;
90
- MeijuCloudSecurity._LOGIN_KEY = 'ad0ee21d48a64bf49f4fb583ab76e799';
91
- class SimpleSecurity extends CloudSecurity {
92
- constructor(login_key) {
93
- super(login_key);
94
- }
95
- encrpytIAMPassword() {
96
- return '';
97
- }
98
- sign(url, query) {
99
- const parsedUrl = new URL(url);
100
- const path = parsedUrl.pathname;
101
- return (0, crypto_1.createHash)('sha256')
102
- .update(Buffer.from(`${path}${unescape_plus(query)}${this.LOGIN_KEY}`, 'ascii'))
103
- .digest('hex');
104
- }
105
- }
106
- exports.SimpleSecurity = SimpleSecurity;
107
- class NetHomePlusSecurity extends SimpleSecurity {
108
- constructor() {
109
- super(NetHomePlusSecurity._LOGIN_KEY);
110
- }
111
- }
112
- exports.NetHomePlusSecurity = NetHomePlusSecurity;
113
- NetHomePlusSecurity._LOGIN_KEY = '3742e9e5842d4ad59c2db887e12449f9';
114
- class ArtisonClimaSecurity extends SimpleSecurity {
115
- constructor() {
116
- super(ArtisonClimaSecurity._LOGIN_KEY);
117
- }
118
- }
119
- exports.ArtisonClimaSecurity = ArtisonClimaSecurity;
120
- ArtisonClimaSecurity._LOGIN_KEY = '434a209a5ce141c3b726de067835d7f0';
121
- class LocalSecurity {
122
- constructor() {
123
- this.aes_key = Buffer.from(BigInt('141661095494369103254425781617665632877').toString(16), 'hex');
124
- this.salt = Buffer.from(BigInt('233912452794221312800602098970898185176935770387238278451789080441632479840061417076563').toString(16), 'hex');
125
- this.iv = Buffer.alloc(16);
126
- this.request_count = 0;
127
- this.response_count = 0;
128
- }
129
- aes_cbc_encrpyt(raw, key) {
130
- const cipher = (0, crypto_1.createCipheriv)('aes-256-cbc', key, this.iv);
131
- cipher.setAutoPadding(false);
132
- return Buffer.concat([cipher.update(raw), cipher.final()]);
133
- }
134
- aes_cbc_decrypt(raw, key) {
135
- const decipher = (0, crypto_1.createDecipheriv)('aes-256-cbc', key, this.iv);
136
- decipher.setAutoPadding(false);
137
- return Buffer.concat([decipher.update(raw), decipher.final()]);
138
- }
139
- aes_encrypt(data) {
140
- const cipher = (0, crypto_1.createCipheriv)('aes-128-ecb', this.aes_key, null);
141
- return Buffer.concat([cipher.update(data), cipher.final()]);
142
- }
143
- aes_decrypt(data) {
144
- const decipher = (0, crypto_1.createDecipheriv)('aes-128-ecb', this.aes_key, null);
145
- return Buffer.concat([decipher.update(data), decipher.final()]);
146
- }
147
- encode32_data(raw) {
148
- return (0, crypto_1.createHash)('md5')
149
- .update(Buffer.concat([raw, this.salt]))
150
- .digest();
151
- }
152
- tcp_key_from_resp(response, key) {
153
- if (response.toString() === 'ERROR') {
154
- throw Error('Authentication response is ERROR, cannot get TCP key.');
155
- }
156
- if (response.length !== 64) {
157
- throw Error('Authentication response has unexpected data length, cannot get TCP key..');
158
- }
159
- const payload = response.subarray(0, 32);
160
- const sign = response.subarray(32, response.length);
161
- const plain = this.aes_cbc_decrypt(payload, key);
162
- if ((0, crypto_1.createHash)('sha256').update(plain).digest().compare(sign) !== 0) {
163
- throw Error('Authentication sign does not match, cannot get TCP key.');
164
- }
165
- this.tcp_key = (0, MideaUtils_1.strxor)(plain, key);
166
- this.request_count = 0;
167
- this.response_count = 0;
168
- return this.tcp_key;
169
- }
170
- encode_8370(data, message_type) {
171
- let header = Buffer.from([0x83, 0x70]);
172
- let size = data.length;
173
- let padding = 0;
174
- if (message_type === MideaConstants_1.TCPMessageType.ENCRYPTED_REQUEST || message_type === MideaConstants_1.TCPMessageType.ENCRYPTED_RESPONSE) {
175
- if ((size + 2) % 16 !== 0) {
176
- padding = 16 - ((size + 2) & 0xf);
177
- size += padding + 32;
178
- data = Buffer.concat([data, (0, crypto_1.randomBytes)(padding)]);
179
- }
180
- }
181
- header = Buffer.concat([header, (0, MideaUtils_1.numberToUint8Array)(size, 2, MideaConstants_1.Endianness.Big)]);
182
- header = Buffer.concat([header, Buffer.from([0x20, (padding << 4) | message_type])]);
183
- data = Buffer.concat([(0, MideaUtils_1.numberToUint8Array)(this.request_count, 2, MideaConstants_1.Endianness.Big), data]);
184
- this.request_count += 1;
185
- if (this.request_count >= 0xffff) {
186
- this.request_count = 0;
187
- }
188
- if (message_type === MideaConstants_1.TCPMessageType.ENCRYPTED_REQUEST || message_type === MideaConstants_1.TCPMessageType.ENCRYPTED_RESPONSE) {
189
- const sign = (0, crypto_1.createHash)('sha256')
190
- .update(Buffer.concat([header, data]))
191
- .digest();
192
- data = Buffer.concat([this.aes_cbc_encrpyt(data, this.tcp_key), sign]);
193
- }
194
- return Buffer.concat([header, data]);
195
- }
196
- decode_8370(data) {
197
- if (data.length < 6) {
198
- return [[], data];
199
- }
200
- const header = data.subarray(0, 6);
201
- if (header[0] !== 0x83 || header[1] !== 0x70) {
202
- throw new Error('Not an 8370 message.');
203
- }
204
- const size = header.subarray(2, 4).readUInt16BE() + 8;
205
- let leftover = Buffer.alloc(0);
206
- if (data.length < size) {
207
- return [[], data];
208
- }
209
- else if (data.length > size) {
210
- leftover = data.subarray(size, data.length);
211
- data = data.subarray(0, size);
212
- }
213
- if (header[4] !== 0x20) {
214
- throw new Error('Missing byte 4');
215
- }
216
- const padding = header[5] >> 4;
217
- const message_type_received = header[5] & 0xf;
218
- data = data.subarray(6, data.length);
219
- if ([MideaConstants_1.TCPMessageType.ENCRYPTED_RESPONSE, MideaConstants_1.TCPMessageType.ENCRYPTED_REQUEST].includes(message_type_received)) {
220
- const sign = data.subarray(data.length - 32, data.length);
221
- data = data.subarray(0, data.length - 32);
222
- data = this.aes_cbc_decrypt(data, this.tcp_key);
223
- if ((0, crypto_1.createHash)('sha256')
224
- .update(Buffer.concat([header, data]))
225
- .digest()
226
- .compare(sign) !== 0) {
227
- throw new Error('Sign does not match');
228
- }
229
- if (padding) {
230
- data = data.subarray(0, data.length - padding);
231
- }
232
- }
233
- this.response_count = data.subarray(0, 2).readUInt16BE();
234
- data = data.subarray(2, data.length);
235
- if (leftover.length > 0) {
236
- const [packets, incomplete] = this.decode_8370(leftover);
237
- return [[data, ...packets], incomplete];
238
- }
239
- return [[data], leftover];
240
- }
241
- }
242
- exports.LocalSecurity = LocalSecurity;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalSecurity = exports.ArtisonClimaSecurity = exports.NetHomePlusSecurity = exports.SimpleSecurity = exports.MeijuCloudSecurity = exports.MSmartHomeCloudSecurity = exports.ProxiedSecurity = exports.CloudSecurity = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const MideaConstants_1 = require("./MideaConstants");
6
+ const MideaUtils_1 = require("./MideaUtils");
7
+ const querystring_1 = require("querystring");
8
+ function unescape_plus(str) {
9
+ return (0, querystring_1.unescape)(str.replace(/\+/g, ' '));
10
+ }
11
+ class CloudSecurity {
12
+ constructor(login_key, iot_key, hmac_key) {
13
+ this.LOGIN_KEY = login_key;
14
+ if (hmac_key) {
15
+ this.HMAC_KEY = Buffer.from(hmac_key.toString(16), 'hex').toString();
16
+ }
17
+ if (iot_key) {
18
+ this.IOT_KEY = Buffer.from(iot_key.toString(16), 'hex').toString();
19
+ }
20
+ }
21
+ // Generate a HMAC signature for the provided data and random data.
22
+ sign(data, random) {
23
+ const message = `${this.IOT_KEY}${data}${random}`;
24
+ return (0, crypto_1.createHmac)('sha256', this.HMAC_KEY).update(message).digest('hex');
25
+ }
26
+ // Encrypt the password for cloud API password.
27
+ encrpytPassword(loginId, password) {
28
+ const m1 = (0, crypto_1.createHash)('sha256').update(password);
29
+ const login_hash = `${loginId}${m1.digest('hex')}${this.LOGIN_KEY}`;
30
+ const m2 = (0, crypto_1.createHash)('sha256').update(login_hash);
31
+ return m2.digest('hex');
32
+ }
33
+ static getUDPID(device_id_buf) {
34
+ const data = (0, crypto_1.createHash)('sha256').update(device_id_buf).digest();
35
+ const output = Buffer.alloc(16);
36
+ for (let i = 0; i < 16; i++) {
37
+ output[i] = data[i] ^ data[i + 16];
38
+ }
39
+ return output.toString('hex');
40
+ }
41
+ }
42
+ exports.CloudSecurity = CloudSecurity;
43
+ class ProxiedSecurity extends CloudSecurity {
44
+ getAppKeyAndIv() {
45
+ const hash = (0, crypto_1.createHash)('sha256').update(Buffer.from(this.APP_KEY, 'utf8')).digest('hex');
46
+ return {
47
+ appKey: Buffer.from(hash.substring(0, 16)),
48
+ iv: Buffer.from(hash.substring(16, 32)),
49
+ };
50
+ }
51
+ encryptAESAppKey(data) {
52
+ const { appKey, iv } = this.getAppKeyAndIv();
53
+ const cipher = (0, crypto_1.createCipheriv)('aes-128-cbc', appKey, iv);
54
+ return Buffer.concat([cipher.update(data), cipher.final()]);
55
+ }
56
+ decryptAESAppKey(data) {
57
+ const { appKey, iv } = this.getAppKeyAndIv();
58
+ const decipher = (0, crypto_1.createDecipheriv)('aes-128-cbc', appKey, iv);
59
+ return Buffer.concat([decipher.update(data), decipher.final()]);
60
+ }
61
+ }
62
+ exports.ProxiedSecurity = ProxiedSecurity;
63
+ class MSmartHomeCloudSecurity extends ProxiedSecurity {
64
+ constructor() {
65
+ super(MSmartHomeCloudSecurity._LOGIN_KEY, BigInt('7882822598523843940'), BigInt('117390035944627627450677220413733956185864939010425'));
66
+ this.APP_KEY = 'ac21b9f9cbfe4ca5a88562ef25e2b768';
67
+ }
68
+ encrpytIAMPassword(loginId, password) {
69
+ const m1 = (0, crypto_1.createHash)('md5').update(password);
70
+ const m2 = (0, crypto_1.createHash)('md5').update(m1.digest('hex'));
71
+ const login_hash = `${loginId}${m2.digest('hex')}${this.LOGIN_KEY}`;
72
+ const sha = (0, crypto_1.createHash)('sha256').update(login_hash);
73
+ return sha.digest('hex');
74
+ }
75
+ }
76
+ exports.MSmartHomeCloudSecurity = MSmartHomeCloudSecurity;
77
+ MSmartHomeCloudSecurity._LOGIN_KEY = 'ac21b9f9cbfe4ca5a88562ef25e2b768';
78
+ class MeijuCloudSecurity extends ProxiedSecurity {
79
+ constructor() {
80
+ super(MeijuCloudSecurity._LOGIN_KEY, BigInt('9795516279659324117647275084689641883661667'), BigInt('117390035944627627450677220413733956185864939010425'));
81
+ this.APP_KEY = 'ac21b9f9cbfe4ca5a88562ef25e2b768';
82
+ }
83
+ encrpytIAMPassword(_loginId, password) {
84
+ const m1 = (0, crypto_1.createHash)('md5').update(password);
85
+ const m2 = (0, crypto_1.createHash)('md5').update(m1.digest('hex'));
86
+ return m2.digest('hex');
87
+ }
88
+ }
89
+ exports.MeijuCloudSecurity = MeijuCloudSecurity;
90
+ MeijuCloudSecurity._LOGIN_KEY = 'ad0ee21d48a64bf49f4fb583ab76e799';
91
+ class SimpleSecurity extends CloudSecurity {
92
+ constructor(login_key) {
93
+ super(login_key);
94
+ }
95
+ encrpytIAMPassword() {
96
+ return '';
97
+ }
98
+ sign(url, query) {
99
+ const parsedUrl = new URL(url);
100
+ const path = parsedUrl.pathname;
101
+ return (0, crypto_1.createHash)('sha256')
102
+ .update(Buffer.from(`${path}${unescape_plus(query)}${this.LOGIN_KEY}`, 'ascii'))
103
+ .digest('hex');
104
+ }
105
+ }
106
+ exports.SimpleSecurity = SimpleSecurity;
107
+ class NetHomePlusSecurity extends SimpleSecurity {
108
+ constructor() {
109
+ super(NetHomePlusSecurity._LOGIN_KEY);
110
+ }
111
+ }
112
+ exports.NetHomePlusSecurity = NetHomePlusSecurity;
113
+ NetHomePlusSecurity._LOGIN_KEY = '3742e9e5842d4ad59c2db887e12449f9';
114
+ class ArtisonClimaSecurity extends SimpleSecurity {
115
+ constructor() {
116
+ super(ArtisonClimaSecurity._LOGIN_KEY);
117
+ }
118
+ }
119
+ exports.ArtisonClimaSecurity = ArtisonClimaSecurity;
120
+ ArtisonClimaSecurity._LOGIN_KEY = '434a209a5ce141c3b726de067835d7f0';
121
+ class LocalSecurity {
122
+ constructor() {
123
+ this.aes_key = Buffer.from(BigInt('141661095494369103254425781617665632877').toString(16), 'hex');
124
+ this.salt = Buffer.from(BigInt('233912452794221312800602098970898185176935770387238278451789080441632479840061417076563').toString(16), 'hex');
125
+ this.iv = Buffer.alloc(16);
126
+ this.request_count = 0;
127
+ this.response_count = 0;
128
+ }
129
+ aes_cbc_encrpyt(raw, key) {
130
+ const cipher = (0, crypto_1.createCipheriv)('aes-256-cbc', key, this.iv);
131
+ cipher.setAutoPadding(false);
132
+ return Buffer.concat([cipher.update(raw), cipher.final()]);
133
+ }
134
+ aes_cbc_decrypt(raw, key) {
135
+ const decipher = (0, crypto_1.createDecipheriv)('aes-256-cbc', key, this.iv);
136
+ decipher.setAutoPadding(false);
137
+ return Buffer.concat([decipher.update(raw), decipher.final()]);
138
+ }
139
+ aes_encrypt(data) {
140
+ const cipher = (0, crypto_1.createCipheriv)('aes-128-ecb', this.aes_key, null);
141
+ return Buffer.concat([cipher.update(data), cipher.final()]);
142
+ }
143
+ aes_decrypt(data) {
144
+ const decipher = (0, crypto_1.createDecipheriv)('aes-128-ecb', this.aes_key, null);
145
+ return Buffer.concat([decipher.update(data), decipher.final()]);
146
+ }
147
+ encode32_data(raw) {
148
+ return (0, crypto_1.createHash)('md5')
149
+ .update(Buffer.concat([raw, this.salt]))
150
+ .digest();
151
+ }
152
+ tcp_key_from_resp(response, key) {
153
+ if (response.toString() === 'ERROR') {
154
+ throw Error('Authentication response is ERROR, cannot get TCP key.');
155
+ }
156
+ if (response.length !== 64) {
157
+ throw Error('Authentication response has unexpected data length, cannot get TCP key..');
158
+ }
159
+ const payload = response.subarray(0, 32);
160
+ const sign = response.subarray(32, response.length);
161
+ const plain = this.aes_cbc_decrypt(payload, key);
162
+ if ((0, crypto_1.createHash)('sha256').update(plain).digest().compare(sign) !== 0) {
163
+ throw Error('Authentication sign does not match, cannot get TCP key.');
164
+ }
165
+ this.tcp_key = (0, MideaUtils_1.strxor)(plain, key);
166
+ this.request_count = 0;
167
+ this.response_count = 0;
168
+ return this.tcp_key;
169
+ }
170
+ encode_8370(data, message_type) {
171
+ let header = Buffer.from([0x83, 0x70]);
172
+ let size = data.length;
173
+ let padding = 0;
174
+ if (message_type === MideaConstants_1.TCPMessageType.ENCRYPTED_REQUEST || message_type === MideaConstants_1.TCPMessageType.ENCRYPTED_RESPONSE) {
175
+ if ((size + 2) % 16 !== 0) {
176
+ padding = 16 - ((size + 2) & 0xf);
177
+ size += padding + 32;
178
+ data = Buffer.concat([data, (0, crypto_1.randomBytes)(padding)]);
179
+ }
180
+ }
181
+ header = Buffer.concat([header, (0, MideaUtils_1.numberToUint8Array)(size, 2, MideaConstants_1.Endianness.Big)]);
182
+ header = Buffer.concat([header, Buffer.from([0x20, (padding << 4) | message_type])]);
183
+ data = Buffer.concat([(0, MideaUtils_1.numberToUint8Array)(this.request_count, 2, MideaConstants_1.Endianness.Big), data]);
184
+ this.request_count += 1;
185
+ if (this.request_count >= 0xffff) {
186
+ this.request_count = 0;
187
+ }
188
+ if (message_type === MideaConstants_1.TCPMessageType.ENCRYPTED_REQUEST || message_type === MideaConstants_1.TCPMessageType.ENCRYPTED_RESPONSE) {
189
+ const sign = (0, crypto_1.createHash)('sha256')
190
+ .update(Buffer.concat([header, data]))
191
+ .digest();
192
+ data = Buffer.concat([this.aes_cbc_encrpyt(data, this.tcp_key), sign]);
193
+ }
194
+ return Buffer.concat([header, data]);
195
+ }
196
+ decode_8370(data) {
197
+ if (data.length < 6) {
198
+ return [[], data];
199
+ }
200
+ const header = data.subarray(0, 6);
201
+ if (header[0] !== 0x83 || header[1] !== 0x70) {
202
+ throw new Error('Not an 8370 message.');
203
+ }
204
+ const size = header.subarray(2, 4).readUInt16BE() + 8;
205
+ let leftover = Buffer.alloc(0);
206
+ if (data.length < size) {
207
+ return [[], data];
208
+ }
209
+ else if (data.length > size) {
210
+ leftover = data.subarray(size, data.length);
211
+ data = data.subarray(0, size);
212
+ }
213
+ if (header[4] !== 0x20) {
214
+ throw new Error('Missing byte 4');
215
+ }
216
+ const padding = header[5] >> 4;
217
+ const message_type_received = header[5] & 0xf;
218
+ data = data.subarray(6, data.length);
219
+ if ([MideaConstants_1.TCPMessageType.ENCRYPTED_RESPONSE, MideaConstants_1.TCPMessageType.ENCRYPTED_REQUEST].includes(message_type_received)) {
220
+ const sign = data.subarray(data.length - 32, data.length);
221
+ data = data.subarray(0, data.length - 32);
222
+ data = this.aes_cbc_decrypt(data, this.tcp_key);
223
+ if ((0, crypto_1.createHash)('sha256')
224
+ .update(Buffer.concat([header, data]))
225
+ .digest()
226
+ .compare(sign) !== 0) {
227
+ throw new Error('Sign does not match');
228
+ }
229
+ if (padding) {
230
+ data = data.subarray(0, data.length - padding);
231
+ }
232
+ }
233
+ this.response_count = data.subarray(0, 2).readUInt16BE();
234
+ data = data.subarray(2, data.length);
235
+ if (leftover.length > 0) {
236
+ const [packets, incomplete] = this.decode_8370(leftover);
237
+ return [[data, ...packets], incomplete];
238
+ }
239
+ return [[data], leftover];
240
+ }
241
+ }
242
+ exports.LocalSecurity = LocalSecurity;
243
243
  //# sourceMappingURL=MideaSecurity.js.map
@@ -1,33 +1,33 @@
1
- /// <reference types="node" />
2
- /***********************************************************************
3
- * Homebridge-midea-platform miscellaneous support functions.
4
- *
5
- * Copyright (c) 2023 Kovalovszky Patrik, https://github.com/kovapatrik
6
- * Portions Copyright (c) 2023 David Kerr, https://github.com/dkerr64
7
- *
8
- * Includes very basic implementation of a promise-wrapped Socket class.
9
- *
10
- */
11
- import { Logger } from 'homebridge';
12
- import { Endianness } from './MideaConstants';
13
- export declare function numberToUint8Array(num: number, byte_length: number, endianness: Endianness): Uint8Array;
14
- export declare function strxor(a: Buffer, b: Buffer): Buffer;
15
- export declare function calculate(data: Buffer): number;
16
- /*********************************************************************
17
- * PromiseSocket
18
- * A very basic implementation of promise-wrapped Socket
19
- *
20
- */
21
- export declare class PromiseSocket {
22
- private readonly logger;
23
- private readonly logerror;
24
- private innerSok;
25
- destroyed: boolean;
26
- constructor(logger: Logger, logerror: boolean);
27
- connect(port: number, host: string): Promise<void>;
28
- setTimeout(t: number): void;
29
- destroy(): void;
30
- write(data: string | Buffer, encoding?: BufferEncoding): Promise<void>;
31
- read(): Promise<Buffer>;
32
- }
1
+ /// <reference types="node" />
2
+ /***********************************************************************
3
+ * Homebridge-midea-platform miscellaneous support functions.
4
+ *
5
+ * Copyright (c) 2023 Kovalovszky Patrik, https://github.com/kovapatrik
6
+ * Portions Copyright (c) 2023 David Kerr, https://github.com/dkerr64
7
+ *
8
+ * Includes very basic implementation of a promise-wrapped Socket class.
9
+ *
10
+ */
11
+ import { Logger } from 'homebridge';
12
+ import { Endianness } from './MideaConstants';
13
+ export declare function numberToUint8Array(num: number, byte_length: number, endianness: Endianness): Uint8Array;
14
+ export declare function strxor(a: Buffer, b: Buffer): Buffer;
15
+ export declare function calculate(data: Buffer): number;
16
+ /*********************************************************************
17
+ * PromiseSocket
18
+ * A very basic implementation of promise-wrapped Socket
19
+ *
20
+ */
21
+ export declare class PromiseSocket {
22
+ private readonly logger;
23
+ private readonly logerror;
24
+ private innerSok;
25
+ destroyed: boolean;
26
+ constructor(logger: Logger, logerror: boolean);
27
+ connect(port: number, host: string): Promise<void>;
28
+ setTimeout(t: number): void;
29
+ destroy(): void;
30
+ write(data: string | Buffer, encoding?: BufferEncoding): Promise<void>;
31
+ read(): Promise<Buffer>;
32
+ }
33
33
  //# sourceMappingURL=MideaUtils.d.ts.map