[meater] Fix broken cloud communication (#16994)

* Improve error handling

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
Jacob Laursen 2024-07-05 16:55:05 +02:00
parent 4b7220c552
commit ac2ff96f25
2 changed files with 56 additions and 51 deletions

View File

@ -35,6 +35,7 @@ import org.openhab.binding.meater.internal.dto.MeaterProbeDTO.Device;
import org.openhab.binding.meater.internal.exceptions.MeaterAuthenticationException; import org.openhab.binding.meater.internal.exceptions.MeaterAuthenticationException;
import org.openhab.binding.meater.internal.exceptions.MeaterException; import org.openhab.binding.meater.internal.exceptions.MeaterException;
import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.LocaleProvider;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -63,6 +64,7 @@ public class MeaterRestAPI {
private final Gson gson; private final Gson gson;
private final HttpClient httpClient; private final HttpClient httpClient;
private final MeaterBridgeConfiguration configuration; private final MeaterBridgeConfiguration configuration;
private final String userAgent;
private String authToken = ""; private String authToken = "";
private LocaleProvider localeProvider; private LocaleProvider localeProvider;
@ -72,42 +74,42 @@ public class MeaterRestAPI {
this.configuration = configuration; this.configuration = configuration;
this.httpClient = httpClient; this.httpClient = httpClient;
this.localeProvider = localeProvider; this.localeProvider = localeProvider;
userAgent = "openHAB/" + FrameworkUtil.getBundle(this.getClass()).getVersion().toString();
} }
public boolean refresh(Map<String, MeaterProbeDTO.Device> meaterProbeThings) { public void refresh(Map<String, MeaterProbeDTO.Device> meaterProbeThings) throws MeaterException {
try { MeaterProbeDTO dto = getDevices(MeaterProbeDTO.class);
MeaterProbeDTO dto = getDevices(MeaterProbeDTO.class); if (dto != null) {
if (dto != null) { List<Device> devices = dto.getData().getDevices();
List<Device> devices = dto.getData().getDevices(); if (devices != null) {
if (devices != null) { if (!devices.isEmpty()) {
if (!devices.isEmpty()) { for (Device meaterProbe : devices) {
for (Device meaterProbe : devices) { meaterProbeThings.put(meaterProbe.id, meaterProbe);
meaterProbeThings.put(meaterProbe.id, meaterProbe);
}
} else {
meaterProbeThings.clear();
} }
return true; } else {
meaterProbeThings.clear();
} }
} }
} catch (MeaterException e) {
logger.warn("Failed to refresh! {}", e.getMessage());
} }
return false;
} }
private void login() throws MeaterException { private void login() throws MeaterException {
try { try {
// Login // Login
String json = "{ \"email\": \"" + configuration.email + "\", \"password\": \"" + configuration.password String json = """
+ "\" }"; {
Request request = httpClient.newRequest(API_ENDPOINT + LOGIN).method(HttpMethod.POST) "email": "%s",
.timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); "password": "%s"
request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE); }
request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE); """.formatted(configuration.email, configuration.password);
request.content(new StringContentProvider(json), JSON_CONTENT_TYPE); Request request = httpClient.newRequest(API_ENDPOINT + LOGIN) //
.method(HttpMethod.POST) //
.timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS) //
.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE) //
.agent(userAgent) //
.content(new StringContentProvider(json), JSON_CONTENT_TYPE);
logger.trace("{}.", request.toString()); logger.trace("{}", request.toString());
ContentResponse httpResponse = request.send(); ContentResponse httpResponse = request.send();
if (!HttpStatus.isSuccess(httpResponse.getStatus())) { if (!HttpStatus.isSuccess(httpResponse.getStatus())) {
@ -135,24 +137,25 @@ public class MeaterRestAPI {
try { try {
for (int i = 0; i < MAX_RETRIES; i++) { for (int i = 0; i < MAX_RETRIES; i++) {
try { try {
Request request = httpClient.newRequest(API_ENDPOINT + uri).method(HttpMethod.GET) Request request = httpClient.newRequest(API_ENDPOINT + uri) //
.timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); .method(HttpMethod.GET) //
request.header(HttpHeader.AUTHORIZATION, "Bearer " + authToken); .timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)
request.header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE); .header(HttpHeader.AUTHORIZATION, "Bearer " + authToken)
request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE); .header(HttpHeader.ACCEPT, JSON_CONTENT_TYPE)
request.header(HttpHeader.ACCEPT_LANGUAGE, localeProvider.getLocale().getLanguage()); .header(HttpHeader.ACCEPT_LANGUAGE, localeProvider.getLocale().getLanguage())
.agent(userAgent);
ContentResponse response = request.send(); ContentResponse response = request.send();
String content = response.getContentAsString(); String content = response.getContentAsString();
logger.trace("API response: {}", content); logger.trace("API response: {}", content);
if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) { int status = response.getStatus();
if (status == HttpStatus.UNAUTHORIZED_401) {
// This will currently not happen because "WWW-Authenticate" header is missing; see below. // This will currently not happen because "WWW-Authenticate" header is missing; see below.
logger.debug("getFromApi failed, authentication failed, HTTP status: 401"); logger.debug("getFromApi failed, authentication failed, HTTP status: 401");
throw new MeaterAuthenticationException("Authentication failed"); throw new MeaterAuthenticationException("Authentication failed");
} else if (!HttpStatus.isSuccess(response.getStatus())) { } else if (!HttpStatus.isSuccess(status)) {
logger.debug("getFromApi failed, HTTP status: {}", response.getStatus()); throw new MeaterException(HttpStatus.getCode(status).getMessage());
throw new MeaterException("Failed to fetch from API!");
} else { } else {
return content; return content;
} }
@ -160,7 +163,7 @@ public class MeaterRestAPI {
logger.debug("TimeoutException error in get: {}", e.getMessage()); logger.debug("TimeoutException error in get: {}", e.getMessage());
} }
} }
throw new MeaterException("Failed to fetch from API!"); throw new MeaterException("Failed to fetch from API");
} catch (ExecutionException e) { } catch (ExecutionException e) {
Throwable cause = e.getCause(); Throwable cause = e.getCause();
if (cause instanceof HttpResponseException httpResponseException) { if (cause instanceof HttpResponseException httpResponseException) {
@ -201,7 +204,7 @@ public class MeaterRestAPI {
} }
if (json.isEmpty()) { if (json.isEmpty()) {
throw new MeaterException("JSON from API is empty!"); throw new MeaterException("JSON from API is empty");
} else { } else {
try { try {
return gson.fromJson(json, dto); return gson.fromJson(json, dto);

View File

@ -28,6 +28,7 @@ import org.openhab.binding.meater.internal.MeaterBridgeConfiguration;
import org.openhab.binding.meater.internal.api.MeaterRestAPI; import org.openhab.binding.meater.internal.api.MeaterRestAPI;
import org.openhab.binding.meater.internal.discovery.MeaterDiscoveryService; import org.openhab.binding.meater.internal.discovery.MeaterDiscoveryService;
import org.openhab.binding.meater.internal.dto.MeaterProbeDTO; import org.openhab.binding.meater.internal.dto.MeaterProbeDTO;
import org.openhab.binding.meater.internal.exceptions.MeaterException;
import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Bridge;
@ -109,23 +110,24 @@ public class MeaterBridgeHandler extends BaseBridgeHandler {
meaterProbeThings.clear(); meaterProbeThings.clear();
} }
private boolean refreshAndUpdateStatus() { private void refreshAndUpdateStatus() {
MeaterRestAPI localAPI = api; MeaterRestAPI localAPI = api;
if (localAPI != null) { if (localAPI == null) {
if (localAPI.refresh(meaterProbeThings)) { return;
updateStatus(ThingStatus.ONLINE); }
getThing().getThings().stream().forEach(thing -> {
MeaterHandler handler = (MeaterHandler) thing.getHandler(); try {
if (handler != null) { localAPI.refresh(meaterProbeThings);
handler.update(); updateStatus(ThingStatus.ONLINE);
} getThing().getThings().stream().forEach(thing -> {
}); MeaterHandler handler = (MeaterHandler) thing.getHandler();
return true; if (handler != null) {
} else { handler.update();
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); }
} });
} catch (MeaterException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
} }
return false;
} }
private void startAutomaticRefresh() { private void startAutomaticRefresh() {