homebridge-enphase-envoy 9.4.2 → 9.4.3-beta.1

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
@@ -2,7 +2,7 @@
2
2
  "private": false,
3
3
  "displayName": "Enphase Envoy",
4
4
  "name": "homebridge-enphase-envoy",
5
- "version": "9.4.2",
5
+ "version": "9.4.3-beta.1",
6
6
  "description": "Homebridge plugin for Photovoltaic Energy System manufactured by Enphase.",
7
7
  "license": "MIT",
8
8
  "author": "grzegorz914",
@@ -602,12 +602,24 @@ class EnvoyDevice extends EventEmitter {
602
602
  this.envoyTokenFile = envoyTokenFile;
603
603
  this.envoyInstallerPasswordFile = envoyInstallerPasswordFile;
604
604
  this.startPrepareAccessory = true;
605
- this.getJwtTokenRunning = false;
606
- this.validateToken = false;
605
+ this.checkJwtTokenRunning = false;
606
+ this.cookie = '';
607
+ this.newCookie = '';
607
608
 
608
609
  //url
609
610
  this.url = this.envoyFirmware7xx ? `https://${this.host}` : `http://${this.host}`;
610
611
 
612
+ //create axios instance
613
+ this.axiosInstance = axios.create({
614
+ method: 'GET',
615
+ baseURL: this.url,
616
+ withCredentials: true,
617
+ headers: {
618
+ Accept: 'application/json'
619
+ },
620
+ timeout: 25000
621
+ });
622
+
611
623
  //envoy dev id
612
624
  this.envoyDevId = '';
613
625
 
@@ -864,6 +876,9 @@ class EnvoyDevice extends EventEmitter {
864
876
  const updateInventory = updateHome ? await this.updateInventory() : false;
865
877
  } catch (error) {
866
878
  this.emit('error', `Impulse generator: ${error}`);
879
+ const errorString = error.toString();
880
+ const tokenNotValid = errorString.includes('status code 401');
881
+ this.checkJwtTokenRunning = !tokenNotValid;
867
882
  };
868
883
  }).on('updateMeters', async () => {
869
884
  try {
@@ -872,6 +887,9 @@ class EnvoyDevice extends EventEmitter {
872
887
  const updateMetersReading = updateMeters ? await this.updateMetersReading() : false;
873
888
  } catch (error) {
874
889
  this.emit('error', `Impulse generator: ${error}`);
890
+ const errorString = error.toString();
891
+ const tokenNotValid = errorString.includes('status code 401');
892
+ this.checkJwtTokenRunning = !tokenNotValid;
875
893
  };
876
894
  }).on('updateMicroinvertersStatus', async () => {
877
895
  try {
@@ -879,6 +897,9 @@ class EnvoyDevice extends EventEmitter {
879
897
  const updateMicroinvertersStatus = !tokenValid ? false : await this.updateMicroinvertersStatus();
880
898
  } catch (error) {
881
899
  this.emit('error', `Impulse generator: ${error}`);
900
+ const errorString = error.toString();
901
+ const tokenNotValid = errorString.includes('status code 401');
902
+ this.checkJwtTokenRunning = !tokenNotValid;
882
903
  };
883
904
  }).on('updateProduction', async () => {
884
905
  try {
@@ -887,6 +908,9 @@ class EnvoyDevice extends EventEmitter {
887
908
  const updateProductionCt = updateProduction ? await this.updateProductionCt() : false;
888
909
  } catch (error) {
889
910
  this.emit('error', `Impulse generator: ${error}`);
911
+ const errorString = error.toString();
912
+ const tokenNotValid = errorString.includes('status code 401');
913
+ this.checkJwtTokenRunning = !tokenNotValid;
890
914
  };
891
915
  }).on('updateEnsemble', async () => {
892
916
  try {
@@ -901,6 +925,9 @@ class EnvoyDevice extends EventEmitter {
901
925
  const updateGeneratorSettings = updateGenerator ? await this.updateGeneratorSettings() : false;
902
926
  } catch (error) {
903
927
  this.emit('error', `Impulse generator: ${error}`);
928
+ const errorString = error.toString();
929
+ const tokenNotValid = errorString.includes('status code 401');
930
+ this.checkJwtTokenRunning = !tokenNotValid;
904
931
  };
905
932
  }).on('updateLiveData', async () => {
906
933
  try {
@@ -908,6 +935,9 @@ class EnvoyDevice extends EventEmitter {
908
935
  const updateLiveData = !tokenValid ? false : await this.updateLiveData();
909
936
  } catch (error) {
910
937
  this.emit('error', `Impulse generator: ${error}`);
938
+ const errorString = error.toString();
939
+ const tokenNotValid = errorString.includes('status code 401');
940
+ this.checkJwtTokenRunning = !tokenNotValid;
911
941
  };
912
942
  }).on('state', (state) => {
913
943
  const emitState = state ? this.emit('success', `Impulse generator started.`) : this.emit('warn', `Impulse generator stopped.`);
@@ -962,20 +992,9 @@ class EnvoyDevice extends EventEmitter {
962
992
  this.refreshEnsemble = false;
963
993
  this.refreshLiveData = false;
964
994
 
965
- //create axios instance
966
- this.axiosInstance = axios.create({
967
- method: 'GET',
968
- baseURL: this.url,
969
- withCredentials: true,
970
- headers: {
971
- Accept: 'application/json'
972
- },
973
- timeout: 25000
974
- });
975
-
976
995
  //get and validate jwt token
977
996
  this.validateToken = true;
978
- const getJwtToken = this.envoyFirmware7xx ? this.envoyFirmware7xxTokenGenerationMode === 0 ? await this.getJwtToken() : true : false;
997
+ const checkJwtToken = this.envoyFirmware7xx ? this.envoyFirmware7xxTokenGenerationMode === 0 ? await this.checkJwtToken() : true : false;
979
998
 
980
999
  //update grid profile
981
1000
  await this.updateGridProfile();
@@ -999,17 +1018,17 @@ class EnvoyDevice extends EventEmitter {
999
1018
  const updateMetersReading = this.refreshMeters ? await this.updateMetersReading() : false;
1000
1019
 
1001
1020
  //acces with envoy password
1002
- this.refreshMicroinverters = getJwtToken || calculateEnvoyPassword ? await this.updateMicroinvertersStatus() : false;
1021
+ this.refreshMicroinverters = checkJwtToken || calculateEnvoyPassword ? await this.updateMicroinvertersStatus() : false;
1003
1022
 
1004
1023
  //get production and production ct
1005
1024
  this.refreshProduction = await this.updateProduction();
1006
1025
  const updateProductionCt = this.refreshProduction ? await this.updateProductionCt() : false;
1007
1026
 
1008
1027
  //access with installer password and envoy dev id
1009
- const updatePowerProductionState = envoyDevIdExist && (getJwtToken || calculateInstallerPassword) ? await this.updateProductionPowerState() : false;
1028
+ const updatePowerProductionState = envoyDevIdExist && (checkJwtToken || calculateInstallerPassword) ? await this.updateProductionPowerState() : false;
1010
1029
 
1011
1030
  //get ensemble data only FW. >= 7.x.x.
1012
- this.refreshEnsemble = getJwtToken ? await this.updateEnsembleInventory() : false;
1031
+ this.refreshEnsemble = checkJwtToken ? await this.updateEnsembleInventory() : false;
1013
1032
  const updateEnsembleStatus = this.refreshEnsemble ? await this.updateEnsembleStatus() : false;
1014
1033
  const updateEnchargeSettings = this.refreshEnsemble ? await this.updateEnchargesSettings() : false;
1015
1034
  const updateTariffSettings = this.refreshEnsemble ? await this.updateTariff() : false;
@@ -1019,8 +1038,8 @@ class EnvoyDevice extends EventEmitter {
1019
1038
  const updateGeneratorSettings = updateGenerator ? await this.updateGeneratorSettings() : false;
1020
1039
 
1021
1040
  //get plc communication level
1022
- const updateCommLevel = this.supportPlcLevel && (getJwtToken || calculateInstallerPassword) ? await this.updateCommLevel() : false;
1023
- this.refreshLiveData = getJwtToken ? await this.updateLiveData() : false;
1041
+ const updateCommLevel = this.supportPlcLevel && (checkJwtToken || calculateInstallerPassword) ? await this.updateCommLevel() : false;
1042
+ this.refreshLiveData = checkJwtToken ? await this.updateLiveData() : false;
1024
1043
 
1025
1044
  //connect to deice success
1026
1045
  this.emit('success', `Connect Success.`)
@@ -1049,19 +1068,47 @@ class EnvoyDevice extends EventEmitter {
1049
1068
  };
1050
1069
  };
1051
1070
 
1052
- async getJwtToken() {
1053
- const debug = this.enableDebugMode ? this.emit('debug', `Requesting JWT token.`) : false;
1071
+ async checkJwtToken() {
1072
+ const debug = this.enableDebugMode ? this.emit('debug', `Requesting check JWT token.`) : false;
1054
1073
 
1055
1074
  if (!this.envoyFirmware7xx || (this.envoyFirmware7xx && this.envoyFirmware7xxTokenGenerationMode === 1)) {
1056
1075
  return true;
1057
1076
  };
1058
1077
 
1059
- if (this.getJwtTokenRunning) {
1078
+ if (this.checkJwtTokenRunning) {
1060
1079
  return false;
1061
1080
  };
1062
1081
 
1063
1082
  try {
1064
- this.getJwtTokenRunning = true;
1083
+ this.checkJwtTokenRunning = true;
1084
+
1085
+ //read token from file
1086
+ const data = await this.readData(this.tokenFile);
1087
+ const parsedData = JSON.parse(data);
1088
+ parsedData.token ? parsedData : null;
1089
+ const tokenIsValid = parsedData && Math.floor(Date.now() / 1000) + 60 <= parsedData.expires_at;
1090
+
1091
+ if (tokenIsValid && (this.cookie === this.newCookie)) {
1092
+ return true;
1093
+ }
1094
+
1095
+ this.emit('warn', `JWT Token expired or not valid, refreshing.`);
1096
+ await new Promise(resolve => setTimeout(resolve, 30000));
1097
+ const getToken = await this.getJwtToken();
1098
+ const validateToken = getToken ? await this.validateJwtToken() : false;
1099
+
1100
+ this.checkJwtTokenRunning = false;
1101
+ return validateToken;
1102
+ } catch (error) {
1103
+ this.checkJwtTokenRunning = false;
1104
+ throw new Error(`Chack JWT token error: ${error.message || error}`);
1105
+ };
1106
+ };
1107
+
1108
+ async getJwtToken() {
1109
+ const debug = this.enableDebugMode ? this.emit('debug', `Requesting JWT token.`) : false;
1110
+
1111
+ try {
1065
1112
  const envoyToken = new EnvoyToken({
1066
1113
  user: this.enlightenUser,
1067
1114
  passwd: this.enlightenPassword,
@@ -1069,14 +1116,17 @@ class EnvoyDevice extends EventEmitter {
1069
1116
  tokenFile: this.envoyTokenFile
1070
1117
  }).on('success', (message) => {
1071
1118
  this.emit('success', message);
1072
- this.validateToken = true;
1073
1119
  }).on('warn', (message) => {
1074
1120
  this.emit('warn', message);
1075
1121
  }).on('error', (error) => {
1076
1122
  throw new Error(error.message || error);
1077
1123
  });
1078
1124
 
1079
- const tokenData = await envoyToken.checkToken();
1125
+ const tokenData = await envoyToken.refreshToken();
1126
+ if (!tokenData) {
1127
+ return false;
1128
+ }
1129
+
1080
1130
  const updatedTokenData = {
1081
1131
  ...tokenData,
1082
1132
  token: 'removed'
@@ -1084,26 +1134,21 @@ class EnvoyDevice extends EventEmitter {
1084
1134
  const debug = this.enableDebugMode ? this.emit('debug', `JWT token:`, updatedTokenData) : false;
1085
1135
  this.jwtToken = tokenData;
1086
1136
 
1087
- //validate new token
1088
- const validateToken = this.validateToken ? await this.validateJwtToken() : false;
1089
- this.validateToken = validateToken ? false : this.validateToken;
1090
-
1091
1137
  //restFul
1092
1138
  const restFul = this.restFulConnected ? this.restFul.update('token', tokenData) : false;
1093
1139
 
1094
1140
  //mqtt
1095
1141
  const mqtt = this.mqttConnected ? this.mqtt.emit('publish', 'Token', tokenData) : false;
1096
1142
 
1097
- this.getJwtTokenRunning = false;
1098
1143
  return true;
1099
1144
  } catch (error) {
1100
- this.getJwtTokenRunning = this.getJwtTokenRunning ? false : this.getJwtTokenRunning;
1101
1145
  throw new Error(`Get JWT token error: ${error.message || error}`);
1102
1146
  };
1103
1147
  };
1104
1148
 
1105
1149
  async validateJwtToken() {
1106
1150
  const debug = this.enableDebugMode ? this.emit('debug', `Requesting validate JWT token.`) : false;
1151
+
1107
1152
  try {
1108
1153
  const axiosInstanceToken = axios.create({
1109
1154
  method: 'GET',
@@ -1122,7 +1167,7 @@ class EnvoyDevice extends EventEmitter {
1122
1167
 
1123
1168
  const response = await axiosInstanceToken(CONSTANTS.ApiUrls.CheckJwt);
1124
1169
  const debug = this.enableDebugMode ? this.emit('debug', `JWT token: Valid`) : false;
1125
- const cookie = response.headers['set-cookie'];
1170
+ this.newCookie = response.headers['set-cookie'];
1126
1171
 
1127
1172
  //create axios instance get with cookie
1128
1173
  this.axiosInstance = axios.create({
@@ -1130,7 +1175,7 @@ class EnvoyDevice extends EventEmitter {
1130
1175
  baseURL: this.url,
1131
1176
  headers: {
1132
1177
  Accept: 'application/json',
1133
- Cookie: cookie
1178
+ Cookie: this.newCookie
1134
1179
  },
1135
1180
  withCredentials: true,
1136
1181
  httpsAgent: new https.Agent({
@@ -1139,7 +1184,8 @@ class EnvoyDevice extends EventEmitter {
1139
1184
  }),
1140
1185
  timeout: 20000
1141
1186
  });
1142
- this.cookie = cookie;
1187
+
1188
+ this.cookie = this.newCookie;
1143
1189
  this.emit('success', `JWT Token valid: ${new Date(this.jwtToken.expires_at * 1000).toLocaleString()}`);
1144
1190
 
1145
1191
  return true;
package/src/envoytoken.js CHANGED
@@ -14,32 +14,23 @@ class EnvoyToken extends EventEmitter {
14
14
  this.tokenFile = config.tokenFile;
15
15
  };
16
16
 
17
- async checkToken() {
17
+ async refreshToken() {
18
18
  try {
19
- const data = await this.readData(this.tokenFile);
20
- const parsedData = JSON.parse(data);
21
- parsedData.token ? parsedData : null;
22
- const tokenIsValid = parsedData && Math.floor(Date.now() / 1000) + 12 <= parsedData.expires_at;
19
+ const cookie = await this.loginToEnlighten();
20
+ const tokenData = await this.getToken(cookie);
23
21
 
24
- if (tokenIsValid) {
25
- return parsedData;
26
- } else {
27
- this.emit('warn', `JWT Token expired, refreshing.`);
28
- await new Promise(resolve => setTimeout(resolve, 15000));
29
- const newToken = await this.refreshToken();
30
- return newToken;
22
+ if (!tokenData.token) {
23
+ this.emit('warn', `Token missing in response: ${JSON.stringify(tokenData)}`);
24
+ return false;
31
25
  }
32
- } catch (error) {
33
- this.emit('error', `Check token error: ${error}`);
34
- }
35
- }
36
26
 
37
- async refreshToken() {
38
- try {
39
- const cookie = await this.loginToEnlighten();
40
- const newToken = await this.getToken(cookie);
27
+ //save token
28
+ await this.saveData(this.tokenFile, tokenData);
29
+
30
+ //emit success
41
31
  this.emit('success', `JWT Token refresh success.`);
42
- return newToken;
32
+
33
+ return tokenData;
43
34
  } catch (error) {
44
35
  this.emit('error', `Refresh token error: ${error}`);
45
36
  }
@@ -90,13 +81,6 @@ class EnvoyToken extends EventEmitter {
90
81
 
91
82
  const response = await axiosInstance(CONSTANTS.EnphaseUrls.EntrezAuthToken);
92
83
  const tokenData = response.data;
93
- if (!tokenData.token) {
94
- this.emit('error', `Token missing in response: ${JSON.stringify(tokenData)}`);
95
- return;
96
- }
97
-
98
- tokenData.expires_at = tokenData.expires_at || Math.floor(Date.now() / 1000) + 3600; // Assume 1 hour expiry if not provided
99
- await this.saveData(this.tokenFile, tokenData);
100
84
  return tokenData;
101
85
  } catch (error) {
102
86
  this.emit('error', `Get token error: ${error.message ?? error}`);