myenergi-api 0.0.7-0 → 0.0.8-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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myenergi-api",
3
- "version": "0.0.7-0",
3
+ "version": "0.0.8-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",
@@ -0,0 +1,119 @@
1
+ import * as crypto from 'crypto';
2
+
3
+ export class AuthDigest {
4
+ private _password?: string | undefined;
5
+ private _username?: string | undefined;
6
+ private _initialized: boolean;
7
+
8
+ public get username(): string | undefined {
9
+ return this._username;
10
+ }
11
+ private _realm?: string | undefined;
12
+ public get realm(): string | undefined {
13
+ return this._realm;
14
+ }
15
+ private _nonce?: string | undefined;
16
+ public get nonce(): string | undefined {
17
+ return this._nonce;
18
+ }
19
+ private _uri?: string | undefined;
20
+ public get uri(): string | undefined {
21
+ return this._uri;
22
+ }
23
+ private _algorithm?: string | undefined;
24
+ public get algorithm(): string | undefined {
25
+ return this._algorithm;
26
+ }
27
+ private _response?: string | undefined;
28
+ public get response(): string | undefined {
29
+ return this._response;
30
+ }
31
+ private _opaque?: string | undefined;
32
+ public get opaque(): string | undefined {
33
+ return this._opaque;
34
+ }
35
+ private _qop?: string | undefined;
36
+ public get qop(): string | undefined {
37
+ return this._qop;
38
+ }
39
+ private _nc: number;
40
+ public get nc(): string | undefined {
41
+ this._nc++;
42
+ var myHex = ("0000000" + this._nc?.toString(16)).substr(-8);
43
+ return myHex;
44
+ }
45
+ private _cnonce?: string | undefined;
46
+ public get cnonce(): string | undefined {
47
+ this._cnonce = this.md5(String(new Date().getTime()));
48
+ return this._cnonce;
49
+ }
50
+
51
+ private md5(data: string) {
52
+ let md5 = crypto.createHash('md5');
53
+ let result = md5.update(data).digest('hex');
54
+ return result;
55
+ }
56
+
57
+ constructor(username: string, password: string) {
58
+ this._username = username;
59
+ this._password = password;
60
+ this._nc = 0;
61
+ this._initialized = false;
62
+ }
63
+
64
+ public init(wwwAuthHeader: string) {
65
+
66
+ //let realm, nonce, qop, opaque;
67
+ let authSplit = wwwAuthHeader.split(',') as string[];
68
+
69
+ for (let item of authSplit) {
70
+ if (item.indexOf('realm=') >= 0) {
71
+ let realmSplit = item.split('="');
72
+ this._realm = realmSplit[realmSplit.length - 1];
73
+ this._realm = this._realm.substring(0, this._realm.length - 1);
74
+ }
75
+
76
+ if (item.indexOf('nonce=') >= 0) {
77
+ let nonceSplit = item.split('="');
78
+ this._nonce = nonceSplit[nonceSplit.length - 1];
79
+ this._nonce = this._nonce.substring(0, this._nonce.length - 1);
80
+ }
81
+
82
+ if (item.indexOf('qop=') >= 0) {
83
+ let qopSplit = item.split('="');
84
+ this._qop = qopSplit[qopSplit.length - 1];
85
+ this._qop = this._qop.substring(0, this._qop.length - 1);
86
+ }
87
+
88
+ if (item.indexOf('opaque=') >= 0) {
89
+ let opaqueSplit = item.split('="');
90
+ this._opaque = opaqueSplit[opaqueSplit.length - 1];
91
+ this._opaque = this._opaque.substring(0, this._opaque.length - 1);
92
+ }
93
+ }
94
+ this._initialized = true;
95
+ }
96
+
97
+ public getAuthorization(httpMethod: string, path: string): string {
98
+ if(!this._initialized)
99
+ return '';
100
+
101
+ const nc = this.nc;
102
+ const cnonce = this.cnonce;
103
+ let HA1 = this.md5(this.username + ":" + this.realm + ":" + this._password);
104
+ let HA2 = this.md5(httpMethod + ":" + path);
105
+ let response = this.md5(HA1 + ":" + this.nonce + ":" + nc + ":" + cnonce + ":" + this.qop + ":" + HA2);
106
+
107
+ let res = `Digest username="${this._username}",`;
108
+ res += `realm="${this.realm}",`;
109
+ res += `nonce="${this.nonce}",`;
110
+ res += `uri="${path}",`;
111
+ res += `cnonce="${cnonce}",`;
112
+ res += `nc=${nc},`;
113
+ res += `algorithm=MD5,`;
114
+ res += `response="${response}",`;
115
+ res += `qop="${this.qop}",`;
116
+ res += `opaque="${this.opaque}"`;
117
+ return res;
118
+ }
119
+ }
package/src/Digest.ts CHANGED
@@ -1,91 +1,35 @@
1
1
  import * as https from 'https';
2
- import * as crypto from 'crypto';
3
2
  import { RequestOptions } from 'https';
4
3
  import * as url from "url";
4
+ import { AuthDigest } from './AuthDigest';
5
5
 
6
6
  export class Digest {
7
- private _username: string;
8
- private _password: string;
9
- private _authorization: string | undefined;
7
+ private _authorization?: AuthDigest;
10
8
 
11
9
  constructor(username: string, password: string) {
12
- this._username = username;
13
- this._password = password;
14
- this._authorization = '';
10
+ this._authorization = new AuthDigest(username, password);
15
11
  }
16
12
 
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> {
13
+ private request(options: RequestOptions, data?: any, retryCount: number = 0): Promise<any> {
51
14
  return new Promise<any>((resolve, reject) => {
52
15
  let resData: string = '';
16
+ if (!options.headers)
17
+ options.headers = {};
18
+ options.headers.Authorization = this._authorization?.getAuthorization(options.method as string, options.path as string);
53
19
  const req = https.request(options, (res: any) => {
54
20
 
55
21
  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
- }
22
+ if (retryCount > 1) {
23
+ reject("Authentication failed");
24
+ return;
79
25
  }
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);
26
+ retryCount++;
27
+ let auth = res.headers["www-authenticate"];
28
+ this._authorization?.init(auth);
84
29
  if (!options.headers)
85
30
  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 => {
31
+ options.headers.Authorization = this._authorization?.getAuthorization(options.method as string, options.path as string);
32
+ return this.request(options, data, retryCount).then(value => {
89
33
  resolve(value);
90
34
  }).catch(resaon => {
91
35
  reject(resaon);
@@ -119,7 +63,7 @@ export class Digest {
119
63
  }
120
64
 
121
65
  public get(requestUrl: string, data?: any): Promise<any> {
122
- const uri = url.parse(requestUrl);
66
+ const uri = url.parse(requestUrl, true);
123
67
  const options: RequestOptions = {
124
68
  hostname: uri.hostname,
125
69
  port: uri.port,
@@ -127,12 +71,11 @@ export class Digest {
127
71
  method: 'GET',
128
72
  headers: {
129
73
  'Connection': 'Keep-Alive',
130
- 'Content-Type': 'text/plain',
74
+ 'Content-Type': 'application/json',
131
75
  'Host': uri.hostname as string,
132
- 'Authorization': this._authorization
133
76
  }
134
77
  };
135
- return this.requestDigest(options, data);
78
+ return this.request(options, data);
136
79
  }
137
80
 
138
81
  }
package/src/MyEnergi.ts CHANGED
@@ -23,30 +23,28 @@ export class MyEnergi {
23
23
  zappi_mode_url: 'https://s18.myenergi.net/cgi-zappi-mode-Z'
24
24
  //https://s18.myenergi.net/cgi-jdayhour-Znnnnnnnn-YYYY-MM-DD
25
25
  }
26
- private _client: any;
27
- private _asn: any;
26
+
27
+ private _digest: Digest;
28
28
 
29
29
  constructor(username: string, password: string) {
30
30
  this._config.username = username;
31
31
  this._config.password = password;
32
+ this._digest = new Digest(this._config.username, this._config.password);
32
33
  }
33
34
 
34
35
  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);
36
+ const data = await this._digest.get(this._config.status_url);
37
37
  return data;
38
38
  }
39
39
 
40
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);
41
+ const data = await this._digest.get(this._config.zappi_url);
43
42
  return data;
44
43
  }
45
44
 
46
45
  public async setZappiChargeMode(serialNo: string, chargeMode: ZappiChargeMode): Promise<any> {
47
- const digest = new Digest(this._config.username, this._config.password);
48
46
  const url = `${this._config.zappi_mode_url}${serialNo}-${chargeMode}-0-0-0000`;
49
- const data = await digest.get(url);
47
+ const data = await this._digest.get(url);
50
48
  return data;
51
49
  }
52
50
 
package/tests/debug.ts CHANGED
@@ -13,6 +13,15 @@ myenergi.getStatus().then(val => {
13
13
  myenergi.setZappiChargeMode(sno, ZappiChargeMode.Fast).then(x => {
14
14
  console.log(x);
15
15
  exit();
16
+ }).catch(err => {
17
+ console.error(err);
18
+ exit();
16
19
  });
20
+ }).catch(err => {
21
+ console.error(err);
22
+ exit();
17
23
  });
24
+ }).catch(err => {
25
+ console.error(err);
26
+ exit();
18
27
  });