[senseenergy] Address reconnect issues on failure (#18463)
* Address reconnect issues on failure Signed-off-by: Jeff James <jeff@james-online.com>pull/18636/head
parent
d86fea3104
commit
4b175571be
|
@ -18,8 +18,6 @@ import java.time.Instant;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import javax.measure.quantity.Dimensionless;
|
import javax.measure.quantity.Dimensionless;
|
||||||
import javax.measure.quantity.Energy;
|
import javax.measure.quantity.Energy;
|
||||||
|
@ -105,7 +103,7 @@ public class SenseEnergyMonitorActions implements ThingActions {
|
||||||
SenseEnergyApiGetTrends trends;
|
SenseEnergyApiGetTrends trends;
|
||||||
try {
|
try {
|
||||||
trends = localDeviceHandler.getApi().getTrendData(localDeviceHandler.getId(), trendScale, localDateTime);
|
trends = localDeviceHandler.getApi().getTrendData(localDeviceHandler.getId(), trendScale, localDateTime);
|
||||||
} catch (InterruptedException | TimeoutException | ExecutionException | SenseEnergyApiException e) {
|
} catch (SenseEnergyApiException e) {
|
||||||
logger.warn("queryEnergyTrends function failed - {}", e.getMessage());
|
logger.warn("queryEnergyTrends function failed - {}", e.getMessage());
|
||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.client.util.FormContentProvider;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.util.Fields;
|
import org.eclipse.jetty.util.Fields;
|
||||||
|
import org.openhab.binding.senseenergy.internal.api.SenseEnergyApiException.SEVERITY;
|
||||||
import org.openhab.binding.senseenergy.internal.api.dto.SenseEnergyApiAuthenticate;
|
import org.openhab.binding.senseenergy.internal.api.dto.SenseEnergyApiAuthenticate;
|
||||||
import org.openhab.binding.senseenergy.internal.api.dto.SenseEnergyApiDevice;
|
import org.openhab.binding.senseenergy.internal.api.dto.SenseEnergyApiDevice;
|
||||||
import org.openhab.binding.senseenergy.internal.api.dto.SenseEnergyApiGetTrends;
|
import org.openhab.binding.senseenergy.internal.api.dto.SenseEnergyApiGetTrends;
|
||||||
|
@ -60,7 +61,6 @@ import com.google.gson.JsonSyntaxException;
|
||||||
* implementation here: https://github.com/scottbonline/sense
|
* implementation here: https://github.com/scottbonline/sense
|
||||||
*
|
*
|
||||||
* @author Jeff James - Initial contribution
|
* @author Jeff James - Initial contribution
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class SenseEnergyApi {
|
public class SenseEnergyApi {
|
||||||
|
@ -119,17 +119,8 @@ public class SenseEnergyApi {
|
||||||
* @param password
|
* @param password
|
||||||
*
|
*
|
||||||
* @return a set of IDs for all the monitors associated with this account
|
* @return a set of IDs for all the monitors associated with this account
|
||||||
*
|
|
||||||
* @throws SenseEnergyApiException on authentication error
|
|
||||||
*
|
|
||||||
* @throws InterruptedException
|
|
||||||
*
|
|
||||||
* @throws TimeoutException
|
|
||||||
*
|
|
||||||
* @throws ExecutionException
|
|
||||||
*/
|
*/
|
||||||
public Set<Long> initialize(String email, String password)
|
public Set<Long> initialize(String email, String password) throws SenseEnergyApiException {
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
|
||||||
Fields fields = new Fields();
|
Fields fields = new Fields();
|
||||||
fields.put("email", email);
|
fields.put("email", email);
|
||||||
fields.put("password", password);
|
fields.put("password", password);
|
||||||
|
@ -143,7 +134,7 @@ public class SenseEnergyApi {
|
||||||
SenseEnergyApiAuthenticate.class);
|
SenseEnergyApiAuthenticate.class);
|
||||||
|
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
throw new SenseEnergyApiException("@text/api.response-invalid", false);
|
throw new SenseEnergyApiException("@text/api.response-invalid", SenseEnergyApiException.SEVERITY.FATAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken = data.accessToken;
|
accessToken = data.accessToken;
|
||||||
|
@ -157,17 +148,8 @@ public class SenseEnergyApi {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* renew authentication credentials. Timeout of credentials is ~24 hours.
|
* renew authentication credentials. Timeout of credentials is ~24 hours.
|
||||||
*
|
|
||||||
* @throws InterruptedException
|
|
||||||
*
|
|
||||||
* @throws TimeoutException
|
|
||||||
*
|
|
||||||
* @throws ExecutionException
|
|
||||||
*
|
|
||||||
* @throws SenseEnergyApiException
|
|
||||||
*/
|
*/
|
||||||
public void refreshToken()
|
public void refreshToken() throws SenseEnergyApiException {
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
|
||||||
Fields fields = new Fields();
|
Fields fields = new Fields();
|
||||||
fields.add("user_id", Long.toString(this.userID));
|
fields.add("user_id", Long.toString(this.userID));
|
||||||
fields.add("refresh_token", this.refreshToken);
|
fields.add("refresh_token", this.refreshToken);
|
||||||
|
@ -181,7 +163,7 @@ public class SenseEnergyApi {
|
||||||
SenseEnergyApiRefreshToken.class);
|
SenseEnergyApiRefreshToken.class);
|
||||||
|
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
throw new SenseEnergyApiException("@text/api.response-invalid", false);
|
throw new SenseEnergyApiException("text/api.response-invalid", SenseEnergyApiException.SEVERITY.TRANSIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Successful refreshToken {}", data.accessToken);
|
logger.debug("Successful refreshToken {}", data.accessToken);
|
||||||
|
@ -192,7 +174,7 @@ public class SenseEnergyApi {
|
||||||
tokenExpiresAt = data.expires.minus(1, ChronoUnit.HOURS); // refresh an hour before token expires
|
tokenExpiresAt = data.expires.minus(1, ChronoUnit.HOURS); // refresh an hour before token expires
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logout() throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
public void logout() throws SenseEnergyApiException {
|
||||||
Request request = httpClient.newRequest(APIURL_LOGOUT).method(HttpMethod.GET);
|
Request request = httpClient.newRequest(APIURL_LOGOUT).method(HttpMethod.GET);
|
||||||
|
|
||||||
sendRequest(request);
|
sendRequest(request);
|
||||||
|
@ -204,17 +186,8 @@ public class SenseEnergyApi {
|
||||||
* @param id of the monitor
|
* @param id of the monitor
|
||||||
*
|
*
|
||||||
* @return dto structure containing monitor info
|
* @return dto structure containing monitor info
|
||||||
*
|
|
||||||
* @throws InterruptedException
|
|
||||||
*
|
|
||||||
* @throws TimeoutException
|
|
||||||
*
|
|
||||||
* @throws ExecutionException
|
|
||||||
*
|
|
||||||
* @throws SenseEnergyApiException
|
|
||||||
*/
|
*/
|
||||||
public SenseEnergyApiMonitor getMonitorOverview(long id)
|
public SenseEnergyApiMonitor getMonitorOverview(long id) throws SenseEnergyApiException {
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
|
||||||
String url = String.format(APIURL_MONITOR_OVERVIEW, id);
|
String url = String.format(APIURL_MONITOR_OVERVIEW, id);
|
||||||
Request request = httpClient.newRequest(url).method(HttpMethod.GET);
|
Request request = httpClient.newRequest(url).method(HttpMethod.GET);
|
||||||
|
|
||||||
|
@ -222,17 +195,11 @@ public class SenseEnergyApi {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JsonObject jsonResponse = JsonParser.parseString(response.getContentAsString()).getAsJsonObject();
|
JsonObject jsonResponse = JsonParser.parseString(response.getContentAsString()).getAsJsonObject();
|
||||||
SenseEnergyApiMonitor monitor = gson.fromJson(
|
return apiRequireNonNull(
|
||||||
jsonResponse.getAsJsonObject("monitor_overview").getAsJsonObject("monitor"),
|
gson.fromJson(jsonResponse.getAsJsonObject("monitor_overview").getAsJsonObject("monitor"),
|
||||||
SenseEnergyApiMonitor.class);
|
SenseEnergyApiMonitor.class));
|
||||||
|
|
||||||
if (monitor == null) {
|
|
||||||
throw new SenseEnergyApiException("@text/api.response-invalid", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return monitor;
|
|
||||||
} catch (JsonSyntaxException e) {
|
} catch (JsonSyntaxException e) {
|
||||||
throw new SenseEnergyApiException("@text/api.response-invalid", false);
|
throw new SenseEnergyApiException("@text/api.response-invalid", SenseEnergyApiException.SEVERITY.TRANSIENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,32 +209,18 @@ public class SenseEnergyApi {
|
||||||
* @param id - id of monitor
|
* @param id - id of monitor
|
||||||
*
|
*
|
||||||
* @return dto structure containing monitor status
|
* @return dto structure containing monitor status
|
||||||
*
|
|
||||||
* @throws InterruptedException
|
|
||||||
*
|
|
||||||
* @throws TimeoutException
|
|
||||||
*
|
|
||||||
* @throws ExecutionException
|
|
||||||
*
|
|
||||||
* @throws SenseEnergyApiException
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public SenseEnergyApiMonitorStatus getMonitorStatus(long id) throws SenseEnergyApiException {
|
||||||
public SenseEnergyApiMonitorStatus getMonitorStatus(long id)
|
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
|
||||||
String url = String.format(APIURL_MONITOR_STATUS, id);
|
String url = String.format(APIURL_MONITOR_STATUS, id);
|
||||||
Request request = httpClient.newRequest(url).method(HttpMethod.GET);
|
Request request = httpClient.newRequest(url).method(HttpMethod.GET);
|
||||||
|
|
||||||
ContentResponse response = sendRequest(request);
|
ContentResponse response = sendRequest(request);
|
||||||
|
|
||||||
final SenseEnergyApiMonitorStatus data = gson.fromJson(response.getContentAsString(),
|
return apiRequireNonNull(gson.fromJson(response.getContentAsString(), SenseEnergyApiMonitorStatus.class));
|
||||||
SenseEnergyApiMonitorStatus.class);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public SenseEnergyApiGetTrends getTrendData(long id, TrendScale trendScale)
|
public SenseEnergyApiGetTrends getTrendData(long id, TrendScale trendScale) throws SenseEnergyApiException {
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
|
||||||
return getTrendData(id, trendScale, Instant.now());
|
return getTrendData(id, trendScale, Instant.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,27 +234,16 @@ public class SenseEnergyApi {
|
||||||
* @param datetime a datetime within the scale of which to receive data. Does not need to be the start or end .
|
* @param datetime a datetime within the scale of which to receive data. Does not need to be the start or end .
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*
|
|
||||||
* @throws InterruptedException
|
|
||||||
*
|
|
||||||
* @throws TimeoutException
|
|
||||||
*
|
|
||||||
* @throws ExecutionException
|
|
||||||
*
|
|
||||||
* @throws SenseEnergyApiException
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public SenseEnergyApiGetTrends getTrendData(long id, TrendScale trendScale, Instant datetime)
|
public SenseEnergyApiGetTrends getTrendData(long id, TrendScale trendScale, Instant datetime)
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
throws SenseEnergyApiException {
|
||||||
String url = String.format(APIURL_GET_TRENDS, id, trendScale.toString(), datetime.toString());
|
String url = String.format(APIURL_GET_TRENDS, id, trendScale.toString(), datetime.toString());
|
||||||
Request request = httpClient.newRequest(url).method(HttpMethod.GET);
|
Request request = httpClient.newRequest(url).method(HttpMethod.GET);
|
||||||
|
|
||||||
ContentResponse response = sendRequest(request);
|
ContentResponse response = sendRequest(request);
|
||||||
|
|
||||||
final SenseEnergyApiGetTrends data = gson.fromJson(response.getContentAsString(),
|
return gson.fromJson(response.getContentAsString(), SenseEnergyApiGetTrends.class);
|
||||||
SenseEnergyApiGetTrends.class);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -323,17 +265,8 @@ public class SenseEnergyApi {
|
||||||
* @param id of the monitor device
|
* @param id of the monitor device
|
||||||
*
|
*
|
||||||
* @return Map of discovered devices with the ID of the device as key and the dto object SenseEnergyApiDevice
|
* @return Map of discovered devices with the ID of the device as key and the dto object SenseEnergyApiDevice
|
||||||
*
|
|
||||||
* @throws InterruptedException
|
|
||||||
*
|
|
||||||
* @throws TimeoutException
|
|
||||||
*
|
|
||||||
* @throws ExecutionException
|
|
||||||
*
|
|
||||||
* @throws SenseEnergyApiException
|
|
||||||
*/
|
*/
|
||||||
public Map<String, SenseEnergyApiDevice> getDevices(long id)
|
public Map<String, SenseEnergyApiDevice> getDevices(long id) throws SenseEnergyApiException {
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
|
||||||
String url = String.format(APIURL_GET_DEVICES, id);
|
String url = String.format(APIURL_GET_DEVICES, id);
|
||||||
Request request = httpClient.newRequest(url).method(HttpMethod.GET);
|
Request request = httpClient.newRequest(url).method(HttpMethod.GET);
|
||||||
|
|
||||||
|
@ -341,9 +274,6 @@ public class SenseEnergyApi {
|
||||||
|
|
||||||
JsonArray jsonDevices = JsonParser.parseString(response.getContentAsString()).getAsJsonArray();
|
JsonArray jsonDevices = JsonParser.parseString(response.getContentAsString()).getAsJsonArray();
|
||||||
|
|
||||||
@SuppressWarnings("null") // prevent this warning on d.tags - [WARNING] Potential null pointer access: this
|
|
||||||
// expression has
|
|
||||||
// a '@Nullable' type
|
|
||||||
Map<String, SenseEnergyApiDevice> mapDevices = StreamSupport.stream(jsonDevices.spliterator(), false) //
|
Map<String, SenseEnergyApiDevice> mapDevices = StreamSupport.stream(jsonDevices.spliterator(), false) //
|
||||||
.map(j -> jsonToSenseEnergyDevice(j)) //
|
.map(j -> jsonToSenseEnergyDevice(j)) //
|
||||||
.filter(Objects::nonNull) //
|
.filter(Objects::nonNull) //
|
||||||
|
@ -362,28 +292,34 @@ public class SenseEnergyApi {
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void verifyToken()
|
public void verifyToken() throws SenseEnergyApiException {
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
|
||||||
if (tokenExpiresAt.isBefore(Instant.now())) {
|
if (tokenExpiresAt.isBefore(Instant.now())) {
|
||||||
refreshToken();
|
refreshToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentResponse sendRequest(Request request)
|
ContentResponse sendRequest(Request request) throws SenseEnergyApiException {
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
|
||||||
return sendRequest(request, true);
|
return sendRequest(request, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentResponse sendRequest(Request request, boolean verifyToken)
|
ContentResponse sendRequest(Request request, boolean verifyToken) throws SenseEnergyApiException {
|
||||||
throws InterruptedException, TimeoutException, ExecutionException, SenseEnergyApiException {
|
|
||||||
if (verifyToken) {
|
if (verifyToken) {
|
||||||
verifyToken();
|
verifyToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
setHeaders(request);
|
setHeaders(request);
|
||||||
|
|
||||||
logger.trace("REQUEST: {}", request.toString());
|
ContentResponse response;
|
||||||
ContentResponse response = request.send();
|
try {
|
||||||
|
logger.trace("REQUEST: {}", request.toString());
|
||||||
|
response = request.send();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new SenseEnergyApiException("@text/api.connection-closed", SEVERITY.FATAL, e);
|
||||||
|
} catch (TimeoutException | ExecutionException e) {
|
||||||
|
throw new SenseEnergyApiException("@text/api.connection-timeout", SEVERITY.TRANSIENT, e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SenseEnergyApiException("@text/api.request-error", SenseEnergyApiException.SEVERITY.TRANSIENT, e);
|
||||||
|
}
|
||||||
logger.trace("RESPONSE: {}", response.getContentAsString());
|
logger.trace("RESPONSE: {}", response.getContentAsString());
|
||||||
|
|
||||||
switch (response.getStatus()) {
|
switch (response.getStatus()) {
|
||||||
|
@ -391,13 +327,24 @@ public class SenseEnergyApi {
|
||||||
break;
|
break;
|
||||||
case 400: // API responses with 400 when user credentials are invalid
|
case 400: // API responses with 400 when user credentials are invalid
|
||||||
case 401:
|
case 401:
|
||||||
throw new SenseEnergyApiException("@text/api.invalid-user-credentials", true);
|
throw new SenseEnergyApiException("@text/api.invalid-user-credentials",
|
||||||
|
SenseEnergyApiException.SEVERITY.CONFIG);
|
||||||
case 429:
|
case 429:
|
||||||
throw new SenseEnergyApiException("@text/api.rate-limit-exceeded", false);
|
throw new SenseEnergyApiException("@text/api.rate-limit-exceeded",
|
||||||
|
SenseEnergyApiException.SEVERITY.TRANSIENT);
|
||||||
default:
|
default:
|
||||||
throw new SenseEnergyApiException("Unexpected API error: " + response.getReason(), false);
|
throw new SenseEnergyApiException("Unexpected API error: " + response.getReason(),
|
||||||
|
SenseEnergyApiException.SEVERITY.TRANSIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> T apiRequireNonNull(@Nullable T obj) throws SenseEnergyApiException {
|
||||||
|
if (obj == null) {
|
||||||
|
throw new SenseEnergyApiException("@text/api.response-invalid", SenseEnergyApiException.SEVERITY.TRANSIENT);
|
||||||
|
} else {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
package org.openhab.binding.senseenergy.internal.api;
|
package org.openhab.binding.senseenergy.internal.api;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link SenseEnergyApiException} exception class for any api exception
|
* {@link SenseEnergyApiException} exception class for any api exception
|
||||||
|
@ -22,20 +23,33 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class SenseEnergyApiException extends Exception {
|
public class SenseEnergyApiException extends Exception {
|
||||||
private static final long serialVersionUID = -7059398508028583720L;
|
private static final long serialVersionUID = -7059398508028583720L;
|
||||||
private final boolean configurationIssue;
|
public final SEVERITY severity;
|
||||||
|
@Nullable
|
||||||
|
public final Exception e;
|
||||||
|
|
||||||
public SenseEnergyApiException(String message, boolean configurationIssue) {
|
public static enum SEVERITY {
|
||||||
super(message);
|
CONFIG,
|
||||||
this.configurationIssue = configurationIssue;
|
TRANSIENT,
|
||||||
|
DATA,
|
||||||
|
FATAL
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConfigurationIssue() {
|
public SenseEnergyApiException(String message, SEVERITY severity) {
|
||||||
return configurationIssue;
|
super(message);
|
||||||
|
this.severity = severity;
|
||||||
|
this.e = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SenseEnergyApiException(String message, SEVERITY severity, Exception e) {
|
||||||
|
super(message);
|
||||||
|
this.severity = severity;
|
||||||
|
this.e = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("SenseEnergyApiException{message='%s', configurationIssue=%b}", getMessage(),
|
Exception localE = e;
|
||||||
configurationIssue);
|
return String.format("SenseEnergyApiException{message='%s', severity=%s}",
|
||||||
|
(localE == null) ? getMessage() : localE.getMessage(), severity.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import java.io.IOException;
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.channels.ClosedByInterruptException;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
@ -78,17 +79,12 @@ public class SenseEnergyDatagram {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
connected = false;
|
logger.debug("datagram stop");
|
||||||
|
Thread localUdpThread = udpListener;
|
||||||
try {
|
if (localUdpThread != null) {
|
||||||
DatagramSocket localSocket = datagramSocket;
|
connected = false;
|
||||||
if (localSocket != null) {
|
localUdpThread.interrupt();
|
||||||
localSocket.close();
|
udpListener = null;
|
||||||
datagramSocket = null;
|
|
||||||
logger.debug("Closing datagram listener");
|
|
||||||
}
|
|
||||||
} catch (Exception exception) {
|
|
||||||
logger.debug("closeConnection(): Error closing connection - {}", exception.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +99,6 @@ public class SenseEnergyDatagram {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UDPListener implements Runnable {
|
private class UDPListener implements Runnable {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Run method. Runs the MessageListener thread
|
* Run method. Runs the MessageListener thread
|
||||||
*/
|
*/
|
||||||
|
@ -121,41 +116,48 @@ public class SenseEnergyDatagram {
|
||||||
|
|
||||||
DatagramPacket packet = new DatagramPacket(new byte[BUFFERSIZE], BUFFERSIZE);
|
DatagramPacket packet = new DatagramPacket(new byte[BUFFERSIZE], BUFFERSIZE);
|
||||||
|
|
||||||
while (connected) {
|
try {
|
||||||
try {
|
while (connected && !localSocket.isClosed() && !Thread.currentThread().isInterrupted()) {
|
||||||
localSocket.receive(packet);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.debug("Exception during packet read - {}", e.getMessage());
|
|
||||||
try {
|
try {
|
||||||
|
localSocket.receive(packet);
|
||||||
|
} catch (ClosedByInterruptException e) {
|
||||||
|
logger.debug("ClosedByInterruptExcepetion");
|
||||||
|
throw e;
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug("Exception during packet read - {}", e.getMessage());
|
||||||
Thread.sleep(100); // allow CPU to breath
|
Thread.sleep(100); // allow CPU to breath
|
||||||
} catch (InterruptedException ie) {
|
continue;
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't receive more than 1 request a second. Necessary to filter out receiving the same
|
// don't receive more than 1 request a second. Necessary to filter out receiving the same
|
||||||
// broadcast request packet on multiple interfaces (i.e. wi-fi and wired) at the same time
|
// broadcast request packet on multiple interfaces (i.e. wi-fi and wired) at the same time
|
||||||
if (System.nanoTime() < nextPacketTime) {
|
if (System.nanoTime() < nextPacketTime) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject jsonResponse;
|
JsonObject jsonResponse;
|
||||||
String decryptedPacket = new String(TpLinkEncryption.decrypt(packet.getData(), packet.getLength()));
|
String decryptedPacket = new String(TpLinkEncryption.decrypt(packet.getData(), packet.getLength()));
|
||||||
try {
|
try {
|
||||||
jsonResponse = JsonParser.parseString(decryptedPacket).getAsJsonObject();
|
jsonResponse = JsonParser.parseString(decryptedPacket).getAsJsonObject();
|
||||||
} catch (JsonSyntaxException jsonSyntaxException) {
|
} catch (JsonSyntaxException jsonSyntaxException) {
|
||||||
logger.trace("Invalid JSON received");
|
logger.trace("Invalid JSON received");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextPacketTime = System.nanoTime() + 1000000000L;
|
nextPacketTime = System.nanoTime() + 1000000000L;
|
||||||
if (jsonResponse.has("system") && jsonResponse.has("emeter")) {
|
if (jsonResponse.has("system") && jsonResponse.has("emeter")) {
|
||||||
SenseEnergyDatagramListener localPacketListener = packetListener;
|
SenseEnergyDatagramListener localPacketListener = packetListener;
|
||||||
if (localPacketListener != null) {
|
if (localPacketListener != null) {
|
||||||
localPacketListener.requestReceived(packet.getSocketAddress());
|
localPacketListener.requestReceived(packet.getSocketAddress());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (InterruptedException | ClosedByInterruptException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} finally {
|
||||||
|
localSocket.close();
|
||||||
|
datagramSocket = null;
|
||||||
|
connected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,12 @@
|
||||||
*/
|
*/
|
||||||
package org.openhab.binding.senseenergy.internal.api;
|
package org.openhab.binding.senseenergy.internal.api;
|
||||||
|
|
||||||
|
import static org.openhab.binding.senseenergy.internal.SenseEnergyBindingConstants.HEARTBEAT_MINUTES;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
@ -51,6 +54,10 @@ public class SenseEnergyWebSocket implements WebSocketListener {
|
||||||
private boolean closing;
|
private boolean closing;
|
||||||
private long monitorId;
|
private long monitorId;
|
||||||
|
|
||||||
|
private static int BACKOFF_TIME_START = 300;
|
||||||
|
private static int BACKOFF_TIME_MAX = (int) Duration.ofMinutes(HEARTBEAT_MINUTES).toMillis();
|
||||||
|
private int backOffTime = BACKOFF_TIME_START;
|
||||||
|
|
||||||
private Gson gson = new Gson();
|
private Gson gson = new Gson();
|
||||||
|
|
||||||
public boolean isClosing() {
|
public boolean isClosing() {
|
||||||
|
@ -62,7 +69,8 @@ public class SenseEnergyWebSocket implements WebSocketListener {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(long monitorId, String accessToken) throws Exception {
|
public void start(long monitorId, String accessToken)
|
||||||
|
throws InterruptedException, ExecutionException, IOException, URISyntaxException {
|
||||||
logger.debug("Starting Sense Energy WebSocket for monitor ID: {}", monitorId);
|
logger.debug("Starting Sense Energy WebSocket for monitor ID: {}", monitorId);
|
||||||
this.monitorId = monitorId;
|
this.monitorId = monitorId;
|
||||||
|
|
||||||
|
@ -71,16 +79,26 @@ public class SenseEnergyWebSocket implements WebSocketListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void restart(String accessToken)
|
public void restart(String accessToken)
|
||||||
throws InterruptedException, ExecutionException, IOException, URISyntaxException, Exception {
|
throws InterruptedException, ExecutionException, IOException, URISyntaxException {
|
||||||
logger.debug("Re-starting Sense Energy WebSocket");
|
logger.debug("Re-starting Sense Energy WebSocket");
|
||||||
|
|
||||||
stop();
|
stop();
|
||||||
start(monitorId, accessToken);
|
start(monitorId, accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void restartWithBackoff(String accessToken)
|
||||||
|
throws InterruptedException, ExecutionException, IOException, URISyntaxException {
|
||||||
|
logger.debug("Re-starting Sense Energy WebSocket - backoff {} ms", backOffTime);
|
||||||
|
|
||||||
|
stop();
|
||||||
|
Thread.sleep(backOffTime);
|
||||||
|
backOffTime = Math.min(backOffTime * 2, BACKOFF_TIME_MAX);
|
||||||
|
start(monitorId, accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void stop() {
|
public synchronized void stop() {
|
||||||
closing = true;
|
closing = true;
|
||||||
logger.trace("Stopping Sense Energy WebSocket");
|
logger.debug("Stopping Sense Energy WebSocket");
|
||||||
|
|
||||||
WebSocketSession localSession = session;
|
WebSocketSession localSession = session;
|
||||||
if (localSession != null) {
|
if (localSession != null) {
|
||||||
|
@ -115,6 +133,7 @@ public class SenseEnergyWebSocket implements WebSocketListener {
|
||||||
public void onWebSocketConnect(@Nullable Session session) {
|
public void onWebSocketConnect(@Nullable Session session) {
|
||||||
closing = false;
|
closing = false;
|
||||||
logger.debug("Connected to Sense Energy WebSocket");
|
logger.debug("Connected to Sense Energy WebSocket");
|
||||||
|
listener.onWebSocketConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -139,6 +158,8 @@ public class SenseEnergyWebSocket implements WebSocketListener {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug("onWebSocketText");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JsonObject jsonResponse = JsonParser.parseString(message).getAsJsonObject();
|
JsonObject jsonResponse = JsonParser.parseString(message).getAsJsonObject();
|
||||||
String type = jsonResponse.get("type").getAsString();
|
String type = jsonResponse.get("type").getAsString();
|
||||||
|
@ -150,6 +171,9 @@ public class SenseEnergyWebSocket implements WebSocketListener {
|
||||||
if (update != null) {
|
if (update != null) {
|
||||||
listener.onWebSocketRealtimeUpdate(update);
|
listener.onWebSocketRealtimeUpdate(update);
|
||||||
}
|
}
|
||||||
|
// Clear backoff time after a successful received packet to address issue of immediate Error/Close after
|
||||||
|
// Connect
|
||||||
|
backOffTime = BACKOFF_TIME_START;
|
||||||
} else if ("error".equals(type)) {
|
} else if ("error".equals(type)) {
|
||||||
logger.warn("WebSocket error {}", jsonResponse.get("payload").toString());
|
logger.warn("WebSocket error {}", jsonResponse.get("payload").toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,11 @@ import org.openhab.binding.senseenergy.internal.api.dto.SenseEnergyWebSocketReal
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public interface SenseEnergyWebSocketListener {
|
public interface SenseEnergyWebSocketListener {
|
||||||
|
/**
|
||||||
|
* called when web socket connects
|
||||||
|
*/
|
||||||
|
void onWebSocketConnect();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* called when the web socket is closed
|
* called when the web socket is closed
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,10 +18,8 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
@ -92,7 +90,7 @@ public class SenseEnergyBridgeHandler extends BaseBridgeHandler {
|
||||||
public void goOnline() {
|
public void goOnline() {
|
||||||
try {
|
try {
|
||||||
this.monitorIDs = api.initialize(config.email, config.password);
|
this.monitorIDs = api.initialize(config.email, config.password);
|
||||||
} catch (InterruptedException | TimeoutException | ExecutionException | SenseEnergyApiException e) {
|
} catch (SenseEnergyApiException e) {
|
||||||
handleApiException(e);
|
handleApiException(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -105,6 +103,7 @@ public class SenseEnergyBridgeHandler extends BaseBridgeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void heartbeat() {
|
private void heartbeat() {
|
||||||
|
logger.trace("heartbeat");
|
||||||
ThingStatus thingStatus = getThing().getStatus();
|
ThingStatus thingStatus = getThing().getStatus();
|
||||||
|
|
||||||
if (thingStatus == ThingStatus.OFFLINE
|
if (thingStatus == ThingStatus.OFFLINE
|
||||||
|
@ -120,7 +119,7 @@ public class SenseEnergyBridgeHandler extends BaseBridgeHandler {
|
||||||
// token is verified on each api call, called here in case no API calls are made in the alloted period
|
// token is verified on each api call, called here in case no API calls are made in the alloted period
|
||||||
try {
|
try {
|
||||||
getApi().verifyToken();
|
getApi().verifyToken();
|
||||||
} catch (InterruptedException | TimeoutException | ExecutionException | SenseEnergyApiException e) {
|
} catch (SenseEnergyApiException e) {
|
||||||
handleApiException(e);
|
handleApiException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,17 +132,27 @@ public class SenseEnergyBridgeHandler extends BaseBridgeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleApiException(Exception e) {
|
public void handleApiException(Exception e) {
|
||||||
ThingStatusDetail statusDetail = ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR;
|
|
||||||
|
|
||||||
if (e instanceof SenseEnergyApiException apiException) {
|
if (e instanceof SenseEnergyApiException apiException) {
|
||||||
statusDetail = apiException.isConfigurationIssue() ? ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR
|
switch (apiException.severity) {
|
||||||
: ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR;
|
case TRANSIENT:
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
|
||||||
|
break;
|
||||||
|
case CONFIG:
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR);
|
||||||
|
break;
|
||||||
|
case FATAL:
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.NONE, e.getMessage());
|
||||||
|
break;
|
||||||
|
case DATA:
|
||||||
|
logger.warn("Data exception: {}", e.toString());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.warn("SenseEnergyApiException: {}", e.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Unhandled Exception", e);
|
logger.warn("Unhandled Exception", e);
|
||||||
statusDetail = ThingStatusDetail.OFFLINE.NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatus(ThingStatus.OFFLINE, statusDetail, e.getLocalizedMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -16,6 +16,7 @@ import static org.openhab.binding.senseenergy.internal.SenseEnergyBindingConstan
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -26,7 +27,6 @@ import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.measure.Unit;
|
import javax.measure.Unit;
|
||||||
|
@ -170,7 +170,7 @@ public class SenseEnergyMonitorHandler extends BaseBridgeHandler
|
||||||
this.solarConfigured = apiMonitor.solarConfigured;
|
this.solarConfigured = apiMonitor.solarConfigured;
|
||||||
apiMonitorStatus = getApi().getMonitorStatus(id);
|
apiMonitorStatus = getApi().getMonitorStatus(id);
|
||||||
refreshDevices();
|
refreshDevices();
|
||||||
} catch (InterruptedException | TimeoutException | ExecutionException | SenseEnergyApiException e) {
|
} catch (SenseEnergyApiException e) {
|
||||||
handleApiException(e);
|
handleApiException(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ public class SenseEnergyMonitorHandler extends BaseBridgeHandler
|
||||||
|
|
||||||
try {
|
try {
|
||||||
webSocket.start(id, getApi().getAccessToken());
|
webSocket.start(id, getApi().getAccessToken());
|
||||||
} catch (Exception e) {
|
} catch (InterruptedException | ExecutionException | IOException | URISyntaxException e) {
|
||||||
handleApiException(e);
|
handleApiException(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -221,7 +221,7 @@ public class SenseEnergyMonitorHandler extends BaseBridgeHandler
|
||||||
logger.debug("heartbeat: webSocket not running");
|
logger.debug("heartbeat: webSocket not running");
|
||||||
try {
|
try {
|
||||||
webSocket.restart(getApi().getAccessToken());
|
webSocket.restart(getApi().getAccessToken());
|
||||||
} catch (Exception e) {
|
} catch (InterruptedException | ExecutionException | IOException | URISyntaxException e) {
|
||||||
handleApiException(e);
|
handleApiException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,17 +230,27 @@ public class SenseEnergyMonitorHandler extends BaseBridgeHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleApiException(Exception e) {
|
public void handleApiException(Exception e) {
|
||||||
ThingStatusDetail statusDetail = ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR;
|
|
||||||
|
|
||||||
if (e instanceof SenseEnergyApiException apiException) {
|
if (e instanceof SenseEnergyApiException apiException) {
|
||||||
statusDetail = apiException.isConfigurationIssue() ? ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR
|
switch (apiException.severity) {
|
||||||
: ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR;
|
case TRANSIENT:
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
|
||||||
|
break;
|
||||||
|
case CONFIG:
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR);
|
||||||
|
break;
|
||||||
|
case FATAL:
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.NONE, e.getMessage());
|
||||||
|
break;
|
||||||
|
case DATA:
|
||||||
|
logger.warn("Data exception: {}", e.toString());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.warn("SenseEnergyApiException: {}", e.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Unhandled Exception", e);
|
logger.warn("Unhandled Exception", e);
|
||||||
statusDetail = ThingStatusDetail.OFFLINE.NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatus(ThingStatus.OFFLINE, statusDetail, e.getLocalizedMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -355,13 +365,14 @@ public class SenseEnergyMonitorHandler extends BaseBridgeHandler
|
||||||
* Refreshes the list of devices by retrieving them from the API and then updating the map of DeviceTypes.
|
* Refreshes the list of devices by retrieving them from the API and then updating the map of DeviceTypes.
|
||||||
*/
|
*/
|
||||||
private void refreshDevices() {
|
private void refreshDevices() {
|
||||||
|
logger.trace("refreshDevices");
|
||||||
try {
|
try {
|
||||||
senseDevices = getApi().getDevices(id);
|
senseDevices = getApi().getDevices(id);
|
||||||
|
|
||||||
senseDevices.entrySet().stream() //
|
senseDevices.entrySet().stream() //
|
||||||
.filter(e -> !senseDevicesType.containsKey(e.getKey())) //
|
.filter(e -> !senseDevicesType.containsKey(e.getKey())) //
|
||||||
.forEach(e -> senseDevicesType.put(e.getKey(), deduceDeviceType(e.getValue())));
|
.forEach(e -> senseDevicesType.put(e.getKey(), deduceDeviceType(e.getValue())));
|
||||||
} catch (InterruptedException | TimeoutException | ExecutionException | SenseEnergyApiException e) {
|
} catch (SenseEnergyApiException e) {
|
||||||
handleApiException(e);
|
handleApiException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -538,11 +549,11 @@ public class SenseEnergyMonitorHandler extends BaseBridgeHandler
|
||||||
.isPresent();
|
.isPresent();
|
||||||
|
|
||||||
if (childOnline && !datagram.isRunning()) {
|
if (childOnline && !datagram.isRunning()) {
|
||||||
datagram.stop();
|
|
||||||
try {
|
try {
|
||||||
datagram.start(SENSE_DATAGRAM_BCAST_PORT, datagramListenerThreadName);
|
datagram.start(SENSE_DATAGRAM_BCAST_PORT, datagramListenerThreadName);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
handleApiException(e);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||||
|
logger.warn("Unable to start datagram: {}", e.getLocalizedMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,15 +615,26 @@ public class SenseEnergyMonitorHandler extends BaseBridgeHandler
|
||||||
|
|
||||||
/***** SenseEnergyeWSListener interfaces *****/
|
/***** SenseEnergyeWSListener interfaces *****/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketConnect() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWebSocketClose(int statusCode, @Nullable String reason) {
|
public void onWebSocketClose(int statusCode, @Nullable String reason) {
|
||||||
logger.debug("onWebSocketClose ({}), {}", statusCode, reason);
|
logger.debug("onWebSocketClose ({}), {}", statusCode, reason);
|
||||||
// will restart on heartbeat
|
try {
|
||||||
|
webSocket.restartWithBackoff(getApi().getAccessToken());
|
||||||
|
} catch (InterruptedException | ExecutionException | IOException | URISyntaxException e) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||||
|
logger.warn("Exeception when restarting webSocket: {}", e.getMessage());
|
||||||
|
// will retry at next heartbeat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWebSocketError(String msg) {
|
public void onWebSocketError(String msg) {
|
||||||
// no action - let heartbeat restart webSocket
|
logger.debug("onWebSocketError {}", msg);
|
||||||
|
// restart will occur on onWebSocketClose
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -86,6 +86,7 @@ api.invalid-user-credentials = Invalid user credentials, please check configurat
|
||||||
api.response-fail = API response fail
|
api.response-fail = API response fail
|
||||||
api.response-invalid = API response invalid
|
api.response-invalid = API response invalid
|
||||||
api.rate-limit-exceeded = API rate limit exceeded
|
api.rate-limit-exceeded = API rate limit exceeded
|
||||||
|
api.request-error = Error occurred during API request
|
||||||
|
|
||||||
# actions
|
# actions
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue