mirror of
https://github.com/skydiver/ewelink-api.git
synced 2025-12-21 13:23:05 +01:00
* Added current temperature, humidity and firmware * fixed wrong parameter * linting * fixed test cases * fixed serverless test cases * fixed test cases * variable renamed * version bump
397 lines
9.5 KiB
JavaScript
397 lines
9.5 KiB
JavaScript
const rp = require('request-promise');
|
|
|
|
const { _get } = require('./lib/helpers');
|
|
|
|
const {
|
|
makeAuthorizationSign,
|
|
getDeviceChannelCount,
|
|
} = require('./lib/ewelink-helper');
|
|
|
|
const payloads = require('./lib/payloads');
|
|
|
|
const { ChangeState } = require('./classes/PowerState');
|
|
const { DeviceRaw, CurrentMonth } = require('./classes/PowerUsage');
|
|
|
|
class eWeLink {
|
|
constructor({ region = 'us', email, password, at, apiKey }) {
|
|
if (!at && (!email && !password)) {
|
|
return { error: 'No credentials provided' };
|
|
}
|
|
|
|
this.region = region;
|
|
this.email = email;
|
|
this.password = password;
|
|
this.at = at;
|
|
this.apiKey = apiKey;
|
|
}
|
|
|
|
/**
|
|
* Generate eWeLink API URL
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
getApiUrl() {
|
|
return `https://${this.region}-api.coolkit.cc:8080/api`;
|
|
}
|
|
|
|
/**
|
|
* Generate eWeLink WebSocket URL
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
getApiWebSocket() {
|
|
return `wss://${this.region}-pconnect3.coolkit.cc:8080/api/ws`;
|
|
}
|
|
|
|
/**
|
|
* Generate http requests helpers
|
|
*
|
|
* @param method
|
|
* @param uri
|
|
* @param body
|
|
* @param qs
|
|
*
|
|
* @returns {Promise<{msg: string, error: *}>}
|
|
*/
|
|
async makeRequest({ method = 'GET', uri, body = {}, qs = {} }) {
|
|
const { at } = this;
|
|
|
|
if (!at) {
|
|
await this.login();
|
|
}
|
|
|
|
const response = await rp({
|
|
method,
|
|
uri: `${this.getApiUrl()}${uri}`,
|
|
headers: { Authorization: `Bearer ${this.at}` },
|
|
body,
|
|
qs,
|
|
json: true,
|
|
});
|
|
|
|
const error = _get(response, 'error', false);
|
|
if (error && [401, 402].indexOf(parseInt(error)) !== -1) {
|
|
return { error, msg: 'Authentication error' };
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
/**
|
|
* Helper to login into eWeLink API
|
|
*
|
|
* @returns {Promise<{msg: string, error: *}>}
|
|
*/
|
|
async login() {
|
|
const body = payloads.loginPayload({
|
|
email: this.email,
|
|
password: this.password,
|
|
});
|
|
|
|
let response = await rp({
|
|
method: 'POST',
|
|
uri: `${this.getApiUrl()}/user/login`,
|
|
headers: { Authorization: `Sign ${makeAuthorizationSign(body)}` },
|
|
body,
|
|
json: true,
|
|
});
|
|
|
|
const error = _get(response, 'error', false);
|
|
const region = _get(response, 'region', false);
|
|
|
|
if (error && [400, 401, 404].indexOf(parseInt(error)) !== -1) {
|
|
return { error, msg: 'Authentication error' };
|
|
}
|
|
|
|
if (error && parseInt(error) === 301 && region) {
|
|
if (this.region !== region) {
|
|
this.region = region;
|
|
response = await this.login();
|
|
return response;
|
|
}
|
|
return { error, msg: 'Region does not exist' };
|
|
}
|
|
|
|
this.apiKey = _get(response, 'user.apikey', '');
|
|
this.at = _get(response, 'at', '');
|
|
return response;
|
|
}
|
|
|
|
/**
|
|
* Check if authentication credentials doesn't exists then perform a login
|
|
*
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async logIfNeeded() {
|
|
if (!this.at) {
|
|
await this.login();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get specific device information
|
|
*
|
|
* @returns {Promise<{msg: string, error: *}>}
|
|
*/
|
|
async getDevices() {
|
|
return this.makeRequest({
|
|
uri: '/user/device',
|
|
qs: { lang: 'en', getTags: 1 },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get information about all associated devices to account
|
|
*
|
|
* @param deviceId
|
|
*
|
|
* @returns {Promise<{msg: string, error: *}>}
|
|
*/
|
|
async getDevice(deviceId) {
|
|
return this.makeRequest({
|
|
uri: `/user/device/${deviceId}`,
|
|
qs: { lang: 'en', getTags: 1 },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get current power state for a specific device
|
|
*
|
|
* @param deviceId
|
|
* @param channel
|
|
*
|
|
* @returns {Promise<{state: *, status: string}|{msg: string, error: *}>}
|
|
*/
|
|
async getDevicePowerState(deviceId, channel = 1) {
|
|
const device = await this.getDevice(deviceId);
|
|
const error = _get(device, 'error', false);
|
|
const uiid = _get(device, 'extra.extra.uiid', false);
|
|
|
|
let state = _get(device, 'params.switch', false);
|
|
const switches = _get(device, 'params.switches', false);
|
|
|
|
const switchesAmount = getDeviceChannelCount(uiid);
|
|
|
|
if (switchesAmount > 0 && switchesAmount < channel) {
|
|
return { error, msg: 'Device channel does not exist' };
|
|
}
|
|
|
|
if (error || (!state && !switches)) {
|
|
if (error && parseInt(error) === 401) {
|
|
return device;
|
|
}
|
|
return { error, msg: 'Device does not exist' };
|
|
}
|
|
|
|
if (switches) {
|
|
state = switches[channel - 1].switch;
|
|
}
|
|
|
|
return { status: 'ok', state };
|
|
}
|
|
|
|
/**
|
|
* Change power state for a specific device
|
|
*
|
|
* @param deviceId
|
|
* @param state
|
|
* @param channel
|
|
*
|
|
* @returns {Promise<{state: *, status: string}|{msg: string, error: *}>}
|
|
*/
|
|
async setDevicePowerState(deviceId, state, channel = 1) {
|
|
const device = await this.getDevice(deviceId);
|
|
const error = _get(device, 'error', false);
|
|
const uiid = _get(device, 'extra.extra.uiid', false);
|
|
|
|
let status = _get(device, 'params.switch', false);
|
|
const switches = _get(device, 'params.switches', false);
|
|
|
|
const switchesAmount = getDeviceChannelCount(uiid);
|
|
|
|
if (switchesAmount > 0 && switchesAmount < channel) {
|
|
return { error, msg: 'Device channel does not exist' };
|
|
}
|
|
|
|
if (error || (!status && !switches)) {
|
|
if (error && parseInt(error) === 401) {
|
|
return device;
|
|
}
|
|
return { error, msg: 'Device does not exist' };
|
|
}
|
|
|
|
let stateToSwitch = state;
|
|
const params = {};
|
|
|
|
if (switches) {
|
|
status = switches[channel - 1].switch;
|
|
}
|
|
|
|
if (state === 'toggle') {
|
|
stateToSwitch = status === 'on' ? 'off' : 'on';
|
|
}
|
|
|
|
if (switches) {
|
|
params.switches = switches;
|
|
params.switches[channel - 1].switch = stateToSwitch;
|
|
} else {
|
|
params.switch = stateToSwitch;
|
|
}
|
|
|
|
return ChangeState.set({
|
|
apiUrl: this.getApiWebSocket(),
|
|
at: this.at,
|
|
apiKey: this.apiKey,
|
|
deviceId,
|
|
params,
|
|
state: stateToSwitch,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Toggle power state for a specific device
|
|
*
|
|
* @param deviceId
|
|
* @param channel
|
|
*
|
|
* @returns {Promise<{state: *, status: string}|{msg: string, error: *}>}
|
|
*/
|
|
async toggleDevice(deviceId, channel = 1) {
|
|
return this.setDevicePowerState(deviceId, 'toggle', channel);
|
|
}
|
|
|
|
/**
|
|
* Get device raw power usage
|
|
*
|
|
* @param deviceId
|
|
*
|
|
* @returns {Promise<{error: string}|{response: {hundredDaysKwhData: *}, status: string}>}
|
|
*/
|
|
async getDeviceRawPowerUsage(deviceId) {
|
|
await this.logIfNeeded();
|
|
|
|
return DeviceRaw.get({
|
|
apiUrl: this.getApiWebSocket(),
|
|
at: this.at,
|
|
apiKey: this.apiKey,
|
|
deviceId,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get device power usage for current month
|
|
*
|
|
* @param deviceId
|
|
*
|
|
* @returns {Promise<{error: string}|{daily: *, monthly: *}>}
|
|
*/
|
|
async getDevicePowerUsage(deviceId) {
|
|
const response = await this.getDeviceRawPowerUsage(deviceId);
|
|
|
|
const error = _get(response, 'error', false);
|
|
const hundredDaysKwhData = _get(response, 'data.hundredDaysKwhData', false);
|
|
|
|
if (error) {
|
|
return response;
|
|
}
|
|
|
|
return {
|
|
status: 'ok',
|
|
...CurrentMonth.parse({ hundredDaysKwhData }),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get device current temperature
|
|
*
|
|
* @param deviceId
|
|
*
|
|
* @returns {Promise<{temperature: *, status: string}|{msg: string, error: *}>}
|
|
*/
|
|
async getDeviceCurrentTemperature(deviceId) {
|
|
const device = await this.getDevice(deviceId);
|
|
const error = _get(device, 'error', false);
|
|
const model = _get(device, 'extra.extra.model', '');
|
|
const temperature = _get(device, 'params.currentTemperature', false);
|
|
|
|
if (error || model !== 'PSA-BHA-GL' || !temperature) {
|
|
if (error && parseInt(error) === 401) {
|
|
return device;
|
|
}
|
|
return { error, msg: 'Device does not exist' };
|
|
}
|
|
|
|
return { status: 'ok', temperature };
|
|
}
|
|
|
|
/**
|
|
* Get device current humidity
|
|
*
|
|
* @param deviceId
|
|
*
|
|
* @returns {Promise<{humidity: *, status: string}|{msg: string, error: *}>}
|
|
*/
|
|
async getDeviceCurrentHumidity(deviceId) {
|
|
const device = await this.getDevice(deviceId);
|
|
const error = _get(device, 'error', false);
|
|
const model = _get(device, 'extra.extra.model', '');
|
|
const humidity = _get(device, 'params.currentHumidity', false);
|
|
|
|
if (error || model !== 'PSA-BHA-GL' || !humidity) {
|
|
if (error && parseInt(error) === 401) {
|
|
return device;
|
|
}
|
|
return { error, msg: 'Device does not exist' };
|
|
}
|
|
|
|
return { status: 'ok', humidity };
|
|
}
|
|
|
|
/**
|
|
* Get device channel count
|
|
*
|
|
* @param deviceId
|
|
*
|
|
* @returns {Promise<{switchesAmount: *, status: string}
|
|
*/
|
|
async getDeviceChannelCount(deviceId) {
|
|
const device = await this.getDevice(deviceId);
|
|
const error = _get(device, 'error', false);
|
|
const uiid = _get(device, 'extra.extra.uiid', false);
|
|
const switchesAmount = getDeviceChannelCount(uiid);
|
|
|
|
if (error) {
|
|
if (error && parseInt(error) === 401) {
|
|
return device;
|
|
}
|
|
return { error, msg: 'Device does not exist' };
|
|
}
|
|
|
|
return { status: 'ok', switchesAmount };
|
|
}
|
|
|
|
/**
|
|
* Get device firmware version
|
|
*
|
|
* @param deviceId
|
|
*
|
|
* @returns {Promise<{fwVersion: *, status: string}|{msg: string, error: *}>}
|
|
*/
|
|
async getFirmwareVersion(deviceId) {
|
|
const device = await this.getDevice(deviceId);
|
|
const error = _get(device, 'error', false);
|
|
const fwVersion = _get(device, 'params.fwVersion', false);
|
|
|
|
if (error || !fwVersion) {
|
|
if (error && parseInt(error) === 401) {
|
|
return device;
|
|
}
|
|
return { error, msg: 'Device does not exist' };
|
|
}
|
|
|
|
return { status: 'ok', fwVersion };
|
|
}
|
|
}
|
|
|
|
module.exports = eWeLink;
|