myenergi-api 0.0.4-0 → 0.0.7-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -6
- package/src/Digest.ts +138 -0
- package/src/MyEnergi.ts +36 -2
- package/tests/MyEnergi-spec.ts +13 -0
- package/tests/debug.ts +18 -0
- package/tsconfig.testing.json +14 -0
- package/dist/MyEnergi.d.ts +0 -4
- package/dist/MyEnergi.js +0 -16
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/npm-publish.sh +0 -73
- package/src/types/digest-fetch.d.ts +0 -1
- package/tsconfig.json +0 -15
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myenergi-api",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7-0",
|
|
4
4
|
"description": "NodeJS implementation of MyEnergi API for controlling Zappi, Eddi and other MyEnergi products",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
|
-
"test": "
|
|
9
|
+
"test": "env TS_NODE_PROJECT='tsconfig.testing.json' mocha -r ts-node/register tests/**/*-spec.ts"
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
@@ -24,11 +24,18 @@
|
|
|
24
24
|
"url": "https://github.com/bisand/myenergi-api/issues"
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/bisand/myenergi-api#readme",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"dotenv": "^10.0.0",
|
|
29
|
+
"node-digest-auth-client": "^1.0.6"
|
|
30
|
+
},
|
|
27
31
|
"devDependencies": {
|
|
28
|
-
"@types/
|
|
32
|
+
"@types/mocha": "^9.0.0",
|
|
33
|
+
"@types/node": "^16.11.9",
|
|
34
|
+
"assert": "^2.0.0",
|
|
35
|
+
"chai": "^4.3.4",
|
|
36
|
+
"mocha": "^9.1.3",
|
|
37
|
+
"ts-node": "^10.4.0",
|
|
38
|
+
"tslint": "^6.1.3",
|
|
29
39
|
"typescript": "^4.5.2"
|
|
30
|
-
},
|
|
31
|
-
"dependencies": {
|
|
32
|
-
"digest-fetch": "^1.2.1"
|
|
33
40
|
}
|
|
34
41
|
}
|
package/src/Digest.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import * as https from 'https';
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
import { RequestOptions } from 'https';
|
|
4
|
+
import * as url from "url";
|
|
5
|
+
|
|
6
|
+
export class Digest {
|
|
7
|
+
private _username: string;
|
|
8
|
+
private _password: string;
|
|
9
|
+
private _authorization: string | undefined;
|
|
10
|
+
|
|
11
|
+
constructor(username: string, password: string) {
|
|
12
|
+
this._username = username;
|
|
13
|
+
this._password = password;
|
|
14
|
+
this._authorization = '';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
private md5(data: string) {
|
|
18
|
+
let md5 = crypto.createHash('md5');
|
|
19
|
+
let result = md5.update(data).digest('hex');
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private request(options: RequestOptions, data?: any): Promise<any> {
|
|
24
|
+
return new Promise<any>((resolve, reject) => {
|
|
25
|
+
const req = https.request(options, (res: any) => {
|
|
26
|
+
let resData: string = '';
|
|
27
|
+
|
|
28
|
+
res.setEncoding('utf8');
|
|
29
|
+
res.on('data', (chunk: string) => {
|
|
30
|
+
resData += chunk;
|
|
31
|
+
});
|
|
32
|
+
res.on('end', () => {
|
|
33
|
+
resolve(resData);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
req.on('error', (e: any) => {
|
|
38
|
+
console.error(`problem with request: ${e.message}`);
|
|
39
|
+
reject(e);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (data) {
|
|
43
|
+
req.write(data);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
req.end();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private requestDigest(options: RequestOptions, data?: any): Promise<any> {
|
|
51
|
+
return new Promise<any>((resolve, reject) => {
|
|
52
|
+
let resData: string = '';
|
|
53
|
+
const req = https.request(options, (res: any) => {
|
|
54
|
+
|
|
55
|
+
if (res.statusCode == 401) {
|
|
56
|
+
let cnonce = this.md5(String(new Date().getTime()));
|
|
57
|
+
let auth = res.headers["www-authenticate"];
|
|
58
|
+
let realm, nonce, qop;
|
|
59
|
+
let authSplit = auth?.split(",") as string[];
|
|
60
|
+
|
|
61
|
+
for (let item of authSplit) {
|
|
62
|
+
if (item.indexOf("realm=") >= 0) {
|
|
63
|
+
let realmSplit = item.split("=\"");
|
|
64
|
+
realm = realmSplit[realmSplit.length - 1];
|
|
65
|
+
realm = realm.substring(0, realm.length - 1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (item.indexOf("nonce=") >= 0) {
|
|
69
|
+
let nonceSplit = item.split("=\"");
|
|
70
|
+
nonce = nonceSplit[nonceSplit.length - 1];
|
|
71
|
+
nonce = nonce.substring(0, nonce.length - 1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (item.indexOf("qop=") >= 0) {
|
|
75
|
+
let qopSplit = item.split("=\"");
|
|
76
|
+
qop = qopSplit[qopSplit.length - 1];
|
|
77
|
+
qop = qop.substring(0, qop.length - 1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let HA1 = this.md5(this._username + ":" + realm + ":" + this._password);
|
|
82
|
+
let HA2 = this.md5(options.method + ":" + options.path);
|
|
83
|
+
let response = this.md5(HA1 + ":" + nonce + ":00000001:" + cnonce + ":" + qop + ":" + HA2);
|
|
84
|
+
if (!options.headers)
|
|
85
|
+
options.headers = {};
|
|
86
|
+
this._authorization = "Digest username=\"" + this._username + "\",realm=\"" + realm + "\",nonce=\"" + nonce + "\",uri=\"" + options.path + "\",cnonce=\"" + cnonce + "\",nc=00000001,algorithm=MD5,response=\"" + response + "\",qop=\"" + qop + "\"";
|
|
87
|
+
options.headers.Authorization = this._authorization;
|
|
88
|
+
return this.requestDigest(options, data).then(value => {
|
|
89
|
+
resolve(value);
|
|
90
|
+
}).catch(resaon => {
|
|
91
|
+
reject(resaon);
|
|
92
|
+
});
|
|
93
|
+
} else if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
94
|
+
res.setEncoding('utf8');
|
|
95
|
+
res.on('data', (chunk: string) => {
|
|
96
|
+
resData += chunk;
|
|
97
|
+
});
|
|
98
|
+
res.on('end', () => {
|
|
99
|
+
resolve(resData);
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
console.error("status code failed!!");
|
|
103
|
+
reject("status code failed!!");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
req.on('error', (e: any) => {
|
|
109
|
+
console.error(`problem with request: ${e.message}`);
|
|
110
|
+
reject(e);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (data) {
|
|
114
|
+
req.write(data);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
req.end();
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public get(requestUrl: string, data?: any): Promise<any> {
|
|
122
|
+
const uri = url.parse(requestUrl);
|
|
123
|
+
const options: RequestOptions = {
|
|
124
|
+
hostname: uri.hostname,
|
|
125
|
+
port: uri.port,
|
|
126
|
+
path: uri.pathname,
|
|
127
|
+
method: 'GET',
|
|
128
|
+
headers: {
|
|
129
|
+
'Connection': 'Keep-Alive',
|
|
130
|
+
'Content-Type': 'text/plain',
|
|
131
|
+
'Host': uri.hostname as string,
|
|
132
|
+
'Authorization': this._authorization
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
return this.requestDigest(options, data);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
}
|
package/src/MyEnergi.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Digest } from './Digest';
|
|
2
2
|
|
|
3
|
+
export enum ZappiChargeMode {
|
|
4
|
+
Off = 4,
|
|
5
|
+
Fast = 1,
|
|
6
|
+
Eco = 2,
|
|
7
|
+
EcoPlus = 3
|
|
8
|
+
}
|
|
9
|
+
export enum ZappiBoostMode {
|
|
10
|
+
Fast = 0,
|
|
11
|
+
Eco = 1,
|
|
12
|
+
EcoPlus = 2
|
|
13
|
+
}
|
|
3
14
|
export class MyEnergi {
|
|
4
15
|
private _config = {
|
|
5
16
|
username: '',
|
|
@@ -8,12 +19,35 @@ export class MyEnergi {
|
|
|
8
19
|
zappi_url: 'https://s18.myenergi.net/cgi-jstatus-Z',
|
|
9
20
|
harvi_url: 'https://s18.myenergi.net/cgi-jstatus-H',
|
|
10
21
|
status_url: 'https://s18.myenergi.net/cgi-jstatus-*',
|
|
11
|
-
dayhour_url: 'https://s18.myenergi.net/cgi-jdayhour-'
|
|
22
|
+
dayhour_url: 'https://s18.myenergi.net/cgi-jdayhour-',
|
|
23
|
+
zappi_mode_url: 'https://s18.myenergi.net/cgi-zappi-mode-Z'
|
|
12
24
|
//https://s18.myenergi.net/cgi-jdayhour-Znnnnnnnn-YYYY-MM-DD
|
|
13
25
|
}
|
|
26
|
+
private _client: any;
|
|
27
|
+
private _asn: any;
|
|
14
28
|
|
|
15
29
|
constructor(username: string, password: string) {
|
|
16
30
|
this._config.username = username;
|
|
17
31
|
this._config.password = password;
|
|
18
32
|
}
|
|
33
|
+
|
|
34
|
+
public async getStatus(): Promise<any> {
|
|
35
|
+
const digest = new Digest(this._config.username, this._config.password);
|
|
36
|
+
const data = await digest.get(this._config.status_url);
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public async getZappi(): Promise<any> {
|
|
41
|
+
const digest = new Digest(this._config.username, this._config.password);
|
|
42
|
+
const data = await digest.get(this._config.zappi_url);
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public async setZappiChargeMode(serialNo: string, chargeMode: ZappiChargeMode): Promise<any> {
|
|
47
|
+
const digest = new Digest(this._config.username, this._config.password);
|
|
48
|
+
const url = `${this._config.zappi_mode_url}${serialNo}-${chargeMode}-0-0-0000`;
|
|
49
|
+
const data = await digest.get(url);
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
|
|
19
53
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MyEnergi } from '../src/MyEnergi'
|
|
2
|
+
import { equal } from "assert";
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
|
|
5
|
+
describe("MyEnergi Tests", () => {
|
|
6
|
+
it("should be able to test", () => {
|
|
7
|
+
equal(true, true);
|
|
8
|
+
});
|
|
9
|
+
it("should be able to create instance", () => {
|
|
10
|
+
const myenergi = new MyEnergi('test', 'pwd');
|
|
11
|
+
expect(myenergi).to.be.instanceOf(MyEnergi);
|
|
12
|
+
});
|
|
13
|
+
});
|
package/tests/debug.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { exit } from 'process';
|
|
2
|
+
import { MyEnergi } from '../src';
|
|
3
|
+
import * as dotenv from 'dotenv';
|
|
4
|
+
import { ZappiChargeMode } from '../src/MyEnergi';
|
|
5
|
+
|
|
6
|
+
dotenv.config();
|
|
7
|
+
const myenergi = new MyEnergi(process.env.USERNAME as string, process.env.PASSWORD as string);
|
|
8
|
+
myenergi.getStatus().then(val => {
|
|
9
|
+
console.log(val);
|
|
10
|
+
myenergi.getZappi().then(v => {
|
|
11
|
+
console.log(v);
|
|
12
|
+
const sno: string = JSON.parse(v).zappi[0].sno;
|
|
13
|
+
myenergi.setZappiChargeMode(sno, ZappiChargeMode.Fast).then(x => {
|
|
14
|
+
console.log(x);
|
|
15
|
+
exit();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"target": "es2015",
|
|
5
|
+
"lib": ["es2017"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"declaration": false,
|
|
8
|
+
"noImplicitAny": false,
|
|
9
|
+
"removeComments": true,
|
|
10
|
+
"inlineSourceMap": true,
|
|
11
|
+
"moduleResolution": "node",
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*.ts", "src/**/*.d.ts", "tests/**/*.ts"]
|
|
14
|
+
}
|
package/dist/MyEnergi.d.ts
DELETED
package/dist/MyEnergi.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export class MyEnergi {
|
|
2
|
-
constructor(username, password) {
|
|
3
|
-
this._config = {
|
|
4
|
-
username: '',
|
|
5
|
-
password: '',
|
|
6
|
-
eddi_url: 'https://s18.myenergi.net/cgi-jstatus-E',
|
|
7
|
-
zappi_url: 'https://s18.myenergi.net/cgi-jstatus-Z',
|
|
8
|
-
harvi_url: 'https://s18.myenergi.net/cgi-jstatus-H',
|
|
9
|
-
status_url: 'https://s18.myenergi.net/cgi-jstatus-*',
|
|
10
|
-
dayhour_url: 'https://s18.myenergi.net/cgi-jdayhour-'
|
|
11
|
-
//https://s18.myenergi.net/cgi-jdayhour-Znnnnnnnn-YYYY-MM-DD
|
|
12
|
-
};
|
|
13
|
-
this._config.username = username;
|
|
14
|
-
this._config.password = password;
|
|
15
|
-
}
|
|
16
|
-
}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
package/npm-publish.sh
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
|
|
3
|
-
DEV_BRANCH_NAME="develop"
|
|
4
|
-
MASTER_BRANCH_NAME="master"
|
|
5
|
-
RELEASE_TYPE="$1"
|
|
6
|
-
|
|
7
|
-
confirm() {
|
|
8
|
-
# call with a prompt string or use a default
|
|
9
|
-
read -r -p "${1:-Are you sure? [y/N]} " response
|
|
10
|
-
case "$response" in
|
|
11
|
-
[yY][eE][sS]|[yY])
|
|
12
|
-
true
|
|
13
|
-
;;
|
|
14
|
-
*)
|
|
15
|
-
false
|
|
16
|
-
;;
|
|
17
|
-
esac
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
execute() {
|
|
21
|
-
if git checkout ${DEV_BRANCH_NAME} &&
|
|
22
|
-
git fetch origin ${DEV_BRANCH_NAME} &&
|
|
23
|
-
# [ `git rev-list HEAD...origin/${DEV_BRANCH_NAME} --count` != 0 ] &&
|
|
24
|
-
git merge origin/${DEV_BRANCH_NAME}
|
|
25
|
-
then
|
|
26
|
-
npm install
|
|
27
|
-
echo "Performing ${RELEASE_TYPE} release preparations..."
|
|
28
|
-
if npm version ${RELEASE_TYPE} -m "Release version %s"
|
|
29
|
-
then
|
|
30
|
-
echo "NPM version done. Proceeding with git operations..."
|
|
31
|
-
if git add . &&
|
|
32
|
-
git push &&
|
|
33
|
-
git checkout origin/${MASTER_BRANCH_NAME} &&
|
|
34
|
-
git pull &&
|
|
35
|
-
git merge ${DEV_BRANCH_NAME} &&
|
|
36
|
-
git push &&
|
|
37
|
-
git push --tags
|
|
38
|
-
then
|
|
39
|
-
git checkout origin/${DEV_BRANCH_NAME}
|
|
40
|
-
git merge ${MASTER_BRANCH_NAME}
|
|
41
|
-
git push
|
|
42
|
-
echo "Changes merged to ${MASTER_BRANCH_NAME} branch and pushed to origin."
|
|
43
|
-
true
|
|
44
|
-
else
|
|
45
|
-
echo "Failed to merge to ${MASTER_BRANCH_NAME} branch and push to origin."
|
|
46
|
-
false
|
|
47
|
-
fi
|
|
48
|
-
else
|
|
49
|
-
echo "NPM version failed!"
|
|
50
|
-
false
|
|
51
|
-
fi
|
|
52
|
-
else
|
|
53
|
-
echo "Could not prepare for new release. Please make sure the repository is up to date with the origin before you continue."
|
|
54
|
-
false
|
|
55
|
-
fi
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
run() {
|
|
59
|
-
if confirm && execute
|
|
60
|
-
then
|
|
61
|
-
echo "Successfully completed!"
|
|
62
|
-
else
|
|
63
|
-
echo "Failed to complete!"
|
|
64
|
-
fi
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if [ "${RELEASE_TYPE}" = "patch" ] || [ "${RELEASE_TYPE}" = "minor" ] || [ "${RELEASE_TYPE}" = "major" ] ||
|
|
68
|
-
[ "${RELEASE_TYPE}" = "prepatch" ] || [ "${RELEASE_TYPE}" = "preminor" ] || [ "${RELEASE_TYPE}" = "premajor" ] || [ "${RELEASE_TYPE}" = "prerelease" ]
|
|
69
|
-
then
|
|
70
|
-
run
|
|
71
|
-
else
|
|
72
|
-
echo "'${RELEASE_TYPE}' is not a valid release type. Please use 'patch', 'minor', 'major', 'prepatch', 'preminor' or 'premajor'"
|
|
73
|
-
fi
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
declare module "digest-fetch";
|
package/tsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2015",
|
|
4
|
-
"module": "ES2020",
|
|
5
|
-
"declaration": true,
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"forceConsistentCasingInFileNames": true,
|
|
8
|
-
"moduleResolution": "node",
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"strict": true
|
|
11
|
-
},
|
|
12
|
-
"include": [
|
|
13
|
-
"./src"
|
|
14
|
-
]
|
|
15
|
-
}
|