Skip to content

Commit

Permalink
feat(localization): add localization support (#6)
Browse files Browse the repository at this point in the history
* Add refrigerator support

* Add method getDevice

* Add dishwasher

* Refactoring

* Add dryer

* Add washer

* Fixed import paths in devices/ac

* Fix Celsius to Fahrenheit convertion table

* Fixed swing mode so they are mapped to the right enum.

* Add the ACDevice to the client loader

* Small fix

* Update ac.ts

* Update client.ts

* All parameters to lowercase

* Add multi language support

* Fix for comments

* Add localization support

* Update README.md

* Add tslint

* Refactoring

* Some fixes for tslint

* Add devices to export

* Fix "[DEP0091] DeprecationWarning: crypto.DEFAULT_ENCODING is deprecated." (nodejs/node#23203)

* Add more options for RefrigeratorDevice

* Small fix for washer

* Add parameter --token into cli

* Add method setOn

* Add OnOffEnum

* Update README.md

* Fix for lookupReference
Add more device types

Co-authored-by: Tom Green <[email protected]>
  • Loading branch information
aklimov-work and squareplanetdesign committed Feb 4, 2020
1 parent 16e7e62 commit e245355
Show file tree
Hide file tree
Showing 25 changed files with 490 additions and 165 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Options:
-v, --version output the version number
-c, --country <type> Country code for account (default: "US")
-l, --language <type> Language code for account (default: "en-US")
-t, --token <type> Refresh token (optional)
-s, --state-path <type> State file path (default: "wideq-state.json")
-h, --help output usage information

Expand Down Expand Up @@ -56,14 +57,14 @@ polling...

## Implementation Status

| *Device* | *Implementation* | *Control* | *Status* |
| *Device* | *Implementation* | *Status* | *Control* |
| --- | --- | --- | --- |
| Dehumidifier | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| AC | :heavy_check_mark: | :warning: needs testing | :warning: needs testing |
| Refrigerator | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Dishwasher | :heavy_check_mark: | :x: | :x: |
| Dryer | :heavy_check_mark: | :x: | :x: |
| Washer | :heavy_check_mark: | :x: | :x: |
| Dishwasher | :heavy_check_mark: | :warning: needs testing | :x: |
| Dryer | :heavy_check_mark: | :warning: needs testing | :x: |
| Washer | :heavy_check_mark: | :warning: needs testing | :x: |

## Credits

Expand Down
29 changes: 17 additions & 12 deletions lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const input = (question: string) => new Promise<string>((resolve) => {
const options = {
country: constants.DEFAULT_COUNTRY,
language: constants.DEFAULT_LANGUAGE,
refreshToken: '',
statePath: 'wideq-state.json',
};

Expand All @@ -32,16 +33,18 @@ program
.on('option:country', (value) => options.country = value)
.option('-l, --language <type>', 'Language code for account', constants.DEFAULT_LANGUAGE)
.on('option:language', (value) => options.language = value)
.option('-t, --token <type>', 'Refresh token')
.on('option:token', (value) => options.refreshToken = value)
.option('-s, --state-path <type>', 'State file path', 'wideq-state.json')
.on('option:statePath', (value) => options.statePath = value);

program
.command('auth')
.description('Authenticate')
.action(async () => {
const { country, language, statePath } = options;
const { country, language, refreshToken, statePath } = options;

const client = await init(country, language, statePath);
const client = await init(country, language, refreshToken, statePath);

console.info('Refresh token: ' + client.auth.refreshToken);

Expand All @@ -53,8 +56,8 @@ program
.command('ls', { isDefault: true })
.description('List devices')
.action(async () => {
const { country, language, statePath } = options;
const client = await init(country, language, statePath);
const { country, language, refreshToken, statePath } = options;
const client = await init(country, language, refreshToken, statePath);

for (const device of client.devices) {
console.info(String(device));
Expand All @@ -68,8 +71,8 @@ program
.command('monitor <deviceId>')
.description('Monitor any device, displaying generic information about its status.')
.action(async (deviceId: string) => {
const { country, language, statePath } = options;
const client = await init(country, language, statePath);
const { country, language, refreshToken, statePath } = options;
const client = await init(country, language, refreshToken, statePath);

const dev = await client.getDevice(deviceId);
saveState(statePath, client);
Expand Down Expand Up @@ -121,7 +124,7 @@ async function authenticate(gateway: Gateway) {
return Auth.fromUrl(gateway, callbackUrl);
}

async function init(country: string, language: string, stateFilePath?: string) {
async function init(country: string, language: string, refreshToken: string, stateFilePath?: string) {
let state: any = {};

if (stateFilePath) {
Expand All @@ -132,12 +135,14 @@ async function init(country: string, language: string, stateFilePath?: string) {
}
}

const client = Client.loadFromState({
country,
language,
const client = refreshToken ?
await Client.loadFromToken(refreshToken, country, language) :
Client.loadFromState({
country,
language,

...state,
});
...state,
});

if (!client.gateway) {
client.gateway = await Gateway.discover(country, language);
Expand Down
55 changes: 47 additions & 8 deletions lib/client.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { ACDevice } from './devices/ac';
import { DishwasherDevice } from './devices/dishwasher';
import { Auth } from './core/auth';
import { Gateway } from './core/gateway';
import { Session } from './core/session';
import * as constants from './core/constants';
import { DeviceType } from './core/constants';
import { Device } from './core/device';
import { DeviceInfo } from './core/device-info';
import { Gateway } from './core/gateway';
import { LangPackModel } from './core/lang-pack-model';
import { LangPackProduct } from './core/lang-pack-product';
import { ModelInfo } from './core/model-info';
import { Session } from './core/session';
import { ACDevice } from './devices/ac';
import { DehumidifierDevice } from './devices/dehumidifier';
import { RefrigeratorDevice } from './devices/refrigerator';
import { DishwasherDevice } from './devices/dishwasher';
import { DryerDevice } from './devices/dryer';
import { RefrigeratorDevice } from './devices/refrigerator';
import { WasherDevice } from './devices/washer';
import { DeviceType } from './core/constants';

export class Client {
public devices: DeviceInfo[] = [];
public modelInfo: { [key: string]: any } = {};
public langPackProduct: { [key: string]: any } = {};
public langPackModel: { [key: string]: any } = {};

public constructor(
public gateway: Gateway,
Expand All @@ -37,14 +41,16 @@ export class Client {
}

public static loadFromState(state: {
[key in 'gateway' | 'auth' | 'session' | 'modelInfo' | 'country' | 'language']: any;
[key in 'gateway' | 'auth' | 'session' | 'modelInfo' | 'country' | 'language' | 'langPackProduct' | 'langPackModel']: any;
}) {
let gateway: Gateway;
let auth: Auth;
let session: Session;
let modelInfo: Client['modelInfo'] = {};
let country: string = constants.DEFAULT_COUNTRY;
let language: string = constants.DEFAULT_LANGUAGE;
let langPackProduct: Client['langPackProduct'] = {};
let langPackModel: Client['langPackModel'] = {};

for (const key of Object.keys(state)) {
switch (key) {
Expand Down Expand Up @@ -83,6 +89,14 @@ export class Client {
case 'language':
language = state.language;
break;

case 'langPackProduct':
langPackProduct = state.langPackProduct;
break;

case 'langPackModel':
langPackModel = state.langPackModel;
break;
}
}

Expand All @@ -94,6 +108,8 @@ export class Client {
language,
);
client.modelInfo = modelInfo;
client.langPackProduct = langPackProduct;
client.langPackModel = langPackModel;

return client;
}
Expand Down Expand Up @@ -125,6 +141,9 @@ export class Client {

country: this.country,
language: this.language,

langPackProduct: this.langPackProduct,
langPackModel: this.langPackModel,
};
}

Expand All @@ -149,7 +168,9 @@ export class Client {
throw new Error(`Device not found: ${deviceInfo}`);
}

const modelInfo = await this.getModelInfo(deviceInfo);
await this.getModelInfo(deviceInfo);
await this.getLangPackProduct(deviceInfo);
await this.getLangPackModel(deviceInfo);

switch (deviceInfo.data.deviceType) {
case DeviceType.AC:
Expand Down Expand Up @@ -187,4 +208,22 @@ export class Client {

return new ModelInfo(this.modelInfo[url]);
}

public async getLangPackProduct(device: DeviceInfo) {
const url = device.langPackProductUrl;
if (!(url in this.langPackProduct)) {
this.langPackProduct[url] = await device.loadLangPackProduct();
}

return new LangPackProduct(this.langPackProduct[url]);
}

public async getLangPackModel(device: DeviceInfo) {
const url = device.langPackModelUrl;
if (!(url in this.langPackModel)) {
this.langPackModel[url] = await device.loadLangPackModel();
}

return new LangPackModel(this.langPackModel[url]);
}
}
14 changes: 7 additions & 7 deletions lib/core/auth.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { TokenError } from './errors';
import { Session } from './session';
import { URL, resolve as resolveUrl } from 'url';
import * as qs from 'qs';
import * as assert from 'assert';
import * as crypto from 'crypto';
import crypto from 'crypto';
import { DateTime } from 'luxon';
import * as qs from 'qs';
import { resolve as resolveUrl, URL } from 'url';

import { Gateway } from './gateway';
import * as constants from './constants';
import { TokenError } from './errors';
import { Gateway } from './gateway';
import { requestClient } from './request';
import { DateTime } from 'luxon';
import { Session } from './session';

export class Auth {
public constructor(
Expand Down
13 changes: 9 additions & 4 deletions lib/core/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ export enum DeviceType {
MICROWAVE = 302,
COOKTOP = 303,
HOOD = 304,
/** Includes heat pumps, etc., possibly all HVAC devices. */
AC = 401,
AC = 401, // Includes heat pumps, etc., possibly all HVAC devices.
AIR_PURIFIER = 402,
DEHUMIDIFIER = 403,
/** Robot vacuum cleaner? */
ROBOT_KING = 501,
ROBOT_KING = 501, // This is Robotic vacuum cleaner
TV = 701,
BOILER = 801,
SPEAKER = 901,
HOMEVU = 902,
ARCH = 1001,
MISSG = 3001,
SENSOR = 3002,
Expand All @@ -38,4 +40,7 @@ export enum DeviceType {
IOT_DUST_SENSOR = 3006,
EMS_AIR_STATION = 4001,
AIR_SENSOR = 4003,
PURICARE_AIR_DETECTOR = 4004,
V2PHONE = 6001,
HOMEROBOT = 9000,
}
23 changes: 23 additions & 0 deletions lib/core/device-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export interface DeviceData {
modelNm: string;
deviceId: string;
modelJsonUrl: string;
langPackProductTypeUri: string;
langPackModelUri: string;
macAddress: string;
alias: string;
deviceType: DeviceType;
}
Expand All @@ -19,6 +22,14 @@ export class DeviceInfo {
return requestClient.get(this.modelInfoUrl).then(resp => resp.data);
}

public async loadLangPackProduct() {
return this.langPackProductUrl && requestClient.get(this.langPackProductUrl).then(resp => resp.data);
}

public async loadLangPackModel() {
return this.langPackModelUrl && requestClient.get(this.langPackModelUrl).then(resp => resp.data);
}

public get modelId() {
return this.data.modelNm;
}
Expand All @@ -31,6 +42,18 @@ export class DeviceInfo {
return this.data.modelJsonUrl;
}

public get langPackProductUrl() {
return this.data.langPackProductTypeUri;
}

public get langPackModelUrl() {
return this.data.langPackModelUri;
}

public get macAddress() {
return this.data.macAddress;
}

public get name() {
return this.data.alias;
}
Expand Down
11 changes: 11 additions & 0 deletions lib/core/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@ import { Monitor } from './monitor';

import { Client } from '../client';
import { DeviceInfo } from './device-info';
import { LangPackModel } from './lang-pack-model';
import { LangPackProduct } from './lang-pack-product';
import { ModelInfo } from './model-info';

export enum OnOffEnum {
OFF = '@CP_OFF_EN_W',
ON = '@CP_ON_EN_W',
}

export class Device {
public model!: ModelInfo;
public langPackProduct!: LangPackProduct;
public langPackModel!: LangPackModel;
public monitor?: Monitor;

public constructor(
Expand All @@ -20,6 +29,8 @@ export class Device {

public async load() {
this.model = await this.client.getModelInfo(this.device);
this.langPackProduct = await this.client.getLangPackProduct(this.device);
this.langPackModel = await this.client.getLangPackModel(this.device);
}

public async setControl(key: string, value: any) {
Expand Down
4 changes: 2 additions & 2 deletions lib/core/gateway.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as url from 'url';
import * as qs from 'qs';
import * as url from 'url';

import { GATEWAY_URL } from './constants';
import { requestClient } from './request';
import * as constants from './constants';
import { requestClient } from './request';

export class Gateway {
public constructor(
Expand Down
Loading

0 comments on commit e245355

Please sign in to comment.