homebridge-midea-platform 1.1.1-beta.1 → 1.1.2-beta.1

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 (81) hide show
  1. package/.husky/pre-commit +0 -0
  2. package/dist/accessory/AccessoryFactory.d.ts +11 -11
  3. package/dist/accessory/AccessoryFactory.js +28 -28
  4. package/dist/accessory/AirConditionerAccessory.d.ts +86 -86
  5. package/dist/accessory/AirConditionerAccessory.js +560 -560
  6. package/dist/accessory/BaseAccessory.d.ts +11 -11
  7. package/dist/accessory/BaseAccessory.js +21 -21
  8. package/dist/accessory/DehumidifierAccessory.d.ts +45 -45
  9. package/dist/accessory/DehumidifierAccessory.js +343 -343
  10. package/dist/accessory/ElectricWaterHeaterAccessory.d.ts +44 -44
  11. package/dist/accessory/ElectricWaterHeaterAccessory.js +176 -176
  12. package/dist/accessory/FanAccessory.d.ts +39 -39
  13. package/dist/accessory/FanAccessory.js +123 -123
  14. package/dist/accessory/FrontLoadWasherAccessory.d.ts +31 -0
  15. package/dist/accessory/FrontLoadWasherAccessory.d.ts.map +1 -0
  16. package/dist/accessory/FrontLoadWasherAccessory.js +61 -0
  17. package/dist/accessory/FrontLoadWasherAccessory.js.map +1 -0
  18. package/dist/accessory/GasWaterHeaterAccessory.d.ts +51 -51
  19. package/dist/accessory/GasWaterHeaterAccessory.js +216 -216
  20. package/dist/core/MideaCloud.d.ts +35 -35
  21. package/dist/core/MideaCloud.js +350 -350
  22. package/dist/core/MideaConstants.d.ts +49 -48
  23. package/dist/core/MideaConstants.d.ts.map +1 -1
  24. package/dist/core/MideaConstants.js +57 -56
  25. package/dist/core/MideaConstants.js.map +1 -1
  26. package/dist/core/MideaDevice.d.ts +76 -76
  27. package/dist/core/MideaDevice.d.ts.map +1 -1
  28. package/dist/core/MideaDevice.js +411 -411
  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.d.ts.map +1 -1
  33. package/dist/core/MideaMessage.js +184 -191
  34. package/dist/core/MideaMessage.js.map +1 -1
  35. package/dist/core/MideaPacketBuilder.d.ts +10 -10
  36. package/dist/core/MideaPacketBuilder.js +60 -60
  37. package/dist/core/MideaSecurity.d.ts +63 -63
  38. package/dist/core/MideaSecurity.js +242 -242
  39. package/dist/core/MideaUtils.d.ts +32 -32
  40. package/dist/core/MideaUtils.js +181 -181
  41. package/dist/devices/DeviceFactory.d.ts +11 -11
  42. package/dist/devices/DeviceFactory.js +30 -30
  43. package/dist/devices/a1/MideaA1Device.d.ts +76 -76
  44. package/dist/devices/a1/MideaA1Device.js +136 -136
  45. package/dist/devices/a1/MideaA1Message.d.ts +40 -40
  46. package/dist/devices/a1/MideaA1Message.js +198 -198
  47. package/dist/devices/ac/MideaACDevice.d.ts +100 -100
  48. package/dist/devices/ac/MideaACDevice.js +370 -370
  49. package/dist/devices/ac/MideaACMessage.d.ts +92 -92
  50. package/dist/devices/ac/MideaACMessage.d.ts.map +1 -1
  51. package/dist/devices/ac/MideaACMessage.js +589 -591
  52. package/dist/devices/ac/MideaACMessage.js.map +1 -1
  53. package/dist/devices/db/MideaDBDevice.d.ts +30 -0
  54. package/dist/devices/db/MideaDBDevice.d.ts.map +1 -0
  55. package/dist/devices/db/MideaDBDevice.js +89 -0
  56. package/dist/devices/db/MideaDBDevice.js.map +1 -0
  57. package/dist/devices/db/MideaDBMessage.d.ts +33 -0
  58. package/dist/devices/db/MideaDBMessage.d.ts.map +1 -0
  59. package/dist/devices/db/MideaDBMessage.js +102 -0
  60. package/dist/devices/db/MideaDBMessage.js.map +1 -0
  61. package/dist/devices/e2/MideaE2Device.d.ts +44 -44
  62. package/dist/devices/e2/MideaE2Device.js +119 -119
  63. package/dist/devices/e2/MideaE2Message.d.ts +33 -33
  64. package/dist/devices/e2/MideaE2Message.js +132 -132
  65. package/dist/devices/e3/MideaE3Device.d.ts +43 -43
  66. package/dist/devices/e3/MideaE3Device.js +125 -125
  67. package/dist/devices/e3/MideaE3Message.d.ts +51 -51
  68. package/dist/devices/e3/MideaE3Message.js +136 -136
  69. package/dist/devices/fa/MideaFADevice.d.ts +36 -36
  70. package/dist/devices/fa/MideaFADevice.js +92 -92
  71. package/dist/devices/fa/MideaFAMessage.d.ts +38 -38
  72. package/dist/devices/fa/MideaFAMessage.js +98 -98
  73. package/dist/index.d.ts +6 -6
  74. package/dist/index.js +6 -6
  75. package/dist/platform.d.ts +60 -60
  76. package/dist/platform.js +213 -213
  77. package/dist/platformUtils.d.ts +97 -97
  78. package/dist/platformUtils.js +95 -95
  79. package/dist/settings.d.ts +8 -8
  80. package/dist/settings.js +11 -11
  81. package/package.json +9 -4
@@ -1,64 +1,64 @@
1
- /// <reference types="node" />
2
- import { TCPMessageType } from './MideaConstants';
3
- export type KeyToken = Buffer | undefined;
4
- export declare abstract class CloudSecurity {
5
- protected readonly LOGIN_KEY: string;
6
- readonly IOT_KEY?: string;
7
- readonly HMAC_KEY?: string;
8
- constructor(login_key: string, iot_key?: bigint, hmac_key?: bigint);
9
- sign(data: string, random: string): string;
10
- encrpytPassword(loginId: string, password: string): string;
11
- static getUDPID(device_id_buf: Uint8Array): string;
12
- }
13
- export declare abstract class ProxiedSecurity extends CloudSecurity {
14
- abstract readonly APP_KEY: string;
15
- abstract encrpytIAMPassword(loginId: string, password: string): string;
16
- getAppKeyAndIv(): {
17
- appKey: Buffer;
18
- iv: Buffer;
19
- };
20
- encryptAESAppKey(data: Buffer): Buffer;
21
- decryptAESAppKey(data: Buffer): Buffer;
22
- }
23
- export declare class MSmartHomeCloudSecurity extends ProxiedSecurity {
24
- static readonly _LOGIN_KEY = "ac21b9f9cbfe4ca5a88562ef25e2b768";
25
- readonly APP_KEY = "ac21b9f9cbfe4ca5a88562ef25e2b768";
26
- constructor();
27
- encrpytIAMPassword(loginId: string, password: string): string;
28
- }
29
- export declare class MeijuCloudSecurity extends ProxiedSecurity {
30
- static readonly _LOGIN_KEY = "ad0ee21d48a64bf49f4fb583ab76e799";
31
- readonly APP_KEY = "ac21b9f9cbfe4ca5a88562ef25e2b768";
32
- constructor();
33
- encrpytIAMPassword(_loginId: string, password: string): string;
34
- }
35
- export declare class SimpleSecurity extends CloudSecurity {
36
- constructor(login_key: string);
37
- encrpytIAMPassword(): string;
38
- sign(url: string, query: string): string;
39
- }
40
- export declare class NetHomePlusSecurity extends SimpleSecurity {
41
- static readonly _LOGIN_KEY = "3742e9e5842d4ad59c2db887e12449f9";
42
- constructor();
43
- }
44
- export declare class ArtisonClimaSecurity extends SimpleSecurity {
45
- static readonly _LOGIN_KEY = "434a209a5ce141c3b726de067835d7f0";
46
- constructor();
47
- }
48
- export declare class LocalSecurity {
49
- private readonly aes_key;
50
- private readonly salt;
51
- private readonly iv;
52
- private request_count;
53
- private response_count;
54
- private tcp_key?;
55
- private aes_cbc_encrpyt;
56
- private aes_cbc_decrypt;
57
- aes_encrypt(data: Buffer): Buffer;
58
- aes_decrypt(data: Buffer): Buffer;
59
- encode32_data(raw: Buffer): Buffer;
60
- tcp_key_from_resp(response: Buffer, key: Buffer): Buffer;
61
- encode_8370(data: Buffer, message_type: TCPMessageType): Buffer;
62
- decode_8370(data: Buffer): any;
63
- }
1
+ /// <reference types="node" />
2
+ import { TCPMessageType } from './MideaConstants';
3
+ export type KeyToken = Buffer | undefined;
4
+ export declare abstract class CloudSecurity {
5
+ protected readonly LOGIN_KEY: string;
6
+ readonly IOT_KEY?: string;
7
+ readonly HMAC_KEY?: string;
8
+ constructor(login_key: string, iot_key?: bigint, hmac_key?: bigint);
9
+ sign(data: string, random: string): string;
10
+ encrpytPassword(loginId: string, password: string): string;
11
+ static getUDPID(device_id_buf: Uint8Array): string;
12
+ }
13
+ export declare abstract class ProxiedSecurity extends CloudSecurity {
14
+ abstract readonly APP_KEY: string;
15
+ abstract encrpytIAMPassword(loginId: string, password: string): string;
16
+ getAppKeyAndIv(): {
17
+ appKey: Buffer;
18
+ iv: Buffer;
19
+ };
20
+ encryptAESAppKey(data: Buffer): Buffer;
21
+ decryptAESAppKey(data: Buffer): Buffer;
22
+ }
23
+ export declare class MSmartHomeCloudSecurity extends ProxiedSecurity {
24
+ static readonly _LOGIN_KEY = "ac21b9f9cbfe4ca5a88562ef25e2b768";
25
+ readonly APP_KEY = "ac21b9f9cbfe4ca5a88562ef25e2b768";
26
+ constructor();
27
+ encrpytIAMPassword(loginId: string, password: string): string;
28
+ }
29
+ export declare class MeijuCloudSecurity extends ProxiedSecurity {
30
+ static readonly _LOGIN_KEY = "ad0ee21d48a64bf49f4fb583ab76e799";
31
+ readonly APP_KEY = "ac21b9f9cbfe4ca5a88562ef25e2b768";
32
+ constructor();
33
+ encrpytIAMPassword(_loginId: string, password: string): string;
34
+ }
35
+ export declare class SimpleSecurity extends CloudSecurity {
36
+ constructor(login_key: string);
37
+ encrpytIAMPassword(): string;
38
+ sign(url: string, query: string): string;
39
+ }
40
+ export declare class NetHomePlusSecurity extends SimpleSecurity {
41
+ static readonly _LOGIN_KEY = "3742e9e5842d4ad59c2db887e12449f9";
42
+ constructor();
43
+ }
44
+ export declare class ArtisonClimaSecurity extends SimpleSecurity {
45
+ static readonly _LOGIN_KEY = "434a209a5ce141c3b726de067835d7f0";
46
+ constructor();
47
+ }
48
+ export declare class LocalSecurity {
49
+ private readonly aes_key;
50
+ private readonly salt;
51
+ private readonly iv;
52
+ private request_count;
53
+ private response_count;
54
+ private tcp_key?;
55
+ private aes_cbc_encrpyt;
56
+ private aes_cbc_decrypt;
57
+ aes_encrypt(data: Buffer): Buffer;
58
+ aes_decrypt(data: Buffer): Buffer;
59
+ encode32_data(raw: Buffer): Buffer;
60
+ tcp_key_from_resp(response: Buffer, key: Buffer): Buffer;
61
+ encode_8370(data: Buffer, message_type: TCPMessageType): Buffer;
62
+ decode_8370(data: Buffer): any;
63
+ }
64
64
  //# sourceMappingURL=MideaSecurity.d.ts.map
@@ -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