@pipechela/ewelink-api 1.0.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +22 -0
  3. package/index.d.ts +223 -0
  4. package/main.js +114 -0
  5. package/package.json +67 -0
  6. package/src/classes/ChangeStateZeroconf.js +34 -0
  7. package/src/classes/DevicePowerUsageRaw.js +58 -0
  8. package/src/classes/WebSocket.js +57 -0
  9. package/src/classes/Zeroconf.js +91 -0
  10. package/src/data/constants.js +7 -0
  11. package/src/data/devices-channel-length.json +20 -0
  12. package/src/data/devices-type-uuid.json +55 -0
  13. package/src/data/errors.js +23 -0
  14. package/src/helpers/device-control.js +58 -0
  15. package/src/helpers/ewelink.js +68 -0
  16. package/src/helpers/utilities.js +29 -0
  17. package/src/mixins/checkDeviceUpdate.js +48 -0
  18. package/src/mixins/checkDevicesUpdates.js +54 -0
  19. package/src/mixins/deviceControl.js +272 -0
  20. package/src/mixins/getCredentials.js +54 -0
  21. package/src/mixins/getDevice.js +37 -0
  22. package/src/mixins/getDeviceChannelCount.js +26 -0
  23. package/src/mixins/getDeviceCurrentTH.js +55 -0
  24. package/src/mixins/getDeviceIP.js +15 -0
  25. package/src/mixins/getDevicePowerState.js +47 -0
  26. package/src/mixins/getDevicePowerUsage.js +27 -0
  27. package/src/mixins/getDevicePowerUsageRaw.js +30 -0
  28. package/src/mixins/getDevices.js +37 -0
  29. package/src/mixins/getFirmwareVersion.js +23 -0
  30. package/src/mixins/getRegion.js +23 -0
  31. package/src/mixins/index.js +43 -0
  32. package/src/mixins/makeRequest.js +54 -0
  33. package/src/mixins/openWebSocket.js +44 -0
  34. package/src/mixins/saveDevicesCache.js +29 -0
  35. package/src/mixins/setDevicePowerState.js +90 -0
  36. package/src/mixins/toggleDevice.js +13 -0
  37. package/src/parsers/parseFirmwareUpdates.js +16 -0
  38. package/src/parsers/parsePowerUsage.js +38 -0
  39. package/src/payloads/credentialsPayload.js +13 -0
  40. package/src/payloads/deviceStatus.js +12 -0
  41. package/src/payloads/wssLoginPayload.js +19 -0
  42. package/src/payloads/wssUpdatePayload.js +17 -0
  43. package/src/payloads/zeroConfUpdatePayload.js +17 -0
@@ -0,0 +1,20 @@
1
+ {
2
+ "SOCKET": 1,
3
+ "SWITCH_CHANGE": 1,
4
+ "GSM_UNLIMIT_SOCKET": 1,
5
+ "SWITCH": 1,
6
+ "THERMOSTAT": 1,
7
+ "SOCKET_POWER": 1,
8
+ "GSM_SOCKET": 1,
9
+ "POWER_DETECTION_SOCKET": 1,
10
+ "SOCKET_2": 2,
11
+ "GSM_SOCKET_2": 2,
12
+ "SWITCH_2": 2,
13
+ "SOCKET_3": 3,
14
+ "GSM_SOCKET_3": 3,
15
+ "SWITCH_3": 3,
16
+ "SOCKET_4": 4,
17
+ "GSM_SOCKET_4": 4,
18
+ "SWITCH_4": 4,
19
+ "CUN_YOU_DOOR": 4
20
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "1": "SOCKET",
3
+ "2": "SOCKET_2",
4
+ "3": "SOCKET_3",
5
+ "4": "SOCKET_4",
6
+ "6": "SWITCH",
7
+ "5": "SOCKET_POWER",
8
+ "7": "SWITCH_2",
9
+ "8": "SWITCH_3",
10
+ "9": "SWITCH_4",
11
+ "10": "OSPF",
12
+ "11": "CURTAIN",
13
+ "12": "EW-RE",
14
+ "13": "FIREPLACE",
15
+ "14": "SWITCH_CHANGE",
16
+ "15": "THERMOSTAT",
17
+ "16": "COLD_WARM_LED",
18
+ "17": "THREE_GEAR_FAN",
19
+ "18": "SENSORS_CENTER",
20
+ "19": "HUMIDIFIER",
21
+ "22": "RGB_BALL_LIGHT",
22
+ "23": "NEST_THERMOSTAT",
23
+ "24": "GSM_SOCKET",
24
+ "25": "AROMATHERAPY",
25
+ "26": "BJ_THERMOSTAT",
26
+ "27": "GSM_UNLIMIT_SOCKET",
27
+ "28": "RF_BRIDGE",
28
+ "29": "GSM_SOCKET_2",
29
+ "30": "GSM_SOCKET_3",
30
+ "31": "GSM_SOCKET_4",
31
+ "32": "POWER_DETECTION_SOCKET",
32
+ "33": "LIGHT_BELT",
33
+ "34": "FAN_LIGHT",
34
+ "35": "EZVIZ_CAMERA",
35
+ "36": "SINGLE_CHANNEL_DIMMER_SWITCH",
36
+ "38": "HOME_KIT_BRIDGE",
37
+ "40": "FUJIN_OPS",
38
+ "41": "CUN_YOU_DOOR",
39
+ "42": "SMART_BEDSIDE_AND_NEW_RGB_BALL_LIGHT",
40
+ "43": "",
41
+ "44": "",
42
+ "45": "DOWN_CEILING_LIGHT",
43
+ "46": "AIR_CLEANER",
44
+ "49": "MACHINE_BED",
45
+ "51": "COLD_WARM_DESK_LIGHT",
46
+ "52": "DOUBLE_COLOR_DEMO_LIGHT",
47
+ "53": "ELECTRIC_FAN_WITH_LAMP",
48
+ "55": "SWEEPING_ROBOT",
49
+ "56": "RGB_BALL_LIGHT_4",
50
+ "57": "MONOCHROMATIC_BALL_LIGHT",
51
+ "59": "MEARICAMERA",
52
+ "1001": "BLADELESS_FAN",
53
+ "1002": "NEW_HUMIDIFIER",
54
+ "1003": "WARM_AIR_BLOWER"
55
+ }
@@ -0,0 +1,23 @@
1
+ const errors = {
2
+ 400: 'Parameter error',
3
+ 401: 'Wrong account or password',
4
+ 402: 'Email inactivated',
5
+ 403: 'Forbidden',
6
+ 404: 'Device does not exist',
7
+ 406: 'Authentication failed',
8
+ 503: 'Service Temporarily Unavailable or Device is offline'
9
+ };
10
+
11
+ const customErrors = {
12
+ ch404: 'Device channel does not exist',
13
+ unknown: 'An unknown error occurred',
14
+ noDevices: 'No devices found',
15
+ noPower: 'No power usage data found',
16
+ noSensor: "Can't read sensor data from device",
17
+ noFirmware: "Can't get model or firmware version",
18
+ invalidAuth: 'Library needs to be initialized using email and password',
19
+ invalidCredentials: 'Invalid credentials provided',
20
+ invalidPowerState: 'Invalid power state. Expecting: "on", "off" or "toggle"',
21
+ };
22
+
23
+ module.exports = Object.assign(errors, customErrors);
@@ -0,0 +1,58 @@
1
+ const STATE_ON = 'on';
2
+ const STATE_OFF = 'off';
3
+ const STATE_TOGGLE = 'toggle';
4
+
5
+ const VALID_POWER_STATES = [STATE_ON, STATE_OFF, STATE_TOGGLE];
6
+
7
+ /**
8
+ * Return new device state based on current conditions
9
+ */
10
+ const getNewPowerState = (currentState, newState) => {
11
+ if (newState !== STATE_TOGGLE) {
12
+ return newState;
13
+ }
14
+ return currentState === STATE_ON ? STATE_OFF : STATE_ON;
15
+ };
16
+
17
+ /**
18
+ * Get current device parameters and
19
+ */
20
+ const getPowerStateParams = (params, newState, channel) => {
21
+ if (params.switches) {
22
+ const switches = [...params.switches];
23
+ const channelToSwitch = channel - 1;
24
+ switches[channelToSwitch].switch = newState;
25
+ return { switches };
26
+ }
27
+ return { switch: newState };
28
+ };
29
+
30
+ /**
31
+ * Return status of all channels on a multi-channel device
32
+ */
33
+ const getAllChannelsState = params => {
34
+ const { switches } = params;
35
+ return switches.map(ch => ({
36
+ channel: ch.outlet + 1,
37
+ state: ch.switch,
38
+ }));
39
+ };
40
+
41
+ /**
42
+ * Return status of specific channel on multi-channel device
43
+ */
44
+ const getSpecificChannelState = (params, channel) => {
45
+ const { switches } = params;
46
+ return switches[channel - 1].switch;
47
+ };
48
+
49
+ module.exports = {
50
+ STATE_ON,
51
+ STATE_OFF,
52
+ STATE_TOGGLE,
53
+ VALID_POWER_STATES,
54
+ getNewPowerState,
55
+ getPowerStateParams,
56
+ getAllChannelsState,
57
+ getSpecificChannelState,
58
+ };
@@ -0,0 +1,68 @@
1
+ const crypto = require('crypto');
2
+ const CryptoJS = require('crypto-js');
3
+ const random = require('random');
4
+
5
+ const DEVICE_TYPE_UUID = require('../data/devices-type-uuid.json');
6
+ const DEVICE_CHANNEL_LENGTH = require('../data/devices-channel-length.json');
7
+
8
+ const makeAuthorizationSign = (APP_SECRET, body) =>
9
+ crypto
10
+ .createHmac('sha256', APP_SECRET)
11
+ .update(JSON.stringify(body))
12
+ .digest('base64');
13
+
14
+ const getDeviceTypeByUiid = uiid => DEVICE_TYPE_UUID[uiid] || '';
15
+
16
+ const getDeviceChannelCountByType = deviceType =>
17
+ DEVICE_CHANNEL_LENGTH[deviceType] || 0;
18
+
19
+ const getDeviceChannelCount = deviceUUID => {
20
+ const deviceType = getDeviceTypeByUiid(deviceUUID);
21
+ return getDeviceChannelCountByType(deviceType);
22
+ };
23
+
24
+ const create16Uiid = () => {
25
+ let result = '';
26
+ for (let i = 0; i < 16; i += 1) {
27
+ result += random.int(0, 9);
28
+ }
29
+ return result;
30
+ };
31
+
32
+ const encryptionBase64 = t =>
33
+ CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(t));
34
+
35
+ const decryptionBase64 = t =>
36
+ CryptoJS.enc.Base64.parse(t).toString(CryptoJS.enc.Utf8);
37
+
38
+ const encryptationData = (data, key) => {
39
+ const encryptedMessage = {};
40
+ const uid = create16Uiid();
41
+ const iv = encryptionBase64(uid);
42
+ const code = CryptoJS.AES.encrypt(data, CryptoJS.MD5(key), {
43
+ iv: CryptoJS.enc.Utf8.parse(uid),
44
+ mode: CryptoJS.mode.CBC,
45
+ padding: CryptoJS.pad.Pkcs7,
46
+ });
47
+ encryptedMessage.uid = uid;
48
+ encryptedMessage.iv = iv;
49
+ encryptedMessage.data = code.ciphertext.toString(CryptoJS.enc.Base64);
50
+ return encryptedMessage;
51
+ };
52
+
53
+ const decryptionData = (data, key, iv) => {
54
+ const iv64 = decryptionBase64(iv);
55
+ const code = CryptoJS.AES.decrypt(data, CryptoJS.MD5(key), {
56
+ iv: CryptoJS.enc.Utf8.parse(iv64),
57
+ mode: CryptoJS.mode.CBC,
58
+ padding: CryptoJS.pad.Pkcs7,
59
+ });
60
+ return code.toString(CryptoJS.enc.Utf8);
61
+ };
62
+
63
+ module.exports = {
64
+ makeAuthorizationSign,
65
+ getDeviceChannelCount,
66
+ encryptationData,
67
+ decryptionData,
68
+ };
@@ -0,0 +1,29 @@
1
+ const nonce = Math.random()
2
+ .toString(36)
3
+ .slice(5);
4
+
5
+ const timestamp = Math.floor(new Date() / 1000);
6
+
7
+ const _get = (obj, path, defaultValue = null) =>
8
+ String.prototype.split
9
+ .call(path, /[,[\].]+?/)
10
+ .filter(Boolean)
11
+ .reduce(
12
+ (a, c) => (Object.hasOwnProperty.call(a, c) ? a[c] : defaultValue),
13
+ obj
14
+ );
15
+
16
+ const _empty = obj => Object.entries(obj).length === 0;
17
+
18
+ const toQueryString = object =>
19
+ `?${Object.keys(object)
20
+ .map(key => `${key}=${object[key].toString()}`)
21
+ .join('&')}`;
22
+
23
+ module.exports = {
24
+ nonce,
25
+ timestamp,
26
+ _get,
27
+ _empty,
28
+ toQueryString,
29
+ };
@@ -0,0 +1,48 @@
1
+ const { _get } = require('../helpers/utilities');
2
+ const parseFirmwareUpdates = require('../parsers/parseFirmwareUpdates');
3
+
4
+ module.exports = {
5
+ /**
6
+ * Check device firmware update
7
+ *
8
+ * @param deviceId
9
+ *
10
+ * @returns {Promise<{msg: string, version: *}|{msg: string, error: number}|{msg: string, error: *}|Device|{msg: string}>}
11
+ */
12
+ async checkDeviceUpdate(deviceId) {
13
+ const device = await this.getDevice(deviceId);
14
+
15
+ const error = _get(device, 'error', false);
16
+
17
+ if (error) {
18
+ return device;
19
+ }
20
+
21
+ const deviceInfoList = parseFirmwareUpdates([device]);
22
+
23
+ const deviceInfoListError = _get(deviceInfoList, 'error', false);
24
+
25
+ if (deviceInfoListError) {
26
+ return deviceInfoList;
27
+ }
28
+
29
+ const update = await this.makeRequest({
30
+ method: 'post',
31
+ url: this.getOtaUrl(),
32
+ uri: '/app',
33
+ body: { deviceInfoList },
34
+ });
35
+
36
+ const isUpdate = _get(update, 'upgradeInfoList.0.version', false);
37
+
38
+ if (!isUpdate) {
39
+ return { status: 'ok', msg: 'No update available' };
40
+ }
41
+
42
+ return {
43
+ status: 'ok',
44
+ msg: 'Update available',
45
+ version: isUpdate,
46
+ };
47
+ },
48
+ };
@@ -0,0 +1,54 @@
1
+ const { _get } = require('../helpers/utilities');
2
+ const parseFirmwareUpdates = require('../parsers/parseFirmwareUpdates');
3
+
4
+ module.exports = {
5
+ async checkDevicesUpdates() {
6
+ const devices = await this.getDevices();
7
+
8
+ const error = _get(devices, 'error', false);
9
+
10
+ if (error) {
11
+ return devices;
12
+ }
13
+
14
+ const deviceInfoList = parseFirmwareUpdates(devices);
15
+
16
+ const deviceInfoListError = _get(deviceInfoList, 'error', false);
17
+
18
+ if (deviceInfoListError) {
19
+ return deviceInfoList;
20
+ }
21
+
22
+ const updates = await this.makeRequest({
23
+ method: 'post',
24
+ url: this.getOtaUrl(),
25
+ uri: '/app',
26
+ body: { deviceInfoList },
27
+ });
28
+
29
+ const upgradeInfoList = _get(updates, 'upgradeInfoList', false);
30
+
31
+ if (!upgradeInfoList) {
32
+ return { error: "Can't find firmware update information" };
33
+ }
34
+
35
+ return upgradeInfoList.map(device => {
36
+ const upd = _get(device, 'version', false);
37
+
38
+ if (!upd) {
39
+ return {
40
+ status: 'ok',
41
+ deviceId: device.deviceid,
42
+ msg: 'No update available',
43
+ };
44
+ }
45
+
46
+ return {
47
+ status: 'ok',
48
+ deviceId: device.deviceid,
49
+ msg: 'Update available',
50
+ version: upd,
51
+ };
52
+ });
53
+ },
54
+ };
@@ -0,0 +1,272 @@
1
+ const W3CWebSocket = require('websocket').w3cwebsocket;
2
+ const WebSocketAsPromised = require('websocket-as-promised');
3
+ const delay = require('delay');
4
+
5
+ const { nonce, timestamp } = require('../helpers/utilities');
6
+ const errors = require('../data/errors');
7
+
8
+ const {
9
+ VALID_POWER_STATES,
10
+ getNewPowerState,
11
+ getPowerStateParams,
12
+ getAllChannelsState,
13
+ getSpecificChannelState,
14
+ } = require('../helpers/device-control');
15
+
16
+ module.exports = {
17
+ async initDeviceControl(params = {}) {
18
+ // check if socket is already initialized
19
+ if (this.wsp) {
20
+ return;
21
+ }
22
+
23
+ const { APP_ID, at, apiKey } = this;
24
+
25
+ // set delay between socket messages
26
+ const { delayTime = 1000 } = params;
27
+ this.wsDelayTime = delayTime;
28
+
29
+ // request credentials if needed
30
+ if (at === null || apiKey === null) {
31
+ await this.getCredentials();
32
+ }
33
+
34
+ // request distribution service
35
+ const dispatch = await this.makeRequest({
36
+ method: 'post',
37
+ url: `https://${this.region}-api.coolkit.cc:8080`,
38
+ uri: '/dispatch/app',
39
+ body: {
40
+ accept: 'ws',
41
+ appid: APP_ID,
42
+ nonce,
43
+ ts: timestamp,
44
+ version: 8,
45
+ },
46
+ });
47
+
48
+ // WebSocket parameters
49
+ const WSS_URL = `wss://${dispatch.domain}:${dispatch.port}/api/ws`;
50
+ const WSS_CONFIG = { createWebSocket: wss => new W3CWebSocket(wss) };
51
+
52
+ // open WebSocket connection
53
+ this.wsp = new WebSocketAsPromised(WSS_URL, WSS_CONFIG);
54
+
55
+ // catch autentication errors
56
+ let socketError;
57
+ this.wsp.onMessage.addListener(async message => {
58
+ const data = JSON.parse(message);
59
+ if (data.error) {
60
+ socketError = data;
61
+ await this.webSocketClose();
62
+ }
63
+ });
64
+
65
+ // open socket connection
66
+ await this.wsp.open();
67
+
68
+ // WebSocket handshake
69
+ await this.webSocketHandshake();
70
+
71
+ // if auth error exists, throw an error
72
+ if (socketError) {
73
+ throw new Error(errors[socketError.error]);
74
+ }
75
+ },
76
+
77
+ /**
78
+ * WebSocket authentication process
79
+ */
80
+ async webSocketHandshake() {
81
+ const apikey = this.deviceApiKey || this.apiKey;
82
+
83
+ const payload = JSON.stringify({
84
+ action: 'userOnline',
85
+ version: 8,
86
+ ts: timestamp,
87
+ at: this.at,
88
+ userAgent: 'app',
89
+ apikey,
90
+ appid: this.APP_ID,
91
+ nonce,
92
+ sequence: Math.floor(timestamp * 1000),
93
+ });
94
+
95
+ await this.wsp.send(payload);
96
+ await delay(this.wsDelayTime);
97
+ },
98
+
99
+ /**
100
+ * Close WebSocket connection and class cleanup
101
+ */
102
+ async webSocketClose() {
103
+ await this.wsp.close();
104
+ delete this.wsDelayTime;
105
+ delete this.wsp;
106
+ delete this.deviceApiKey;
107
+ },
108
+
109
+ /**
110
+ * Update device status (timers, share status, on/off etc)
111
+ */
112
+ async updateDeviceStatus(deviceId, params) {
113
+ await this.initDeviceControl();
114
+
115
+ const apikey = this.deviceApiKey || this.apiKey;
116
+
117
+ const payload = JSON.stringify({
118
+ action: 'update',
119
+ deviceid: deviceId,
120
+ apikey,
121
+ userAgent: 'app',
122
+ sequence: Math.floor(timestamp * 1000),
123
+ ts: timestamp,
124
+ params,
125
+ });
126
+
127
+ return this.wsp.send(payload);
128
+ },
129
+
130
+ /**
131
+ * Check device status (timers, share status, on/off etc)
132
+ */
133
+ async getWSDeviceStatus(deviceId, params) {
134
+ await this.initDeviceControl();
135
+
136
+ let response = null;
137
+
138
+ this.wsp.onMessage.addListener(message => {
139
+ const data = JSON.parse(message);
140
+ if (data.deviceid === deviceId) {
141
+ response = data;
142
+ }
143
+ });
144
+
145
+ const apikey = this.deviceApiKey || this.apiKey;
146
+
147
+ const payload = JSON.stringify({
148
+ action: 'query',
149
+ deviceid: deviceId,
150
+ apikey,
151
+ userAgent: 'app',
152
+ sequence: Math.floor(timestamp * 1000),
153
+ ts: timestamp,
154
+ params,
155
+ });
156
+
157
+ this.wsp.send(payload);
158
+ await delay(this.wsDelayTime);
159
+
160
+ // throw error on invalid device
161
+ if (response.error) {
162
+ throw new Error(errors[response.error]);
163
+ }
164
+
165
+ return response;
166
+ },
167
+
168
+ /**
169
+ * Get device power state
170
+ */
171
+ async getWSDevicePowerState(deviceId, options = {}) {
172
+ // get extra parameters
173
+ const { channel = 1, allChannels = false, shared = false } = options;
174
+
175
+ // if device is shared by other account, fetch device api key
176
+ if (shared) {
177
+ const device = await this.getDevice(deviceId);
178
+ this.deviceApiKey = device.apikey;
179
+ }
180
+
181
+ // get device current state
182
+ const status = await this.getWSDeviceStatus(deviceId, [
183
+ 'switch',
184
+ 'switches',
185
+ ]);
186
+
187
+ // close websocket connection
188
+ await this.webSocketClose();
189
+
190
+ // check for multi-channel device
191
+ const multiChannelDevice = !!status.params.switches;
192
+
193
+ // returns all channels
194
+ if (multiChannelDevice && allChannels) {
195
+ return {
196
+ status: 'ok',
197
+ state: getAllChannelsState(status.params),
198
+ };
199
+ }
200
+
201
+ // multi-channel device & requested channel
202
+ if (multiChannelDevice) {
203
+ return {
204
+ status: 'ok',
205
+ state: getSpecificChannelState(status.params, channel),
206
+ channel,
207
+ };
208
+ }
209
+
210
+ // single channel device
211
+ return {
212
+ status: 'ok',
213
+ state: status.params.switch,
214
+ channel,
215
+ };
216
+ },
217
+
218
+ /**
219
+ * Set device power state
220
+ */
221
+ async setWSDevicePowerState(deviceId, state, options = {}) {
222
+ // check for valid power state
223
+ if (!VALID_POWER_STATES.includes(state)) {
224
+ throw new Error(errors.invalidPowerState);
225
+ }
226
+
227
+ // get extra parameters
228
+ const { channel = 1, shared = false } = options;
229
+
230
+ // if device is shared by other account, fetch device api key
231
+ if (shared) {
232
+ const device = await this.getDevice(deviceId);
233
+ this.deviceApiKey = device.apikey;
234
+ }
235
+
236
+ // get device current state
237
+ const status = await this.getWSDeviceStatus(deviceId, [
238
+ 'switch',
239
+ 'switches',
240
+ ]);
241
+
242
+ // check for multi-channel device
243
+ const multiChannelDevice = !!status.params.switches;
244
+
245
+ // get current device state
246
+ const currentState = multiChannelDevice
247
+ ? status.params.switches[channel - 1].switch
248
+ : status.params.switch;
249
+
250
+ // resolve new power state
251
+ const stateToSwitch = getNewPowerState(currentState, state);
252
+
253
+ // build request payload
254
+ const params = getPowerStateParams(status.params, stateToSwitch, channel);
255
+
256
+ // change device status
257
+ try {
258
+ await this.updateDeviceStatus(deviceId, params);
259
+ await delay(this.wsDelayTime);
260
+ } catch (error) {
261
+ throw new Error(error);
262
+ } finally {
263
+ await this.webSocketClose();
264
+ }
265
+
266
+ return {
267
+ status: 'ok',
268
+ state: stateToSwitch,
269
+ channel: multiChannelDevice ? channel : 1,
270
+ };
271
+ },
272
+ };
@@ -0,0 +1,54 @@
1
+ const fetch = require('node-fetch');
2
+
3
+ const { _get } = require('../helpers/utilities');
4
+ const credentialsPayload = require('../payloads/credentialsPayload');
5
+ const { makeAuthorizationSign } = require('../helpers/ewelink');
6
+ const errors = require('../data/errors');
7
+
8
+ module.exports = {
9
+ /**
10
+ * Returns user credentials information
11
+ *
12
+ * @returns {Promise<{msg: string, error: *}>}
13
+ */
14
+ async getCredentials() {
15
+ const { APP_ID, APP_SECRET } = this;
16
+
17
+ const body = credentialsPayload({
18
+ appid: APP_ID,
19
+ email: this.email,
20
+ phoneNumber: this.phoneNumber,
21
+ password: this.password,
22
+ });
23
+
24
+ const request = await fetch(`${this.getApiUrl()}/user/login`, {
25
+ method: 'post',
26
+ headers: {
27
+ Authorization: `Sign ${makeAuthorizationSign(APP_SECRET, body)}`,
28
+ },
29
+ body: JSON.stringify(body),
30
+ });
31
+
32
+ let response = await request.json();
33
+
34
+ const error = _get(response, 'error', false);
35
+ const region = _get(response, 'region', false);
36
+
37
+ if (error && [400, 401, 404].indexOf(parseInt(error)) !== -1) {
38
+ return { error: 406, msg: errors['406'] };
39
+ }
40
+
41
+ if (error && parseInt(error) === 301 && region) {
42
+ if (this.region !== region) {
43
+ this.region = region;
44
+ response = await this.getCredentials();
45
+ return response;
46
+ }
47
+ return { error, msg: 'Region does not exist' };
48
+ }
49
+
50
+ this.apiKey = _get(response, 'user.apikey', '');
51
+ this.at = _get(response, 'at', '');
52
+ return response;
53
+ },
54
+ };