From 2fcd8b6c7a89deecec4592dd9692f354ffa39016 Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Fri, 4 Apr 2025 22:52:31 +0200 Subject: [PATCH 01/12] fix(communication): [huesync] Bugfix for #18376 [huesync] Exception message is not resolved using language resource strings #18376 - https://github.com/openhab/openhab-addons/issues/18376 --- .../connection/HueSyncConnection.java | 17 +-- .../internal/handler/HueSyncHandler.java | 106 +++++++++--------- .../huesync/internal/i18n/ResourceHelper.java | 12 +- 3 files changed, 60 insertions(+), 75 deletions(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java index 57b4e8bfef21e..282acdb5cbdcb 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java @@ -48,6 +48,7 @@ /** * * @author Patrik Gfeller - Initial Contribution + * @author Patrik Gfeller - Issue #18376, Fix/improve log message and exception handling */ @NonNullByDefault public class HueSyncConnection { @@ -55,10 +56,8 @@ public class HueSyncConnection { .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/ and in some cases sub-resource - * level - * /api/v1//. + * level (port 443, /api/v1), resource level /api/v1/ + * and in some cases sub-resource level /api/v1//. */ private static final String REQUEST_FORMAT = "https://%s:%s/%s/%s"; private static final String API = "api/v1"; @@ -182,10 +181,8 @@ protected void dispose() { * 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 * @@ -204,9 +201,7 @@ protected void dispose() { } throw new HueSyncConnectionException(message, new HttpResponseException(message, response)); } catch (JsonProcessingException | InterruptedException | ExecutionException | TimeoutException e) { - - var logMessage = message + " {}"; - this.logger.warn(logMessage, e.toString()); + this.logger.warn("{}", e.getMessage()); throw new HueSyncConnectionException(message, e); } diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java index d1dcc41330ccf..84033e2479081 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java @@ -36,6 +36,7 @@ import org.openhab.binding.huesync.internal.connection.HueSyncDeviceConnection; import org.openhab.binding.huesync.internal.exceptions.HueSyncApiException; import org.openhab.binding.huesync.internal.exceptions.HueSyncConnectionException; +import org.openhab.binding.huesync.internal.exceptions.HueSyncException; import org.openhab.binding.huesync.internal.handler.tasks.HueSyncRegistrationTask; import org.openhab.binding.huesync.internal.handler.tasks.HueSyncUpdateTask; import org.openhab.binding.huesync.internal.handler.tasks.HueSyncUpdateTaskResult; @@ -62,6 +63,7 @@ * channels. * * @author Patrik Gfeller - Initial contribution + * @author Patrik Gfeller - Issue #18376, Fix/improve log message and exception handling */ @NonNullByDefault public class HueSyncHandler extends BaseThingHandler { @@ -71,6 +73,7 @@ public class HueSyncHandler extends BaseThingHandler { * * @author Patrik Gfeller - Initial contribution * @author Patrik Gfeller - Issue #18062, improve connection exception handling. + * @author Patrik Gfeller - Issue #18376, Fix/improve log message and exception handling */ private class ExceptionHandler implements HueSyncExceptionHandler { private final HueSyncHandler handler; @@ -84,24 +87,28 @@ public void handle(Exception exception) { ThingStatusDetail detail = ThingStatusDetail.COMMUNICATION_ERROR; String description; - if (exception instanceof HueSyncConnectionException connectionException) { - if (connectionException.getInnerException() instanceof HttpResponseException innerException) { - switch (innerException.getResponse().getStatus()) { - case HttpStatus.BAD_REQUEST_400 -> { - detail = ThingStatusDetail.CONFIGURATION_PENDING; - } - case HttpStatus.UNAUTHORIZED_401 -> { - detail = ThingStatusDetail.CONFIGURATION_ERROR; - } - default -> { - detail = ThingStatusDetail.COMMUNICATION_ERROR; + switch (exception) { + case HueSyncConnectionException connectionException -> { + if (connectionException.getInnerException() instanceof HttpResponseException innerException) { + switch (innerException.getResponse().getStatus()) { + case HttpStatus.BAD_REQUEST_400 -> { + detail = ThingStatusDetail.CONFIGURATION_PENDING; + } + case HttpStatus.UNAUTHORIZED_401 -> { + detail = ThingStatusDetail.CONFIGURATION_ERROR; + } + default -> { + detail = ThingStatusDetail.COMMUNICATION_ERROR; + } } } + description = connectionException.getLocalizedMessage(); + } + case HueSyncException hueSyncException -> description = hueSyncException.getLocalizedMessage(); + default -> { + detail = ThingStatusDetail.COMMUNICATION_ERROR; + description = exception.getLocalizedMessage(); } - description = connectionException.getLocalizedMessage(); - } else { - detail = ThingStatusDetail.COMMUNICATION_ERROR; - description = exception.getLocalizedMessage(); } ThingStatusInfo statusInfo = new ThingStatusInfo(ThingStatus.OFFLINE, detail, description); @@ -127,21 +134,10 @@ public void handle(Exception exception) { public HueSyncHandler(Thing thing, HttpClientFactory httpClientFactory) { super(thing); - this.updateStatus(ThingStatus.UNKNOWN); - this.exceptionHandler = new ExceptionHandler(this); this.httpClient = httpClientFactory.getCommonHttpClient(); } - // #region override - @Override - protected Configuration editConfiguration() { - this.logger.debug("Configuration change detected."); - - return new Configuration(this.thing.getConfiguration().getProperties()); - } - // #endregion - // #region private private Runnable initializeConnection() { return () -> { @@ -197,10 +193,7 @@ private synchronized void startTasks(HueSyncDeviceConnection connection) { switch (id) { case POLL -> { - this.updateStatus(ThingStatus.ONLINE); - initialDelay = HueSyncConstants.POLL_INITIAL_DELAY; - interval = this.getConfigAs(HueSyncConfiguration.class).statusUpdateInterval; task = new HueSyncUpdateTask(connection, this.deviceInfo.get(), deviceStatus -> this.handleUpdate(deviceStatus), this.exceptionHandler); @@ -228,9 +221,6 @@ private synchronized void stopTasks() { this.tasks.values().forEach(task -> this.stopTask(task)); this.tasks.clear(); - - this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, - "@text/thing.config.huesync.box.registration"); } private synchronized void stopTask(@Nullable ScheduledFuture task) { @@ -243,10 +233,10 @@ private synchronized void stopTask(@Nullable ScheduledFuture task) { private void handleUpdate(@Nullable HueSyncUpdateTaskResult dto) { synchronized (this) { - ThingStatus status = this.thing.getStatus(); + ThingStatusInfo statusInfo = this.thing.getStatusInfo(); - switch (status) { - case ONLINE: + switch (statusInfo.getStatusDetail()) { + case CONFIGURATION_PENDING, NONE -> { Optional.ofNullable(dto).ifPresent(taskResult -> { Optional.ofNullable(taskResult.deviceStatus) .ifPresent(payload -> this.updateFirmwareInformation(payload)); @@ -255,8 +245,12 @@ private void handleUpdate(@Nullable HueSyncUpdateTaskResult dto) { Optional.ofNullable(taskResult.execution) .ifPresent(payload -> this.updateExecutionInformation(payload)); }); - break; - case OFFLINE: + + if (statusInfo.getStatus() != ThingStatus.ONLINE) { + this.updateStatus(ThingStatus.ONLINE); + } + } + case COMMUNICATION_ERROR -> { this.stopTasks(); this.connection.ifPresent(connectionInstance -> { @@ -264,9 +258,9 @@ private void handleUpdate(@Nullable HueSyncUpdateTaskResult dto) { this.connect(connectionInstance, deviceInfoInstance); }); }); - break; - default: - this.logger.debug("Unable to execute update - Status: [{}]", status); + } + default -> this.logger.debug("Unable to execute update - Status: [{}, {}]", statusInfo.getStatus(), + statusInfo.getStatusDetail()); } } } @@ -367,14 +361,9 @@ private void saveProperty(String key, String value, Map properti @Override public void initialize() { try { - this.stopTasks(); - this.updateStatus(ThingStatus.OFFLINE); - scheduler.execute(initializeConnection()); } catch (Exception e) { - this.stopTasks(); this.logger.warn("{}", e.getMessage()); - this.exceptionHandler.handle(e); } } @@ -397,18 +386,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { } @Override - public void dispose() { - synchronized (this) { - super.dispose(); + public synchronized void dispose() { + super.dispose(); - try { - this.stopTasks(); - this.connection.orElseThrow().dispose(); - } catch (Exception e) { - this.logger.warn("{}", e.getMessage()); - } finally { - this.logger.debug("Thing {} ({}) disposed.", this.thing.getLabel(), this.thing.getUID()); - } + try { + this.stopTasks(); + this.connection.orElseThrow().dispose(); + } catch (Exception e) { + this.logger.warn("{}", e.getMessage()); + } finally { + this.logger.debug("Thing {} ({}) disposed.", this.thing.getLabel(), this.thing.getUID()); } } @@ -421,5 +408,12 @@ public void handleRemoval() { } } + @Override + protected Configuration editConfiguration() { + this.logger.debug("Configuration change detected."); + + return new Configuration(this.thing.getConfiguration().getProperties()); + } + // #endregion } diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/i18n/ResourceHelper.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/i18n/ResourceHelper.java index a3d036546d6b3..27c0f82eedbf6 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/i18n/ResourceHelper.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/i18n/ResourceHelper.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.huesync.internal.i18n; -import java.util.Locale; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.i18n.TranslationProvider; import org.osgi.framework.Bundle; @@ -24,26 +22,24 @@ /** * * @author Patrik Gfeller - Initial Contribution + * @author Patrik Gfeller - Issue #18376, Fix/improve log message and exception handling */ @NonNullByDefault public class ResourceHelper { - private static final Locale LOCALE = Locale.ENGLISH; private static final BundleContext BUNDLE_CONTEXT = FrameworkUtil.getBundle(ResourceHelper.class) .getBundleContext(); private static final ServiceReference SERVICE_REFERENCE = BUNDLE_CONTEXT .getServiceReference(TranslationProvider.class); private static final Bundle BUNDLE = BUNDLE_CONTEXT.getBundle(); + private static final TranslationProvider TRANSLATION_PROVIDER = BUNDLE_CONTEXT.getService(SERVICE_REFERENCE); public static String getResourceString(String key) { String lookupKey = key.replace("@text/", ""); String missingKey = "Missing Translation: " + key; - String result = (BUNDLE_CONTEXT - .getService(SERVICE_REFERENCE) instanceof TranslationProvider translationProvider) - ? translationProvider.getText(BUNDLE, lookupKey, missingKey, LOCALE) - : missingKey; + var localizedString = TRANSLATION_PROVIDER.getText(BUNDLE, lookupKey, missingKey, null); - return result == null ? missingKey : result; + return localizedString == null ? missingKey : localizedString; } } From 2d162779077f0e93779d96d028dbd75e0954d73f Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Mon, 28 Apr 2025 19:25:31 +0200 Subject: [PATCH 02/12] fix(communication): [huesync] Bugfix for #18376 Basic manual test with openHAB 5.0.0 - Build #4616 Signed-off-by: Patrik Gfeller --- .../huesync/internal/HueSyncConstants.java | 5 - .../connection/HueSyncConnection.java | 61 ++-- .../connection/HueSyncDeviceConnection.java | 16 +- .../HueSyncConnectionException.java | 15 + .../internal/handler/HueSyncHandler.java | 270 +++++++++--------- .../handler/tasks/HueSyncConnectionTask.java | 57 ++++ .../tasks/HueSyncRegistrationTask.java | 30 +- 7 files changed, 288 insertions(+), 166 deletions(-) create mode 100644 bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncConnectionTask.java diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java index 1b2aedcd188f7..0a6bd7f9cd6d3 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java @@ -89,11 +89,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"; } diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java index 282acdb5cbdcb..b56d32f0645f9 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java @@ -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; @@ -109,10 +110,11 @@ 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)); @@ -122,9 +124,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()) { @@ -172,10 +175,10 @@ protected void dispose() { // #region private private @Nullable T executeRequest(Request request, @Nullable Class 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 @@ -189,22 +192,46 @@ protected void dispose() { * 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) { - this.logger.warn("{}", e.getMessage()); + + 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 deserialize(String json, @Nullable Class type) throws JsonProcessingException { diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java index 920ce7d5ae517..19942be640bb9 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java @@ -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 { @@ -64,9 +65,14 @@ public HueSyncDeviceConnection(HttpClient httpClient, HueSyncConfiguration confi 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 @@ -200,9 +206,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); } diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncConnectionException.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncConnectionException.java index 57132a79275e2..81ccd21d87744 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncConnectionException.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncConnectionException.java @@ -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 { @@ -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; + } } diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java index 84033e2479081..5235753421e00 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java @@ -19,6 +19,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -36,7 +37,7 @@ import org.openhab.binding.huesync.internal.connection.HueSyncDeviceConnection; import org.openhab.binding.huesync.internal.exceptions.HueSyncApiException; import org.openhab.binding.huesync.internal.exceptions.HueSyncConnectionException; -import org.openhab.binding.huesync.internal.exceptions.HueSyncException; +import org.openhab.binding.huesync.internal.handler.tasks.HueSyncConnectionTask; import org.openhab.binding.huesync.internal.handler.tasks.HueSyncRegistrationTask; import org.openhab.binding.huesync.internal.handler.tasks.HueSyncUpdateTask; import org.openhab.binding.huesync.internal.handler.tasks.HueSyncUpdateTaskResult; @@ -53,6 +54,7 @@ import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.ThingStatusInfo; import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder; import org.openhab.core.types.Command; import org.openhab.core.types.State; import org.slf4j.Logger; @@ -68,6 +70,20 @@ @NonNullByDefault public class HueSyncHandler extends BaseThingHandler { + /** + * @author Patrik Gfeller - Initial contribution, Issue #18376 + */ + public static class TASK_TYPE { + public static final String CONNECT = "Connect"; + public static final String REGISTER = "Registration"; + public static final String POLL = "Update"; + + public static Map DELAY_MAP = Map.ofEntries(Map.entry(TASK_TYPE.CONNECT, 0), + Map.entry(TASK_TYPE.REGISTER, 5), Map.entry(TASK_TYPE.POLL, 10)); + public static Map INTERVAL_MAP = Map.ofEntries(Map.entry(TASK_TYPE.CONNECT, 10), + Map.entry(TASK_TYPE.REGISTER, 1), Map.entry(TASK_TYPE.POLL, 10)); + } + /** * Exception handler implementation * @@ -85,40 +101,48 @@ private ExceptionHandler(HueSyncHandler handler) { @Override public void handle(Exception exception) { ThingStatusDetail detail = ThingStatusDetail.COMMUNICATION_ERROR; - String description; - - switch (exception) { - case HueSyncConnectionException connectionException -> { - if (connectionException.getInnerException() instanceof HttpResponseException innerException) { - switch (innerException.getResponse().getStatus()) { - case HttpStatus.BAD_REQUEST_400 -> { - detail = ThingStatusDetail.CONFIGURATION_PENDING; - } - case HttpStatus.UNAUTHORIZED_401 -> { - detail = ThingStatusDetail.CONFIGURATION_ERROR; - } - default -> { - detail = ThingStatusDetail.COMMUNICATION_ERROR; - } - } - } - description = connectionException.getLocalizedMessage(); + String description = exception.getLocalizedMessage(); + + HttpResponseException httpResponseException = null; + + if (exception instanceof HueSyncConnectionException connectionException + && connectionException.getInnerException() instanceof HttpResponseException responseException) { + httpResponseException = responseException; + + } + if (exception instanceof HttpResponseException responseException) { + httpResponseException = responseException; + } + + if (httpResponseException != null) { + detail = getThingStatusDetail(httpResponseException); + } + + ThingStatusInfo statusInfo = new ThingStatusInfo(ThingStatus.OFFLINE, detail, description); + this.handler.thing.setStatusInfo(statusInfo); + + if (!(detail == ThingStatusDetail.CONFIGURATION_PENDING && tasks.containsKey(TASK_TYPE.REGISTER))) { + scheduler.execute(initializeHandler()); + } + } + + private ThingStatusDetail getThingStatusDetail(HttpResponseException innerException) { + ThingStatusDetail detail; + switch (innerException.getResponse().getStatus()) { + case HttpStatus.BAD_REQUEST_400 -> { + detail = ThingStatusDetail.CONFIGURATION_PENDING; + } + case HttpStatus.UNAUTHORIZED_401 -> { + detail = ThingStatusDetail.CONFIGURATION_ERROR; } - case HueSyncException hueSyncException -> description = hueSyncException.getLocalizedMessage(); default -> { detail = ThingStatusDetail.COMMUNICATION_ERROR; - description = exception.getLocalizedMessage(); } } - - ThingStatusInfo statusInfo = new ThingStatusInfo(ThingStatus.OFFLINE, detail, description); - this.handler.thing.setStatusInfo(statusInfo); + return detail; } } - private static final String REGISTER = "Registration"; - private static final String POLL = "Update"; - private static final String PROPERTY_API_VERSION = "apiVersion"; private final ExceptionHandler exceptionHandler; @@ -139,85 +163,65 @@ public HueSyncHandler(Thing thing, HttpClientFactory httpClientFactory) { } // #region private - private Runnable initializeConnection() { + private synchronized Runnable initializeHandler() { return () -> { - try { - var connectionInstance = new HueSyncDeviceConnection(this.httpClient, - this.getConfigAs(HueSyncConfiguration.class), this.exceptionHandler); - - this.connection = Optional.of(connectionInstance); - this.deviceInfo = Optional.ofNullable(connectionInstance.getDeviceInfo()); - - this.deviceInfo.ifPresent(info -> { - connect(connectionInstance, info); - }); - - } catch (Exception e) { - this.exceptionHandler.handle(e); - } + this.stopTasks(); + this.startTasks(); }; } - private void connect(HueSyncDeviceConnection connectionInstance, HueSyncDevice info) { - setProperty(Thing.PROPERTY_SERIAL_NUMBER, info.uniqueId != null ? info.uniqueId : ""); - setProperty(Thing.PROPERTY_MODEL_ID, info.deviceType); - setProperty(Thing.PROPERTY_FIRMWARE_VERSION, info.firmwareVersion); - - setProperty(HueSyncHandler.PROPERTY_API_VERSION, String.format("%d", info.apiLevel)); - - try { - this.checkCompatibility(); - } catch (HueSyncApiException e) { - this.exceptionHandler.handle(e); - } finally { - this.startTasks(connectionInstance); - } - } - private @Nullable ScheduledFuture executeTask(Runnable task, long initialDelay, long interval) { return scheduler.scheduleWithFixedDelay(task, initialDelay, interval, TimeUnit.SECONDS); } - private synchronized void startTasks(HueSyncDeviceConnection connection) { - this.stopTasks(); + private synchronized void startTasks() { + String taskId = TASK_TYPE.POLL; - connection.updateConfiguration(this.getConfigAs(HueSyncConfiguration.class)); + if (this.connection.isEmpty()) { + taskId = TASK_TYPE.CONNECT; + } else if (!this.connection.get().isRegistered()) { + taskId = TASK_TYPE.REGISTER; + } Runnable task = null; - String id = connection.isRegistered() ? POLL : REGISTER; - this.logger.debug("startTasks - [{}]", id); + long delay = TASK_TYPE.DELAY_MAP.get(taskId); + long interval = TASK_TYPE.INTERVAL_MAP.get(taskId); - long initialDelay = 0; - long interval = 0; + this.logger.trace("startTasks - [{}, delay: {}s, interval: {}s]", taskId, delay, interval); - switch (id) { - case POLL -> { - initialDelay = HueSyncConstants.POLL_INITIAL_DELAY; - interval = this.getConfigAs(HueSyncConfiguration.class).statusUpdateInterval; - task = new HueSyncUpdateTask(connection, this.deviceInfo.get(), - deviceStatus -> this.handleUpdate(deviceStatus), this.exceptionHandler); + switch (taskId) { + case TASK_TYPE.CONNECT -> { + task = new HueSyncConnectionTask(this, this.httpClient, instance -> this.handleConnection(instance), + this.exceptionHandler); + break; } - case REGISTER -> { - initialDelay = HueSyncConstants.REGISTRATION_INITIAL_DELAY; - interval = HueSyncConstants.REGISTRATION_INTERVAL; + case TASK_TYPE.POLL -> { + ThingStatusInfo statusInfo = ThingStatusInfoBuilder.create(ThingStatus.ONLINE).build(); + this.thing.setStatusInfo(statusInfo); - this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, - "@text/thing.config.huesync.box.registration"); + interval = this.getHueSyncConfiguration().statusUpdateInterval; - task = new HueSyncRegistrationTask(connection, this.deviceInfo.get(), - registration -> this.handleRegistration(registration, connection), this.exceptionHandler); + task = new HueSyncUpdateTask(this.connection.get(), this.deviceInfo.get(), + deviceStatus -> this.handleUpdate(deviceStatus), this.exceptionHandler); + break; + } + case TASK_TYPE.REGISTER -> { + task = new HueSyncRegistrationTask(this.connection.get(), this.deviceInfo.get(), + this.getHueSyncConfiguration(), registration -> this.handleRegistration(registration), + this.exceptionHandler); + break; } } if (task != null) { - logger.debug("Starting task [{}]", id); - this.tasks.put(id, this.executeTask(task, initialDelay, interval)); + logger.info("Starting task [{}]", taskId); + this.tasks.put(taskId, this.executeTask(task, delay, interval)); } } private synchronized void stopTasks() { - logger.debug("Stopping {} task(s): {}", this.tasks.values().size(), String.join(",", this.tasks.keySet())); + logger.info("Stopping {} task(s): {}", this.tasks.values().size(), String.join(",", this.tasks.keySet())); this.tasks.values().forEach(task -> this.stopTask(task)); this.tasks.clear(); @@ -232,37 +236,18 @@ private synchronized void stopTask(@Nullable ScheduledFuture task) { } private void handleUpdate(@Nullable HueSyncUpdateTaskResult dto) { - synchronized (this) { - ThingStatusInfo statusInfo = this.thing.getStatusInfo(); - - switch (statusInfo.getStatusDetail()) { - case CONFIGURATION_PENDING, NONE -> { - Optional.ofNullable(dto).ifPresent(taskResult -> { - Optional.ofNullable(taskResult.deviceStatus) - .ifPresent(payload -> this.updateFirmwareInformation(payload)); - Optional.ofNullable(taskResult.hdmiStatus) - .ifPresent(payload -> this.updateHdmiInformation(payload)); - Optional.ofNullable(taskResult.execution) - .ifPresent(payload -> this.updateExecutionInformation(payload)); - }); - - if (statusInfo.getStatus() != ThingStatus.ONLINE) { - this.updateStatus(ThingStatus.ONLINE); - } - } - case COMMUNICATION_ERROR -> { - this.stopTasks(); - - this.connection.ifPresent(connectionInstance -> { - this.deviceInfo.ifPresent(deviceInfoInstance -> { - this.connect(connectionInstance, deviceInfoInstance); - }); - }); - } - default -> this.logger.debug("Unable to execute update - Status: [{}, {}]", statusInfo.getStatus(), - statusInfo.getStatusDetail()); - } - } + var result = Optional.ofNullable(dto).orElseThrow(); + + @NonNull + HueSyncDeviceDetailed deviceStatus = Optional.ofNullable(result.deviceStatus).orElseThrow(); + @NonNull + HueSyncHdmi hdmiStatus = Optional.ofNullable(result.hdmiStatus).orElseThrow(); + @NonNull + HueSyncExecution execution = Optional.ofNullable(result.execution).orElseThrow(); + + this.updateFirmwareInformation(deviceStatus); + this.updateHdmiInformation(hdmiStatus); + this.updateExecutionInformation(execution); } private void updateHdmiInformation(HueSyncHdmi hdmiStatus) { @@ -306,28 +291,46 @@ private void updateExecutionInformation(HueSyncExecution executionStatus) { this.updateState(HueSyncConstants.CHANNELS.COMMANDS.BRIGHTNESS, new DecimalType(executionStatus.brightness)); } - private void handleRegistration(HueSyncRegistration registration, HueSyncDeviceConnection connection) { - this.stopTasks(); + private void handleConnection(HueSyncDeviceConnection connectionInstance) { + try { + var information = Optional.ofNullable(connectionInstance.getDeviceInfo()); + + this.deviceInfo = Optional.of(this.checkCompatibility(information)); + this.connection = Optional.of(connectionInstance); + scheduler.execute(initializeHandler()); + } catch (Exception e) { + this.exceptionHandler.handle(e); + } + } + + private void handleRegistration(HueSyncRegistration registration) { setProperty(HueSyncConstants.REGISTRATION_ID, registration.registrationId); - Configuration configuration = this.editConfiguration(); + if ((this.getHueSyncConfiguration().apiAccessToken == null ? registration.accessToken != null + : !this.getHueSyncConfiguration().apiAccessToken.equals(registration.accessToken)) + && (this.getHueSyncConfiguration().registrationId == null ? registration.registrationId != null + : !this.getHueSyncConfiguration().registrationId.equals(registration.registrationId))) { + Configuration configuration = this.editConfiguration(); - configuration.put(HueSyncConstants.REGISTRATION_ID, registration.registrationId); - configuration.put(HueSyncConstants.API_TOKEN, registration.accessToken); + configuration.put(HueSyncConstants.REGISTRATION_ID, registration.registrationId); + configuration.put(HueSyncConstants.API_TOKEN, registration.accessToken); - this.updateConfiguration(configuration); + this.updateConfiguration(configuration); + } - this.startTasks(connection); + scheduler.execute(initializeHandler()); } - private void checkCompatibility() throws HueSyncApiException { + private HueSyncDevice checkCompatibility(Optional deviceInfo) throws HueSyncApiException { try { - HueSyncDevice deviceInformation = this.deviceInfo.orElseThrow(); + HueSyncDevice deviceInformation = deviceInfo.orElseThrow(); if (deviceInformation.apiLevel < HueSyncConstants.MINIMAL_API_VERSION) { throw new HueSyncApiException("@text/api.minimal-version"); } + + return deviceInformation; } catch (NoSuchElementException e) { throw new HueSyncApiException("@text/api.communication-problem"); } @@ -354,21 +357,31 @@ private void saveProperty(String key, String value, Map properti this.updateProperties(properties); } + private HueSyncConfiguration getHueSyncConfiguration() { + return this.getConfigAs(HueSyncConfiguration.class); + } // #endregion // #region Override @Override - public void initialize() { + public synchronized void initialize() { try { - scheduler.execute(initializeConnection()); + scheduler.execute(initializeHandler()); } catch (Exception e) { this.logger.warn("{}", e.getMessage()); } } @Override - public void handleCommand(ChannelUID channelUID, Command command) { + protected synchronized Configuration editConfiguration() { + this.logger.debug("Configuration change detected."); + + return new Configuration(this.thing.getConfiguration().getProperties()); + } + + @Override + public synchronized void handleCommand(ChannelUID channelUID, Command command) { if (thing.getStatus() != ThingStatus.ONLINE || this.connection.isEmpty()) { this.logger.warn("Device status: {} - Command {} for channel {} will be ignored", thing.getStatus().toString(), command.toFullString(), channelUID.toString()); @@ -400,7 +413,7 @@ public synchronized void dispose() { } @Override - public void handleRemoval() { + public synchronized void handleRemoval() { super.handleRemoval(); if (this.connection.isPresent()) { @@ -408,12 +421,5 @@ public void handleRemoval() { } } - @Override - protected Configuration editConfiguration() { - this.logger.debug("Configuration change detected."); - - return new Configuration(this.thing.getConfiguration().getProperties()); - } - // #endregion } diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncConnectionTask.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncConnectionTask.java new file mode 100644 index 0000000000000..42ab075017aa7 --- /dev/null +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncConnectionTask.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.huesync.internal.handler.tasks; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.security.cert.CertificateException; +import java.util.function.Consumer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.huesync.internal.config.HueSyncConfiguration; +import org.openhab.binding.huesync.internal.connection.HueSyncDeviceConnection; +import org.openhab.binding.huesync.internal.handler.HueSyncHandler; +import org.openhab.binding.huesync.internal.types.HueSyncExceptionHandler; + +/** + * @author Patrik Gfeller - Initial contribution, Issue #18376 + */ +@NonNullByDefault +public class HueSyncConnectionTask implements Runnable { + private final Consumer connectedHandler; + private final HueSyncExceptionHandler exceptionHandler; + private final HttpClient httpClient; + private final HueSyncHandler handler; + + public HueSyncConnectionTask(HueSyncHandler handler, HttpClient httpClient, + Consumer connectionHandler, HueSyncExceptionHandler exceptionHandler) { + + this.handler = handler; + this.httpClient = httpClient; + this.connectedHandler = connectionHandler; + this.exceptionHandler = exceptionHandler; + } + + @Override + public void run() { + try { + var connection = new HueSyncDeviceConnection(this.httpClient, + this.handler.getThing().getConfiguration().as(HueSyncConfiguration.class), this.exceptionHandler); + + this.connectedHandler.accept(connection); + } catch (IOException | URISyntaxException | CertificateException e) { + this.exceptionHandler.handle(e); + } + } +} diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java index a778f6cc98d87..a6f0ebd1c2272 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java @@ -17,6 +17,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.huesync.internal.api.dto.device.HueSyncDevice; import org.openhab.binding.huesync.internal.api.dto.registration.HueSyncRegistration; +import org.openhab.binding.huesync.internal.config.HueSyncConfiguration; import org.openhab.binding.huesync.internal.connection.HueSyncDeviceConnection; import org.openhab.binding.huesync.internal.types.HueSyncExceptionHandler; import org.slf4j.Logger; @@ -26,6 +27,7 @@ * Task to handle device registration. * * @author Patrik Gfeller - Initial contribution + * @author Patrik Gfeller - Issue #18376, Fix/improve log message and exception handling */ @NonNullByDefault public class HueSyncRegistrationTask implements Runnable { @@ -34,35 +36,49 @@ public class HueSyncRegistrationTask implements Runnable { private final HueSyncDeviceConnection connection; private final HueSyncDevice deviceInfo; private final HueSyncExceptionHandler exceptionHandler; - private final Consumer action; + private final Consumer registrationAccepted; + private final HueSyncConfiguration configuration; public HueSyncRegistrationTask(HueSyncDeviceConnection connection, HueSyncDevice deviceInfo, - Consumer action, HueSyncExceptionHandler exceptionHandler) { + HueSyncConfiguration configuration, Consumer registrationAccepted, + HueSyncExceptionHandler exceptionHandler) { this.exceptionHandler = exceptionHandler; this.connection = connection; this.deviceInfo = deviceInfo; - this.action = action; + this.registrationAccepted = registrationAccepted; + this.configuration = configuration; } @Override public void run() { try { String id = this.deviceInfo.uniqueId; + HueSyncRegistration registration; - if (this.connection.isRegistered() || id == null) { + if (this.connection.isRegistered()) { + this.logger.debug("API token for {} already configured", this.deviceInfo.name); + + registration = new HueSyncRegistration(); + registration.registrationId = this.configuration.registrationId; + registration.accessToken = this.configuration.apiAccessToken; + + this.registrationAccepted.accept(registration); return; } this.logger.debug("Listening for device registration - {} {}:{}", this.deviceInfo.name, this.deviceInfo.deviceType, id); - HueSyncRegistration registration = this.connection.registerDevice(id); + if (id == null) { + throw new Exception("Device information id must not be null"); + } + + registration = this.connection.registerDevice(id); if (registration != null) { this.logger.debug("API token for {} received", this.deviceInfo.name); - - this.action.accept(registration); + this.registrationAccepted.accept(registration); } } catch (Exception e) { this.exceptionHandler.handle(e); From 1dc3cb13e82e0baabd98f7d29da5f387fedabbd0 Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 20:52:49 +0200 Subject: [PATCH 03/12] style(SAT): Remove unused const "EXCEPTION_TYPES" Signed-off-by: Patrik Gfeller --- .../binding/huesync/internal/HueSyncConstants.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java index 0a6bd7f9cd6d3..6512e9b7c328b 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java @@ -23,14 +23,7 @@ */ @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"; public static final String REGISTRATIONS = "registrations"; From 4994453562c6c8ecad17d0b7f553a81358f40dca Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 20:56:45 +0200 Subject: [PATCH 04/12] style(SAT): NoEmptyLineSeparatorCheck - HueSyncConnection Signed-off-by: Patrik Gfeller --- .../binding/huesync/internal/connection/HueSyncConnection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java index b56d32f0645f9..134915bd7f346 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java @@ -112,7 +112,6 @@ protected ContentResponse execute() throws InterruptedException, ExecutionExcept public HueSyncConnection(HttpClient httpClient, HueSyncConfiguration configuration) throws CertificateException, IOException, URISyntaxException { - this.host = configuration.host; this.port = configuration.port; From 373c2155238bf95f6afdf9ac4e6d9ca573553283 Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 21:05:15 +0200 Subject: [PATCH 05/12] style(spotless): HueSyncConstants Signed-off-by: Patrik Gfeller --- .../org/openhab/binding/huesync/internal/HueSyncConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java index 6512e9b7c328b..10dfbc65e6a47 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java @@ -23,7 +23,7 @@ */ @NonNullByDefault public class HueSyncConstants { - + public static class ENDPOINTS { public static final String DEVICE = "device"; public static final String REGISTRATIONS = "registrations"; From 275855fe2dcba49c8213e223a58991ce97cf52f8 Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 21:07:37 +0200 Subject: [PATCH 06/12] style(SAT): HueSyncDeviceConnection Signed-off-by: Patrik Gfeller --- .../huesync/internal/connection/HueSyncDeviceConnection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java index 19942be640bb9..c9162aa8e20ce 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java @@ -63,7 +63,6 @@ public class HueSyncDeviceConnection { public HueSyncDeviceConnection(HttpClient httpClient, HueSyncConfiguration configuration, HueSyncExceptionHandler exceptionHandler) throws CertificateException, IOException, URISyntaxException { - this.exceptionHandler = exceptionHandler; try { this.connection = new HueSyncConnection(httpClient, configuration); From 50e176f302bdf2f210b9a900b9fbbcd9a2051968 Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 21:14:24 +0200 Subject: [PATCH 07/12] style(SAT): HueSyncHandler Signed-off-by: Patrik Gfeller --- .../internal/handler/HueSyncHandler.java | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java index 5235753421e00..e69e566c053ac 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java @@ -19,7 +19,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -73,15 +72,15 @@ public class HueSyncHandler extends BaseThingHandler { /** * @author Patrik Gfeller - Initial contribution, Issue #18376 */ - public static class TASK_TYPE { + public static class TASKS { public static final String CONNECT = "Connect"; public static final String REGISTER = "Registration"; public static final String POLL = "Update"; - public static Map DELAY_MAP = Map.ofEntries(Map.entry(TASK_TYPE.CONNECT, 0), - Map.entry(TASK_TYPE.REGISTER, 5), Map.entry(TASK_TYPE.POLL, 10)); - public static Map INTERVAL_MAP = Map.ofEntries(Map.entry(TASK_TYPE.CONNECT, 10), - Map.entry(TASK_TYPE.REGISTER, 1), Map.entry(TASK_TYPE.POLL, 10)); + public static Map DELAYS = Map.ofEntries(Map.entry(TASKS.CONNECT, 0), + Map.entry(TASKS.REGISTER, 5), Map.entry(TASKS.POLL, 10)); + public static Map INTERVALS = Map.ofEntries(Map.entry(TASKS.CONNECT, 10), + Map.entry(TASKS.REGISTER, 1), Map.entry(TASKS.POLL, 10)); } /** @@ -108,7 +107,6 @@ public void handle(Exception exception) { if (exception instanceof HueSyncConnectionException connectionException && connectionException.getInnerException() instanceof HttpResponseException responseException) { httpResponseException = responseException; - } if (exception instanceof HttpResponseException responseException) { httpResponseException = responseException; @@ -121,7 +119,7 @@ public void handle(Exception exception) { ThingStatusInfo statusInfo = new ThingStatusInfo(ThingStatus.OFFLINE, detail, description); this.handler.thing.setStatusInfo(statusInfo); - if (!(detail == ThingStatusDetail.CONFIGURATION_PENDING && tasks.containsKey(TASK_TYPE.REGISTER))) { + if (!(detail == ThingStatusDetail.CONFIGURATION_PENDING && tasks.containsKey(TASKS.REGISTER))) { scheduler.execute(initializeHandler()); } } @@ -175,28 +173,28 @@ private synchronized Runnable initializeHandler() { } private synchronized void startTasks() { - String taskId = TASK_TYPE.POLL; + String taskId = TASKS.POLL; if (this.connection.isEmpty()) { - taskId = TASK_TYPE.CONNECT; + taskId = TASKS.CONNECT; } else if (!this.connection.get().isRegistered()) { - taskId = TASK_TYPE.REGISTER; + taskId = TASKS.REGISTER; } Runnable task = null; - long delay = TASK_TYPE.DELAY_MAP.get(taskId); - long interval = TASK_TYPE.INTERVAL_MAP.get(taskId); + long delay = TASKS.DELAYS.get(taskId); + long interval = TASKS.INTERVALS.get(taskId); this.logger.trace("startTasks - [{}, delay: {}s, interval: {}s]", taskId, delay, interval); switch (taskId) { - case TASK_TYPE.CONNECT -> { + case TASKS.CONNECT -> { task = new HueSyncConnectionTask(this, this.httpClient, instance -> this.handleConnection(instance), this.exceptionHandler); break; } - case TASK_TYPE.POLL -> { + case TASKS.POLL -> { ThingStatusInfo statusInfo = ThingStatusInfoBuilder.create(ThingStatus.ONLINE).build(); this.thing.setStatusInfo(statusInfo); @@ -206,7 +204,7 @@ private synchronized void startTasks() { deviceStatus -> this.handleUpdate(deviceStatus), this.exceptionHandler); break; } - case TASK_TYPE.REGISTER -> { + case TASKS.REGISTER -> { task = new HueSyncRegistrationTask(this.connection.get(), this.deviceInfo.get(), this.getHueSyncConfiguration(), registration -> this.handleRegistration(registration), this.exceptionHandler); @@ -238,11 +236,8 @@ private synchronized void stopTask(@Nullable ScheduledFuture task) { private void handleUpdate(@Nullable HueSyncUpdateTaskResult dto) { var result = Optional.ofNullable(dto).orElseThrow(); - @NonNull HueSyncDeviceDetailed deviceStatus = Optional.ofNullable(result.deviceStatus).orElseThrow(); - @NonNull HueSyncHdmi hdmiStatus = Optional.ofNullable(result.hdmiStatus).orElseThrow(); - @NonNull HueSyncExecution execution = Optional.ofNullable(result.execution).orElseThrow(); this.updateFirmwareInformation(deviceStatus); From 6afa3527daa2175415f37363243a4b6492676c8e Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 21:17:37 +0200 Subject: [PATCH 08/12] style(SAT): HueSyncConnectionTask Signed-off-by: Patrik Gfeller --- .../huesync/internal/handler/tasks/HueSyncConnectionTask.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncConnectionTask.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncConnectionTask.java index 42ab075017aa7..34e5afce85a97 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncConnectionTask.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncConnectionTask.java @@ -36,7 +36,6 @@ public class HueSyncConnectionTask implements Runnable { public HueSyncConnectionTask(HueSyncHandler handler, HttpClient httpClient, Consumer connectionHandler, HueSyncExceptionHandler exceptionHandler) { - this.handler = handler; this.httpClient = httpClient; this.connectedHandler = connectionHandler; From a7756891aec9af306d7c936e47b7caeaeb871b4a Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 21:21:42 +0200 Subject: [PATCH 09/12] style(SAT): HueSyncRegistrationTask Signed-off-by: Patrik Gfeller --- .../internal/handler/tasks/HueSyncRegistrationTask.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java index a6f0ebd1c2272..80dd1b4827671 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java @@ -19,6 +19,7 @@ import org.openhab.binding.huesync.internal.api.dto.registration.HueSyncRegistration; import org.openhab.binding.huesync.internal.config.HueSyncConfiguration; import org.openhab.binding.huesync.internal.connection.HueSyncDeviceConnection; +import org.openhab.binding.huesync.internal.exceptions.HueSyncTaskException; import org.openhab.binding.huesync.internal.types.HueSyncExceptionHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +43,6 @@ public class HueSyncRegistrationTask implements Runnable { public HueSyncRegistrationTask(HueSyncDeviceConnection connection, HueSyncDevice deviceInfo, HueSyncConfiguration configuration, Consumer registrationAccepted, HueSyncExceptionHandler exceptionHandler) { - this.exceptionHandler = exceptionHandler; this.connection = connection; this.deviceInfo = deviceInfo; @@ -71,7 +71,7 @@ public void run() { this.deviceInfo.deviceType, id); if (id == null) { - throw new Exception("Device information id must not be null"); + throw new HueSyncTaskException("Device information id must not be null"); } registration = this.connection.registerDevice(id); From 7c6a159375da5a3d21f3332a38ee07ad3b5009b1 Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 21:24:36 +0200 Subject: [PATCH 10/12] style(SAT): HueSyncUpdateTask Signed-off-by: Patrik Gfeller --- .../huesync/internal/handler/tasks/HueSyncUpdateTask.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTask.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTask.java index e054f6cac5637..bda2bfcf9c96f 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTask.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTask.java @@ -40,7 +40,6 @@ public class HueSyncUpdateTask implements Runnable { public HueSyncUpdateTask(HueSyncDeviceConnection connection, HueSyncDevice deviceInfo, Consumer<@Nullable HueSyncUpdateTaskResult> action, HueSyncExceptionHandler exceptionHandler) { - this.exceptionHandler = exceptionHandler; this.connection = connection; this.deviceInfo = deviceInfo; From 89cd2c026da82656ec82580b68952820bfca9657 Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 21:40:15 +0200 Subject: [PATCH 11/12] style(SAT): HueSyncConnection Signed-off-by: Patrik Gfeller --- .../binding/huesync/internal/connection/HueSyncConnection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java index 134915bd7f346..d5e762b2e0c90 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java @@ -140,7 +140,6 @@ public final void updateAuthentication(String id, String token) { // #region protected protected @Nullable T executeRequest(HttpMethod method, String endpoint, String payload, @Nullable Class type) throws HueSyncConnectionException { - return this.executeRequest(new Request(method, endpoint, payload), type); } From 095189bb04d907c2038baad0121be6750c0679ba Mon Sep 17 00:00:00 2001 From: Patrik Gfeller Date: Sat, 24 May 2025 21:42:59 +0200 Subject: [PATCH 12/12] style(SAT): HueSyncHandler Signed-off-by: Patrik Gfeller --- .../binding/huesync/internal/handler/HueSyncHandler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java index e69e566c053ac..c481f1a7c292b 100644 --- a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java +++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java @@ -77,9 +77,9 @@ public static class TASKS { public static final String REGISTER = "Registration"; public static final String POLL = "Update"; - public static Map DELAYS = Map.ofEntries(Map.entry(TASKS.CONNECT, 0), + public static Map delays = Map.ofEntries(Map.entry(TASKS.CONNECT, 0), Map.entry(TASKS.REGISTER, 5), Map.entry(TASKS.POLL, 10)); - public static Map INTERVALS = Map.ofEntries(Map.entry(TASKS.CONNECT, 10), + public static Map intervals = Map.ofEntries(Map.entry(TASKS.CONNECT, 10), Map.entry(TASKS.REGISTER, 1), Map.entry(TASKS.POLL, 10)); } @@ -183,8 +183,8 @@ private synchronized void startTasks() { Runnable task = null; - long delay = TASKS.DELAYS.get(taskId); - long interval = TASKS.INTERVALS.get(taskId); + long delay = TASKS.delays.get(taskId); + long interval = TASKS.intervals.get(taskId); this.logger.trace("startTasks - [{}, delay: {}s, interval: {}s]", taskId, delay, interval);