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 +1 -1
- package/src/envoydevice.js +80 -34
- package/src/envoytoken.js +12 -28
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.
|
|
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",
|
package/src/envoydevice.js
CHANGED
|
@@ -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.
|
|
606
|
-
this.
|
|
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
|
|
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 =
|
|
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 && (
|
|
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 =
|
|
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 && (
|
|
1023
|
-
this.refreshLiveData =
|
|
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
|
|
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.
|
|
1078
|
+
if (this.checkJwtTokenRunning) {
|
|
1060
1079
|
return false;
|
|
1061
1080
|
};
|
|
1062
1081
|
|
|
1063
1082
|
try {
|
|
1064
|
-
this.
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
17
|
+
async refreshToken() {
|
|
18
18
|
try {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
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 (
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
//save token
|
|
28
|
+
await this.saveData(this.tokenFile, tokenData);
|
|
29
|
+
|
|
30
|
+
//emit success
|
|
41
31
|
this.emit('success', `JWT Token refresh success.`);
|
|
42
|
-
|
|
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}`);
|