From e6f60724f18a2c107b56e79c7e8c421293b37f4d Mon Sep 17 00:00:00 2001 From: Martin M Date: Sat, 2 Nov 2019 22:42:55 -0300 Subject: [PATCH] Release v1.8.0 (#23) * new method to check for device firmware updates * moved mixin * generate firmware update payload * new method to check for devices firmware updates * removed unused function * moved firmware tests to own file * added firmware test cases * return device id on response * updated credentials list * version bump --- lib/payloads/firmwareUpdate.js | 15 +++ lib/payloads/index.js | 2 + main.js | 45 +++++--- mixins/firmware/checkDeviceUpdateMixin.js | 48 +++++++++ mixins/firmware/checkDevicesUpdatesMixin.js | 54 ++++++++++ .../getFirmwareVersionMixin.js | 0 package-lock.json | 2 +- package.json | 2 +- test/_setup/credentials.json | 4 +- test/_setup/expectations.js | 7 ++ test/firmware.spec.js | 101 ++++++++++++++++++ test/main-env-node.spec.js | 9 -- test/main-env-serverless.spec.js | 10 -- test/main-invalid-credentials.spec.js | 8 -- test/main-valid-credentials.spec.js | 8 -- 15 files changed, 262 insertions(+), 53 deletions(-) create mode 100644 lib/payloads/firmwareUpdate.js create mode 100644 mixins/firmware/checkDeviceUpdateMixin.js create mode 100644 mixins/firmware/checkDevicesUpdatesMixin.js rename mixins/{devices => firmware}/getFirmwareVersionMixin.js (100%) create mode 100644 test/firmware.spec.js diff --git a/lib/payloads/firmwareUpdate.js b/lib/payloads/firmwareUpdate.js new file mode 100644 index 0000000..0f6b3cc --- /dev/null +++ b/lib/payloads/firmwareUpdate.js @@ -0,0 +1,15 @@ +const { _get } = require('../../lib/helpers'); + +const firmwareUpdate = devicesList => + devicesList.map(device => { + const model = _get(device, 'extra.extra.model', false); + const fwVersion = _get(device, 'params.fwVersion', false); + + if (!model || !fwVersion) { + return { error: 500, msg: "Can't get model or firmware version" }; + } + + return { model, version: fwVersion, deviceid: device.deviceid }; + }); + +module.exports = firmwareUpdate; diff --git a/lib/payloads/index.js b/lib/payloads/index.js index 48baf7e..9ec1a8f 100644 --- a/lib/payloads/index.js +++ b/lib/payloads/index.js @@ -1,8 +1,10 @@ +const firmwareUpdate = require('./firmwareUpdate'); const loginPayload = require('./loginPayload'); const wssLoginPayload = require('./wssLoginPayload'); const wssUpdatePayload = require('./wssUpdatePayload'); module.exports = { + firmwareUpdate, loginPayload, wssLoginPayload, wssUpdatePayload, diff --git a/main.js b/main.js index 0b5827c..2562751 100644 --- a/main.js +++ b/main.js @@ -28,6 +28,14 @@ class eWeLink { return `https://${this.region}-api.coolkit.cc:8080/api`; } + /** + * Generate eWeLink OTA API URL + * @returns {string} + */ + getOtaUrl() { + return `https://${this.region}-ota.coolkit.cc:8080/otaother`; + } + /** * Generate eWeLink WebSocket URL * @@ -41,22 +49,29 @@ class eWeLink { * Generate http requests helpers * * @param method + * @param url * @param uri * @param body * @param qs * * @returns {Promise<{msg: string, error: *}>} */ - async makeRequest({ method = 'GET', uri, body = {}, qs = {} }) { + async makeRequest({ method = 'GET', url, uri, body = {}, qs = {} }) { const { at } = this; if (!at) { await this.login(); } + let apiUrl = this.getApiUrl(); + + if (url) { + apiUrl = url; + } + const response = await rp({ method, - uri: `${this.getApiUrl()}${uri}`, + uri: `${apiUrl}${uri}`, headers: { Authorization: `Bearer ${this.at}` }, body, qs, @@ -111,16 +126,6 @@ class eWeLink { return response; } - /** - * Check if authentication credentials doesn't exists then perform a login - * - * @returns {Promise} - */ - async logIfNeeded() { - if (!this.at) { - await this.login(); - } - } } /* LOAD MIXINS: power state */ @@ -139,7 +144,11 @@ const getTHMixin = require('./mixins/temphumd/getTHMixin'); const getDevicesMixin = require('./mixins/devices/getDevicesMixin'); const getDeviceMixin = require('./mixins/devices/getDeviceMixin'); const getDeviceChannelCountMixin = require('./mixins/devices/getDeviceChannelCountMixin'); -const getFirmwareVersionMixin = require('./mixins/devices/getFirmwareVersionMixin'); + +/* LOAD MIXINS: firmware */ +const getFirmwareVersionMixin = require('./mixins/firmware/getFirmwareVersionMixin'); +const checkDeviceUpdateMixin = require('./mixins/firmware/checkDeviceUpdateMixin'); +const checkDevicesUpdatesMixin = require('./mixins/firmware/checkDevicesUpdatesMixin'); /* LOAD MIXINS: websocket */ const openWebSocketMixin = require('./mixins/websocket/openWebSocketMixin'); @@ -163,8 +172,14 @@ Object.assign( eWeLink.prototype, getDevicesMixin, getDeviceMixin, - getDeviceChannelCountMixin, - getFirmwareVersionMixin + getDeviceChannelCountMixin +); + +Object.assign( + eWeLink.prototype, + getFirmwareVersionMixin, + checkDeviceUpdateMixin, + checkDevicesUpdatesMixin ); Object.assign(eWeLink.prototype, openWebSocketMixin); diff --git a/mixins/firmware/checkDeviceUpdateMixin.js b/mixins/firmware/checkDeviceUpdateMixin.js new file mode 100644 index 0000000..46ac6e3 --- /dev/null +++ b/mixins/firmware/checkDeviceUpdateMixin.js @@ -0,0 +1,48 @@ +const { _get } = require('../../lib/helpers'); +const payloads = require('../../lib/payloads'); + +const checkDeviceUpdateMixin = { + /** + * Check device firmware update + * + * @param deviceId + * + * @returns {Promise<{msg: string, version: *}|{msg: string, error: number}|{msg: string, error: *}|Device|{msg: string}>} + */ + async checkDeviceUpdate(deviceId) { + const device = await this.getDevice(deviceId); + + const error = _get(device, 'error', false); + + if (error) { + return device; + } + + const deviceInfoList = payloads.firmwareUpdate([device]); + + if (error) { + return deviceInfoList; + } + + const update = await this.makeRequest({ + method: 'POST', + url: this.getOtaUrl(), + uri: '/app', + body: { deviceInfoList }, + }); + + const isUpdate = _get(update, 'upgradeInfoList.0.version', false); + + if (!isUpdate) { + return { status: 'ok', msg: 'No update available' }; + } + + return { + status: 'ok', + msg: 'Update available', + version: isUpdate, + }; + }, +}; + +module.exports = checkDeviceUpdateMixin; diff --git a/mixins/firmware/checkDevicesUpdatesMixin.js b/mixins/firmware/checkDevicesUpdatesMixin.js new file mode 100644 index 0000000..625c644 --- /dev/null +++ b/mixins/firmware/checkDevicesUpdatesMixin.js @@ -0,0 +1,54 @@ +const { _get } = require('../../lib/helpers'); +const payloads = require('../../lib/payloads'); + +const checkDevicesUpdatesMixin = { + async checkDevicesUpdates() { + const devices = await this.getDevices(); + + const error = _get(devices, 'error', false); + + if (error) { + return devices; + } + + const deviceInfoList = payloads.firmwareUpdate(devices); + + if (error) { + return deviceInfoList; + } + + const updates = await this.makeRequest({ + method: 'POST', + url: this.getOtaUrl(), + uri: '/app', + body: { deviceInfoList }, + }); + + const upgradeInfoList = _get(updates, 'upgradeInfoList', false); + + if (!upgradeInfoList) { + return { error: "Can't find firmware update information" }; + } + + return upgradeInfoList.map(device => { + const upd = _get(device, 'version', false); + + if (!upd) { + return { + status: 'ok', + deviceId: device.deviceid, + msg: 'No update available', + }; + } + + return { + status: 'ok', + deviceId: device.deviceid, + msg: 'Update available', + version: upd, + }; + }); + }, +}; + +module.exports = checkDevicesUpdatesMixin; diff --git a/mixins/devices/getFirmwareVersionMixin.js b/mixins/firmware/getFirmwareVersionMixin.js similarity index 100% rename from mixins/devices/getFirmwareVersionMixin.js rename to mixins/firmware/getFirmwareVersionMixin.js diff --git a/package-lock.json b/package-lock.json index 6e70975..f7b1be1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ewelink-api", - "version": "1.7.0", + "version": "1.8.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 112cd71..35b6620 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ewelink-api", - "version": "1.7.0", + "version": "1.8.0", "description": "eWeLink API for Node.js", "author": "Martín M.", "license": "MIT", diff --git a/test/_setup/credentials.json b/test/_setup/credentials.json index 9369819..a8025c9 100644 --- a/test/_setup/credentials.json +++ b/test/_setup/credentials.json @@ -6,5 +6,7 @@ "deviceIdWithoutPower": "", "deviceIdWithTempAndHum": "", "deviceIdWithoutTempAndHum": "", - "fourChannelsDevice": "" + "fourChannelsDevice": "", + "outdatedFirmwareDevice": "", + "updatedFirmwareDevice": "" } \ No newline at end of file diff --git a/test/_setup/expectations.js b/test/_setup/expectations.js index 23b628e..a95edb5 100644 --- a/test/_setup/expectations.js +++ b/test/_setup/expectations.js @@ -44,10 +44,17 @@ const currentMonthPowerUsageExpectations = { daily: expect.any(Array), }; +const firmwareExpectations = { + status: expect.any(String), + deviceId: expect.any(String), + msg: expect.any(String), +}; + module.exports = { loginExpectations, allDevicesExpectations, specificDeviceExpectations, rawPowerUsageExpectations, currentMonthPowerUsageExpectations, + firmwareExpectations, }; diff --git a/test/firmware.spec.js b/test/firmware.spec.js new file mode 100644 index 0000000..2188f48 --- /dev/null +++ b/test/firmware.spec.js @@ -0,0 +1,101 @@ +const ewelink = require('../main'); + +const { + email, + password, + singleChannelDeviceId, + outdatedFirmwareDevice, + updatedFirmwareDevice, +} = require('./_setup/credentials.json'); + +const { firmwareExpectations } = require('./_setup/expectations'); + +describe('firmware: get version methods', () => { + let connection; + + beforeAll(() => { + connection = new ewelink({ email, password }); + }); + + test('get firmware version', async () => { + const device = await connection.getDevice(singleChannelDeviceId); + const currentVersion = device.params.fwVersion; + const firmware = await connection.getFirmwareVersion(singleChannelDeviceId); + expect(typeof firmware).toBe('object'); + expect(firmware.status).toBe('ok'); + expect(firmware.fwVersion).toBe(currentVersion); + }); + + test('get device firmware version should be right message', async () => { + const login = await connection.login(); + const accessToken = login.at; + const conn = new ewelink({ at: accessToken }); + const device = await conn.getDevice(singleChannelDeviceId); + const currentVersion = device.params.fwVersion; + const firmware = await conn.getFirmwareVersion(singleChannelDeviceId); + expect(typeof firmware).toBe('object'); + expect(firmware.status).toBe('ok'); + expect(firmware.fwVersion).toBe(currentVersion); + }); + + test('get invalid device firmware version should fail', async () => { + const conn = new ewelink({ email, password }); + const firmwareVersion = await conn.getFirmwareVersion('invalid deviceid'); + expect(typeof firmwareVersion).toBe('object'); + expect(firmwareVersion.msg).toBe('Device does not exist'); + expect(firmwareVersion.error).toBe(500); + }); + + test('get device firmware version using invalid credentials should fail', async () => { + const conn = new ewelink({ email: 'invalid', password: 'credentials' }); + const firmware = await conn.getFirmwareVersion(singleChannelDeviceId); + expect(typeof firmware).toBe('object'); + expect(firmware.msg).toBe('Authentication error'); + expect(firmware.error).toBe(401); + }); +}); + +describe('firmware: check updates methods', () => { + let connection; + + beforeAll(() => { + connection = new ewelink({ email, password }); + }); + + test('outdated device firmware should return available version', async () => { + const status = await connection.checkDeviceUpdate(outdatedFirmwareDevice); + expect(typeof status).toBe('object'); + expect(typeof status).toBe('object'); + expect(status.status).toBe('ok'); + expect(status.msg).toBe('Update available'); + expect(typeof status.version).toBe('string'); + }); + + test('updated device firmware should return right message', async () => { + const status = await connection.checkDeviceUpdate(updatedFirmwareDevice); + expect(typeof status).toBe('object'); + expect(typeof status).toBe('object'); + expect(status.status).toBe('ok'); + expect(status.msg).toBe('No update available'); + }); + + test('invalid device update check should return error', async () => { + const status = await connection.checkDeviceUpdate('invalid deviceid'); + expect(typeof status).toBe('object'); + expect(status.error).toBe(500); + }); + + test('get devices update check should be valid response', async () => { + const status = await connection.checkDevicesUpdates(); + expect(typeof status).toBe('object'); + expect(status[0]).toMatchObject(firmwareExpectations); + }); + + test('get devices update check with invalid credentials should fail', async () => { + const conn = new ewelink({ email: 'invalid', password: 'credentials' }); + const status = await conn.checkDevicesUpdates(); + expect(typeof status).toBe('object'); + expect(status.msg).toBe('Authentication error'); + expect(status.error).toBe(401); + }); +}); diff --git a/test/main-env-node.spec.js b/test/main-env-node.spec.js index 843cf0c..742c0aa 100644 --- a/test/main-env-node.spec.js +++ b/test/main-env-node.spec.js @@ -152,13 +152,4 @@ describe('env: node script', () => { expect(result.status).toBe('ok'); expect(result.switchesAmount).toBe(4); }); - - test('get firmware version', async () => { - const device = await conn.getDevice(singleChannelDeviceId); - const currentVersion = device.params.fwVersion; - const firmware = await conn.getFirmwareVersion(singleChannelDeviceId); - expect(typeof firmware).toBe('object'); - expect(firmware.status).toBe('ok'); - expect(firmware.fwVersion).toBe(currentVersion); - }); }); diff --git a/test/main-env-serverless.spec.js b/test/main-env-serverless.spec.js index 7c517d1..bc24c5b 100644 --- a/test/main-env-serverless.spec.js +++ b/test/main-env-serverless.spec.js @@ -108,14 +108,4 @@ describe('env: serverless', () => { expect(result.status).toBe('ok'); expect(result.switchesAmount).toBe(4); }); - - test('get device firmware version', async () => { - const conn = new ewelink({ at: accessToken }); - const device = await conn.getDevice(singleChannelDeviceId); - const currentVersion = device.params.fwVersion; - const firmware = await conn.getFirmwareVersion(singleChannelDeviceId); - expect(typeof firmware).toBe('object'); - expect(firmware.status).toBe('ok'); - expect(firmware.fwVersion).toBe(currentVersion); - }); }); diff --git a/test/main-invalid-credentials.spec.js b/test/main-invalid-credentials.spec.js index f73e0e7..96fdba7 100644 --- a/test/main-invalid-credentials.spec.js +++ b/test/main-invalid-credentials.spec.js @@ -88,12 +88,4 @@ describe('invalid credentials', () => { expect(switchesAmount.msg).toBe('Authentication error'); expect(switchesAmount.error).toBe(401); }); - - test('get device firmware version should fail', async () => { - const conn = new ewelink({ email: 'invalid', password: 'credentials' }); - const firmware = await conn.getFirmwareVersion(singleChannelDeviceId); - expect(typeof firmware).toBe('object'); - expect(firmware.msg).toBe('Authentication error'); - expect(firmware.error).toBe(401); - }); }); diff --git a/test/main-valid-credentials.spec.js b/test/main-valid-credentials.spec.js index a7d4a96..07a5d3d 100644 --- a/test/main-valid-credentials.spec.js +++ b/test/main-valid-credentials.spec.js @@ -96,14 +96,6 @@ describe('valid credentials, invalid device', () => { expect(switchesAmount.msg).toBe('Device does not exist'); expect(switchesAmount.error).toBe(500); }); - - test('get device firmware version should fail', async () => { - const conn = new ewelink({ email, password }); - const firmwareVersion = await conn.getFirmwareVersion('invalid deviceid'); - expect(typeof firmwareVersion).toBe('object'); - expect(firmwareVersion.msg).toBe('Device does not exist'); - expect(firmwareVersion.error).toBe(500); - }); }); describe('valid credentials, wrong region', () => {