diff --git a/lib/helper.js b/lib/helper.js new file mode 100644 index 0000000..23ac07d --- /dev/null +++ b/lib/helper.js @@ -0,0 +1,71 @@ +const crypto = require('crypto'); +const random = require('random'); +const nonce = require('nonce')(); + +const makeFakeIMEI = () => { + const num1 = random.int(1000, 9999); + const num2 = random.int(1000, 9999); + return `DF7425A0-${num1}-${num2}-9F5E-3BC9179E48FB`; +}; + +const loginPayload = ({ email, password }) => ({ + email, + password, + version: 6, + ts: `${Math.round(new Date().getTime() / 1000)}`, + nonce: `${nonce()}`, + appid: 'oeVkj2lYFGnJu5XUtWisfW4utiN4u9Mq', + imei: makeFakeIMEI(), + os: 'iOS', + model: 'iPhone10,6', + romVersion: '11.1.2', + appVersion: '3.5.3', +}); + +const wssLoginPayload = ({ at, apiKey }) => { + const timeStamp = new Date() / 1000; + const ts = Math.floor(timeStamp); + const sequence = Math.floor(timeStamp * 1000); + const payload = { + action: 'userOnline', + userAgent: 'app', + version: 6, + nonce: `${nonce()}`, + apkVesrion: '1.8', + os: 'ios', + at, + apikey: apiKey, + ts: `${ts}`, + model: 'iPhone10,6', + romVersion: '11.1.2', + sequence, + }; + return JSON.stringify(payload); +}; + +const wssUpdatePayload = ({ apiKey, deviceId, params }) => { + const timeStamp = new Date() / 1000; + const sequence = Math.floor(timeStamp * 1000); + const payload = { + action: 'update', + userAgent: 'app', + apikey: apiKey, + deviceid: `${deviceId}`, + params, + sequence, + }; + return JSON.stringify(payload); +}; + +const makeAuthorizationSign = body => + crypto + .createHmac('sha256', '6Nz4n0xA8s8qdxQf2GqurZj2Fs55FUvM') + .update(JSON.stringify(body)) + .digest('base64'); + +module.exports = { + makeAuthorizationSign, + loginPayload, + wssLoginPayload, + wssUpdatePayload, +}; diff --git a/main.js b/main.js new file mode 100644 index 0000000..3261a38 --- /dev/null +++ b/main.js @@ -0,0 +1,100 @@ +const rp = require('request-promise'); +const W3CWebSocket = require('websocket').w3cwebsocket; +const WebSocketAsPromised = require('websocket-as-promised'); +const delay = require('delay'); + +const { + makeAuthorizationSign, + loginPayload, + wssLoginPayload, + wssUpdatePayload, +} = require('./lib/helper'); + +class eWeLink { + constructor({ region = 'us', email, password }) { + this.apiUrl = `https://${region}-api.coolkit.cc:8080/api`; + this.apiWebSocket = 'wss://us-pconnect3.coolkit.cc:8080/api/ws'; + this.email = email; + this.password = password; + this.apiKey = ''; + this.at = ''; + } + + async makeRequest({ method = 'GET', uri, body = {}, qs = {} }) { + const { apiKey, at } = this; + + if (!apiKey && !at) { + await this.login(); + } + + const response = await rp({ + method, + uri: `${this.apiUrl}${uri}`, + headers: { Authorization: `Bearer ${this.at}` }, + body, + qs, + json: true, + }); + + return response; + } + + async login() { + const body = loginPayload({ + email: this.email, + password: this.password, + }); + const response = await rp({ + method: 'POST', + uri: `${this.apiUrl}/user/login`, + headers: { Authorization: `Sign ${makeAuthorizationSign(body)}` }, + body, + json: true, + }); + this.apiKey = response.user.apikey; + this.at = response.at; + return response; + } + + async getDevices() { + const response = await this.makeRequest({ + uri: '/user/device', + qs: { lang: 'en', getTags: 1 }, + }); + return response; + } + + async getDevice(deviceId) { + const response = await this.makeRequest({ + uri: `/user/device/${deviceId}`, + qs: { lang: 'en', getTags: 1 }, + }); + return response; + } + + async toggleDevice(deviceId) { + const device = await this.getDevice(deviceId); + const status = device.params.switch === 'on' ? 'off' : 'on'; + + const payloadLogin = wssLoginPayload({ at: this.at, apiKey: this.apiKey }); + const payloadUpdate = wssUpdatePayload({ + apiKey: this.apiKey, + deviceId, + params: { switch: status }, + }); + + const wsp = new WebSocketAsPromised(this.apiWebSocket, { + createWebSocket: url => new W3CWebSocket(url), + }); + + await wsp.open(); + await wsp.send(payloadLogin); + await delay(1000); + await wsp.send(payloadUpdate); + await wsp.close(); + + return { status: 'ok' }; + } +} + +module.exports = eWeLink; diff --git a/package.json b/package.json index 4474af2..74827c5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ewelink-api", "version": "1.0.0", "description": "eWeLink API for Node.js", - "main": "index.js", + "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" },