Skip to content

Commit 098011b

Browse files
Release v2.0.0 (#44)
* Added arpTableSolver (#18) * Added arpTableSolver * fix package import * linting class * changed arp library * refactor arp class * using arpping fork * refactor arpTableSolver class * Added Zero Conf functionality (LAN mode) (#46) * added crypto-js * zeroconf helper functions * zeroconf update payload * new method to save devices cache file * class renamed * refactor Zeroconf class * return cached device if exists * moved method to get local ip address * fix mac addresses without leading zeroes * refactor Zeroconf class * using new zeroconf functionality * zeroconf working with single and multichannel devices * save device mixin enhancement * working on zeroconf test cases * catch errors on filesystem methods * zeroconf: added extra test cases * better error handling * zeroconf: 100% code coverage * removed deprecated login method * updates on credentials file * version bump * Docs for v2.0 (#52) * added v1 docs * added zeroconf docs * updated readme * docs updated * removed zeroconf article warning * updated vscode config Co-authored-by: Luis Llamas <luisllamas@hotmail.com>
1 parent 6e240c1 commit 098011b

55 files changed

Lines changed: 1227 additions & 60 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,9 @@ typings/
9090

9191
# End of https://www.gitignore.io/api/node
9292

93-
.idea/
93+
.idea/
94+
95+
demo.js
96+
arp-table.json
97+
devices-cache.json
98+
test/_setup/credentials.js

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@
22
"eslint.validate": [
33
"javascript"
44
],
5-
"eslint.autoFixOnSave": true
5+
"eslint.autoFixOnSave": true,
6+
"editor.codeActionsOnSave": {
7+
"source.fixAll.eslint": true
8+
}
69
}

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
* set on/off devices
88
* get power consumption on devices like Sonoff POW
99
* listen for devices events
10+
* using zeroconf (LAN mode), no internet connection required
1011

1112

1213
## Installation
13-
``` sh
14+
```sh
1415
npm install ewelink-api
1516
```
1617

1718

1819
## Usage
19-
Check docs at https://ewelink-api.now.sh
20+
Check library documentation and examples at https://github.com/skydiver/ewelink-api/docs
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const rp = require('request-promise');
2+
3+
const WebSocket = require('../WebSocket');
4+
const payloads = require('../../lib/payloads');
5+
const { _get } = require('../../lib/helpers');
6+
7+
class ChangeStateZeroconf extends WebSocket {
8+
static async set({ url, device, params, switches, state }) {
9+
const selfApikey = device.apikey;
10+
const deviceId = device.deviceid;
11+
const deviceKey = device.devicekey;
12+
13+
const endpoint = switches ? 'switches' : 'switch';
14+
const localUrl = `${url}/${endpoint}`;
15+
16+
const body = payloads.zeroConfUpdatePayload(
17+
selfApikey,
18+
deviceId,
19+
deviceKey,
20+
params
21+
);
22+
23+
const response = await rp({
24+
method: 'POST',
25+
uri: localUrl,
26+
body,
27+
json: true,
28+
});
29+
30+
const error = _get(response, 'error', false);
31+
32+
if (error === 403) {
33+
return { error, msg: response.reason };
34+
}
35+
36+
return { status: 'ok', state };
37+
}
38+
}
39+
40+
module.exports = ChangeStateZeroconf;

classes/PowerState/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
const ChangeState = require('./ChangeState');
2+
const ChangeStateZeroconf = require('./ChangeStateZeroconf');
23

34
module.exports = {
45
ChangeState,
6+
ChangeStateZeroconf,
57
};

classes/Zeroconf.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
const fs = require('fs');
2+
const arpping = require('arpping')({});
3+
4+
class Zeroconf {
5+
/**
6+
* Build the ARP table
7+
* @param ip
8+
* @returns {Promise<unknown>}
9+
*/
10+
static getArpTable(ip = null) {
11+
return new Promise((resolve, reject) => {
12+
arpping.discover(ip, (err, hosts) => {
13+
if (err) {
14+
return reject(err);
15+
}
16+
const arpTable = Zeroconf.fixMacAddresses(hosts);
17+
return resolve(arpTable);
18+
});
19+
});
20+
}
21+
22+
/**
23+
* Sometime arp command returns mac addresses without leading zeroes.
24+
* @param hosts
25+
*/
26+
static fixMacAddresses(hosts) {
27+
return hosts.map(host => {
28+
const octets = host.mac.split(':');
29+
30+
const fixedMac = octets.map(octet => {
31+
if (octet.length === 1) {
32+
return `0${octet}`;
33+
}
34+
return octet;
35+
});
36+
37+
return {
38+
ip: host.ip,
39+
mac: fixedMac.join(':'),
40+
};
41+
});
42+
}
43+
44+
/**
45+
* Save ARP table to local file
46+
* @param config
47+
* @returns {Promise<{error: string}|{file: {request: string; resolved: string} | any | string | string, status: string}>}
48+
*/
49+
static async saveArpTable(config = {}) {
50+
const ip = config.ip || null;
51+
const fileName = config.file || './arp-table.json';
52+
try {
53+
const arpTable = await Zeroconf.getArpTable(ip);
54+
const jsonContent = JSON.stringify(arpTable, null, 2);
55+
fs.writeFileSync(fileName, jsonContent, 'utf8');
56+
return { status: 'ok', file: fileName };
57+
} catch (e) {
58+
return { error: e.toString() };
59+
}
60+
}
61+
62+
/**
63+
* Read ARP table file
64+
* @param fileName
65+
* @returns {Promise<{error: string}|any>}
66+
*/
67+
static async loadArpTable(fileName = './arp-table.json') {
68+
try {
69+
const jsonContent = await fs.readFileSync(fileName);
70+
return JSON.parse(jsonContent);
71+
} catch (e) {
72+
return { error: e.toString() };
73+
}
74+
}
75+
76+
/**
77+
* Read devices cache file
78+
* @param fileName
79+
* @returns {Promise<{error: string}>}
80+
*/
81+
static async loadCachedDevices(fileName = './devices-cache.json') {
82+
try {
83+
const jsonContent = await fs.readFileSync(fileName);
84+
return JSON.parse(jsonContent);
85+
} catch (e) {
86+
return { error: e.toString() };
87+
}
88+
}
89+
}
90+
91+
module.exports = Zeroconf;

docs/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Documentation
2+
3+
* [Introduction](introduction.md)
4+
* [Quickstart](quickstart.md)
5+
* [Class Instantiation](class-instantiation.md)
6+
* [Demos](demos/README.md)
7+
* [node script](demos/node.md)
8+
* [serverless](demos/serverless.md)
9+
* [Available Methods](available-methods/README.md)
10+
* [getCredentials](available-methods/getcredentials.md)
11+
* [openWebSocket](available-methods/openwebsocket.md)
12+
* [getDevice](available-methods/getdevice.md)
13+
* [getDevices](available-methods/getdevices.md)
14+
* [getDevicePowerState](available-methods/getdevicepowerstate.md)
15+
* [setDevicePowerState](available-methods/setdevicepowerstate.md)
16+
* [toggleDevice](available-methods/toggledevice.md)
17+
* [getDevicePowerUsage](available-methods/getdevicepowerusage.md)
18+
* [getDeviceCurrentTH](available-methods/getdevicecurrentth.md)
19+
* [getDeviceCurrentTemperature](available-methods/getdevicecurrenttemperature.md)
20+
* [getDeviceCurrentHumidity](available-methods/getdevicecurrenthumidity.md)
21+
* [getDeviceChannelCount](available-methods/getdevicechannelcount.md)
22+
* [getRegion](available-methods/getregion.md)
23+
* [getFirmwareVersion](available-methods/getfirmwareversion.md)
24+
* [saveDevicesCache](available-methods/savedevicescache.md)
25+
* [login](available-methods/login.md) <sup>_*deprecated_</sup>
26+
* [Zeroconf (LAN mode)](zeroconf.md)
27+
* [Testing](testing.md)

docs/available-methods/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Available Methods
2+
3+
Here is the list of available methods.
4+
5+
* [getCredentials](getcredentials.md)
6+
* [openWebSocket](openwebsocket.md)
7+
* [getDevice](getdevice.md)
8+
* [getDevices](getdevices.md)
9+
* [getDevicePowerState](getdevicepowerstate.md)
10+
* [setDevicePowerState](setdevicepowerstate.md)
11+
* [toggleDevice](toggledevice.md)
12+
* [getDevicePowerUsage](getdevicepowerusage.md)
13+
* [getDeviceCurrentTH](getdevicecurrentth.md)
14+
* [getDeviceCurrentTemperature](getdevicecurrenttemperature.md)
15+
* [getDeviceCurrentHumidity](getdevicecurrenthumidity.md)
16+
* [getDeviceChannelCount](getdevicechannelcount.md)
17+
* [getRegion](getregion.md)
18+
* [getFirmwareVersion](getfirmwareversion.md)
19+
* [saveDevicesCache](savedevicescache.md)
20+
* [login](login.md) <sup>_*deprecated_</sup>
21+
22+
Remember to instantiate class before usage.
23+
24+
Also, take a look at the provided demos for [node script](../demos/node.md) and [serverless](../demos/serverless.md).
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# getCredentials
2+
3+
Get your access token, api key and region.
4+
5+
This method is useful on serverless context, where you need to obtain auth credentials to make individual requests.
6+
7+
8+
### Usage
9+
```
10+
const auth = await connection.getCredentials();
11+
12+
console.log('access token: ', auth.at);
13+
console.log('api key: ', auth.user.apikey);
14+
console.log('region: ', auth.region);
15+
16+
```
17+
18+
<sup>* _Remember to instantiate class before use_</sup>
19+
20+
> Access token and api key will be invalidate after you login again using email and password.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# getDevice
2+
3+
Return information for specified device.
4+
5+
6+
### Usage
7+
```
8+
/* get specific device information */
9+
const device = await connection.getDevice('<your device id>');
10+
console.log(device);
11+
```
12+
13+
<sup>* _Remember to instantiate class before use_</sup>

0 commit comments

Comments
 (0)