myenergi-api 0.0.9-0 → 0.1.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 +3 -4
- package/src/AuthDigest.ts +21 -8
- package/src/Digest.ts +4 -3
- package/src/MyEnergi.ts +24 -3
- package/src/models/Zappi.ts +41 -0
- package/tests/MyEnergi-spec.ts +1 -2
- package/tests/debug.ts +10 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myenergi-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.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",
|
|
@@ -25,13 +25,12 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/bisand/myenergi-api#readme",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"dotenv": "^10.0.0"
|
|
29
|
-
"node-digest-auth-client": "^1.0.6"
|
|
28
|
+
"dotenv": "^10.0.0"
|
|
30
29
|
},
|
|
31
30
|
"devDependencies": {
|
|
31
|
+
"@types/chai": "^4.2.22",
|
|
32
32
|
"@types/mocha": "^9.0.0",
|
|
33
33
|
"@types/node": "^16.11.9",
|
|
34
|
-
"assert": "^2.0.0",
|
|
35
34
|
"chai": "^4.3.4",
|
|
36
35
|
"mocha": "^9.1.3",
|
|
37
36
|
"ts-node": "^10.4.0",
|
package/src/AuthDigest.ts
CHANGED
|
@@ -4,6 +4,7 @@ export class AuthDigest {
|
|
|
4
4
|
private _password?: string | undefined;
|
|
5
5
|
private _username?: string | undefined;
|
|
6
6
|
private _initialized: boolean;
|
|
7
|
+
private _onError?: (errorMessage: string) => void;
|
|
7
8
|
|
|
8
9
|
public get username(): string | undefined {
|
|
9
10
|
return this._username;
|
|
@@ -20,8 +21,8 @@ export class AuthDigest {
|
|
|
20
21
|
public get uri(): string | undefined {
|
|
21
22
|
return this._uri;
|
|
22
23
|
}
|
|
23
|
-
private _algorithm
|
|
24
|
-
public get algorithm(): string
|
|
24
|
+
private _algorithm: string;
|
|
25
|
+
public get algorithm(): string {
|
|
25
26
|
return this._algorithm;
|
|
26
27
|
}
|
|
27
28
|
private _response?: string | undefined;
|
|
@@ -54,11 +55,14 @@ export class AuthDigest {
|
|
|
54
55
|
return result;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
constructor(username: string, password: string) {
|
|
58
|
+
constructor(username: string, password: string, errorHandler?: (errorMessage: string) => void) {
|
|
58
59
|
this._username = username;
|
|
59
60
|
this._password = password;
|
|
60
61
|
this._nc = 0;
|
|
61
62
|
this._initialized = false;
|
|
63
|
+
this._algorithm = 'MD5';
|
|
64
|
+
if (errorHandler)
|
|
65
|
+
this._onError = errorHandler;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
public init(wwwAuthHeader: string) {
|
|
@@ -89,19 +93,28 @@ export class AuthDigest {
|
|
|
89
93
|
this._opaque = opaqueSplit[opaqueSplit.length - 1];
|
|
90
94
|
this._opaque = this._opaque.substring(0, this._opaque.length - 1);
|
|
91
95
|
}
|
|
96
|
+
|
|
97
|
+
if (item.indexOf('algorithm=') >= 0) {
|
|
98
|
+
let algorithmSplit = item.split('=');
|
|
99
|
+
this._algorithm = algorithmSplit[algorithmSplit.length - 1];
|
|
100
|
+
this._algorithm = this._algorithm.substring(0, this._algorithm.length);
|
|
101
|
+
if (this._onError && this.algorithm !== 'MD5') {
|
|
102
|
+
this._onError(`Algorithm ${this.algorithm} is ont supported. Only MD5 is supportet`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
92
105
|
}
|
|
93
106
|
this._initialized = true;
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
public getAuthorization(httpMethod: string, path: string): string {
|
|
97
|
-
if(!this._initialized)
|
|
110
|
+
if (!this._initialized)
|
|
98
111
|
return '';
|
|
99
112
|
|
|
100
113
|
const nc = this.nc;
|
|
101
114
|
const cnonce = this.cnonce;
|
|
102
|
-
let HA1 = this.md5(this.username +
|
|
103
|
-
let HA2 = this.md5(httpMethod +
|
|
104
|
-
let response = this.md5(HA1 +
|
|
115
|
+
let HA1 = this.md5(this.username + ':' + this.realm + ':' + this._password);
|
|
116
|
+
let HA2 = this.md5(httpMethod + ':' + path);
|
|
117
|
+
let response = this.md5(HA1 + ':' + this.nonce + ':' + nc + ':' + cnonce + ':' + this.qop + ':' + HA2);
|
|
105
118
|
|
|
106
119
|
let res = `Digest username="${this.username}",`;
|
|
107
120
|
res += `realm="${this.realm}",`;
|
|
@@ -109,7 +122,7 @@ export class AuthDigest {
|
|
|
109
122
|
res += `uri="${path}",`;
|
|
110
123
|
res += `cnonce="${cnonce}",`;
|
|
111
124
|
res += `nc=${nc},`;
|
|
112
|
-
res += `algorithm
|
|
125
|
+
res += `algorithm=${this.algorithm},`;
|
|
113
126
|
res += `response="${response}",`;
|
|
114
127
|
res += `qop="${this.qop}",`;
|
|
115
128
|
res += `opaque="${this.opaque}"`;
|
package/src/Digest.ts
CHANGED
|
@@ -7,7 +7,9 @@ export class Digest {
|
|
|
7
7
|
private _authDigest?: AuthDigest;
|
|
8
8
|
|
|
9
9
|
constructor(username: string, password: string) {
|
|
10
|
-
this._authDigest = new AuthDigest(username, password)
|
|
10
|
+
this._authDigest = new AuthDigest(username, password, (err) => {
|
|
11
|
+
console.error(err);
|
|
12
|
+
});
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
private request(options: RequestOptions, data?: any, retryCount: number = 0): Promise<any> {
|
|
@@ -25,8 +27,7 @@ export class Digest {
|
|
|
25
27
|
}
|
|
26
28
|
retryCount++;
|
|
27
29
|
const wwwAuth = res.headers["www-authenticate"] as string;
|
|
28
|
-
if(!wwwAuth.startsWith('Digest'))
|
|
29
|
-
{
|
|
30
|
+
if (!wwwAuth.startsWith('Digest')) {
|
|
30
31
|
reject('Unsupported authentication method. Supported authentication schemes: Digest');
|
|
31
32
|
return;
|
|
32
33
|
}
|
package/src/MyEnergi.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Digest } from './Digest';
|
|
2
|
+
import { Zappi } from './models/Zappi';
|
|
2
3
|
|
|
3
4
|
export enum ZappiChargeMode {
|
|
4
5
|
Off = 4,
|
|
@@ -35,14 +36,34 @@ export class MyEnergi {
|
|
|
35
36
|
this._digest = new Digest(this._config.username, this._config.password);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
public async
|
|
39
|
+
public async getStatusAll(): Promise<any> {
|
|
39
40
|
const data = await this._digest.get(this._config.status_url);
|
|
40
41
|
return data;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
public async
|
|
44
|
+
public async getStatusZappiAll(): Promise<Zappi[]> {
|
|
44
45
|
const data = await this._digest.get(this._config.zappi_url);
|
|
45
|
-
|
|
46
|
+
const jsonData = JSON.parse(data);
|
|
47
|
+
if (jsonData.zappi)
|
|
48
|
+
return Object.assign<Zappi[], any>([] as Zappi[], jsonData.zappi);
|
|
49
|
+
else
|
|
50
|
+
return [] as Zappi[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async getStatusZappi(serialNumber: string): Promise<Zappi | null> {
|
|
54
|
+
const data = await this._digest.get(this._config.zappi_url);
|
|
55
|
+
const jsonData = JSON.parse(data);
|
|
56
|
+
if (jsonData.zappi) {
|
|
57
|
+
const zappi = (Object.assign<Zappi[], any>([] as Zappi[], jsonData.zappi) as Zappi[]).find(zappi => {
|
|
58
|
+
return zappi.sno === serialNumber;
|
|
59
|
+
});
|
|
60
|
+
if (zappi)
|
|
61
|
+
return Object.assign<Zappi, any>({} as Zappi, zappi);
|
|
62
|
+
else
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
return null;
|
|
46
67
|
}
|
|
47
68
|
|
|
48
69
|
public async setZappiChargeMode(serialNo: string, chargeMode: ZappiChargeMode): Promise<any> {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface Zappi {
|
|
2
|
+
sno: string;
|
|
3
|
+
dat: string;
|
|
4
|
+
tim: string;
|
|
5
|
+
ectp1: number;
|
|
6
|
+
ectp2: number;
|
|
7
|
+
ectp3: number;
|
|
8
|
+
ectp4: number;
|
|
9
|
+
ectp5: number;
|
|
10
|
+
ectp6: number;
|
|
11
|
+
ectt1: string;
|
|
12
|
+
ectt2: string;
|
|
13
|
+
ectt3: string;
|
|
14
|
+
ectt4: string;
|
|
15
|
+
ectt5: string;
|
|
16
|
+
ectt6: string;
|
|
17
|
+
bsm: number;
|
|
18
|
+
bst: number;
|
|
19
|
+
cmt: number;
|
|
20
|
+
dst: number;
|
|
21
|
+
div: number;
|
|
22
|
+
frq: number;
|
|
23
|
+
fwv: string;
|
|
24
|
+
gen: number;
|
|
25
|
+
grd: number;
|
|
26
|
+
pha: number;
|
|
27
|
+
pri: number;
|
|
28
|
+
sta: number;
|
|
29
|
+
tz: number;
|
|
30
|
+
vol: number;
|
|
31
|
+
che: number;
|
|
32
|
+
bss: number;
|
|
33
|
+
lck: number;
|
|
34
|
+
pst: string;
|
|
35
|
+
zmo: number;
|
|
36
|
+
zs: number;
|
|
37
|
+
rdc: number;
|
|
38
|
+
rac: number;
|
|
39
|
+
rrac: number;
|
|
40
|
+
zsl: number;
|
|
41
|
+
}
|
package/tests/MyEnergi-spec.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { MyEnergi } from '../src/MyEnergi'
|
|
2
|
-
import { equal } from "assert";
|
|
3
2
|
import { expect } from 'chai'
|
|
4
3
|
|
|
5
4
|
describe("MyEnergi Tests", () => {
|
|
6
5
|
it("should be able to test", () => {
|
|
7
|
-
|
|
6
|
+
expect(true).to.be.true;
|
|
8
7
|
});
|
|
9
8
|
it("should be able to create instance", () => {
|
|
10
9
|
const myenergi = new MyEnergi('test', 'pwd');
|
package/tests/debug.ts
CHANGED
|
@@ -5,18 +5,24 @@ import { ZappiBoostMode, ZappiChargeMode } from '../src/MyEnergi';
|
|
|
5
5
|
|
|
6
6
|
dotenv.config();
|
|
7
7
|
const myenergi = new MyEnergi(process.env.USERNAME as string, process.env.PASSWORD as string);
|
|
8
|
-
myenergi.
|
|
8
|
+
myenergi.getStatusAll().then(val => {
|
|
9
9
|
console.log(val);
|
|
10
|
-
myenergi.
|
|
10
|
+
myenergi.getStatusZappiAll().then(v => {
|
|
11
11
|
console.log(v);
|
|
12
|
-
const sno: string =
|
|
12
|
+
const sno: string = v[0].sno;
|
|
13
13
|
myenergi.setZappiChargeMode(sno, ZappiChargeMode.Fast).then(x => {
|
|
14
14
|
console.log(x);
|
|
15
15
|
myenergi.setZappiBoostMode(sno, ZappiBoostMode.Stop).then(x => {
|
|
16
16
|
console.log(x);
|
|
17
17
|
myenergi.setZappiGreenLevel(sno, 0).then(x => {
|
|
18
18
|
console.log(x);
|
|
19
|
-
|
|
19
|
+
myenergi.getStatusZappi(sno).then(x => {
|
|
20
|
+
console.log(x);
|
|
21
|
+
exit();
|
|
22
|
+
}).catch(err => {
|
|
23
|
+
console.error(err);
|
|
24
|
+
exit();
|
|
25
|
+
});
|
|
20
26
|
}).catch(err => {
|
|
21
27
|
console.error(err);
|
|
22
28
|
exit();
|