svetz
Works in theory! Practice? That's something else
Looks like Enphase has modified local access, requests like http://envoy.local/api/v1/production/inverters now returns a 301 (Moved) response, the new location is https://envoy.local/api/v1/production/inverters, that requires a token which sends you to https://entrez.enphaseenergy.com ... not sure yet if that requires a web lookup to resolve...I'll be annoyed if you lose access when the web is down.
Thread on it here: https://community.enphase.com/s/que...with-loss-of-local-api-connectivity-to-envoys.
Finally had some time to update my homegrown monitoring system. The server issues a JSON Web Token which is sent to the Envoy to be authorized which provides a cookie that is used to validate the request. Here's the first-pass replacement networking class (needs polishing, but shows the "successful" path), keep in mind it won't work when the internet is down as the token server is remote:
Java:
import java.io.FileInputStream;
import java.io.IOException;
import java.net.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
import java.net.http.HttpClient;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
public class EnvoyNet {
public EnvoyNet() { }
public static void main(String[] args) throws Exception { // example test program
Properties properties = new Properties(); // load local data from a propeties file
properties.load(new FileInputStream("envoy.properties")); // local file that hold Envoy ID, serial#, and enlighten login
String envoyAddr = properties.getProperty("envoyIP", "envoy.local"); // e.g., 192.168.0.22
String envoySerial = properties.getProperty("envoySerial"); // you can see this when you login locally with a browser
String enlightenPassword = properties.getProperty("enlightenPassword");
String enlightenLogin = properties.getProperty("enlightenLogin"); // usually your email address
EnvoyNet.init(enlightenLogin, enlightenPassword, envoyAddr, envoySerial); // do this first once to initialize the class and get the cookie
System.out.println(EnvoyNet.get("https://"+ envoyAddr + "/api/v1/production")); // repeat the "get" for the url of interest
}
static HttpClient client;
private static SSLContext disableCertificateCheck() {
javax.net.ssl.TrustManager x509 = new javax.net.ssl.X509ExtendedTrustManager() {
public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException { }
public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException { }
public java.security.cert.X509Certificate[] getAcceptedIssuers() {return null; }
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {}
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { }
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { }
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { }
};
SSLContext ctx = null;
try {
ctx = SSLContext.getInstance("SSL");
ctx.init(null, new javax.net.ssl.TrustManager[] { x509 }, null);
} catch (java.security.GeneralSecurityException ex) {
System.out.println("Exception while disabling certificate checks: "+ex.getLocalizedMessage());
}
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
return ctx;
}
public static void init(String enlightenLogin, String enlightenPassword, String envoyIP, String envoySerial) throws Exception {
client = HttpClient.newBuilder()
.sslContext(disableCertificateCheck())
.cookieHandler(new CookieManager())
.build();
String urlParams = "username="+ enlightenLogin + "&password="+enlightenPassword;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://entrez.enphaseenergy.com/login"))
.headers("Content-Type", "application/x-www-form-urlencoded")
.POST(BodyPublishers.ofString(urlParams))
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
urlParams = "client=envoy&serialNum="+envoySerial;
request = HttpRequest.newBuilder()
.uri(URI.create("https://entrez.enphaseenergy.com/entrez_tokens"))
.headers("Content-Type", "application/x-www-form-urlencoded")
.POST(BodyPublishers.ofString(urlParams))
.build();
response = client.send(request, BodyHandlers.ofString());
String body = response.body();
// <textarea name="accessToken" id="JWTToken" cols="30" rows="10" >eyJr...rA</textarea>
int i = body.indexOf("id=\"JWTToken\"");
if (i < 0) throw new Exception("JWTToken not retrieved");
i = body.indexOf('>', i) +1;
int j = body.indexOf("</textarea>", i);
String JwtToken = body.substring(i,j);
request = HttpRequest.newBuilder()
.uri(URI.create("https://"+envoyIP+"/auth/check_jwt"))
.headers("Authorization", "Bearer "+JwtToken)
.build();
response = client.send(request, BodyHandlers.ofString());
}
public static String get(String url) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
HttpResponse<String> response;
try {
response = client.send(request, BodyHandlers.ofString());
return response.body();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
Example output from main():
JSON:
{
"wattHoursToday": 12468,
"wattHoursSevenDays": 18100792,
"wattHoursLifetime": 18113260,
"wattsNow": 4683
}
Last edited: