Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,6 @@
*/
@NonNullByDefault
public class HueSyncConstants {
public static class EXCEPTION_TYPES {
public static class CONNECTION {
public static final String UNAUTHORIZED_401 = "invalidLogin";
public static final String NOT_FOUND_404 = "notFound";
public static final String INTERNAL_SERVER_ERROR_500 = "deviceError";
}
}

public static class ENDPOINTS {
public static final String DEVICE = "device";
Expand Down Expand Up @@ -89,11 +82,6 @@ public static class HDMI {
public static final String PARAMETER_HOST = "host";
public static final String PARAMETER_PORT = "port";

public static final Integer REGISTRATION_INITIAL_DELAY = 5;
public static final Integer REGISTRATION_INTERVAL = 1;

public static final Integer POLL_INITIAL_DELAY = 10;

public static final String REGISTRATION_ID = "registrationId";
public static final String API_TOKEN = "apiAccessToken";
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes;
import org.openhab.binding.huesync.internal.HueSyncConstants.ENDPOINTS;
import org.openhab.binding.huesync.internal.config.HueSyncConfiguration;
import org.openhab.binding.huesync.internal.exceptions.HueSyncConnectionException;
import org.openhab.core.io.net.http.TlsTrustManagerProvider;
import org.osgi.framework.BundleContext;
Expand All @@ -48,17 +49,16 @@
/**
*
* @author Patrik Gfeller - Initial Contribution
* @author Patrik Gfeller - Issue #18376, Fix/improve log message and exception handling
*/
@NonNullByDefault
public class HueSyncConnection {
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
/**
* Request format: The Sync Box API can be accessed locally via HTTPS on root
* level (port 443,
* /api/v1), resource level /api/v1/<resource> and in some cases sub-resource
* level
* /api/v1/<resource>/<sub-resource>.
* level (port 443, /api/v1), resource level /api/v1/<resource>
* and in some cases sub-resource level /api/v1/<resource>/<sub-resource>.
*/
private static final String REQUEST_FORMAT = "https://%s:%s/%s/%s";
private static final String API = "api/v1";
Expand Down Expand Up @@ -110,10 +110,10 @@ protected ContentResponse execute() throws InterruptedException, ExecutionExcept

protected String registrationId = "";

public HueSyncConnection(HttpClient httpClient, String host, Integer port)
public HueSyncConnection(HttpClient httpClient, HueSyncConfiguration configuration)
throws CertificateException, IOException, URISyntaxException {
this.host = host;
this.port = port;
this.host = configuration.host;
this.port = configuration.port;

this.deviceUri = new URI(String.format("https://%s:%s", this.host, this.port));

Expand All @@ -123,9 +123,10 @@ public HueSyncConnection(HttpClient httpClient, String host, Integer port)
this.tlsProviderService = context.registerService(TlsTrustManagerProvider.class.getName(), trustManagerProvider,
null);
this.httpClient = httpClient;
this.updateAuthentication(configuration.registrationId, configuration.apiAccessToken);
}

public void updateAuthentication(String id, String token) {
public final void updateAuthentication(String id, String token) {
this.removeAuthentication();

if (!id.isBlank() && !token.isBlank()) {
Expand All @@ -139,7 +140,6 @@ public void updateAuthentication(String id, String token) {
// #region protected
protected @Nullable <T> T executeRequest(HttpMethod method, String endpoint, String payload,
@Nullable Class<T> type) throws HueSyncConnectionException {

return this.executeRequest(new Request(method, endpoint, payload), type);
}

Expand Down Expand Up @@ -173,43 +173,63 @@ protected void dispose() {
// #region private

private @Nullable <T> T executeRequest(Request request, @Nullable Class<T> type) throws HueSyncConnectionException {
String message = "@text/connection.generic-error";
var message = "@text/connection.generic-error";

try {
ContentResponse response = request.execute();
var response = request.execute();

/*
* 400 Invalid State: Registration in progress
*
* 401 Authentication failed: If credentials are missing or invalid, errors out.
* If
* credentials are missing, continues on to GET only the Configuration state
* when
* unauthenticated, to allow for device identification.
* If credentials are missing, continues on to GET only the Configuration
* state when unauthenticated, to allow for device identification.
*
* 404 Invalid URI Path: Accessing URI path which is not supported
*
* 500 Internal: Internal errors like out of memory
*/
switch (response.getStatus()) {
case HttpStatus.OK_200 -> {
case HttpStatus.OK_200:
return this.deserialize(response.getContentAsString(), type);
}
case HttpStatus.BAD_REQUEST_400 -> {
logger.debug("registration in progress: no token received yet");
return null;
}
case HttpStatus.UNAUTHORIZED_401 -> message = "@text/connection.invalid-login";
case HttpStatus.NOT_FOUND_404 -> message = "@text/connection.generic-error";
}
throw new HueSyncConnectionException(message, new HttpResponseException(message, response));
} catch (JsonProcessingException | InterruptedException | ExecutionException | TimeoutException e) {

var logMessage = message + " {}";
this.logger.warn(logMessage, e.toString());
handleResponseStatus(response.getStatus(), new HttpResponseException(response.getReason(), response));
} catch (ExecutionException e) {
this.logger.trace("{}: {}", e.getMessage(), message);

if (e.getCause() instanceof HttpResponseException httpResponseException) {
handleResponseStatus(httpResponseException.getResponse().getStatus(), httpResponseException);
}

throw new HueSyncConnectionException(message, e);
} catch (HttpResponseException e) {
handleResponseStatus(e.getResponse().getStatus(), e);
} catch (JsonProcessingException | InterruptedException | TimeoutException e) {
this.logger.trace("{}: {}", e.getMessage(), message);

throw new HueSyncConnectionException(message, e);
}

throw new HueSyncConnectionException(message);
}

private void handleResponseStatus(int status, Exception e) throws HueSyncConnectionException {
var message = "@text/connection.generic-error";

switch (status) {
case HttpStatus.BAD_REQUEST_400:
case HttpStatus.UNAUTHORIZED_401:
message = "@text/connection.invalid-login";
break;
case HttpStatus.NOT_FOUND_404:
message = "@text/connection.generic-error";
break;
}

this.logger.trace("Status: {}, Message Key: {}", status, message);

throw new HueSyncConnectionException(message, e);
}

private @Nullable <T> T deserialize(String json, @Nullable Class<T> type) throws JsonProcessingException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
* Handles the connection to a Hue HDMI Sync Box using the official API.
*
* @author Patrik Gfeller - Initial Contribution
* @author Patrik Gfeller - Issue #18376, Fix/improve log message and exception handling
*/
@NonNullByDefault
public class HueSyncDeviceConnection {
Expand All @@ -62,11 +63,15 @@ public class HueSyncDeviceConnection {

public HueSyncDeviceConnection(HttpClient httpClient, HueSyncConfiguration configuration,
HueSyncExceptionHandler exceptionHandler) throws CertificateException, IOException, URISyntaxException {

this.exceptionHandler = exceptionHandler;
this.connection = new HueSyncConnection(httpClient, configuration.host, configuration.port);
try {
this.connection = new HueSyncConnection(httpClient, configuration);

registerCommandHandlers();
registerCommandHandlers();
} catch (IOException | URISyntaxException | CertificateException e) {
exceptionHandler.handle(e);
throw e;
}
}

// #region private
Expand Down Expand Up @@ -200,9 +205,9 @@ public void dispose() {
this.connection.dispose();
}

public void updateConfiguration(HueSyncConfiguration config) {
this.logger.debug("Connection configuration update for device {}:{} - Registration Id [{}]", config.host,
config.port, config.registrationId);
public void updateAuthentication(HueSyncConfiguration config) {
this.logger.debug("Configure authentication for device {}:{} - Registration Id [{}]", config.host, config.port,
config.registrationId);

this.connection.updateAuthentication(config.registrationId, config.apiAccessToken);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
*/
package org.openhab.binding.huesync.internal.exceptions;

import java.util.Optional;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
*
* @author Patrik Gfeller - Initial contribution
* @author Patrik Gfeller - Issue #18376, Fix/improve log message and exception handling
*/
@NonNullByDefault
public class HueSyncConnectionException extends HueSyncException {
Expand All @@ -36,4 +39,16 @@ public HueSyncConnectionException(String message) {
public @Nullable Exception getInnerException() {
return this.innerException;
}

@Override
public @Nullable String getLocalizedMessage() {
var innerMessage = Optional.ofNullable(this.innerException.getLocalizedMessage());
var message = super.getLocalizedMessage();

if (innerMessage.isPresent()) {
message = message + " (" + innerMessage.get() + ")";
}

return message;
}
}
Loading