Skip to content
This repository was archived by the owner on Nov 21, 2024. It is now read-only.

Commit db78231

Browse files
authored
Merge pull request #6 from basvanrooten/develop
hwl-api v1.1
2 parents 172e274 + 8d4a43d commit db78231

File tree

11 files changed

+305
-733
lines changed

11 files changed

+305
-733
lines changed

README.md

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,40 @@ HomeWizard Lite Proxy API, built for integrating cheap HomeWizard Lite smartplug
88
$ npm start
99
$ node index.js
1010

11-
The API depends on environment variables for authentication with HomeWizard API.
12-
13-
| Key | Value |
14-
|--|--|
15-
| PORT | Port (default 3000) |
16-
| HWL_USERNAME | HomeWizard Lite App Username |
17-
| HWL_PASSWORD | HomeWizard Lite App Password |
18-
| LOGLEVEL | [Tracer Logger Level](https://github.com/baryon/tracer#customize-output-format) (default trace) |
11+
The API depends on environment variables. **Make sure to configure the bold variables, as these have no default**.
12+
13+
| Key | Type | Value |
14+
|--|--|--|
15+
| PORT | number | Port on which the API will listen (default: 3000) |
16+
| | | |
17+
| **HWL_USERNAME** | string | HomeWizard Lite App Username |
18+
| **HWL_PASSWORD** | string | HomeWizard Lite App Password |
19+
| | | |
20+
| **SMARTPLUG_ID** | string | ID of the Internet Connected Smartplug. GET this ID from /api/smartplug |
21+
| MIN_DIMMING_VALUE | number | The API supports dimmers. Some dimmers have a certain minimum threshold. When you request the API to go below the minimum dimming value, it'll instead turn off the light. (default: 1) |
22+
| | | |
23+
| CACHE_TTL | number | Default expiration time for cache objects in seconds. Currently only in use for caching session-tokens from HWL. (default: 1800)
24+
| LOGLEVEL | string | [Tracer Logger Level](https://github.com/baryon/tracer#customize-output-format) (default: warn) |
25+
| | | |
1926

2027
## Deploying
21-
The API should be deployed on a local network, because it has no built-in authentication. It is always possible to protect the API with HTTP Basic Auth and make it accessible over the internet, but take note of the insecurities of HTTP Basic Auth.
22-
23-
The API could also be deployed in a docker container
24-
25-
## Requests
26-
|Method | URL | Description |
27-
| -- | -- | -- |
28-
| GET | /api/test/session | Returns valid session key (Only necessary for testing)* |
29-
| GET | /api/test/communication | Returns status 200 when API can connect to Google Test API to verify internet connectivity |
30-
| -- | -- | -- |
31-
| GET | /api/plug | Returns all registered plugs |
32-
| GET | /api/act/smartplug/:smartPlugID/plug/:plugID | Returns `true` or `false` for is_active state of a plug specified by plugID |
33-
| POST | /api/act/smartplug/:smartPlugID/plug/:plugID | Switches plug when request body contains `"action": "on"` or `"action": "off"`. Returns is_active state.
34-
28+
* The API should be deployed on a local network and shouldn't be accessible over the interwebs, because it currently has no built-in authentication. It is always possible to configure [HTTP Basic Authentication](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/).
29+
* The API should have access to homewizard.com and its subdomains.
30+
31+
Using the included Dockerfile, it is also possible to run this API inside a container.
32+
33+
## Endpoints
34+
| Method | URL | Description | Example Request | Example Response |
35+
| -- | -- | -- | -- | -- |
36+
| GET | /api/test/session | Returns valid session key (Only necessary for testing) | | `{ "session": "string"` } |
37+
| GET | /api/test/communication | Returns status 200 when API can connect to Google to verify internet connectivity |
38+
| | | |
39+
| GET | /api/plug | Returns all registered plugs | | * |
40+
| GET | /api/smartplug | Returns info about your internet connected smartplug. (Tip: Use this to determine the ID of your smartplug. Use the `id` property.) | | * |
41+
| GET | /api/plug/:plugID | Returns `true` or `false` for is_active state of a plug specified by plugID | | `{ "is_active": false }` |
42+
| POST | /api/plug/:plugID | [DIMMER] Change dimmer state when request body contains `"type": "dimmer"` and `"value": number`. When value is below `MIN_DIMMING_VALUE`, the dimmer will switch off. Returns `is_active state`. | `{ "type": "dimmer", "value": 75 }` | `{ "is_active": true }` |
43+
| | | [SWITCH] Switches plug when request body contains `"type": "switch"` and `"value": "on/off"`. Returns `is_active` state |`{ "type": "switch", "value": "on" }` | `{ "is_active": true }` |
44+
45+
`*` Try this yourself. Response contains a bunch of useful info and does not fit needly in this table.
3546
## Contribution
3647
This project was started by an IT-student as a small 2 day project to improve home-automation by enabling Home Assistant to control HomeWizard Lite plugs. Development will continue when I need more features. Contribution is appreciated.

app.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ const express = require('express');
33
const cors = require('cors');
44
const bodyParser = require('body-parser');
55
const morgan = require("morgan");
6-
const config = require('./config/config');
7-
const logger = require('./config/config').logger
86

97
// Import models
108
const ApiResponse = require('./models/ApiResponse');

auth/authentication_manager.js

Lines changed: 40 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,48 @@
1-
const logger = require('../config/config').logger
2-
const config = require('../config/config');
3-
var sha1 = require('sha1');
4-
const axios = require('axios');
1+
const logger = require("../config/config").logger;
2+
const config = require("../config/config");
3+
var sha1 = require("sha1");
4+
const axios = require("axios");
5+
const NodeCache = require("node-cache");
56

6-
module.exports = {
7+
const cache = new NodeCache({ stdTTL: config.cacheTTL });
78

9+
module.exports = {
810
// Get Session Key from HomeWizard Lite
911
getSessionKey() {
10-
11-
// Get Username and Password from config file
12-
let username = config.hwlUsername;
13-
let password = sha1(config.hwlPassword);
14-
logger.debug(username);
15-
logger.debug(password);
16-
17-
// Login to HWL using user credentials
18-
return axios({
19-
method: 'get',
20-
url: 'https://cloud.homewizard.com/account/login',
12+
// Check if sessionkey is cached
13+
let sessionKey = cache.get("session");
14+
15+
if (sessionKey) {
16+
logger.debug(`Cached session key found: ${sessionKey}`);
17+
return Promise.resolve(sessionKey);
18+
} else {
19+
// Session key is not found in cache
20+
let username = config.hwlUsername;
21+
let password = sha1(config.hwlPassword);
22+
logger.debug(username);
23+
logger.debug(password);
24+
25+
return axios({
26+
method: "get",
27+
url: "https://cloud.homewizard.com/account/login",
2128
auth: {
2229
username: username,
23-
password: password
24-
}
30+
password: password,
31+
},
2532
})
26-
.then(response => {
27-
28-
// Check status
29-
if (response.data.status === "ok") {
30-
31-
// Authentication passed, returning session token
32-
logger.debug(response.data.session);
33+
.then((response) => {
34+
logger.debug(
35+
`Received session key: ${response.data.session}`
36+
);
37+
cache.set("session", response.data.session);
3338
return response.data.session;
34-
35-
} else if (response.data.status === "failed" && response.data.error === 110) {
36-
37-
// Authentication failed, returning FAILED
38-
logger.error("HWL returned invalid credentials! Check credentials for validity!")
39-
logger.debug(response.data);
40-
return "ERROR";
41-
42-
} else {
43-
44-
// Authentication failed, but with unknown reason
45-
logger.error("HWL returned unknown authentication error. ERROR :", response.data);
46-
return "ERROR";
47-
}
48-
})
49-
.catch(e => {
50-
// Cannot communicate with HWL, returning error
51-
logger.error("Failed to communicate with HWL! ERROR: ", e.message);
52-
return "ERROR";
53-
});
54-
}
55-
56-
}
39+
})
40+
.catch((e) => {
41+
// Exception occured while trying to communicate with HWL.
42+
logger.error("Can't get session key from HW");
43+
logger.error(e.response);
44+
return Promise.reject("Can't get session key from HW. Check logs");
45+
});
46+
}
47+
},
48+
};

config/config.js

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,33 @@
11
// Set debug level for tracer
2-
const loglevel = process.env.LOGLEVEL || 'trace'
2+
const loglevel = process.env.LOGLEVEL || "warn";
33

44
module.exports = {
5-
65
// Web port where the server will listen to
7-
"webPort": process.env.PORT || 3000,
8-
"authKey": process.env.AUTHKEY || "",
6+
webPort: process.env.PORT || 3000,
97

108
// Authentication for Homewizard Lite API
11-
"hwlUsername": process.env.HWL_USERNAME || "",
12-
"hwlPassword": process.env.HWL_PASSWORD || "",
9+
hwlUsername: process.env.HWL_USERNAME || "",
10+
hwlPassword: process.env.HWL_PASSWORD || "",
11+
12+
// SmartPlugID
13+
// Should be returned from HW when you GET on /api/smartplug
14+
smartPlugId:
15+
process.env.SMARTPLUG_ID || "",
1316

17+
// Dimmer Ranges
18+
// Some dimmers only support dimming up to a certain amount.
19+
minDimmingValue: process.env.MIN_DIMMING_VALUE || 1,
20+
21+
// Cache Time To Live
22+
cacheTTL: process.env.CACHE_TTL || 1800,
1423

1524
// Tracer for logging purposes
16-
logger: require('tracer')
17-
.console({
18-
format: [
19-
"{{timestamp}} <{{title}}> {{file}}:{{line}} : {{message}}"
20-
],
25+
logger: require("tracer").console({
26+
format: ["{{timestamp}} <{{title}}> {{file}}:{{line}} : {{message}}"],
2127
preprocess: function (data) {
2228
data.title = data.title.toUpperCase();
2329
},
2430
dateformat: "isoUtcDateTime",
25-
level: loglevel
26-
})
27-
28-
}
31+
level: loglevel,
32+
}),
33+
};

0 commit comments

Comments
 (0)