From 42a19025ac8b649b89fab2014cb8f75bdf9a6262 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 4 Sep 2022 15:06:55 +0100 Subject: [PATCH 01/64] [hdpowerview] generation 3 PR2 Signed-off-by: Andrew Fiddian-Green --- .../internal/HDPowerViewWebTargets.java | 608 ++++++----------- .../internal/_v1/HDPowerViewWebTargetsV1.java | 624 ++++++++++++++++++ .../internal/_v3/HDPowerViewWebTargetsV3.java | 551 ++++++++++++++++ .../hdpowerview/internal/_v3/SseSinkV3.java | 42 ++ .../hdpowerview/internal/api/ShadeData.java | 46 ++ .../internal/api/ShadePosition.java | 336 +--------- .../internal/api/_v1/ShadeDataV1.java | 47 ++ .../internal/api/_v1/ShadePositionV1.java | 387 +++++++++++ .../internal/api/_v3/ShadeDataV3.java | 48 ++ .../internal/api/_v3/ShadePositionV3.java | 86 +++ .../internal/api/responses/Scene.java | 40 ++ .../internal/api/responses/Scenes.java | 56 -- .../api/responses/ScheduledEvent.java | 40 ++ .../api/responses/ScheduledEvents.java | 91 --- .../internal/api/responses/Shade.java | 2 +- .../internal/api/responses/Shades.java | 41 +- .../internal/api/responses/_v1/SceneV1.java | 73 ++ .../api/responses/_v1/ScheduledEventV1.java | 116 ++++ .../internal/api/responses/_v3/InfoV3.java | 65 ++ .../internal/api/responses/_v3/SceneV3.java | 85 +++ .../api/responses/_v3/ScheduledEventV3.java | 134 ++++ .../api/responses/_v3/SseSceneV3.java | 29 + .../api/responses/_v3/SseShadeV3.java | 30 + .../builders/AutomationChannelBuilder.java | 27 +- .../builders/SceneChannelBuilder.java | 8 +- .../HDPowerViewDeviceDiscoveryService.java | 2 +- .../handler/HDPowerViewHubHandler.java | 125 +++- .../handler/HDPowerViewShadeHandler.java | 41 +- .../AutomationChannelBuilderTest.java | 36 +- .../hdpowerview/Generation3DtoTest.java | 191 ++++++ .../hdpowerview/HDPowerViewJUnitTests.java | 18 +- .../hdpowerview/OnlineCommunicationTest.java | 12 +- .../hdpowerview/SceneChannelBuilderTest.java | 5 +- .../hdpowerview/ShadePositionTest.java | 53 +- .../binding/hdpowerview/{ => _v1}/duette.json | 0 .../{ => _v1}/sceneCollections.json | 0 .../binding/hdpowerview/{ => _v1}/scenes.json | 0 .../binding/hdpowerview/{ => _v1}/shades.json | 0 .../binding/hdpowerview/_v3/automations.json | 13 + .../binding/hdpowerview/_v3/motion.json | 25 + .../binding/hdpowerview/_v3/scene-event.json | 17 + .../binding/hdpowerview/_v3/scenes.json | 14 + .../binding/hdpowerview/_v3/shade-event.json | 17 + .../binding/hdpowerview/_v3/shades.json | 25 + 44 files changed, 3209 insertions(+), 997 deletions(-) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => _v1}/duette.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => _v1}/sceneCollections.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => _v1}/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => _v1}/shades.json (100%) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index f8ba04c153391..4e9cf8ed2bf3d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.hdpowerview.internal; +import java.lang.reflect.Type; import java.time.Duration; import java.time.Instant; import java.util.List; @@ -29,30 +30,18 @@ import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.api.Color; import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.SurveyData; import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; -import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.Shade; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.responses.Survey; -import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; @@ -61,20 +50,20 @@ import org.slf4j.LoggerFactory; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; /** - * JAX-RS targets for communicating with an HD PowerView hub + * Abstract class for JAX-RS targets for communicating with an HD PowerView hub. * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Added support for secondary rail positions - * @author Jacob Laursen - Added support for scene groups and automations + * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewWebTargets { +public abstract class HDPowerViewWebTargets { private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class); @@ -87,24 +76,69 @@ public class HDPowerViewWebTargets { private final Duration maintenancePeriod = Duration.ofMinutes(5); private Instant maintenanceScheduledEnd = Instant.MIN; - private final String base; - private final String firmwareVersion; - private final String shades; - private final String sceneActivate; - private final String scenes; - private final String sceneCollectionActivate; - private final String sceneCollections; - private final String scheduledEvents; - private final String repeaters; - private final String userData; + /* + * De-serializer target class types + */ + private final Class sceneClass; + private final Class shadeDataClass; + private final Class shadePositionClass; + private final Class scheduledEventClass; - private final Gson gson = new Gson(); - private final HttpClient httpClient; + protected final Gson gson; + protected final HttpClient httpClient; + + /* + * Private helper class for de-serialization of Scene + */ + private class SceneDeserializer implements JsonDeserializer { + @Override + public @Nullable Scene deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + return context.deserialize(jsonObject, sceneClass); + } + } + + /* + * Private helper class for de-serialization of ShadeData + */ + private class ShadeDataDeserializer implements JsonDeserializer { + @Override + public @Nullable ShadeData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + return context.deserialize(jsonObject, shadeDataClass); + } + } + + /* + * Private helper class for de-serialization of ShadePosition + */ + private class ShadePositionDeserializer implements JsonDeserializer { + @Override + public @Nullable ShadePosition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + return context.deserialize(jsonObject, shadePositionClass); + } + } + + /* + * Private helper class for de-serialization of ScheduledEvent + */ + private class ScheduledEventDeserializer implements JsonDeserializer { + @Override + public @Nullable ScheduledEvent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + return context.deserialize(jsonObject, scheduledEventClass); + } + } /** - * private helper class for passing http url query parameters + * Protected helper class for passing http url query parameters */ - private static class Query { + protected static class Query { private final String key; private final String value; @@ -137,73 +171,96 @@ public String toString() { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) { - base = "http://" + ipAddress + "/api/"; - shades = base + "shades/"; - firmwareVersion = base + "fwversion"; - sceneActivate = base + "scenes"; - scenes = base + "scenes/"; - - // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections". - sceneCollectionActivate = base + "scenecollections"; - sceneCollections = base + "scenecollections/"; - - scheduledEvents = base + "scheduledevents"; - repeaters = base + "repeaters/"; - userData = base + "userdata"; - + public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class sceneClass, Class shadeDataClass, + Class shadePositionClass, Class scheduledEventClass) { this.httpClient = httpClient; + this.sceneClass = sceneClass; + this.shadeDataClass = shadeDataClass; + this.shadePositionClass = shadePositionClass; + this.scheduledEventClass = scheduledEventClass; + this.gson = new GsonBuilder() + // @formatter:off + .registerTypeAdapter(Scene.class, new SceneDeserializer()) + .registerTypeAdapter(ShadeData.class, new ShadeDataDeserializer()) + .registerTypeAdapter(ShadePosition.class, new ShadePositionDeserializer()) + .registerTypeAdapter(ScheduledEvent.class, new ScheduledEventDeserializer()) + // @formatter:on + .create(); } /** - * Fetches a JSON package with firmware information for the hub. + * Get the gson de-serializer. * - * @return FirmwareVersions class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance + * @return the gson de-serializer. */ - public HubFirmware getFirmwareVersions() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, firmwareVersion, null, null); - try { - FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class); - if (firmwareVersion == null) { - throw new HubInvalidResponseException("Missing firmware response"); - } - HubFirmware firmwareVersions = firmwareVersion.firmware; - if (firmwareVersions == null) { - throw new HubInvalidResponseException("Missing 'firmware' element"); - } - return firmwareVersions; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing firmware response", e); - } + public Gson getGson() { + return gson; } /** - * Fetches a JSON package with user data information for the hub. + * Common protected method to invoke a call on the hub server to retrieve information or send a command * - * @return {@link UserData} class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance + * @param method GET or PUT + * @param url the host url to be called + * @param query the http query parameter + * @param jsonCommand the request command content (as a json string) + * @return the response content (as a json string) + * @throws HubMaintenanceException + * @throws HubProcessingException */ - public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, userData, null, null); - try { - UserDataResponse userDataResponse = gson.fromJson(json, UserDataResponse.class); - if (userDataResponse == null) { - throw new HubInvalidResponseException("Missing userData response"); + protected synchronized String invoke(HttpMethod method, String url, @Nullable Query query, + @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { + if (logger.isTraceEnabled()) { + if (query != null) { + logger.trace("API command {} {}{}", method, url, query); + } else { + logger.trace("API command {} {}", method, url); + } + if (jsonCommand != null) { + logger.trace("JSON command = {}", jsonCommand); } - UserData userData = userDataResponse.userData; - if (userData == null) { - throw new HubInvalidResponseException("Missing 'userData' element"); + } + Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); + if (query != null) { + request.param(query.getKey(), query.getValue()); + } + if (jsonCommand != null) { + request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); + } + ContentResponse response; + try { + response = request.send(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } catch (TimeoutException | ExecutionException e) { + if (Instant.now().isBefore(maintenanceScheduledEnd)) { + // throw "softer" exception during maintenance window + logger.debug("Hub still undergoing maintenance"); + throw new HubMaintenanceException("Hub still undergoing maintenance"); } - return userData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing userData response", e); + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } + int statusCode = response.getStatus(); + if (statusCode == HttpStatus.LOCKED_423) { + // set end of maintenance window, and throw a "softer" exception + maintenanceScheduledEnd = Instant.now().plus(maintenancePeriod); + logger.debug("Hub undergoing maintenance"); + throw new HubMaintenanceException("Hub undergoing maintenance"); + } + if (statusCode != HttpStatus.OK_200) { + logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); + throw new HubProcessingException(String.format("HTTP %d error", statusCode)); + } + String jsonResponse = response.getContentAsString(); + if (logger.isTraceEnabled()) { + logger.trace("JSON response = {}", jsonResponse); + } + if (jsonResponse == null || jsonResponse.isEmpty()) { + logger.warn("Hub returned no content"); + throw new HubProcessingException("Missing response entity"); } + return jsonResponse; } /** @@ -215,22 +272,42 @@ public UserData getUserData() throws HubInvalidResponseException, HubProcessingE * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, shades, null, null); - try { - Shades shades = gson.fromJson(json, Shades.class); - if (shades == null) { - throw new HubInvalidResponseException("Missing shades response"); - } - List shadeData = shades.shadeData; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); - } - return shades; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shades response", e); - } - } + public abstract Shades getShades() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + + /** + * Fetches a JSON package that describes all scenes in the hub, and wraps it in + * a Scenes class instance + * + * @return Scenes class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + public abstract Scenes getScenes() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + + /** + * Fetches a JSON package with firmware information for the hub. + * + * @return FirmwareVersions class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + public abstract HubFirmware getFirmwareVersions() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + + /** + * Fetches a JSON package with user data information for the hub. + * + * @return {@link UserData} class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + public abstract UserData getUserData() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Instructs the hub to move a specific shade @@ -243,31 +320,8 @@ public Shades getShades() throws HubInvalidResponseException, HubProcessingExcep * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeMove(position)); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException { - try { - Shade shade = gson.fromJson(json, Shade.class); - if (shade == null) { - throw new HubInvalidResponseException("Missing shade response"); - } - ShadeData shadeData = shade.shade; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shade.shade' element"); - } - if (Boolean.TRUE.equals(shadeData.timedOut)) { - throw new HubShadeTimeoutException("Timeout when sending request to the shade"); - } - return shadeData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shade response", e); - } - } + public abstract ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to stop movement of a specific shade @@ -279,12 +333,8 @@ private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseExcept * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeStop()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to jog a specific shade @@ -296,12 +346,8 @@ public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubP * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeJog()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to calibrate a specific shade @@ -313,38 +359,8 @@ public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubPr * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeCalibrate()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Fetches a JSON package that describes all scenes in the hub, and wraps it in - * a Scenes class instance - * - * @return Scenes class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, scenes, null, null); - try { - Scenes scenes = gson.fromJson(json, Scenes.class); - if (scenes == null) { - throw new HubInvalidResponseException("Missing scenes response"); - } - List sceneData = scenes.sceneData; - if (sceneData == null) { - throw new HubInvalidResponseException("Missing 'scenes.sceneData' element"); - } - return scenes; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scenes response", e); - } - } + public abstract ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to execute a specific scene @@ -353,9 +369,7 @@ public Scenes getScenes() throws HubInvalidResponseException, HubProcessingExcep * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); - } + public abstract void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes all scene collections in the hub, and wraps it in @@ -366,23 +380,8 @@ public void activateScene(int sceneId) throws HubProcessingException, HubMainten * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public SceneCollections getSceneCollections() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, sceneCollections, null, null); - try { - SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class); - if (sceneCollections == null) { - throw new HubInvalidResponseException("Missing sceneCollections response"); - } - List sceneCollectionData = sceneCollections.sceneCollectionData; - if (sceneCollectionData == null) { - throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element"); - } - return sceneCollections; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing sceneCollections response", e); - } - } + public abstract SceneCollections getSceneCollections() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Instructs the hub to execute a specific scene collection @@ -391,10 +390,8 @@ public SceneCollections getSceneCollections() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.GET, sceneCollectionActivate, - Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null); - } + public abstract void activateSceneCollection(int sceneCollectionId) + throws HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in @@ -405,50 +402,20 @@ public void activateSceneCollection(int sceneCollectionId) throws HubProcessingE * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public ScheduledEvents getScheduledEvents() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, scheduledEvents, null, null); - try { - ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class); - if (scheduledEvents == null) { - throw new HubInvalidResponseException("Missing scheduledEvents response"); - } - List scheduledEventData = scheduledEvents.scheduledEventData; - if (scheduledEventData == null) { - throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element"); - } - return scheduledEvents; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scheduledEvents response", e); - } - } + public abstract ScheduledEvents getScheduledEvents() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Enables or disables a scheduled event in the hub. - * + * * @param scheduledEventId id of the scheduled event to be enabled or disabled * @param enable true to enable scheduled event, false to disable * @throws HubInvalidResponseException if response is invalid * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void enableScheduledEvent(int scheduledEventId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String uri = scheduledEvents + "/" + scheduledEventId; - String jsonResponse = invoke(HttpMethod.GET, uri, null, null); - try { - JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject(); - JsonElement scheduledEventElement = jsonObject.get("scheduledEvent"); - if (scheduledEventElement == null) { - throw new HubInvalidResponseException("Missing 'scheduledEvent' element"); - } - JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject(); - scheduledEventObject.addProperty("enabled", enable); - invoke(HttpMethod.PUT, uri, null, jsonObject.toString()); - } catch (JsonParseException | IllegalStateException e) { - throw new HubInvalidResponseException("Error parsing scheduledEvent response", e); - } - } + public abstract void enableScheduledEvent(int scheduledEventId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes all repeaters in the hub, and wraps it in @@ -459,23 +426,8 @@ public void enableScheduledEvent(int scheduledEventId, boolean enable) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public Repeaters getRepeaters() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, repeaters, null, null); - try { - Repeaters repeaters = gson.fromJson(json, Repeaters.class); - if (repeaters == null) { - throw new HubInvalidResponseException("Missing repeaters response"); - } - List repeaterData = repeaters.repeaterData; - if (repeaterData == null) { - throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element"); - } - return repeaters; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing repeaters response", e); - } - } + public abstract Repeaters getRepeaters() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes a specific repeater in the hub, and wraps it @@ -487,27 +439,8 @@ public Repeaters getRepeaters() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public RepeaterData getRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null); - return repeaterDataFromJson(jsonResponse); - } - - private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException { - try { - Repeater repeater = gson.fromJson(json, Repeater.class); - if (repeater == null) { - throw new HubInvalidResponseException("Missing repeater response"); - } - RepeaterData repeaterData = repeater.repeater; - if (repeaterData == null) { - throw new HubInvalidResponseException("Missing 'repeater.repeater' element"); - } - return repeaterData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing repeater response", e); - } - } + public abstract RepeaterData getRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Instructs the hub to identify a specific repeater by blinking @@ -518,16 +451,12 @@ private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponse * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public RepeaterData identifyRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId, - Query.of("identify", Boolean.toString(true)), null); - return repeaterDataFromJson(jsonResponse); - } + public abstract RepeaterData identifyRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Enables or disables blinking for a repeater - * + * * @param repeaterId id of the repeater for which to be enable or disable blinking * @param enable true to enable blinking, false to disable * @return RepeaterData class instance @@ -535,12 +464,8 @@ public RepeaterData identifyRepeater(int repeaterId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable)); - String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); - return repeaterDataFromJson(jsonResponse); - } + public abstract RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Sets color and brightness for a repeater @@ -551,78 +476,8 @@ public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public RepeaterData setRepeaterColor(int repeaterId, Color color) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color)); - String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); - return repeaterDataFromJson(jsonResponse); - } - - /** - * Invoke a call on the hub server to retrieve information or send a command - * - * @param method GET or PUT - * @param url the host url to be called - * @param query the http query parameter - * @param jsonCommand the request command content (as a json string) - * @return the response content (as a json string) - * @throws HubMaintenanceException - * @throws HubProcessingException - */ - private synchronized String invoke(HttpMethod method, String url, @Nullable Query query, - @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { - if (logger.isTraceEnabled()) { - if (query != null) { - logger.trace("API command {} {}{}", method, url, query); - } else { - logger.trace("API command {} {}", method, url); - } - if (jsonCommand != null) { - logger.trace("JSON command = {}", jsonCommand); - } - } - Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); - if (query != null) { - request.param(query.getKey(), query.getValue()); - } - if (jsonCommand != null) { - request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); - } - ContentResponse response; - try { - response = request.send(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); - } catch (TimeoutException | ExecutionException e) { - if (Instant.now().isBefore(maintenanceScheduledEnd)) { - // throw "softer" exception during maintenance window - logger.debug("Hub still undergoing maintenance"); - throw new HubMaintenanceException("Hub still undergoing maintenance"); - } - throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); - } - int statusCode = response.getStatus(); - if (statusCode == HttpStatus.LOCKED_423) { - // set end of maintenance window, and throw a "softer" exception - maintenanceScheduledEnd = Instant.now().plus(maintenancePeriod); - logger.debug("Hub undergoing maintenance"); - throw new HubMaintenanceException("Hub undergoing maintenance"); - } - if (statusCode != HttpStatus.OK_200) { - logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); - throw new HubProcessingException(String.format("HTTP %d error", statusCode)); - } - String jsonResponse = response.getContentAsString(); - if (logger.isTraceEnabled()) { - logger.trace("JSON response = {}", jsonResponse); - } - if (jsonResponse == null || jsonResponse.isEmpty()) { - logger.warn("Hub returned no content"); - throw new HubProcessingException("Missing response entity"); - } - return jsonResponse; - } + public abstract RepeaterData setRepeaterColor(int repeaterId, Color color) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes a specific shade in the hub, and wraps it @@ -635,11 +490,8 @@ private synchronized String invoke(HttpMethod method, String url, @Nullable Quer * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -653,12 +505,8 @@ public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubPr * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData refreshShadePosition(int shadeId) - throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("refresh", Boolean.toString(true)), null); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData refreshShadePosition(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -672,24 +520,8 @@ public ShadeData refreshShadePosition(int shadeId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public List getShadeSurvey(int shadeId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("survey", Boolean.toString(true)), null); - try { - Survey survey = gson.fromJson(jsonResponse, Survey.class); - if (survey == null) { - throw new HubInvalidResponseException("Missing survey response"); - } - List surveyData = survey.surveyData; - if (surveyData == null) { - throw new HubInvalidResponseException("Missing 'survey.surveyData' element"); - } - return surveyData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing survey response", e); - } - } + public abstract List getShadeSurvey(int shadeId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -703,10 +535,6 @@ public List getShadeSurvey(int shadeId) * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("updateBatteryLevel", Boolean.toString(true)), null); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java new file mode 100644 index 0000000000000..1eff3b0b31b32 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java @@ -0,0 +1,624 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal._v1; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethod; +import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal.api.Color; +import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api.SurveyData; +import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; +import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; +import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; +import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; +import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; +import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.Shade; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Survey; +import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; +import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; + +/** + * JAX-RS targets for communicating with an HD PowerView hub Generation 1/2 + * + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Added support for secondary rail positions + * @author Jacob Laursen - Added support for scene groups and automations + */ +@NonNullByDefault +public class HDPowerViewWebTargetsV1 extends HDPowerViewWebTargets { + + private final String firmwareVersion; + private final String shades; + private final String sceneActivate; + private final String scenes; + private final String sceneCollectionActivate; + private final String sceneCollections; + private final String scheduledEvents; + private final String repeaters; + private final String userData; + + /** + * Initialize the web targets + * + * @param httpClient the HTTP client (the binding) + * @param ipAddress the IP address of the server (the hub) + */ + public HDPowerViewWebTargetsV1(HttpClient httpClient, String ipAddress) { + super(httpClient, ipAddress, SceneV1.class, ShadeDataV1.class, ShadePositionV1.class, ScheduledEventV1.class); + + // initialize the urls + String base = "http://" + ipAddress + "/api/"; + shades = base + "shades/"; + firmwareVersion = base + "fwversion"; + sceneActivate = base + "scenes"; + scenes = base + "scenes/"; + + // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections". + sceneCollectionActivate = base + "scenecollections"; + sceneCollections = base + "scenecollections/"; + + scheduledEvents = base + "scheduledevents"; + repeaters = base + "repeaters/"; + userData = base + "userdata"; + } + + /** + * Protected method to create ShadeData instances from a JSON payload. + * + * @param json the json payload + * @return a ShadeData instance + * @throws HubInvalidResponseException in case od missing or invalid response + * @throws HubShadeTimeoutException in case of connection time out (in V1 implementation) + */ + protected ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException { + try { + Shade shade = gson.fromJson(json, Shade.class); + if (shade == null) { + throw new HubInvalidResponseException("Missing shade response"); + } + ShadeData shadeData = shade.shade; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shade.shade' element"); + } + if (shadeData.version() == 1 && Boolean.TRUE.equals(((ShadeDataV1) shadeData).timedOut)) { + throw new HubShadeTimeoutException("Timeout when sending request to the shade"); + } + return shadeData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shade response", e); + } + } + + /** + * Fetches a JSON package with firmware information for the hub. + * + * @return FirmwareVersions class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public HubFirmware getFirmwareVersions() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, firmwareVersion, null, null); + try { + FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class); + if (firmwareVersion == null) { + throw new HubInvalidResponseException("Missing firmware response"); + } + HubFirmware firmwareVersions = firmwareVersion.firmware; + if (firmwareVersions == null) { + throw new HubInvalidResponseException("Missing 'firmware' element"); + } + return firmwareVersions; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing firmware response", e); + } + } + + /** + * Fetches a JSON package with user data information for the hub. + * + * @return {@link UserData} class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, userData, null, null); + try { + UserDataResponse userDataResponse = gson.fromJson(json, UserDataResponse.class); + if (userDataResponse == null) { + throw new HubInvalidResponseException("Missing userData response"); + } + UserData userData = userDataResponse.userData; + if (userData == null) { + throw new HubInvalidResponseException("Missing 'userData' element"); + } + return userData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing userData response", e); + } + } + + /** + * Fetches a JSON package that describes all shades in the hub, and wraps it in + * a Shades class instance + * + * @return Shades class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades, null, null); + try { + Shades shades = gson.fromJson(json, Shades.class); + if (shades == null) { + throw new HubInvalidResponseException("Missing shades response"); + } + List shadeData = shades.shadeData; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); + } + return shades; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shades response", e); + } + } + + /** + * Instructs the hub to move a specific shade + * + * @param shadeId id of the shade to be moved + * @param position instance of ShadePosition containing the new position + * @return ShadeData class instance (with new position) + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeMove(position)); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to stop movement of a specific shade + * + * @param shadeId id of the shade to be stopped + * @return ShadeData class instance (new position cannot be relied upon) + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeStop()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to jog a specific shade + * + * @param shadeId id of the shade to be jogged + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeJog()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to calibrate a specific shade + * + * @param shadeId id of the shade to be calibrated + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeCalibrate()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Fetches a JSON package that describes all scenes in the hub, and wraps it in + * a Scenes class instance + * + * @return Scenes class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scenes, null, null); + try { + Scenes scenes = gson.fromJson(json, Scenes.class); + if (scenes == null) { + throw new HubInvalidResponseException("Missing scenes response"); + } + List sceneData = scenes.sceneData; + if (sceneData == null) { + throw new HubInvalidResponseException("Missing 'scenes.sceneData' element"); + } + return scenes; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scenes response", e); + } + } + + /** + * Instructs the hub to execute a specific scene + * + * @param sceneId id of the scene to be executed + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); + } + + /** + * Fetches a JSON package that describes all scene collections in the hub, and wraps it in + * a SceneCollections class instance + * + * @return SceneCollections class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public SceneCollections getSceneCollections() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, sceneCollections, null, null); + try { + SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class); + if (sceneCollections == null) { + throw new HubInvalidResponseException("Missing sceneCollections response"); + } + List sceneCollectionData = sceneCollections.sceneCollectionData; + if (sceneCollectionData == null) { + throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element"); + } + return sceneCollections; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing sceneCollections response", e); + } + } + + /** + * Instructs the hub to execute a specific scene collection + * + * @param sceneCollectionId id of the scene collection to be executed + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneCollectionActivate, + Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null); + } + + /** + * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in + * a ScheduledEvents class instance + * + * @return ScheduledEvents class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public ScheduledEvents getScheduledEvents() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scheduledEvents, null, null); + try { + ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class); + if (scheduledEvents == null) { + throw new HubInvalidResponseException("Missing scheduledEvents response"); + } + List scheduledEventData = scheduledEvents.scheduledEventData; + if (scheduledEventData == null) { + throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element"); + } + return scheduledEvents; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scheduledEvents response", e); + } + } + + /** + * Enables or disables a scheduled event in the hub. + * + * @param scheduledEventId id of the scheduled event to be enabled or disabled + * @param enable true to enable scheduled event, false to disable + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public void enableScheduledEvent(int scheduledEventId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String uri = scheduledEvents + "/" + scheduledEventId; + String jsonResponse = invoke(HttpMethod.GET, uri, null, null); + try { + JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject(); + JsonElement scheduledEventElement = jsonObject.get("scheduledEvent"); + if (scheduledEventElement == null) { + throw new HubInvalidResponseException("Missing 'scheduledEvent' element"); + } + JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject(); + scheduledEventObject.addProperty("enabled", enable); + invoke(HttpMethod.PUT, uri, null, jsonObject.toString()); + } catch (JsonParseException | IllegalStateException e) { + throw new HubInvalidResponseException("Error parsing scheduledEvent response", e); + } + } + + /** + * Fetches a JSON package that describes all repeaters in the hub, and wraps it in + * a Repeaters class instance + * + * @return Repeaters class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public Repeaters getRepeaters() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, repeaters, null, null); + try { + Repeaters repeaters = gson.fromJson(json, Repeaters.class); + if (repeaters == null) { + throw new HubInvalidResponseException("Missing repeaters response"); + } + List repeaterData = repeaters.repeaterData; + if (repeaterData == null) { + throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element"); + } + return repeaters; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing repeaters response", e); + } + } + + /** + * Fetches a JSON package that describes a specific repeater in the hub, and wraps it + * in a RepeaterData class instance + * + * @param repeaterId id of the repeater to be fetched + * @return RepeaterData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public RepeaterData getRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null); + return repeaterDataFromJson(jsonResponse); + } + + private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException { + try { + Repeater repeater = gson.fromJson(json, Repeater.class); + if (repeater == null) { + throw new HubInvalidResponseException("Missing repeater response"); + } + RepeaterData repeaterData = repeater.repeater; + if (repeaterData == null) { + throw new HubInvalidResponseException("Missing 'repeater.repeater' element"); + } + return repeaterData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing repeater response", e); + } + } + + /** + * Instructs the hub to identify a specific repeater by blinking + * + * @param repeaterId id of the repeater to be identified + * @return RepeaterData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public RepeaterData identifyRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId, + Query.of("identify", Boolean.toString(true)), null); + return repeaterDataFromJson(jsonResponse); + } + + /** + * Enables or disables blinking for a repeater + * + * @param repeaterId id of the repeater for which to be enable or disable blinking + * @param enable true to enable blinking, false to disable + * @return RepeaterData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable)); + String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); + return repeaterDataFromJson(jsonResponse); + } + + /** + * Sets color and brightness for a repeater + * + * @param repeaterId id of the repeater for which to set color and brightness + * @return RepeaterData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public RepeaterData setRepeaterColor(int repeaterId, Color color) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color)); + String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); + return repeaterDataFromJson(jsonResponse); + } + + /** + * Fetches a JSON package that describes a specific shade in the hub, and wraps it + * in a Shade class instance + * + * @param shadeId id of the shade to be fetched + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on + * a specific shade's position; fetches a JSON package that describes that shade, + * and wraps it in a Shade class instance + * + * @param shadeId id of the shade to be refreshed + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData refreshShadePosition(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("refresh", Boolean.toString(true)), null); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on + * a specific shade's survey data, which will also refresh signal strength; + * fetches a JSON package that describes that survey, and wraps it in a Survey + * class instance + * + * @param shadeId id of the shade to be surveyed + * @return List of SurveyData class instances + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public List getShadeSurvey(int shadeId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("survey", Boolean.toString(true)), null); + try { + Survey survey = gson.fromJson(jsonResponse, Survey.class); + if (survey == null) { + throw new HubInvalidResponseException("Missing survey response"); + } + List surveyData = survey.surveyData; + if (surveyData == null) { + throw new HubInvalidResponseException("Missing 'survey.surveyData' element"); + } + return surveyData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing survey response", e); + } + } + + /** + * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on + * a specific shade's battery level; fetches a JSON package that describes that shade, + * and wraps it in a Shade class instance + * + * @param shadeId id of the shade to be refreshed + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("updateBatteryLevel", Boolean.toString(true)), null); + return shadeDataFromJson(jsonResponse); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java new file mode 100644 index 0000000000000..18b0979f4c5a2 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java @@ -0,0 +1,551 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal._v3; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.sse.InboundSseEvent; +import javax.ws.rs.sse.SseEventSource; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethod; +import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal.api.Color; +import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api.SurveyData; +import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.Shade; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.InfoV3; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.SceneV3; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.ScheduledEventV3; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.SseSceneV3; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.SseShadeV3; +import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +/** + * JAX-RS targets for communicating with an HD PowerView hub Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewWebTargetsV3 extends HDPowerViewWebTargets { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargetsV3.class); + + private static final String IDS = "ids"; + + /** + * Simple DTO for registering the binding with the hub. + * + * @author AndrewFG - Initial contribution + */ + @SuppressWarnings("unused") + private static class GatewayRegistration { + public String todo = "org.openhab.binding.hdpowerview"; // TODO + } + + private final String shades; + private final String scenes; + private final String sceneActivate; + private final String shadeMotion; + private final String shadeStop; + private final String shadePositions; + private final String firmware; + private final String automations; + private final String register; + private final String shadeEvents; + private final String sceneEvents; + + // @formatter:off + private final Type shadeListType = new TypeToken>() {}.getType(); + // @formatter:on + + // @formatter:off + private final Type sceneListType = new TypeToken>() {}.getType(); + // @formatter:on + + // @formatter:off + private final Type scheduledEventListType = + new TypeToken>() {}.getType(); + // @formatter:on + + private boolean isRegistered; + private @Nullable SseSinkV3 sseSink; + private final Client sseClient = ClientBuilder.newClient(); + private @Nullable SseEventSource shadeEventSource; + private @Nullable SseEventSource sceneEventSource; + + /** + * Initialize the web targets + * + * @param httpClient the HTTP client (the binding) + * @param ipAddress the IP address of the server (the hub) + */ + public HDPowerViewWebTargetsV3(HttpClient httpClient, String ipAddress) { + super(httpClient, ipAddress, SceneV3.class, ShadeDataV3.class, ShadePositionV3.class, ScheduledEventV3.class); + + // initialize the urls + String base = "http://" + ipAddress + "/"; + + String home = base + "home/"; + shades = home + "shades"; + scenes = home + "scenes"; + sceneActivate = home + "scenes/%d/activate"; + shadeMotion = home + "shades/%d/motion"; + shadeStop = home + "shades/stop"; + shadePositions = home + "shades/positions"; + automations = home + "automations"; + shadeEvents = home + "shades/events"; + sceneEvents = home + "scenes/events"; + + String gateway = base + "gateway/"; + firmware = gateway + "info"; + register = gateway + "TBD"; // TODO + } + + @Override + public HubFirmware getFirmwareVersions() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, firmware, null, null); + return toFirmware(json); + } + + @Override + public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getUserData(): method not implemented"); + } + + @Override + public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades, null, null); + return toShades(json); + } + + @Override + public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), + gson.toJson(position)); + return getShade(shadeId); + } + + @Override + public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + invoke(HttpMethod.PUT, shadeStop, Query.of(IDS, Integer.valueOf(shadeId).toString()), null); + return getShade(shadeId); + } + + @Override + public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String json = gson.toJson(new ShadeJog()); + invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); + return getShade(shadeId); + } + + @Override + public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String json = gson.toJson(new ShadeCalibrate()); + invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); + return getShade(shadeId); + } + + @Override + public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, this.scenes, null, null); + return toScenes(json); + } + + @Override + public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.PUT, String.format(sceneActivate, sceneId), null, null); + } + + @Override + public SceneCollections getSceneCollections() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getSceneCollections(): method not implemented"); + } + + @Override + public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("activateSceneCollection(): method not implemented"); + } + + @Override + public ScheduledEvents getScheduledEvents() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, automations, null, null); + return toScheduledEvents(json); + } + + @Override + public void enableScheduledEvent(int scheduledEventId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("enableScheduledEvent(): method not implemented"); + } + + @Override + public Repeaters getRepeaters() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getRepeaters(): method not implemented"); + } + + @Override + public RepeaterData getRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getRepeater(): method not implemented"); + } + + @Override + public RepeaterData identifyRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("identifyRepeater(): method not implemented"); + } + + @Override + public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("enableRepeaterBlinking(): method not implemented"); + } + + @Override + public RepeaterData setRepeaterColor(int repeaterId, Color color) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("setRepeaterColor(): method not implemented"); + } + + @Override + public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); + return toShadeData(json); + } + + @Override + public ShadeData refreshShadePosition(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + return getShade(shadeId); + } + + @Override + public List getShadeSurvey(int shadeId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getShadeSurvey(): method not implemented"); + } + + @Override + public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + return getShade(shadeId); + } + + /** + * Register the binding with the hub (if not already registered). + * + * @throws HubProcessingException + * @throws HubMaintenanceException + */ + private void gatewayRegister() throws HubMaintenanceException, HubProcessingException { + if (!isRegistered) { + String json = gson.toJson(new GatewayRegistration()); + invoke(HttpMethod.PUT, register, null, json); + isRegistered = true; + } + } + + /** + * Set the sink for SSE events. + * + * @param sseSink a class that implements the SseSinkV3 interface. + * @return true if registered for SSE events. + * @throws HubProcessingException + * @throws HubMaintenanceException + */ + public boolean sseSubscribe(@Nullable SseSinkV3 sseSink) throws HubMaintenanceException, HubProcessingException { + SseEventSource source; + + this.sseSink = sseSink; + + if (sseSink != null) { + // register ourself with the gateway (if necessary) + gatewayRegister(); + + // open SSE channel for shades + SseEventSource shadeEventSource = this.shadeEventSource; + if (shadeEventSource == null || !shadeEventSource.isOpen()) { + source = SseEventSource.target(sseClient.target(shadeEvents)).build(); + source.register((event) -> onShadeEvent(event)); + source.open(); + if (!source.isOpen()) { + throw new HubProcessingException("setEventSink(): failed to open SSE channel for shades"); + } + this.shadeEventSource = source; + } + + // open SSE channel for scenes + SseEventSource sceneEventSource = this.sceneEventSource; + if (sceneEventSource == null || !sceneEventSource.isOpen()) { + source = SseEventSource.target(sseClient.target(sceneEvents)).build(); + source.register((event) -> onSceneEvent(event)); + source.open(); + if (!source.isOpen()) { + throw new HubProcessingException("setEventSink(): failed to open SSE channel for scenes"); + } + this.sceneEventSource = source; + } + return true; + } + + // close SSE channel for shades + source = shadeEventSource; + if (source != null) { + source.close(); + shadeEventSource = null; + } + + // close SSE channel for scenes + source = sceneEventSource; + if (source != null) { + source.close(); + sceneEventSource = null; + } + return false; + } + + /** + * Handle inbound SSE events for a shade. + * + * @param sseEvent the inbound event + */ + private void onShadeEvent(InboundSseEvent sseEvent) { + SseSinkV3 eventSink = this.sseSink; + if (eventSink != null) { + String json = sseEvent.readData(); + logger.trace("onShadeEvent(): {}", json); + try { + ShadeData shadeData = toShadeData2(json); + ShadePosition shadePosition = shadeData.positions; + if (shadePosition != null) { + String evt = toEvt(json); + eventSink.sseShade(evt, shadeData.id, shadePosition); + } + } catch (HubInvalidResponseException e) { + // swallow the exception; don't pass back to caller + } + } + } + + /** + * Handle inbound SSE events for a scene. + * + * @param sseEvent the inbound event + */ + private void onSceneEvent(InboundSseEvent sseEvent) { + SseSinkV3 eventSink = this.sseSink; + if (eventSink != null) { + String json = sseEvent.readData(); + logger.trace("onSceneEvent(): {}", json); + try { + Scene scene = toScene(json); + String evt = toEvt(json); + eventSink.sseScene(evt, scene.id); + } catch (HubInvalidResponseException e) { + // swallow the exception; don't pass back to caller + } + } + } + + /** + * Create ShadeData instance from a JSON payload. + * + * @param json the json payload + * @return a ShadeData instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private ShadeData toShadeData(String json) throws HubInvalidResponseException { + try { + Shade shade = gson.fromJson(json, Shade.class); + if (shade == null) { + throw new HubInvalidResponseException("Missing shade response"); + } + ShadeData shadeData = shade.shade; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shade.shade' element"); + } + return shadeData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shade response", e); + } + } + + /** + * Create Shades instance from a JSON payload. + * + * @param json the json payload + * @return a Shades instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private Shades toShades(String json) throws HubInvalidResponseException { + try { + Shades shades = new Shades(); + shades.shadeData = gson.fromJson(json, shadeListType); + if (shades.shadeData == null) { + throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); + } + return shades; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shades response", e); + } + } + + /** + * Create HubFirmware instance from a JSON payload. + * + * @param json the json payload + * @return a HubFirmware instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private HubFirmware toFirmware(String json) throws HubProcessingException { + try { + InfoV3 gatewayInfo = gson.fromJson(json, InfoV3.class); + if (gatewayInfo == null) { + throw new HubProcessingException("getFirmwareVersions(): missing gatewayInfo"); + } + return gatewayInfo.toHubFirmware(); + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing gateway info response", e); + } + } + + /** + * Create Scenes instance from a JSON payload. + * + * @param json the json payload + * @return a Scenes instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private Scenes toScenes(String json) throws HubInvalidResponseException { + try { + Scenes scenes = new Scenes(); + scenes.sceneData = gson.fromJson(json, sceneListType); + return scenes; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scenes response", e); + } + } + + /** + * Create ScheduledEvents instance from a JSON payload. + * + * @param json the json payload + * @return a ScheduledEvents instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private ScheduledEvents toScheduledEvents(String json) throws HubInvalidResponseException { + try { + ScheduledEvents scheduledEvents = new ScheduledEvents(); + scheduledEvents.scheduledEventData = gson.fromJson(json, scheduledEventListType); + return scheduledEvents; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing automation response", e); + } + } + + /** + * Get the 'evt' message from an event JSON payload. + * + * @param json the json payload + * @return the message + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private String toEvt(String json) throws HubInvalidResponseException { + SseShadeV3 sseShade = gson.fromJson(json, SseShadeV3.class); + if (sseShade != null) { + String evt = sseShade.evt; + if (evt != null) { + return evt; + } + } + throw new HubInvalidResponseException("Error parsing event"); + } + + /** + * Create ShadeData instance from a JSON shade event payload. + * + * @param json the json payload + * @return a ShadeData instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private ShadeData toShadeData2(String json) throws HubInvalidResponseException { + SseShadeV3 sseShade = gson.fromJson(json, SseShadeV3.class); + if (sseShade != null) { + ShadePosition shadePosition = sseShade.currentPositions; + if (shadePosition != null) { + ShadeData shadeData = new ShadeDataV3(); + shadeData.id = sseShade.id; + shadeData.positions = sseShade.currentPositions; + return shadeData; + } + } + throw new HubInvalidResponseException("Error parsing shade event"); + } + + /** + * Create Scene instance from a JSON scene event payload. + * + * @param json the json payload + * @return a Scene instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private Scene toScene(String json) throws HubInvalidResponseException { + SseSceneV3 sceneEvent = gson.fromJson(json, SseSceneV3.class); + if (sceneEvent != null) { + Scene scene = sceneEvent.scene; + if (scene != null) { + return scene; + } + } + throw new HubInvalidResponseException("Error parsing scene event"); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java new file mode 100644 index 0000000000000..bf827dfe17580 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; + +/** + * Interface for receiving SSE events from Generation 3 hubs. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public interface SseSinkV3 { + + /** + * Method that is called when a shade changes state. + * + * @param evt contents of json evt element. + * @param shadeId the id of the shade that changed. + * @param shadePosition the shade's new position. + */ + public void sseShade(String evt, int shadeId, ShadePosition shadePosition); + + /** + * Method that is called when a scene changes state. + * + * @param evt contents of json 'evt' element. + * @param sceneId the id of the scene that changed. + */ + public void sseScene(String evt, int sceneId); +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java new file mode 100644 index 0000000000000..4d240e7aec367 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api; + +import java.util.Base64; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Abstract class for state of a Shade as returned by an HD PowerView hub. + * + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Refactored into separate class + */ +@NonNullByDefault +public abstract class ShadeData { + // fields common to Generation 1/2 and 3 hubs + public int id; + public @Nullable String name; + public int roomId; + public int type; + public int batteryStatus; + public @Nullable ShadePosition positions; + public int signalStrength; + public @Nullable Integer capabilities; + public @Nullable Firmware firmware; + + public String getName() { + return new String(Base64.getDecoder().decode(name)); + } + + public abstract BatteryKind getBatteryKind(); + + public abstract int version(); +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java index 0eee7aff49ce8..08a0ec1a2308d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java @@ -12,45 +12,17 @@ */ package org.openhab.binding.hdpowerview.internal.api; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; - import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; -import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; -import org.openhab.core.types.UnDefType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * The position of a single shade, as returned by the HD PowerView hub + * Abstract class for position of a Shade as returned by an HD PowerView hub. * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Added support for secondary rail positions + * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadePosition { - - private final transient Logger logger = LoggerFactory.getLogger(ShadePosition.class); - - /** - * Primary actuator position. - */ - private int posKind1; - private int position1; - - /** - * Secondary actuator position. - * - * Here we have to use Integer objects rather than just int primitives because these are secondary optional position - * elements in the JSON payload, so the GSON de-serializer might leave them as null. - */ - private @Nullable Integer posKind2 = null; - private @Nullable Integer position2 = null; - - public ShadePosition() { - } +public abstract class ShadePosition { /** * Get the shade's State for the given actuator class resp. coordinate system. @@ -59,248 +31,14 @@ public ShadePosition() { * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. * @return the current state. */ - public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - State result = getPosition1(shadeCapabilities, posKindCoords); - if (result == UnDefType.UNDEF) { - result = getPosition2(shadeCapabilities, posKindCoords); - } - logger.trace("getState(): capabilities={}, coords={} => result={}", shadeCapabilities, posKindCoords, result); - return result; - } - - /** - * Set the shade's position1 value for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - */ - private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { - // on dual rail shades constrain percent to not move the lower rail above the upper - State secondary = getState(shadeCapabilities, SECONDARY_POSITION); - if (secondary instanceof PercentType) { - int secPercent = ((PercentType) secondary).intValue(); - if (percent < secPercent) { - percent = secPercent; - } - } - } - posKind1 = posKindCoords.ordinal(); - position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); - break; - - case SECONDARY_POSITION: - /* - * Secondary, blackout shade a 'Duolite' shade: => INVERTED - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - posKind1 = posKindCoords.ordinal(); - if (shadeCapabilities.supportsSecondaryOverlapped()) { - position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); - } else { - position1 = (int) Math.round((double) percent / 100 * MAX_SHADE); - } - break; - - case VANE_TILT_POSITION: - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - */ - posKind1 = posKindCoords.ordinal(); - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - position1 = (int) Math.round((double) percent / 100 * max); - break; - - default: - posKind1 = CoordinateSystem.NONE.ordinal(); - position1 = 0; - } - } - - /** - * Get the shade's position1 State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the State (or UNDEF if not available). - */ - private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (posKindCoords.equals(posKind1)) { - return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); - } - if (VANE_TILT_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { - return PercentType.HUNDRED; - } - if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { - return PercentType.HUNDRED; - } - break; - - case SECONDARY_POSITION: - /* - * Secondary, blackout shade a 'Duolite' shade: => INVERTED - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (posKindCoords.equals(posKind1)) { - if (shadeCapabilities.supportsSecondaryOverlapped()) { - return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); - } - return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100)); - } - if (!SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { - return PercentType.ZERO; - } - break; - - case VANE_TILT_POSITION: - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - * - * If the shades are not open, the vane position is undefined; if the the shades - * are exactly open then the vanes are at zero; otherwise return the actual vane - * position itself - * - * note: sometimes the hub may return a value of position1 > MAX_VANE (seems to - * be a bug in the hub) so we avoid an out of range exception via the Math.min() - * function below.. - */ - if (posKindCoords.equals(posKind1)) { - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - return new PercentType((int) Math.round((double) Math.min(position1, max) / max * 100)); - } - if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { - return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO; - } - if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped() - && shadeCapabilities.supportsTiltOnClosed()) { - return PercentType.HUNDRED; - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, return UNDEF - } - return UnDefType.UNDEF; - } - - /** - * Set the shade's position2 value for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - */ - private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - posKind2 = posKindCoords.ordinal(); - position2 = Integer.valueOf(MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE)); - break; - - case SECONDARY_POSITION: - /* - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { - // on dual rail shades constrain percent to not move the upper rail below the lower - State primary = getState(shadeCapabilities, PRIMARY_POSITION); - if (primary instanceof PercentType) { - int primaryPercent = ((PercentType) primary).intValue(); - if (percent > primaryPercent) { - percent = primaryPercent; - } - } - } - posKind2 = posKindCoords.ordinal(); - position2 = Integer.valueOf((int) Math.round((double) percent / 100 * MAX_SHADE)); - break; - - case VANE_TILT_POSITION: - posKind2 = posKindCoords.ordinal(); - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - position2 = Integer.valueOf((int) Math.round((double) percent / 100 * max)); - break; - - default: - posKind2 = null; - position2 = null; - } - } - - /** - * Get the shade's position2 State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the State (or UNDEF if not available). - */ - private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - Integer posKind2 = this.posKind2; - Integer position2 = this.position2; - - if (position2 == null || posKind2 == null) { - return UnDefType.UNDEF; - } - - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (posKindCoords.equals(posKind2)) { - return new PercentType(100 - (int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); - } - break; - - case SECONDARY_POSITION: - /* - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (posKindCoords.equals(posKind2)) { - return new PercentType((int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); - } - break; - - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - */ - case VANE_TILT_POSITION: - if (posKindCoords.equals(posKind2)) { - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - return new PercentType((int) Math.round((double) Math.min(position2.intValue(), max) / max * 100)); - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, return UNDEF - } - return UnDefType.UNDEF; - } + public abstract State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords); /** * Detect if the ShadePosition has a posKindN value indicating potential support for a secondary rail. * * @return true if the ShadePosition supports a secondary rail. */ - public boolean secondaryRailDetected() { - return SECONDARY_POSITION.equals(posKind1) || SECONDARY_POSITION.equals(posKind2); - } + public abstract boolean secondaryRailDetected(); /** * Detect if the ShadePosition has both a posKindN value indicating potential support for tilt, AND a posKindN @@ -308,10 +46,7 @@ public boolean secondaryRailDetected() { * * @return true if potential support for tilt anywhere functionality was detected. */ - public boolean tiltAnywhereDetected() { - return ((PRIMARY_POSITION.equals(posKind1)) && (VANE_TILT_POSITION.equals(posKind2)) - || ((PRIMARY_POSITION.equals(posKind2) && (VANE_TILT_POSITION.equals(posKind1))))); - } + public abstract boolean tiltAnywhereDetected(); /** * Set the shade's position for the given actuator class resp. coordinate system. @@ -321,61 +56,6 @@ public boolean tiltAnywhereDetected() { * @param percent the new position value. * @return this object. */ - public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - logger.trace("setPosition(): capabilities={}, coords={}, percent={}", shadeCapabilities, posKindCoords, - percent); - // if necessary swap the order of position1 and position2 - if (PRIMARY_POSITION.equals(posKind2) && !PRIMARY_POSITION.equals(posKind1)) { - final Integer posKind2Temp = posKind2; - final Integer position2Temp = position2; - posKind2 = Integer.valueOf(posKind1); - position2 = Integer.valueOf(position1); - posKind1 = posKind2Temp != null ? posKind2Temp.intValue() : NONE.ordinal(); - position1 = position2Temp != null ? position2Temp.intValue() : 0; - } - - // delete position2 if it has an invalid position kind - if (ERROR_UNKNOWN.equals(posKind2) || NONE.equals(posKind2)) { - posKind2 = null; - position2 = null; - } - - // logic to set either position1 or position2 - switch (posKindCoords) { - case PRIMARY_POSITION: - if (shadeCapabilities.supportsPrimary()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case SECONDARY_POSITION: - if (shadeCapabilities.supportsSecondary()) { - if (shadeCapabilities.supportsPrimary()) { - setPosition2(shadeCapabilities, posKindCoords, percent); - } else { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - } else if (shadeCapabilities.supportsSecondaryOverlapped()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case VANE_TILT_POSITION: - if (shadeCapabilities.supportsPrimary()) { - if (shadeCapabilities.supportsTiltOnClosed()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } else if (shadeCapabilities.supportsTiltAnywhere()) { - setPosition2(shadeCapabilities, posKindCoords, percent); - } - } else if (shadeCapabilities.supportsTiltAnywhere()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, do nothing - } - return this; - } + public abstract ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, + int percent); } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java new file mode 100644 index 0000000000000..ebe97bed29f71 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v1; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.BatteryKind; +import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; + +/** + * State of a Shade as returned by an HD PowerView hub of Generation 1 or 2. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ShadeDataV1 extends ShadeData { + // fields for Generation 1/2 hubs only; NOTE: all these are Nullable + public int groupId; + public int order; + public double batteryStrength; + public boolean batteryIsLow; + public @Nullable Boolean timedOut; + public @Nullable Firmware motor; + // note: in old JSON batteryKind was a string but now it's a number; fortunately GSON string accepts either + public @Nullable String batteryKind; + + @Override + public BatteryKind getBatteryKind() { + return BatteryKind.fromString(batteryKind); + } + + @Override + public int version() { + return 1; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java new file mode 100644 index 0000000000000..3a7371f9587a7 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java @@ -0,0 +1,387 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v1; + +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The position of a single shade, as returned by an HD PowerView hub of Generation 1/2 + * + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Added support for secondary rail positions + */ +@NonNullByDefault +public class ShadePositionV1 extends ShadePosition { + + private final transient Logger logger = LoggerFactory.getLogger(ShadePositionV1.class); + + /** + * Primary actuator position. + */ + private int posKind1; + private int position1; + + /** + * Secondary actuator position. + * + * Here we have to use Integer objects rather than just int primitives because these are secondary optional position + * elements in the JSON payload, so the GSON de-serializer might leave them as null. + */ + private @Nullable Integer posKind2 = null; + private @Nullable Integer position2 = null; + + public ShadePositionV1() { + } + + /** + * Get the shade's State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the current state. + */ + @Override + public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + State result = getPosition1(shadeCapabilities, posKindCoords); + if (result == UnDefType.UNDEF) { + result = getPosition2(shadeCapabilities, posKindCoords); + } + logger.trace("getState(): capabilities={}, coords={} => result={}", shadeCapabilities, posKindCoords, result); + return result; + } + + /** + * Set the shade's position1 value for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + */ + private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { + // on dual rail shades constrain percent to not move the lower rail above the upper + State secondary = getState(shadeCapabilities, SECONDARY_POSITION); + if (secondary instanceof PercentType) { + int secPercent = ((PercentType) secondary).intValue(); + if (percent < secPercent) { + percent = secPercent; + } + } + } + posKind1 = posKindCoords.ordinal(); + position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); + break; + + case SECONDARY_POSITION: + /* + * Secondary, blackout shade a 'Duolite' shade: => INVERTED + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + posKind1 = posKindCoords.ordinal(); + if (shadeCapabilities.supportsSecondaryOverlapped()) { + position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); + } else { + position1 = (int) Math.round((double) percent / 100 * MAX_SHADE); + } + break; + + case VANE_TILT_POSITION: + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + */ + posKind1 = posKindCoords.ordinal(); + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + position1 = (int) Math.round((double) percent / 100 * max); + break; + + default: + posKind1 = CoordinateSystem.NONE.ordinal(); + position1 = 0; + } + } + + /** + * Get the shade's position1 State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the State (or UNDEF if not available). + */ + private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (posKindCoords.equals(posKind1)) { + return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); + } + if (VANE_TILT_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { + return PercentType.HUNDRED; + } + if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { + return PercentType.HUNDRED; + } + break; + + case SECONDARY_POSITION: + /* + * Secondary, blackout shade a 'Duolite' shade: => INVERTED + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (posKindCoords.equals(posKind1)) { + if (shadeCapabilities.supportsSecondaryOverlapped()) { + return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); + } + return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100)); + } + if (!SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { + return PercentType.ZERO; + } + break; + + case VANE_TILT_POSITION: + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + * + * If the shades are not open, the vane position is undefined; if the the shades + * are exactly open then the vanes are at zero; otherwise return the actual vane + * position itself + * + * note: sometimes the hub may return a value of position1 > MAX_VANE (seems to + * be a bug in the hub) so we avoid an out of range exception via the Math.min() + * function below.. + */ + if (posKindCoords.equals(posKind1)) { + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + return new PercentType((int) Math.round((double) Math.min(position1, max) / max * 100)); + } + if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { + return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO; + } + if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped() + && shadeCapabilities.supportsTiltOnClosed()) { + return PercentType.HUNDRED; + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, return UNDEF + } + return UnDefType.UNDEF; + } + + /** + * Set the shade's position2 value for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + */ + private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + posKind2 = posKindCoords.ordinal(); + position2 = Integer.valueOf(MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE)); + break; + + case SECONDARY_POSITION: + /* + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { + // on dual rail shades constrain percent to not move the upper rail below the lower + State primary = getState(shadeCapabilities, PRIMARY_POSITION); + if (primary instanceof PercentType) { + int primaryPercent = ((PercentType) primary).intValue(); + if (percent > primaryPercent) { + percent = primaryPercent; + } + } + } + posKind2 = posKindCoords.ordinal(); + position2 = Integer.valueOf((int) Math.round((double) percent / 100 * MAX_SHADE)); + break; + + case VANE_TILT_POSITION: + posKind2 = posKindCoords.ordinal(); + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + position2 = Integer.valueOf((int) Math.round((double) percent / 100 * max)); + break; + + default: + posKind2 = null; + position2 = null; + } + } + + /** + * Get the shade's position2 State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the State (or UNDEF if not available). + */ + private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + Integer posKind2 = this.posKind2; + Integer position2 = this.position2; + + if (position2 == null || posKind2 == null) { + return UnDefType.UNDEF; + } + + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (posKindCoords.equals(posKind2)) { + return new PercentType(100 - (int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); + } + break; + + case SECONDARY_POSITION: + /* + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (posKindCoords.equals(posKind2)) { + return new PercentType((int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); + } + break; + + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + */ + case VANE_TILT_POSITION: + if (posKindCoords.equals(posKind2)) { + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + return new PercentType((int) Math.round((double) Math.min(position2.intValue(), max) / max * 100)); + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, return UNDEF + } + return UnDefType.UNDEF; + } + + /** + * Detect if the ShadePosition has a posKindN value indicating potential support for a secondary rail. + * + * @return true if the ShadePosition supports a secondary rail. + */ + @Override + public boolean secondaryRailDetected() { + return SECONDARY_POSITION.equals(posKind1) || SECONDARY_POSITION.equals(posKind2); + } + + /** + * Detect if the ShadePosition has both a posKindN value indicating potential support for tilt, AND a posKindN + * indicating support for a primary rail. i.e. it potentially supports tilt anywhere functionality. + * + * @return true if potential support for tilt anywhere functionality was detected. + */ + @Override + public boolean tiltAnywhereDetected() { + return ((PRIMARY_POSITION.equals(posKind1)) && (VANE_TILT_POSITION.equals(posKind2)) + || ((PRIMARY_POSITION.equals(posKind2) && (VANE_TILT_POSITION.equals(posKind1))))); + } + + /** + * Set the shade's position for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + * @return this object. + */ + @Override + public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + logger.trace("setPosition(): capabilities={}, coords={}, percent={}", shadeCapabilities, posKindCoords, + percent); + // if necessary swap the order of position1 and position2 + if (PRIMARY_POSITION.equals(posKind2) && !PRIMARY_POSITION.equals(posKind1)) { + final Integer posKind2Temp = posKind2; + final Integer position2Temp = position2; + posKind2 = Integer.valueOf(posKind1); + position2 = Integer.valueOf(position1); + posKind1 = posKind2Temp != null ? posKind2Temp.intValue() : NONE.ordinal(); + position1 = position2Temp != null ? position2Temp.intValue() : 0; + } + + // delete position2 if it has an invalid position kind + if (ERROR_UNKNOWN.equals(posKind2) || NONE.equals(posKind2)) { + posKind2 = null; + position2 = null; + } + + // logic to set either position1 or position2 + switch (posKindCoords) { + case PRIMARY_POSITION: + if (shadeCapabilities.supportsPrimary()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case SECONDARY_POSITION: + if (shadeCapabilities.supportsSecondary()) { + if (shadeCapabilities.supportsPrimary()) { + setPosition2(shadeCapabilities, posKindCoords, percent); + } else { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + } else if (shadeCapabilities.supportsSecondaryOverlapped()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case VANE_TILT_POSITION: + if (shadeCapabilities.supportsPrimary()) { + if (shadeCapabilities.supportsTiltOnClosed()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } else if (shadeCapabilities.supportsTiltAnywhere()) { + setPosition2(shadeCapabilities, posKindCoords, percent); + } + } else if (shadeCapabilities.supportsTiltAnywhere()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, do nothing + } + return this; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java new file mode 100644 index 0000000000000..cadd5f62c4a56 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.BatteryKind; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; + +/** + * State of a Shade as returned by an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ShadeDataV3 extends ShadeData { + public @Nullable String ptName; + public @Nullable String powerType; + public @Nullable String bleName; + // TODO: public @Nullable Motion motion; + + @Override + public String getName() { + return String.join(" ", super.getName(), ptName); + } + + @Override + public BatteryKind getBatteryKind() { + // TODO Auto-generated method stub + // NOTE: the schema for powerType is not clear; is may be a string? or an integer? + return BatteryKind.ERROR_UNKNOWN; + } + + @Override + public int version() { + return 3; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java new file mode 100644 index 0000000000000..516d00df121dc --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * The position of a single shade, as returned by an HD PowerView hub of Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ShadePositionV3 extends ShadePosition { + + private @Nullable Double primary; + private @Nullable Double secondary; + private @Nullable Double tilt; + // private @Nullable Double velocity; + + @Override + public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + Double value; + switch (posKindCoords) { + case PRIMARY_POSITION: + value = primary; + break; + case SECONDARY_POSITION: + value = secondary; + break; + case VANE_TILT_POSITION: + value = tilt; + break; + default: + value = null; + } + return value != null ? new PercentType((int) (value.doubleValue() * 100)) : UnDefType.UNDEF; + } + + @Override + public boolean secondaryRailDetected() { + return secondary != null; + } + + @Override + public boolean tiltAnywhereDetected() { + return tilt != null; + } + + @Override + public ShadePositionV3 setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + Double value = Double.valueOf(percent) / 100.0; + switch (posKindCoords) { + case PRIMARY_POSITION: + primary = shadeCapabilities.supportsPrimary() ? value : null; + break; + case SECONDARY_POSITION: + secondary = shadeCapabilities.supportsSecondary() || shadeCapabilities.supportsSecondaryOverlapped() + ? value + : null; + break; + case VANE_TILT_POSITION: + tilt = shadeCapabilities.supportsTiltOnClosed() || shadeCapabilities.supportsTiltAnywhere() ? value + : null; + break; + default: + } + return this; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java new file mode 100644 index 0000000000000..4191c5bd96b95 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Scene object as returned by an HD PowerView hub + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public abstract class Scene implements Comparable { + // fields that are common to Generation 1/2 and 3 hubs + public int id; + public @Nullable String name; + + @Override + public abstract int compareTo(Scene other); + + public String getName() { + return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8); + } + + public abstract int version(); +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java index 5cb25a8aab0b4..4fa5a5fe4264a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.hdpowerview.internal.api.responses; -import java.nio.charset.StandardCharsets; -import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -29,58 +27,4 @@ public class Scenes { public @Nullable List sceneData; public @Nullable List sceneIds; - - /* - * the following SuppressWarnings annotation is because the Eclipse compiler - * does NOT expect a NonNullByDefault annotation on the inner class, since it is - * implicitly inherited from the outer class, whereas the Maven compiler always - * requires an explicit NonNullByDefault annotation on all classes - */ - @SuppressWarnings("null") - @NonNullByDefault - public static class Scene implements Comparable { - public int id; - public @Nullable String name; - public int roomId; - public int order; - public int colorId; - public int iconId; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof Scene)) { - return false; - } - Scene other = (Scene) o; - - return this.id == other.id && this.name.equals(other.name) && this.roomId == other.roomId - && this.order == other.order && this.colorId == other.colorId && this.iconId == other.iconId; - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + (name == null ? 0 : name.hashCode()); - result = prime * result + roomId; - result = prime * result + order; - result = prime * result + colorId; - result = prime * result + iconId; - - return result; - } - - @Override - public int compareTo(Scene other) { - return Integer.compare(order, other.order); - } - - public String getName() { - return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8); - } - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java new file mode 100644 index 0000000000000..7033358730002 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses; + +import java.time.DayOfWeek; +import java.util.EnumSet; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Abstract class for scheduled event as returned by an HD PowerView hub. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public abstract class ScheduledEvent { + // fields common to Generation 1/2 and 3 hubs + public int id; + public int type; + public boolean enabled; + public int hour; + public int minute; + public int sceneId; + + public abstract EnumSet getDays(); + + public abstract int getEventType(); + + public abstract int version(); +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java index e7ae6ac1ae440..5a595cd206a25 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java @@ -38,95 +38,4 @@ public class ScheduledEvents { public @Nullable List scheduledEventData; public @Nullable List scheduledEventIds; - - /* - * the following SuppressWarnings annotation is because the Eclipse compiler - * does NOT expect a NonNullByDefault annotation on the inner class, since it is - * implicitly inherited from the outer class, whereas the Maven compiler always - * requires an explicit NonNullByDefault annotation on all classes - */ - @SuppressWarnings("null") - @NonNullByDefault - public static class ScheduledEvent { - public int id; - public boolean enabled; - public int sceneId; - public int sceneCollectionId; - public boolean daySunday; - public boolean dayMonday; - public boolean dayTuesday; - public boolean dayWednesday; - public boolean dayThursday; - public boolean dayFriday; - public boolean daySaturday; - public int eventType; - public int hour; - public int minute; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof ScheduledEvent)) { - return false; - } - ScheduledEvent other = (ScheduledEvent) o; - - return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId - && this.sceneCollectionId == other.sceneCollectionId && this.daySunday == other.daySunday - && this.dayMonday == other.dayMonday && this.dayTuesday == other.dayTuesday - && this.dayWednesday == other.dayWednesday && this.dayThursday == other.dayThursday - && this.dayFriday == other.dayFriday && this.daySaturday == other.daySaturday - && this.eventType == other.eventType && this.hour == other.hour && this.minute == other.minute; - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + (enabled ? 1 : 0); - result = prime * result + sceneId; - result = prime * result + sceneCollectionId; - result = prime * result + (daySunday ? 1 : 0); - result = prime * result + (dayMonday ? 1 : 0); - result = prime * result + (dayTuesday ? 1 : 0); - result = prime * result + (dayWednesday ? 1 : 0); - result = prime * result + (dayThursday ? 1 : 0); - result = prime * result + (dayFriday ? 1 : 0); - result = prime * result + (daySaturday ? 1 : 0); - result = prime * result + eventType; - result = prime * result + hour; - result = prime * result + minute; - - return result; - } - - public EnumSet getDays() { - EnumSet days = EnumSet.noneOf(DayOfWeek.class); - if (daySunday) { - days.add(DayOfWeek.SUNDAY); - } - if (dayMonday) { - days.add(DayOfWeek.MONDAY); - } - if (dayTuesday) { - days.add(DayOfWeek.TUESDAY); - } - if (dayWednesday) { - days.add(DayOfWeek.WEDNESDAY); - } - if (dayThursday) { - days.add(DayOfWeek.THURSDAY); - } - if (dayFriday) { - days.add(DayOfWeek.FRIDAY); - } - if (daySaturday) { - days.add(DayOfWeek.SATURDAY); - } - return days; - } - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java index 69da9378395b7..f73635e929c27 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java @@ -14,7 +14,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; /** * State of a single Shade, as returned by an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java index 2746523b47a97..72d820d1ee7db 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java @@ -12,14 +12,11 @@ */ package org.openhab.binding.hdpowerview.internal.api.responses; -import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; /** * State of all Shades, as returned by an HD PowerView hub @@ -31,40 +28,4 @@ public class Shades { public @Nullable List shadeData; public @Nullable List shadeIds; - - /* - * the following SuppressWarnings annotation is because the Eclipse compiler - * does NOT expect a NonNullByDefault annotation on the inner class, since it is - * implicitly inherited from the outer class, whereas the Maven compiler always - * requires an explicit NonNullByDefault annotation on all classes - */ - @SuppressWarnings("null") - @NonNullByDefault - public static class ShadeData { - public int id; - public @Nullable String name; - public int roomId; - public int groupId; - public int order; - public int type; - public double batteryStrength; - public int batteryStatus; - public boolean batteryIsLow; - public @Nullable ShadePosition positions; - public @Nullable Boolean timedOut; - public int signalStrength; - public @Nullable Integer capabilities; - public @Nullable Firmware firmware; - public @Nullable Firmware motor; - // note: in old JSON batteryKind was a string but now it's a number; fortunately GSON string accepts either - public @Nullable String batteryKind; - - public String getName() { - return new String(Base64.getDecoder().decode(name)); - } - - public BatteryKind getBatteryKind() { - return BatteryKind.fromString(batteryKind); - } - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java new file mode 100644 index 0000000000000..f4efb2805964a --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v1; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; + +/** + * Scene object as returned by an HD PowerView hub of Generation 3 + * + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Refactored into separate class + */ +@NonNullByDefault +public class SceneV1 extends Scene { + public int roomId; + public int order; + public int colorId; + public int iconId; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof SceneV1)) { + return false; + } + SceneV1 other = (SceneV1) o; + + return this.id == other.id && getName().equals(other.getName()) && this.roomId == other.roomId + && this.order == other.order && this.colorId == other.colorId && this.iconId == other.iconId; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + id; + result = prime * result + getName().hashCode(); + result = prime * result + roomId; + result = prime * result + order; + result = prime * result + colorId; + result = prime * result + iconId; + + return result; + } + + @Override + public int compareTo(Scene other) throws IllegalArgumentException { + if (other.version() == version()) { + return Integer.compare(order, ((SceneV1) other).order); + } + throw new IllegalArgumentException("Cannot compare scenes from different hub generations"); + } + + @Override + public int version() { + return 1; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java new file mode 100644 index 0000000000000..b7ef3c878288c --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v1; + +import java.time.DayOfWeek; +import java.util.EnumSet; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; + +/** + * class for scheduled event as returned by an HD PowerView Generation 1/2 hub. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ScheduledEventV1 extends ScheduledEvent { + // fields specific to Generation 1/2 + public int sceneCollectionId; + public boolean daySunday; + public boolean dayMonday; + public boolean dayTuesday; + public boolean dayWednesday; + public boolean dayThursday; + public boolean dayFriday; + public boolean daySaturday; + public int eventType; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ScheduledEventV1)) { + return false; + } + ScheduledEventV1 other = (ScheduledEventV1) o; + + return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId + && this.sceneCollectionId == other.sceneCollectionId && this.daySunday == other.daySunday + && this.dayMonday == other.dayMonday && this.dayTuesday == other.dayTuesday + && this.dayWednesday == other.dayWednesday && this.dayThursday == other.dayThursday + && this.dayFriday == other.dayFriday && this.daySaturday == other.daySaturday + && this.eventType == other.eventType && this.hour == other.hour && this.minute == other.minute; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + (enabled ? 1 : 0); + result = prime * result + sceneId; + result = prime * result + sceneCollectionId; + result = prime * result + (daySunday ? 1 : 0); + result = prime * result + (dayMonday ? 1 : 0); + result = prime * result + (dayTuesday ? 1 : 0); + result = prime * result + (dayWednesday ? 1 : 0); + result = prime * result + (dayThursday ? 1 : 0); + result = prime * result + (dayFriday ? 1 : 0); + result = prime * result + (daySaturday ? 1 : 0); + result = prime * result + eventType; + result = prime * result + hour; + result = prime * result + minute; + + return result; + } + + @Override + public EnumSet getDays() { + EnumSet days = EnumSet.noneOf(DayOfWeek.class); + if (daySunday) { + days.add(DayOfWeek.SUNDAY); + } + if (dayMonday) { + days.add(DayOfWeek.MONDAY); + } + if (dayTuesday) { + days.add(DayOfWeek.TUESDAY); + } + if (dayWednesday) { + days.add(DayOfWeek.WEDNESDAY); + } + if (dayThursday) { + days.add(DayOfWeek.THURSDAY); + } + if (dayFriday) { + days.add(DayOfWeek.FRIDAY); + } + if (daySaturday) { + days.add(DayOfWeek.SATURDAY); + } + return days; + } + + @Override + public int getEventType() { + return eventType; + } + + @Override + public int version() { + return 1; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java new file mode 100644 index 0000000000000..9302a29782404 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.api.HubFirmware; + +/** + * DTO for the Generation 3 gateway information. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class InfoV3 { + public @Nullable String fwVersion; + public @Nullable String serialNumber; + + public HubFirmware toHubFirmware() { + Firmware firmware = new Firmware(); + String fwVersion = this.fwVersion; + if (fwVersion != null) { + String[] parts = fwVersion.split("\\."); + if (parts.length > 0) { + try { + firmware.revision = Integer.valueOf(parts[0]); + } catch (NumberFormatException e) { + } + } + if (parts.length > 1) { + try { + firmware.subRevision = Integer.valueOf(parts[1]); + } catch (NumberFormatException e) { + } + } + if (parts.length > 2) { + try { + firmware.build = Integer.valueOf(parts[2]); + } catch (NumberFormatException e) { + } + } + } + + String name = "Generation 3 Hub"; + if (serialNumber != null) { + name = String.format("%s (serial: %s)", name, serialNumber); + } + firmware.name = name; + + HubFirmware result = new HubFirmware(); + result.mainProcessor = firmware; + return result; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java new file mode 100644 index 0000000000000..5756f1a9a7e52 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; + +/** + * Scene object as returned by an HD PowerView hub of Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class SceneV3 extends Scene { + public @Nullable String ptName; + public @Nullable String color; + public @Nullable String icon; + public @Nullable List roomIds; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof SceneV3)) { + return false; + } + SceneV3 other = (SceneV3) o; + String color = this.color; + String icon = this.icon; + + // TODO check roomIds as well ?? + return this.id == other.id && getName().equals(other.getName()) && (color != null && color.equals(other.color)) + && (icon != null && icon.equals(other.icon)); + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + + String color = this.color; + String icon = this.icon; + + // TODO hash roomIds as well ?? + result = prime * result + id; + result = prime * result + getName().hashCode(); + result = prime * result + (color == null ? 0 : color.hashCode()); + result = prime * result + (icon == null ? 0 : icon.hashCode()); + + return result; + } + + @Override + public String getName() { + return String.join(" ", super.getName(), ptName); + } + + @Override + public int compareTo(Scene other) throws IllegalArgumentException { + if (other.version() == version()) { + // TODO fix this code.. + return this.equals(other) ? 0 : Integer.MAX_VALUE; + } + throw new IllegalArgumentException("Cannot compare scenes from different hub generations"); + } + + @Override + public int version() { + return 3; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java new file mode 100644 index 0000000000000..ea1504a262a8b --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import java.time.DayOfWeek; +import java.util.EnumSet; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; + +/** + * class for scheduled event as returned by an HD PowerView Generation 3 hub. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ScheduledEventV3 extends ScheduledEvent { + // fields specific to Generation 3 + public @Nullable String days; + + private static final int MON = 0x01; + private static final int TUE = 0x02; + private static final int WED = 0x04; + private static final int THU = 0x08; + private static final int FRI = 0x10; + private static final int SAT = 0x20; + private static final int SUN = 0x40; + + private static final int CLOCK_BASED = 0; + private static final int BEFORE_SUNRISE = 2; + private static final int BEFORE_SUNSET = 6; + private static final int AFTER_SUNRISE = 10; + private static final int AFTER_SUNSET = 14; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ScheduledEventV3)) { + return false; + } + ScheduledEventV3 other = (ScheduledEventV3) o; + String days = this.days; + + return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId + && (days != null && days.equals(other.days)) && this.hour == other.hour && this.minute == other.minute; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + String days = this.days; + result = prime * result + id; + result = prime * result + (enabled ? 1 : 0); + result = prime * result + sceneId; + result = prime * result + (days != null ? days.hashCode() : 0); + result = prime * result + hour; + result = prime * result + minute; + return result; + } + + @Override + public EnumSet getDays() { + EnumSet daySet = EnumSet.noneOf(DayOfWeek.class); + String days = this.days; + if (days != null) { + try { + int daysInt = Integer.valueOf(days).intValue(); + if ((daysInt & MON) != 0) { + daySet.add(DayOfWeek.MONDAY); + } + if ((daysInt & TUE) != 0) { + daySet.add(DayOfWeek.TUESDAY); + } + if ((daysInt & WED) != 0) { + daySet.add(DayOfWeek.WEDNESDAY); + } + if ((daysInt & THU) != 0) { + daySet.add(DayOfWeek.THURSDAY); + } + if ((daysInt & FRI) != 0) { + daySet.add(DayOfWeek.FRIDAY); + } + if ((daysInt & SAT) != 0) { + daySet.add(DayOfWeek.SATURDAY); + } + if ((daysInt & SUN) != 0) { + daySet.add(DayOfWeek.SUNDAY); + } + } catch (NumberFormatException e) { + // fall through + } + } + return daySet; + } + + @Override + public int getEventType() { + switch (type) { + case CLOCK_BASED: + return ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME; + + case BEFORE_SUNRISE: + case AFTER_SUNRISE: + // TODO handle before and after sunrise cases separately + return ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE; + + case BEFORE_SUNSET: + case AFTER_SUNSET: + // TODO handle before and after sunset cases separately + return ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET; + } + return 0; + } + + @Override + public int version() { + return 3; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java new file mode 100644 index 0000000000000..492f499a17257 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Scene SSE event object as supplied an HD PowerView hub of Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class SseSceneV3 { + public @Nullable String evt; + public @Nullable String isoDate; + public int id; + public @Nullable SceneV3 scene; +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java new file mode 100644 index 0000000000000..f9454ff4ed0f5 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; + +/** + * Shade SSE event object as supplied an HD PowerView hub of Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class SseShadeV3 { + public @Nullable String evt; + public @Nullable String isoDate; + public int id; + public @Nullable ShadePositionV3 currentPositions; +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java index 10782b22b991c..00a07ea4de4fa 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java @@ -25,10 +25,11 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; @@ -63,7 +64,7 @@ private AutomationChannelBuilder(HDPowerViewTranslationProvider translationProvi /** * Creates an {@link AutomationChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and * {@link ChannelGroupUID}. - * + * * @param translationProvider the {@link HDPowerViewTranslationProvider} * @param channelGroupUid parent {@link ChannelGroupUID} for created channels * @return channel builder @@ -75,7 +76,7 @@ public static AutomationChannelBuilder create(HDPowerViewTranslationProvider tra /** * Adds created channels to existing list. - * + * * @param channels list that channels will be added to * @return channel builder */ @@ -86,7 +87,7 @@ public AutomationChannelBuilder withChannels(List channels) { /** * Sets the scenes. - * + * * @param scenes the scenes * @return channel builder */ @@ -97,7 +98,7 @@ public AutomationChannelBuilder withScenes(List scenes) { /** * Sets the scene collections. - * + * * @param sceneCollections the scene collections * @return channel builder */ @@ -109,7 +110,7 @@ public AutomationChannelBuilder withSceneCollections(List scene /** * Sets the scheduled events. - * + * * @param scheduledEvents the sceduled events * @return channel builder */ @@ -168,20 +169,21 @@ public List build() { } logger.warn("Scene '{}' was not found for scheduled event '{}'", scheduledEvent.sceneId, scheduledEvent.id); return null; - } else if (scheduledEvent.sceneCollectionId > 0) { + } else if (scheduledEvent.version() == 1 && ((ScheduledEventV1) scheduledEvent).sceneCollectionId > 0) { Map sceneCollections = this.sceneCollections; + ScheduledEventV1 scheduledEventV1 = (ScheduledEventV1) scheduledEvent; if (sceneCollections == null) { logger.warn( "Scheduled event '{}' references scene collection '{}', but no scene collections are loaded", - scheduledEvent.id, scheduledEvent.sceneCollectionId); + scheduledEvent.id, scheduledEventV1.sceneCollectionId); return null; } - SceneCollection sceneCollection = sceneCollections.get(scheduledEvent.sceneCollectionId); + SceneCollection sceneCollection = sceneCollections.get(scheduledEventV1.sceneCollectionId); if (sceneCollection != null) { return sceneCollection.getName(); } logger.warn("Scene collection '{}' was not found for scheduled event '{}'", - scheduledEvent.sceneCollectionId, scheduledEvent.id); + scheduledEventV1.sceneCollectionId, scheduledEvent.id); return null; } else { logger.warn("Scheduled event '{}'' not related to any scene or scene collection", scheduledEvent.id); @@ -191,8 +193,7 @@ public List build() { private String getScheduledEventName(String sceneName, ScheduledEvent scheduledEvent) { String timeString, daysString; - - switch (scheduledEvent.eventType) { + switch (scheduledEvent.getEventType()) { case ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME: timeString = LocalTime.of(scheduledEvent.hour, scheduledEvent.minute).toString(); break; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java index 01c05137e9ece..8d9624620223e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; @@ -45,7 +45,7 @@ private SceneChannelBuilder(HDPowerViewTranslationProvider translationProvider, /** * Creates a {@link SceneChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and * {@link ChannelGroupUID}. - * + * * @param translationProvider the {@link HDPowerViewTranslationProvider} * @param channelGroupUid parent {@link ChannelGroupUID} for created channels * @return channel builder @@ -57,7 +57,7 @@ public static SceneChannelBuilder create(HDPowerViewTranslationProvider translat /** * Adds created channels to existing list. - * + * * @param channels list that channels will be added to * @return channel builder */ @@ -68,7 +68,7 @@ public SceneChannelBuilder withChannels(List channels) { /** * Sets the scenes. - * + * * @param scenes the scenes * @return channel builder */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java index 98d108befcfb1..37d9b6f952a48 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java @@ -21,9 +21,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index b3991f3aad3bd..ee4e935f27942 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; @@ -30,17 +31,23 @@ import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; +import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; +import org.openhab.binding.hdpowerview.internal._v3.SseSinkV3; import org.openhab.binding.hdpowerview.internal.api.Firmware; import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder; @@ -79,7 +86,7 @@ * @author Jacob Laursen - Added support for scene groups and automations */ @NonNullByDefault -public class HDPowerViewHubHandler extends BaseBridgeHandler { +public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV3 { private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class); private final HttpClient httpClient; @@ -163,8 +170,14 @@ public void initialize() { return; } + try { + webTargets = newWebTargets(host); + } catch (InstantiationException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + return; + } + pendingShadeInitializations.clear(); - webTargets = new HDPowerViewWebTargets(httpClient, host); refreshInterval = config.refresh; hardRefreshPositionInterval = config.hardRefresh; hardRefreshBatteryLevelInterval = config.hardRefreshBatteryLevel; @@ -227,7 +240,12 @@ public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { } private void schedulePoll() { - scheduleSoftPoll(); + if (sseSubscribe(this)) { + scheduler.submit(this::poll); // do a single poll to fetch the initial state + } else { + scheduleSoftPoll(); + } + // do hard polls (even on generation 3) in case SSE subscriptions have dropped scheduleHardPoll(); } @@ -280,6 +298,8 @@ private synchronized void stopPoll() { future.cancel(true); } this.hardRefreshBatteryLevelFuture = null; + + sseSubscribe(null); } private synchronized void poll() { @@ -422,7 +442,7 @@ private void updateUnknownShadeThing(Thing thing) { HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler()); if (thingHandler == null) { logger.debug("Shade '{}' handler not initialized", shadeId); - pendingShadeInitializations.put(thing.getUID(), new ShadeData()); + pendingShadeInitializations.put(thing.getUID(), newShadeData()); return; } ThingStatus thingStatus = thingHandler.getThing().getStatus(); @@ -436,7 +456,7 @@ private void updateUnknownShadeThing(Thing thing) { case UNINITIALIZED: case INITIALIZING: logger.debug("Shade '{}' handler not yet ready; status: {}", shadeId, thingStatus); - pendingShadeInitializations.put(thing.getUID(), new ShadeData()); + pendingShadeInitializations.put(thing.getUID(), newShadeData()); break; case REMOVING: case REMOVED: @@ -655,6 +675,9 @@ private void requestRefreshShadePositions() { logger.debug("Shade '{}' handler not initialized", shadeId); } } + + // re-subscribe (in case SSE connections went down) + sseSubscribe(this); } private void requestRefreshShadeBatteryLevels() { @@ -675,4 +698,90 @@ private void requestRefreshShadeBatteryLevels() { } } } + + /** + * Instantiate the web targets. + * + * @param host the ip address + * @return instance of HDPowerViewWebTargets class (either V1 or V3). + * @throws InstantiationException if neither a V1 nor a V3 web target was instantiated. + */ + private HDPowerViewWebTargets newWebTargets(String host) throws InstantiationException { + HDPowerViewWebTargets webTargets = this.webTargets; + if (webTargets != null) { + return webTargets; + } + try { + // try communicating via V1 web targets + webTargets = new HDPowerViewWebTargetsV1(httpClient, host); + webTargets.getFirmwareVersions(); + this.webTargets = webTargets; + return webTargets; + } catch (HubProcessingException | HubMaintenanceException e) { + // fall through + } + try { + // try communicating via V3 web targets + webTargets = new HDPowerViewWebTargetsV3(httpClient, host); + webTargets.getFirmwareVersions(); + this.webTargets = webTargets; + return webTargets; + } catch (HubProcessingException | HubMaintenanceException e) { + // fall through + } + throw new InstantiationException("Unable to instantiate the web targets"); + } + + /** + * Check if gateway is generation 1 + * + * @return true if gateway is generation 1 + */ + private boolean isGeneration1() { + return webTargets instanceof HDPowerViewWebTargetsV1; + } + + /** + * Create a new ShadeData instance; either V1 or V3 depending on the gateway generation. + * + * @return new ShadeData instance. + */ + private ShadeData newShadeData() { + return isGeneration1() ? new ShadeDataV1() : new ShadeDataV3(); + } + + /** + * If the gateway is generation 3 try to (un)subscribe to SSE on it. If 'sseSinK' is not null, make the + * subscription, otherwise cancel it. + * + * @param sseSinK the sink for the SSE call backs (may be null). + * @return true if the subscription succeeded. + */ + private boolean sseSubscribe(@Nullable SseSinkV3 sseSinK) { + if (webTargets instanceof HDPowerViewWebTargetsV3) { + try { + return ((HDPowerViewWebTargetsV3) webTargets).sseSubscribe(sseSinK); + } catch (HubMaintenanceException | HubProcessingException e) { + logger.warn("Failed to {}subscribe for SSE '{}'", sseSinK == null ? "un-" : "", e.getMessage()); + } + } + return false; + } + + @Override + public void sseShade(String evt, int shadeId, ShadePosition shadePosition) { + Optional thing = getShadeThingIdMap().entrySet().stream() + .filter(e -> e.getValue().equals(Integer.valueOf(shadeId))).findFirst().map(Map.Entry::getKey); + if (thing.isPresent()) { + ThingHandler handler = thing.get().getHandler(); + if (handler instanceof HDPowerViewShadeHandler) { + ((HDPowerViewShadeHandler) handler).sseShadePosition(shadePosition); + } + } + } + + @Override + public void sseScene(String evt, int sceneId) { + // TODO (if anything) + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java index 53e77e0425cdb..51be5b55cc09c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java @@ -32,9 +32,12 @@ import org.openhab.binding.hdpowerview.internal.api.BatteryKind; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; @@ -95,6 +98,8 @@ private enum RefreshKind { private int shadeId; private boolean isDisposing; + private boolean isGeneration1 = true; + public HDPowerViewShadeHandler(Thing thing) { super(thing); } @@ -250,6 +255,7 @@ private void handleShadeCommand(String channelId, Command command, HDPowerViewWe * @param shadeData the ShadeData to be used. */ protected void onReceiveUpdate(ShadeData shadeData) { + isGeneration1 = shadeData.version() == 1; updateStatus(ThingStatus.ONLINE); updateCapabilities(shadeData); updateSoftProperties(shadeData); @@ -258,7 +264,7 @@ protected void onReceiveUpdate(ShadeData shadeData) { if (shadePosition != null) { updatePositionStates(shadePosition); } - updateBatteryStates(shadeData.batteryStatus, shadeData.batteryStrength); + updateBatteryStates(shadeData); updateSignalStrengthState(shadeData.signalStrength); } @@ -334,7 +340,7 @@ private void updateSoftProperties(ShadeData shadeData) { private void updateFirmwareProperties(ShadeData shadeData) { Map properties = editProperties(); Firmware shadeFirmware = shadeData.firmware; - Firmware motorFirmware = shadeData.motor; + Firmware motorFirmware = (shadeData.version() == 1) ? ((ShadeDataV1) shadeData).motor : null; if (shadeFirmware != null) { properties.put(Thing.PROPERTY_FIRMWARE_VERSION, shadeFirmware.toString()); } @@ -399,8 +405,9 @@ private void updatePositionStates(ShadePosition shadePos) { updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePos.getState(capabilities, SECONDARY_POSITION)); } - private void updateBatteryStates(int batteryStatus, double batteryStrength) { - updateBatteryLevelStates(batteryStatus); + private void updateBatteryStates(ShadeData shadeData) { + updateBatteryLevelStates(shadeData.batteryStatus); + double batteryStrength = shadeData.version() == 1 ? ((ShadeDataV1) shadeData).batteryStrength : 0; updateState(CHANNEL_SHADE_BATTERY_VOLTAGE, batteryStrength > 0 ? new QuantityType<>(batteryStrength / 10, Units.VOLT) : UnDefType.UNDEF); } @@ -441,7 +448,7 @@ private void moveShade(CoordinateSystem coordSys, int newPercent, HDPowerViewWeb newPosition = shadeData.positions; // if no positions returned, then create a new position if (newPosition == null) { - newPosition = new ShadePosition(); + newPosition = newShadePosition(); } Capabilities capabilities = getCapabilitiesOrDefault(); // set the new position value, and write the positions to the hub @@ -570,7 +577,7 @@ private void doRefreshShade(RefreshKind kind) { break; case BATTERY_LEVEL: shadeData = webTargets.refreshShadeBatteryLevel(shadeId); - updateBatteryStates(shadeData.batteryStatus, shadeData.batteryStrength); + updateBatteryStates(shadeData); break; default: throw new NotSupportedException("Unsupported refresh kind " + kind.toString()); @@ -648,4 +655,24 @@ private void updateDynamicChannels(Capabilities capabilities, ShadeData shade) { updateThing(editThing().withoutChannels(removeList).build()); } } + + private ShadePosition newShadePosition() { + return isGeneration1 ? new ShadePositionV1() : new ShadePositionV3(); + } + + /** + * Update position states with the new position provided by an SSE event. + * + * @param shadePosition the new position + */ + public void sseShadePosition(ShadePosition shadePosition) { + if (thing.getStatus() == ThingStatus.ONLINE) { + Capabilities capabilities = this.capabilities; + if (capabilities != null) { + updateState(CHANNEL_SHADE_POSITION, shadePosition.getState(capabilities, PRIMARY_POSITION)); + updateState(CHANNEL_SHADE_VANE, shadePosition.getState(capabilities, VANE_TILT_POSITION)); + updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePosition.getState(capabilities, SECONDARY_POSITION)); + } + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java index ba6cb99574fc4..8c92ec0fd366c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java @@ -24,10 +24,12 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; @@ -65,7 +67,7 @@ private void setUp() { logger.setLevel(Level.OFF); builder = AutomationChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID); - Scene scene = new Scene(); + Scene scene = new SceneV1(); scene.id = 1; scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes()); scenes = new ArrayList<>(List.of(scene)); @@ -78,7 +80,7 @@ private void setUp() { @Test public void sceneSunriseWeekends() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.daySaturday = true; scheduledEvent.daySunday = true; @@ -91,7 +93,7 @@ public void sceneSunriseWeekends() { @Test public void sceneSunsetWeekdays() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.dayWednesday = true; @@ -107,7 +109,7 @@ public void sceneSunsetWeekdays() { @Test public void sceneTimeAllDays() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.dayWednesday = true; @@ -127,7 +129,7 @@ public void sceneTimeAllDays() { @Test public void sceneMinutesBeforeSunriseMondayTuesday() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.minute = -15; @@ -141,7 +143,7 @@ public void sceneMinutesBeforeSunriseMondayTuesday() { @Test public void sceneHoursMinutesAfterSunriseMonday() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.dayMonday = true; scheduledEvent.minute = 61; @@ -154,7 +156,7 @@ public void sceneHoursMinutesAfterSunriseMonday() { @Test public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayWednesday = true; scheduledEvent.dayThursday = true; scheduledEvent.dayFriday = true; @@ -169,7 +171,7 @@ public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() { @Test public void sceneHourAfterSunsetFridaySaturdaySunday() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayFriday = true; scheduledEvent.daySaturday = true; scheduledEvent.daySunday = true; @@ -224,7 +226,7 @@ public void emptyListWhenNoScenesOrSceneCollections() { @Test public void emptyListWhenNoSceneForScheduledEvent() { - ScheduledEvent scheduledEvent = createScheduledEventWithSceneCollection( + ScheduledEventV1 scheduledEvent = createScheduledEventWithSceneCollection( ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build(); @@ -234,7 +236,7 @@ public void emptyListWhenNoSceneForScheduledEvent() { @Test public void emptyListWhenNoSceneCollectionForScheduledEvent() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents) @@ -245,7 +247,7 @@ public void emptyListWhenNoSceneCollectionForScheduledEvent() { @Test public void groupAndIdAreCorrect() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.id = 42; List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build(); @@ -255,16 +257,16 @@ public void groupAndIdAreCorrect() { assertEquals(Integer.toString(scheduledEvent.id), channels.get(0).getUID().getIdWithoutGroup()); } - private ScheduledEvent createScheduledEventWithScene(int eventType) { - ScheduledEvent scheduledEvent = new ScheduledEvent(); + private ScheduledEventV1 createScheduledEventWithScene(int eventType) { + ScheduledEventV1 scheduledEvent = new ScheduledEventV1(); scheduledEvent.id = 1; scheduledEvent.sceneId = scenes.get(0).id; scheduledEvent.eventType = eventType; return scheduledEvent; } - private ScheduledEvent createScheduledEventWithSceneCollection(int eventType) { - ScheduledEvent scheduledEvent = new ScheduledEvent(); + private ScheduledEventV1 createScheduledEventWithSceneCollection(int eventType) { + ScheduledEventV1 scheduledEvent = new ScheduledEventV1(); scheduledEvent.id = 1; scheduledEvent.sceneCollectionId = sceneCollections.get(0).id; scheduledEvent.eventType = eventType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java new file mode 100644 index 0000000000000..f32f5ca731ecb --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.junit.jupiter.api.Test; +import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.UnDefType; + +/** + * Unit tests for Generation 3 DTO's. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class Generation3DtoTest { + + private final HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV3(new HttpClient(), ""); + private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); + + private String loadJson(String filename) throws IOException { + try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { + if (inputStream == null) { + throw new IOException("inputstream is null"); + } + byte[] bytes = inputStream.readAllBytes(); + if (bytes == null) { + throw new IOException("Resulting byte-array empty"); + } + return new String(bytes, StandardCharsets.UTF_8); + } + } + + /** + * Test JSON scenes response. + */ + @Test + public void testScenesParsing() throws IOException { + try { + Method method = webTargets.getClass().getDeclaredMethod("toScenes", String.class); + method.setAccessible(true); + String json = loadJson("_v3/scenes.json"); + Object result = method.invoke(webTargets, json); + assertTrue(result instanceof Scenes); + List sceneData = ((Scenes) result).sceneData; + assertNotNull(sceneData); + assertEquals(1, sceneData.size()); + Scene scene = sceneData.get(0); + assertEquals("Open All Office Shades\n ABC", scene.getName()); + assertEquals(234, scene.id); + assertEquals(3, scene.version()); + } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { + fail(e.getMessage()); + } + } + + /** + * Test JSON shades response. + */ + @Test + public void testShadesParsing() throws IOException { + try { + Method method = webTargets.getClass().getDeclaredMethod("toShades", String.class); + method.setAccessible(true); + String json = loadJson("_v3/shades.json"); + Object result = method.invoke(webTargets, json); + assertTrue(result instanceof Shades); + List shadeDataList = ((Shades) result).shadeData; + assertNotNull(shadeDataList); + assertEquals(1, shadeDataList.size()); + ShadeData shadeData = shadeDataList.get(0); + assertEquals("Shade 2 ABC", shadeData.getName()); + assertEquals(789, shadeData.id); + assertEquals(3, shadeData.version()); + } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { + fail(e.getMessage()); + } + } + + /** + * Test JSON automation response. + */ + @Test + public void testAutomationParsing() throws IOException { + try { + Method method = webTargets.getClass().getDeclaredMethod("toScheduledEvents", String.class); + method.setAccessible(true); + String json = loadJson("_v3/automations.json"); + Object result = method.invoke(webTargets, json); + assertTrue(result instanceof ScheduledEvents); + List scheduledEventList = ((ScheduledEvents) result).scheduledEventData; + assertNotNull(scheduledEventList); + assertEquals(1, scheduledEventList.size()); + ScheduledEvent scheduledEvent = scheduledEventList.get(0); + assertEquals(33, scheduledEvent.id); + assertTrue(scheduledEvent.enabled); + } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { + fail(e.getMessage()); + } + } + + /** + * Test JSON shade event response. + */ + @Test + public void testShadeEventParsing() throws IOException { + try { + Method method = webTargets.getClass().getDeclaredMethod("toShadeData2", String.class); + method.setAccessible(true); + String json = loadJson("_v3/shade-event.json"); + Object result = method.invoke(webTargets, json); + assertTrue(result instanceof ShadeData); + ShadeData shadeData = ((ShadeData) result); + assertEquals(3, shadeData.version()); + ShadePosition position = shadeData.positions; + assertNotNull(position); + assertEquals(PercentType.valueOf("99"), + position.getState(DB.getCapabilities(0), CoordinateSystem.PRIMARY_POSITION)); + assertEquals(PercentType.valueOf("98"), + position.getState(DB.getCapabilities(0), CoordinateSystem.SECONDARY_POSITION)); + assertEquals(PercentType.ZERO, + position.getState(DB.getCapabilities(0), CoordinateSystem.VANE_TILT_POSITION)); + } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { + fail(e.getMessage()); + } + } + + /** + * Test JSON shade position setting. + */ + @Test + public void testShadePositions() { + ShadePositionV3 pos; + Capabilities caps; + + caps = DB.getCapabilities(0); // test with only primary support + pos = new ShadePositionV3(); + pos.setPosition(caps, CoordinateSystem.PRIMARY_POSITION, 11); + pos.setPosition(caps, CoordinateSystem.SECONDARY_POSITION, 22); + pos.setPosition(caps, CoordinateSystem.VANE_TILT_POSITION, 33); + assertEquals(PercentType.valueOf("11"), pos.getState(caps, CoordinateSystem.PRIMARY_POSITION)); + assertEquals(UnDefType.UNDEF, pos.getState(caps, CoordinateSystem.SECONDARY_POSITION)); + assertEquals(UnDefType.UNDEF, pos.getState(caps, CoordinateSystem.VANE_TILT_POSITION)); + + caps = DB.getCapabilities(9);// test with primary, secondary, and tilt support + pos = new ShadePositionV3(); + pos.setPosition(caps, CoordinateSystem.PRIMARY_POSITION, 11); + pos.setPosition(caps, CoordinateSystem.SECONDARY_POSITION, 22); + pos.setPosition(caps, CoordinateSystem.VANE_TILT_POSITION, 33); + assertEquals(PercentType.valueOf("11"), pos.getState(caps, CoordinateSystem.PRIMARY_POSITION)); + assertEquals(PercentType.valueOf("22"), pos.getState(caps, CoordinateSystem.SECONDARY_POSITION)); + assertEquals(PercentType.valueOf("33"), pos.getState(caps, CoordinateSystem.VANE_TILT_POSITION)); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java index 19cd35c7c33ee..737c932c46813 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java @@ -22,15 +22,17 @@ import java.util.Objects; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; +import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; import org.openhab.binding.hdpowerview.internal.api.BatteryKind; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; @@ -48,7 +50,7 @@ @NonNullByDefault public class HDPowerViewJUnitTests { - private Gson gson = new Gson(); + private final Gson gson = new HDPowerViewWebTargetsV1(new HttpClient(), "").getGson(); private T getObjectFromJson(String filename, Class clazz) throws IOException { try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { @@ -69,7 +71,7 @@ private T getObjectFromJson(String filename, Class clazz) throws IOExcept */ @Test public void shadeNameIsDecoded() throws IOException { - Shades shades = getObjectFromJson("shades.json", Shades.class); + Shades shades = getObjectFromJson("_v1/shades.json", Shades.class); List shadeData = shades.shadeData; assertNotNull(shadeData); assertEquals(3, shadeData.size()); @@ -82,7 +84,7 @@ public void shadeNameIsDecoded() throws IOException { */ @Test public void testBatteryKind() throws IOException { - Shades shades = getObjectFromJson("shades.json", Shades.class); + Shades shades = getObjectFromJson("_v1/shades.json", Shades.class); List shadeData = shades.shadeData; assertNotNull(shadeData); ShadeData shade = shadeData.get(0); @@ -96,7 +98,7 @@ public void testBatteryKind() throws IOException { */ @Test public void sceneNameIsDecoded() throws IOException { - Scenes scenes = getObjectFromJson("scenes.json", Scenes.class); + Scenes scenes = getObjectFromJson("_v1/scenes.json", Scenes.class); List sceneData = scenes.sceneData; assertNotNull(sceneData); assertEquals(4, sceneData.size()); @@ -109,7 +111,7 @@ public void sceneNameIsDecoded() throws IOException { */ @Test public void sceneCollectionNameIsDecoded() throws IOException { - SceneCollections sceneCollections = getObjectFromJson("sceneCollections.json", SceneCollections.class); + SceneCollections sceneCollections = getObjectFromJson("_v1/sceneCollections.json", SceneCollections.class); List sceneCollectionData = sceneCollections.sceneCollectionData; assertNotNull(sceneCollectionData); @@ -124,7 +126,7 @@ public void sceneCollectionNameIsDecoded() throws IOException { */ @Test public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws IOException { - Shades shades = getObjectFromJson("duette.json", Shades.class); + Shades shades = getObjectFromJson("_v1/duette.json", Shades.class); List shadesData = shades.shadeData; assertNotNull(shadesData); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java index b8670bb91ec92..cdb3660f01ee1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java @@ -13,7 +13,7 @@ package org.openhab.binding.hdpowerview; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.PRIMARY_POSITION; import java.util.List; import java.util.regex.Pattern; @@ -22,11 +22,13 @@ import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; @@ -83,7 +85,7 @@ public void testOnlineCommunication() { fail(e.getMessage()); } - HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress); + HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV1(client, hubIPAddress); assertNotNull(webTargets); int shadeId = 0; @@ -168,7 +170,7 @@ public void testOnlineCommunication() { int position = ((PercentType) pos).intValue(); position = position + ((position <= 10) ? 5 : -5); - ShadePosition targetPosition = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, + ShadePosition targetPosition = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, position); assertNotNull(targetPosition); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java index eca2a1f2e9ebb..2b96fa966540e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java @@ -24,7 +24,8 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; @@ -110,7 +111,7 @@ public void emptyListWhenNoScenes() { } private List createScenes() { - Scene scene = new Scene(); + Scene scene = new SceneV1(); scene.id = 1; scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes()); return new ArrayList<>(List.of(scene)); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java index 50dcd0e7ff4c2..55688bccf1c65 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java @@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; @@ -90,7 +91,7 @@ private void assertShadePosition(State position, int value) { @Test public void testCaps1ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -104,7 +105,7 @@ public void testCaps1ShadePositionParsingFullyUp() { @Test public void testCaps1ShadePositionParsingShadeFullyDown1() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -118,7 +119,7 @@ public void testCaps1ShadePositionParsingShadeFullyDown1() { @Test public void testCaps1ShadePositionParsingShadeFullyDown2() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 0); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -132,7 +133,7 @@ public void testCaps1ShadePositionParsingShadeFullyDown2() { @Test public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 88); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 88); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -147,7 +148,7 @@ public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() { @Test public void testDualRailConstraints() { Capabilities capabilities = db.getCapabilities(7); - ShadePosition test = new ShadePosition(); + ShadePosition test = new ShadePositionV1(); // ==== OK !! primary at bottom, secondary at top ==== test.setPosition(capabilities, PRIMARY_POSITION, 100).setPosition(capabilities, SECONDARY_POSITION, 0); @@ -207,42 +208,42 @@ public void testDuoliteShadePositionParsing() { ShadePosition test; // both shades up - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 50% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 50); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 50); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 50); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 0% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 0% down (ALTERNATE) - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 50% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 50); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 50); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 50); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 100% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 100); @@ -260,70 +261,70 @@ public void testDuoliteTiltShadePositionParsing() { ShadePosition test; // front shade up - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 30% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 30); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0); // tilt 0% - test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0); // tilt 30% - test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 30); + test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30); // tilt 100% - test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 0% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 30% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 30); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 30); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 100% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 100); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // test constraints on impossible values: primary 30% => tilt 30% - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30).setPosition(capabilities, + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30).setPosition(capabilities, VANE_TILT_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); @@ -331,7 +332,7 @@ public void testDuoliteTiltShadePositionParsing() { assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30); // test constraints on impossible values: primary 30% => tilt 30% => back shade 30% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30) + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30) .setPosition(capabilities, VANE_TILT_POSITION, 30).setPosition(capabilities, SECONDARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); @@ -346,7 +347,7 @@ public void testDuoliteTiltShadePositionParsing() { @Test public void testCaps0ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(0); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -360,7 +361,7 @@ public void testCaps0ShadePositionParsingFullyUp() { @Test public void testCap0ShadePositionParsingShadeFullyDown() { Capabilities capabilities = db.getCapabilities(0); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -384,7 +385,7 @@ private void assertShadePosition(State actual, State target) { @Test public void testType44ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(44, null); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -398,7 +399,7 @@ public void testType44ShadePositionParsingFullyUp() { @Test public void testType44ShadePositionParsingShadeFullyDownVaneOpen() { Capabilities capabilities = db.getCapabilities(44, null); - ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 88); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 88); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/duette.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/duette.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/sceneCollections.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/sceneCollections.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/shades.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json new file mode 100644 index 0000000000000..1481512f41f60 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json @@ -0,0 +1,13 @@ +[ + { + "id": 33, + "type": 6, + "enabled": true, + "days": "24", + "hour": 4, + "min": 5, + "bleId": 83, + "sceneId": 116, + "errorShd_Ids": null + } +] diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json new file mode 100644 index 0000000000000..5a4c78d315a7e --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json @@ -0,0 +1,25 @@ +[ + { + "id": 789, + "type": 23, + "name": "Q2VudGVyCg==", + "ptName": "Center", + "capabilities": 0, + "powerType": "wand", + "batteryStatus": 2, + "roomId": 6833, + "signalStrength": -55, + "bleName": "PIR:5933", + "firmware": { + "revision": 3, + "subRevision": 0, + "build": 309 + }, + "positions": { + "primary": 0.99, + "secondary": 0.99, + "tilt": 0, + "velocity": 0.5 + } + } +] diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json new file mode 100644 index 0000000000000..6690715b23e44 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json @@ -0,0 +1,17 @@ +{ + "evt": "scene-activated", + "isoDate": "2021-12-06T20:01:11.934Z", + "id": 55, + "scene": { + "id": 234, + "name": "T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo=", + "ptName": "Open All Office Shades", + "color": "37", + "icon": "669", + "networkNumber": 7383, + "roomIds": [ + 55, + 66 + ] + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json new file mode 100644 index 0000000000000..3c131f1449736 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json @@ -0,0 +1,14 @@ +[ + { + "id": 234, + "name": "T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo=", + "ptName": "ABC", + "color": "37", + "icon": "669", + "networkNumber": 7383, + "roomIds": [ + 55, + 66 + ] + } +] diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json new file mode 100644 index 0000000000000..eb15d42a3b329 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json @@ -0,0 +1,17 @@ +{ + "evt": "motion-started", + "isoDate": "2021-12-06T20:01:11.934Z", + "bleName": "PIR:1233", + "id": 55, + "currentPositions": { + "primary": 0.99, + "secondary": 0.98, + "tilt": 0 + }, + "targetPositions": { + "etaInSeconds": 7, + "primary": 0.99, + "secondary": 0.99, + "tilt": 0 + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json new file mode 100644 index 0000000000000..a903771e23e37 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json @@ -0,0 +1,25 @@ +[ + { + "id": 789, + "type": 23, + "name": "U2hhZGUgMg==", + "ptName": "ABC", + "capabilities": 0, + "powerType": "wand", + "batteryStatus": 2, + "roomId": 6833, + "signalStrength": -55, + "bleName": "PIR:5933", + "firmware": { + "revision": 3, + "subRevision": 0, + "build": 309 + }, + "positions": { + "primary": 0.99, + "secondary": 0.99, + "tilt": 0, + "velocity": 0.5 + } + } +] From d1b773e799dc86110b5a72813e734f55ec10b187 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 4 Sep 2022 18:47:09 +0100 Subject: [PATCH 02/64] [hdpowerview] add missing serializer Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/HDPowerViewWebTargets.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 4e9cf8ed2bf3d..7d336a3120127 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -56,6 +56,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; /** * Abstract class for JAX-RS targets for communicating with an HD PowerView hub. @@ -123,6 +125,16 @@ private class ShadePositionDeserializer implements JsonDeserializer { + @Override + public JsonElement serialize(ShadePosition src, Type typeOfT, JsonSerializationContext context) { + return context.serialize(src, shadePositionClass); + } + }; + /* * Private helper class for de-serialization of ScheduledEvent */ @@ -183,6 +195,7 @@ public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class s .registerTypeAdapter(Scene.class, new SceneDeserializer()) .registerTypeAdapter(ShadeData.class, new ShadeDataDeserializer()) .registerTypeAdapter(ShadePosition.class, new ShadePositionDeserializer()) + .registerTypeAdapter(ShadePosition.class, new ShadePositionSerializer()) .registerTypeAdapter(ScheduledEvent.class, new ScheduledEventDeserializer()) // @formatter:on .create(); From 16049dfd828b46821763c7fe4e277b233f12aa29 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 4 Sep 2022 19:29:59 +0100 Subject: [PATCH 03/64] [hdpowerview] fix PUT command json Signed-off-by: Andrew Fiddian-Green --- .../internal/_v3/HDPowerViewWebTargetsV3.java | 10 +++++----- .../hdpowerview/internal/api/requests/ShadeMotion.java | 2 +- .../internal/api/requests/ShadePositions.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java index 18b0979f4c5a2..e43fc66e7ad2f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java @@ -34,8 +34,8 @@ import org.openhab.binding.hdpowerview.internal.api.UserData; import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadePositions; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; import org.openhab.binding.hdpowerview.internal.api.responses.Scene; @@ -163,7 +163,7 @@ public Shades getShades() throws HubInvalidResponseException, HubProcessingExcep public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), - gson.toJson(position)); + gson.toJson(new ShadePositions(position))); return getShade(shadeId); } @@ -177,7 +177,7 @@ public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubP @Override public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String json = gson.toJson(new ShadeJog()); + String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.JOG)); invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); return getShade(shadeId); } @@ -185,7 +185,7 @@ public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubPr @Override public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String json = gson.toJson(new ShadeCalibrate()); + String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.CALIBRATE)); invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); return getShade(shadeId); } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java index c6b16a000065e..13d3179c4841a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java @@ -20,7 +20,7 @@ * @author Jacob Laursen - Initial contribution */ @NonNullByDefault -class ShadeMotion { +public class ShadeMotion { public enum Type { STOP("stop"), diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java index 1c544c047e878..07b4c87e778a1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java @@ -21,7 +21,7 @@ * @author Andy Lintner - Initial contribution */ @NonNullByDefault -class ShadePositions { +public class ShadePositions { public ShadePosition positions; From 81d8c94376dc4d0a3e9ee3df4abb3bde20eefdab Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 4 Sep 2022 19:43:03 +0100 Subject: [PATCH 04/64] [hdpowerview] delete unnecessary json file Signed-off-by: Andrew Fiddian-Green --- .../binding/hdpowerview/_v3/motion.json | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json deleted file mode 100644 index 5a4c78d315a7e..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "id": 789, - "type": 23, - "name": "Q2VudGVyCg==", - "ptName": "Center", - "capabilities": 0, - "powerType": "wand", - "batteryStatus": 2, - "roomId": 6833, - "signalStrength": -55, - "bleName": "PIR:5933", - "firmware": { - "revision": 3, - "subRevision": 0, - "build": 309 - }, - "positions": { - "primary": 0.99, - "secondary": 0.99, - "tilt": 0, - "velocity": 0.5 - } - } -] From 9581cf15cecdfb2bcd77cc4cf5aa6862bb68539b Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 5 Sep 2022 11:27:17 +0100 Subject: [PATCH 05/64] [hdpowerview] tweak serializer Signed-off-by: Andrew Fiddian-Green --- .../binding/hdpowerview/internal/HDPowerViewWebTargets.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 7d336a3120127..8360e3fdd742b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -130,8 +130,8 @@ private class ShadePositionDeserializer implements JsonDeserializer { @Override - public JsonElement serialize(ShadePosition src, Type typeOfT, JsonSerializationContext context) { - return context.serialize(src, shadePositionClass); + public JsonElement serialize(ShadePosition src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src, src.getClass()); } }; From 24adb14ad8d732da1b4eeffa004521b9c6e03ab7 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 7 Sep 2022 16:43:16 +0100 Subject: [PATCH 06/64] [hpowerview] stronger type check on generics Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/HDPowerViewWebTargets.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 8360e3fdd742b..94480db4d8d7d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -81,10 +81,10 @@ public abstract class HDPowerViewWebTargets { /* * De-serializer target class types */ - private final Class sceneClass; - private final Class shadeDataClass; - private final Class shadePositionClass; - private final Class scheduledEventClass; + private final Class sceneClass; + private final Class shadeDataClass; + private final Class shadePositionClass; + private final Class scheduledEventClass; protected final Gson gson; protected final HttpClient httpClient; @@ -183,8 +183,9 @@ public String toString() { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class sceneClass, Class shadeDataClass, - Class shadePositionClass, Class scheduledEventClass) { + public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class sceneClass, + Class shadeDataClass, Class shadePositionClass, + Class scheduledEventClass) { this.httpClient = httpClient; this.sceneClass = sceneClass; this.shadeDataClass = shadeDataClass; From 57fb1bc9bd0c861ffb3a860038a31f0bac6d4c37 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 10 Sep 2022 12:46:42 +0100 Subject: [PATCH 07/64] [hdpowerview] implement battery kind on gen 3 Signed-off-by: Andrew Fiddian-Green --- .../internal/api/_v3/ShadeDataV3.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java index cadd5f62c4a56..ef6b3edde8b07 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java @@ -27,7 +27,6 @@ public class ShadeDataV3 extends ShadeData { public @Nullable String ptName; public @Nullable String powerType; public @Nullable String bleName; - // TODO: public @Nullable Motion motion; @Override public String getName() { @@ -36,8 +35,23 @@ public String getName() { @Override public BatteryKind getBatteryKind() { - // TODO Auto-generated method stub - // NOTE: the schema for powerType is not clear; is may be a string? or an integer? + // TODO the schema for powerType is not clear; is may be a string? or an integer? + String powerType = this.powerType; + if (powerType != null) { + try { + Integer powerTypeInt = Integer.valueOf(powerType); + switch (powerTypeInt) { + case 0: + return BatteryKind.BATTERY_WAND; + case 1: + return BatteryKind.HARDWIRED_POWER_SUPPLY; + case 2: + return BatteryKind.RECHARGEABLE_BATTERY_WAND; + } + } catch (NumberFormatException e) { + // fall through + } + } return BatteryKind.ERROR_UNKNOWN; } From 86a0f2c8498ba84bd508c2e124d5d7357c2c11bc Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 10 Sep 2022 12:47:44 +0100 Subject: [PATCH 08/64] [hdpowerview] implement generation configuration Signed-off-by: Andrew Fiddian-Green --- .../config/HDPowerViewHubConfiguration.java | 3 +++ .../handler/HDPowerViewHubHandler.java | 27 +++++++++---------- .../OH-INF/i18n/hdpowerview.properties | 2 ++ .../main/resources/OH-INF/thing/bridge.xml | 5 ++++ 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java index 14cd3b3424b72..156a4835fc07e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java @@ -24,10 +24,13 @@ public class HDPowerViewHubConfiguration { public static final String HOST = "host"; + public static final String GENERATION = "generation"; public @Nullable String host; public long refresh; public long hardRefresh; public long hardRefreshBatteryLevel; + + public int generation; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index ee4e935f27942..862bf0441b6b4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -711,23 +711,20 @@ private HDPowerViewWebTargets newWebTargets(String host) throws InstantiationExc if (webTargets != null) { return webTargets; } - try { - // try communicating via V1 web targets - webTargets = new HDPowerViewWebTargetsV1(httpClient, host); - webTargets.getFirmwareVersions(); - this.webTargets = webTargets; - return webTargets; - } catch (HubProcessingException | HubMaintenanceException e) { - // fall through + HDPowerViewHubConfiguration config = getConfigAs(HDPowerViewHubConfiguration.class); + int hubGeneration = config.generation; + switch (hubGeneration) { + case 0: // for non breaking of existing installations + case 1: // both generation 1 and 2 hubs use V1 web targets + case 2: + webTargets = new HDPowerViewWebTargetsV1(httpClient, host); + break; + case 3: // generation 3 hubs use V3 web targets + webTargets = new HDPowerViewWebTargetsV3(httpClient, host); } - try { - // try communicating via V3 web targets - webTargets = new HDPowerViewWebTargetsV3(httpClient, host); - webTargets.getFirmwareVersions(); + if (webTargets != null) { this.webTargets = webTargets; return webTargets; - } catch (HubProcessingException | HubMaintenanceException e) { - // fall through } throw new InstantiationException("Unable to instantiate the web targets"); } @@ -782,6 +779,6 @@ public void sseShade(String evt, int shadeId, ShadePosition shadePosition) { @Override public void sseScene(String evt, int sceneId) { - // TODO (if anything) + // TODO perhaps we don't need to do anything? } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index 6aeb6076f9582..cc4d76d24ddf4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -22,6 +22,8 @@ thing-type.hdpowerview.shade.channel.secondary.description = The secondary verti # thing types config +thing-type.config.hdpowerview.hub.generation.label = Hub Generation +thing-type.config.hdpowerview.hub.generation.description = The PowerView Hub Generation thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index 1c20f86e965d2..9458b79274956 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -27,6 +27,11 @@ The Host address of the PowerView Hub network-address + + + The PowerView Hub Generation + 0 + The number of milliseconds between fetches of the PowerView Hub shade state From 40a19f8cb95c2b1913f6e87663f5240ca479fa9f Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 10 Sep 2022 12:48:16 +0100 Subject: [PATCH 09/64] [hdpowerview] implement generation 1/2 and 3 mDNS Signed-off-by: Andrew Fiddian-Green --- .../HDPowerViewHubDiscoveryParticipant.java | 35 ++--------- .../HDPowerViewHubDiscoveryParticipantV1.java | 62 +++++++++++++++++++ .../HDPowerViewHubDiscoveryParticipantV3.java | 62 +++++++++++++++++++ 3 files changed, 130 insertions(+), 29 deletions(-) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java index 4ee2a8c4025b9..b118704083e38 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.hdpowerview.internal.discovery; -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; import java.util.Collections; import java.util.Set; @@ -22,28 +22,20 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; import org.openhab.core.config.discovery.DiscoveryResult; -import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; -import org.osgi.service.component.annotations.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * Discovers HD PowerView hubs by means of mDNS + * Abstract (component) class that discovers HD PowerView hubs by means of mDNS * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -@Component -public class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticipant { +public abstract class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticipant { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant.class); - - private static final Pattern VALID_IP_V4_ADDRESS = Pattern + protected static final Pattern VALID_IP_V4_ADDRESS = Pattern .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); @Override @@ -52,25 +44,10 @@ public Set getSupportedThingTypeUIDs() { } @Override - public String getServiceType() { - return "_powerview._tcp.local."; - } + public abstract String getServiceType(); @Override - public @Nullable DiscoveryResult createResult(ServiceInfo service) { - for (String host : service.getHostAddresses()) { - if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); - DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) - .withProperty(HDPowerViewHubConfiguration.HOST, host) - .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel("PowerView Hub (" + host + ")").build(); - logger.debug("mDNS discovered hub on host '{}'", host); - return hub; - } - } - return null; - } + public abstract @Nullable DiscoveryResult createResult(ServiceInfo service); @Override public @Nullable ThingUID getThingUID(ServiceInfo service) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java new file mode 100644 index 0000000000000..bb138c5eef52c --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.discovery._v1; + +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; + +import javax.jmdns.ServiceInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; +import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewHubDiscoveryParticipant; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Discovers HD PowerView generation 1/2 hubs by means of mDNS + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +@Component +public class HDPowerViewHubDiscoveryParticipantV1 extends HDPowerViewHubDiscoveryParticipant { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV1.class); + + @Override + public String getServiceType() { + return "_powerview._tcp.local."; + } + + @Override + public @Nullable DiscoveryResult createResult(ServiceInfo service) { + for (String host : service.getHostAddresses()) { + if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { + ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); + DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) + .withProperty(HDPowerViewHubConfiguration.HOST, host) + .withProperty(HDPowerViewHubConfiguration.GENERATION, 1) + .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) + .withLabel("PowerView Hub (" + host + ")").build(); + logger.debug("mDNS discovered generation 1/2 hub on host '{}'", host); + return hub; + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java new file mode 100644 index 0000000000000..be0b5a7577004 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.discovery._v3; + +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; + +import javax.jmdns.ServiceInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; +import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewHubDiscoveryParticipant; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Discovers HD PowerView generation 3 hubs by means of mDNS + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +@Component +public class HDPowerViewHubDiscoveryParticipantV3 extends HDPowerViewHubDiscoveryParticipant { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV3.class); + + @Override + public String getServiceType() { + return "_powerview-g3._tcp.local."; + } + + @Override + public @Nullable DiscoveryResult createResult(ServiceInfo service) { + for (String host : service.getHostAddresses()) { + if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { + ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); + DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) + .withProperty(HDPowerViewHubConfiguration.HOST, host) + .withProperty(HDPowerViewHubConfiguration.GENERATION, 3) + .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) + .withLabel("PowerView Hub (" + host + ")").build(); + logger.debug("mDNS discovered generation 3 hub on host '{}'", host); + return hub; + } + } + return null; + } +} From 4bac717b89264f0e0a0811f3fdddce6d1fa57c2b Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 10 Sep 2022 13:26:50 +0100 Subject: [PATCH 10/64] [hdpowerview] spotless:apply Signed-off-by: Andrew Fiddian-Green --- .../src/main/resources/OH-INF/thing/bridge.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index 9458b79274956..b752d79e370a7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -27,11 +27,11 @@ The Host address of the PowerView Hub network-address - - - The PowerView Hub Generation - 0 - + + + The PowerView Hub Generation + 0 + The number of milliseconds between fetches of the PowerView Hub shade state From b34755815d7476fe323a4cf24225dd5ef6240835 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Thu, 15 Sep 2022 11:41:27 +0100 Subject: [PATCH 11/64] [hdpowerview] return empty repeaters object rather than throw exception Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java index e43fc66e7ad2f..8849e3c608b6d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java @@ -228,7 +228,7 @@ public void enableScheduledEvent(int scheduledEventId, boolean enable) @Override public Repeaters getRepeaters() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getRepeaters(): method not implemented"); + return new Repeaters(); } @Override From a2fef4ac25c9bc5cf6f4db3795d5bbfe9d23d016 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Thu, 20 Oct 2022 13:59:50 +0100 Subject: [PATCH 12/64] [hdpowerview] use osgi ClientBuilder, SseEventSourceFactory instances Signed-off-by: Andrew Fiddian-Green --- .../internal/HDPowerViewHandlerFactory.java | 11 +++++++- .../internal/HDPowerViewWebTargets.java | 10 +++++++- .../internal/_v1/HDPowerViewWebTargetsV1.java | 9 +++++-- .../internal/_v3/HDPowerViewWebTargetsV3.java | 25 ++++++++++--------- .../handler/HDPowerViewHubHandler.java | 14 ++++++++--- .../hdpowerview/Generation3DtoTest.java | 7 +++++- .../hdpowerview/HDPowerViewJUnitTests.java | 7 +++++- .../hdpowerview/OnlineCommunicationTest.java | 7 +++++- 8 files changed, 68 insertions(+), 22 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index af8c201b99ff4..1fcaa336717ec 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -14,6 +14,8 @@ import java.util.Hashtable; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -35,6 +37,7 @@ import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; /** * The {@link HDPowerViewHandlerFactory} is responsible for creating things and thing @@ -48,15 +51,20 @@ public class HDPowerViewHandlerFactory extends BaseThingHandlerFactory { private final HttpClient httpClient; private final HDPowerViewTranslationProvider translationProvider; + private final ClientBuilder clientBuilder; + private final SseEventSourceFactory eventSourceFactory; @Activate public HDPowerViewHandlerFactory(@Reference HttpClientFactory httpClientFactory, final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider, + final @Reference ClientBuilder clientBuilder, final @Reference SseEventSourceFactory eventSourceFactory, ComponentContext componentContext) { super.activate(componentContext); this.httpClient = httpClientFactory.getCommonHttpClient(); this.translationProvider = new HDPowerViewTranslationProvider(getBundleContext().getBundle(), i18nProvider, localeProvider); + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; } @Override @@ -69,7 +77,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { - HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider); + HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider, + clientBuilder, eventSourceFactory); registerService(new HDPowerViewDeviceDiscoveryService(handler)); return handler; } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE.equals(thingTypeUID)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 94480db4d8d7d..ccad2af04eca8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -19,6 +19,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -46,6 +48,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -88,6 +91,8 @@ public abstract class HDPowerViewWebTargets { protected final Gson gson; protected final HttpClient httpClient; + protected final ClientBuilder clientBuilder; + protected final SseEventSourceFactory eventSourceFactory; /* * Private helper class for de-serialization of Scene @@ -183,10 +188,13 @@ public String toString() { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class sceneClass, + public HDPowerViewWebTargets(HttpClient httpClient, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory, String ipAddress, Class sceneClass, Class shadeDataClass, Class shadePositionClass, Class scheduledEventClass) { this.httpClient = httpClient; + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; this.sceneClass = sceneClass; this.shadeDataClass = shadeDataClass; this.shadePositionClass = shadePositionClass; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java index 1eff3b0b31b32..5a47ce2b46b0a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java @@ -14,6 +14,8 @@ import java.util.List; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpMethod; @@ -52,6 +54,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -84,8 +87,10 @@ public class HDPowerViewWebTargetsV1 extends HDPowerViewWebTargets { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargetsV1(HttpClient httpClient, String ipAddress) { - super(httpClient, ipAddress, SceneV1.class, ShadeDataV1.class, ShadePositionV1.class, ScheduledEventV1.class); + public HDPowerViewWebTargetsV1(HttpClient httpClient, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory, String ipAddress) { + super(httpClient, clientBuilder, eventSourceFactory, ipAddress, SceneV1.class, ShadeDataV1.class, + ShadePositionV1.class, ScheduledEventV1.class); // initialize the urls String base = "http://" + ipAddress + "/api/"; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java index 8849e3c608b6d..c803f4e2c25cc 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java @@ -16,8 +16,9 @@ import java.util.ArrayList; import java.util.List; -import javax.ws.rs.client.Client; +import javax.net.ssl.SSLContext; import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; import javax.ws.rs.sse.InboundSseEvent; import javax.ws.rs.sse.SseEventSource; @@ -54,6 +55,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -109,7 +111,6 @@ private static class GatewayRegistration { private boolean isRegistered; private @Nullable SseSinkV3 sseSink; - private final Client sseClient = ClientBuilder.newClient(); private @Nullable SseEventSource shadeEventSource; private @Nullable SseEventSource sceneEventSource; @@ -119,8 +120,10 @@ private static class GatewayRegistration { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargetsV3(HttpClient httpClient, String ipAddress) { - super(httpClient, ipAddress, SceneV3.class, ShadeDataV3.class, ShadePositionV3.class, ScheduledEventV3.class); + public HDPowerViewWebTargetsV3(HttpClient httpClient, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory, String ipAddress) { + super(httpClient, clientBuilder, eventSourceFactory, ipAddress, SceneV3.class, ShadeDataV3.class, + ShadePositionV3.class, ScheduledEventV3.class); // initialize the urls String base = "http://" + ipAddress + "/"; @@ -314,24 +317,22 @@ public boolean sseSubscribe(@Nullable SseSinkV3 sseSink) throws HubMaintenanceEx // open SSE channel for shades SseEventSource shadeEventSource = this.shadeEventSource; if (shadeEventSource == null || !shadeEventSource.isOpen()) { - source = SseEventSource.target(sseClient.target(shadeEvents)).build(); + SSLContext context = httpClient.getSslContextFactory().getSslContext(); + WebTarget target = clientBuilder.sslContext(context).build().target(shadeEvents); + source = eventSourceFactory.newSource(target); source.register((event) -> onShadeEvent(event)); source.open(); - if (!source.isOpen()) { - throw new HubProcessingException("setEventSink(): failed to open SSE channel for shades"); - } this.shadeEventSource = source; } // open SSE channel for scenes SseEventSource sceneEventSource = this.sceneEventSource; if (sceneEventSource == null || !sceneEventSource.isOpen()) { - source = SseEventSource.target(sseClient.target(sceneEvents)).build(); + SSLContext context = httpClient.getSslContextFactory().getSslContext(); + WebTarget target = clientBuilder.sslContext(context).build().target(sceneEvents); + source = eventSourceFactory.newSource(target); source.register((event) -> onSceneEvent(event)); source.open(); - if (!source.isOpen()) { - throw new HubProcessingException("setEventSink(): failed to open SSE channel for scenes"); - } this.sceneEventSource = source; } return true; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index 862bf0441b6b4..db0e2f403e286 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -25,6 +25,8 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -74,6 +76,7 @@ import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,6 +96,8 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV private final HDPowerViewTranslationProvider translationProvider; private final ConcurrentHashMap pendingShadeInitializations = new ConcurrentHashMap<>(); private final Duration firmwareVersionValidityPeriod = Duration.ofDays(1); + private final ClientBuilder clientBuilder; + private final SseEventSourceFactory eventSourceFactory; private long refreshInterval; private long hardRefreshPositionInterval; @@ -119,10 +124,13 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED); public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient, - HDPowerViewTranslationProvider translationProvider) { + HDPowerViewTranslationProvider translationProvider, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory) { super(bridge); this.httpClient = httpClient; this.translationProvider = translationProvider; + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; } @Override @@ -717,10 +725,10 @@ private HDPowerViewWebTargets newWebTargets(String host) throws InstantiationExc case 0: // for non breaking of existing installations case 1: // both generation 1 and 2 hubs use V1 web targets case 2: - webTargets = new HDPowerViewWebTargetsV1(httpClient, host); + webTargets = new HDPowerViewWebTargetsV1(httpClient, clientBuilder, eventSourceFactory, host); break; case 3: // generation 3 hubs use V3 web targets - webTargets = new HDPowerViewWebTargetsV3(httpClient, host); + webTargets = new HDPowerViewWebTargetsV3(httpClient, clientBuilder, eventSourceFactory, host); } if (webTargets != null) { this.webTargets = webTargets; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java index f32f5ca731ecb..b7474b6c251b7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java @@ -21,9 +21,12 @@ import java.nio.charset.StandardCharsets; import java.util.List; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; @@ -39,6 +42,7 @@ import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.UnDefType; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; /** * Unit tests for Generation 3 DTO's. @@ -48,7 +52,8 @@ @NonNullByDefault public class Generation3DtoTest { - private final HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV3(new HttpClient(), ""); + private final HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV3(new HttpClient(), + Mockito.mock(ClientBuilder.class), Mockito.mock(SseEventSourceFactory.class), ""); private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); private String loadJson(String filename) throws IOException { diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java index 737c932c46813..e59f341cdefd1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java @@ -21,9 +21,12 @@ import java.util.List; import java.util.Objects; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; import org.openhab.binding.hdpowerview.internal.api.BatteryKind; import org.openhab.binding.hdpowerview.internal.api.ShadeData; @@ -38,6 +41,7 @@ import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import com.google.gson.Gson; @@ -50,7 +54,8 @@ @NonNullByDefault public class HDPowerViewJUnitTests { - private final Gson gson = new HDPowerViewWebTargetsV1(new HttpClient(), "").getGson(); + private final Gson gson = new HDPowerViewWebTargetsV1(new HttpClient(), Mockito.mock(ClientBuilder.class), + Mockito.mock(SseEventSourceFactory.class), "").getGson(); private T getObjectFromJson(String filename, Class clazz) throws IOException { try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java index cdb3660f01ee1..588fcfa9ef23f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java @@ -18,9 +18,12 @@ import java.util.List; import java.util.regex.Pattern; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; import org.openhab.binding.hdpowerview.internal.api.ShadeData; @@ -36,6 +39,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; /** * Unit tests for HD PowerView binding. @@ -85,7 +89,8 @@ public void testOnlineCommunication() { fail(e.getMessage()); } - HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV1(client, hubIPAddress); + HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV1(client, Mockito.mock(ClientBuilder.class), + Mockito.mock(SseEventSourceFactory.class), hubIPAddress); assertNotNull(webTargets); int shadeId = 0; From adb2b96af366a6767323bc5a67d0de867b1c8ded Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 26 Oct 2022 19:46:47 +0100 Subject: [PATCH 13/64] [hdpowerview] adopt reviewer request Signed-off-by: Andrew Fiddian-Green --- .../internal/HDPowerViewBindingConstants.java | 8 + .../internal/HDPowerViewHandlerFactory.java | 17 +- .../internal/HDPowerViewWebTargets.java | 626 ++++++++++------- .../internal/_v1/HDPowerViewWebTargetsV1.java | 629 ------------------ .../internal/_v3/HDPowerViewWebTargetsV3.java | 552 --------------- .../hdpowerview/internal/_v3/SseSinkV3.java | 42 -- .../hdpowerview/internal/api/ShadeData.java | 46 -- .../internal/api/ShadePosition.java | 338 +++++++++- .../internal/api/_v1/ShadeDataV1.java | 47 -- .../internal/api/_v1/ShadePositionV1.java | 387 ----------- .../internal/api/_v3/ShadeDataV3.java | 62 -- .../internal/api/requests/ShadePositions.java | 2 +- .../internal/api/responses/Scene.java | 40 -- .../internal/api/responses/Scenes.java | 56 ++ .../api/responses/ScheduledEvent.java | 40 -- .../api/responses/ScheduledEvents.java | 91 +++ .../internal/api/responses/Shade.java | 2 +- .../internal/api/responses/Shades.java | 41 +- .../internal/api/responses/_v1/SceneV1.java | 73 -- .../api/responses/_v1/ScheduledEventV1.java | 116 ---- .../internal/api/responses/_v3/InfoV3.java | 65 -- .../internal/api/responses/_v3/SceneV3.java | 85 --- .../builders/AutomationChannelBuilder.java | 27 +- .../builders/SceneChannelBuilder.java | 8 +- .../config/HDPowerViewHubConfiguration.java | 3 - .../HDPowerViewDeviceDiscoveryService.java | 2 +- .../HDPowerViewHubDiscoveryParticipant.java | 33 +- .../HDPowerViewHubDiscoveryParticipantV1.java | 62 -- .../HDPowerViewDeviceDiscoveryServiceV3.java | 112 ++++ .../HDPowerViewHubDiscoveryParticipantV3.java | 21 +- .../SseShadeV3.java => gen3/dto/Info3.java} | 22 +- .../hdpowerview/internal/gen3/dto/Scene3.java | 59 ++ .../dto/SceneEvent.java} | 23 +- .../dto/ScheduledEvent3.java} | 39 +- .../hdpowerview/internal/gen3/dto/Shade3.java | 158 +++++ .../internal/gen3/dto/ShadeEvent.java | 38 ++ .../dto/ShadePosition3.java} | 62 +- .../gen3/handler/HDPowerViewHubHandler3.java | 284 ++++++++ .../handler/HDPowerViewShadeHandler3.java | 285 ++++++++ .../webtargets/HDPowerViewWebTargets3.java | 445 +++++++++++++ .../handler/HDPowerViewHubHandler.java | 132 +--- .../handler/HDPowerViewShadeHandler.java | 41 +- .../OH-INF/i18n/hdpowerview.properties | 12 +- .../main/resources/OH-INF/thing/bridge.xml | 34 +- .../src/main/resources/OH-INF/thing/shade.xml | 30 + .../AutomationChannelBuilderTest.java | 36 +- .../hdpowerview/Generation3DtoTest.java | 196 ------ .../hdpowerview/HDPowerViewJUnitTests.java | 23 +- .../hdpowerview/OnlineCommunicationTest.java | 17 +- .../hdpowerview/SceneChannelBuilderTest.java | 5 +- .../hdpowerview/ShadePositionTest.java | 53 +- .../hdpowerview/gen3/Generation3DtoTest.java | 153 +++++ .../binding/hdpowerview/{_v1 => }/duette.json | 0 .../{_v3 => gen3}/automations.json | 0 .../{_v3 => gen3}/scene-event.json | 0 .../hdpowerview/{_v3 => gen3}/scenes.json | 0 .../{_v3 => gen3}/shade-event.json | 0 .../hdpowerview/{_v3 => gen3}/shades.json | 0 .../{_v1 => }/sceneCollections.json | 0 .../binding/hdpowerview/{_v1 => }/scenes.json | 0 .../binding/hdpowerview/{_v1 => }/shades.json | 0 61 files changed, 2732 insertions(+), 3048 deletions(-) delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{discovery/_v3 => gen3/discovery}/HDPowerViewHubDiscoveryParticipantV3.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api/responses/_v3/SseShadeV3.java => gen3/dto/Info3.java} (54%) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api/responses/_v3/SseSceneV3.java => gen3/dto/SceneEvent.java} (60%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api/responses/_v3/ScheduledEventV3.java => gen3/dto/ScheduledEvent3.java} (79%) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api/_v3/ShadePositionV3.java => gen3/dto/ShadePosition3.java} (56%) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v1 => }/duette.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/automations.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/scene-event.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/shade-event.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/shades.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v1 => }/sceneCollections.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v1 => }/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v1 => }/shades.json (100%) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java index e680de5d7f452..610e0d82d025f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java @@ -76,4 +76,12 @@ public class HDPowerViewBindingConstants { public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_HUB, THING_TYPE_SHADE, THING_TYPE_REPEATER); + + // generation 3 + public static final ThingTypeUID THING_TYPE_HUB_GEN3 = new ThingTypeUID(BINDING_ID, "gen3"); + public static final ThingTypeUID THING_TYPE_SHADE_GEN3 = new ThingTypeUID(BINDING_ID, "shade3"); + + public static final String PROPERTY_NAME = "name"; + public static final String PROPERTY_POWER_TYPE = "powerType"; + public static final String PROPERTY_BLE_NAME = "bleName"; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 1fcaa336717ec..c59011021297c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -20,6 +20,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewDeviceDiscoveryService; +import org.openhab.binding.hdpowerview.internal.gen3.discovery.HDPowerViewDeviceDiscoveryServiceV3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewShadeHandler3; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewRepeaterHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler; @@ -75,10 +78,18 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - - if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { - HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider, + // generation 3 + if (HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3.equals(thingTypeUID)) { + HDPowerViewHubHandler3 handler = new HDPowerViewHubHandler3((Bridge) thing, httpClient, translationProvider, clientBuilder, eventSourceFactory); + registerService(new HDPowerViewDeviceDiscoveryServiceV3(handler)); + return handler; + } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3.equals(thingTypeUID)) { + return new HDPowerViewShadeHandler3(thing); + } else + // generation 1/2 + if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { + HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider); registerService(new HDPowerViewDeviceDiscoveryService(handler)); return handler; } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE.equals(thingTypeUID)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index ccad2af04eca8..562d37bc504c7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -12,15 +12,12 @@ */ package org.openhab.binding.hdpowerview.internal; -import java.lang.reflect.Type; import java.time.Duration; import java.time.Instant; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -32,43 +29,52 @@ import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.api.Color; import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.SurveyData; import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; +import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; +import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; +import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Shade; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.responses.Survey; +import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; +import com.google.gson.JsonParser; /** - * Abstract class for JAX-RS targets for communicating with an HD PowerView hub. + * JAX-RS targets for communicating with an HD PowerView hub * - * @author Andrew Fiddian-Green - Initial contribution + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Added support for secondary rail positions + * @author Jacob Laursen - Added support for scene groups and automations */ @NonNullByDefault -public abstract class HDPowerViewWebTargets { +public class HDPowerViewWebTargets { private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class); @@ -81,81 +87,24 @@ public abstract class HDPowerViewWebTargets { private final Duration maintenancePeriod = Duration.ofMinutes(5); private Instant maintenanceScheduledEnd = Instant.MIN; - /* - * De-serializer target class types - */ - private final Class sceneClass; - private final Class shadeDataClass; - private final Class shadePositionClass; - private final Class scheduledEventClass; - - protected final Gson gson; - protected final HttpClient httpClient; - protected final ClientBuilder clientBuilder; - protected final SseEventSourceFactory eventSourceFactory; - - /* - * Private helper class for de-serialization of Scene - */ - private class SceneDeserializer implements JsonDeserializer { - @Override - public @Nullable Scene deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - JsonObject jsonObject = json.getAsJsonObject(); - return context.deserialize(jsonObject, sceneClass); - } - } - - /* - * Private helper class for de-serialization of ShadeData - */ - private class ShadeDataDeserializer implements JsonDeserializer { - @Override - public @Nullable ShadeData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - JsonObject jsonObject = json.getAsJsonObject(); - return context.deserialize(jsonObject, shadeDataClass); - } - } - - /* - * Private helper class for de-serialization of ShadePosition - */ - private class ShadePositionDeserializer implements JsonDeserializer { - @Override - public @Nullable ShadePosition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - JsonObject jsonObject = json.getAsJsonObject(); - return context.deserialize(jsonObject, shadePositionClass); - } - } + private final String base; + private final String firmwareVersion; + private final String shades; + private final String sceneActivate; + private final String scenes; + private final String sceneCollectionActivate; + private final String sceneCollections; + private final String scheduledEvents; + private final String repeaters; + private final String userData; - /* - * Private helper class for serialization of ShadePosition - */ - private class ShadePositionSerializer implements JsonSerializer { - @Override - public JsonElement serialize(ShadePosition src, Type typeOfSrc, JsonSerializationContext context) { - return context.serialize(src, src.getClass()); - } - }; - - /* - * Private helper class for de-serialization of ScheduledEvent - */ - private class ScheduledEventDeserializer implements JsonDeserializer { - @Override - public @Nullable ScheduledEvent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - JsonObject jsonObject = json.getAsJsonObject(); - return context.deserialize(jsonObject, scheduledEventClass); - } - } + private final Gson gson = new Gson(); + private final HttpClient httpClient; /** - * Protected helper class for passing http url query parameters + * private helper class for passing http url query parameters */ - protected static class Query { + public static class Query { private final String key; private final String value; @@ -188,101 +137,73 @@ public String toString() { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(HttpClient httpClient, ClientBuilder clientBuilder, - SseEventSourceFactory eventSourceFactory, String ipAddress, Class sceneClass, - Class shadeDataClass, Class shadePositionClass, - Class scheduledEventClass) { + public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) { + base = "http://" + ipAddress + "/api/"; + shades = base + "shades/"; + firmwareVersion = base + "fwversion"; + sceneActivate = base + "scenes"; + scenes = base + "scenes/"; + + // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections". + sceneCollectionActivate = base + "scenecollections"; + sceneCollections = base + "scenecollections/"; + + scheduledEvents = base + "scheduledevents"; + repeaters = base + "repeaters/"; + userData = base + "userdata"; + this.httpClient = httpClient; - this.clientBuilder = clientBuilder; - this.eventSourceFactory = eventSourceFactory; - this.sceneClass = sceneClass; - this.shadeDataClass = shadeDataClass; - this.shadePositionClass = shadePositionClass; - this.scheduledEventClass = scheduledEventClass; - this.gson = new GsonBuilder() - // @formatter:off - .registerTypeAdapter(Scene.class, new SceneDeserializer()) - .registerTypeAdapter(ShadeData.class, new ShadeDataDeserializer()) - .registerTypeAdapter(ShadePosition.class, new ShadePositionDeserializer()) - .registerTypeAdapter(ShadePosition.class, new ShadePositionSerializer()) - .registerTypeAdapter(ScheduledEvent.class, new ScheduledEventDeserializer()) - // @formatter:on - .create(); } /** - * Get the gson de-serializer. + * Fetches a JSON package with firmware information for the hub. * - * @return the gson de-serializer. + * @return FirmwareVersions class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance */ - public Gson getGson() { - return gson; + public HubFirmware getFirmwareVersions() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, firmwareVersion, null, null); + try { + FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class); + if (firmwareVersion == null) { + throw new HubInvalidResponseException("Missing firmware response"); + } + HubFirmware firmwareVersions = firmwareVersion.firmware; + if (firmwareVersions == null) { + throw new HubInvalidResponseException("Missing 'firmware' element"); + } + return firmwareVersions; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing firmware response", e); + } } /** - * Common protected method to invoke a call on the hub server to retrieve information or send a command + * Fetches a JSON package with user data information for the hub. * - * @param method GET or PUT - * @param url the host url to be called - * @param query the http query parameter - * @param jsonCommand the request command content (as a json string) - * @return the response content (as a json string) - * @throws HubMaintenanceException - * @throws HubProcessingException + * @return {@link UserData} class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance */ - protected synchronized String invoke(HttpMethod method, String url, @Nullable Query query, - @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { - if (logger.isTraceEnabled()) { - if (query != null) { - logger.trace("API command {} {}{}", method, url, query); - } else { - logger.trace("API command {} {}", method, url); - } - if (jsonCommand != null) { - logger.trace("JSON command = {}", jsonCommand); - } - } - Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); - if (query != null) { - request.param(query.getKey(), query.getValue()); - } - if (jsonCommand != null) { - request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); - } - ContentResponse response; + public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, userData, null, null); try { - response = request.send(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); - } catch (TimeoutException | ExecutionException e) { - if (Instant.now().isBefore(maintenanceScheduledEnd)) { - // throw "softer" exception during maintenance window - logger.debug("Hub still undergoing maintenance"); - throw new HubMaintenanceException("Hub still undergoing maintenance"); + UserDataResponse userDataResponse = gson.fromJson(json, UserDataResponse.class); + if (userDataResponse == null) { + throw new HubInvalidResponseException("Missing userData response"); } - throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); - } - int statusCode = response.getStatus(); - if (statusCode == HttpStatus.LOCKED_423) { - // set end of maintenance window, and throw a "softer" exception - maintenanceScheduledEnd = Instant.now().plus(maintenancePeriod); - logger.debug("Hub undergoing maintenance"); - throw new HubMaintenanceException("Hub undergoing maintenance"); - } - if (statusCode != HttpStatus.OK_200) { - logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); - throw new HubProcessingException(String.format("HTTP %d error", statusCode)); - } - String jsonResponse = response.getContentAsString(); - if (logger.isTraceEnabled()) { - logger.trace("JSON response = {}", jsonResponse); - } - if (jsonResponse == null || jsonResponse.isEmpty()) { - logger.warn("Hub returned no content"); - throw new HubProcessingException("Missing response entity"); + UserData userData = userDataResponse.userData; + if (userData == null) { + throw new HubInvalidResponseException("Missing 'userData' element"); + } + return userData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing userData response", e); } - return jsonResponse; } /** @@ -294,42 +215,22 @@ protected synchronized String invoke(HttpMethod method, String url, @Nullable Qu * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract Shades getShades() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; - - /** - * Fetches a JSON package that describes all scenes in the hub, and wraps it in - * a Scenes class instance - * - * @return Scenes class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - public abstract Scenes getScenes() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; - - /** - * Fetches a JSON package with firmware information for the hub. - * - * @return FirmwareVersions class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - public abstract HubFirmware getFirmwareVersions() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; - - /** - * Fetches a JSON package with user data information for the hub. - * - * @return {@link UserData} class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - public abstract UserData getUserData() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades, null, null); + try { + Shades shades = gson.fromJson(json, Shades.class); + if (shades == null) { + throw new HubInvalidResponseException("Missing shades response"); + } + List shadeData = shades.shadeData; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); + } + return shades; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shades response", e); + } + } /** * Instructs the hub to move a specific shade @@ -342,8 +243,31 @@ public abstract UserData getUserData() * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; + public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeMove(position)); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException { + try { + Shade shade = gson.fromJson(json, Shade.class); + if (shade == null) { + throw new HubInvalidResponseException("Missing shade response"); + } + ShadeData shadeData = shade.shade; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shade.shade' element"); + } + if (Boolean.TRUE.equals(shadeData.timedOut)) { + throw new HubShadeTimeoutException("Timeout when sending request to the shade"); + } + return shadeData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shade response", e); + } + } /** * Instructs the hub to stop movement of a specific shade @@ -355,8 +279,12 @@ public abstract ShadeData moveShade(int shadeId, ShadePosition position) throws * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException; + public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeStop()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } /** * Instructs the hub to jog a specific shade @@ -368,8 +296,12 @@ public abstract ShadeData stopShade(int shadeId) throws HubInvalidResponseExcept * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException; + public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeJog()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } /** * Instructs the hub to calibrate a specific shade @@ -381,8 +313,38 @@ public abstract ShadeData jogShade(int shadeId) throws HubInvalidResponseExcepti * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException; + public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeCalibrate()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Fetches a JSON package that describes all scenes in the hub, and wraps it in + * a Scenes class instance + * + * @return Scenes class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scenes, null, null); + try { + Scenes scenes = gson.fromJson(json, Scenes.class); + if (scenes == null) { + throw new HubInvalidResponseException("Missing scenes response"); + } + List sceneData = scenes.sceneData; + if (sceneData == null) { + throw new HubInvalidResponseException("Missing 'scenes.sceneData' element"); + } + return scenes; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scenes response", e); + } + } /** * Instructs the hub to execute a specific scene @@ -391,7 +353,9 @@ public abstract ShadeData calibrateShade(int shadeId) throws HubInvalidResponseE * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException; + public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); + } /** * Fetches a JSON package that describes all scene collections in the hub, and wraps it in @@ -402,8 +366,23 @@ public abstract ShadeData calibrateShade(int shadeId) throws HubInvalidResponseE * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract SceneCollections getSceneCollections() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public SceneCollections getSceneCollections() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, sceneCollections, null, null); + try { + SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class); + if (sceneCollections == null) { + throw new HubInvalidResponseException("Missing sceneCollections response"); + } + List sceneCollectionData = sceneCollections.sceneCollectionData; + if (sceneCollectionData == null) { + throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element"); + } + return sceneCollections; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing sceneCollections response", e); + } + } /** * Instructs the hub to execute a specific scene collection @@ -412,8 +391,10 @@ public abstract SceneCollections getSceneCollections() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract void activateSceneCollection(int sceneCollectionId) - throws HubProcessingException, HubMaintenanceException; + public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneCollectionActivate, + Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null); + } /** * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in @@ -424,8 +405,23 @@ public abstract void activateSceneCollection(int sceneCollectionId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract ScheduledEvents getScheduledEvents() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public ScheduledEvents getScheduledEvents() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scheduledEvents, null, null); + try { + ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class); + if (scheduledEvents == null) { + throw new HubInvalidResponseException("Missing scheduledEvents response"); + } + List scheduledEventData = scheduledEvents.scheduledEventData; + if (scheduledEventData == null) { + throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element"); + } + return scheduledEvents; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scheduledEvents response", e); + } + } /** * Enables or disables a scheduled event in the hub. @@ -436,8 +432,23 @@ public abstract ScheduledEvents getScheduledEvents() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract void enableScheduledEvent(int scheduledEventId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public void enableScheduledEvent(int scheduledEventId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String uri = scheduledEvents + "/" + scheduledEventId; + String jsonResponse = invoke(HttpMethod.GET, uri, null, null); + try { + JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject(); + JsonElement scheduledEventElement = jsonObject.get("scheduledEvent"); + if (scheduledEventElement == null) { + throw new HubInvalidResponseException("Missing 'scheduledEvent' element"); + } + JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject(); + scheduledEventObject.addProperty("enabled", enable); + invoke(HttpMethod.PUT, uri, null, jsonObject.toString()); + } catch (JsonParseException | IllegalStateException e) { + throw new HubInvalidResponseException("Error parsing scheduledEvent response", e); + } + } /** * Fetches a JSON package that describes all repeaters in the hub, and wraps it in @@ -448,8 +459,23 @@ public abstract void enableScheduledEvent(int scheduledEventId, boolean enable) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract Repeaters getRepeaters() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public Repeaters getRepeaters() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, repeaters, null, null); + try { + Repeaters repeaters = gson.fromJson(json, Repeaters.class); + if (repeaters == null) { + throw new HubInvalidResponseException("Missing repeaters response"); + } + List repeaterData = repeaters.repeaterData; + if (repeaterData == null) { + throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element"); + } + return repeaters; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing repeaters response", e); + } + } /** * Fetches a JSON package that describes a specific repeater in the hub, and wraps it @@ -461,8 +487,27 @@ public abstract Repeaters getRepeaters() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract RepeaterData getRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public RepeaterData getRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null); + return repeaterDataFromJson(jsonResponse); + } + + private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException { + try { + Repeater repeater = gson.fromJson(json, Repeater.class); + if (repeater == null) { + throw new HubInvalidResponseException("Missing repeater response"); + } + RepeaterData repeaterData = repeater.repeater; + if (repeaterData == null) { + throw new HubInvalidResponseException("Missing 'repeater.repeater' element"); + } + return repeaterData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing repeater response", e); + } + } /** * Instructs the hub to identify a specific repeater by blinking @@ -473,8 +518,12 @@ public abstract RepeaterData getRepeater(int repeaterId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract RepeaterData identifyRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public RepeaterData identifyRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId, + Query.of("identify", Boolean.toString(true)), null); + return repeaterDataFromJson(jsonResponse); + } /** * Enables or disables blinking for a repeater @@ -486,8 +535,12 @@ public abstract RepeaterData identifyRepeater(int repeaterId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable)); + String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); + return repeaterDataFromJson(jsonResponse); + } /** * Sets color and brightness for a repeater @@ -498,8 +551,78 @@ public abstract RepeaterData enableRepeaterBlinking(int repeaterId, boolean enab * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract RepeaterData setRepeaterColor(int repeaterId, Color color) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public RepeaterData setRepeaterColor(int repeaterId, Color color) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color)); + String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); + return repeaterDataFromJson(jsonResponse); + } + + /** + * Invoke a call on the hub server to retrieve information or send a command + * + * @param method GET or PUT + * @param url the host url to be called + * @param query the http query parameter + * @param jsonCommand the request command content (as a json string) + * @return the response content (as a json string) + * @throws HubMaintenanceException + * @throws HubProcessingException + */ + private synchronized String invoke(HttpMethod method, String url, @Nullable Query query, + @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { + if (logger.isTraceEnabled()) { + if (query != null) { + logger.trace("API command {} {}{}", method, url, query); + } else { + logger.trace("API command {} {}", method, url); + } + if (jsonCommand != null) { + logger.trace("JSON command = {}", jsonCommand); + } + } + Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); + if (query != null) { + request.param(query.getKey(), query.getValue()); + } + if (jsonCommand != null) { + request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); + } + ContentResponse response; + try { + response = request.send(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } catch (TimeoutException | ExecutionException e) { + if (Instant.now().isBefore(maintenanceScheduledEnd)) { + // throw "softer" exception during maintenance window + logger.debug("Hub still undergoing maintenance"); + throw new HubMaintenanceException("Hub still undergoing maintenance"); + } + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } + int statusCode = response.getStatus(); + if (statusCode == HttpStatus.LOCKED_423) { + // set end of maintenance window, and throw a "softer" exception + maintenanceScheduledEnd = Instant.now().plus(maintenancePeriod); + logger.debug("Hub undergoing maintenance"); + throw new HubMaintenanceException("Hub undergoing maintenance"); + } + if (statusCode != HttpStatus.OK_200) { + logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); + throw new HubProcessingException(String.format("HTTP %d error", statusCode)); + } + String jsonResponse = response.getContentAsString(); + if (logger.isTraceEnabled()) { + logger.trace("JSON response = {}", jsonResponse); + } + if (jsonResponse == null || jsonResponse.isEmpty()) { + logger.warn("Hub returned no content"); + throw new HubProcessingException("Missing response entity"); + } + return jsonResponse; + } /** * Fetches a JSON package that describes a specific shade in the hub, and wraps it @@ -512,8 +635,11 @@ public abstract RepeaterData setRepeaterColor(int repeaterId, Color color) * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException; + public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); + return shadeDataFromJson(jsonResponse); + } /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -527,8 +653,12 @@ public abstract ShadeData getShade(int shadeId) throws HubInvalidResponseExcepti * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData refreshShadePosition(int shadeId) - throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; + public ShadeData refreshShadePosition(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("refresh", Boolean.toString(true)), null); + return shadeDataFromJson(jsonResponse); + } /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -542,8 +672,24 @@ public abstract ShadeData refreshShadePosition(int shadeId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract List getShadeSurvey(int shadeId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public List getShadeSurvey(int shadeId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("survey", Boolean.toString(true)), null); + try { + Survey survey = gson.fromJson(jsonResponse, Survey.class); + if (survey == null) { + throw new HubInvalidResponseException("Missing survey response"); + } + List surveyData = survey.surveyData; + if (surveyData == null) { + throw new HubInvalidResponseException("Missing 'survey.surveyData' element"); + } + return surveyData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing survey response", e); + } + } /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -557,6 +703,10 @@ public abstract List getShadeSurvey(int shadeId) * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; + public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("updateBatteryLevel", Boolean.toString(true)), null); + return shadeDataFromJson(jsonResponse); + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java deleted file mode 100644 index 5a47ce2b46b0a..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java +++ /dev/null @@ -1,629 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal._v1; - -import java.util.List; - -import javax.ws.rs.client.ClientBuilder; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.http.HttpMethod; -import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; -import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.Shade; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Survey; -import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; -import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; - -/** - * JAX-RS targets for communicating with an HD PowerView hub Generation 1/2 - * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Added support for secondary rail positions - * @author Jacob Laursen - Added support for scene groups and automations - */ -@NonNullByDefault -public class HDPowerViewWebTargetsV1 extends HDPowerViewWebTargets { - - private final String firmwareVersion; - private final String shades; - private final String sceneActivate; - private final String scenes; - private final String sceneCollectionActivate; - private final String sceneCollections; - private final String scheduledEvents; - private final String repeaters; - private final String userData; - - /** - * Initialize the web targets - * - * @param httpClient the HTTP client (the binding) - * @param ipAddress the IP address of the server (the hub) - */ - public HDPowerViewWebTargetsV1(HttpClient httpClient, ClientBuilder clientBuilder, - SseEventSourceFactory eventSourceFactory, String ipAddress) { - super(httpClient, clientBuilder, eventSourceFactory, ipAddress, SceneV1.class, ShadeDataV1.class, - ShadePositionV1.class, ScheduledEventV1.class); - - // initialize the urls - String base = "http://" + ipAddress + "/api/"; - shades = base + "shades/"; - firmwareVersion = base + "fwversion"; - sceneActivate = base + "scenes"; - scenes = base + "scenes/"; - - // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections". - sceneCollectionActivate = base + "scenecollections"; - sceneCollections = base + "scenecollections/"; - - scheduledEvents = base + "scheduledevents"; - repeaters = base + "repeaters/"; - userData = base + "userdata"; - } - - /** - * Protected method to create ShadeData instances from a JSON payload. - * - * @param json the json payload - * @return a ShadeData instance - * @throws HubInvalidResponseException in case od missing or invalid response - * @throws HubShadeTimeoutException in case of connection time out (in V1 implementation) - */ - protected ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException { - try { - Shade shade = gson.fromJson(json, Shade.class); - if (shade == null) { - throw new HubInvalidResponseException("Missing shade response"); - } - ShadeData shadeData = shade.shade; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shade.shade' element"); - } - if (shadeData.version() == 1 && Boolean.TRUE.equals(((ShadeDataV1) shadeData).timedOut)) { - throw new HubShadeTimeoutException("Timeout when sending request to the shade"); - } - return shadeData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shade response", e); - } - } - - /** - * Fetches a JSON package with firmware information for the hub. - * - * @return FirmwareVersions class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public HubFirmware getFirmwareVersions() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, firmwareVersion, null, null); - try { - FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class); - if (firmwareVersion == null) { - throw new HubInvalidResponseException("Missing firmware response"); - } - HubFirmware firmwareVersions = firmwareVersion.firmware; - if (firmwareVersions == null) { - throw new HubInvalidResponseException("Missing 'firmware' element"); - } - return firmwareVersions; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing firmware response", e); - } - } - - /** - * Fetches a JSON package with user data information for the hub. - * - * @return {@link UserData} class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, userData, null, null); - try { - UserDataResponse userDataResponse = gson.fromJson(json, UserDataResponse.class); - if (userDataResponse == null) { - throw new HubInvalidResponseException("Missing userData response"); - } - UserData userData = userDataResponse.userData; - if (userData == null) { - throw new HubInvalidResponseException("Missing 'userData' element"); - } - return userData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing userData response", e); - } - } - - /** - * Fetches a JSON package that describes all shades in the hub, and wraps it in - * a Shades class instance - * - * @return Shades class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, shades, null, null); - try { - Shades shades = gson.fromJson(json, Shades.class); - if (shades == null) { - throw new HubInvalidResponseException("Missing shades response"); - } - List shadeData = shades.shadeData; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); - } - return shades; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shades response", e); - } - } - - /** - * Instructs the hub to move a specific shade - * - * @param shadeId id of the shade to be moved - * @param position instance of ShadePosition containing the new position - * @return ShadeData class instance (with new position) - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeMove(position)); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to stop movement of a specific shade - * - * @param shadeId id of the shade to be stopped - * @return ShadeData class instance (new position cannot be relied upon) - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeStop()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to jog a specific shade - * - * @param shadeId id of the shade to be jogged - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeJog()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to calibrate a specific shade - * - * @param shadeId id of the shade to be calibrated - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeCalibrate()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Fetches a JSON package that describes all scenes in the hub, and wraps it in - * a Scenes class instance - * - * @return Scenes class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, scenes, null, null); - try { - Scenes scenes = gson.fromJson(json, Scenes.class); - if (scenes == null) { - throw new HubInvalidResponseException("Missing scenes response"); - } - List sceneData = scenes.sceneData; - if (sceneData == null) { - throw new HubInvalidResponseException("Missing 'scenes.sceneData' element"); - } - return scenes; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scenes response", e); - } - } - - /** - * Instructs the hub to execute a specific scene - * - * @param sceneId id of the scene to be executed - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); - } - - /** - * Fetches a JSON package that describes all scene collections in the hub, and wraps it in - * a SceneCollections class instance - * - * @return SceneCollections class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public SceneCollections getSceneCollections() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, sceneCollections, null, null); - try { - SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class); - if (sceneCollections == null) { - throw new HubInvalidResponseException("Missing sceneCollections response"); - } - List sceneCollectionData = sceneCollections.sceneCollectionData; - if (sceneCollectionData == null) { - throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element"); - } - return sceneCollections; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing sceneCollections response", e); - } - } - - /** - * Instructs the hub to execute a specific scene collection - * - * @param sceneCollectionId id of the scene collection to be executed - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.GET, sceneCollectionActivate, - Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null); - } - - /** - * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in - * a ScheduledEvents class instance - * - * @return ScheduledEvents class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public ScheduledEvents getScheduledEvents() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, scheduledEvents, null, null); - try { - ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class); - if (scheduledEvents == null) { - throw new HubInvalidResponseException("Missing scheduledEvents response"); - } - List scheduledEventData = scheduledEvents.scheduledEventData; - if (scheduledEventData == null) { - throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element"); - } - return scheduledEvents; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scheduledEvents response", e); - } - } - - /** - * Enables or disables a scheduled event in the hub. - * - * @param scheduledEventId id of the scheduled event to be enabled or disabled - * @param enable true to enable scheduled event, false to disable - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public void enableScheduledEvent(int scheduledEventId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String uri = scheduledEvents + "/" + scheduledEventId; - String jsonResponse = invoke(HttpMethod.GET, uri, null, null); - try { - JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject(); - JsonElement scheduledEventElement = jsonObject.get("scheduledEvent"); - if (scheduledEventElement == null) { - throw new HubInvalidResponseException("Missing 'scheduledEvent' element"); - } - JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject(); - scheduledEventObject.addProperty("enabled", enable); - invoke(HttpMethod.PUT, uri, null, jsonObject.toString()); - } catch (JsonParseException | IllegalStateException e) { - throw new HubInvalidResponseException("Error parsing scheduledEvent response", e); - } - } - - /** - * Fetches a JSON package that describes all repeaters in the hub, and wraps it in - * a Repeaters class instance - * - * @return Repeaters class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public Repeaters getRepeaters() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, repeaters, null, null); - try { - Repeaters repeaters = gson.fromJson(json, Repeaters.class); - if (repeaters == null) { - throw new HubInvalidResponseException("Missing repeaters response"); - } - List repeaterData = repeaters.repeaterData; - if (repeaterData == null) { - throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element"); - } - return repeaters; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing repeaters response", e); - } - } - - /** - * Fetches a JSON package that describes a specific repeater in the hub, and wraps it - * in a RepeaterData class instance - * - * @param repeaterId id of the repeater to be fetched - * @return RepeaterData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public RepeaterData getRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null); - return repeaterDataFromJson(jsonResponse); - } - - private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException { - try { - Repeater repeater = gson.fromJson(json, Repeater.class); - if (repeater == null) { - throw new HubInvalidResponseException("Missing repeater response"); - } - RepeaterData repeaterData = repeater.repeater; - if (repeaterData == null) { - throw new HubInvalidResponseException("Missing 'repeater.repeater' element"); - } - return repeaterData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing repeater response", e); - } - } - - /** - * Instructs the hub to identify a specific repeater by blinking - * - * @param repeaterId id of the repeater to be identified - * @return RepeaterData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public RepeaterData identifyRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId, - Query.of("identify", Boolean.toString(true)), null); - return repeaterDataFromJson(jsonResponse); - } - - /** - * Enables or disables blinking for a repeater - * - * @param repeaterId id of the repeater for which to be enable or disable blinking - * @param enable true to enable blinking, false to disable - * @return RepeaterData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable)); - String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); - return repeaterDataFromJson(jsonResponse); - } - - /** - * Sets color and brightness for a repeater - * - * @param repeaterId id of the repeater for which to set color and brightness - * @return RepeaterData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public RepeaterData setRepeaterColor(int repeaterId, Color color) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color)); - String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); - return repeaterDataFromJson(jsonResponse); - } - - /** - * Fetches a JSON package that describes a specific shade in the hub, and wraps it - * in a Shade class instance - * - * @param shadeId id of the shade to be fetched - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on - * a specific shade's position; fetches a JSON package that describes that shade, - * and wraps it in a Shade class instance - * - * @param shadeId id of the shade to be refreshed - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData refreshShadePosition(int shadeId) - throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("refresh", Boolean.toString(true)), null); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on - * a specific shade's survey data, which will also refresh signal strength; - * fetches a JSON package that describes that survey, and wraps it in a Survey - * class instance - * - * @param shadeId id of the shade to be surveyed - * @return List of SurveyData class instances - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public List getShadeSurvey(int shadeId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("survey", Boolean.toString(true)), null); - try { - Survey survey = gson.fromJson(jsonResponse, Survey.class); - if (survey == null) { - throw new HubInvalidResponseException("Missing survey response"); - } - List surveyData = survey.surveyData; - if (surveyData == null) { - throw new HubInvalidResponseException("Missing 'survey.surveyData' element"); - } - return surveyData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing survey response", e); - } - } - - /** - * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on - * a specific shade's battery level; fetches a JSON package that describes that shade, - * and wraps it in a Shade class instance - * - * @param shadeId id of the shade to be refreshed - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("updateBatteryLevel", Boolean.toString(true)), null); - return shadeDataFromJson(jsonResponse); - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java deleted file mode 100644 index c803f4e2c25cc..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java +++ /dev/null @@ -1,552 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal._v3; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; - -import javax.net.ssl.SSLContext; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.sse.InboundSseEvent; -import javax.ws.rs.sse.SseEventSource; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.http.HttpMethod; -import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadePositions; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.Shade; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.InfoV3; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.SceneV3; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.ScheduledEventV3; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.SseSceneV3; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.SseShadeV3; -import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonParseException; -import com.google.gson.reflect.TypeToken; - -/** - * JAX-RS targets for communicating with an HD PowerView hub Generation 3 - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class HDPowerViewWebTargetsV3 extends HDPowerViewWebTargets { - - private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargetsV3.class); - - private static final String IDS = "ids"; - - /** - * Simple DTO for registering the binding with the hub. - * - * @author AndrewFG - Initial contribution - */ - @SuppressWarnings("unused") - private static class GatewayRegistration { - public String todo = "org.openhab.binding.hdpowerview"; // TODO - } - - private final String shades; - private final String scenes; - private final String sceneActivate; - private final String shadeMotion; - private final String shadeStop; - private final String shadePositions; - private final String firmware; - private final String automations; - private final String register; - private final String shadeEvents; - private final String sceneEvents; - - // @formatter:off - private final Type shadeListType = new TypeToken>() {}.getType(); - // @formatter:on - - // @formatter:off - private final Type sceneListType = new TypeToken>() {}.getType(); - // @formatter:on - - // @formatter:off - private final Type scheduledEventListType = - new TypeToken>() {}.getType(); - // @formatter:on - - private boolean isRegistered; - private @Nullable SseSinkV3 sseSink; - private @Nullable SseEventSource shadeEventSource; - private @Nullable SseEventSource sceneEventSource; - - /** - * Initialize the web targets - * - * @param httpClient the HTTP client (the binding) - * @param ipAddress the IP address of the server (the hub) - */ - public HDPowerViewWebTargetsV3(HttpClient httpClient, ClientBuilder clientBuilder, - SseEventSourceFactory eventSourceFactory, String ipAddress) { - super(httpClient, clientBuilder, eventSourceFactory, ipAddress, SceneV3.class, ShadeDataV3.class, - ShadePositionV3.class, ScheduledEventV3.class); - - // initialize the urls - String base = "http://" + ipAddress + "/"; - - String home = base + "home/"; - shades = home + "shades"; - scenes = home + "scenes"; - sceneActivate = home + "scenes/%d/activate"; - shadeMotion = home + "shades/%d/motion"; - shadeStop = home + "shades/stop"; - shadePositions = home + "shades/positions"; - automations = home + "automations"; - shadeEvents = home + "shades/events"; - sceneEvents = home + "scenes/events"; - - String gateway = base + "gateway/"; - firmware = gateway + "info"; - register = gateway + "TBD"; // TODO - } - - @Override - public HubFirmware getFirmwareVersions() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, firmware, null, null); - return toFirmware(json); - } - - @Override - public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getUserData(): method not implemented"); - } - - @Override - public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, shades, null, null); - return toShades(json); - } - - @Override - public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), - gson.toJson(new ShadePositions(position))); - return getShade(shadeId); - } - - @Override - public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - invoke(HttpMethod.PUT, shadeStop, Query.of(IDS, Integer.valueOf(shadeId).toString()), null); - return getShade(shadeId); - } - - @Override - public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.JOG)); - invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); - return getShade(shadeId); - } - - @Override - public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.CALIBRATE)); - invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); - return getShade(shadeId); - } - - @Override - public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, this.scenes, null, null); - return toScenes(json); - } - - @Override - public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.PUT, String.format(sceneActivate, sceneId), null, null); - } - - @Override - public SceneCollections getSceneCollections() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getSceneCollections(): method not implemented"); - } - - @Override - public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("activateSceneCollection(): method not implemented"); - } - - @Override - public ScheduledEvents getScheduledEvents() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, automations, null, null); - return toScheduledEvents(json); - } - - @Override - public void enableScheduledEvent(int scheduledEventId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("enableScheduledEvent(): method not implemented"); - } - - @Override - public Repeaters getRepeaters() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - return new Repeaters(); - } - - @Override - public RepeaterData getRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getRepeater(): method not implemented"); - } - - @Override - public RepeaterData identifyRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("identifyRepeater(): method not implemented"); - } - - @Override - public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("enableRepeaterBlinking(): method not implemented"); - } - - @Override - public RepeaterData setRepeaterColor(int repeaterId, Color color) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("setRepeaterColor(): method not implemented"); - } - - @Override - public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); - return toShadeData(json); - } - - @Override - public ShadeData refreshShadePosition(int shadeId) - throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - return getShade(shadeId); - } - - @Override - public List getShadeSurvey(int shadeId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getShadeSurvey(): method not implemented"); - } - - @Override - public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - return getShade(shadeId); - } - - /** - * Register the binding with the hub (if not already registered). - * - * @throws HubProcessingException - * @throws HubMaintenanceException - */ - private void gatewayRegister() throws HubMaintenanceException, HubProcessingException { - if (!isRegistered) { - String json = gson.toJson(new GatewayRegistration()); - invoke(HttpMethod.PUT, register, null, json); - isRegistered = true; - } - } - - /** - * Set the sink for SSE events. - * - * @param sseSink a class that implements the SseSinkV3 interface. - * @return true if registered for SSE events. - * @throws HubProcessingException - * @throws HubMaintenanceException - */ - public boolean sseSubscribe(@Nullable SseSinkV3 sseSink) throws HubMaintenanceException, HubProcessingException { - SseEventSource source; - - this.sseSink = sseSink; - - if (sseSink != null) { - // register ourself with the gateway (if necessary) - gatewayRegister(); - - // open SSE channel for shades - SseEventSource shadeEventSource = this.shadeEventSource; - if (shadeEventSource == null || !shadeEventSource.isOpen()) { - SSLContext context = httpClient.getSslContextFactory().getSslContext(); - WebTarget target = clientBuilder.sslContext(context).build().target(shadeEvents); - source = eventSourceFactory.newSource(target); - source.register((event) -> onShadeEvent(event)); - source.open(); - this.shadeEventSource = source; - } - - // open SSE channel for scenes - SseEventSource sceneEventSource = this.sceneEventSource; - if (sceneEventSource == null || !sceneEventSource.isOpen()) { - SSLContext context = httpClient.getSslContextFactory().getSslContext(); - WebTarget target = clientBuilder.sslContext(context).build().target(sceneEvents); - source = eventSourceFactory.newSource(target); - source.register((event) -> onSceneEvent(event)); - source.open(); - this.sceneEventSource = source; - } - return true; - } - - // close SSE channel for shades - source = shadeEventSource; - if (source != null) { - source.close(); - shadeEventSource = null; - } - - // close SSE channel for scenes - source = sceneEventSource; - if (source != null) { - source.close(); - sceneEventSource = null; - } - return false; - } - - /** - * Handle inbound SSE events for a shade. - * - * @param sseEvent the inbound event - */ - private void onShadeEvent(InboundSseEvent sseEvent) { - SseSinkV3 eventSink = this.sseSink; - if (eventSink != null) { - String json = sseEvent.readData(); - logger.trace("onShadeEvent(): {}", json); - try { - ShadeData shadeData = toShadeData2(json); - ShadePosition shadePosition = shadeData.positions; - if (shadePosition != null) { - String evt = toEvt(json); - eventSink.sseShade(evt, shadeData.id, shadePosition); - } - } catch (HubInvalidResponseException e) { - // swallow the exception; don't pass back to caller - } - } - } - - /** - * Handle inbound SSE events for a scene. - * - * @param sseEvent the inbound event - */ - private void onSceneEvent(InboundSseEvent sseEvent) { - SseSinkV3 eventSink = this.sseSink; - if (eventSink != null) { - String json = sseEvent.readData(); - logger.trace("onSceneEvent(): {}", json); - try { - Scene scene = toScene(json); - String evt = toEvt(json); - eventSink.sseScene(evt, scene.id); - } catch (HubInvalidResponseException e) { - // swallow the exception; don't pass back to caller - } - } - } - - /** - * Create ShadeData instance from a JSON payload. - * - * @param json the json payload - * @return a ShadeData instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private ShadeData toShadeData(String json) throws HubInvalidResponseException { - try { - Shade shade = gson.fromJson(json, Shade.class); - if (shade == null) { - throw new HubInvalidResponseException("Missing shade response"); - } - ShadeData shadeData = shade.shade; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shade.shade' element"); - } - return shadeData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shade response", e); - } - } - - /** - * Create Shades instance from a JSON payload. - * - * @param json the json payload - * @return a Shades instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private Shades toShades(String json) throws HubInvalidResponseException { - try { - Shades shades = new Shades(); - shades.shadeData = gson.fromJson(json, shadeListType); - if (shades.shadeData == null) { - throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); - } - return shades; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shades response", e); - } - } - - /** - * Create HubFirmware instance from a JSON payload. - * - * @param json the json payload - * @return a HubFirmware instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private HubFirmware toFirmware(String json) throws HubProcessingException { - try { - InfoV3 gatewayInfo = gson.fromJson(json, InfoV3.class); - if (gatewayInfo == null) { - throw new HubProcessingException("getFirmwareVersions(): missing gatewayInfo"); - } - return gatewayInfo.toHubFirmware(); - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing gateway info response", e); - } - } - - /** - * Create Scenes instance from a JSON payload. - * - * @param json the json payload - * @return a Scenes instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private Scenes toScenes(String json) throws HubInvalidResponseException { - try { - Scenes scenes = new Scenes(); - scenes.sceneData = gson.fromJson(json, sceneListType); - return scenes; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scenes response", e); - } - } - - /** - * Create ScheduledEvents instance from a JSON payload. - * - * @param json the json payload - * @return a ScheduledEvents instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private ScheduledEvents toScheduledEvents(String json) throws HubInvalidResponseException { - try { - ScheduledEvents scheduledEvents = new ScheduledEvents(); - scheduledEvents.scheduledEventData = gson.fromJson(json, scheduledEventListType); - return scheduledEvents; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing automation response", e); - } - } - - /** - * Get the 'evt' message from an event JSON payload. - * - * @param json the json payload - * @return the message - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private String toEvt(String json) throws HubInvalidResponseException { - SseShadeV3 sseShade = gson.fromJson(json, SseShadeV3.class); - if (sseShade != null) { - String evt = sseShade.evt; - if (evt != null) { - return evt; - } - } - throw new HubInvalidResponseException("Error parsing event"); - } - - /** - * Create ShadeData instance from a JSON shade event payload. - * - * @param json the json payload - * @return a ShadeData instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private ShadeData toShadeData2(String json) throws HubInvalidResponseException { - SseShadeV3 sseShade = gson.fromJson(json, SseShadeV3.class); - if (sseShade != null) { - ShadePosition shadePosition = sseShade.currentPositions; - if (shadePosition != null) { - ShadeData shadeData = new ShadeDataV3(); - shadeData.id = sseShade.id; - shadeData.positions = sseShade.currentPositions; - return shadeData; - } - } - throw new HubInvalidResponseException("Error parsing shade event"); - } - - /** - * Create Scene instance from a JSON scene event payload. - * - * @param json the json payload - * @return a Scene instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private Scene toScene(String json) throws HubInvalidResponseException { - SseSceneV3 sceneEvent = gson.fromJson(json, SseSceneV3.class); - if (sceneEvent != null) { - Scene scene = sceneEvent.scene; - if (scene != null) { - return scene; - } - } - throw new HubInvalidResponseException("Error parsing scene event"); - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java deleted file mode 100644 index bf827dfe17580..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal._v3; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; - -/** - * Interface for receiving SSE events from Generation 3 hubs. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public interface SseSinkV3 { - - /** - * Method that is called when a shade changes state. - * - * @param evt contents of json evt element. - * @param shadeId the id of the shade that changed. - * @param shadePosition the shade's new position. - */ - public void sseShade(String evt, int shadeId, ShadePosition shadePosition); - - /** - * Method that is called when a scene changes state. - * - * @param evt contents of json 'evt' element. - * @param sceneId the id of the scene that changed. - */ - public void sseScene(String evt, int sceneId); -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java deleted file mode 100644 index 4d240e7aec367..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api; - -import java.util.Base64; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Abstract class for state of a Shade as returned by an HD PowerView hub. - * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Refactored into separate class - */ -@NonNullByDefault -public abstract class ShadeData { - // fields common to Generation 1/2 and 3 hubs - public int id; - public @Nullable String name; - public int roomId; - public int type; - public int batteryStatus; - public @Nullable ShadePosition positions; - public int signalStrength; - public @Nullable Integer capabilities; - public @Nullable Firmware firmware; - - public String getName() { - return new String(Base64.getDecoder().decode(name)); - } - - public abstract BatteryKind getBatteryKind(); - - public abstract int version(); -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java index 08a0ec1a2308d..9b66e798bbcbc 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java @@ -12,17 +12,45 @@ */ package org.openhab.binding.hdpowerview.internal.api; +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Abstract class for position of a Shade as returned by an HD PowerView hub. + * The position of a single shade, as returned by the HD PowerView hub * - * @author Andrew Fiddian-Green - Initial contribution + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Added support for secondary rail positions */ @NonNullByDefault -public abstract class ShadePosition { +public class ShadePosition { + + private final transient Logger logger = LoggerFactory.getLogger(ShadePosition.class); + + /** + * Primary actuator position. + */ + private int posKind1; + private int position1; + + /** + * Secondary actuator position. + * + * Here we have to use Integer objects rather than just int primitives because these are secondary optional position + * elements in the JSON payload, so the GSON de-serializer might leave them as null. + */ + private @Nullable Integer posKind2 = null; + private @Nullable Integer position2 = null; + + public ShadePosition() { + } /** * Get the shade's State for the given actuator class resp. coordinate system. @@ -31,14 +59,250 @@ public abstract class ShadePosition { * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. * @return the current state. */ - public abstract State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords); + public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + State result = getPosition1(shadeCapabilities, posKindCoords); + if (result == UnDefType.UNDEF) { + result = getPosition2(shadeCapabilities, posKindCoords); + } + logger.trace("getState(): capabilities={}, coords={} => result={}", shadeCapabilities, posKindCoords, result); + return result; + } + + /** + * Set the shade's position1 value for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + */ + private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percentArg) { + int percent = percentArg; + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { + // on dual rail shades constrain percent to not move the lower rail above the upper + State secondary = getState(shadeCapabilities, SECONDARY_POSITION); + if (secondary instanceof PercentType) { + int secPercent = ((PercentType) secondary).intValue(); + if (percent < secPercent) { + percent = secPercent; + } + } + } + posKind1 = posKindCoords.ordinal(); + position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); + break; + + case SECONDARY_POSITION: + /* + * Secondary, blackout shade a 'Duolite' shade: => INVERTED + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + posKind1 = posKindCoords.ordinal(); + if (shadeCapabilities.supportsSecondaryOverlapped()) { + position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); + } else { + position1 = (int) Math.round((double) percent / 100 * MAX_SHADE); + } + break; + + case VANE_TILT_POSITION: + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + */ + posKind1 = posKindCoords.ordinal(); + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + position1 = (int) Math.round((double) percent / 100 * max); + break; + + default: + posKind1 = CoordinateSystem.NONE.ordinal(); + position1 = 0; + } + } + + /** + * Get the shade's position1 State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the State (or UNDEF if not available). + */ + private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (posKindCoords.equals(posKind1)) { + return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); + } + if (VANE_TILT_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { + return PercentType.HUNDRED; + } + if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { + return PercentType.HUNDRED; + } + break; + + case SECONDARY_POSITION: + /* + * Secondary, blackout shade a 'Duolite' shade: => INVERTED + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (posKindCoords.equals(posKind1)) { + if (shadeCapabilities.supportsSecondaryOverlapped()) { + return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); + } + return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100)); + } + if (!SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { + return PercentType.ZERO; + } + break; + + case VANE_TILT_POSITION: + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + * + * If the shades are not open, the vane position is undefined; if the the shades + * are exactly open then the vanes are at zero; otherwise return the actual vane + * position itself + * + * note: sometimes the hub may return a value of position1 > MAX_VANE (seems to + * be a bug in the hub) so we avoid an out of range exception via the Math.min() + * function below.. + */ + if (posKindCoords.equals(posKind1)) { + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + return new PercentType((int) Math.round((double) Math.min(position1, max) / max * 100)); + } + if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { + return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO; + } + if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped() + && shadeCapabilities.supportsTiltOnClosed()) { + return PercentType.HUNDRED; + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, return UNDEF + } + return UnDefType.UNDEF; + } + + /** + * Set the shade's position2 value for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + */ + private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percentArg) { + int percent = percentArg; + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + posKind2 = posKindCoords.ordinal(); + position2 = Integer.valueOf(MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE)); + break; + + case SECONDARY_POSITION: + /* + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { + // on dual rail shades constrain percent to not move the upper rail below the lower + State primary = getState(shadeCapabilities, PRIMARY_POSITION); + if (primary instanceof PercentType) { + int primaryPercent = ((PercentType) primary).intValue(); + if (percent > primaryPercent) { + percent = primaryPercent; + } + } + } + posKind2 = posKindCoords.ordinal(); + position2 = Integer.valueOf((int) Math.round((double) percent / 100 * MAX_SHADE)); + break; + + case VANE_TILT_POSITION: + posKind2 = posKindCoords.ordinal(); + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + position2 = Integer.valueOf((int) Math.round((double) percent / 100 * max)); + break; + + default: + posKind2 = null; + position2 = null; + } + } + + /** + * Get the shade's position2 State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the State (or UNDEF if not available). + */ + private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + Integer posKind2 = this.posKind2; + Integer position2 = this.position2; + + if (position2 == null || posKind2 == null) { + return UnDefType.UNDEF; + } + + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (posKindCoords.equals(posKind2)) { + return new PercentType(100 - (int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); + } + break; + + case SECONDARY_POSITION: + /* + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (posKindCoords.equals(posKind2)) { + return new PercentType((int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); + } + break; + + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + */ + case VANE_TILT_POSITION: + if (posKindCoords.equals(posKind2)) { + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + return new PercentType((int) Math.round((double) Math.min(position2.intValue(), max) / max * 100)); + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, return UNDEF + } + return UnDefType.UNDEF; + } /** * Detect if the ShadePosition has a posKindN value indicating potential support for a secondary rail. * * @return true if the ShadePosition supports a secondary rail. */ - public abstract boolean secondaryRailDetected(); + public boolean secondaryRailDetected() { + return SECONDARY_POSITION.equals(posKind1) || SECONDARY_POSITION.equals(posKind2); + } /** * Detect if the ShadePosition has both a posKindN value indicating potential support for tilt, AND a posKindN @@ -46,7 +310,10 @@ public abstract class ShadePosition { * * @return true if potential support for tilt anywhere functionality was detected. */ - public abstract boolean tiltAnywhereDetected(); + public boolean tiltAnywhereDetected() { + return ((PRIMARY_POSITION.equals(posKind1)) && (VANE_TILT_POSITION.equals(posKind2)) + || ((PRIMARY_POSITION.equals(posKind2) && (VANE_TILT_POSITION.equals(posKind1))))); + } /** * Set the shade's position for the given actuator class resp. coordinate system. @@ -56,6 +323,61 @@ public abstract class ShadePosition { * @param percent the new position value. * @return this object. */ - public abstract ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, - int percent); + public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + logger.trace("setPosition(): capabilities={}, coords={}, percent={}", shadeCapabilities, posKindCoords, + percent); + // if necessary swap the order of position1 and position2 + if (PRIMARY_POSITION.equals(posKind2) && !PRIMARY_POSITION.equals(posKind1)) { + final Integer posKind2Temp = posKind2; + final Integer position2Temp = position2; + posKind2 = Integer.valueOf(posKind1); + position2 = Integer.valueOf(position1); + posKind1 = posKind2Temp != null ? posKind2Temp.intValue() : NONE.ordinal(); + position1 = position2Temp != null ? position2Temp.intValue() : 0; + } + + // delete position2 if it has an invalid position kind + if (ERROR_UNKNOWN.equals(posKind2) || NONE.equals(posKind2)) { + posKind2 = null; + position2 = null; + } + + // logic to set either position1 or position2 + switch (posKindCoords) { + case PRIMARY_POSITION: + if (shadeCapabilities.supportsPrimary()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case SECONDARY_POSITION: + if (shadeCapabilities.supportsSecondary()) { + if (shadeCapabilities.supportsPrimary()) { + setPosition2(shadeCapabilities, posKindCoords, percent); + } else { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + } else if (shadeCapabilities.supportsSecondaryOverlapped()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case VANE_TILT_POSITION: + if (shadeCapabilities.supportsPrimary()) { + if (shadeCapabilities.supportsTiltOnClosed()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } else if (shadeCapabilities.supportsTiltAnywhere()) { + setPosition2(shadeCapabilities, posKindCoords, percent); + } + } else if (shadeCapabilities.supportsTiltAnywhere()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, do nothing + } + return this; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java deleted file mode 100644 index ebe97bed29f71..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v1; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; - -/** - * State of a Shade as returned by an HD PowerView hub of Generation 1 or 2. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class ShadeDataV1 extends ShadeData { - // fields for Generation 1/2 hubs only; NOTE: all these are Nullable - public int groupId; - public int order; - public double batteryStrength; - public boolean batteryIsLow; - public @Nullable Boolean timedOut; - public @Nullable Firmware motor; - // note: in old JSON batteryKind was a string but now it's a number; fortunately GSON string accepts either - public @Nullable String batteryKind; - - @Override - public BatteryKind getBatteryKind() { - return BatteryKind.fromString(batteryKind); - } - - @Override - public int version() { - return 1; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java deleted file mode 100644 index 3a7371f9587a7..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java +++ /dev/null @@ -1,387 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v1; - -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; -import org.openhab.core.library.types.PercentType; -import org.openhab.core.types.State; -import org.openhab.core.types.UnDefType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The position of a single shade, as returned by an HD PowerView hub of Generation 1/2 - * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Added support for secondary rail positions - */ -@NonNullByDefault -public class ShadePositionV1 extends ShadePosition { - - private final transient Logger logger = LoggerFactory.getLogger(ShadePositionV1.class); - - /** - * Primary actuator position. - */ - private int posKind1; - private int position1; - - /** - * Secondary actuator position. - * - * Here we have to use Integer objects rather than just int primitives because these are secondary optional position - * elements in the JSON payload, so the GSON de-serializer might leave them as null. - */ - private @Nullable Integer posKind2 = null; - private @Nullable Integer position2 = null; - - public ShadePositionV1() { - } - - /** - * Get the shade's State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the current state. - */ - @Override - public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - State result = getPosition1(shadeCapabilities, posKindCoords); - if (result == UnDefType.UNDEF) { - result = getPosition2(shadeCapabilities, posKindCoords); - } - logger.trace("getState(): capabilities={}, coords={} => result={}", shadeCapabilities, posKindCoords, result); - return result; - } - - /** - * Set the shade's position1 value for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - */ - private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { - // on dual rail shades constrain percent to not move the lower rail above the upper - State secondary = getState(shadeCapabilities, SECONDARY_POSITION); - if (secondary instanceof PercentType) { - int secPercent = ((PercentType) secondary).intValue(); - if (percent < secPercent) { - percent = secPercent; - } - } - } - posKind1 = posKindCoords.ordinal(); - position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); - break; - - case SECONDARY_POSITION: - /* - * Secondary, blackout shade a 'Duolite' shade: => INVERTED - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - posKind1 = posKindCoords.ordinal(); - if (shadeCapabilities.supportsSecondaryOverlapped()) { - position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); - } else { - position1 = (int) Math.round((double) percent / 100 * MAX_SHADE); - } - break; - - case VANE_TILT_POSITION: - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - */ - posKind1 = posKindCoords.ordinal(); - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - position1 = (int) Math.round((double) percent / 100 * max); - break; - - default: - posKind1 = CoordinateSystem.NONE.ordinal(); - position1 = 0; - } - } - - /** - * Get the shade's position1 State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the State (or UNDEF if not available). - */ - private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (posKindCoords.equals(posKind1)) { - return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); - } - if (VANE_TILT_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { - return PercentType.HUNDRED; - } - if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { - return PercentType.HUNDRED; - } - break; - - case SECONDARY_POSITION: - /* - * Secondary, blackout shade a 'Duolite' shade: => INVERTED - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (posKindCoords.equals(posKind1)) { - if (shadeCapabilities.supportsSecondaryOverlapped()) { - return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); - } - return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100)); - } - if (!SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { - return PercentType.ZERO; - } - break; - - case VANE_TILT_POSITION: - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - * - * If the shades are not open, the vane position is undefined; if the the shades - * are exactly open then the vanes are at zero; otherwise return the actual vane - * position itself - * - * note: sometimes the hub may return a value of position1 > MAX_VANE (seems to - * be a bug in the hub) so we avoid an out of range exception via the Math.min() - * function below.. - */ - if (posKindCoords.equals(posKind1)) { - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - return new PercentType((int) Math.round((double) Math.min(position1, max) / max * 100)); - } - if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { - return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO; - } - if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped() - && shadeCapabilities.supportsTiltOnClosed()) { - return PercentType.HUNDRED; - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, return UNDEF - } - return UnDefType.UNDEF; - } - - /** - * Set the shade's position2 value for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - */ - private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - posKind2 = posKindCoords.ordinal(); - position2 = Integer.valueOf(MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE)); - break; - - case SECONDARY_POSITION: - /* - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { - // on dual rail shades constrain percent to not move the upper rail below the lower - State primary = getState(shadeCapabilities, PRIMARY_POSITION); - if (primary instanceof PercentType) { - int primaryPercent = ((PercentType) primary).intValue(); - if (percent > primaryPercent) { - percent = primaryPercent; - } - } - } - posKind2 = posKindCoords.ordinal(); - position2 = Integer.valueOf((int) Math.round((double) percent / 100 * MAX_SHADE)); - break; - - case VANE_TILT_POSITION: - posKind2 = posKindCoords.ordinal(); - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - position2 = Integer.valueOf((int) Math.round((double) percent / 100 * max)); - break; - - default: - posKind2 = null; - position2 = null; - } - } - - /** - * Get the shade's position2 State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the State (or UNDEF if not available). - */ - private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - Integer posKind2 = this.posKind2; - Integer position2 = this.position2; - - if (position2 == null || posKind2 == null) { - return UnDefType.UNDEF; - } - - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (posKindCoords.equals(posKind2)) { - return new PercentType(100 - (int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); - } - break; - - case SECONDARY_POSITION: - /* - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (posKindCoords.equals(posKind2)) { - return new PercentType((int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); - } - break; - - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - */ - case VANE_TILT_POSITION: - if (posKindCoords.equals(posKind2)) { - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - return new PercentType((int) Math.round((double) Math.min(position2.intValue(), max) / max * 100)); - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, return UNDEF - } - return UnDefType.UNDEF; - } - - /** - * Detect if the ShadePosition has a posKindN value indicating potential support for a secondary rail. - * - * @return true if the ShadePosition supports a secondary rail. - */ - @Override - public boolean secondaryRailDetected() { - return SECONDARY_POSITION.equals(posKind1) || SECONDARY_POSITION.equals(posKind2); - } - - /** - * Detect if the ShadePosition has both a posKindN value indicating potential support for tilt, AND a posKindN - * indicating support for a primary rail. i.e. it potentially supports tilt anywhere functionality. - * - * @return true if potential support for tilt anywhere functionality was detected. - */ - @Override - public boolean tiltAnywhereDetected() { - return ((PRIMARY_POSITION.equals(posKind1)) && (VANE_TILT_POSITION.equals(posKind2)) - || ((PRIMARY_POSITION.equals(posKind2) && (VANE_TILT_POSITION.equals(posKind1))))); - } - - /** - * Set the shade's position for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - * @return this object. - */ - @Override - public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - logger.trace("setPosition(): capabilities={}, coords={}, percent={}", shadeCapabilities, posKindCoords, - percent); - // if necessary swap the order of position1 and position2 - if (PRIMARY_POSITION.equals(posKind2) && !PRIMARY_POSITION.equals(posKind1)) { - final Integer posKind2Temp = posKind2; - final Integer position2Temp = position2; - posKind2 = Integer.valueOf(posKind1); - position2 = Integer.valueOf(position1); - posKind1 = posKind2Temp != null ? posKind2Temp.intValue() : NONE.ordinal(); - position1 = position2Temp != null ? position2Temp.intValue() : 0; - } - - // delete position2 if it has an invalid position kind - if (ERROR_UNKNOWN.equals(posKind2) || NONE.equals(posKind2)) { - posKind2 = null; - position2 = null; - } - - // logic to set either position1 or position2 - switch (posKindCoords) { - case PRIMARY_POSITION: - if (shadeCapabilities.supportsPrimary()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case SECONDARY_POSITION: - if (shadeCapabilities.supportsSecondary()) { - if (shadeCapabilities.supportsPrimary()) { - setPosition2(shadeCapabilities, posKindCoords, percent); - } else { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - } else if (shadeCapabilities.supportsSecondaryOverlapped()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case VANE_TILT_POSITION: - if (shadeCapabilities.supportsPrimary()) { - if (shadeCapabilities.supportsTiltOnClosed()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } else if (shadeCapabilities.supportsTiltAnywhere()) { - setPosition2(shadeCapabilities, posKindCoords, percent); - } - } else if (shadeCapabilities.supportsTiltAnywhere()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, do nothing - } - return this; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java deleted file mode 100644 index ef6b3edde8b07..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v3; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; - -/** - * State of a Shade as returned by an HD PowerView hub of Generation 3. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class ShadeDataV3 extends ShadeData { - public @Nullable String ptName; - public @Nullable String powerType; - public @Nullable String bleName; - - @Override - public String getName() { - return String.join(" ", super.getName(), ptName); - } - - @Override - public BatteryKind getBatteryKind() { - // TODO the schema for powerType is not clear; is may be a string? or an integer? - String powerType = this.powerType; - if (powerType != null) { - try { - Integer powerTypeInt = Integer.valueOf(powerType); - switch (powerTypeInt) { - case 0: - return BatteryKind.BATTERY_WAND; - case 1: - return BatteryKind.HARDWIRED_POWER_SUPPLY; - case 2: - return BatteryKind.RECHARGEABLE_BATTERY_WAND; - } - } catch (NumberFormatException e) { - // fall through - } - } - return BatteryKind.ERROR_UNKNOWN; - } - - @Override - public int version() { - return 3; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java index 07b4c87e778a1..1c544c047e878 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java @@ -21,7 +21,7 @@ * @author Andy Lintner - Initial contribution */ @NonNullByDefault -public class ShadePositions { +class ShadePositions { public ShadePosition positions; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java deleted file mode 100644 index 4191c5bd96b95..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Scene object as returned by an HD PowerView hub - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public abstract class Scene implements Comparable { - // fields that are common to Generation 1/2 and 3 hubs - public int id; - public @Nullable String name; - - @Override - public abstract int compareTo(Scene other); - - public String getName() { - return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8); - } - - public abstract int version(); -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java index 4fa5a5fe4264a..5cb25a8aab0b4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.hdpowerview.internal.api.responses; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -27,4 +29,58 @@ public class Scenes { public @Nullable List sceneData; public @Nullable List sceneIds; + + /* + * the following SuppressWarnings annotation is because the Eclipse compiler + * does NOT expect a NonNullByDefault annotation on the inner class, since it is + * implicitly inherited from the outer class, whereas the Maven compiler always + * requires an explicit NonNullByDefault annotation on all classes + */ + @SuppressWarnings("null") + @NonNullByDefault + public static class Scene implements Comparable { + public int id; + public @Nullable String name; + public int roomId; + public int order; + public int colorId; + public int iconId; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof Scene)) { + return false; + } + Scene other = (Scene) o; + + return this.id == other.id && this.name.equals(other.name) && this.roomId == other.roomId + && this.order == other.order && this.colorId == other.colorId && this.iconId == other.iconId; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + (name == null ? 0 : name.hashCode()); + result = prime * result + roomId; + result = prime * result + order; + result = prime * result + colorId; + result = prime * result + iconId; + + return result; + } + + @Override + public int compareTo(Scene other) { + return Integer.compare(order, other.order); + } + + public String getName() { + return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8); + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java deleted file mode 100644 index 7033358730002..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses; - -import java.time.DayOfWeek; -import java.util.EnumSet; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * Abstract class for scheduled event as returned by an HD PowerView hub. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public abstract class ScheduledEvent { - // fields common to Generation 1/2 and 3 hubs - public int id; - public int type; - public boolean enabled; - public int hour; - public int minute; - public int sceneId; - - public abstract EnumSet getDays(); - - public abstract int getEventType(); - - public abstract int version(); -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java index 5a595cd206a25..e7ae6ac1ae440 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java @@ -38,4 +38,95 @@ public class ScheduledEvents { public @Nullable List scheduledEventData; public @Nullable List scheduledEventIds; + + /* + * the following SuppressWarnings annotation is because the Eclipse compiler + * does NOT expect a NonNullByDefault annotation on the inner class, since it is + * implicitly inherited from the outer class, whereas the Maven compiler always + * requires an explicit NonNullByDefault annotation on all classes + */ + @SuppressWarnings("null") + @NonNullByDefault + public static class ScheduledEvent { + public int id; + public boolean enabled; + public int sceneId; + public int sceneCollectionId; + public boolean daySunday; + public boolean dayMonday; + public boolean dayTuesday; + public boolean dayWednesday; + public boolean dayThursday; + public boolean dayFriday; + public boolean daySaturday; + public int eventType; + public int hour; + public int minute; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ScheduledEvent)) { + return false; + } + ScheduledEvent other = (ScheduledEvent) o; + + return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId + && this.sceneCollectionId == other.sceneCollectionId && this.daySunday == other.daySunday + && this.dayMonday == other.dayMonday && this.dayTuesday == other.dayTuesday + && this.dayWednesday == other.dayWednesday && this.dayThursday == other.dayThursday + && this.dayFriday == other.dayFriday && this.daySaturday == other.daySaturday + && this.eventType == other.eventType && this.hour == other.hour && this.minute == other.minute; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + (enabled ? 1 : 0); + result = prime * result + sceneId; + result = prime * result + sceneCollectionId; + result = prime * result + (daySunday ? 1 : 0); + result = prime * result + (dayMonday ? 1 : 0); + result = prime * result + (dayTuesday ? 1 : 0); + result = prime * result + (dayWednesday ? 1 : 0); + result = prime * result + (dayThursday ? 1 : 0); + result = prime * result + (dayFriday ? 1 : 0); + result = prime * result + (daySaturday ? 1 : 0); + result = prime * result + eventType; + result = prime * result + hour; + result = prime * result + minute; + + return result; + } + + public EnumSet getDays() { + EnumSet days = EnumSet.noneOf(DayOfWeek.class); + if (daySunday) { + days.add(DayOfWeek.SUNDAY); + } + if (dayMonday) { + days.add(DayOfWeek.MONDAY); + } + if (dayTuesday) { + days.add(DayOfWeek.TUESDAY); + } + if (dayWednesday) { + days.add(DayOfWeek.WEDNESDAY); + } + if (dayThursday) { + days.add(DayOfWeek.THURSDAY); + } + if (dayFriday) { + days.add(DayOfWeek.FRIDAY); + } + if (daySaturday) { + days.add(DayOfWeek.SATURDAY); + } + return days; + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java index f73635e929c27..69da9378395b7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java @@ -14,7 +14,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; /** * State of a single Shade, as returned by an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java index 72d820d1ee7db..2746523b47a97 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.hdpowerview.internal.api.responses; +import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.BatteryKind; +import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; /** * State of all Shades, as returned by an HD PowerView hub @@ -28,4 +31,40 @@ public class Shades { public @Nullable List shadeData; public @Nullable List shadeIds; + + /* + * the following SuppressWarnings annotation is because the Eclipse compiler + * does NOT expect a NonNullByDefault annotation on the inner class, since it is + * implicitly inherited from the outer class, whereas the Maven compiler always + * requires an explicit NonNullByDefault annotation on all classes + */ + @SuppressWarnings("null") + @NonNullByDefault + public static class ShadeData { + public int id; + public @Nullable String name; + public int roomId; + public int groupId; + public int order; + public int type; + public double batteryStrength; + public int batteryStatus; + public boolean batteryIsLow; + public @Nullable ShadePosition positions; + public @Nullable Boolean timedOut; + public int signalStrength; + public @Nullable Integer capabilities; + public @Nullable Firmware firmware; + public @Nullable Firmware motor; + // note: in old JSON batteryKind was a string but now it's a number; fortunately GSON string accepts either + public @Nullable String batteryKind; + + public String getName() { + return new String(Base64.getDecoder().decode(name)); + } + + public BatteryKind getBatteryKind() { + return BatteryKind.fromString(batteryKind); + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java deleted file mode 100644 index f4efb2805964a..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v1; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; - -/** - * Scene object as returned by an HD PowerView hub of Generation 3 - * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Refactored into separate class - */ -@NonNullByDefault -public class SceneV1 extends Scene { - public int roomId; - public int order; - public int colorId; - public int iconId; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof SceneV1)) { - return false; - } - SceneV1 other = (SceneV1) o; - - return this.id == other.id && getName().equals(other.getName()) && this.roomId == other.roomId - && this.order == other.order && this.colorId == other.colorId && this.iconId == other.iconId; - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + id; - result = prime * result + getName().hashCode(); - result = prime * result + roomId; - result = prime * result + order; - result = prime * result + colorId; - result = prime * result + iconId; - - return result; - } - - @Override - public int compareTo(Scene other) throws IllegalArgumentException { - if (other.version() == version()) { - return Integer.compare(order, ((SceneV1) other).order); - } - throw new IllegalArgumentException("Cannot compare scenes from different hub generations"); - } - - @Override - public int version() { - return 1; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java deleted file mode 100644 index b7ef3c878288c..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v1; - -import java.time.DayOfWeek; -import java.util.EnumSet; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; - -/** - * class for scheduled event as returned by an HD PowerView Generation 1/2 hub. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class ScheduledEventV1 extends ScheduledEvent { - // fields specific to Generation 1/2 - public int sceneCollectionId; - public boolean daySunday; - public boolean dayMonday; - public boolean dayTuesday; - public boolean dayWednesday; - public boolean dayThursday; - public boolean dayFriday; - public boolean daySaturday; - public int eventType; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof ScheduledEventV1)) { - return false; - } - ScheduledEventV1 other = (ScheduledEventV1) o; - - return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId - && this.sceneCollectionId == other.sceneCollectionId && this.daySunday == other.daySunday - && this.dayMonday == other.dayMonday && this.dayTuesday == other.dayTuesday - && this.dayWednesday == other.dayWednesday && this.dayThursday == other.dayThursday - && this.dayFriday == other.dayFriday && this.daySaturday == other.daySaturday - && this.eventType == other.eventType && this.hour == other.hour && this.minute == other.minute; - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + (enabled ? 1 : 0); - result = prime * result + sceneId; - result = prime * result + sceneCollectionId; - result = prime * result + (daySunday ? 1 : 0); - result = prime * result + (dayMonday ? 1 : 0); - result = prime * result + (dayTuesday ? 1 : 0); - result = prime * result + (dayWednesday ? 1 : 0); - result = prime * result + (dayThursday ? 1 : 0); - result = prime * result + (dayFriday ? 1 : 0); - result = prime * result + (daySaturday ? 1 : 0); - result = prime * result + eventType; - result = prime * result + hour; - result = prime * result + minute; - - return result; - } - - @Override - public EnumSet getDays() { - EnumSet days = EnumSet.noneOf(DayOfWeek.class); - if (daySunday) { - days.add(DayOfWeek.SUNDAY); - } - if (dayMonday) { - days.add(DayOfWeek.MONDAY); - } - if (dayTuesday) { - days.add(DayOfWeek.TUESDAY); - } - if (dayWednesday) { - days.add(DayOfWeek.WEDNESDAY); - } - if (dayThursday) { - days.add(DayOfWeek.THURSDAY); - } - if (dayFriday) { - days.add(DayOfWeek.FRIDAY); - } - if (daySaturday) { - days.add(DayOfWeek.SATURDAY); - } - return days; - } - - @Override - public int getEventType() { - return eventType; - } - - @Override - public int version() { - return 1; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java deleted file mode 100644 index 9302a29782404..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; - -/** - * DTO for the Generation 3 gateway information. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class InfoV3 { - public @Nullable String fwVersion; - public @Nullable String serialNumber; - - public HubFirmware toHubFirmware() { - Firmware firmware = new Firmware(); - String fwVersion = this.fwVersion; - if (fwVersion != null) { - String[] parts = fwVersion.split("\\."); - if (parts.length > 0) { - try { - firmware.revision = Integer.valueOf(parts[0]); - } catch (NumberFormatException e) { - } - } - if (parts.length > 1) { - try { - firmware.subRevision = Integer.valueOf(parts[1]); - } catch (NumberFormatException e) { - } - } - if (parts.length > 2) { - try { - firmware.build = Integer.valueOf(parts[2]); - } catch (NumberFormatException e) { - } - } - } - - String name = "Generation 3 Hub"; - if (serialNumber != null) { - name = String.format("%s (serial: %s)", name, serialNumber); - } - firmware.name = name; - - HubFirmware result = new HubFirmware(); - result.mainProcessor = firmware; - return result; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java deleted file mode 100644 index 5756f1a9a7e52..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; - -import java.util.List; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; - -/** - * Scene object as returned by an HD PowerView hub of Generation 3 - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class SceneV3 extends Scene { - public @Nullable String ptName; - public @Nullable String color; - public @Nullable String icon; - public @Nullable List roomIds; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof SceneV3)) { - return false; - } - SceneV3 other = (SceneV3) o; - String color = this.color; - String icon = this.icon; - - // TODO check roomIds as well ?? - return this.id == other.id && getName().equals(other.getName()) && (color != null && color.equals(other.color)) - && (icon != null && icon.equals(other.icon)); - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - - String color = this.color; - String icon = this.icon; - - // TODO hash roomIds as well ?? - result = prime * result + id; - result = prime * result + getName().hashCode(); - result = prime * result + (color == null ? 0 : color.hashCode()); - result = prime * result + (icon == null ? 0 : icon.hashCode()); - - return result; - } - - @Override - public String getName() { - return String.join(" ", super.getName(), ptName); - } - - @Override - public int compareTo(Scene other) throws IllegalArgumentException { - if (other.version() == version()) { - // TODO fix this code.. - return this.equals(other) ? 0 : Integer.MAX_VALUE; - } - throw new IllegalArgumentException("Cannot compare scenes from different hub generations"); - } - - @Override - public int version() { - return 3; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java index 00a07ea4de4fa..10782b22b991c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java @@ -25,11 +25,10 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; @@ -64,7 +63,7 @@ private AutomationChannelBuilder(HDPowerViewTranslationProvider translationProvi /** * Creates an {@link AutomationChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and * {@link ChannelGroupUID}. - * + * * @param translationProvider the {@link HDPowerViewTranslationProvider} * @param channelGroupUid parent {@link ChannelGroupUID} for created channels * @return channel builder @@ -76,7 +75,7 @@ public static AutomationChannelBuilder create(HDPowerViewTranslationProvider tra /** * Adds created channels to existing list. - * + * * @param channels list that channels will be added to * @return channel builder */ @@ -87,7 +86,7 @@ public AutomationChannelBuilder withChannels(List channels) { /** * Sets the scenes. - * + * * @param scenes the scenes * @return channel builder */ @@ -98,7 +97,7 @@ public AutomationChannelBuilder withScenes(List scenes) { /** * Sets the scene collections. - * + * * @param sceneCollections the scene collections * @return channel builder */ @@ -110,7 +109,7 @@ public AutomationChannelBuilder withSceneCollections(List scene /** * Sets the scheduled events. - * + * * @param scheduledEvents the sceduled events * @return channel builder */ @@ -169,21 +168,20 @@ public List build() { } logger.warn("Scene '{}' was not found for scheduled event '{}'", scheduledEvent.sceneId, scheduledEvent.id); return null; - } else if (scheduledEvent.version() == 1 && ((ScheduledEventV1) scheduledEvent).sceneCollectionId > 0) { + } else if (scheduledEvent.sceneCollectionId > 0) { Map sceneCollections = this.sceneCollections; - ScheduledEventV1 scheduledEventV1 = (ScheduledEventV1) scheduledEvent; if (sceneCollections == null) { logger.warn( "Scheduled event '{}' references scene collection '{}', but no scene collections are loaded", - scheduledEvent.id, scheduledEventV1.sceneCollectionId); + scheduledEvent.id, scheduledEvent.sceneCollectionId); return null; } - SceneCollection sceneCollection = sceneCollections.get(scheduledEventV1.sceneCollectionId); + SceneCollection sceneCollection = sceneCollections.get(scheduledEvent.sceneCollectionId); if (sceneCollection != null) { return sceneCollection.getName(); } logger.warn("Scene collection '{}' was not found for scheduled event '{}'", - scheduledEventV1.sceneCollectionId, scheduledEvent.id); + scheduledEvent.sceneCollectionId, scheduledEvent.id); return null; } else { logger.warn("Scheduled event '{}'' not related to any scene or scene collection", scheduledEvent.id); @@ -193,7 +191,8 @@ public List build() { private String getScheduledEventName(String sceneName, ScheduledEvent scheduledEvent) { String timeString, daysString; - switch (scheduledEvent.getEventType()) { + + switch (scheduledEvent.eventType) { case ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME: timeString = LocalTime.of(scheduledEvent.hour, scheduledEvent.minute).toString(); break; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java index 8d9624620223e..01c05137e9ece 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; @@ -45,7 +45,7 @@ private SceneChannelBuilder(HDPowerViewTranslationProvider translationProvider, /** * Creates a {@link SceneChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and * {@link ChannelGroupUID}. - * + * * @param translationProvider the {@link HDPowerViewTranslationProvider} * @param channelGroupUid parent {@link ChannelGroupUID} for created channels * @return channel builder @@ -57,7 +57,7 @@ public static SceneChannelBuilder create(HDPowerViewTranslationProvider translat /** * Adds created channels to existing list. - * + * * @param channels list that channels will be added to * @return channel builder */ @@ -68,7 +68,7 @@ public SceneChannelBuilder withChannels(List channels) { /** * Sets the scenes. - * + * * @param scenes the scenes * @return channel builder */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java index 156a4835fc07e..14cd3b3424b72 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java @@ -24,13 +24,10 @@ public class HDPowerViewHubConfiguration { public static final String HOST = "host"; - public static final String GENERATION = "generation"; public @Nullable String host; public long refresh; public long hardRefresh; public long hardRefreshBatteryLevel; - - public int generation; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java index 37d9b6f952a48..98d108befcfb1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java @@ -21,9 +21,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java index b118704083e38..d9c9bf063e795 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java @@ -22,20 +22,28 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Abstract (component) class that discovers HD PowerView hubs by means of mDNS + * Discovers HD PowerView hubs by means of mDNS * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public abstract class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticipant { +@Component +public class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticipant { - protected static final Pattern VALID_IP_V4_ADDRESS = Pattern + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant.class); + + public static final Pattern VALID_IP_V4_ADDRESS = Pattern .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); @Override @@ -44,10 +52,25 @@ public Set getSupportedThingTypeUIDs() { } @Override - public abstract String getServiceType(); + public String getServiceType() { + return "_powerview._tcp.local."; + } @Override - public abstract @Nullable DiscoveryResult createResult(ServiceInfo service); + public @Nullable DiscoveryResult createResult(ServiceInfo service) { + for (String host : service.getHostAddresses()) { + if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { + ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); + DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) + .withProperty(HDPowerViewHubConfiguration.HOST, host) + .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) + .withLabel("PowerView Hub (" + host + ")").build(); + logger.debug("mDNS discovered hub on host '{}'", host); + return hub; + } + } + return null; + } @Override public @Nullable ThingUID getThingUID(ServiceInfo service) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java deleted file mode 100644 index bb138c5eef52c..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.discovery._v1; - -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; - -import javax.jmdns.ServiceInfo; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; -import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewHubDiscoveryParticipant; -import org.openhab.core.config.discovery.DiscoveryResult; -import org.openhab.core.config.discovery.DiscoveryResultBuilder; -import org.openhab.core.thing.ThingUID; -import org.osgi.service.component.annotations.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Discovers HD PowerView generation 1/2 hubs by means of mDNS - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -@Component -public class HDPowerViewHubDiscoveryParticipantV1 extends HDPowerViewHubDiscoveryParticipant { - - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV1.class); - - @Override - public String getServiceType() { - return "_powerview._tcp.local."; - } - - @Override - public @Nullable DiscoveryResult createResult(ServiceInfo service) { - for (String host : service.getHostAddresses()) { - if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); - DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) - .withProperty(HDPowerViewHubConfiguration.HOST, host) - .withProperty(HDPowerViewHubConfiguration.GENERATION, 1) - .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel("PowerView Hub (" + host + ")").build(); - logger.debug("mDNS discovered generation 1/2 hub on host '{}'", host); - return hub; - } - } - return null; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java new file mode 100644 index 0000000000000..f7051465b6e20 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.discovery; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.core.config.discovery.AbstractDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.ThingUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Discovers HD PowerView Shades and Repeaters from an existing hub + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewDeviceDiscoveryServiceV3 extends AbstractDiscoveryService { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewDeviceDiscoveryServiceV3.class); + private final HDPowerViewHubHandler3 hub; + private final Runnable scanner; + private @Nullable ScheduledFuture backgroundFuture; + private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase(); + + public HDPowerViewDeviceDiscoveryServiceV3(HDPowerViewHubHandler3 hub) { + super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3), 600, true); + this.hub = hub; + this.scanner = createScanner(); + } + + @Override + protected void startScan() { + scheduler.execute(scanner); + } + + @Override + protected void startBackgroundDiscovery() { + ScheduledFuture backgroundFuture = this.backgroundFuture; + if (backgroundFuture != null && !backgroundFuture.isDone()) { + backgroundFuture.cancel(true); + } + this.backgroundFuture = scheduler.scheduleWithFixedDelay(scanner, 0, 60, TimeUnit.SECONDS); + } + + @Override + protected void stopBackgroundDiscovery() { + ScheduledFuture backgroundFuture = this.backgroundFuture; + if (backgroundFuture != null && !backgroundFuture.isDone()) { + backgroundFuture.cancel(true); + this.backgroundFuture = null; + } + super.stopBackgroundDiscovery(); + } + + private Runnable createScanner() { + return () -> { + HDPowerViewWebTargets3 webTargets = hub.getWebTargets(); + try { + discoverShades(webTargets); + } catch (HubProcessingException e) { + logger.warn("Unexpected exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + } + stopScan(); + }; + } + + private void discoverShades(HDPowerViewWebTargets3 webTargets) throws HubProcessingException { + List shades = webTargets.getShades(); + ThingUID bridgeUid = hub.getThing().getUID(); + for (Shade3 shadeData : shades) { + if (shadeData.getId() == 0) { + continue; + } + String id = Integer.toString(shadeData.getId()); + ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3, bridgeUid, id); + + DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shadeData.getName()) + .withBridge(bridgeUid).withProperty(HDPowerViewShadeConfiguration.ID, id) + .withRepresentationProperty(HDPowerViewShadeConfiguration.ID); + String type = shadeData.getTypeString(); + if (type != null) { + builder.withProperty(HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, type); + } + logger.debug("Hub discovered shade '{}'", id); + thingDiscovered(builder.build()); + } + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java index be0b5a7577004..8f2273250a328 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java @@ -10,9 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.discovery._v3; +package org.openhab.binding.hdpowerview.internal.gen3.discovery; -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3; import javax.jmdns.ServiceInfo; @@ -28,9 +28,9 @@ import org.slf4j.LoggerFactory; /** - * Discovers HD PowerView generation 3 hubs by means of mDNS + * Discovers HD PowerView generation 3 hubs by means of mDNS. * - * @author Andrew Fiddian-Green - Initial contribution + * @author Andrew Fiddian-Green - Initial contribution. */ @NonNullByDefault @Component @@ -38,19 +38,13 @@ public class HDPowerViewHubDiscoveryParticipantV3 extends HDPowerViewHubDiscover private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV3.class); - @Override - public String getServiceType() { - return "_powerview-g3._tcp.local."; - } - @Override public @Nullable DiscoveryResult createResult(ServiceInfo service) { for (String host : service.getHostAddresses()) { if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); + ThingUID thingUID = new ThingUID(THING_TYPE_HUB_GEN3, host.replace('.', '_')); DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) - .withProperty(HDPowerViewHubConfiguration.GENERATION, 3) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) .withLabel("PowerView Hub (" + host + ")").build(); logger.debug("mDNS discovered generation 3 hub on host '{}'", host); @@ -59,4 +53,9 @@ public String getServiceType() { } return null; } + + @Override + public String getServiceType() { + return "_powerview-g3._tcp.local."; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java similarity index 54% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java index f9454ff4ed0f5..e0d97be96d655 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java @@ -10,21 +10,25 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses._v3; +package org.openhab.binding.hdpowerview.internal.gen3.dto; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; /** - * Shade SSE event object as supplied an HD PowerView hub of Generation 3 + * DTO for the Generation 3 gateway information. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class SseShadeV3 { - public @Nullable String evt; - public @Nullable String isoDate; - public int id; - public @Nullable ShadePositionV3 currentPositions; +public class Info3 { + private @NonNullByDefault({}) String fwVersion; + private @NonNullByDefault({}) String serialNumber; + + public String getFwVersion() { + return fwVersion; + } + + public String getSerialNumber() { + return serialNumber; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java new file mode 100644 index 0000000000000..8d07cdd7c6342 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.dto; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Scene object as returned by an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution. + */ +@NonNullByDefault +public class Scene3 { + private int id; + private @NonNullByDefault({}) String name; + private @NonNullByDefault({}) String ptName; + private @NonNullByDefault({}) String color; + private @NonNullByDefault({}) String icon; + private @NonNullByDefault({}) List roomIds; + + public String getColor() { + return color; + } + + public String getIcon() { + return icon; + } + + public int getId() { + return id; + } + + public String getName() { + return String.join(" ", new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8), ptName); + } + + public String getPtName() { + return ptName; + } + + public List getRoomIds() { + List roomIds = this.roomIds; + return roomIds != null ? roomIds : List.of(); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java similarity index 60% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java index 492f499a17257..cb8fb302b6d75 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java @@ -10,20 +10,27 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses._v3; +package org.openhab.binding.hdpowerview.internal.gen3.dto; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; /** - * Scene SSE event object as supplied an HD PowerView hub of Generation 3 + * Scene SSE event object as supplied an HD PowerView hub of Generation 3. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class SseSceneV3 { - public @Nullable String evt; - public @Nullable String isoDate; - public int id; - public @Nullable SceneV3 scene; +public class SceneEvent { + private int id; + // private @NonNullByDefault({}) String evt; + // private @NonNullByDefault({}) String isoDate; + private @NonNullByDefault({}) Scene3 scene; + + public int getId() { + return id; + } + + public Scene3 getScene() { + return scene; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java similarity index 79% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java index ea1504a262a8b..74cd526170f06 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java @@ -10,14 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses._v3; +package org.openhab.binding.hdpowerview.internal.gen3.dto; import java.time.DayOfWeek; import java.util.EnumSet; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; /** @@ -26,9 +25,14 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ScheduledEventV3 extends ScheduledEvent { - // fields specific to Generation 3 - public @Nullable String days; +public class ScheduledEvent3 { + public int id; + public int type; + public boolean enabled; + public int hour; + public int minute; + public int sceneId; + public @NonNullByDefault({}) String days; private static final int MON = 0x01; private static final int TUE = 0x02; @@ -49,31 +53,16 @@ public boolean equals(@Nullable Object o) { if (o == this) { return true; } - if (!(o instanceof ScheduledEventV3)) { + if (!(o instanceof ScheduledEvent3)) { return false; } - ScheduledEventV3 other = (ScheduledEventV3) o; + ScheduledEvent3 other = (ScheduledEvent3) o; String days = this.days; return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId && (days != null && days.equals(other.days)) && this.hour == other.hour && this.minute == other.minute; } - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - String days = this.days; - result = prime * result + id; - result = prime * result + (enabled ? 1 : 0); - result = prime * result + sceneId; - result = prime * result + (days != null ? days.hashCode() : 0); - result = prime * result + hour; - result = prime * result + minute; - return result; - } - - @Override public EnumSet getDays() { EnumSet daySet = EnumSet.noneOf(DayOfWeek.class); String days = this.days; @@ -108,7 +97,6 @@ public EnumSet getDays() { return daySet; } - @Override public int getEventType() { switch (type) { case CLOCK_BASED: @@ -126,9 +114,4 @@ public int getEventType() { } return 0; } - - @Override - public int version() { - return 3; - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java new file mode 100644 index 0000000000000..f71fe9d498075 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.dto; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * State of a Shade as returned by an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class Shade3 { + private int id; + private @Nullable Integer type; + private @Nullable String name; + private @Nullable String ptName; + private @Nullable Integer capabilities; + private @Nullable String powerType; // TODO unclear if this is String or Integer + private @Nullable Integer batteryStatus; + // private @Nullable Integer roomId; + private @Nullable Integer signalStrength; + private @Nullable String bleName; + private @Nullable Firmware firmware; + private @Nullable ShadePosition3 positions; + + private transient boolean partialState; + + public State getBatteryLevel() { + Integer batteryStatus = this.batteryStatus; + return batteryStatus == null ? UnDefType.UNDEF + : new PercentType(Math.max(0, Math.min(100, (100 * batteryStatus) / 3))); + } + + public @Nullable String getBleName() { + return bleName; + } + + public @Nullable Integer getCapabilities() { + return capabilities; + } + + public @Nullable String getCapabilitieString() { + Integer capabilities = this.capabilities; + return capabilities == null ? null : Integer.toString(capabilities); + } + + public @Nullable String getFirmware() { + Firmware firmware = this.firmware; + return firmware == null ? null + : String.format("%d.%d.%d", firmware.revision, firmware.subRevision, firmware.build); + } + + public int getId() { + return id; + } + + public State getLowBattery() { + Integer batteryStatus = this.batteryStatus; + return batteryStatus == null ? UnDefType.UNDEF : OnOffType.from(batteryStatus == 0); + } + + public String getName() { + return String.join(" ", new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8), ptName); + } + + public State getPosition(CoordinateSystem posKindCoords) { + ShadePosition3 positions = this.positions; + return positions == null ? UnDefType.UNDEF : positions.getState(posKindCoords); + } + + public @Nullable String getPowerType() { + return powerType; + } + + public @Nullable ShadePosition3 getShadePositions() { + return positions; + } + + public State getSignalStrength() { + Integer signalStrength = this.signalStrength; + return signalStrength == null ? UnDefType.UNDEF : new DecimalType(signalStrength); + } + + public @Nullable Integer getType() { + return type; + } + + public @Nullable String getTypeString() { + Integer type = this.type; + return type == null ? null : Integer.toString(type); + } + + public boolean hasFullState() { + return !partialState; + } + + public boolean isMainsPowered() { + // check powerType and return true or false + return false; + } + + public Shade3 setCapabilities(int capabilities) { + this.capabilities = capabilities; + return this; + } + + public Shade3 setId(int id) { + this.id = id; + return this; + } + + public Shade3 setPartialState() { + this.partialState = true; + return this; + } + + public Shade3 setPosition(CoordinateSystem coordinates, int percent) { + ShadePosition3 positions = this.positions; + if (positions == null) { + positions = new ShadePosition3(); + this.positions = positions; + } + positions.setPosition(coordinates, percent); + return this; + } + + public Shade3 setShadePosition(ShadePosition3 position) { + this.positions = position; + return this; + } + + public Shade3 setType(int type) { + this.type = type; + return this; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java new file mode 100644 index 0000000000000..af38fc9c4d96b --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.dto; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Shade SSE event object as supplied an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ShadeEvent { + private int id; + // private @NonNullByDefault({}) String evt; + // private @NonNullByDefault({}) String isoDate; + // private @NonNullByDefault({}) String bleName; + private @NonNullByDefault({}) ShadePosition3 currentPositions; + // private @NonNullByDefault({}) ShadePosition3 targetPositions; + + public ShadePosition3 getCurrentPositions() { + return currentPositions; + } + + public int getId() { + return id; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java similarity index 56% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java index 516d00df121dc..93800309a5546 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java @@ -10,13 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api._v3; +package org.openhab.binding.hdpowerview.internal.gen3.dto; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; @@ -27,15 +24,13 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadePositionV3 extends ShadePosition { +public class ShadePosition3 { + private @NonNullByDefault({}) Double primary; + private @NonNullByDefault({}) Double secondary; + private @NonNullByDefault({}) Double tilt; + // private @NonNullByDefault({}) Double velocity; - private @Nullable Double primary; - private @Nullable Double secondary; - private @Nullable Double tilt; - // private @Nullable Double velocity; - - @Override - public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + public State getState(CoordinateSystem posKindCoords) { Double value; switch (posKindCoords) { case PRIMARY_POSITION: @@ -53,34 +48,39 @@ public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCo return value != null ? new PercentType((int) (value.doubleValue() * 100)) : UnDefType.UNDEF; } - @Override - public boolean secondaryRailDetected() { - return secondary != null; - } - - @Override - public boolean tiltAnywhereDetected() { - return tilt != null; - } - - @Override - public ShadePositionV3 setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + /** + * Set a new position value for this object based on the given coordinates, and the given new value. + * + * @param coordinates which of the position fields shall be set. + * @param percent the new value in percent. + * @return this object. + */ + public ShadePosition3 setPosition(CoordinateSystem coordinates, int percent) { Double value = Double.valueOf(percent) / 100.0; - switch (posKindCoords) { + switch (coordinates) { case PRIMARY_POSITION: - primary = shadeCapabilities.supportsPrimary() ? value : null; + primary = value; break; case SECONDARY_POSITION: - secondary = shadeCapabilities.supportsSecondary() || shadeCapabilities.supportsSecondaryOverlapped() - ? value - : null; + secondary = value; break; case VANE_TILT_POSITION: - tilt = shadeCapabilities.supportsTiltOnClosed() || shadeCapabilities.supportsTiltAnywhere() ? value - : null; + tilt = value; break; default: } return this; } + + public boolean supportsPrimary() { + return primary != null; + } + + public boolean supportsSecondary() { + return secondary != null; + } + + public boolean supportsTilt() { + return tilt != null; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java new file mode 100644 index 0000000000000..fbfe1f8cf3352 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java @@ -0,0 +1,284 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.handler; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.client.ClientBuilder; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.thing.type.AutoUpdatePolicy; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Bridge handler for an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewHubHandler3 extends BaseBridgeHandler { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler3.class); + private final String channelTypeId = HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE; + private final String channelGroupId = HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES; + + private final HttpClient httpClient; + private final HDPowerViewTranslationProvider translationProvider; + private final ClientBuilder clientBuilder; + private final SseEventSourceFactory eventSourceFactory; + + private @Nullable HDPowerViewWebTargets3 webTargets; + private @Nullable ScheduledFuture refreshTask; + + private boolean scenesLoaded; + private boolean propertiesLoaded; + private boolean isDisposing; + + public HDPowerViewHubHandler3(Bridge bridge, HttpClient httpClient, + HDPowerViewTranslationProvider translationProvider, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory) { + super(bridge); + this.httpClient = httpClient; + this.translationProvider = translationProvider; + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; + } + + @Override + public void dispose() { + isDisposing = true; + ScheduledFuture future = this.refreshTask; + if (future != null) { + future.cancel(true); + } + this.refreshTask = null; + + HDPowerViewWebTargets3 webTargets = this.webTargets; + if (webTargets != null) { + try { + webTargets.close(); + } catch (IOException e) { + } + this.webTargets = null; + } + } + + /** + * Refresh the state of all things. Normally the thing's position state is updated by SSE. However we must do a + * refresh once on start up in order to get the initial state. Also the other properties (battery, signal strength + * etc.) are not updated by SSE. Furthermore we need to do periodic refreshes just in case the SSE connection may + * have been lost. + */ + private void doRefresh() { + try { + getWebTargets().openSSE(); + refreshProperties(); + refreshShades(); + refreshScenes(); + } catch (IllegalStateException | HubProcessingException e) { + logger.warn("doRefresh() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + } + } + + /** + * Getter for the list of all HDPowerViewShadeHandler3 child thing handlers. + * + * @return the list of handlers. + * @throws IllegalStateException if the bridge is not properly initialized. + */ + private List getThingHandlers() throws IllegalStateException { + Bridge bridge = getBridge(); + if (bridge != null) { + List result = new ArrayList<>(); + bridge.getThings().stream().map(thing -> thing.getHandler()).forEach(handler -> { + if (handler instanceof HDPowerViewShadeHandler3) { + result.add((HDPowerViewShadeHandler3) handler); + } + }); + return result; + } + throw new IllegalStateException("Bridge not initialized."); + } + + /** + * Getter for the webTargets. + * + * @return the webTargets. + * @throws IllegalStateException if webTargets is not initialized. + */ + public HDPowerViewWebTargets3 getWebTargets() throws IllegalStateException { + HDPowerViewWebTargets3 webTargets = this.webTargets; + if (webTargets != null) { + return webTargets; + } + throw new IllegalStateException("WebTargets not initialized."); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (RefreshType.REFRESH == command) { + scheduler.submit(() -> doRefresh()); + return; + } + Channel channel = getThing().getChannel(channelUID.getId()); + if (channel == null) { + return; + } + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + if (channelTypeUID == null) { + return; + } + if (channelTypeId.equals(channelTypeUID.getId()) && OnOffType.ON == command) { + try { + getWebTargets().activateScene(Integer.parseInt(channelUID.getIdWithoutGroup())); + } catch (HubProcessingException | IllegalStateException e) { + logger.warn("handleCommand() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + } + } + } + + @Override + public void initialize() { + HDPowerViewHubConfiguration config = getConfigAs(HDPowerViewHubConfiguration.class); + String host = config.host; + + if (host == null || host.isEmpty()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error.no-host-address"); + return; + } + + webTargets = new HDPowerViewWebTargets3(this, httpClient, clientBuilder, eventSourceFactory, host); + scenesLoaded = false; + propertiesLoaded = false; + isDisposing = false; + + /* + * Normally the thing's position state is updated by SSE. However we must do a refresh once on start up in order + * to get the initial state. Also the other properties (battery, signal strength etc.) are not updated by SSE. + * Furthermore we need to do periodic refreshes just in case the SSE connection may have been lost. So we + * schedule the refresh at the 'hardRefresh' interval. + */ + ScheduledFuture refreshTask = this.refreshTask; + if (refreshTask != null) { + refreshTask.cancel(false); + } + this.refreshTask = scheduler.scheduleWithFixedDelay(() -> doRefresh(), 10, config.hardRefresh, + TimeUnit.MINUTES); + + updateStatus(ThingStatus.UNKNOWN); + } + + /** + * Method that is called when a scene changes state. + * + * @param scene the one that changed. + */ + public void onSceneEvent(Scene3 scene) { + // TODO perhaps we should trigger an OH core event here ?? + } + + /** + * Method that is called when a shade changes state. + * + * @param shade the one that changed. + */ + public void onShadeEvent(Shade3 shade) { + try { + for (HDPowerViewShadeHandler3 handler : getThingHandlers()) { + if (isDisposing || handler.notify(shade)) { + break; + } + } + } catch (IllegalStateException e) { + logger.warn("onShadeEvent() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + } + } + + private void refreshProperties() throws HubProcessingException, IllegalStateException { + if (propertiesLoaded || isDisposing) { + return; + } + thing.setProperties(getWebTargets().getInformation()); + propertiesLoaded = true; + } + + /** + * Create the dynamic list of scene channels. + * + * @throws HubProcessingException if the web target connection caused an error. + * @throws IllegalStateException if this handler is in an illegal state. + */ + private void refreshScenes() throws HubProcessingException, IllegalStateException { + if (scenesLoaded || isDisposing) { + return; + } + ChannelTypeUID typeUID = new ChannelTypeUID(channelTypeId); + ChannelGroupUID groupUID = new ChannelGroupUID(thing.getUID(), channelGroupId); + List channels = new ArrayList<>(); + for (Scene3 scene : getWebTargets().getScenes()) { + ChannelUID channelUID = new ChannelUID(groupUID, Integer.toString(scene.getId())); + String name = scene.getName(); + String description = translationProvider.getText("dynamic-channel.scene-activate.description", name); + channels.add(ChannelBuilder.create(channelUID, CoreItemFactory.SWITCH).withType(typeUID).withLabel(name) + .withDescription(description).withAutoUpdatePolicy(AutoUpdatePolicy.VETO).build()); + } + updateThing(editThing().withChannels(channels).build()); + scenesLoaded = true; + } + + /** + * Get the full list of shades data and notify each of the thing handlers. + * + * @throws HubProcessingException if the web target connection caused an error. + * @throws IllegalStateException if this handler is in an illegal state. + */ + private void refreshShades() throws HubProcessingException, IllegalStateException { + List handlers = getThingHandlers(); + for (Shade3 shade : getWebTargets().getShades()) { + for (HDPowerViewShadeHandler3 handler : handlers) { + if (isDisposing || handler.notify(shade)) { + break; + } + } + } + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java new file mode 100644 index 0000000000000..19fd295d80e2c --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java @@ -0,0 +1,285 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.handler; + +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.BridgeHandler; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Thing handler for an HD PowerView shade of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewShadeHandler3 extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewShadeHandler3.class); + + private static final String INVALID_CHANNEL = "invalid channel"; + private static final String INVALID_COMMAND = "invalid command"; + private static final String COMMAND_CALIBRATE = "CALIBRATE"; + private static final String COMMAND_IDENTIFY = "IDENTIFY"; + + private final Shade3 thisShade = new Shade3(); + private boolean isInitialized; + + public HDPowerViewShadeHandler3(Thing thing) { + super(thing); + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + } + + /** + * Getter for the hub handler. + * + * @return the hub handler. + * @throws IllegalStateException if the bridge or its handler are not initialized. + */ + private HDPowerViewHubHandler3 getHandler() throws IllegalStateException { + Bridge bridge = this.getBridge(); + if (bridge == null) { + throw new IllegalStateException("Bridge not initialised."); + } + BridgeHandler handler = bridge.getHandler(); + if (!(handler instanceof HDPowerViewHubHandler3)) { + throw new IllegalStateException("Bridge handler not initialised."); + } + return (HDPowerViewHubHandler3) handler; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (RefreshType.REFRESH == command) { + getHandler().handleCommand(channelUID, command); + return; + } + + HDPowerViewWebTargets3 webTargets = getHandler().getWebTargets(); + ShadePosition3 position = new ShadePosition3(); + int shadeId = thisShade.getId(); + try { + switch (channelUID.getId()) { + case CHANNEL_SHADE_POSITION: + if (command instanceof PercentType) { + position.setPosition(PRIMARY_POSITION, ((PercentType) command).intValue()); + webTargets.moveShade(shadeId, position); + break; + } else if (command instanceof UpDownType) { + position.setPosition(PRIMARY_POSITION, UpDownType.UP == command ? 0 : 100); + webTargets.moveShade(shadeId, position); + break; + } else if (StopMoveType.STOP == command) { + webTargets.stopShade(shadeId); + break; + } + throw new IllegalArgumentException(INVALID_COMMAND); + + case CHANNEL_SHADE_SECONDARY_POSITION: + if (command instanceof PercentType) { + position.setPosition(SECONDARY_POSITION, ((PercentType) command).intValue()); + webTargets.moveShade(shadeId, position); + break; + } else if (command instanceof UpDownType) { + position.setPosition(SECONDARY_POSITION, UpDownType.UP == command ? 0 : 100); + webTargets.moveShade(shadeId, position); + break; + } else if (StopMoveType.STOP == command) { + webTargets.stopShade(shadeId); + break; + } + throw new IllegalArgumentException(INVALID_COMMAND); + + case CHANNEL_SHADE_VANE: + if (command instanceof PercentType) { + position.setPosition(VANE_TILT_POSITION, ((PercentType) command).intValue()); + webTargets.moveShade(shadeId, position); + break; + } else if (command instanceof UpDownType) { + position.setPosition(VANE_TILT_POSITION, UpDownType.UP == command ? 0 : 100); + webTargets.moveShade(shadeId, position); + break; + } + throw new IllegalArgumentException(INVALID_COMMAND); + + case CHANNEL_SHADE_COMMAND: + if (command instanceof StringType) { + if (COMMAND_IDENTIFY.equals(((StringType) command).toString())) { + webTargets.jogShade(shadeId); + break; + } else if (COMMAND_CALIBRATE.equals(((StringType) command).toString())) { + webTargets.calibrateShade(shadeId); + break; + } + } + throw new IllegalArgumentException(INVALID_COMMAND); + + default: + throw new IllegalArgumentException(INVALID_CHANNEL); + } + } catch (HubProcessingException | IllegalArgumentException e) { + logger.warn("handleCommand() shadeId:{}, channelUID:{}, command:{}, exception:{}, message:{}", shadeId, + channelUID, command, e.getClass().getSimpleName(), e.getMessage()); + } + } + + @Override + public void initialize() { + thisShade.setId(getConfigAs(HDPowerViewShadeConfiguration.class).id); + Bridge bridge = getBridge(); + if (bridge == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error.invalid-bridge-handler"); + return; + } + isInitialized = false; + updateStatus(ThingStatus.UNKNOWN); + } + + /** + * Handle shade state change notifications. + * + * @param shade the new shade state. + * @return true if we handled the call. + */ + public boolean notify(Shade3 shade) { + if (thisShade.getId() == shade.getId()) { + updateStatus(ThingStatus.ONLINE); + if (!isInitialized) { + ShadePosition3 position = shade.getShadePositions(); + if (position != null) { + thisShade.setShadePosition(position); + } + updateProperties(shade); + updateDynamicChannels(shade); + } + isInitialized = true; + updateChannels(shade); + return true; + } + return false; + } + + /** + * Update channels based on the data in the passed shade instance. + * + * @param shade containing the channel data. + */ + private void updateChannels(Shade3 shade) { + updateState(CHANNEL_SHADE_POSITION, shade.getPosition(PRIMARY_POSITION)); + updateState(CHANNEL_SHADE_VANE, shade.getPosition(VANE_TILT_POSITION)); + updateState(CHANNEL_SHADE_SECONDARY_POSITION, shade.getPosition(SECONDARY_POSITION)); + if (shade.hasFullState()) { + updateState(CHANNEL_SHADE_LOW_BATTERY, shade.getLowBattery()); + updateState(CHANNEL_SHADE_BATTERY_LEVEL, shade.getBatteryLevel()); + updateState(CHANNEL_SHADE_SIGNAL_STRENGTH, shade.getSignalStrength()); + } + } + + /** + * If the given channel exists in the thing, but is NOT required in the thing, then add it to a list of channels to + * be removed. Or if the channel does NOT exist in the thing, but is required in the thing, then log a warning. + * + * @param removeList the list of channels to be removed from the thing. + * @param channelId the id of the channel to be (eventually) removed. + * @param channelRequired true if the thing requires this channel. + */ + private void updateDynamicChannel(List removeList, String channelId, boolean channelRequired) { + Channel channel = thing.getChannel(channelId); + if (!channelRequired && channel != null) { + removeList.add(channel); + } else if (channelRequired && channel == null) { + logger.warn("updateDynamicChannel() shadeId:{} is missing channel:{} => please recreate the thing", + thisShade.getId(), channelId); + } + } + + /** + * Remove previously statically created channels if the shade does not support them or they are not relevant. + * + * @param shade containing the channel data. + */ + private void updateDynamicChannels(Shade3 shade) { + List removeChannels = new ArrayList<>(); + + ShadePosition3 positions = shade.getShadePositions(); + if (positions != null) { + updateDynamicChannel(removeChannels, CHANNEL_SHADE_POSITION, positions.supportsPrimary()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_SECONDARY_POSITION, positions.supportsSecondary()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_VANE, positions.supportsTilt()); + } + + updateDynamicChannel(removeChannels, CHANNEL_SHADE_BATTERY_LEVEL, shade.isMainsPowered()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_LOW_BATTERY, shade.isMainsPowered()); + + if (!removeChannels.isEmpty()) { + if (logger.isDebugEnabled()) { + StringJoiner joiner = new StringJoiner(", "); + removeChannels.forEach(c -> joiner.add(c.getUID().getId())); + logger.debug("updateDynamicChannels() shadeId:{}, removing unsupported channels:{}", thisShade.getId(), + joiner.toString()); + } + updateThing(editThing().withoutChannels(removeChannels).build()); + } + } + + /** + * Update thing properties based on the data in the passed shade instance. + * + * @param shade containing the property data. + */ + private void updateProperties(Shade3 shade) { + if (shade.hasFullState()) { + thing.setProperties(Stream.of(new String[][] { // + { HDPowerViewBindingConstants.PROPERTY_NAME, shade.getName() }, + { HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, shade.getTypeString() }, + { HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES, shade.getCapabilitieString() }, + { HDPowerViewBindingConstants.PROPERTY_POWER_TYPE, shade.getPowerType() }, + { HDPowerViewBindingConstants.PROPERTY_BLE_NAME, shade.getBleName() }, + { Thing.PROPERTY_FIRMWARE_VERSION, shade.getFirmware() } // + }).collect(Collectors.toMap(data -> data[0], data -> data[1]))); + } + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java new file mode 100644 index 0000000000000..3c3c540d13cc6 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java @@ -0,0 +1,445 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.webtargets; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.net.ssl.SSLContext; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.sse.InboundSseEvent; +import javax.ws.rs.sse.SseEventSource; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets.Query; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Info3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; +import org.openhab.core.thing.Thing; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +/** + * JAX-RS targets for communicating with an HD PowerView hub Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewWebTargets3 implements Closeable { + + private static final String IDS = "ids"; + + // @formatter:off + public static final Type LIST_SHADES = new TypeToken>() {}.getType(); + public static final Type LIST_SCENES = new TypeToken>() {}.getType(); + public static final Type LIST_EVENTS =new TypeToken>() {}.getType(); + // @formatter:on + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets3.class); + private final String shades; + private final String scenes; + private final String sceneActivate; + private final String shadeMotion; + private final String shadeStop; + private final String shadePositions; + private final String info; + private final String automations; + + private final String register; + private final String shadeEvents; + private final String sceneEvents; + private final Gson gson = new Gson(); + private final HttpClient httpClient; + + private final ClientBuilder clientBuilder; + private final SseEventSourceFactory eventSourceFactory; + private final HDPowerViewHubHandler3 hubHandler; + + private boolean isRegistered; + + private @Nullable SseEventSource shadeEventSource; + private @Nullable SseEventSource sceneEventSource; + + /** + * Simple DTO for registering the binding with the hub. + * + * @author Andrew Fiddian-Green - Initial contribution + */ + @SuppressWarnings("unused") + private static class GatewayRegistration { + public String todo = "org.openhab.binding.hdpowerview"; // TODO + } + + /** + * Initialize the web targets + * + * @param httpClient the HTTP client (the binding) + * @param ipAddress the IP address of the server (the hub) + */ + public HDPowerViewWebTargets3(HDPowerViewHubHandler3 hubHandler, HttpClient httpClient, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory, String ipAddress) { + String base = "http://" + ipAddress + "/"; + String home = base + "home/"; + + shades = home + "shades"; + scenes = home + "scenes"; + sceneActivate = home + "scenes/%d/activate"; + shadeMotion = home + "shades/%d/motion"; + shadeStop = home + "shades/stop"; + shadePositions = home + "shades/positions"; + automations = home + "automations"; + shadeEvents = home + "shades/events"; + sceneEvents = home + "scenes/events"; + + info = base + "gateway/info"; + register = "TBD"; // TODO + + this.httpClient = httpClient; + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; + this.hubHandler = hubHandler; + } + + /** + * Issue a command a activate a scene. + * + * @param sceneId the scene to be activated. + * @throws HubProcessingException if any error occurs. + */ + public void activateScene(int sceneId) throws HubProcessingException { + invoke(HttpMethod.PUT, String.format(sceneActivate, sceneId), null, null); + } + + /** + * Issue a calibrate command to a shade. + * + * @param shadeId the shade to be calibrated. + * @throws HubProcessingException if any error occurs. + */ + public void calibrateShade(int shadeId) throws HubProcessingException { + String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.CALIBRATE)); + invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); + } + + @Override + public void close() throws IOException { + SseEventSource source; + source = this.shadeEventSource; + if (source != null) { + source.close(); + this.shadeEventSource = null; + } + source = this.sceneEventSource; + if (source != null) { + source.close(); + this.sceneEventSource = null; + } + } + + /** + * Register the binding with the hub (if not already registered). + * + * @throws HubProcessingException if any error occurs. + */ + private void gatewayRegister() throws HubProcessingException { + if (!isRegistered) { + String json = gson.toJson(new GatewayRegistration()); + invoke(HttpMethod.PUT, register, null, json); + isRegistered = true; + } + } + + /** + * Get hub properties. + * + * @return a map containing the hub properties. + * @throws HubProcessingException if any error occurs. + */ + public Map getInformation() throws HubProcessingException { + String json = invoke(HttpMethod.GET, info, null, null); + try { + Info3 result = gson.fromJson(json, Info3.class); + if (result == null) { + throw new HubProcessingException("getInformation(): missing response"); + } + return Map.of( // + Thing.PROPERTY_FIRMWARE_VERSION, result.getFwVersion(), // + Thing.PROPERTY_SERIAL_NUMBER, result.getSerialNumber()); + } catch (JsonParseException e) { + throw new HubProcessingException("getFirmwareVersions(): JsonParseException"); + } + } + + /** + * Get the list of scenes. + * + * @return the list of scenes. + * @throws HubProcessingException if any error occurs. + */ + public List getScenes() throws HubProcessingException { + String json = invoke(HttpMethod.GET, scenes, null, null); + try { + List result = gson.fromJson(json, LIST_SCENES); + if (result == null) { + throw new HubProcessingException("getScenes() missing response"); + } + return result; + } catch (JsonParseException e) { + throw new HubProcessingException("getScenes() JsonParseException"); + } + } + + /** + * Get the list of scheduled events. + * + * @return the list of scheduled events. + * @throws HubProcessingException if any error occurs. + */ + public List getScheduledEvents() throws HubProcessingException { + String json = invoke(HttpMethod.GET, automations, null, null); + try { + List result = gson.fromJson(json, LIST_EVENTS); + if (result == null) { + throw new HubProcessingException("getScheduledEvents() missing response"); + } + return result; + } catch (JsonParseException e) { + throw new HubProcessingException("getScheduledEvents() JsonParseException"); + } + } + + /** + * Get the data for a single shade. + * + * @param shadeId the id of the shade to get. + * @return the shade. + * @throws HubProcessingException if any error occurs. + */ + public Shade3 getShade(int shadeId) throws HubProcessingException { + String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); + try { + Shade3 result = gson.fromJson(json, Shade3.class); + if (result == null) { + throw new HubProcessingException("getShade() missing response"); + } + return result; + } catch (JsonParseException e) { + throw new HubProcessingException("getShade() JsonParseException"); + } + } + + /** + * Get the list of shades. + * + * @return the list of shades. + * @throws HubProcessingException if any error occurs. + */ + public List getShades() throws HubProcessingException { + String json = invoke(HttpMethod.GET, shades, null, null); + try { + List result = gson.fromJson(json, LIST_SHADES); + if (result == null) { + throw new HubProcessingException("getShades() missing response"); + } + return result; + } catch (JsonParseException e) { + throw new HubProcessingException("getShades() JsonParseException"); + } + } + + /** + * Invoke a call on the hub server to retrieve information or send a command. + * + * @param method GET or PUT. + * @param url the host URL to be called. + * @param query the HTTP query parameter. + * @param jsonCommand the request command content (as a JSON string). + * @return the response content (as a JSON string). + * @throws HubProcessingException if something goes wrong. + */ + protected synchronized String invoke(HttpMethod method, String url, @Nullable Query query, + @Nullable String jsonCommand) throws HubProcessingException { + if (logger.isTraceEnabled()) { + if (query != null) { + logger.trace("invoke() method:{}, url:{}, query:{}", method, url, query); + } else { + logger.trace("invoke() method:{}, url:{}", method, url); + } + if (jsonCommand != null) { + logger.trace("invoke() request JSON:{}", jsonCommand); + } + } + Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); + if (query != null) { + request.param(query.getKey(), query.getValue()); + } + if (jsonCommand != null) { + request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); + } + ContentResponse response; + try { + response = request.send(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } catch (TimeoutException | ExecutionException e) { + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } + int statusCode = response.getStatus(); + if (statusCode != HttpStatus.OK_200) { + logger.warn("invoke() HTTP status:{}, reason:{}", statusCode, response.getReason()); + throw new HubProcessingException(String.format("HTTP %d error", statusCode)); + } + String jsonResponse = response.getContentAsString(); + if (logger.isTraceEnabled()) { + logger.trace("invoke() response JSON:{}", jsonResponse); + } + if (jsonResponse == null || jsonResponse.isEmpty()) { + logger.warn("invoke() hub returned no content"); + throw new HubProcessingException("Missing response entity"); + } + return jsonResponse; + } + + /** + * Issue a jog command to a shade. + * + * @param shadeId the shade to be jogged. + * @throws HubProcessingException if any error occurs. + */ + public void jogShade(int shadeId) throws HubProcessingException { + String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.JOG)); + invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); + } + + /** + * Issue a ccommand to move a shade. + * + * @param shadeId the shade to be moved. + * @param position the new position. + * @throws HubProcessingException if any error occurs. + */ + public void moveShade(int shadeId, ShadePosition3 position) throws HubProcessingException { + invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), + gson.toJson(position)); + } + + /** + * Handle inbound SSE events for a scene. + * + * @param sseEvent the inbound event. + */ + private void onSceneEvent(InboundSseEvent sseEvent) { + String json = sseEvent.readData(); + logger.trace("onSceneEvent() json:{}", json); + SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); + if (sceneEvent != null) { + Scene3 scene = sceneEvent.getScene(); + hubHandler.onSceneEvent(scene); + } + } + + /** + * Handle inbound SSE events for a shade. + * + * @param sseEvent the inbound event. + */ + private void onShadeEvent(InboundSseEvent sseEvent) { + String json = sseEvent.readData(); + logger.trace("onShadeEvent() json:{}", json); + ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); + if (shadeEvent != null) { + ShadePosition3 positions = shadeEvent.getCurrentPositions(); + hubHandler + .onShadeEvent(new Shade3().setId(shadeEvent.getId()).setShadePosition(positions).setPartialState()); + } + } + + /** + * Open the SSE subscriptions. + * + * @return true if registered for SSE events. + * @throws HubProcessingException if any error occurs. + */ + public void openSSE() throws HubProcessingException { + SseEventSource shadeEventSource = this.shadeEventSource; + SseEventSource sceneEventSource = this.sceneEventSource; + + if (shadeEventSource == null || !shadeEventSource.isOpen() || sceneEventSource == null + || !sceneEventSource.isOpen()) { + + try { + close(); + } catch (IOException e) { + } + + // register ourself with the gateway (if necessary) + gatewayRegister(); + + SSLContext context = httpClient.getSslContextFactory().getSslContext(); + WebTarget target; + + // open SSE channel for shades + target = clientBuilder.sslContext(context).build().target(shadeEvents); + shadeEventSource = eventSourceFactory.newSource(target); + shadeEventSource.register((event) -> onShadeEvent(event)); + shadeEventSource.open(); + this.shadeEventSource = shadeEventSource; + + // open SSE channel for scenes + target = clientBuilder.sslContext(context).build().target(sceneEvents); + sceneEventSource = eventSourceFactory.newSource(target); + sceneEventSource.register((event) -> onSceneEvent(event)); + sceneEventSource.open(); + this.sceneEventSource = sceneEventSource; + } + } + + /** + * Issue a stop command to a shade. + * + * @param shadeId the shade to be stopped. + * @throws HubProcessingException if any error occurs. + */ + public void stopShade(int shadeId) throws HubProcessingException { + invoke(HttpMethod.PUT, shadeStop, Query.of(IDS, Integer.valueOf(shadeId).toString()), null); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index db0e2f403e286..b3991f3aad3bd 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -19,37 +19,28 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; -import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; -import org.openhab.binding.hdpowerview.internal._v3.SseSinkV3; import org.openhab.binding.hdpowerview.internal.api.Firmware; import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder; @@ -76,7 +67,6 @@ import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,15 +79,13 @@ * @author Jacob Laursen - Added support for scene groups and automations */ @NonNullByDefault -public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV3 { +public class HDPowerViewHubHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class); private final HttpClient httpClient; private final HDPowerViewTranslationProvider translationProvider; private final ConcurrentHashMap pendingShadeInitializations = new ConcurrentHashMap<>(); private final Duration firmwareVersionValidityPeriod = Duration.ofDays(1); - private final ClientBuilder clientBuilder; - private final SseEventSourceFactory eventSourceFactory; private long refreshInterval; private long hardRefreshPositionInterval; @@ -124,13 +112,10 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED); public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient, - HDPowerViewTranslationProvider translationProvider, ClientBuilder clientBuilder, - SseEventSourceFactory eventSourceFactory) { + HDPowerViewTranslationProvider translationProvider) { super(bridge); this.httpClient = httpClient; this.translationProvider = translationProvider; - this.clientBuilder = clientBuilder; - this.eventSourceFactory = eventSourceFactory; } @Override @@ -178,14 +163,8 @@ public void initialize() { return; } - try { - webTargets = newWebTargets(host); - } catch (InstantiationException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); - return; - } - pendingShadeInitializations.clear(); + webTargets = new HDPowerViewWebTargets(httpClient, host); refreshInterval = config.refresh; hardRefreshPositionInterval = config.hardRefresh; hardRefreshBatteryLevelInterval = config.hardRefreshBatteryLevel; @@ -248,12 +227,7 @@ public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { } private void schedulePoll() { - if (sseSubscribe(this)) { - scheduler.submit(this::poll); // do a single poll to fetch the initial state - } else { - scheduleSoftPoll(); - } - // do hard polls (even on generation 3) in case SSE subscriptions have dropped + scheduleSoftPoll(); scheduleHardPoll(); } @@ -306,8 +280,6 @@ private synchronized void stopPoll() { future.cancel(true); } this.hardRefreshBatteryLevelFuture = null; - - sseSubscribe(null); } private synchronized void poll() { @@ -450,7 +422,7 @@ private void updateUnknownShadeThing(Thing thing) { HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler()); if (thingHandler == null) { logger.debug("Shade '{}' handler not initialized", shadeId); - pendingShadeInitializations.put(thing.getUID(), newShadeData()); + pendingShadeInitializations.put(thing.getUID(), new ShadeData()); return; } ThingStatus thingStatus = thingHandler.getThing().getStatus(); @@ -464,7 +436,7 @@ private void updateUnknownShadeThing(Thing thing) { case UNINITIALIZED: case INITIALIZING: logger.debug("Shade '{}' handler not yet ready; status: {}", shadeId, thingStatus); - pendingShadeInitializations.put(thing.getUID(), newShadeData()); + pendingShadeInitializations.put(thing.getUID(), new ShadeData()); break; case REMOVING: case REMOVED: @@ -683,9 +655,6 @@ private void requestRefreshShadePositions() { logger.debug("Shade '{}' handler not initialized", shadeId); } } - - // re-subscribe (in case SSE connections went down) - sseSubscribe(this); } private void requestRefreshShadeBatteryLevels() { @@ -706,87 +675,4 @@ private void requestRefreshShadeBatteryLevels() { } } } - - /** - * Instantiate the web targets. - * - * @param host the ip address - * @return instance of HDPowerViewWebTargets class (either V1 or V3). - * @throws InstantiationException if neither a V1 nor a V3 web target was instantiated. - */ - private HDPowerViewWebTargets newWebTargets(String host) throws InstantiationException { - HDPowerViewWebTargets webTargets = this.webTargets; - if (webTargets != null) { - return webTargets; - } - HDPowerViewHubConfiguration config = getConfigAs(HDPowerViewHubConfiguration.class); - int hubGeneration = config.generation; - switch (hubGeneration) { - case 0: // for non breaking of existing installations - case 1: // both generation 1 and 2 hubs use V1 web targets - case 2: - webTargets = new HDPowerViewWebTargetsV1(httpClient, clientBuilder, eventSourceFactory, host); - break; - case 3: // generation 3 hubs use V3 web targets - webTargets = new HDPowerViewWebTargetsV3(httpClient, clientBuilder, eventSourceFactory, host); - } - if (webTargets != null) { - this.webTargets = webTargets; - return webTargets; - } - throw new InstantiationException("Unable to instantiate the web targets"); - } - - /** - * Check if gateway is generation 1 - * - * @return true if gateway is generation 1 - */ - private boolean isGeneration1() { - return webTargets instanceof HDPowerViewWebTargetsV1; - } - - /** - * Create a new ShadeData instance; either V1 or V3 depending on the gateway generation. - * - * @return new ShadeData instance. - */ - private ShadeData newShadeData() { - return isGeneration1() ? new ShadeDataV1() : new ShadeDataV3(); - } - - /** - * If the gateway is generation 3 try to (un)subscribe to SSE on it. If 'sseSinK' is not null, make the - * subscription, otherwise cancel it. - * - * @param sseSinK the sink for the SSE call backs (may be null). - * @return true if the subscription succeeded. - */ - private boolean sseSubscribe(@Nullable SseSinkV3 sseSinK) { - if (webTargets instanceof HDPowerViewWebTargetsV3) { - try { - return ((HDPowerViewWebTargetsV3) webTargets).sseSubscribe(sseSinK); - } catch (HubMaintenanceException | HubProcessingException e) { - logger.warn("Failed to {}subscribe for SSE '{}'", sseSinK == null ? "un-" : "", e.getMessage()); - } - } - return false; - } - - @Override - public void sseShade(String evt, int shadeId, ShadePosition shadePosition) { - Optional thing = getShadeThingIdMap().entrySet().stream() - .filter(e -> e.getValue().equals(Integer.valueOf(shadeId))).findFirst().map(Map.Entry::getKey); - if (thing.isPresent()) { - ThingHandler handler = thing.get().getHandler(); - if (handler instanceof HDPowerViewShadeHandler) { - ((HDPowerViewShadeHandler) handler).sseShadePosition(shadePosition); - } - } - } - - @Override - public void sseScene(String evt, int sceneId) { - // TODO perhaps we don't need to do anything? - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java index 51be5b55cc09c..53e77e0425cdb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java @@ -32,12 +32,9 @@ import org.openhab.binding.hdpowerview.internal.api.BatteryKind; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; @@ -98,8 +95,6 @@ private enum RefreshKind { private int shadeId; private boolean isDisposing; - private boolean isGeneration1 = true; - public HDPowerViewShadeHandler(Thing thing) { super(thing); } @@ -255,7 +250,6 @@ private void handleShadeCommand(String channelId, Command command, HDPowerViewWe * @param shadeData the ShadeData to be used. */ protected void onReceiveUpdate(ShadeData shadeData) { - isGeneration1 = shadeData.version() == 1; updateStatus(ThingStatus.ONLINE); updateCapabilities(shadeData); updateSoftProperties(shadeData); @@ -264,7 +258,7 @@ protected void onReceiveUpdate(ShadeData shadeData) { if (shadePosition != null) { updatePositionStates(shadePosition); } - updateBatteryStates(shadeData); + updateBatteryStates(shadeData.batteryStatus, shadeData.batteryStrength); updateSignalStrengthState(shadeData.signalStrength); } @@ -340,7 +334,7 @@ private void updateSoftProperties(ShadeData shadeData) { private void updateFirmwareProperties(ShadeData shadeData) { Map properties = editProperties(); Firmware shadeFirmware = shadeData.firmware; - Firmware motorFirmware = (shadeData.version() == 1) ? ((ShadeDataV1) shadeData).motor : null; + Firmware motorFirmware = shadeData.motor; if (shadeFirmware != null) { properties.put(Thing.PROPERTY_FIRMWARE_VERSION, shadeFirmware.toString()); } @@ -405,9 +399,8 @@ private void updatePositionStates(ShadePosition shadePos) { updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePos.getState(capabilities, SECONDARY_POSITION)); } - private void updateBatteryStates(ShadeData shadeData) { - updateBatteryLevelStates(shadeData.batteryStatus); - double batteryStrength = shadeData.version() == 1 ? ((ShadeDataV1) shadeData).batteryStrength : 0; + private void updateBatteryStates(int batteryStatus, double batteryStrength) { + updateBatteryLevelStates(batteryStatus); updateState(CHANNEL_SHADE_BATTERY_VOLTAGE, batteryStrength > 0 ? new QuantityType<>(batteryStrength / 10, Units.VOLT) : UnDefType.UNDEF); } @@ -448,7 +441,7 @@ private void moveShade(CoordinateSystem coordSys, int newPercent, HDPowerViewWeb newPosition = shadeData.positions; // if no positions returned, then create a new position if (newPosition == null) { - newPosition = newShadePosition(); + newPosition = new ShadePosition(); } Capabilities capabilities = getCapabilitiesOrDefault(); // set the new position value, and write the positions to the hub @@ -577,7 +570,7 @@ private void doRefreshShade(RefreshKind kind) { break; case BATTERY_LEVEL: shadeData = webTargets.refreshShadeBatteryLevel(shadeId); - updateBatteryStates(shadeData); + updateBatteryStates(shadeData.batteryStatus, shadeData.batteryStrength); break; default: throw new NotSupportedException("Unsupported refresh kind " + kind.toString()); @@ -655,24 +648,4 @@ private void updateDynamicChannels(Capabilities capabilities, ShadeData shade) { updateThing(editThing().withoutChannels(removeList).build()); } } - - private ShadePosition newShadePosition() { - return isGeneration1 ? new ShadePositionV1() : new ShadePositionV3(); - } - - /** - * Update position states with the new position provided by an SSE event. - * - * @param shadePosition the new position - */ - public void sseShadePosition(ShadePosition shadePosition) { - if (thing.getStatus() == ThingStatus.ONLINE) { - Capabilities capabilities = this.capabilities; - if (capabilities != null) { - updateState(CHANNEL_SHADE_POSITION, shadePosition.getState(capabilities, PRIMARY_POSITION)); - updateState(CHANNEL_SHADE_VANE, shadePosition.getState(capabilities, VANE_TILT_POSITION)); - updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePosition.getState(capabilities, SECONDARY_POSITION)); - } - } - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index cc4d76d24ddf4..c118891fa9855 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -5,6 +5,8 @@ binding.hdpowerview.description = The Hunter Douglas PowerView binding provides # thing types +thing-type.hdpowerview.gen3.label = PowerView Hub Generation 3 +thing-type.hdpowerview.gen3.description = Hunter Douglas (Luxaflex) PowerView Hub Generation 3 thing-type.hdpowerview.hub.label = PowerView Hub thing-type.hdpowerview.hub.description = Hunter Douglas (Luxaflex) PowerView Hub thing-type.hdpowerview.repeater.label = PowerView Repeater @@ -19,11 +21,17 @@ thing-type.hdpowerview.shade.channel.repeaterRssi.label = Repeater RSSI thing-type.hdpowerview.shade.channel.repeaterRssi.description = Received Signal Strength Indicator for Repeater thing-type.hdpowerview.shade.channel.secondary.label = Secondary Position thing-type.hdpowerview.shade.channel.secondary.description = The secondary vertical position (on top-down/bottom-up shades) +thing-type.hdpowerview.shade3.label = PowerView Shade +thing-type.hdpowerview.shade3.description = Hunter Douglas (Luxaflex) PowerView Shade +thing-type.hdpowerview.shade3.channel.secondary.label = Secondary Position +thing-type.hdpowerview.shade3.channel.secondary.description = The secondary vertical position (on top-down/bottom-up shades) # thing types config -thing-type.config.hdpowerview.hub.generation.label = Hub Generation -thing-type.config.hdpowerview.hub.generation.description = The PowerView Hub Generation +thing-type.config.hdpowerview.gen3.hardRefresh.label = Hard Refresh Interval +thing-type.config.hdpowerview.gen3.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Hub +thing-type.config.hdpowerview.gen3.host.label = Host +thing-type.config.hdpowerview.gen3.host.description = The Host address of the PowerView Hub thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index b752d79e370a7..d438d8646ebea 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -27,11 +27,6 @@ The Host address of the PowerView Hub network-address - - - The PowerView Hub Generation - 0 - The number of milliseconds between fetches of the PowerView Hub shade state @@ -52,4 +47,33 @@ + + + Hunter Douglas (Luxaflex) PowerView Hub Generation 3 + + + + + + + Hunter Douglas (Luxaflex) + PowerView Hub + + + host + + + + + The Host address of the PowerView Hub + network-address + + + + The number of minutes between hard refreshes from the PowerView Hub + 180 + + + + diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml index a8dd168767b77..53d64243d74a8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml @@ -43,4 +43,34 @@ + + + + + + Hunter Douglas (Luxaflex) PowerView Shade + + + + + + The secondary vertical position (on top-down/bottom-up shades) + + + + + + + + + + Hunter Douglas (Luxaflex) + PowerView Motorized Shade + + + id + + + + diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java index 8c92ec0fd366c..ba6cb99574fc4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java @@ -24,12 +24,10 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; @@ -67,7 +65,7 @@ private void setUp() { logger.setLevel(Level.OFF); builder = AutomationChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID); - Scene scene = new SceneV1(); + Scene scene = new Scene(); scene.id = 1; scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes()); scenes = new ArrayList<>(List.of(scene)); @@ -80,7 +78,7 @@ private void setUp() { @Test public void sceneSunriseWeekends() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.daySaturday = true; scheduledEvent.daySunday = true; @@ -93,7 +91,7 @@ public void sceneSunriseWeekends() { @Test public void sceneSunsetWeekdays() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.dayWednesday = true; @@ -109,7 +107,7 @@ public void sceneSunsetWeekdays() { @Test public void sceneTimeAllDays() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.dayWednesday = true; @@ -129,7 +127,7 @@ public void sceneTimeAllDays() { @Test public void sceneMinutesBeforeSunriseMondayTuesday() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.minute = -15; @@ -143,7 +141,7 @@ public void sceneMinutesBeforeSunriseMondayTuesday() { @Test public void sceneHoursMinutesAfterSunriseMonday() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.dayMonday = true; scheduledEvent.minute = 61; @@ -156,7 +154,7 @@ public void sceneHoursMinutesAfterSunriseMonday() { @Test public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayWednesday = true; scheduledEvent.dayThursday = true; scheduledEvent.dayFriday = true; @@ -171,7 +169,7 @@ public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() { @Test public void sceneHourAfterSunsetFridaySaturdaySunday() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayFriday = true; scheduledEvent.daySaturday = true; scheduledEvent.daySunday = true; @@ -226,7 +224,7 @@ public void emptyListWhenNoScenesOrSceneCollections() { @Test public void emptyListWhenNoSceneForScheduledEvent() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithSceneCollection( + ScheduledEvent scheduledEvent = createScheduledEventWithSceneCollection( ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build(); @@ -236,7 +234,7 @@ public void emptyListWhenNoSceneForScheduledEvent() { @Test public void emptyListWhenNoSceneCollectionForScheduledEvent() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents) @@ -247,7 +245,7 @@ public void emptyListWhenNoSceneCollectionForScheduledEvent() { @Test public void groupAndIdAreCorrect() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.id = 42; List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build(); @@ -257,16 +255,16 @@ public void groupAndIdAreCorrect() { assertEquals(Integer.toString(scheduledEvent.id), channels.get(0).getUID().getIdWithoutGroup()); } - private ScheduledEventV1 createScheduledEventWithScene(int eventType) { - ScheduledEventV1 scheduledEvent = new ScheduledEventV1(); + private ScheduledEvent createScheduledEventWithScene(int eventType) { + ScheduledEvent scheduledEvent = new ScheduledEvent(); scheduledEvent.id = 1; scheduledEvent.sceneId = scenes.get(0).id; scheduledEvent.eventType = eventType; return scheduledEvent; } - private ScheduledEventV1 createScheduledEventWithSceneCollection(int eventType) { - ScheduledEventV1 scheduledEvent = new ScheduledEventV1(); + private ScheduledEvent createScheduledEventWithSceneCollection(int eventType) { + ScheduledEvent scheduledEvent = new ScheduledEvent(); scheduledEvent.id = 1; scheduledEvent.sceneCollectionId = sceneCollections.get(0).id; scheduledEvent.eventType = eventType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java deleted file mode 100644 index b7474b6c251b7..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview; - -import static org.junit.jupiter.api.Assertions.*; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; -import java.util.List; - -import javax.ws.rs.client.ClientBuilder; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jetty.client.HttpClient; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; -import org.openhab.core.library.types.PercentType; -import org.openhab.core.types.UnDefType; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; - -/** - * Unit tests for Generation 3 DTO's. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class Generation3DtoTest { - - private final HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV3(new HttpClient(), - Mockito.mock(ClientBuilder.class), Mockito.mock(SseEventSourceFactory.class), ""); - private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); - - private String loadJson(String filename) throws IOException { - try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { - if (inputStream == null) { - throw new IOException("inputstream is null"); - } - byte[] bytes = inputStream.readAllBytes(); - if (bytes == null) { - throw new IOException("Resulting byte-array empty"); - } - return new String(bytes, StandardCharsets.UTF_8); - } - } - - /** - * Test JSON scenes response. - */ - @Test - public void testScenesParsing() throws IOException { - try { - Method method = webTargets.getClass().getDeclaredMethod("toScenes", String.class); - method.setAccessible(true); - String json = loadJson("_v3/scenes.json"); - Object result = method.invoke(webTargets, json); - assertTrue(result instanceof Scenes); - List sceneData = ((Scenes) result).sceneData; - assertNotNull(sceneData); - assertEquals(1, sceneData.size()); - Scene scene = sceneData.get(0); - assertEquals("Open All Office Shades\n ABC", scene.getName()); - assertEquals(234, scene.id); - assertEquals(3, scene.version()); - } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException - | InvocationTargetException e) { - fail(e.getMessage()); - } - } - - /** - * Test JSON shades response. - */ - @Test - public void testShadesParsing() throws IOException { - try { - Method method = webTargets.getClass().getDeclaredMethod("toShades", String.class); - method.setAccessible(true); - String json = loadJson("_v3/shades.json"); - Object result = method.invoke(webTargets, json); - assertTrue(result instanceof Shades); - List shadeDataList = ((Shades) result).shadeData; - assertNotNull(shadeDataList); - assertEquals(1, shadeDataList.size()); - ShadeData shadeData = shadeDataList.get(0); - assertEquals("Shade 2 ABC", shadeData.getName()); - assertEquals(789, shadeData.id); - assertEquals(3, shadeData.version()); - } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException - | InvocationTargetException e) { - fail(e.getMessage()); - } - } - - /** - * Test JSON automation response. - */ - @Test - public void testAutomationParsing() throws IOException { - try { - Method method = webTargets.getClass().getDeclaredMethod("toScheduledEvents", String.class); - method.setAccessible(true); - String json = loadJson("_v3/automations.json"); - Object result = method.invoke(webTargets, json); - assertTrue(result instanceof ScheduledEvents); - List scheduledEventList = ((ScheduledEvents) result).scheduledEventData; - assertNotNull(scheduledEventList); - assertEquals(1, scheduledEventList.size()); - ScheduledEvent scheduledEvent = scheduledEventList.get(0); - assertEquals(33, scheduledEvent.id); - assertTrue(scheduledEvent.enabled); - } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException - | InvocationTargetException e) { - fail(e.getMessage()); - } - } - - /** - * Test JSON shade event response. - */ - @Test - public void testShadeEventParsing() throws IOException { - try { - Method method = webTargets.getClass().getDeclaredMethod("toShadeData2", String.class); - method.setAccessible(true); - String json = loadJson("_v3/shade-event.json"); - Object result = method.invoke(webTargets, json); - assertTrue(result instanceof ShadeData); - ShadeData shadeData = ((ShadeData) result); - assertEquals(3, shadeData.version()); - ShadePosition position = shadeData.positions; - assertNotNull(position); - assertEquals(PercentType.valueOf("99"), - position.getState(DB.getCapabilities(0), CoordinateSystem.PRIMARY_POSITION)); - assertEquals(PercentType.valueOf("98"), - position.getState(DB.getCapabilities(0), CoordinateSystem.SECONDARY_POSITION)); - assertEquals(PercentType.ZERO, - position.getState(DB.getCapabilities(0), CoordinateSystem.VANE_TILT_POSITION)); - } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException - | InvocationTargetException e) { - fail(e.getMessage()); - } - } - - /** - * Test JSON shade position setting. - */ - @Test - public void testShadePositions() { - ShadePositionV3 pos; - Capabilities caps; - - caps = DB.getCapabilities(0); // test with only primary support - pos = new ShadePositionV3(); - pos.setPosition(caps, CoordinateSystem.PRIMARY_POSITION, 11); - pos.setPosition(caps, CoordinateSystem.SECONDARY_POSITION, 22); - pos.setPosition(caps, CoordinateSystem.VANE_TILT_POSITION, 33); - assertEquals(PercentType.valueOf("11"), pos.getState(caps, CoordinateSystem.PRIMARY_POSITION)); - assertEquals(UnDefType.UNDEF, pos.getState(caps, CoordinateSystem.SECONDARY_POSITION)); - assertEquals(UnDefType.UNDEF, pos.getState(caps, CoordinateSystem.VANE_TILT_POSITION)); - - caps = DB.getCapabilities(9);// test with primary, secondary, and tilt support - pos = new ShadePositionV3(); - pos.setPosition(caps, CoordinateSystem.PRIMARY_POSITION, 11); - pos.setPosition(caps, CoordinateSystem.SECONDARY_POSITION, 22); - pos.setPosition(caps, CoordinateSystem.VANE_TILT_POSITION, 33); - assertEquals(PercentType.valueOf("11"), pos.getState(caps, CoordinateSystem.PRIMARY_POSITION)); - assertEquals(PercentType.valueOf("22"), pos.getState(caps, CoordinateSystem.SECONDARY_POSITION)); - assertEquals(PercentType.valueOf("33"), pos.getState(caps, CoordinateSystem.VANE_TILT_POSITION)); - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java index e59f341cdefd1..19cd35c7c33ee 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java @@ -21,27 +21,21 @@ import java.util.List; import java.util.Objects; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; import com.google.gson.Gson; @@ -54,8 +48,7 @@ @NonNullByDefault public class HDPowerViewJUnitTests { - private final Gson gson = new HDPowerViewWebTargetsV1(new HttpClient(), Mockito.mock(ClientBuilder.class), - Mockito.mock(SseEventSourceFactory.class), "").getGson(); + private Gson gson = new Gson(); private T getObjectFromJson(String filename, Class clazz) throws IOException { try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { @@ -76,7 +69,7 @@ private T getObjectFromJson(String filename, Class clazz) throws IOExcept */ @Test public void shadeNameIsDecoded() throws IOException { - Shades shades = getObjectFromJson("_v1/shades.json", Shades.class); + Shades shades = getObjectFromJson("shades.json", Shades.class); List shadeData = shades.shadeData; assertNotNull(shadeData); assertEquals(3, shadeData.size()); @@ -89,7 +82,7 @@ public void shadeNameIsDecoded() throws IOException { */ @Test public void testBatteryKind() throws IOException { - Shades shades = getObjectFromJson("_v1/shades.json", Shades.class); + Shades shades = getObjectFromJson("shades.json", Shades.class); List shadeData = shades.shadeData; assertNotNull(shadeData); ShadeData shade = shadeData.get(0); @@ -103,7 +96,7 @@ public void testBatteryKind() throws IOException { */ @Test public void sceneNameIsDecoded() throws IOException { - Scenes scenes = getObjectFromJson("_v1/scenes.json", Scenes.class); + Scenes scenes = getObjectFromJson("scenes.json", Scenes.class); List sceneData = scenes.sceneData; assertNotNull(sceneData); assertEquals(4, sceneData.size()); @@ -116,7 +109,7 @@ public void sceneNameIsDecoded() throws IOException { */ @Test public void sceneCollectionNameIsDecoded() throws IOException { - SceneCollections sceneCollections = getObjectFromJson("_v1/sceneCollections.json", SceneCollections.class); + SceneCollections sceneCollections = getObjectFromJson("sceneCollections.json", SceneCollections.class); List sceneCollectionData = sceneCollections.sceneCollectionData; assertNotNull(sceneCollectionData); @@ -131,7 +124,7 @@ public void sceneCollectionNameIsDecoded() throws IOException { */ @Test public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws IOException { - Shades shades = getObjectFromJson("_v1/duette.json", Shades.class); + Shades shades = getObjectFromJson("duette.json", Shades.class); List shadesData = shades.shadeData; assertNotNull(shadesData); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java index 588fcfa9ef23f..b8670bb91ec92 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java @@ -13,25 +13,20 @@ package org.openhab.binding.hdpowerview; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.PRIMARY_POSITION; +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; import java.util.List; import java.util.regex.Pattern; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; @@ -39,7 +34,6 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; /** * Unit tests for HD PowerView binding. @@ -89,8 +83,7 @@ public void testOnlineCommunication() { fail(e.getMessage()); } - HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV1(client, Mockito.mock(ClientBuilder.class), - Mockito.mock(SseEventSourceFactory.class), hubIPAddress); + HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress); assertNotNull(webTargets); int shadeId = 0; @@ -175,7 +168,7 @@ public void testOnlineCommunication() { int position = ((PercentType) pos).intValue(); position = position + ((position <= 10) ? 5 : -5); - ShadePosition targetPosition = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, + ShadePosition targetPosition = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, position); assertNotNull(targetPosition); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java index 2b96fa966540e..eca2a1f2e9ebb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java @@ -24,8 +24,7 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; @@ -111,7 +110,7 @@ public void emptyListWhenNoScenes() { } private List createScenes() { - Scene scene = new SceneV1(); + Scene scene = new Scene(); scene.id = 1; scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes()); return new ArrayList<>(List.of(scene)); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java index 55688bccf1c65..50dcd0e7ff4c2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java @@ -18,7 +18,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; @@ -91,7 +90,7 @@ private void assertShadePosition(State position, int value) { @Test public void testCaps1ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -105,7 +104,7 @@ public void testCaps1ShadePositionParsingFullyUp() { @Test public void testCaps1ShadePositionParsingShadeFullyDown1() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -119,7 +118,7 @@ public void testCaps1ShadePositionParsingShadeFullyDown1() { @Test public void testCaps1ShadePositionParsingShadeFullyDown2() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 0); + ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -133,7 +132,7 @@ public void testCaps1ShadePositionParsingShadeFullyDown2() { @Test public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 88); + ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 88); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -148,7 +147,7 @@ public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() { @Test public void testDualRailConstraints() { Capabilities capabilities = db.getCapabilities(7); - ShadePosition test = new ShadePositionV1(); + ShadePosition test = new ShadePosition(); // ==== OK !! primary at bottom, secondary at top ==== test.setPosition(capabilities, PRIMARY_POSITION, 100).setPosition(capabilities, SECONDARY_POSITION, 0); @@ -208,42 +207,42 @@ public void testDuoliteShadePositionParsing() { ShadePosition test; // both shades up - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 50% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 50); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 50); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 50); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 0% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 0% down (ALTERNATE) - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 50% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 50); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 50); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 50); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 100% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 100); @@ -261,70 +260,70 @@ public void testDuoliteTiltShadePositionParsing() { ShadePosition test; // front shade up - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 30% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 30); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0); // tilt 0% - test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0); // tilt 30% - test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 30); + test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30); // tilt 100% - test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 0% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 30% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 30); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 30); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 100% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 100); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // test constraints on impossible values: primary 30% => tilt 30% - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30).setPosition(capabilities, + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30).setPosition(capabilities, VANE_TILT_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); @@ -332,7 +331,7 @@ public void testDuoliteTiltShadePositionParsing() { assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30); // test constraints on impossible values: primary 30% => tilt 30% => back shade 30% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30) + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30) .setPosition(capabilities, VANE_TILT_POSITION, 30).setPosition(capabilities, SECONDARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); @@ -347,7 +346,7 @@ public void testDuoliteTiltShadePositionParsing() { @Test public void testCaps0ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(0); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -361,7 +360,7 @@ public void testCaps0ShadePositionParsingFullyUp() { @Test public void testCap0ShadePositionParsingShadeFullyDown() { Capabilities capabilities = db.getCapabilities(0); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -385,7 +384,7 @@ private void assertShadePosition(State actual, State target) { @Test public void testType44ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(44, null); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -399,7 +398,7 @@ public void testType44ShadePositionParsingFullyUp() { @Test public void testType44ShadePositionParsingShadeFullyDownVaneOpen() { Capabilities capabilities = db.getCapabilities(44, null); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 88); + ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 88); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java new file mode 100644 index 0000000000000..d6985fd9894d3 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.gen3; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.UnDefType; + +import com.google.gson.Gson; + +/** + * Unit tests for Generation 3 DTO's. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class Generation3DtoTest { + + private final Gson gson = new Gson(); + + private String loadJson(String filename) throws IOException { + try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { + if (inputStream == null) { + throw new IOException("inputstream is null"); + } + byte[] bytes = inputStream.readAllBytes(); + if (bytes == null) { + throw new IOException("Resulting byte-array empty"); + } + return new String(bytes, StandardCharsets.UTF_8); + } + } + + /** + * Test JSON automation response. + */ + @Test + public void testAutomationParsing() throws IOException { + String json = loadJson("gen3/automations.json"); + List scheduledEventList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_EVENTS); + assertNotNull(scheduledEventList); + assertEquals(1, scheduledEventList.size()); + ScheduledEvent3 scheduledEvent = scheduledEventList.get(0); + assertEquals(33, scheduledEvent.id); + assertTrue(scheduledEvent.enabled); + } + + /** + * Test JSON scene event response. + */ + @Test + public void testSceneEventParsing() throws IOException { + String json = loadJson("gen3/scene-event.json"); + SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); + assertNotNull(sceneEvent); + Scene3 scene = sceneEvent.getScene(); + assertNotNull(scene); + assertEquals("Open All Office Shades\n Open All Office Shades", scene.getName()); + assertEquals(234, scene.getId()); + } + + /** + * Test JSON scenes response. + */ + @Test + public void testScenesParsing() throws IOException { + String json = loadJson("gen3/scenes.json"); + List sceneList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_SCENES); + assertNotNull(sceneList); + assertEquals(1, sceneList.size()); + Scene3 scene = sceneList.get(0); + assertEquals("Open All Office Shades\n ABC", scene.getName()); + assertEquals(234, scene.getId()); + } + + /** + * Test JSON shade event response. + */ + @Test + public void testShadeEventParsing() throws IOException { + String json = loadJson("gen3/shade-event.json"); + ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); + assertNotNull(shadeEvent); + ShadePosition3 position = shadeEvent.getCurrentPositions(); + assertNotNull(position); + assertEquals(PercentType.valueOf("99"), position.getState(CoordinateSystem.PRIMARY_POSITION)); + assertEquals(PercentType.valueOf("98"), position.getState(CoordinateSystem.SECONDARY_POSITION)); + assertEquals(PercentType.ZERO, position.getState(CoordinateSystem.VANE_TILT_POSITION)); + } + + /** + * Test JSON shade position setting. + */ + @Test + public void testShadePositions() { + ShadePosition3 pos; + + pos = new ShadePosition3(); + pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); + assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION)); + assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.SECONDARY_POSITION)); + assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.VANE_TILT_POSITION)); + + pos = new ShadePosition3(); + pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); + pos.setPosition(CoordinateSystem.SECONDARY_POSITION, 22); + pos.setPosition(CoordinateSystem.VANE_TILT_POSITION, 33); + assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION)); + assertEquals(PercentType.valueOf("22"), pos.getState(CoordinateSystem.SECONDARY_POSITION)); + assertEquals(PercentType.valueOf("33"), pos.getState(CoordinateSystem.VANE_TILT_POSITION)); + } + + /** + * Test JSON shades response. + */ + @Test + public void testShadesParsing() throws IOException { + String json = loadJson("gen3/shades.json"); + List shadeList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_SHADES); + assertNotNull(shadeList); + assertEquals(1, shadeList.size()); + Shade3 shadeData = shadeList.get(0); + assertEquals("Shade 2 ABC", shadeData.getName()); + assertEquals(789, shadeData.getId()); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/duette.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/duette.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/automations.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/automations.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scene-event.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scene-event.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shade-event.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shade-event.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shades.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/sceneCollections.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/sceneCollections.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json From 6ce09b9d230f889f6412c41883a4042e50a501d4 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 26 Oct 2022 19:51:04 +0100 Subject: [PATCH 14/64] [hdpowerview] cosmetics Signed-off-by: Andrew Fiddian-Green --- .../HDPowerViewDeviceDiscoveryServiceV3.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java index f7051465b6e20..b736015a46aeb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java @@ -13,7 +13,6 @@ package org.openhab.binding.hdpowerview.internal.gen3.discovery; import java.util.Collections; -import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -89,19 +88,19 @@ private Runnable createScanner() { } private void discoverShades(HDPowerViewWebTargets3 webTargets) throws HubProcessingException { - List shades = webTargets.getShades(); ThingUID bridgeUid = hub.getThing().getUID(); - for (Shade3 shadeData : shades) { - if (shadeData.getId() == 0) { + for (Shade3 shade : webTargets.getShades()) { + if (shade.getId() == 0) { continue; } - String id = Integer.toString(shadeData.getId()); + + String id = Integer.toString(shade.getId()); ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3, bridgeUid, id); - DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shadeData.getName()) + DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shade.getName()) .withBridge(bridgeUid).withProperty(HDPowerViewShadeConfiguration.ID, id) .withRepresentationProperty(HDPowerViewShadeConfiguration.ID); - String type = shadeData.getTypeString(); + String type = shade.getTypeString(); if (type != null) { builder.withProperty(HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, type); } From 0684ff350602f3f22e8e820811c9929a7037f129 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 26 Oct 2022 19:56:38 +0100 Subject: [PATCH 15/64] [hdpowerview] rename classes Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/HDPowerViewHandlerFactory.java | 4 ++-- ...iceV3.java => HDPowerViewDeviceDiscoveryService3.java} | 6 +++--- ...ntV3.java => HDPowerViewHubDiscoveryParticipant3.java} | 4 ++-- .../gen3/dto/{SceneEvent.java => SceneEvent3.java} | 2 +- .../gen3/dto/{ShadeEvent.java => ShadeEvent3.java} | 2 +- .../internal/gen3/webtargets/HDPowerViewWebTargets3.java | 8 ++++---- .../binding/hdpowerview/gen3/Generation3DtoTest.java | 8 ++++---- 7 files changed, 17 insertions(+), 17 deletions(-) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/{HDPowerViewDeviceDiscoveryServiceV3.java => HDPowerViewDeviceDiscoveryService3.java} (95%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/{HDPowerViewHubDiscoveryParticipantV3.java => HDPowerViewHubDiscoveryParticipant3.java} (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/{SceneEvent.java => SceneEvent3.java} (97%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/{ShadeEvent.java => ShadeEvent3.java} (97%) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index c59011021297c..799f05196ac58 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -20,7 +20,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewDeviceDiscoveryService; -import org.openhab.binding.hdpowerview.internal.gen3.discovery.HDPowerViewDeviceDiscoveryServiceV3; +import org.openhab.binding.hdpowerview.internal.gen3.discovery.HDPowerViewDeviceDiscoveryService3; import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewShadeHandler3; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; @@ -82,7 +82,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3.equals(thingTypeUID)) { HDPowerViewHubHandler3 handler = new HDPowerViewHubHandler3((Bridge) thing, httpClient, translationProvider, clientBuilder, eventSourceFactory); - registerService(new HDPowerViewDeviceDiscoveryServiceV3(handler)); + registerService(new HDPowerViewDeviceDiscoveryService3(handler)); return handler; } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3.equals(thingTypeUID)) { return new HDPowerViewShadeHandler3(thing); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java similarity index 95% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java index b736015a46aeb..532369088d594 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java @@ -37,15 +37,15 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewDeviceDiscoveryServiceV3 extends AbstractDiscoveryService { +public class HDPowerViewDeviceDiscoveryService3 extends AbstractDiscoveryService { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewDeviceDiscoveryServiceV3.class); + private final Logger logger = LoggerFactory.getLogger(HDPowerViewDeviceDiscoveryService3.class); private final HDPowerViewHubHandler3 hub; private final Runnable scanner; private @Nullable ScheduledFuture backgroundFuture; private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase(); - public HDPowerViewDeviceDiscoveryServiceV3(HDPowerViewHubHandler3 hub) { + public HDPowerViewDeviceDiscoveryService3(HDPowerViewHubHandler3 hub) { super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3), 600, true); this.hub = hub; this.scanner = createScanner(); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java index 8f2273250a328..acaecd0f15247 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java @@ -34,9 +34,9 @@ */ @NonNullByDefault @Component -public class HDPowerViewHubDiscoveryParticipantV3 extends HDPowerViewHubDiscoveryParticipant { +public class HDPowerViewHubDiscoveryParticipant3 extends HDPowerViewHubDiscoveryParticipant { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV3.class); + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant3.class); @Override public @Nullable DiscoveryResult createResult(ServiceInfo service) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java index cb8fb302b6d75..bc54624dc2298 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java @@ -20,7 +20,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class SceneEvent { +public class SceneEvent3 { private int id; // private @NonNullByDefault({}) String evt; // private @NonNullByDefault({}) String isoDate; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java index af38fc9c4d96b..4956dbcc99f1d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java @@ -20,7 +20,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadeEvent { +public class ShadeEvent3 { private int id; // private @NonNullByDefault({}) String evt; // private @NonNullByDefault({}) String isoDate; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java index 3c3c540d13cc6..97ec14353001b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java @@ -42,10 +42,10 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Info3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; import org.openhab.core.thing.Thing; @@ -370,7 +370,7 @@ public void moveShade(int shadeId, ShadePosition3 position) throws HubProcessing private void onSceneEvent(InboundSseEvent sseEvent) { String json = sseEvent.readData(); logger.trace("onSceneEvent() json:{}", json); - SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); + SceneEvent3 sceneEvent = gson.fromJson(json, SceneEvent3.class); if (sceneEvent != null) { Scene3 scene = sceneEvent.getScene(); hubHandler.onSceneEvent(scene); @@ -385,7 +385,7 @@ private void onSceneEvent(InboundSseEvent sseEvent) { private void onShadeEvent(InboundSseEvent sseEvent) { String json = sseEvent.readData(); logger.trace("onShadeEvent() json:{}", json); - ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); + ShadeEvent3 shadeEvent = gson.fromJson(json, ShadeEvent3.class); if (shadeEvent != null) { ShadePosition3 positions = shadeEvent.getCurrentPositions(); hubHandler diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java index d6985fd9894d3..49c04df2363e9 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java @@ -24,10 +24,10 @@ import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; import org.openhab.core.library.types.PercentType; @@ -78,7 +78,7 @@ public void testAutomationParsing() throws IOException { @Test public void testSceneEventParsing() throws IOException { String json = loadJson("gen3/scene-event.json"); - SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); + SceneEvent3 sceneEvent = gson.fromJson(json, SceneEvent3.class); assertNotNull(sceneEvent); Scene3 scene = sceneEvent.getScene(); assertNotNull(scene); @@ -106,7 +106,7 @@ public void testScenesParsing() throws IOException { @Test public void testShadeEventParsing() throws IOException { String json = loadJson("gen3/shade-event.json"); - ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); + ShadeEvent3 shadeEvent = gson.fromJson(json, ShadeEvent3.class); assertNotNull(shadeEvent); ShadePosition3 position = shadeEvent.getCurrentPositions(); assertNotNull(position); From 0f2721f69b0d974bf873fd960fb0d3e4918d7982 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Thu, 27 Oct 2022 13:06:36 +0100 Subject: [PATCH 16/64] [hdpowerview] (interim) adopt reviewer suggestions Signed-off-by: Andrew Fiddian-Green --- .../internal/HDPowerViewBindingConstants.java | 8 ++++++-- .../internal/HDPowerViewHandlerFactory.java | 4 ++-- .../HDPowerViewHubDiscoveryParticipant.java | 6 +----- .../HDPowerViewDeviceDiscoveryService3.java | 8 +++----- .../HDPowerViewHubDiscoveryParticipant3.java | 6 +++--- .../hdpowerview/internal/gen3/dto/Info3.java | 2 +- .../hdpowerview/internal/gen3/dto/Scene3.java | 2 +- .../hdpowerview/internal/gen3/dto/SceneEvent3.java | 4 +--- .../internal/gen3/dto/ScheduledEvent3.java | 2 +- .../hdpowerview/internal/gen3/dto/Shade3.java | 3 +-- .../hdpowerview/internal/gen3/dto/ShadeEvent3.java | 6 +----- .../internal/gen3/dto/ShadePosition3.java | 3 +-- .../gen3/handler/HDPowerViewHubHandler3.java | 2 +- .../gen3/handler/HDPowerViewShadeHandler3.java | 2 +- .../gen3/webtargets/HDPowerViewWebTargets3.java | 2 +- .../resources/OH-INF/i18n/hdpowerview.properties | 14 +++++++------- .../src/main/resources/OH-INF/thing/bridge.xml | 12 ++++++------ 17 files changed, 38 insertions(+), 48 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java index 610e0d82d025f..6b6ccc3fefb45 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingTypeUID; @@ -77,9 +78,12 @@ public class HDPowerViewBindingConstants { public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_HUB, THING_TYPE_SHADE, THING_TYPE_REPEATER); + public static final Pattern VALID_IP_V4_ADDRESS = Pattern + .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); + // generation 3 - public static final ThingTypeUID THING_TYPE_HUB_GEN3 = new ThingTypeUID(BINDING_ID, "gen3"); - public static final ThingTypeUID THING_TYPE_SHADE_GEN3 = new ThingTypeUID(BINDING_ID, "shade3"); + public static final ThingTypeUID THING_TYPE_GATEWAY3 = new ThingTypeUID(BINDING_ID, "gateway3"); + public static final ThingTypeUID THING_TYPE_SHADE3 = new ThingTypeUID(BINDING_ID, "shade3"); public static final String PROPERTY_NAME = "name"; public static final String PROPERTY_POWER_TYPE = "powerType"; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 799f05196ac58..6787e1e861f97 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -79,12 +79,12 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); // generation 3 - if (HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3.equals(thingTypeUID)) { + if (HDPowerViewBindingConstants.THING_TYPE_GATEWAY3.equals(thingTypeUID)) { HDPowerViewHubHandler3 handler = new HDPowerViewHubHandler3((Bridge) thing, httpClient, translationProvider, clientBuilder, eventSourceFactory); registerService(new HDPowerViewDeviceDiscoveryService3(handler)); return handler; - } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3.equals(thingTypeUID)) { + } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE3.equals(thingTypeUID)) { return new HDPowerViewShadeHandler3(thing); } else // generation 1/2 diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java index d9c9bf063e795..924ef4c2b35b6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java @@ -12,11 +12,10 @@ */ package org.openhab.binding.hdpowerview.internal.discovery; -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; import java.util.Collections; import java.util.Set; -import java.util.regex.Pattern; import javax.jmdns.ServiceInfo; @@ -43,9 +42,6 @@ public class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticip private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant.class); - public static final Pattern VALID_IP_V4_ADDRESS = Pattern - .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); - @Override public Set getSupportedThingTypeUIDs() { return Collections.singleton(THING_TYPE_HUB); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java index 532369088d594..fd456543ea11a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java @@ -20,7 +20,6 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; @@ -32,7 +31,7 @@ import org.slf4j.LoggerFactory; /** - * Discovers HD PowerView Shades and Repeaters from an existing hub + * Discovers shades in an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @@ -43,10 +42,9 @@ public class HDPowerViewDeviceDiscoveryService3 extends AbstractDiscoveryService private final HDPowerViewHubHandler3 hub; private final Runnable scanner; private @Nullable ScheduledFuture backgroundFuture; - private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase(); public HDPowerViewDeviceDiscoveryService3(HDPowerViewHubHandler3 hub) { - super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3), 600, true); + super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE3), 600, true); this.hub = hub; this.scanner = createScanner(); } @@ -95,7 +93,7 @@ private void discoverShades(HDPowerViewWebTargets3 webTargets) throws HubProcess } String id = Integer.toString(shade.getId()); - ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3, bridgeUid, id); + ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE3, bridgeUid, id); DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shade.getName()) .withBridge(bridgeUid).withProperty(HDPowerViewShadeConfiguration.ID, id) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java index acaecd0f15247..4985bfb733119 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.hdpowerview.internal.gen3.discovery; -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3; +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; import javax.jmdns.ServiceInfo; @@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory; /** - * Discovers HD PowerView generation 3 hubs by means of mDNS. + * Discovers HD PowerView Generation 3 Gateways by means of mDNS. * * @author Andrew Fiddian-Green - Initial contribution. */ @@ -42,7 +42,7 @@ public class HDPowerViewHubDiscoveryParticipant3 extends HDPowerViewHubDiscovery public @Nullable DiscoveryResult createResult(ServiceInfo service) { for (String host : service.getHostAddresses()) { if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_HUB_GEN3, host.replace('.', '_')); + ThingUID thingUID = new ThingUID(THING_TYPE_GATEWAY3, host.replace('.', '_')); DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java index e0d97be96d655..8b520ebe0a3e8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * DTO for the Generation 3 gateway information. + * DTO for the Generation 3 Gateway information. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java index 8d07cdd7c6342..5a167fcd926d2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java @@ -19,7 +19,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Scene object as returned by an HD PowerView hub of Generation 3. + * Scene object as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution. */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java index bc54624dc2298..c1db4851332af 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java @@ -15,15 +15,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Scene SSE event object as supplied an HD PowerView hub of Generation 3. + * Scene SSE event object as supplied an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault public class SceneEvent3 { private int id; - // private @NonNullByDefault({}) String evt; - // private @NonNullByDefault({}) String isoDate; private @NonNullByDefault({}) Scene3 scene; public int getId() { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java index 74cd526170f06..a7e3d8afb7d93 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java @@ -20,7 +20,7 @@ import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; /** - * class for scheduled event as returned by an HD PowerView Generation 3 hub. + * class for scheduled event as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java index f71fe9d498075..8e70d6a0ca8ad 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java @@ -26,7 +26,7 @@ import org.openhab.core.types.UnDefType; /** - * State of a Shade as returned by an HD PowerView hub of Generation 3. + * State of a Shade as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @@ -39,7 +39,6 @@ public class Shade3 { private @Nullable Integer capabilities; private @Nullable String powerType; // TODO unclear if this is String or Integer private @Nullable Integer batteryStatus; - // private @Nullable Integer roomId; private @Nullable Integer signalStrength; private @Nullable String bleName; private @Nullable Firmware firmware; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java index 4956dbcc99f1d..8db10da33e7c1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java @@ -15,18 +15,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Shade SSE event object as supplied an HD PowerView hub of Generation 3. + * Shade SSE event object as supplied an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault public class ShadeEvent3 { private int id; - // private @NonNullByDefault({}) String evt; - // private @NonNullByDefault({}) String isoDate; - // private @NonNullByDefault({}) String bleName; private @NonNullByDefault({}) ShadePosition3 currentPositions; - // private @NonNullByDefault({}) ShadePosition3 targetPositions; public ShadePosition3 getCurrentPositions() { return currentPositions; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java index 93800309a5546..06866f5341823 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java @@ -19,7 +19,7 @@ import org.openhab.core.types.UnDefType; /** - * The position of a single shade, as returned by an HD PowerView hub of Generation 3 + * The position of a shade as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @@ -28,7 +28,6 @@ public class ShadePosition3 { private @NonNullByDefault({}) Double primary; private @NonNullByDefault({}) Double secondary; private @NonNullByDefault({}) Double tilt; - // private @NonNullByDefault({}) Double velocity; public State getState(CoordinateSystem posKindCoords) { Double value; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java index fbfe1f8cf3352..0fcaeefcd1028 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java @@ -49,7 +49,7 @@ import org.slf4j.LoggerFactory; /** - * Bridge handler for an HD PowerView hub of Generation 3. + * Bridge handler for an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java index 19fd295d80e2c..1f1db93f7d536 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java @@ -46,7 +46,7 @@ import org.slf4j.LoggerFactory; /** - * Thing handler for an HD PowerView shade of Generation 3. + * Thing handler for shades in an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java index 97ec14353001b..0e1d21d485dc4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java @@ -58,7 +58,7 @@ import com.google.gson.reflect.TypeToken; /** - * JAX-RS targets for communicating with an HD PowerView hub Generation 3. + * JAX-RS targets for communicating with an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index c118891fa9855..b7251366e41fb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -5,8 +5,8 @@ binding.hdpowerview.description = The Hunter Douglas PowerView binding provides # thing types -thing-type.hdpowerview.gen3.label = PowerView Hub Generation 3 -thing-type.hdpowerview.gen3.description = Hunter Douglas (Luxaflex) PowerView Hub Generation 3 +thing-type.hdpowerview.gateway3.label = PowerView Gen3 Gateway +thing-type.hdpowerview.gateway3.description = Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro thing-type.hdpowerview.hub.label = PowerView Hub thing-type.hdpowerview.hub.description = Hunter Douglas (Luxaflex) PowerView Hub thing-type.hdpowerview.repeater.label = PowerView Repeater @@ -28,10 +28,10 @@ thing-type.hdpowerview.shade3.channel.secondary.description = The secondary vert # thing types config -thing-type.config.hdpowerview.gen3.hardRefresh.label = Hard Refresh Interval -thing-type.config.hdpowerview.gen3.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Hub -thing-type.config.hdpowerview.gen3.host.label = Host -thing-type.config.hdpowerview.gen3.host.description = The Host address of the PowerView Hub +thing-type.config.hdpowerview.gateway3.hardRefresh.label = Hard Refresh Interval +thing-type.config.hdpowerview.gateway3.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Gateway +thing-type.config.hdpowerview.gateway3.host.label = Host +thing-type.config.hdpowerview.gateway3.host.description = The Host address of the PowerView Hub thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval @@ -43,7 +43,7 @@ thing-type.config.hdpowerview.hub.refresh.description = The number of millisecon thing-type.config.hdpowerview.repeater.id.label = ID thing-type.config.hdpowerview.repeater.id.description = The numeric ID of the PowerView Repeater in the Hub thing-type.config.hdpowerview.shade.id.label = ID -thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Hub +thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Gateway/Hub # channel group types diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index d438d8646ebea..50a610faa5694 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -47,9 +47,9 @@ - - - Hunter Douglas (Luxaflex) PowerView Hub Generation 3 + + + Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro @@ -57,7 +57,7 @@ Hunter Douglas (Luxaflex) - PowerView Hub + PowerView Gateway host @@ -65,12 +65,12 @@ - The Host address of the PowerView Hub + The Host address of the PowerView Gateway network-address - The number of minutes between hard refreshes from the PowerView Hub + The number of minutes between hard refreshes from the PowerView Gateway 180 From 7a4d352e417ee471cc7b3a35f7296693429479f1 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 14:24:20 +0100 Subject: [PATCH 17/64] [hdpowerview] readme, class names, minor bugs Signed-off-by: Andrew Fiddian-Green --- .../org.openhab.binding.hdpowerview/README.md | 152 +++++++++++------- .../internal/HDPowerViewBindingConstants.java | 2 +- .../internal/HDPowerViewHandlerFactory.java | 14 +- ....java => GatewayDiscoveryParticipant.java} | 31 +++- ...rvice3.java => ShadeDiscoveryService.java} | 16 +- ...{ScheduledEvent3.java => Automation3.java} | 8 +- .../hdpowerview/internal/gen3/dto/Scene3.java | 2 +- .../internal/gen3/dto/SceneEvent3.java | 2 +- .../hdpowerview/internal/gen3/dto/Shade3.java | 2 +- .../internal/gen3/dto/ShadeEvent3.java | 2 +- .../internal/gen3/dto/ShadePosition3.java | 2 +- ...andler3.java => GatewayBridgeHandler.java} | 36 ++--- ...deHandler3.java => ShadeThingHandler.java} | 16 +- ...ebTargets3.java => GatewayWebTargets.java} | 14 +- .../OH-INF/i18n/hdpowerview.properties | 12 +- .../main/resources/OH-INF/thing/bridge.xml | 4 +- .../src/main/resources/OH-INF/thing/shade.xml | 2 +- .../hdpowerview/gen3/Generation3DtoTest.java | 12 +- 18 files changed, 196 insertions(+), 133 deletions(-) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/{HDPowerViewHubDiscoveryParticipant3.java => GatewayDiscoveryParticipant.java} (65%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/{HDPowerViewDeviceDiscoveryService3.java => ShadeDiscoveryService.java} (86%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/{ScheduledEvent3.java => Automation3.java} (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/{HDPowerViewHubHandler3.java => GatewayBridgeHandler.java} (88%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/{HDPowerViewShadeHandler3.java => ShadeThingHandler.java} (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/{HDPowerViewWebTargets3.java => GatewayWebTargets.java} (96%) diff --git a/bundles/org.openhab.binding.hdpowerview/README.md b/bundles/org.openhab.binding.hdpowerview/README.md index 41e1d5ffe1ff5..512257afc9092 100644 --- a/bundles/org.openhab.binding.hdpowerview/README.md +++ b/bundles/org.openhab.binding.hdpowerview/README.md @@ -1,7 +1,13 @@ # Hunter Douglas (Luxaflex) PowerView Binding This is an openHAB binding for [Hunter Douglas PowerView](https://www.hunterdouglas.com/operating-systems/motorized/powerview-motorization/overview) motorized shades via their PowerView hub. -In some countries the PowerView system is sold under the brand name [Luxaflex](https://www.luxaflex.com/) +In some countries the PowerView system is sold under the brand name [Luxaflex](https://www.luxaflex.com/). + +This binding supports hubs/gateways of all generations. +Hubs of Generation 1 or 2 are handled commonly and their generation specific features are identified with the *'Generation 1/2 only'* annotation and/or via the [1/2] mark. +Gateways of Generation 3 have generation specific features which are identified with the *'Generation 3 only'* annotation and/or via the [3] mark. +Features that are common to all generations are not annoted or marked. + ![PowerView](doc/hdpowerview.png) @@ -13,48 +19,56 @@ By using a scene to control multiple shades at once, the shades will all begin m ## Supported Things -| Thing | Thing Type | Description | -|----------|------------|-------------| -| hub | Bridge | The PowerView hub provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. | -| shade | Thing | A motorized shade. | -| repeater | Thing | A PowerView signal repeater. | +| Thing | Thing Type | Description | +|--------------------------|------------|-------------| +| hub[1/2] | Bridge | The PowerView hub provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. | +| shade[1/2] | Thing | A motorized shade. | +| repeater[1/2] | Thing | A PowerView signal repeater. | +| gateway[3] | Bridge | Powerview Generation 3 gateway that provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. | +| shade3[3] | Thing | A Powerview Generation 3 motorized shade. | + +[1/2] Generation 1/2 hubs only. [3] Generation 3 gateways only. ## Discovery Make sure your shades are visible in the PowerView app before attempting discovery. -The binding can automatically discover the PowerView hub. +The binding can automatically discover the PowerView hub and/or the PowerView Generation 3 gateway. The discovery process can be started by pressing the refresh button in the Main Configuration UI Inbox. However you can also manually create a (bridge) thing for the hub, and enter the required configuration parameters (see Thing Configuration below). -If the configuration parameters are all valid, the binding will then automatically attempt to connect to the hub. +If the configuration parameters are all valid, the binding will then automatically attempt to connect to the hub/gateway. If the connection succeeds, the hub will indicate its status as Online, otherwise it will show an error status. Once the hub thing has been created and successfully connected, the binding will automatically discover all shades and scenes that are in it. - For each shade discovered: the binding will create a new dedicated thing with its own channels. -- For each repeater discovered: the binding will create a new dedicated thing with its own channels. -- For each scene discovered: the binding will create a new channel dynamically within the hub thing. -- For each scene group discovered: the binding will create a new channel dynamically within the hub thing. -- For each automation discovered: the binding will create a new channel dynamically within the hub thing. +- For each scene discovered: the binding will create a new channel dynamically within the hub/gateway thing. +- [1/2] For each repeater discovered: the binding will create a new dedicated thing with its own channels. +- [1/2] For each scene group discovered: the binding will create a new channel dynamically within the hub thing. +- [1/2] For each automation discovered: the binding will create a new channel dynamically within the hub thing. -If in the future, you add additional shades, repeaters, scenes, scene groups or automations to your system, the binding will discover them too. +[1/2] Generation 1/2 hubs only. + +If in the future, you add additional shades, scenes, repeaters, scene groups or automations to your system, the binding will discover them too. ## Thing Configuration -### Thing Configuration for PowerView Hub +### Thing Configuration for PowerView Hub / Gateway (Thing type `hub`[1/2] or `gateway`[3]) + +| Configuration Parameter | Description | +|-----------------------------------------|---------------| +| host | The host name or IP address of the hub on your network. | +| refresh[1/2] | The number of milli-seconds between fetches of the PowerView hub's shade state (default 60'000 one minute). | +| hardRefresh | The number of minutes between hard refreshes of the PowerView hub's shade state (default 180 three hours). See [Hard Refreshes](#Hard-Refreshes). | +| hardRefreshBatteryLevel[1/2] | The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, defaulting to weekly). See [Hard Refreshes](#Hard-Refreshes). | -| Configuration Parameter | Description | -|-------------------------|---------------| -| host | The host name or IP address of the hub on your network. | -| refresh | The number of milli-seconds between fetches of the PowerView hub's shade state (default 60'000 one minute). | -| hardRefresh | The number of minutes between hard refreshes of the PowerView hub's shade state (default 180 three hours). See [Refreshing the PowerView Hub Cache](#Refreshing-the-PowerView-Hub-Cache). | -| hardRefreshBatteryLevel | The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, defaulting to weekly). See [Refreshing the PowerView Hub Cache](#Refreshing-the-PowerView-Hub-Cache). | +[1/2] Generation 1/2 hubs only. -### Thing Configuration for PowerView Shades and Accessories +### Thing Configuration for PowerView Shades and Accessories[3] -PowerView shades and repeaters should preferably be configured via the automatic discovery process. -However, for manual configuration of shades and repeaters, the console command `openhab:hdpowerview showIds` can be used to identify the IDs of all connected equipment. -This can be used for the `id` parameters described below. +PowerView shades and repeaters[3] should preferably be configured via the automatic discovery process. +It is quite difficult to configure manually as the `id` of the shade or repeater is not exposed in the +PowerView app. However, the configuration parameters are described below. #### Thing Configuration for PowerView Shades @@ -62,7 +76,7 @@ This can be used for the `id` parameters described below. |-------------------------|-------------| | id | The ID of the PowerView shade in the app. Must be an integer. | -#### Thing Configuration for PowerView Repeaters +#### Thing Configuration for PowerView Repeaters[1/2] | Configuration Parameter | Description | |-------------------------|-------------| @@ -70,46 +84,48 @@ This can be used for the `id` parameters described below. ## Channels -### Channels for Hub (Thing type `hub`) +### Channels for Hub[1/2] (Thing type `hub`) or Gateway[3] (Thing type `gateway`) Scene, scene group and automation channels will be added dynamically to the binding as they are discovered in the hub. -Each will have an entry in the hub as shown below, whereby different scenes, scene groups and automations -have different `id` values: +Each will have an entry in the hub as shown below, whereby different scenes, scene groups and automations have different `id` values: -| Channel Group | Channel | Item Type | Description | -|---------------|---------|-----------|-------------| -| scenes | id | Switch | Setting this to ON will activate the scene. Scenes are stateless in the PowerView hub; they have no on/off state. | -| sceneGroups | id | Switch | Setting this to ON will activate the scene group. Scene groups are stateless in the PowerView hub; they have no on/off state. | -| automations | id | Switch | Setting this to ON will enable the automation, while OFF will disable it. | +| Channel Group | Channel | Item Type | Description | +|-----------------------------|---------|-----------|-------------| +| scenes | id | Switch | Setting this to ON will activate the scene. Scenes are stateless in the PowerView hub; they have no on/off state. | +| sceneGroups[1/2] | id | Switch | Setting this to ON will activate the scene group. Scene groups are stateless in the PowerView hub; they have no on/off state. | +| automations[1/2] | id | Switch | Setting this to ON will enable the automation, while OFF will disable it. | -### Channels for Shades (Thing type `shade`) +[1/2] Generation 1/2 hubs only. + +### Channels for Shades (Thing type `shade`[1/2] or `shade3`[3]) A shade always implements a roller shutter channel `position` which controls the vertical position of the shade's (primary) rail. If the shade has slats or rotatable vanes, there is also a dimmer channel `vane` which controls the slat / vane position. If it is a dual action (top-down plus bottom-up) shade, there is also a roller shutter channel `secondary` which controls the vertical position of the secondary rail. All of these channels appear in the binding, but only those which have a physical implementation in the shade, will have any physical effect. -| Channel | Item Type | Description | -|----------------|--------------------------|-------------| -| position | Rollershutter | The vertical position of the shade's rail (if any). -- See [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). Up/Down commands will move the rail completely up or completely down. Percentage commands will move the rail to an intermediate position. Stop commands will halt any current movement of the rail. | -| secondary | Rollershutter | The vertical position of the secondary rail (if any). Its function is similar to the `position` channel above. -- But see [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). | -| vane | Dimmer | The degree of opening of the slats or vanes (if any). On some shade types, setting this to a non-zero value might first move the shade `position` fully down, since the slats or vanes can only have a defined state if the shade is in its down position. See [Interdependency between Channel positions](#Interdependency-between-Channel-positions). | -| command | String | Send a command to the shade. Valid values are: `CALIBRATE`, `IDENTIFY` | -| lowBattery | Switch | Indicates ON when the battery level of the shade is low, as determined by the hub's internal rules. | -| batteryLevel | Number | Battery level (10% = low, 50% = medium, 100% = high) | -| batteryVoltage | Number:ElectricPotential | Battery (resp. mains power supply) voltage reported by the shade. | -| signalStrength | Number | Signal strength (0 for no or unknown signal, 1 for weak, 2 for average, 3 for good or 4 for excellent) | -| hubRssi | Number:Power | Received Signal Strength Indicator for Hub | -| repeaterRssi | Number:Power | Received Signal Strength Indicator for Repeater | +| Channel | Item Type | Description | +|--------------------------------|--------------------------|-------------| +| position | Rollershutter | The vertical position of the shade's rail (if any). -- See [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). Up/Down commands will move the rail completely up or completely down. Percentage commands will move the rail to an intermediate position. Stop commands will halt any current movement of the rail. | +| secondary | Rollershutter | The vertical position of the secondary rail (if any). Its function is similar to the `position` channel above. -- But see [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). | +| vane | Dimmer | The degree of opening of the slats or vanes (if any). On some shade types, setting this to a non-zero value might first move the shade `position` fully down, since the slats or vanes can only have a defined state if the shade is in its down position. See [Interdependency between Channel positions](#Interdependency-between-Channel-positions). | +| command | String | Send a command to the shade. Valid values are: `CALIBRATE`, `IDENTIFY` | +| lowBattery | Switch | Indicates ON when the battery level of the shade is low, as determined by the hub's internal rules. | +| batteryLevel | Number | Battery level (10% = low, 50% = medium, 100% = high) | +| batteryVoltage[1/2] | Number:ElectricPotential | Battery (resp. mains power supply) voltage reported by the shade. | +| signalStrength | Number | Signal strength (0 for no or unknown signal, 1 for weak, 2 for average, 3 for good or 4 for excellent) | +| hubRssi[1/2] | Number:Power | Received Signal Strength Indicator for Hub | +| repeaterRssi[1/2] | Number:Power | Received Signal Strength Indicator for Repeater | Notes: - - The channels `position`, `secondary` and `vane` exist if the shade physically supports such channels. - The shade's Power Option is set via the PowerView app with possible values 'Battery Wand', 'Rechargeable Battery Wand' or 'Hardwired Power Supply'. The channels `lowBattery` and `batteryLevel` exist if you have _not_ selected 'Hardwired Power Supply' in the app. -- The RSSI values will only be updated upon manual request by a `REFRESH` command (e.g. in a rule). +- [1/2] The RSSI values will only be updated upon manual request by a `REFRESH` command (e.g. in a rule). -### Channels for Repeaters (Thing type `repeater`) +[1/2] Generation 1/2 hubs only. + +### Channels for Repeaters (Thing type `repeater`)[1/2] | Channel | Item Type | Description | |-----------------|-----------|-------------------------------| @@ -177,7 +193,15 @@ And the value of `position` is constrained by the prior value of `secondary`. On shades with a secondary blackout panel 'DuoLite', the secondary blackout panel cannot be moved unless the main shade panel is already down. In this case, the position of the secondary blackout panel is reported as 0%. -## Refreshing the PowerView Hub Cache +## Hard Refreshes + +### Hard Refresh on Generation 3 Gateways[3] + +In Generation 3 systems, whenever the state of a shade or scene changes, it immediately notifies the gateway, and the gateway immediately notifies the openHAB binding. +However in case that any notifications may have been lost, (e.g. due to a network error), the binding occasionally requests a full update (i.e. a 'hard refresh') of all shade and scene states from the gateway. +The time interval between hard refreshes is set in the `hardRefresh` configuration parameter. + +### Hard Refresh on Generation 1/2 Hubs[1/2] The hub maintains a cache of the last known state of its shades, and this binding delivers those values. Usually the shades will be moved by this binding, so since the hub is always involved in the moving process, it updates this cache accordingly. @@ -226,10 +250,16 @@ For single shades the refresh takes the item's channel into consideration: ### `demo.things` File ``` +// generation 1/2 Bridge hdpowerview:hub:home "Luxaflex Hub" @ "Living Room" [host="192.168.1.123"] { Thing shade s50150 "Living Room Shade" @ "Living Room" [id="50150"] Thing repeater r16384 "Bedroom Repeater" @ "Bedroom" [id="16384"] } + +// generation 3 +Bridge hdpowerview:gateway:home "Luxaflex Hub" @ "Living Room" [host="192.168.1.123"] { + Thing shade3 s50150 "Living Room Shade" @ "Living Room" [id="50150"] +} ``` ### `demo.items` File @@ -237,6 +267,7 @@ Bridge hdpowerview:hub:home "Luxaflex Hub" @ "Living Room" [host="192.168.1.123" Shade items: ``` +// generation 1/2 Rollershutter Living_Room_Shade_Position "Living Room Shade Position [%.0f %%]" {channel="hdpowerview:shade:home:s50150:position"} Rollershutter Living_Room_Shade_Secondary "Living Room Shade Secondary Position [%.0f %%]" {channel="hdpowerview:shade:home:s50150:secondary"} Dimmer Living_Room_Shade_Vane "Living Room Shade Vane [%.0f %%]" {channel="hdpowerview:shade:home:s50150:vane"} @@ -245,9 +276,17 @@ Number Living_Room_Shade_Battery_Level "Battery Level" {channel="hdpowerview:sha Number:ElectricPotential Living_Room_Shade_Battery_Voltage "Battery Voltage" {channel="hdpowerview:shade:home:s50150:batteryVoltage"} String Living_Room_Shade_Command "Living Room Shade Command" {channel="hdpowerview:shade:home:s50150:command"} Number Living_Room_Shade_SignalStrength "Living Room Shade Signal Strength" {channel="hdpowerview:shade:home:s50150:signalStrength"} + +// generation 3 +Rollershutter Living_Room_Shade_Position "Living Room Shade Position [%.0f %%]" {channel="hdpowerview:shade3:home:s50150:position"} +Rollershutter Living_Room_Shade_Secondary "Living Room Shade Secondary Position [%.0f %%]" {channel="hdpowerview:shade3:home:s50150:secondary"} +Dimmer Living_Room_Shade_Vane "Living Room Shade Vane [%.0f %%]" {channel="hdpowerview:shade3:home:s50150:vane"} +Switch Living_Room_Shade_Battery_Low_Alarm "Living Room Shade Battery Low Alarm [%s]" {channel="hdpowerview:shade3:home:s50150:lowBattery"} +Number Living_Room_Shade_Battery_Level "Battery Level" {channel="hdpowerview:shade3:home:s50150:batteryLevel"} +String Living_Room_Shade_Command "Living Room Shade Command" {channel="hdpowerview:shade3:home:s50150:command"} ``` -Repeater items: +Repeater items[1/2]: ``` Color Bedroom_Repeater_Color "Bedroom Repeater Color" {channel="hdpowerview:repeater:home:r16384:color"} @@ -259,16 +298,20 @@ Switch Bedroom_Repeater_BlinkingEnabled "Bedroom Repeater Blinking Enabled [%s]" Scene items: ``` +// generation 1/2 Switch Living_Room_Shades_Scene_Heart "Living Room Shades Scene Heart" (g_Shades_Scene_Trigger) {channel="hdpowerview:hub:home:scenes#22663"} + +// generation 3 +Switch Living_Room_Shades_Scene_Heart "Living Room Shades Scene Heart" (g_Shades_Scene_Trigger) {channel="hdpowerview:gateway:home:scenes#22663"} ``` -Scene Group items: +Scene Group items[1/2]: ``` Switch Children_Rooms_Shades_Up "Good Morning Children" {channel="hdpowerview:hub:home:sceneGroups#27119"} ``` -Automation items: +Automation items[1/2]: ``` Switch Automation_Children_Up_Sun "Children Up At Sunrise" {channel="hdpowerview:hub:home:automations#1262"} @@ -283,8 +326,9 @@ Frame label="Living Room" { Slider item=Living_Room_Shade_Position Switch item=Living_Room_Shade_Command mappings=[CALIBRATE="Calibrate"] Text item=Living_Room_Shade_SignalStrength - Text item=Living_Room_Shade_Battery_Voltage } + +// generation 1/2 only Frame label="Bedroom" { Colorpicker item=PowerViewRepeater_Color Switch item=PowerViewRepeater_Color diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java index 6b6ccc3fefb45..5a19a9f1438c6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java @@ -82,7 +82,7 @@ public class HDPowerViewBindingConstants { .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); // generation 3 - public static final ThingTypeUID THING_TYPE_GATEWAY3 = new ThingTypeUID(BINDING_ID, "gateway3"); + public static final ThingTypeUID THING_TYPE_GATEWAY = new ThingTypeUID(BINDING_ID, "gateway"); public static final ThingTypeUID THING_TYPE_SHADE3 = new ThingTypeUID(BINDING_ID, "shade3"); public static final String PROPERTY_NAME = "name"; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 6787e1e861f97..0539e64dfe539 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -20,9 +20,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewDeviceDiscoveryService; -import org.openhab.binding.hdpowerview.internal.gen3.discovery.HDPowerViewDeviceDiscoveryService3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewShadeHandler3; +import org.openhab.binding.hdpowerview.internal.gen3.discovery.ShadeDiscoveryService; +import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; +import org.openhab.binding.hdpowerview.internal.gen3.handler.ShadeThingHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewRepeaterHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler; @@ -79,13 +79,13 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); // generation 3 - if (HDPowerViewBindingConstants.THING_TYPE_GATEWAY3.equals(thingTypeUID)) { - HDPowerViewHubHandler3 handler = new HDPowerViewHubHandler3((Bridge) thing, httpClient, translationProvider, + if (HDPowerViewBindingConstants.THING_TYPE_GATEWAY.equals(thingTypeUID)) { + GatewayBridgeHandler handler = new GatewayBridgeHandler((Bridge) thing, httpClient, translationProvider, clientBuilder, eventSourceFactory); - registerService(new HDPowerViewDeviceDiscoveryService3(handler)); + registerService(new ShadeDiscoveryService(handler)); return handler; } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE3.equals(thingTypeUID)) { - return new HDPowerViewShadeHandler3(thing); + return new ShadeThingHandler(thing); } else // generation 1/2 if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java similarity index 65% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java index 4985bfb733119..f2a062825fa63 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java @@ -14,14 +14,18 @@ import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; +import java.util.Collections; +import java.util.Set; + import javax.jmdns.ServiceInfo; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; -import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewHubDiscoveryParticipant; import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; +import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; @@ -34,20 +38,20 @@ */ @NonNullByDefault @Component -public class HDPowerViewHubDiscoveryParticipant3 extends HDPowerViewHubDiscoveryParticipant { +public class GatewayDiscoveryParticipant implements MDNSDiscoveryParticipant { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant3.class); + private final Logger logger = LoggerFactory.getLogger(GatewayDiscoveryParticipant.class); @Override public @Nullable DiscoveryResult createResult(ServiceInfo service) { for (String host : service.getHostAddresses()) { if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_GATEWAY3, host.replace('.', '_')); + ThingUID thingUID = new ThingUID(THING_TYPE_GATEWAY, host.replace('.', '_')); DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel("PowerView Hub (" + host + ")").build(); - logger.debug("mDNS discovered generation 3 hub on host '{}'", host); + .withLabel("PowerView Gateway (" + host + ")").build(); + logger.debug("mDNS discovered Generation 3 Gateway on host '{}'", host); return hub; } } @@ -58,4 +62,19 @@ public class HDPowerViewHubDiscoveryParticipant3 extends HDPowerViewHubDiscovery public String getServiceType() { return "_powerview-g3._tcp.local."; } + + @Override + public Set getSupportedThingTypeUIDs() { + return Collections.singleton(THING_TYPE_GATEWAY); + } + + @Override + public @Nullable ThingUID getThingUID(ServiceInfo service) { + for (String host : service.getHostAddresses()) { + if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { + return new ThingUID(THING_TYPE_GATEWAY, host.replace('.', '_')); + } + } + return null; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java similarity index 86% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java index fd456543ea11a..7a2d1bf13ff42 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java @@ -22,8 +22,8 @@ import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.thing.ThingUID; @@ -36,14 +36,14 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewDeviceDiscoveryService3 extends AbstractDiscoveryService { +public class ShadeDiscoveryService extends AbstractDiscoveryService { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewDeviceDiscoveryService3.class); - private final HDPowerViewHubHandler3 hub; + private final Logger logger = LoggerFactory.getLogger(ShadeDiscoveryService.class); + private final GatewayBridgeHandler hub; private final Runnable scanner; private @Nullable ScheduledFuture backgroundFuture; - public HDPowerViewDeviceDiscoveryService3(HDPowerViewHubHandler3 hub) { + public ShadeDiscoveryService(GatewayBridgeHandler hub) { super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE3), 600, true); this.hub = hub; this.scanner = createScanner(); @@ -75,7 +75,7 @@ protected void stopBackgroundDiscovery() { private Runnable createScanner() { return () -> { - HDPowerViewWebTargets3 webTargets = hub.getWebTargets(); + GatewayWebTargets webTargets = hub.getWebTargets(); try { discoverShades(webTargets); } catch (HubProcessingException e) { @@ -85,7 +85,7 @@ private Runnable createScanner() { }; } - private void discoverShades(HDPowerViewWebTargets3 webTargets) throws HubProcessingException { + private void discoverShades(GatewayWebTargets webTargets) throws HubProcessingException { ThingUID bridgeUid = hub.getThing().getUID(); for (Shade3 shade : webTargets.getShades()) { if (shade.getId() == 0) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java index a7e3d8afb7d93..1753a59672739 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java @@ -20,12 +20,12 @@ import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; /** - * class for scheduled event as returned by an HD PowerView Generation 3 Gateway. + * DTO for automation object as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ScheduledEvent3 { +public class Automation3 { public int id; public int type; public boolean enabled; @@ -53,10 +53,10 @@ public boolean equals(@Nullable Object o) { if (o == this) { return true; } - if (!(o instanceof ScheduledEvent3)) { + if (!(o instanceof Automation3)) { return false; } - ScheduledEvent3 other = (ScheduledEvent3) o; + Automation3 other = (Automation3) o; String days = this.days; return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java index 5a167fcd926d2..3e4a56eb84719 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java @@ -19,7 +19,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Scene object as returned by an HD PowerView Generation 3 Gateway. + * DTO for a scene as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution. */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java index c1db4851332af..47c306af7a8d6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Scene SSE event object as supplied an HD PowerView Generation 3 Gateway. + * DTO for scene SSE event object as supplied an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java index 8e70d6a0ca8ad..073870a71974c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java @@ -26,7 +26,7 @@ import org.openhab.core.types.UnDefType; /** - * State of a Shade as returned by an HD PowerView Generation 3 Gateway. + * DTO for a shade as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java index 8db10da33e7c1..e3fda1a402572 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Shade SSE event object as supplied an HD PowerView Generation 3 Gateway. + * DTO for a shade SSE event object as supplied an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java index 06866f5341823..98f0003e868b0 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java @@ -19,7 +19,7 @@ import org.openhab.core.types.UnDefType; /** - * The position of a shade as returned by an HD PowerView Generation 3 Gateway. + * DTO for the position of a shade as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java similarity index 88% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java index 0fcaeefcd1028..72b47443b943b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java @@ -29,7 +29,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.Bridge; @@ -54,9 +54,9 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewHubHandler3 extends BaseBridgeHandler { +public class GatewayBridgeHandler extends BaseBridgeHandler { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler3.class); + private final Logger logger = LoggerFactory.getLogger(GatewayBridgeHandler.class); private final String channelTypeId = HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE; private final String channelGroupId = HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES; @@ -65,14 +65,14 @@ public class HDPowerViewHubHandler3 extends BaseBridgeHandler { private final ClientBuilder clientBuilder; private final SseEventSourceFactory eventSourceFactory; - private @Nullable HDPowerViewWebTargets3 webTargets; + private @Nullable GatewayWebTargets webTargets; private @Nullable ScheduledFuture refreshTask; private boolean scenesLoaded; private boolean propertiesLoaded; private boolean isDisposing; - public HDPowerViewHubHandler3(Bridge bridge, HttpClient httpClient, + public GatewayBridgeHandler(Bridge bridge, HttpClient httpClient, HDPowerViewTranslationProvider translationProvider, ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory) { super(bridge); @@ -91,7 +91,7 @@ public void dispose() { } this.refreshTask = null; - HDPowerViewWebTargets3 webTargets = this.webTargets; + GatewayWebTargets webTargets = this.webTargets; if (webTargets != null) { try { webTargets.close(); @@ -119,18 +119,18 @@ private void doRefresh() { } /** - * Getter for the list of all HDPowerViewShadeHandler3 child thing handlers. + * Getter for the list of all child shade thing handlers. * - * @return the list of handlers. + * @return the list of shade handlers. * @throws IllegalStateException if the bridge is not properly initialized. */ - private List getThingHandlers() throws IllegalStateException { + private List getShadeThingHandlers() throws IllegalStateException { Bridge bridge = getBridge(); if (bridge != null) { - List result = new ArrayList<>(); + List result = new ArrayList<>(); bridge.getThings().stream().map(thing -> thing.getHandler()).forEach(handler -> { - if (handler instanceof HDPowerViewShadeHandler3) { - result.add((HDPowerViewShadeHandler3) handler); + if (handler instanceof ShadeThingHandler) { + result.add((ShadeThingHandler) handler); } }); return result; @@ -144,8 +144,8 @@ private List getThingHandlers() throws IllegalStateExc * @return the webTargets. * @throws IllegalStateException if webTargets is not initialized. */ - public HDPowerViewWebTargets3 getWebTargets() throws IllegalStateException { - HDPowerViewWebTargets3 webTargets = this.webTargets; + public GatewayWebTargets getWebTargets() throws IllegalStateException { + GatewayWebTargets webTargets = this.webTargets; if (webTargets != null) { return webTargets; } @@ -186,7 +186,7 @@ public void initialize() { return; } - webTargets = new HDPowerViewWebTargets3(this, httpClient, clientBuilder, eventSourceFactory, host); + webTargets = new GatewayWebTargets(this, httpClient, clientBuilder, eventSourceFactory, host); scenesLoaded = false; propertiesLoaded = false; isDisposing = false; @@ -223,7 +223,7 @@ public void onSceneEvent(Scene3 scene) { */ public void onShadeEvent(Shade3 shade) { try { - for (HDPowerViewShadeHandler3 handler : getThingHandlers()) { + for (ShadeThingHandler handler : getShadeThingHandlers()) { if (isDisposing || handler.notify(shade)) { break; } @@ -272,9 +272,9 @@ private void refreshScenes() throws HubProcessingException, IllegalStateExceptio * @throws IllegalStateException if this handler is in an illegal state. */ private void refreshShades() throws HubProcessingException, IllegalStateException { - List handlers = getThingHandlers(); + List handlers = getShadeThingHandlers(); for (Shade3 shade : getWebTargets().getShades()) { - for (HDPowerViewShadeHandler3 handler : handlers) { + for (ShadeThingHandler handler : handlers) { if (isDisposing || handler.notify(shade)) { break; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java index 1f1db93f7d536..97e368ad66e65 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java @@ -27,7 +27,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StopMoveType; import org.openhab.core.library.types.StringType; @@ -51,9 +51,9 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewShadeHandler3 extends BaseThingHandler { +public class ShadeThingHandler extends BaseThingHandler { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewShadeHandler3.class); + private final Logger logger = LoggerFactory.getLogger(ShadeThingHandler.class); private static final String INVALID_CHANNEL = "invalid channel"; private static final String INVALID_COMMAND = "invalid command"; @@ -63,7 +63,7 @@ public class HDPowerViewShadeHandler3 extends BaseThingHandler { private final Shade3 thisShade = new Shade3(); private boolean isInitialized; - public HDPowerViewShadeHandler3(Thing thing) { + public ShadeThingHandler(Thing thing) { super(thing); } @@ -78,16 +78,16 @@ public void dispose() { * @return the hub handler. * @throws IllegalStateException if the bridge or its handler are not initialized. */ - private HDPowerViewHubHandler3 getHandler() throws IllegalStateException { + private GatewayBridgeHandler getHandler() throws IllegalStateException { Bridge bridge = this.getBridge(); if (bridge == null) { throw new IllegalStateException("Bridge not initialised."); } BridgeHandler handler = bridge.getHandler(); - if (!(handler instanceof HDPowerViewHubHandler3)) { + if (!(handler instanceof GatewayBridgeHandler)) { throw new IllegalStateException("Bridge handler not initialised."); } - return (HDPowerViewHubHandler3) handler; + return (GatewayBridgeHandler) handler; } @Override @@ -97,7 +97,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { return; } - HDPowerViewWebTargets3 webTargets = getHandler().getWebTargets(); + GatewayWebTargets webTargets = getHandler().getWebTargets(); ShadePosition3 position = new ShadePosition3(); int shadeId = thisShade.getId(); try { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java index 0e1d21d485dc4..8ff69c80fdd38 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java @@ -40,14 +40,14 @@ import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Automation3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Info3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; import org.openhab.core.thing.Thing; import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; @@ -63,17 +63,17 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewWebTargets3 implements Closeable { +public class GatewayWebTargets implements Closeable { private static final String IDS = "ids"; // @formatter:off public static final Type LIST_SHADES = new TypeToken>() {}.getType(); public static final Type LIST_SCENES = new TypeToken>() {}.getType(); - public static final Type LIST_EVENTS =new TypeToken>() {}.getType(); + public static final Type LIST_EVENTS =new TypeToken>() {}.getType(); // @formatter:on - private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets3.class); + private final Logger logger = LoggerFactory.getLogger(GatewayWebTargets.class); private final String shades; private final String scenes; private final String sceneActivate; @@ -91,7 +91,7 @@ public class HDPowerViewWebTargets3 implements Closeable { private final ClientBuilder clientBuilder; private final SseEventSourceFactory eventSourceFactory; - private final HDPowerViewHubHandler3 hubHandler; + private final GatewayBridgeHandler hubHandler; private boolean isRegistered; @@ -114,7 +114,7 @@ private static class GatewayRegistration { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets3(HDPowerViewHubHandler3 hubHandler, HttpClient httpClient, ClientBuilder clientBuilder, + public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory, String ipAddress) { String base = "http://" + ipAddress + "/"; String home = base + "home/"; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index b7251366e41fb..8ab9e1e1e2889 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -5,8 +5,8 @@ binding.hdpowerview.description = The Hunter Douglas PowerView binding provides # thing types -thing-type.hdpowerview.gateway3.label = PowerView Gen3 Gateway -thing-type.hdpowerview.gateway3.description = Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro +thing-type.hdpowerview.gateway.label = PowerView Gen3 Gateway +thing-type.hdpowerview.gateway.description = Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro thing-type.hdpowerview.hub.label = PowerView Hub thing-type.hdpowerview.hub.description = Hunter Douglas (Luxaflex) PowerView Hub thing-type.hdpowerview.repeater.label = PowerView Repeater @@ -28,10 +28,10 @@ thing-type.hdpowerview.shade3.channel.secondary.description = The secondary vert # thing types config -thing-type.config.hdpowerview.gateway3.hardRefresh.label = Hard Refresh Interval -thing-type.config.hdpowerview.gateway3.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Gateway -thing-type.config.hdpowerview.gateway3.host.label = Host -thing-type.config.hdpowerview.gateway3.host.description = The Host address of the PowerView Hub +thing-type.config.hdpowerview.gateway.hardRefresh.label = Hard Refresh Interval +thing-type.config.hdpowerview.gateway.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Gateway +thing-type.config.hdpowerview.gateway.host.label = Host +thing-type.config.hdpowerview.gateway.host.description = The Host address of the PowerView Hub thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index 50a610faa5694..08a7f28890a5f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -47,7 +47,7 @@ - + Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro @@ -57,7 +57,7 @@ Hunter Douglas (Luxaflex) - PowerView Gateway + PowerView Gen 3 Gateway host diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml index 53d64243d74a8..2c46638868a42 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml @@ -45,7 +45,7 @@ - + Hunter Douglas (Luxaflex) PowerView Shade diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java index 49c04df2363e9..863a7e0bce08a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java @@ -23,13 +23,13 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Automation3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.UnDefType; @@ -64,10 +64,10 @@ private String loadJson(String filename) throws IOException { @Test public void testAutomationParsing() throws IOException { String json = loadJson("gen3/automations.json"); - List scheduledEventList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_EVENTS); + List scheduledEventList = gson.fromJson(json, GatewayWebTargets.LIST_EVENTS); assertNotNull(scheduledEventList); assertEquals(1, scheduledEventList.size()); - ScheduledEvent3 scheduledEvent = scheduledEventList.get(0); + Automation3 scheduledEvent = scheduledEventList.get(0); assertEquals(33, scheduledEvent.id); assertTrue(scheduledEvent.enabled); } @@ -92,7 +92,7 @@ public void testSceneEventParsing() throws IOException { @Test public void testScenesParsing() throws IOException { String json = loadJson("gen3/scenes.json"); - List sceneList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_SCENES); + List sceneList = gson.fromJson(json, GatewayWebTargets.LIST_SCENES); assertNotNull(sceneList); assertEquals(1, sceneList.size()); Scene3 scene = sceneList.get(0); @@ -143,7 +143,7 @@ public void testShadePositions() { @Test public void testShadesParsing() throws IOException { String json = loadJson("gen3/shades.json"); - List shadeList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_SHADES); + List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); assertNotNull(shadeList); assertEquals(1, shadeList.size()); Shade3 shadeData = shadeList.get(0); From baddcac14d63dff4b6b8a39f2b2a680fbe81c663 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 14:54:25 +0100 Subject: [PATCH 18/64] [hdpowerview] extend console extension for gen 3 Signed-off-by: Andrew Fiddian-Green --- bundles/org.openhab.binding.hdpowerview/README.md | 5 +++-- .../binding/hdpowerview/internal/gen3/dto/Shade3.java | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/README.md b/bundles/org.openhab.binding.hdpowerview/README.md index 512257afc9092..c442f6fe75491 100644 --- a/bundles/org.openhab.binding.hdpowerview/README.md +++ b/bundles/org.openhab.binding.hdpowerview/README.md @@ -67,8 +67,8 @@ If in the future, you add additional shades, scenes, repeaters, scene groups or ### Thing Configuration for PowerView Shades and Accessories[3] PowerView shades and repeaters[3] should preferably be configured via the automatic discovery process. -It is quite difficult to configure manually as the `id` of the shade or repeater is not exposed in the -PowerView app. However, the configuration parameters are described below. +However, for manual configuration of shades and repeaters, the console command `openhab:hdpowerview showIds` can be used to identify the IDs of all connected equipment. +This can be used for the `id` parameters described below. #### Thing Configuration for PowerView Shades @@ -118,6 +118,7 @@ All of these channels appear in the binding, but only those which have a physica | repeaterRssi[1/2] | Number:Power | Received Signal Strength Indicator for Repeater | Notes: + - The channels `position`, `secondary` and `vane` exist if the shade physically supports such channels. - The shade's Power Option is set via the PowerView app with possible values 'Battery Wand', 'Rechargeable Battery Wand' or 'Hardwired Power Supply'. The channels `lowBattery` and `batteryLevel` exist if you have _not_ selected 'Hardwired Power Supply' in the app. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java index 073870a71974c..de085e1e2ae82 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java @@ -81,7 +81,8 @@ public State getLowBattery() { } public String getName() { - return String.join(" ", new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8), ptName); + return String.join(" ", new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8), ptName) + .replaceAll("\n", "").replaceAll("\r", ""); } public State getPosition(CoordinateSystem posKindCoords) { From d9b5424f646cc1ee98fd84e5cacab83054e10bb1 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 20:09:45 +0100 Subject: [PATCH 19/64] [hdpowerview] implement class naming convention Signed-off-by: Andrew Fiddian-Green --- .../webtargets => }/GatewayWebTargets.java | 70 ++++------- .../internal/HDPowerViewHandlerFactory.java | 6 +- .../dto/Info3.java => api/gen3/Info.java} | 4 +- .../dto/Scene3.java => api/gen3/Scene.java} | 4 +- .../gen3/SceneEvent.java} | 8 +- .../dto/Shade3.java => api/gen3/Shade.java} | 26 ++-- .../gen3/ShadeEvent.java} | 8 +- .../gen3/ShadePosition.java} | 6 +- .../GatewayDiscoveryParticipant.java | 2 +- .../discovery/ShadeDiscoveryService.java | 10 +- .../internal/gen3/dto/Automation3.java | 117 ------------------ .../handler/GatewayBridgeHandler.java | 16 +-- .../{gen3 => }/handler/ShadeThingHandler.java | 24 ++-- .../hdpowerview/gen3/Generation3DtoTest.java | 49 +++----- 14 files changed, 97 insertions(+), 253 deletions(-) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/webtargets => }/GatewayWebTargets.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/Info3.java => api/gen3/Info.java} (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/Scene3.java => api/gen3/Scene.java} (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/SceneEvent3.java => api/gen3/SceneEvent.java} (81%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/Shade3.java => api/gen3/Shade.java} (86%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/ShadeEvent3.java => api/gen3/ShadeEvent.java} (77%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/ShadePosition3.java => api/gen3/ShadePosition.java} (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3 => }/discovery/GatewayDiscoveryParticipant.java (97%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3 => }/discovery/ShadeDiscoveryService.java (91%) delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3 => }/handler/GatewayBridgeHandler.java (95%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3 => }/handler/ShadeThingHandler.java (94%) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 8ff69c80fdd38..12300adc1746e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.webtargets; +package org.openhab.binding.hdpowerview.internal; import java.io.Closeable; import java.io.IOException; @@ -37,17 +37,15 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets.Query; +import org.openhab.binding.hdpowerview.internal.api.gen3.Info; +import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.api.gen3.SceneEvent; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Automation3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Info3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; +import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.core.thing.Thing; import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; @@ -68,9 +66,8 @@ public class GatewayWebTargets implements Closeable { private static final String IDS = "ids"; // @formatter:off - public static final Type LIST_SHADES = new TypeToken>() {}.getType(); - public static final Type LIST_SCENES = new TypeToken>() {}.getType(); - public static final Type LIST_EVENTS =new TypeToken>() {}.getType(); + public static final Type LIST_SHADES = new TypeToken>() {}.getType(); + public static final Type LIST_SCENES = new TypeToken>() {}.getType(); // @formatter:on private final Logger logger = LoggerFactory.getLogger(GatewayWebTargets.class); @@ -81,7 +78,6 @@ public class GatewayWebTargets implements Closeable { private final String shadeStop; private final String shadePositions; private final String info; - private final String automations; private final String register; private final String shadeEvents; @@ -125,7 +121,6 @@ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, shadeMotion = home + "shades/%d/motion"; shadeStop = home + "shades/stop"; shadePositions = home + "shades/positions"; - automations = home + "automations"; shadeEvents = home + "shades/events"; sceneEvents = home + "scenes/events"; @@ -196,7 +191,7 @@ private void gatewayRegister() throws HubProcessingException { public Map getInformation() throws HubProcessingException { String json = invoke(HttpMethod.GET, info, null, null); try { - Info3 result = gson.fromJson(json, Info3.class); + Info result = gson.fromJson(json, Info.class); if (result == null) { throw new HubProcessingException("getInformation(): missing response"); } @@ -214,10 +209,10 @@ public Map getInformation() throws HubProcessingException { * @return the list of scenes. * @throws HubProcessingException if any error occurs. */ - public List getScenes() throws HubProcessingException { + public List getScenes() throws HubProcessingException { String json = invoke(HttpMethod.GET, scenes, null, null); try { - List result = gson.fromJson(json, LIST_SCENES); + List result = gson.fromJson(json, LIST_SCENES); if (result == null) { throw new HubProcessingException("getScenes() missing response"); } @@ -227,25 +222,6 @@ public List getScenes() throws HubProcessingException { } } - /** - * Get the list of scheduled events. - * - * @return the list of scheduled events. - * @throws HubProcessingException if any error occurs. - */ - public List getScheduledEvents() throws HubProcessingException { - String json = invoke(HttpMethod.GET, automations, null, null); - try { - List result = gson.fromJson(json, LIST_EVENTS); - if (result == null) { - throw new HubProcessingException("getScheduledEvents() missing response"); - } - return result; - } catch (JsonParseException e) { - throw new HubProcessingException("getScheduledEvents() JsonParseException"); - } - } - /** * Get the data for a single shade. * @@ -253,10 +229,10 @@ public List getScheduledEvents() throws HubProcessingException * @return the shade. * @throws HubProcessingException if any error occurs. */ - public Shade3 getShade(int shadeId) throws HubProcessingException { + public Shade getShade(int shadeId) throws HubProcessingException { String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); try { - Shade3 result = gson.fromJson(json, Shade3.class); + Shade result = gson.fromJson(json, Shade.class); if (result == null) { throw new HubProcessingException("getShade() missing response"); } @@ -272,10 +248,10 @@ public Shade3 getShade(int shadeId) throws HubProcessingException { * @return the list of shades. * @throws HubProcessingException if any error occurs. */ - public List getShades() throws HubProcessingException { + public List getShades() throws HubProcessingException { String json = invoke(HttpMethod.GET, shades, null, null); try { - List result = gson.fromJson(json, LIST_SHADES); + List result = gson.fromJson(json, LIST_SHADES); if (result == null) { throw new HubProcessingException("getShades() missing response"); } @@ -357,7 +333,7 @@ public void jogShade(int shadeId) throws HubProcessingException { * @param position the new position. * @throws HubProcessingException if any error occurs. */ - public void moveShade(int shadeId, ShadePosition3 position) throws HubProcessingException { + public void moveShade(int shadeId, ShadePosition position) throws HubProcessingException { invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), gson.toJson(position)); } @@ -370,9 +346,9 @@ public void moveShade(int shadeId, ShadePosition3 position) throws HubProcessing private void onSceneEvent(InboundSseEvent sseEvent) { String json = sseEvent.readData(); logger.trace("onSceneEvent() json:{}", json); - SceneEvent3 sceneEvent = gson.fromJson(json, SceneEvent3.class); + SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); if (sceneEvent != null) { - Scene3 scene = sceneEvent.getScene(); + Scene scene = sceneEvent.getScene(); hubHandler.onSceneEvent(scene); } } @@ -385,11 +361,11 @@ private void onSceneEvent(InboundSseEvent sseEvent) { private void onShadeEvent(InboundSseEvent sseEvent) { String json = sseEvent.readData(); logger.trace("onShadeEvent() json:{}", json); - ShadeEvent3 shadeEvent = gson.fromJson(json, ShadeEvent3.class); + ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); if (shadeEvent != null) { - ShadePosition3 positions = shadeEvent.getCurrentPositions(); + ShadePosition positions = shadeEvent.getCurrentPositions(); hubHandler - .onShadeEvent(new Shade3().setId(shadeEvent.getId()).setShadePosition(positions).setPartialState()); + .onShadeEvent(new Shade().setId(shadeEvent.getId()).setShadePosition(positions).setPartialState()); } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 0539e64dfe539..c7554fe85681d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -20,12 +20,12 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewDeviceDiscoveryService; -import org.openhab.binding.hdpowerview.internal.gen3.discovery.ShadeDiscoveryService; -import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; -import org.openhab.binding.hdpowerview.internal.gen3.handler.ShadeThingHandler; +import org.openhab.binding.hdpowerview.internal.discovery.ShadeDiscoveryService; +import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewRepeaterHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler; +import org.openhab.binding.hdpowerview.internal.handler.ShadeThingHandler; import org.openhab.core.config.discovery.DiscoveryService; import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.TranslationProvider; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java index 8b520ebe0a3e8..19c221a4aa5d2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -20,7 +20,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class Info3 { +public class Info { private @NonNullByDefault({}) String fwVersion; private @NonNullByDefault({}) String serialNumber; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java index 3e4a56eb84719..36c021676a406 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -24,7 +24,7 @@ * @author Andrew Fiddian-Green - Initial contribution. */ @NonNullByDefault -public class Scene3 { +public class Scene { private int id; private @NonNullByDefault({}) String name; private @NonNullByDefault({}) String ptName; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java similarity index 81% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java index 47c306af7a8d6..b96ca16bc5615 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -20,15 +20,15 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class SceneEvent3 { +public class SceneEvent { private int id; - private @NonNullByDefault({}) Scene3 scene; + private @NonNullByDefault({}) Scene scene; public int getId() { return id; } - public Scene3 getScene() { + public Scene getScene() { return scene; } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java similarity index 86% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java index de085e1e2ae82..78e1a852aa0d7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -31,7 +31,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class Shade3 { +public class Shade { private int id; private @Nullable Integer type; private @Nullable String name; @@ -42,7 +42,7 @@ public class Shade3 { private @Nullable Integer signalStrength; private @Nullable String bleName; private @Nullable Firmware firmware; - private @Nullable ShadePosition3 positions; + private @Nullable ShadePosition positions; private transient boolean partialState; @@ -86,7 +86,7 @@ public String getName() { } public State getPosition(CoordinateSystem posKindCoords) { - ShadePosition3 positions = this.positions; + ShadePosition positions = this.positions; return positions == null ? UnDefType.UNDEF : positions.getState(posKindCoords); } @@ -94,7 +94,7 @@ public State getPosition(CoordinateSystem posKindCoords) { return powerType; } - public @Nullable ShadePosition3 getShadePositions() { + public @Nullable ShadePosition getShadePositions() { return positions; } @@ -121,37 +121,37 @@ public boolean isMainsPowered() { return false; } - public Shade3 setCapabilities(int capabilities) { + public Shade setCapabilities(int capabilities) { this.capabilities = capabilities; return this; } - public Shade3 setId(int id) { + public Shade setId(int id) { this.id = id; return this; } - public Shade3 setPartialState() { + public Shade setPartialState() { this.partialState = true; return this; } - public Shade3 setPosition(CoordinateSystem coordinates, int percent) { - ShadePosition3 positions = this.positions; + public Shade setPosition(CoordinateSystem coordinates, int percent) { + ShadePosition positions = this.positions; if (positions == null) { - positions = new ShadePosition3(); + positions = new ShadePosition(); this.positions = positions; } positions.setPosition(coordinates, percent); return this; } - public Shade3 setShadePosition(ShadePosition3 position) { + public Shade setShadePosition(ShadePosition position) { this.positions = position; return this; } - public Shade3 setType(int type) { + public Shade setType(int type) { this.type = type; return this; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java similarity index 77% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java index e3fda1a402572..20cade4c9f61a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -20,11 +20,11 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadeEvent3 { +public class ShadeEvent { private int id; - private @NonNullByDefault({}) ShadePosition3 currentPositions; + private @NonNullByDefault({}) ShadePosition currentPositions; - public ShadePosition3 getCurrentPositions() { + public ShadePosition getCurrentPositions() { return currentPositions; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java index 98f0003e868b0..b6e7ccaee8359 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; @@ -24,7 +24,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadePosition3 { +public class ShadePosition { private @NonNullByDefault({}) Double primary; private @NonNullByDefault({}) Double secondary; private @NonNullByDefault({}) Double tilt; @@ -54,7 +54,7 @@ public State getState(CoordinateSystem posKindCoords) { * @param percent the new value in percent. * @return this object. */ - public ShadePosition3 setPosition(CoordinateSystem coordinates, int percent) { + public ShadePosition setPosition(CoordinateSystem coordinates, int percent) { Double value = Double.valueOf(percent) / 100.0; switch (coordinates) { case PRIMARY_POSITION: diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java index f2a062825fa63..b14425f8ce233 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.discovery; +package org.openhab.binding.hdpowerview.internal.discovery; import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java index 7a2d1bf13ff42..76087bcfda9fc 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.discovery; +package org.openhab.binding.hdpowerview.internal.discovery; import java.util.Collections; import java.util.concurrent.ScheduledFuture; @@ -18,12 +18,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; +import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.thing.ThingUID; @@ -87,7 +87,7 @@ private Runnable createScanner() { private void discoverShades(GatewayWebTargets webTargets) throws HubProcessingException { ThingUID bridgeUid = hub.getThing().getUID(); - for (Shade3 shade : webTargets.getShades()) { + for (Shade shade : webTargets.getShades()) { if (shade.getId() == 0) { continue; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java deleted file mode 100644 index 1753a59672739..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.dto; - -import java.time.DayOfWeek; -import java.util.EnumSet; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; - -/** - * DTO for automation object as returned by an HD PowerView Generation 3 Gateway. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class Automation3 { - public int id; - public int type; - public boolean enabled; - public int hour; - public int minute; - public int sceneId; - public @NonNullByDefault({}) String days; - - private static final int MON = 0x01; - private static final int TUE = 0x02; - private static final int WED = 0x04; - private static final int THU = 0x08; - private static final int FRI = 0x10; - private static final int SAT = 0x20; - private static final int SUN = 0x40; - - private static final int CLOCK_BASED = 0; - private static final int BEFORE_SUNRISE = 2; - private static final int BEFORE_SUNSET = 6; - private static final int AFTER_SUNRISE = 10; - private static final int AFTER_SUNSET = 14; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof Automation3)) { - return false; - } - Automation3 other = (Automation3) o; - String days = this.days; - - return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId - && (days != null && days.equals(other.days)) && this.hour == other.hour && this.minute == other.minute; - } - - public EnumSet getDays() { - EnumSet daySet = EnumSet.noneOf(DayOfWeek.class); - String days = this.days; - if (days != null) { - try { - int daysInt = Integer.valueOf(days).intValue(); - if ((daysInt & MON) != 0) { - daySet.add(DayOfWeek.MONDAY); - } - if ((daysInt & TUE) != 0) { - daySet.add(DayOfWeek.TUESDAY); - } - if ((daysInt & WED) != 0) { - daySet.add(DayOfWeek.WEDNESDAY); - } - if ((daysInt & THU) != 0) { - daySet.add(DayOfWeek.THURSDAY); - } - if ((daysInt & FRI) != 0) { - daySet.add(DayOfWeek.FRIDAY); - } - if ((daysInt & SAT) != 0) { - daySet.add(DayOfWeek.SATURDAY); - } - if ((daysInt & SUN) != 0) { - daySet.add(DayOfWeek.SUNDAY); - } - } catch (NumberFormatException e) { - // fall through - } - } - return daySet; - } - - public int getEventType() { - switch (type) { - case CLOCK_BASED: - return ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME; - - case BEFORE_SUNRISE: - case AFTER_SUNRISE: - // TODO handle before and after sunrise cases separately - return ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE; - - case BEFORE_SUNSET: - case AFTER_SUNSET: - // TODO handle before and after sunset cases separately - return ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET; - } - return 0; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java similarity index 95% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index 72b47443b943b..02753e2ff925d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.handler; +package org.openhab.binding.hdpowerview.internal.handler; import java.io.IOException; import java.util.ArrayList; @@ -23,13 +23,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; +import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.Bridge; @@ -212,7 +212,7 @@ public void initialize() { * * @param scene the one that changed. */ - public void onSceneEvent(Scene3 scene) { + public void onSceneEvent(Scene scene) { // TODO perhaps we should trigger an OH core event here ?? } @@ -221,7 +221,7 @@ public void onSceneEvent(Scene3 scene) { * * @param shade the one that changed. */ - public void onShadeEvent(Shade3 shade) { + public void onShadeEvent(Shade shade) { try { for (ShadeThingHandler handler : getShadeThingHandlers()) { if (isDisposing || handler.notify(shade)) { @@ -254,7 +254,7 @@ private void refreshScenes() throws HubProcessingException, IllegalStateExceptio ChannelTypeUID typeUID = new ChannelTypeUID(channelTypeId); ChannelGroupUID groupUID = new ChannelGroupUID(thing.getUID(), channelGroupId); List channels = new ArrayList<>(); - for (Scene3 scene : getWebTargets().getScenes()) { + for (Scene scene : getWebTargets().getScenes()) { ChannelUID channelUID = new ChannelUID(groupUID, Integer.toString(scene.getId())); String name = scene.getName(); String description = translationProvider.getText("dynamic-channel.scene-activate.description", name); @@ -273,7 +273,7 @@ private void refreshScenes() throws HubProcessingException, IllegalStateExceptio */ private void refreshShades() throws HubProcessingException, IllegalStateException { List handlers = getShadeThingHandlers(); - for (Shade3 shade : getWebTargets().getShades()) { + for (Shade shade : getWebTargets().getShades()) { for (ShadeThingHandler handler : handlers) { if (isDisposing || handler.notify(shade)) { break; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index 97e368ad66e65..21c75263923df 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.handler; +package org.openhab.binding.hdpowerview.internal.handler; import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; @@ -22,12 +22,12 @@ import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StopMoveType; import org.openhab.core.library.types.StringType; @@ -60,7 +60,7 @@ public class ShadeThingHandler extends BaseThingHandler { private static final String COMMAND_CALIBRATE = "CALIBRATE"; private static final String COMMAND_IDENTIFY = "IDENTIFY"; - private final Shade3 thisShade = new Shade3(); + private final Shade thisShade = new Shade(); private boolean isInitialized; public ShadeThingHandler(Thing thing) { @@ -98,7 +98,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } GatewayWebTargets webTargets = getHandler().getWebTargets(); - ShadePosition3 position = new ShadePosition3(); + ShadePosition position = new ShadePosition(); int shadeId = thisShade.getId(); try { switch (channelUID.getId()) { @@ -184,11 +184,11 @@ public void initialize() { * @param shade the new shade state. * @return true if we handled the call. */ - public boolean notify(Shade3 shade) { + public boolean notify(Shade shade) { if (thisShade.getId() == shade.getId()) { updateStatus(ThingStatus.ONLINE); if (!isInitialized) { - ShadePosition3 position = shade.getShadePositions(); + ShadePosition position = shade.getShadePositions(); if (position != null) { thisShade.setShadePosition(position); } @@ -207,7 +207,7 @@ public boolean notify(Shade3 shade) { * * @param shade containing the channel data. */ - private void updateChannels(Shade3 shade) { + private void updateChannels(Shade shade) { updateState(CHANNEL_SHADE_POSITION, shade.getPosition(PRIMARY_POSITION)); updateState(CHANNEL_SHADE_VANE, shade.getPosition(VANE_TILT_POSITION)); updateState(CHANNEL_SHADE_SECONDARY_POSITION, shade.getPosition(SECONDARY_POSITION)); @@ -241,10 +241,10 @@ private void updateDynamicChannel(List removeList, String channelId, bo * * @param shade containing the channel data. */ - private void updateDynamicChannels(Shade3 shade) { + private void updateDynamicChannels(Shade shade) { List removeChannels = new ArrayList<>(); - ShadePosition3 positions = shade.getShadePositions(); + ShadePosition positions = shade.getShadePositions(); if (positions != null) { updateDynamicChannel(removeChannels, CHANNEL_SHADE_POSITION, positions.supportsPrimary()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_SECONDARY_POSITION, positions.supportsSecondary()); @@ -270,7 +270,7 @@ private void updateDynamicChannels(Shade3 shade) { * * @param shade containing the property data. */ - private void updateProperties(Shade3 shade) { + private void updateProperties(Shade shade) { if (shade.hasFullState()) { thing.setProperties(Stream.of(new String[][] { // { HDPowerViewBindingConstants.PROPERTY_NAME, shade.getName() }, diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java index 863a7e0bce08a..bd36cfeacf808 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java @@ -22,14 +22,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Automation3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; +import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.api.gen3.SceneEvent; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.UnDefType; @@ -58,29 +57,15 @@ private String loadJson(String filename) throws IOException { } } - /** - * Test JSON automation response. - */ - @Test - public void testAutomationParsing() throws IOException { - String json = loadJson("gen3/automations.json"); - List scheduledEventList = gson.fromJson(json, GatewayWebTargets.LIST_EVENTS); - assertNotNull(scheduledEventList); - assertEquals(1, scheduledEventList.size()); - Automation3 scheduledEvent = scheduledEventList.get(0); - assertEquals(33, scheduledEvent.id); - assertTrue(scheduledEvent.enabled); - } - /** * Test JSON scene event response. */ @Test public void testSceneEventParsing() throws IOException { String json = loadJson("gen3/scene-event.json"); - SceneEvent3 sceneEvent = gson.fromJson(json, SceneEvent3.class); + SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); assertNotNull(sceneEvent); - Scene3 scene = sceneEvent.getScene(); + Scene scene = sceneEvent.getScene(); assertNotNull(scene); assertEquals("Open All Office Shades\n Open All Office Shades", scene.getName()); assertEquals(234, scene.getId()); @@ -92,10 +77,10 @@ public void testSceneEventParsing() throws IOException { @Test public void testScenesParsing() throws IOException { String json = loadJson("gen3/scenes.json"); - List sceneList = gson.fromJson(json, GatewayWebTargets.LIST_SCENES); + List sceneList = gson.fromJson(json, GatewayWebTargets.LIST_SCENES); assertNotNull(sceneList); assertEquals(1, sceneList.size()); - Scene3 scene = sceneList.get(0); + Scene scene = sceneList.get(0); assertEquals("Open All Office Shades\n ABC", scene.getName()); assertEquals(234, scene.getId()); } @@ -106,9 +91,9 @@ public void testScenesParsing() throws IOException { @Test public void testShadeEventParsing() throws IOException { String json = loadJson("gen3/shade-event.json"); - ShadeEvent3 shadeEvent = gson.fromJson(json, ShadeEvent3.class); + ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); assertNotNull(shadeEvent); - ShadePosition3 position = shadeEvent.getCurrentPositions(); + ShadePosition position = shadeEvent.getCurrentPositions(); assertNotNull(position); assertEquals(PercentType.valueOf("99"), position.getState(CoordinateSystem.PRIMARY_POSITION)); assertEquals(PercentType.valueOf("98"), position.getState(CoordinateSystem.SECONDARY_POSITION)); @@ -120,15 +105,15 @@ public void testShadeEventParsing() throws IOException { */ @Test public void testShadePositions() { - ShadePosition3 pos; + ShadePosition pos; - pos = new ShadePosition3(); + pos = new ShadePosition(); pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION)); assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.SECONDARY_POSITION)); assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.VANE_TILT_POSITION)); - pos = new ShadePosition3(); + pos = new ShadePosition(); pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); pos.setPosition(CoordinateSystem.SECONDARY_POSITION, 22); pos.setPosition(CoordinateSystem.VANE_TILT_POSITION, 33); @@ -143,10 +128,10 @@ public void testShadePositions() { @Test public void testShadesParsing() throws IOException { String json = loadJson("gen3/shades.json"); - List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); + List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); assertNotNull(shadeList); assertEquals(1, shadeList.size()); - Shade3 shadeData = shadeList.get(0); + Shade shadeData = shadeList.get(0); assertEquals("Shade 2 ABC", shadeData.getName()); assertEquals(789, shadeData.getId()); } From f4b9b58f3ca5f75be328baaa119b31be1ed22ce4 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 20:29:18 +0100 Subject: [PATCH 20/64] [hdpowerview] console changes Signed-off-by: Andrew Fiddian-Green --- .../console/HDPowerViewCommandExtension.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java index 4252f8d57bb2b..78dc10aa1cf07 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java @@ -17,11 +17,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; +import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.core.io.console.Console; import org.openhab.core.io.console.ConsoleCommandCompleter; @@ -65,7 +68,7 @@ public void execute(String[] args, Console console) { for (Thing thing : thingRegistry.getAll()) { ThingHandler thingHandler = thing.getHandler(); if (thingHandler instanceof HDPowerViewHubHandler) { - console.println("API bridge: " + thing.getLabel()); + console.println("Generation 1/2 API bridge: " + thing.getLabel()); HDPowerViewWebTargets webTargets = ((HDPowerViewHubHandler) thingHandler).getWebTargets(); try { @@ -87,6 +90,21 @@ public void execute(String[] args, Console console) { } catch (HubException e) { console.println("Error retrieving ID's: " + e.getMessage()); } + } else if (thingHandler instanceof GatewayBridgeHandler) { + console.println("Generation 3 API bridge: " + thing.getLabel()); + GatewayWebTargets webTargets = ((GatewayBridgeHandler) thingHandler).getWebTargets(); + + try { + List shades = webTargets.getShades(); + if (!shades.isEmpty()) { + console.println(" - Shades:"); + for (Shade shade : shades) { + console.println(" - ID: " + shade.getId() + " (" + shade.getName() + ")"); + } + } + } catch (HubException e) { + console.println("Error retrieving ID's: " + e.getMessage()); + } } } } From 7581dc0fbbe4043661ecaf49d007147184657cf9 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 23:39:42 +0100 Subject: [PATCH 21/64] [hdpowerview] console texts Signed-off-by: Andrew Fiddian-Green --- .../internal/console/HDPowerViewCommandExtension.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java index 78dc10aa1cf07..91240155bfa60 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java @@ -68,7 +68,7 @@ public void execute(String[] args, Console console) { for (Thing thing : thingRegistry.getAll()) { ThingHandler thingHandler = thing.getHandler(); if (thingHandler instanceof HDPowerViewHubHandler) { - console.println("Generation 1/2 API bridge: " + thing.getLabel()); + console.println("Generation 1/2 API hub: " + thing.getLabel()); HDPowerViewWebTargets webTargets = ((HDPowerViewHubHandler) thingHandler).getWebTargets(); try { @@ -91,7 +91,7 @@ public void execute(String[] args, Console console) { console.println("Error retrieving ID's: " + e.getMessage()); } } else if (thingHandler instanceof GatewayBridgeHandler) { - console.println("Generation 3 API bridge: " + thing.getLabel()); + console.println("Generation 3 API gateway: " + thing.getLabel()); GatewayWebTargets webTargets = ((GatewayBridgeHandler) thingHandler).getWebTargets(); try { @@ -111,7 +111,7 @@ public void execute(String[] args, Console console) { @Override public List getUsages() { - return Arrays.asList(buildCommandUsage(SHOW_IDS, "list all shades and repeaters")); + return Arrays.asList(buildCommandUsage(SHOW_IDS, "list all shades and eventually repeaters")); } @Override From e64e5ddb980786d4a4e781be6187084c9786eeac Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Tue, 1 Nov 2022 14:25:11 +0000 Subject: [PATCH 22/64] [hdpowerview] move modules Signed-off-by: Andrew Fiddian-Green --- .../internal/GatewayWebTargets.java | 14 ++--- .../internal/HDPowerViewWebTargets.java | 52 +++++++++---------- .../builders/AutomationChannelBuilder.java | 8 +-- .../builders/SceneChannelBuilder.java | 2 +- .../builders/SceneGroupChannelBuilder.java | 2 +- .../console/HDPowerViewCommandExtension.java | 6 +-- .../HDPowerViewDeviceDiscoveryService.java | 6 +-- .../discovery/ShadeDiscoveryService.java | 2 +- .../internal/{api => dto}/BatteryKind.java | 2 +- .../internal/{api => dto}/Color.java | 2 +- .../{api => dto}/CoordinateSystem.java | 2 +- .../internal/{api => dto}/Firmware.java | 2 +- .../internal/{api => dto}/HubFirmware.java | 2 +- .../internal/{api => dto}/ShadePosition.java | 4 +- .../internal/{api => dto}/SurveyData.java | 2 +- .../internal/{api => dto}/Times.java | 2 +- .../internal/{api => dto}/UserData.java | 2 +- .../internal/{api => dto}/gen3/Info.java | 2 +- .../internal/{api => dto}/gen3/Scene.java | 2 +- .../{api => dto}/gen3/SceneEvent.java | 2 +- .../internal/{api => dto}/gen3/Shade.java | 6 +-- .../{api => dto}/gen3/ShadeEvent.java | 2 +- .../{api => dto}/gen3/ShadePosition.java | 4 +- .../requests/RepeaterBlinking.java | 2 +- .../{api => dto}/requests/RepeaterColor.java | 4 +- .../{api => dto}/requests/ShadeCalibrate.java | 2 +- .../{api => dto}/requests/ShadeJog.java | 2 +- .../{api => dto}/requests/ShadeMotion.java | 2 +- .../{api => dto}/requests/ShadeMove.java | 4 +- .../{api => dto}/requests/ShadePositions.java | 4 +- .../{api => dto}/requests/ShadeStop.java | 2 +- .../responses/FirmwareVersion.java | 4 +- .../{api => dto}/responses/Repeater.java | 2 +- .../{api => dto}/responses/RepeaterData.java | 6 +-- .../{api => dto}/responses/Repeaters.java | 2 +- .../responses/SceneCollections.java | 2 +- .../{api => dto}/responses/Scenes.java | 2 +- .../responses/ScheduledEvents.java | 2 +- .../{api => dto}/responses/Shade.java | 4 +- .../{api => dto}/responses/Shades.java | 8 +-- .../{api => dto}/responses/Survey.java | 4 +- .../responses/UserDataResponse.java | 4 +- .../handler/GatewayBridgeHandler.java | 4 +- .../handler/HDPowerViewHubHandler.java | 22 ++++---- .../handler/HDPowerViewRepeaterHandler.java | 6 +-- .../handler/HDPowerViewShadeHandler.java | 14 ++--- .../internal/handler/ShadeThingHandler.java | 6 +-- .../AutomationChannelBuilderTest.java | 16 +++--- .../{ => internal}/HDPowerViewJUnitTests.java | 20 +++---- .../OnlineCommunicationTest.java | 15 +++--- .../SceneChannelBuilderTest.java | 10 ++-- .../SceneGroupChannelBuilderTest.java | 10 ++-- .../{ => internal}/ShadePositionTest.java | 6 +-- .../gen3/Generation3DtoTest.java | 16 +++--- .../providers/MockedLocaleProvider.java | 2 +- .../providers/MockedTranslationProvider.java | 2 +- .../hdpowerview/{ => internal}/duette.json | 0 .../{ => internal}/gen3/automations.json | 0 .../{ => internal}/gen3/scene-event.json | 0 .../{ => internal}/gen3/scenes.json | 0 .../{ => internal}/gen3/shade-event.json | 0 .../{ => internal}/gen3/shades.json | 0 .../{ => internal}/sceneCollections.json | 0 .../hdpowerview/{ => internal}/scenes.json | 0 .../hdpowerview/{ => internal}/shades.json | 0 65 files changed, 167 insertions(+), 174 deletions(-) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/BatteryKind.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/Color.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/CoordinateSystem.java (98%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/Firmware.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/HubFirmware.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/ShadePosition.java (99%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/SurveyData.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/Times.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/UserData.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/Info.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/Scene.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/SceneEvent.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/Shade.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/ShadeEvent.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/ShadePosition.java (95%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/RepeaterBlinking.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/RepeaterColor.java (88%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeCalibrate.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeJog.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeMotion.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeMove.java (85%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadePositions.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeStop.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/FirmwareVersion.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Repeater.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/RepeaterData.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Repeaters.java (92%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/SceneCollections.java (97%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Scenes.java (97%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/ScheduledEvents.java (98%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Shade.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Shades.java (89%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Survey.java (87%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/UserDataResponse.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/AutomationChannelBuilderTest.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/HDPowerViewJUnitTests.java (92%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/OnlineCommunicationTest.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/SceneChannelBuilderTest.java (90%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/SceneGroupChannelBuilderTest.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/ShadePositionTest.java (99%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/gen3/Generation3DtoTest.java (90%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/providers/MockedLocaleProvider.java (92%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/providers/MockedTranslationProvider.java (97%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/duette.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/automations.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/scene-event.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/shade-event.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/shades.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/sceneCollections.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/shades.json (100%) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 12300adc1746e..783bad3dda974 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -37,13 +37,13 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets.Query; -import org.openhab.binding.hdpowerview.internal.api.gen3.Info; -import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; -import org.openhab.binding.hdpowerview.internal.api.gen3.SceneEvent; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadeEvent; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Info; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.dto.gen3.SceneEvent; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeMotion; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.core.thing.Thing; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 562d37bc504c7..2ab79180bde13 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -27,32 +27,32 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; -import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.Shade; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.responses.Survey; -import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; +import org.openhab.binding.hdpowerview.internal.dto.Color; +import org.openhab.binding.hdpowerview.internal.dto.HubFirmware; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.SurveyData; +import org.openhab.binding.hdpowerview.internal.dto.UserData; +import org.openhab.binding.hdpowerview.internal.dto.requests.RepeaterBlinking; +import org.openhab.binding.hdpowerview.internal.dto.requests.RepeaterColor; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeCalibrate; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeMove; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeStop; +import org.openhab.binding.hdpowerview.internal.dto.responses.FirmwareVersion; +import org.openhab.binding.hdpowerview.internal.dto.responses.Repeater; +import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Repeaters; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shade; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Survey; +import org.openhab.binding.hdpowerview.internal.dto.responses.UserDataResponse; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java index 10782b22b991c..f48e722c7bebe 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java @@ -25,10 +25,10 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents.ScheduledEvent; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java index 01c05137e9ece..7c866458f3bf7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneGroupChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneGroupChannelBuilder.java index 4cc4436ea2f32..54d9c8a0cfc1d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneGroupChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneGroupChannelBuilder.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java index 91240155bfa60..d5f633d80338e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java @@ -20,9 +20,9 @@ import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java index 98d108befcfb1..9e74c946e2a62 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java @@ -21,13 +21,13 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java index 76087bcfda9fc..68ced751e154c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java @@ -20,8 +20,8 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.core.config.discovery.AbstractDiscoveryService; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/BatteryKind.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/BatteryKind.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/BatteryKind.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/BatteryKind.java index 229d0abce377c..a3b79bf47f998 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/BatteryKind.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/BatteryKind.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Color.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Color.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Color.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Color.java index 39f1d0f47b24f..7179690638457 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Color.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Color.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.library.types.HSBType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/CoordinateSystem.java similarity index 98% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/CoordinateSystem.java index 236e468947820..47c4a1bb7d2b6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/CoordinateSystem.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Firmware.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Firmware.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Firmware.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Firmware.java index fd627c8b9b146..c17d76e99eff2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Firmware.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Firmware.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/HubFirmware.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/HubFirmware.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/HubFirmware.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/HubFirmware.java index 0a625b2f160ff..5deb23bd566b9 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/HubFirmware.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/HubFirmware.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/ShadePosition.java similarity index 99% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/ShadePosition.java index 9b66e798bbcbc..96d8bea648729 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/ShadePosition.java @@ -10,9 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/SurveyData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/SurveyData.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/SurveyData.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/SurveyData.java index bfb5301bac539..7f3da5ff9c372 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/SurveyData.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/SurveyData.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Times.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Times.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Times.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Times.java index 0dbc911bfd94f..33a9bc99f3b91 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Times.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Times.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/UserData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/UserData.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/UserData.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/UserData.java index 70c33a9189e31..fd36992246cb7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/UserData.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/UserData.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import java.util.Base64; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java index 19c221a4aa5d2..0d98838a67f81 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java index 36c021676a406..144878f2eaa88 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import java.nio.charset.StandardCharsets; import java.util.Base64; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java index b96ca16bc5615..58d557886c06a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java index 78e1a852aa0d7..aa64287c90722 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import java.nio.charset.StandardCharsets; import java.util.Base64; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java index 20cade4c9f61a..7ac276735fdeb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java similarity index 95% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java index b6e7ccaee8359..85316f893bf73 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterBlinking.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterBlinking.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterBlinking.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterBlinking.java index ea3886af1f1c4..e50a308448b90 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterBlinking.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterBlinking.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterColor.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterColor.java similarity index 88% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterColor.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterColor.java index 960e95f5878c9..fc317e7aa1dab 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterColor.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterColor.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.Color; +import org.openhab.binding.hdpowerview.internal.dto.Color; /** * Color state of a single Repeater for being updated by an HD PowerView Hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeCalibrate.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeCalibrate.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeCalibrate.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeCalibrate.java index e1ec80e48743c..0d1d50ec9dc19 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeCalibrate.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeCalibrate.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeJog.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeJog.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeJog.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeJog.java index 71fb0477a09ec..b1317a7eabb32 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeJog.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeJog.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMotion.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMotion.java index 13d3179c4841a..c78fc1dca421f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMotion.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMove.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMove.java similarity index 85% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMove.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMove.java index fe688f4f75b58..4f87fdf0c3358 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMove.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMove.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; /** * A request to set the position of a shade diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadePositions.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadePositions.java index 1c544c047e878..fd98950f5d816 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadePositions.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; /** * The position of a shade to set diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeStop.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeStop.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeStop.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeStop.java index ed7ab4c4b0a8a..34f47f038ca7a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeStop.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeStop.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/FirmwareVersion.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/FirmwareVersion.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/FirmwareVersion.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/FirmwareVersion.java index d046f06a8d760..34b9f202eb942 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/FirmwareVersion.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/FirmwareVersion.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.dto.HubFirmware; /** * Firmware information for an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeater.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeater.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeater.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeater.java index 55ba66665b14f..d743626d9605c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeater.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeater.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/RepeaterData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/RepeaterData.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/RepeaterData.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/RepeaterData.java index a083f91a35a76..1b789637ddded 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/RepeaterData.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/RepeaterData.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.util.Base64; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.Color; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; /** * Repeater data for a single Repeater, as returned by an HD PowerView Hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeaters.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeaters.java similarity index 92% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeaters.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeaters.java index 8b5f15b12fd69..e1aa699b49484 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeaters.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeaters.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.util.List; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/SceneCollections.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/SceneCollections.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/SceneCollections.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/SceneCollections.java index bef9e94b93329..bcf87c9671497 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/SceneCollections.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/SceneCollections.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.nio.charset.StandardCharsets; import java.util.Base64; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Scenes.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Scenes.java index 5cb25a8aab0b4..72de7c35d6cb4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Scenes.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.nio.charset.StandardCharsets; import java.util.Base64; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/ScheduledEvents.java similarity index 98% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/ScheduledEvents.java index e7ae6ac1ae440..41a64b240ace0 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/ScheduledEvents.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.time.DayOfWeek; import java.util.EnumSet; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shade.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shade.java index 69da9378395b7..f585b98ebad06 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shade.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; /** * State of a single Shade, as returned by an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shades.java similarity index 89% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shades.java index 2746523b47a97..868f87ff38892 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shades.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.BatteryKind; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; /** * State of all Shades, as returned by an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Survey.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Survey.java similarity index 87% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Survey.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Survey.java index 21709fe1a0b9a..e55a1f99f84b4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Survey.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Survey.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; +import org.openhab.binding.hdpowerview.internal.dto.SurveyData; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/UserDataResponse.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/UserDataResponse.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/UserDataResponse.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/UserDataResponse.java index 3509ffdb85c6f..15c4cbeae7b06 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/UserDataResponse.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/UserDataResponse.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.dto.UserData; /** * Response with {@link UserData} for an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index 02753e2ff925d..6110f16dc7207 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -26,9 +26,9 @@ import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.OnOffType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index b3991f3aad3bd..f5c9b89bdf0af 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -30,22 +30,22 @@ import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.HubFirmware; +import org.openhab.binding.hdpowerview.internal.dto.UserData; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewRepeaterHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewRepeaterHandler.java index 037c85184d167..c8080fa2c354b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewRepeaterHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewRepeaterHandler.java @@ -20,10 +20,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.Color; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java index 53e77e0425cdb..66d7056200683 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java @@ -13,7 +13,7 @@ package org.openhab.binding.hdpowerview.internal.handler; import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import java.util.ArrayList; import java.util.HashMap; @@ -29,15 +29,15 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.BatteryKind; +import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.SurveyData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index 21c75263923df..16074faa05992 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -13,7 +13,7 @@ package org.openhab.binding.hdpowerview.internal.handler; import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import java.util.ArrayList; import java.util.List; @@ -24,9 +24,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StopMoveType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/AutomationChannelBuilderTest.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/AutomationChannelBuilderTest.java index ba6cb99574fc4..fcadf7a03593f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/AutomationChannelBuilderTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -22,15 +22,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; -import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; -import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.providers.MockedLocaleProvider; +import org.openhab.binding.hdpowerview.internal.providers.MockedTranslationProvider; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; import org.openhab.core.thing.ThingUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/HDPowerViewJUnitTests.java similarity index 92% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/HDPowerViewJUnitTests.java index 19cd35c7c33ee..e7b05686c3b70 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/HDPowerViewJUnitTests.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import java.io.IOException; import java.io.InputStream; @@ -23,16 +23,16 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.BatteryKind; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/OnlineCommunicationTest.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/OnlineCommunicationTest.java index b8670bb91ec92..7aca650e833e2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/OnlineCommunicationTest.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import java.util.List; import java.util.regex.Pattern; @@ -21,14 +21,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneChannelBuilderTest.java similarity index 90% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneChannelBuilderTest.java index eca2a1f2e9ebb..859f30cb0bf6b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneChannelBuilderTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -22,12 +22,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; -import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; -import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.providers.MockedLocaleProvider; +import org.openhab.binding.hdpowerview.internal.providers.MockedTranslationProvider; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; import org.openhab.core.thing.ThingUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneGroupChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneGroupChannelBuilderTest.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneGroupChannelBuilderTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneGroupChannelBuilderTest.java index e2dc8b1b7a0da..d1a4f8c91e90d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneGroupChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneGroupChannelBuilderTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -22,12 +22,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder; -import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; -import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.providers.MockedLocaleProvider; +import org.openhab.binding.hdpowerview.internal.providers.MockedTranslationProvider; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; import org.openhab.core.thing.ThingUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/ShadePositionTest.java similarity index 99% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/ShadePositionTest.java index 50dcd0e7ff4c2..5ced717f203b0 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/ShadePositionTest.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java similarity index 90% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java index bd36cfeacf808..beb0c4d9778ba 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.gen3; +package org.openhab.binding.hdpowerview.internal.gen3; import static org.junit.jupiter.api.Assertions.*; @@ -21,14 +21,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; -import org.openhab.binding.hdpowerview.internal.api.gen3.SceneEvent; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadeEvent; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; +import org.openhab.binding.hdpowerview.internal.HDPowerViewJUnitTests; +import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.dto.gen3.SceneEvent; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.UnDefType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedLocaleProvider.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedLocaleProvider.java similarity index 92% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedLocaleProvider.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedLocaleProvider.java index 8161cc8e28274..d95879f454ea6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedLocaleProvider.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedLocaleProvider.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.providers; +package org.openhab.binding.hdpowerview.internal.providers; import java.util.Locale; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedTranslationProvider.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedTranslationProvider.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedTranslationProvider.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedTranslationProvider.java index da7152ad7504c..9c9503ef1eee7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedTranslationProvider.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedTranslationProvider.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.providers; +package org.openhab.binding.hdpowerview.internal.providers; import static java.util.Map.entry; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/duette.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/duette.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/automations.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/automations.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/automations.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/automations.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scene-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/scene-event.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scene-event.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/scene-event.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shade-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shade-event.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shade-event.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shade-event.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/sceneCollections.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/sceneCollections.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/shades.json From 52cbd882b7ff733e913f203f9b4c1ecb00b607f7 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 4 Sep 2022 15:06:55 +0100 Subject: [PATCH 23/64] [hdpowerview] generation 3 PR2 Signed-off-by: Andrew Fiddian-Green --- .../internal/HDPowerViewWebTargets.java | 608 ++++++----------- .../internal/_v1/HDPowerViewWebTargetsV1.java | 624 ++++++++++++++++++ .../internal/_v3/HDPowerViewWebTargetsV3.java | 551 ++++++++++++++++ .../hdpowerview/internal/_v3/SseSinkV3.java | 42 ++ .../hdpowerview/internal/api/ShadeData.java | 46 ++ .../internal/api/ShadePosition.java | 336 +--------- .../internal/api/_v1/ShadeDataV1.java | 47 ++ .../internal/api/_v1/ShadePositionV1.java | 387 +++++++++++ .../internal/api/_v3/ShadeDataV3.java | 48 ++ .../internal/api/_v3/ShadePositionV3.java | 86 +++ .../internal/api/responses/Scene.java | 40 ++ .../internal/api/responses/Scenes.java | 56 -- .../api/responses/ScheduledEvent.java | 40 ++ .../api/responses/ScheduledEvents.java | 91 --- .../internal/api/responses/Shade.java | 2 +- .../internal/api/responses/Shades.java | 41 +- .../internal/api/responses/_v1/SceneV1.java | 73 ++ .../api/responses/_v1/ScheduledEventV1.java | 116 ++++ .../internal/api/responses/_v3/InfoV3.java | 65 ++ .../internal/api/responses/_v3/SceneV3.java | 85 +++ .../api/responses/_v3/ScheduledEventV3.java | 134 ++++ .../api/responses/_v3/SseSceneV3.java | 29 + .../api/responses/_v3/SseShadeV3.java | 30 + .../builders/AutomationChannelBuilder.java | 27 +- .../builders/SceneChannelBuilder.java | 8 +- .../HDPowerViewDeviceDiscoveryService.java | 2 +- .../handler/HDPowerViewHubHandler.java | 125 +++- .../handler/HDPowerViewShadeHandler.java | 41 +- .../AutomationChannelBuilderTest.java | 36 +- .../hdpowerview/Generation3DtoTest.java | 191 ++++++ .../hdpowerview/HDPowerViewJUnitTests.java | 18 +- .../hdpowerview/OnlineCommunicationTest.java | 12 +- .../hdpowerview/SceneChannelBuilderTest.java | 5 +- .../hdpowerview/ShadePositionTest.java | 53 +- .../binding/hdpowerview/{ => _v1}/duette.json | 0 .../{ => _v1}/sceneCollections.json | 0 .../binding/hdpowerview/{ => _v1}/scenes.json | 0 .../binding/hdpowerview/{ => _v1}/shades.json | 0 .../binding/hdpowerview/_v3/automations.json | 13 + .../binding/hdpowerview/_v3/motion.json | 25 + .../binding/hdpowerview/_v3/scene-event.json | 17 + .../binding/hdpowerview/_v3/scenes.json | 14 + .../binding/hdpowerview/_v3/shade-event.json | 17 + .../binding/hdpowerview/_v3/shades.json | 25 + 44 files changed, 3209 insertions(+), 997 deletions(-) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => _v1}/duette.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => _v1}/sceneCollections.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => _v1}/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => _v1}/shades.json (100%) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index f8ba04c153391..4e9cf8ed2bf3d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.hdpowerview.internal; +import java.lang.reflect.Type; import java.time.Duration; import java.time.Instant; import java.util.List; @@ -29,30 +30,18 @@ import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.api.Color; import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.SurveyData; import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; -import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.Shade; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.responses.Survey; -import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; @@ -61,20 +50,20 @@ import org.slf4j.LoggerFactory; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; /** - * JAX-RS targets for communicating with an HD PowerView hub + * Abstract class for JAX-RS targets for communicating with an HD PowerView hub. * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Added support for secondary rail positions - * @author Jacob Laursen - Added support for scene groups and automations + * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewWebTargets { +public abstract class HDPowerViewWebTargets { private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class); @@ -87,24 +76,69 @@ public class HDPowerViewWebTargets { private final Duration maintenancePeriod = Duration.ofMinutes(5); private Instant maintenanceScheduledEnd = Instant.MIN; - private final String base; - private final String firmwareVersion; - private final String shades; - private final String sceneActivate; - private final String scenes; - private final String sceneCollectionActivate; - private final String sceneCollections; - private final String scheduledEvents; - private final String repeaters; - private final String userData; + /* + * De-serializer target class types + */ + private final Class sceneClass; + private final Class shadeDataClass; + private final Class shadePositionClass; + private final Class scheduledEventClass; - private final Gson gson = new Gson(); - private final HttpClient httpClient; + protected final Gson gson; + protected final HttpClient httpClient; + + /* + * Private helper class for de-serialization of Scene + */ + private class SceneDeserializer implements JsonDeserializer { + @Override + public @Nullable Scene deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + return context.deserialize(jsonObject, sceneClass); + } + } + + /* + * Private helper class for de-serialization of ShadeData + */ + private class ShadeDataDeserializer implements JsonDeserializer { + @Override + public @Nullable ShadeData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + return context.deserialize(jsonObject, shadeDataClass); + } + } + + /* + * Private helper class for de-serialization of ShadePosition + */ + private class ShadePositionDeserializer implements JsonDeserializer { + @Override + public @Nullable ShadePosition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + return context.deserialize(jsonObject, shadePositionClass); + } + } + + /* + * Private helper class for de-serialization of ScheduledEvent + */ + private class ScheduledEventDeserializer implements JsonDeserializer { + @Override + public @Nullable ScheduledEvent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + return context.deserialize(jsonObject, scheduledEventClass); + } + } /** - * private helper class for passing http url query parameters + * Protected helper class for passing http url query parameters */ - private static class Query { + protected static class Query { private final String key; private final String value; @@ -137,73 +171,96 @@ public String toString() { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) { - base = "http://" + ipAddress + "/api/"; - shades = base + "shades/"; - firmwareVersion = base + "fwversion"; - sceneActivate = base + "scenes"; - scenes = base + "scenes/"; - - // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections". - sceneCollectionActivate = base + "scenecollections"; - sceneCollections = base + "scenecollections/"; - - scheduledEvents = base + "scheduledevents"; - repeaters = base + "repeaters/"; - userData = base + "userdata"; - + public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class sceneClass, Class shadeDataClass, + Class shadePositionClass, Class scheduledEventClass) { this.httpClient = httpClient; + this.sceneClass = sceneClass; + this.shadeDataClass = shadeDataClass; + this.shadePositionClass = shadePositionClass; + this.scheduledEventClass = scheduledEventClass; + this.gson = new GsonBuilder() + // @formatter:off + .registerTypeAdapter(Scene.class, new SceneDeserializer()) + .registerTypeAdapter(ShadeData.class, new ShadeDataDeserializer()) + .registerTypeAdapter(ShadePosition.class, new ShadePositionDeserializer()) + .registerTypeAdapter(ScheduledEvent.class, new ScheduledEventDeserializer()) + // @formatter:on + .create(); } /** - * Fetches a JSON package with firmware information for the hub. + * Get the gson de-serializer. * - * @return FirmwareVersions class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance + * @return the gson de-serializer. */ - public HubFirmware getFirmwareVersions() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, firmwareVersion, null, null); - try { - FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class); - if (firmwareVersion == null) { - throw new HubInvalidResponseException("Missing firmware response"); - } - HubFirmware firmwareVersions = firmwareVersion.firmware; - if (firmwareVersions == null) { - throw new HubInvalidResponseException("Missing 'firmware' element"); - } - return firmwareVersions; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing firmware response", e); - } + public Gson getGson() { + return gson; } /** - * Fetches a JSON package with user data information for the hub. + * Common protected method to invoke a call on the hub server to retrieve information or send a command * - * @return {@link UserData} class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance + * @param method GET or PUT + * @param url the host url to be called + * @param query the http query parameter + * @param jsonCommand the request command content (as a json string) + * @return the response content (as a json string) + * @throws HubMaintenanceException + * @throws HubProcessingException */ - public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, userData, null, null); - try { - UserDataResponse userDataResponse = gson.fromJson(json, UserDataResponse.class); - if (userDataResponse == null) { - throw new HubInvalidResponseException("Missing userData response"); + protected synchronized String invoke(HttpMethod method, String url, @Nullable Query query, + @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { + if (logger.isTraceEnabled()) { + if (query != null) { + logger.trace("API command {} {}{}", method, url, query); + } else { + logger.trace("API command {} {}", method, url); + } + if (jsonCommand != null) { + logger.trace("JSON command = {}", jsonCommand); } - UserData userData = userDataResponse.userData; - if (userData == null) { - throw new HubInvalidResponseException("Missing 'userData' element"); + } + Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); + if (query != null) { + request.param(query.getKey(), query.getValue()); + } + if (jsonCommand != null) { + request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); + } + ContentResponse response; + try { + response = request.send(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } catch (TimeoutException | ExecutionException e) { + if (Instant.now().isBefore(maintenanceScheduledEnd)) { + // throw "softer" exception during maintenance window + logger.debug("Hub still undergoing maintenance"); + throw new HubMaintenanceException("Hub still undergoing maintenance"); } - return userData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing userData response", e); + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } + int statusCode = response.getStatus(); + if (statusCode == HttpStatus.LOCKED_423) { + // set end of maintenance window, and throw a "softer" exception + maintenanceScheduledEnd = Instant.now().plus(maintenancePeriod); + logger.debug("Hub undergoing maintenance"); + throw new HubMaintenanceException("Hub undergoing maintenance"); + } + if (statusCode != HttpStatus.OK_200) { + logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); + throw new HubProcessingException(String.format("HTTP %d error", statusCode)); + } + String jsonResponse = response.getContentAsString(); + if (logger.isTraceEnabled()) { + logger.trace("JSON response = {}", jsonResponse); + } + if (jsonResponse == null || jsonResponse.isEmpty()) { + logger.warn("Hub returned no content"); + throw new HubProcessingException("Missing response entity"); } + return jsonResponse; } /** @@ -215,22 +272,42 @@ public UserData getUserData() throws HubInvalidResponseException, HubProcessingE * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, shades, null, null); - try { - Shades shades = gson.fromJson(json, Shades.class); - if (shades == null) { - throw new HubInvalidResponseException("Missing shades response"); - } - List shadeData = shades.shadeData; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); - } - return shades; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shades response", e); - } - } + public abstract Shades getShades() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + + /** + * Fetches a JSON package that describes all scenes in the hub, and wraps it in + * a Scenes class instance + * + * @return Scenes class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + public abstract Scenes getScenes() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + + /** + * Fetches a JSON package with firmware information for the hub. + * + * @return FirmwareVersions class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + public abstract HubFirmware getFirmwareVersions() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + + /** + * Fetches a JSON package with user data information for the hub. + * + * @return {@link UserData} class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + public abstract UserData getUserData() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Instructs the hub to move a specific shade @@ -243,31 +320,8 @@ public Shades getShades() throws HubInvalidResponseException, HubProcessingExcep * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeMove(position)); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException { - try { - Shade shade = gson.fromJson(json, Shade.class); - if (shade == null) { - throw new HubInvalidResponseException("Missing shade response"); - } - ShadeData shadeData = shade.shade; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shade.shade' element"); - } - if (Boolean.TRUE.equals(shadeData.timedOut)) { - throw new HubShadeTimeoutException("Timeout when sending request to the shade"); - } - return shadeData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shade response", e); - } - } + public abstract ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to stop movement of a specific shade @@ -279,12 +333,8 @@ private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseExcept * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeStop()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to jog a specific shade @@ -296,12 +346,8 @@ public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubP * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeJog()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to calibrate a specific shade @@ -313,38 +359,8 @@ public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubPr * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeCalibrate()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Fetches a JSON package that describes all scenes in the hub, and wraps it in - * a Scenes class instance - * - * @return Scenes class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, scenes, null, null); - try { - Scenes scenes = gson.fromJson(json, Scenes.class); - if (scenes == null) { - throw new HubInvalidResponseException("Missing scenes response"); - } - List sceneData = scenes.sceneData; - if (sceneData == null) { - throw new HubInvalidResponseException("Missing 'scenes.sceneData' element"); - } - return scenes; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scenes response", e); - } - } + public abstract ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to execute a specific scene @@ -353,9 +369,7 @@ public Scenes getScenes() throws HubInvalidResponseException, HubProcessingExcep * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); - } + public abstract void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes all scene collections in the hub, and wraps it in @@ -366,23 +380,8 @@ public void activateScene(int sceneId) throws HubProcessingException, HubMainten * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public SceneCollections getSceneCollections() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, sceneCollections, null, null); - try { - SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class); - if (sceneCollections == null) { - throw new HubInvalidResponseException("Missing sceneCollections response"); - } - List sceneCollectionData = sceneCollections.sceneCollectionData; - if (sceneCollectionData == null) { - throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element"); - } - return sceneCollections; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing sceneCollections response", e); - } - } + public abstract SceneCollections getSceneCollections() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Instructs the hub to execute a specific scene collection @@ -391,10 +390,8 @@ public SceneCollections getSceneCollections() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.GET, sceneCollectionActivate, - Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null); - } + public abstract void activateSceneCollection(int sceneCollectionId) + throws HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in @@ -405,50 +402,20 @@ public void activateSceneCollection(int sceneCollectionId) throws HubProcessingE * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public ScheduledEvents getScheduledEvents() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, scheduledEvents, null, null); - try { - ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class); - if (scheduledEvents == null) { - throw new HubInvalidResponseException("Missing scheduledEvents response"); - } - List scheduledEventData = scheduledEvents.scheduledEventData; - if (scheduledEventData == null) { - throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element"); - } - return scheduledEvents; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scheduledEvents response", e); - } - } + public abstract ScheduledEvents getScheduledEvents() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Enables or disables a scheduled event in the hub. - * + * * @param scheduledEventId id of the scheduled event to be enabled or disabled * @param enable true to enable scheduled event, false to disable * @throws HubInvalidResponseException if response is invalid * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public void enableScheduledEvent(int scheduledEventId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String uri = scheduledEvents + "/" + scheduledEventId; - String jsonResponse = invoke(HttpMethod.GET, uri, null, null); - try { - JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject(); - JsonElement scheduledEventElement = jsonObject.get("scheduledEvent"); - if (scheduledEventElement == null) { - throw new HubInvalidResponseException("Missing 'scheduledEvent' element"); - } - JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject(); - scheduledEventObject.addProperty("enabled", enable); - invoke(HttpMethod.PUT, uri, null, jsonObject.toString()); - } catch (JsonParseException | IllegalStateException e) { - throw new HubInvalidResponseException("Error parsing scheduledEvent response", e); - } - } + public abstract void enableScheduledEvent(int scheduledEventId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes all repeaters in the hub, and wraps it in @@ -459,23 +426,8 @@ public void enableScheduledEvent(int scheduledEventId, boolean enable) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public Repeaters getRepeaters() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, repeaters, null, null); - try { - Repeaters repeaters = gson.fromJson(json, Repeaters.class); - if (repeaters == null) { - throw new HubInvalidResponseException("Missing repeaters response"); - } - List repeaterData = repeaters.repeaterData; - if (repeaterData == null) { - throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element"); - } - return repeaters; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing repeaters response", e); - } - } + public abstract Repeaters getRepeaters() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes a specific repeater in the hub, and wraps it @@ -487,27 +439,8 @@ public Repeaters getRepeaters() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public RepeaterData getRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null); - return repeaterDataFromJson(jsonResponse); - } - - private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException { - try { - Repeater repeater = gson.fromJson(json, Repeater.class); - if (repeater == null) { - throw new HubInvalidResponseException("Missing repeater response"); - } - RepeaterData repeaterData = repeater.repeater; - if (repeaterData == null) { - throw new HubInvalidResponseException("Missing 'repeater.repeater' element"); - } - return repeaterData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing repeater response", e); - } - } + public abstract RepeaterData getRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Instructs the hub to identify a specific repeater by blinking @@ -518,16 +451,12 @@ private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponse * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public RepeaterData identifyRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId, - Query.of("identify", Boolean.toString(true)), null); - return repeaterDataFromJson(jsonResponse); - } + public abstract RepeaterData identifyRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Enables or disables blinking for a repeater - * + * * @param repeaterId id of the repeater for which to be enable or disable blinking * @param enable true to enable blinking, false to disable * @return RepeaterData class instance @@ -535,12 +464,8 @@ public RepeaterData identifyRepeater(int repeaterId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable)); - String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); - return repeaterDataFromJson(jsonResponse); - } + public abstract RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Sets color and brightness for a repeater @@ -551,78 +476,8 @@ public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public RepeaterData setRepeaterColor(int repeaterId, Color color) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color)); - String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); - return repeaterDataFromJson(jsonResponse); - } - - /** - * Invoke a call on the hub server to retrieve information or send a command - * - * @param method GET or PUT - * @param url the host url to be called - * @param query the http query parameter - * @param jsonCommand the request command content (as a json string) - * @return the response content (as a json string) - * @throws HubMaintenanceException - * @throws HubProcessingException - */ - private synchronized String invoke(HttpMethod method, String url, @Nullable Query query, - @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { - if (logger.isTraceEnabled()) { - if (query != null) { - logger.trace("API command {} {}{}", method, url, query); - } else { - logger.trace("API command {} {}", method, url); - } - if (jsonCommand != null) { - logger.trace("JSON command = {}", jsonCommand); - } - } - Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); - if (query != null) { - request.param(query.getKey(), query.getValue()); - } - if (jsonCommand != null) { - request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); - } - ContentResponse response; - try { - response = request.send(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); - } catch (TimeoutException | ExecutionException e) { - if (Instant.now().isBefore(maintenanceScheduledEnd)) { - // throw "softer" exception during maintenance window - logger.debug("Hub still undergoing maintenance"); - throw new HubMaintenanceException("Hub still undergoing maintenance"); - } - throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); - } - int statusCode = response.getStatus(); - if (statusCode == HttpStatus.LOCKED_423) { - // set end of maintenance window, and throw a "softer" exception - maintenanceScheduledEnd = Instant.now().plus(maintenancePeriod); - logger.debug("Hub undergoing maintenance"); - throw new HubMaintenanceException("Hub undergoing maintenance"); - } - if (statusCode != HttpStatus.OK_200) { - logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); - throw new HubProcessingException(String.format("HTTP %d error", statusCode)); - } - String jsonResponse = response.getContentAsString(); - if (logger.isTraceEnabled()) { - logger.trace("JSON response = {}", jsonResponse); - } - if (jsonResponse == null || jsonResponse.isEmpty()) { - logger.warn("Hub returned no content"); - throw new HubProcessingException("Missing response entity"); - } - return jsonResponse; - } + public abstract RepeaterData setRepeaterColor(int repeaterId, Color color) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Fetches a JSON package that describes a specific shade in the hub, and wraps it @@ -635,11 +490,8 @@ private synchronized String invoke(HttpMethod method, String url, @Nullable Quer * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -653,12 +505,8 @@ public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubPr * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData refreshShadePosition(int shadeId) - throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("refresh", Boolean.toString(true)), null); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData refreshShadePosition(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -672,24 +520,8 @@ public ShadeData refreshShadePosition(int shadeId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public List getShadeSurvey(int shadeId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("survey", Boolean.toString(true)), null); - try { - Survey survey = gson.fromJson(jsonResponse, Survey.class); - if (survey == null) { - throw new HubInvalidResponseException("Missing survey response"); - } - List surveyData = survey.surveyData; - if (surveyData == null) { - throw new HubInvalidResponseException("Missing 'survey.surveyData' element"); - } - return surveyData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing survey response", e); - } - } + public abstract List getShadeSurvey(int shadeId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -703,10 +535,6 @@ public List getShadeSurvey(int shadeId) * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("updateBatteryLevel", Boolean.toString(true)), null); - return shadeDataFromJson(jsonResponse); - } + public abstract ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java new file mode 100644 index 0000000000000..1eff3b0b31b32 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java @@ -0,0 +1,624 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal._v1; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethod; +import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal.api.Color; +import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api.SurveyData; +import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; +import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; +import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; +import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; +import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; +import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.Shade; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Survey; +import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; +import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; + +/** + * JAX-RS targets for communicating with an HD PowerView hub Generation 1/2 + * + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Added support for secondary rail positions + * @author Jacob Laursen - Added support for scene groups and automations + */ +@NonNullByDefault +public class HDPowerViewWebTargetsV1 extends HDPowerViewWebTargets { + + private final String firmwareVersion; + private final String shades; + private final String sceneActivate; + private final String scenes; + private final String sceneCollectionActivate; + private final String sceneCollections; + private final String scheduledEvents; + private final String repeaters; + private final String userData; + + /** + * Initialize the web targets + * + * @param httpClient the HTTP client (the binding) + * @param ipAddress the IP address of the server (the hub) + */ + public HDPowerViewWebTargetsV1(HttpClient httpClient, String ipAddress) { + super(httpClient, ipAddress, SceneV1.class, ShadeDataV1.class, ShadePositionV1.class, ScheduledEventV1.class); + + // initialize the urls + String base = "http://" + ipAddress + "/api/"; + shades = base + "shades/"; + firmwareVersion = base + "fwversion"; + sceneActivate = base + "scenes"; + scenes = base + "scenes/"; + + // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections". + sceneCollectionActivate = base + "scenecollections"; + sceneCollections = base + "scenecollections/"; + + scheduledEvents = base + "scheduledevents"; + repeaters = base + "repeaters/"; + userData = base + "userdata"; + } + + /** + * Protected method to create ShadeData instances from a JSON payload. + * + * @param json the json payload + * @return a ShadeData instance + * @throws HubInvalidResponseException in case od missing or invalid response + * @throws HubShadeTimeoutException in case of connection time out (in V1 implementation) + */ + protected ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException { + try { + Shade shade = gson.fromJson(json, Shade.class); + if (shade == null) { + throw new HubInvalidResponseException("Missing shade response"); + } + ShadeData shadeData = shade.shade; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shade.shade' element"); + } + if (shadeData.version() == 1 && Boolean.TRUE.equals(((ShadeDataV1) shadeData).timedOut)) { + throw new HubShadeTimeoutException("Timeout when sending request to the shade"); + } + return shadeData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shade response", e); + } + } + + /** + * Fetches a JSON package with firmware information for the hub. + * + * @return FirmwareVersions class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public HubFirmware getFirmwareVersions() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, firmwareVersion, null, null); + try { + FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class); + if (firmwareVersion == null) { + throw new HubInvalidResponseException("Missing firmware response"); + } + HubFirmware firmwareVersions = firmwareVersion.firmware; + if (firmwareVersions == null) { + throw new HubInvalidResponseException("Missing 'firmware' element"); + } + return firmwareVersions; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing firmware response", e); + } + } + + /** + * Fetches a JSON package with user data information for the hub. + * + * @return {@link UserData} class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, userData, null, null); + try { + UserDataResponse userDataResponse = gson.fromJson(json, UserDataResponse.class); + if (userDataResponse == null) { + throw new HubInvalidResponseException("Missing userData response"); + } + UserData userData = userDataResponse.userData; + if (userData == null) { + throw new HubInvalidResponseException("Missing 'userData' element"); + } + return userData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing userData response", e); + } + } + + /** + * Fetches a JSON package that describes all shades in the hub, and wraps it in + * a Shades class instance + * + * @return Shades class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades, null, null); + try { + Shades shades = gson.fromJson(json, Shades.class); + if (shades == null) { + throw new HubInvalidResponseException("Missing shades response"); + } + List shadeData = shades.shadeData; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); + } + return shades; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shades response", e); + } + } + + /** + * Instructs the hub to move a specific shade + * + * @param shadeId id of the shade to be moved + * @param position instance of ShadePosition containing the new position + * @return ShadeData class instance (with new position) + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeMove(position)); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to stop movement of a specific shade + * + * @param shadeId id of the shade to be stopped + * @return ShadeData class instance (new position cannot be relied upon) + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeStop()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to jog a specific shade + * + * @param shadeId id of the shade to be jogged + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeJog()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to calibrate a specific shade + * + * @param shadeId id of the shade to be calibrated + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeCalibrate()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Fetches a JSON package that describes all scenes in the hub, and wraps it in + * a Scenes class instance + * + * @return Scenes class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scenes, null, null); + try { + Scenes scenes = gson.fromJson(json, Scenes.class); + if (scenes == null) { + throw new HubInvalidResponseException("Missing scenes response"); + } + List sceneData = scenes.sceneData; + if (sceneData == null) { + throw new HubInvalidResponseException("Missing 'scenes.sceneData' element"); + } + return scenes; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scenes response", e); + } + } + + /** + * Instructs the hub to execute a specific scene + * + * @param sceneId id of the scene to be executed + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); + } + + /** + * Fetches a JSON package that describes all scene collections in the hub, and wraps it in + * a SceneCollections class instance + * + * @return SceneCollections class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public SceneCollections getSceneCollections() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, sceneCollections, null, null); + try { + SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class); + if (sceneCollections == null) { + throw new HubInvalidResponseException("Missing sceneCollections response"); + } + List sceneCollectionData = sceneCollections.sceneCollectionData; + if (sceneCollectionData == null) { + throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element"); + } + return sceneCollections; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing sceneCollections response", e); + } + } + + /** + * Instructs the hub to execute a specific scene collection + * + * @param sceneCollectionId id of the scene collection to be executed + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneCollectionActivate, + Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null); + } + + /** + * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in + * a ScheduledEvents class instance + * + * @return ScheduledEvents class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public ScheduledEvents getScheduledEvents() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scheduledEvents, null, null); + try { + ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class); + if (scheduledEvents == null) { + throw new HubInvalidResponseException("Missing scheduledEvents response"); + } + List scheduledEventData = scheduledEvents.scheduledEventData; + if (scheduledEventData == null) { + throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element"); + } + return scheduledEvents; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scheduledEvents response", e); + } + } + + /** + * Enables or disables a scheduled event in the hub. + * + * @param scheduledEventId id of the scheduled event to be enabled or disabled + * @param enable true to enable scheduled event, false to disable + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public void enableScheduledEvent(int scheduledEventId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String uri = scheduledEvents + "/" + scheduledEventId; + String jsonResponse = invoke(HttpMethod.GET, uri, null, null); + try { + JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject(); + JsonElement scheduledEventElement = jsonObject.get("scheduledEvent"); + if (scheduledEventElement == null) { + throw new HubInvalidResponseException("Missing 'scheduledEvent' element"); + } + JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject(); + scheduledEventObject.addProperty("enabled", enable); + invoke(HttpMethod.PUT, uri, null, jsonObject.toString()); + } catch (JsonParseException | IllegalStateException e) { + throw new HubInvalidResponseException("Error parsing scheduledEvent response", e); + } + } + + /** + * Fetches a JSON package that describes all repeaters in the hub, and wraps it in + * a Repeaters class instance + * + * @return Repeaters class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public Repeaters getRepeaters() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, repeaters, null, null); + try { + Repeaters repeaters = gson.fromJson(json, Repeaters.class); + if (repeaters == null) { + throw new HubInvalidResponseException("Missing repeaters response"); + } + List repeaterData = repeaters.repeaterData; + if (repeaterData == null) { + throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element"); + } + return repeaters; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing repeaters response", e); + } + } + + /** + * Fetches a JSON package that describes a specific repeater in the hub, and wraps it + * in a RepeaterData class instance + * + * @param repeaterId id of the repeater to be fetched + * @return RepeaterData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public RepeaterData getRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null); + return repeaterDataFromJson(jsonResponse); + } + + private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException { + try { + Repeater repeater = gson.fromJson(json, Repeater.class); + if (repeater == null) { + throw new HubInvalidResponseException("Missing repeater response"); + } + RepeaterData repeaterData = repeater.repeater; + if (repeaterData == null) { + throw new HubInvalidResponseException("Missing 'repeater.repeater' element"); + } + return repeaterData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing repeater response", e); + } + } + + /** + * Instructs the hub to identify a specific repeater by blinking + * + * @param repeaterId id of the repeater to be identified + * @return RepeaterData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public RepeaterData identifyRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId, + Query.of("identify", Boolean.toString(true)), null); + return repeaterDataFromJson(jsonResponse); + } + + /** + * Enables or disables blinking for a repeater + * + * @param repeaterId id of the repeater for which to be enable or disable blinking + * @param enable true to enable blinking, false to disable + * @return RepeaterData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable)); + String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); + return repeaterDataFromJson(jsonResponse); + } + + /** + * Sets color and brightness for a repeater + * + * @param repeaterId id of the repeater for which to set color and brightness + * @return RepeaterData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public RepeaterData setRepeaterColor(int repeaterId, Color color) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color)); + String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); + return repeaterDataFromJson(jsonResponse); + } + + /** + * Fetches a JSON package that describes a specific shade in the hub, and wraps it + * in a Shade class instance + * + * @param shadeId id of the shade to be fetched + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on + * a specific shade's position; fetches a JSON package that describes that shade, + * and wraps it in a Shade class instance + * + * @param shadeId id of the shade to be refreshed + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData refreshShadePosition(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("refresh", Boolean.toString(true)), null); + return shadeDataFromJson(jsonResponse); + } + + /** + * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on + * a specific shade's survey data, which will also refresh signal strength; + * fetches a JSON package that describes that survey, and wraps it in a Survey + * class instance + * + * @param shadeId id of the shade to be surveyed + * @return List of SurveyData class instances + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + @Override + public List getShadeSurvey(int shadeId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("survey", Boolean.toString(true)), null); + try { + Survey survey = gson.fromJson(jsonResponse, Survey.class); + if (survey == null) { + throw new HubInvalidResponseException("Missing survey response"); + } + List surveyData = survey.surveyData; + if (surveyData == null) { + throw new HubInvalidResponseException("Missing 'survey.surveyData' element"); + } + return surveyData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing survey response", e); + } + } + + /** + * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on + * a specific shade's battery level; fetches a JSON package that describes that shade, + * and wraps it in a Shade class instance + * + * @param shadeId id of the shade to be refreshed + * @return ShadeData class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + * @throws HubShadeTimeoutException if the shade did not respond to a request + */ + @Override + public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("updateBatteryLevel", Boolean.toString(true)), null); + return shadeDataFromJson(jsonResponse); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java new file mode 100644 index 0000000000000..18b0979f4c5a2 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java @@ -0,0 +1,551 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal._v3; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.sse.InboundSseEvent; +import javax.ws.rs.sse.SseEventSource; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http.HttpMethod; +import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal.api.Color; +import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api.SurveyData; +import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.Shade; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.InfoV3; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.SceneV3; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.ScheduledEventV3; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.SseSceneV3; +import org.openhab.binding.hdpowerview.internal.api.responses._v3.SseShadeV3; +import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +/** + * JAX-RS targets for communicating with an HD PowerView hub Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewWebTargetsV3 extends HDPowerViewWebTargets { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargetsV3.class); + + private static final String IDS = "ids"; + + /** + * Simple DTO for registering the binding with the hub. + * + * @author AndrewFG - Initial contribution + */ + @SuppressWarnings("unused") + private static class GatewayRegistration { + public String todo = "org.openhab.binding.hdpowerview"; // TODO + } + + private final String shades; + private final String scenes; + private final String sceneActivate; + private final String shadeMotion; + private final String shadeStop; + private final String shadePositions; + private final String firmware; + private final String automations; + private final String register; + private final String shadeEvents; + private final String sceneEvents; + + // @formatter:off + private final Type shadeListType = new TypeToken>() {}.getType(); + // @formatter:on + + // @formatter:off + private final Type sceneListType = new TypeToken>() {}.getType(); + // @formatter:on + + // @formatter:off + private final Type scheduledEventListType = + new TypeToken>() {}.getType(); + // @formatter:on + + private boolean isRegistered; + private @Nullable SseSinkV3 sseSink; + private final Client sseClient = ClientBuilder.newClient(); + private @Nullable SseEventSource shadeEventSource; + private @Nullable SseEventSource sceneEventSource; + + /** + * Initialize the web targets + * + * @param httpClient the HTTP client (the binding) + * @param ipAddress the IP address of the server (the hub) + */ + public HDPowerViewWebTargetsV3(HttpClient httpClient, String ipAddress) { + super(httpClient, ipAddress, SceneV3.class, ShadeDataV3.class, ShadePositionV3.class, ScheduledEventV3.class); + + // initialize the urls + String base = "http://" + ipAddress + "/"; + + String home = base + "home/"; + shades = home + "shades"; + scenes = home + "scenes"; + sceneActivate = home + "scenes/%d/activate"; + shadeMotion = home + "shades/%d/motion"; + shadeStop = home + "shades/stop"; + shadePositions = home + "shades/positions"; + automations = home + "automations"; + shadeEvents = home + "shades/events"; + sceneEvents = home + "scenes/events"; + + String gateway = base + "gateway/"; + firmware = gateway + "info"; + register = gateway + "TBD"; // TODO + } + + @Override + public HubFirmware getFirmwareVersions() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, firmware, null, null); + return toFirmware(json); + } + + @Override + public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getUserData(): method not implemented"); + } + + @Override + public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades, null, null); + return toShades(json); + } + + @Override + public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), + gson.toJson(position)); + return getShade(shadeId); + } + + @Override + public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + invoke(HttpMethod.PUT, shadeStop, Query.of(IDS, Integer.valueOf(shadeId).toString()), null); + return getShade(shadeId); + } + + @Override + public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String json = gson.toJson(new ShadeJog()); + invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); + return getShade(shadeId); + } + + @Override + public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String json = gson.toJson(new ShadeCalibrate()); + invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); + return getShade(shadeId); + } + + @Override + public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, this.scenes, null, null); + return toScenes(json); + } + + @Override + public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.PUT, String.format(sceneActivate, sceneId), null, null); + } + + @Override + public SceneCollections getSceneCollections() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getSceneCollections(): method not implemented"); + } + + @Override + public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("activateSceneCollection(): method not implemented"); + } + + @Override + public ScheduledEvents getScheduledEvents() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, automations, null, null); + return toScheduledEvents(json); + } + + @Override + public void enableScheduledEvent(int scheduledEventId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("enableScheduledEvent(): method not implemented"); + } + + @Override + public Repeaters getRepeaters() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getRepeaters(): method not implemented"); + } + + @Override + public RepeaterData getRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getRepeater(): method not implemented"); + } + + @Override + public RepeaterData identifyRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("identifyRepeater(): method not implemented"); + } + + @Override + public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("enableRepeaterBlinking(): method not implemented"); + } + + @Override + public RepeaterData setRepeaterColor(int repeaterId, Color color) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("setRepeaterColor(): method not implemented"); + } + + @Override + public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); + return toShadeData(json); + } + + @Override + public ShadeData refreshShadePosition(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + return getShade(shadeId); + } + + @Override + public List getShadeSurvey(int shadeId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + throw new HubProcessingException("getShadeSurvey(): method not implemented"); + } + + @Override + public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + return getShade(shadeId); + } + + /** + * Register the binding with the hub (if not already registered). + * + * @throws HubProcessingException + * @throws HubMaintenanceException + */ + private void gatewayRegister() throws HubMaintenanceException, HubProcessingException { + if (!isRegistered) { + String json = gson.toJson(new GatewayRegistration()); + invoke(HttpMethod.PUT, register, null, json); + isRegistered = true; + } + } + + /** + * Set the sink for SSE events. + * + * @param sseSink a class that implements the SseSinkV3 interface. + * @return true if registered for SSE events. + * @throws HubProcessingException + * @throws HubMaintenanceException + */ + public boolean sseSubscribe(@Nullable SseSinkV3 sseSink) throws HubMaintenanceException, HubProcessingException { + SseEventSource source; + + this.sseSink = sseSink; + + if (sseSink != null) { + // register ourself with the gateway (if necessary) + gatewayRegister(); + + // open SSE channel for shades + SseEventSource shadeEventSource = this.shadeEventSource; + if (shadeEventSource == null || !shadeEventSource.isOpen()) { + source = SseEventSource.target(sseClient.target(shadeEvents)).build(); + source.register((event) -> onShadeEvent(event)); + source.open(); + if (!source.isOpen()) { + throw new HubProcessingException("setEventSink(): failed to open SSE channel for shades"); + } + this.shadeEventSource = source; + } + + // open SSE channel for scenes + SseEventSource sceneEventSource = this.sceneEventSource; + if (sceneEventSource == null || !sceneEventSource.isOpen()) { + source = SseEventSource.target(sseClient.target(sceneEvents)).build(); + source.register((event) -> onSceneEvent(event)); + source.open(); + if (!source.isOpen()) { + throw new HubProcessingException("setEventSink(): failed to open SSE channel for scenes"); + } + this.sceneEventSource = source; + } + return true; + } + + // close SSE channel for shades + source = shadeEventSource; + if (source != null) { + source.close(); + shadeEventSource = null; + } + + // close SSE channel for scenes + source = sceneEventSource; + if (source != null) { + source.close(); + sceneEventSource = null; + } + return false; + } + + /** + * Handle inbound SSE events for a shade. + * + * @param sseEvent the inbound event + */ + private void onShadeEvent(InboundSseEvent sseEvent) { + SseSinkV3 eventSink = this.sseSink; + if (eventSink != null) { + String json = sseEvent.readData(); + logger.trace("onShadeEvent(): {}", json); + try { + ShadeData shadeData = toShadeData2(json); + ShadePosition shadePosition = shadeData.positions; + if (shadePosition != null) { + String evt = toEvt(json); + eventSink.sseShade(evt, shadeData.id, shadePosition); + } + } catch (HubInvalidResponseException e) { + // swallow the exception; don't pass back to caller + } + } + } + + /** + * Handle inbound SSE events for a scene. + * + * @param sseEvent the inbound event + */ + private void onSceneEvent(InboundSseEvent sseEvent) { + SseSinkV3 eventSink = this.sseSink; + if (eventSink != null) { + String json = sseEvent.readData(); + logger.trace("onSceneEvent(): {}", json); + try { + Scene scene = toScene(json); + String evt = toEvt(json); + eventSink.sseScene(evt, scene.id); + } catch (HubInvalidResponseException e) { + // swallow the exception; don't pass back to caller + } + } + } + + /** + * Create ShadeData instance from a JSON payload. + * + * @param json the json payload + * @return a ShadeData instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private ShadeData toShadeData(String json) throws HubInvalidResponseException { + try { + Shade shade = gson.fromJson(json, Shade.class); + if (shade == null) { + throw new HubInvalidResponseException("Missing shade response"); + } + ShadeData shadeData = shade.shade; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shade.shade' element"); + } + return shadeData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shade response", e); + } + } + + /** + * Create Shades instance from a JSON payload. + * + * @param json the json payload + * @return a Shades instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private Shades toShades(String json) throws HubInvalidResponseException { + try { + Shades shades = new Shades(); + shades.shadeData = gson.fromJson(json, shadeListType); + if (shades.shadeData == null) { + throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); + } + return shades; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shades response", e); + } + } + + /** + * Create HubFirmware instance from a JSON payload. + * + * @param json the json payload + * @return a HubFirmware instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private HubFirmware toFirmware(String json) throws HubProcessingException { + try { + InfoV3 gatewayInfo = gson.fromJson(json, InfoV3.class); + if (gatewayInfo == null) { + throw new HubProcessingException("getFirmwareVersions(): missing gatewayInfo"); + } + return gatewayInfo.toHubFirmware(); + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing gateway info response", e); + } + } + + /** + * Create Scenes instance from a JSON payload. + * + * @param json the json payload + * @return a Scenes instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private Scenes toScenes(String json) throws HubInvalidResponseException { + try { + Scenes scenes = new Scenes(); + scenes.sceneData = gson.fromJson(json, sceneListType); + return scenes; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scenes response", e); + } + } + + /** + * Create ScheduledEvents instance from a JSON payload. + * + * @param json the json payload + * @return a ScheduledEvents instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private ScheduledEvents toScheduledEvents(String json) throws HubInvalidResponseException { + try { + ScheduledEvents scheduledEvents = new ScheduledEvents(); + scheduledEvents.scheduledEventData = gson.fromJson(json, scheduledEventListType); + return scheduledEvents; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing automation response", e); + } + } + + /** + * Get the 'evt' message from an event JSON payload. + * + * @param json the json payload + * @return the message + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private String toEvt(String json) throws HubInvalidResponseException { + SseShadeV3 sseShade = gson.fromJson(json, SseShadeV3.class); + if (sseShade != null) { + String evt = sseShade.evt; + if (evt != null) { + return evt; + } + } + throw new HubInvalidResponseException("Error parsing event"); + } + + /** + * Create ShadeData instance from a JSON shade event payload. + * + * @param json the json payload + * @return a ShadeData instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private ShadeData toShadeData2(String json) throws HubInvalidResponseException { + SseShadeV3 sseShade = gson.fromJson(json, SseShadeV3.class); + if (sseShade != null) { + ShadePosition shadePosition = sseShade.currentPositions; + if (shadePosition != null) { + ShadeData shadeData = new ShadeDataV3(); + shadeData.id = sseShade.id; + shadeData.positions = sseShade.currentPositions; + return shadeData; + } + } + throw new HubInvalidResponseException("Error parsing shade event"); + } + + /** + * Create Scene instance from a JSON scene event payload. + * + * @param json the json payload + * @return a Scene instance + * @throws HubInvalidResponseException in case of missing or invalid response + */ + private Scene toScene(String json) throws HubInvalidResponseException { + SseSceneV3 sceneEvent = gson.fromJson(json, SseSceneV3.class); + if (sceneEvent != null) { + Scene scene = sceneEvent.scene; + if (scene != null) { + return scene; + } + } + throw new HubInvalidResponseException("Error parsing scene event"); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java new file mode 100644 index 0000000000000..bf827dfe17580 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; + +/** + * Interface for receiving SSE events from Generation 3 hubs. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public interface SseSinkV3 { + + /** + * Method that is called when a shade changes state. + * + * @param evt contents of json evt element. + * @param shadeId the id of the shade that changed. + * @param shadePosition the shade's new position. + */ + public void sseShade(String evt, int shadeId, ShadePosition shadePosition); + + /** + * Method that is called when a scene changes state. + * + * @param evt contents of json 'evt' element. + * @param sceneId the id of the scene that changed. + */ + public void sseScene(String evt, int sceneId); +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java new file mode 100644 index 0000000000000..4d240e7aec367 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api; + +import java.util.Base64; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Abstract class for state of a Shade as returned by an HD PowerView hub. + * + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Refactored into separate class + */ +@NonNullByDefault +public abstract class ShadeData { + // fields common to Generation 1/2 and 3 hubs + public int id; + public @Nullable String name; + public int roomId; + public int type; + public int batteryStatus; + public @Nullable ShadePosition positions; + public int signalStrength; + public @Nullable Integer capabilities; + public @Nullable Firmware firmware; + + public String getName() { + return new String(Base64.getDecoder().decode(name)); + } + + public abstract BatteryKind getBatteryKind(); + + public abstract int version(); +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java index 0eee7aff49ce8..08a0ec1a2308d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java @@ -12,45 +12,17 @@ */ package org.openhab.binding.hdpowerview.internal.api; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; - import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; -import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; -import org.openhab.core.types.UnDefType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * The position of a single shade, as returned by the HD PowerView hub + * Abstract class for position of a Shade as returned by an HD PowerView hub. * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Added support for secondary rail positions + * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadePosition { - - private final transient Logger logger = LoggerFactory.getLogger(ShadePosition.class); - - /** - * Primary actuator position. - */ - private int posKind1; - private int position1; - - /** - * Secondary actuator position. - * - * Here we have to use Integer objects rather than just int primitives because these are secondary optional position - * elements in the JSON payload, so the GSON de-serializer might leave them as null. - */ - private @Nullable Integer posKind2 = null; - private @Nullable Integer position2 = null; - - public ShadePosition() { - } +public abstract class ShadePosition { /** * Get the shade's State for the given actuator class resp. coordinate system. @@ -59,248 +31,14 @@ public ShadePosition() { * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. * @return the current state. */ - public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - State result = getPosition1(shadeCapabilities, posKindCoords); - if (result == UnDefType.UNDEF) { - result = getPosition2(shadeCapabilities, posKindCoords); - } - logger.trace("getState(): capabilities={}, coords={} => result={}", shadeCapabilities, posKindCoords, result); - return result; - } - - /** - * Set the shade's position1 value for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - */ - private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { - // on dual rail shades constrain percent to not move the lower rail above the upper - State secondary = getState(shadeCapabilities, SECONDARY_POSITION); - if (secondary instanceof PercentType) { - int secPercent = ((PercentType) secondary).intValue(); - if (percent < secPercent) { - percent = secPercent; - } - } - } - posKind1 = posKindCoords.ordinal(); - position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); - break; - - case SECONDARY_POSITION: - /* - * Secondary, blackout shade a 'Duolite' shade: => INVERTED - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - posKind1 = posKindCoords.ordinal(); - if (shadeCapabilities.supportsSecondaryOverlapped()) { - position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); - } else { - position1 = (int) Math.round((double) percent / 100 * MAX_SHADE); - } - break; - - case VANE_TILT_POSITION: - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - */ - posKind1 = posKindCoords.ordinal(); - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - position1 = (int) Math.round((double) percent / 100 * max); - break; - - default: - posKind1 = CoordinateSystem.NONE.ordinal(); - position1 = 0; - } - } - - /** - * Get the shade's position1 State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the State (or UNDEF if not available). - */ - private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (posKindCoords.equals(posKind1)) { - return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); - } - if (VANE_TILT_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { - return PercentType.HUNDRED; - } - if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { - return PercentType.HUNDRED; - } - break; - - case SECONDARY_POSITION: - /* - * Secondary, blackout shade a 'Duolite' shade: => INVERTED - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (posKindCoords.equals(posKind1)) { - if (shadeCapabilities.supportsSecondaryOverlapped()) { - return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); - } - return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100)); - } - if (!SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { - return PercentType.ZERO; - } - break; - - case VANE_TILT_POSITION: - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - * - * If the shades are not open, the vane position is undefined; if the the shades - * are exactly open then the vanes are at zero; otherwise return the actual vane - * position itself - * - * note: sometimes the hub may return a value of position1 > MAX_VANE (seems to - * be a bug in the hub) so we avoid an out of range exception via the Math.min() - * function below.. - */ - if (posKindCoords.equals(posKind1)) { - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - return new PercentType((int) Math.round((double) Math.min(position1, max) / max * 100)); - } - if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { - return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO; - } - if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped() - && shadeCapabilities.supportsTiltOnClosed()) { - return PercentType.HUNDRED; - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, return UNDEF - } - return UnDefType.UNDEF; - } - - /** - * Set the shade's position2 value for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - */ - private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - posKind2 = posKindCoords.ordinal(); - position2 = Integer.valueOf(MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE)); - break; - - case SECONDARY_POSITION: - /* - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { - // on dual rail shades constrain percent to not move the upper rail below the lower - State primary = getState(shadeCapabilities, PRIMARY_POSITION); - if (primary instanceof PercentType) { - int primaryPercent = ((PercentType) primary).intValue(); - if (percent > primaryPercent) { - percent = primaryPercent; - } - } - } - posKind2 = posKindCoords.ordinal(); - position2 = Integer.valueOf((int) Math.round((double) percent / 100 * MAX_SHADE)); - break; - - case VANE_TILT_POSITION: - posKind2 = posKindCoords.ordinal(); - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - position2 = Integer.valueOf((int) Math.round((double) percent / 100 * max)); - break; - - default: - posKind2 = null; - position2 = null; - } - } - - /** - * Get the shade's position2 State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the State (or UNDEF if not available). - */ - private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - Integer posKind2 = this.posKind2; - Integer position2 = this.position2; - - if (position2 == null || posKind2 == null) { - return UnDefType.UNDEF; - } - - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (posKindCoords.equals(posKind2)) { - return new PercentType(100 - (int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); - } - break; - - case SECONDARY_POSITION: - /* - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (posKindCoords.equals(posKind2)) { - return new PercentType((int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); - } - break; - - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - */ - case VANE_TILT_POSITION: - if (posKindCoords.equals(posKind2)) { - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - return new PercentType((int) Math.round((double) Math.min(position2.intValue(), max) / max * 100)); - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, return UNDEF - } - return UnDefType.UNDEF; - } + public abstract State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords); /** * Detect if the ShadePosition has a posKindN value indicating potential support for a secondary rail. * * @return true if the ShadePosition supports a secondary rail. */ - public boolean secondaryRailDetected() { - return SECONDARY_POSITION.equals(posKind1) || SECONDARY_POSITION.equals(posKind2); - } + public abstract boolean secondaryRailDetected(); /** * Detect if the ShadePosition has both a posKindN value indicating potential support for tilt, AND a posKindN @@ -308,10 +46,7 @@ public boolean secondaryRailDetected() { * * @return true if potential support for tilt anywhere functionality was detected. */ - public boolean tiltAnywhereDetected() { - return ((PRIMARY_POSITION.equals(posKind1)) && (VANE_TILT_POSITION.equals(posKind2)) - || ((PRIMARY_POSITION.equals(posKind2) && (VANE_TILT_POSITION.equals(posKind1))))); - } + public abstract boolean tiltAnywhereDetected(); /** * Set the shade's position for the given actuator class resp. coordinate system. @@ -321,61 +56,6 @@ public boolean tiltAnywhereDetected() { * @param percent the new position value. * @return this object. */ - public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - logger.trace("setPosition(): capabilities={}, coords={}, percent={}", shadeCapabilities, posKindCoords, - percent); - // if necessary swap the order of position1 and position2 - if (PRIMARY_POSITION.equals(posKind2) && !PRIMARY_POSITION.equals(posKind1)) { - final Integer posKind2Temp = posKind2; - final Integer position2Temp = position2; - posKind2 = Integer.valueOf(posKind1); - position2 = Integer.valueOf(position1); - posKind1 = posKind2Temp != null ? posKind2Temp.intValue() : NONE.ordinal(); - position1 = position2Temp != null ? position2Temp.intValue() : 0; - } - - // delete position2 if it has an invalid position kind - if (ERROR_UNKNOWN.equals(posKind2) || NONE.equals(posKind2)) { - posKind2 = null; - position2 = null; - } - - // logic to set either position1 or position2 - switch (posKindCoords) { - case PRIMARY_POSITION: - if (shadeCapabilities.supportsPrimary()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case SECONDARY_POSITION: - if (shadeCapabilities.supportsSecondary()) { - if (shadeCapabilities.supportsPrimary()) { - setPosition2(shadeCapabilities, posKindCoords, percent); - } else { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - } else if (shadeCapabilities.supportsSecondaryOverlapped()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case VANE_TILT_POSITION: - if (shadeCapabilities.supportsPrimary()) { - if (shadeCapabilities.supportsTiltOnClosed()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } else if (shadeCapabilities.supportsTiltAnywhere()) { - setPosition2(shadeCapabilities, posKindCoords, percent); - } - } else if (shadeCapabilities.supportsTiltAnywhere()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, do nothing - } - return this; - } + public abstract ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, + int percent); } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java new file mode 100644 index 0000000000000..ebe97bed29f71 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v1; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.BatteryKind; +import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; + +/** + * State of a Shade as returned by an HD PowerView hub of Generation 1 or 2. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ShadeDataV1 extends ShadeData { + // fields for Generation 1/2 hubs only; NOTE: all these are Nullable + public int groupId; + public int order; + public double batteryStrength; + public boolean batteryIsLow; + public @Nullable Boolean timedOut; + public @Nullable Firmware motor; + // note: in old JSON batteryKind was a string but now it's a number; fortunately GSON string accepts either + public @Nullable String batteryKind; + + @Override + public BatteryKind getBatteryKind() { + return BatteryKind.fromString(batteryKind); + } + + @Override + public int version() { + return 1; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java new file mode 100644 index 0000000000000..3a7371f9587a7 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java @@ -0,0 +1,387 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v1; + +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The position of a single shade, as returned by an HD PowerView hub of Generation 1/2 + * + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Added support for secondary rail positions + */ +@NonNullByDefault +public class ShadePositionV1 extends ShadePosition { + + private final transient Logger logger = LoggerFactory.getLogger(ShadePositionV1.class); + + /** + * Primary actuator position. + */ + private int posKind1; + private int position1; + + /** + * Secondary actuator position. + * + * Here we have to use Integer objects rather than just int primitives because these are secondary optional position + * elements in the JSON payload, so the GSON de-serializer might leave them as null. + */ + private @Nullable Integer posKind2 = null; + private @Nullable Integer position2 = null; + + public ShadePositionV1() { + } + + /** + * Get the shade's State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the current state. + */ + @Override + public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + State result = getPosition1(shadeCapabilities, posKindCoords); + if (result == UnDefType.UNDEF) { + result = getPosition2(shadeCapabilities, posKindCoords); + } + logger.trace("getState(): capabilities={}, coords={} => result={}", shadeCapabilities, posKindCoords, result); + return result; + } + + /** + * Set the shade's position1 value for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + */ + private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { + // on dual rail shades constrain percent to not move the lower rail above the upper + State secondary = getState(shadeCapabilities, SECONDARY_POSITION); + if (secondary instanceof PercentType) { + int secPercent = ((PercentType) secondary).intValue(); + if (percent < secPercent) { + percent = secPercent; + } + } + } + posKind1 = posKindCoords.ordinal(); + position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); + break; + + case SECONDARY_POSITION: + /* + * Secondary, blackout shade a 'Duolite' shade: => INVERTED + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + posKind1 = posKindCoords.ordinal(); + if (shadeCapabilities.supportsSecondaryOverlapped()) { + position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); + } else { + position1 = (int) Math.round((double) percent / 100 * MAX_SHADE); + } + break; + + case VANE_TILT_POSITION: + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + */ + posKind1 = posKindCoords.ordinal(); + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + position1 = (int) Math.round((double) percent / 100 * max); + break; + + default: + posKind1 = CoordinateSystem.NONE.ordinal(); + position1 = 0; + } + } + + /** + * Get the shade's position1 State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the State (or UNDEF if not available). + */ + private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (posKindCoords.equals(posKind1)) { + return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); + } + if (VANE_TILT_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { + return PercentType.HUNDRED; + } + if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { + return PercentType.HUNDRED; + } + break; + + case SECONDARY_POSITION: + /* + * Secondary, blackout shade a 'Duolite' shade: => INVERTED + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (posKindCoords.equals(posKind1)) { + if (shadeCapabilities.supportsSecondaryOverlapped()) { + return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); + } + return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100)); + } + if (!SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { + return PercentType.ZERO; + } + break; + + case VANE_TILT_POSITION: + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + * + * If the shades are not open, the vane position is undefined; if the the shades + * are exactly open then the vanes are at zero; otherwise return the actual vane + * position itself + * + * note: sometimes the hub may return a value of position1 > MAX_VANE (seems to + * be a bug in the hub) so we avoid an out of range exception via the Math.min() + * function below.. + */ + if (posKindCoords.equals(posKind1)) { + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + return new PercentType((int) Math.round((double) Math.min(position1, max) / max * 100)); + } + if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { + return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO; + } + if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped() + && shadeCapabilities.supportsTiltOnClosed()) { + return PercentType.HUNDRED; + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, return UNDEF + } + return UnDefType.UNDEF; + } + + /** + * Set the shade's position2 value for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + */ + private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + posKind2 = posKindCoords.ordinal(); + position2 = Integer.valueOf(MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE)); + break; + + case SECONDARY_POSITION: + /* + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { + // on dual rail shades constrain percent to not move the upper rail below the lower + State primary = getState(shadeCapabilities, PRIMARY_POSITION); + if (primary instanceof PercentType) { + int primaryPercent = ((PercentType) primary).intValue(); + if (percent > primaryPercent) { + percent = primaryPercent; + } + } + } + posKind2 = posKindCoords.ordinal(); + position2 = Integer.valueOf((int) Math.round((double) percent / 100 * MAX_SHADE)); + break; + + case VANE_TILT_POSITION: + posKind2 = posKindCoords.ordinal(); + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + position2 = Integer.valueOf((int) Math.round((double) percent / 100 * max)); + break; + + default: + posKind2 = null; + position2 = null; + } + } + + /** + * Get the shade's position2 State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the State (or UNDEF if not available). + */ + private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + Integer posKind2 = this.posKind2; + Integer position2 = this.position2; + + if (position2 == null || posKind2 == null) { + return UnDefType.UNDEF; + } + + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (posKindCoords.equals(posKind2)) { + return new PercentType(100 - (int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); + } + break; + + case SECONDARY_POSITION: + /* + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (posKindCoords.equals(posKind2)) { + return new PercentType((int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); + } + break; + + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + */ + case VANE_TILT_POSITION: + if (posKindCoords.equals(posKind2)) { + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + return new PercentType((int) Math.round((double) Math.min(position2.intValue(), max) / max * 100)); + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, return UNDEF + } + return UnDefType.UNDEF; + } + + /** + * Detect if the ShadePosition has a posKindN value indicating potential support for a secondary rail. + * + * @return true if the ShadePosition supports a secondary rail. + */ + @Override + public boolean secondaryRailDetected() { + return SECONDARY_POSITION.equals(posKind1) || SECONDARY_POSITION.equals(posKind2); + } + + /** + * Detect if the ShadePosition has both a posKindN value indicating potential support for tilt, AND a posKindN + * indicating support for a primary rail. i.e. it potentially supports tilt anywhere functionality. + * + * @return true if potential support for tilt anywhere functionality was detected. + */ + @Override + public boolean tiltAnywhereDetected() { + return ((PRIMARY_POSITION.equals(posKind1)) && (VANE_TILT_POSITION.equals(posKind2)) + || ((PRIMARY_POSITION.equals(posKind2) && (VANE_TILT_POSITION.equals(posKind1))))); + } + + /** + * Set the shade's position for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + * @return this object. + */ + @Override + public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + logger.trace("setPosition(): capabilities={}, coords={}, percent={}", shadeCapabilities, posKindCoords, + percent); + // if necessary swap the order of position1 and position2 + if (PRIMARY_POSITION.equals(posKind2) && !PRIMARY_POSITION.equals(posKind1)) { + final Integer posKind2Temp = posKind2; + final Integer position2Temp = position2; + posKind2 = Integer.valueOf(posKind1); + position2 = Integer.valueOf(position1); + posKind1 = posKind2Temp != null ? posKind2Temp.intValue() : NONE.ordinal(); + position1 = position2Temp != null ? position2Temp.intValue() : 0; + } + + // delete position2 if it has an invalid position kind + if (ERROR_UNKNOWN.equals(posKind2) || NONE.equals(posKind2)) { + posKind2 = null; + position2 = null; + } + + // logic to set either position1 or position2 + switch (posKindCoords) { + case PRIMARY_POSITION: + if (shadeCapabilities.supportsPrimary()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case SECONDARY_POSITION: + if (shadeCapabilities.supportsSecondary()) { + if (shadeCapabilities.supportsPrimary()) { + setPosition2(shadeCapabilities, posKindCoords, percent); + } else { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + } else if (shadeCapabilities.supportsSecondaryOverlapped()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case VANE_TILT_POSITION: + if (shadeCapabilities.supportsPrimary()) { + if (shadeCapabilities.supportsTiltOnClosed()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } else if (shadeCapabilities.supportsTiltAnywhere()) { + setPosition2(shadeCapabilities, posKindCoords, percent); + } + } else if (shadeCapabilities.supportsTiltAnywhere()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, do nothing + } + return this; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java new file mode 100644 index 0000000000000..cadd5f62c4a56 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.BatteryKind; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; + +/** + * State of a Shade as returned by an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ShadeDataV3 extends ShadeData { + public @Nullable String ptName; + public @Nullable String powerType; + public @Nullable String bleName; + // TODO: public @Nullable Motion motion; + + @Override + public String getName() { + return String.join(" ", super.getName(), ptName); + } + + @Override + public BatteryKind getBatteryKind() { + // TODO Auto-generated method stub + // NOTE: the schema for powerType is not clear; is may be a string? or an integer? + return BatteryKind.ERROR_UNKNOWN; + } + + @Override + public int version() { + return 3; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java new file mode 100644 index 0000000000000..516d00df121dc --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * The position of a single shade, as returned by an HD PowerView hub of Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ShadePositionV3 extends ShadePosition { + + private @Nullable Double primary; + private @Nullable Double secondary; + private @Nullable Double tilt; + // private @Nullable Double velocity; + + @Override + public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + Double value; + switch (posKindCoords) { + case PRIMARY_POSITION: + value = primary; + break; + case SECONDARY_POSITION: + value = secondary; + break; + case VANE_TILT_POSITION: + value = tilt; + break; + default: + value = null; + } + return value != null ? new PercentType((int) (value.doubleValue() * 100)) : UnDefType.UNDEF; + } + + @Override + public boolean secondaryRailDetected() { + return secondary != null; + } + + @Override + public boolean tiltAnywhereDetected() { + return tilt != null; + } + + @Override + public ShadePositionV3 setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + Double value = Double.valueOf(percent) / 100.0; + switch (posKindCoords) { + case PRIMARY_POSITION: + primary = shadeCapabilities.supportsPrimary() ? value : null; + break; + case SECONDARY_POSITION: + secondary = shadeCapabilities.supportsSecondary() || shadeCapabilities.supportsSecondaryOverlapped() + ? value + : null; + break; + case VANE_TILT_POSITION: + tilt = shadeCapabilities.supportsTiltOnClosed() || shadeCapabilities.supportsTiltAnywhere() ? value + : null; + break; + default: + } + return this; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java new file mode 100644 index 0000000000000..4191c5bd96b95 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Scene object as returned by an HD PowerView hub + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public abstract class Scene implements Comparable { + // fields that are common to Generation 1/2 and 3 hubs + public int id; + public @Nullable String name; + + @Override + public abstract int compareTo(Scene other); + + public String getName() { + return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8); + } + + public abstract int version(); +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java index 5cb25a8aab0b4..4fa5a5fe4264a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.hdpowerview.internal.api.responses; -import java.nio.charset.StandardCharsets; -import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -29,58 +27,4 @@ public class Scenes { public @Nullable List sceneData; public @Nullable List sceneIds; - - /* - * the following SuppressWarnings annotation is because the Eclipse compiler - * does NOT expect a NonNullByDefault annotation on the inner class, since it is - * implicitly inherited from the outer class, whereas the Maven compiler always - * requires an explicit NonNullByDefault annotation on all classes - */ - @SuppressWarnings("null") - @NonNullByDefault - public static class Scene implements Comparable { - public int id; - public @Nullable String name; - public int roomId; - public int order; - public int colorId; - public int iconId; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof Scene)) { - return false; - } - Scene other = (Scene) o; - - return this.id == other.id && this.name.equals(other.name) && this.roomId == other.roomId - && this.order == other.order && this.colorId == other.colorId && this.iconId == other.iconId; - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + (name == null ? 0 : name.hashCode()); - result = prime * result + roomId; - result = prime * result + order; - result = prime * result + colorId; - result = prime * result + iconId; - - return result; - } - - @Override - public int compareTo(Scene other) { - return Integer.compare(order, other.order); - } - - public String getName() { - return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8); - } - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java new file mode 100644 index 0000000000000..7033358730002 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses; + +import java.time.DayOfWeek; +import java.util.EnumSet; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Abstract class for scheduled event as returned by an HD PowerView hub. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public abstract class ScheduledEvent { + // fields common to Generation 1/2 and 3 hubs + public int id; + public int type; + public boolean enabled; + public int hour; + public int minute; + public int sceneId; + + public abstract EnumSet getDays(); + + public abstract int getEventType(); + + public abstract int version(); +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java index e7ae6ac1ae440..5a595cd206a25 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java @@ -38,95 +38,4 @@ public class ScheduledEvents { public @Nullable List scheduledEventData; public @Nullable List scheduledEventIds; - - /* - * the following SuppressWarnings annotation is because the Eclipse compiler - * does NOT expect a NonNullByDefault annotation on the inner class, since it is - * implicitly inherited from the outer class, whereas the Maven compiler always - * requires an explicit NonNullByDefault annotation on all classes - */ - @SuppressWarnings("null") - @NonNullByDefault - public static class ScheduledEvent { - public int id; - public boolean enabled; - public int sceneId; - public int sceneCollectionId; - public boolean daySunday; - public boolean dayMonday; - public boolean dayTuesday; - public boolean dayWednesday; - public boolean dayThursday; - public boolean dayFriday; - public boolean daySaturday; - public int eventType; - public int hour; - public int minute; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof ScheduledEvent)) { - return false; - } - ScheduledEvent other = (ScheduledEvent) o; - - return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId - && this.sceneCollectionId == other.sceneCollectionId && this.daySunday == other.daySunday - && this.dayMonday == other.dayMonday && this.dayTuesday == other.dayTuesday - && this.dayWednesday == other.dayWednesday && this.dayThursday == other.dayThursday - && this.dayFriday == other.dayFriday && this.daySaturday == other.daySaturday - && this.eventType == other.eventType && this.hour == other.hour && this.minute == other.minute; - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + (enabled ? 1 : 0); - result = prime * result + sceneId; - result = prime * result + sceneCollectionId; - result = prime * result + (daySunday ? 1 : 0); - result = prime * result + (dayMonday ? 1 : 0); - result = prime * result + (dayTuesday ? 1 : 0); - result = prime * result + (dayWednesday ? 1 : 0); - result = prime * result + (dayThursday ? 1 : 0); - result = prime * result + (dayFriday ? 1 : 0); - result = prime * result + (daySaturday ? 1 : 0); - result = prime * result + eventType; - result = prime * result + hour; - result = prime * result + minute; - - return result; - } - - public EnumSet getDays() { - EnumSet days = EnumSet.noneOf(DayOfWeek.class); - if (daySunday) { - days.add(DayOfWeek.SUNDAY); - } - if (dayMonday) { - days.add(DayOfWeek.MONDAY); - } - if (dayTuesday) { - days.add(DayOfWeek.TUESDAY); - } - if (dayWednesday) { - days.add(DayOfWeek.WEDNESDAY); - } - if (dayThursday) { - days.add(DayOfWeek.THURSDAY); - } - if (dayFriday) { - days.add(DayOfWeek.FRIDAY); - } - if (daySaturday) { - days.add(DayOfWeek.SATURDAY); - } - return days; - } - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java index 69da9378395b7..f73635e929c27 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java @@ -14,7 +14,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; /** * State of a single Shade, as returned by an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java index 2746523b47a97..72d820d1ee7db 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java @@ -12,14 +12,11 @@ */ package org.openhab.binding.hdpowerview.internal.api.responses; -import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; /** * State of all Shades, as returned by an HD PowerView hub @@ -31,40 +28,4 @@ public class Shades { public @Nullable List shadeData; public @Nullable List shadeIds; - - /* - * the following SuppressWarnings annotation is because the Eclipse compiler - * does NOT expect a NonNullByDefault annotation on the inner class, since it is - * implicitly inherited from the outer class, whereas the Maven compiler always - * requires an explicit NonNullByDefault annotation on all classes - */ - @SuppressWarnings("null") - @NonNullByDefault - public static class ShadeData { - public int id; - public @Nullable String name; - public int roomId; - public int groupId; - public int order; - public int type; - public double batteryStrength; - public int batteryStatus; - public boolean batteryIsLow; - public @Nullable ShadePosition positions; - public @Nullable Boolean timedOut; - public int signalStrength; - public @Nullable Integer capabilities; - public @Nullable Firmware firmware; - public @Nullable Firmware motor; - // note: in old JSON batteryKind was a string but now it's a number; fortunately GSON string accepts either - public @Nullable String batteryKind; - - public String getName() { - return new String(Base64.getDecoder().decode(name)); - } - - public BatteryKind getBatteryKind() { - return BatteryKind.fromString(batteryKind); - } - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java new file mode 100644 index 0000000000000..f4efb2805964a --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v1; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; + +/** + * Scene object as returned by an HD PowerView hub of Generation 3 + * + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Refactored into separate class + */ +@NonNullByDefault +public class SceneV1 extends Scene { + public int roomId; + public int order; + public int colorId; + public int iconId; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof SceneV1)) { + return false; + } + SceneV1 other = (SceneV1) o; + + return this.id == other.id && getName().equals(other.getName()) && this.roomId == other.roomId + && this.order == other.order && this.colorId == other.colorId && this.iconId == other.iconId; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + id; + result = prime * result + getName().hashCode(); + result = prime * result + roomId; + result = prime * result + order; + result = prime * result + colorId; + result = prime * result + iconId; + + return result; + } + + @Override + public int compareTo(Scene other) throws IllegalArgumentException { + if (other.version() == version()) { + return Integer.compare(order, ((SceneV1) other).order); + } + throw new IllegalArgumentException("Cannot compare scenes from different hub generations"); + } + + @Override + public int version() { + return 1; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java new file mode 100644 index 0000000000000..b7ef3c878288c --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v1; + +import java.time.DayOfWeek; +import java.util.EnumSet; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; + +/** + * class for scheduled event as returned by an HD PowerView Generation 1/2 hub. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ScheduledEventV1 extends ScheduledEvent { + // fields specific to Generation 1/2 + public int sceneCollectionId; + public boolean daySunday; + public boolean dayMonday; + public boolean dayTuesday; + public boolean dayWednesday; + public boolean dayThursday; + public boolean dayFriday; + public boolean daySaturday; + public int eventType; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ScheduledEventV1)) { + return false; + } + ScheduledEventV1 other = (ScheduledEventV1) o; + + return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId + && this.sceneCollectionId == other.sceneCollectionId && this.daySunday == other.daySunday + && this.dayMonday == other.dayMonday && this.dayTuesday == other.dayTuesday + && this.dayWednesday == other.dayWednesday && this.dayThursday == other.dayThursday + && this.dayFriday == other.dayFriday && this.daySaturday == other.daySaturday + && this.eventType == other.eventType && this.hour == other.hour && this.minute == other.minute; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + (enabled ? 1 : 0); + result = prime * result + sceneId; + result = prime * result + sceneCollectionId; + result = prime * result + (daySunday ? 1 : 0); + result = prime * result + (dayMonday ? 1 : 0); + result = prime * result + (dayTuesday ? 1 : 0); + result = prime * result + (dayWednesday ? 1 : 0); + result = prime * result + (dayThursday ? 1 : 0); + result = prime * result + (dayFriday ? 1 : 0); + result = prime * result + (daySaturday ? 1 : 0); + result = prime * result + eventType; + result = prime * result + hour; + result = prime * result + minute; + + return result; + } + + @Override + public EnumSet getDays() { + EnumSet days = EnumSet.noneOf(DayOfWeek.class); + if (daySunday) { + days.add(DayOfWeek.SUNDAY); + } + if (dayMonday) { + days.add(DayOfWeek.MONDAY); + } + if (dayTuesday) { + days.add(DayOfWeek.TUESDAY); + } + if (dayWednesday) { + days.add(DayOfWeek.WEDNESDAY); + } + if (dayThursday) { + days.add(DayOfWeek.THURSDAY); + } + if (dayFriday) { + days.add(DayOfWeek.FRIDAY); + } + if (daySaturday) { + days.add(DayOfWeek.SATURDAY); + } + return days; + } + + @Override + public int getEventType() { + return eventType; + } + + @Override + public int version() { + return 1; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java new file mode 100644 index 0000000000000..9302a29782404 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.api.HubFirmware; + +/** + * DTO for the Generation 3 gateway information. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class InfoV3 { + public @Nullable String fwVersion; + public @Nullable String serialNumber; + + public HubFirmware toHubFirmware() { + Firmware firmware = new Firmware(); + String fwVersion = this.fwVersion; + if (fwVersion != null) { + String[] parts = fwVersion.split("\\."); + if (parts.length > 0) { + try { + firmware.revision = Integer.valueOf(parts[0]); + } catch (NumberFormatException e) { + } + } + if (parts.length > 1) { + try { + firmware.subRevision = Integer.valueOf(parts[1]); + } catch (NumberFormatException e) { + } + } + if (parts.length > 2) { + try { + firmware.build = Integer.valueOf(parts[2]); + } catch (NumberFormatException e) { + } + } + } + + String name = "Generation 3 Hub"; + if (serialNumber != null) { + name = String.format("%s (serial: %s)", name, serialNumber); + } + firmware.name = name; + + HubFirmware result = new HubFirmware(); + result.mainProcessor = firmware; + return result; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java new file mode 100644 index 0000000000000..5756f1a9a7e52 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; + +/** + * Scene object as returned by an HD PowerView hub of Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class SceneV3 extends Scene { + public @Nullable String ptName; + public @Nullable String color; + public @Nullable String icon; + public @Nullable List roomIds; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof SceneV3)) { + return false; + } + SceneV3 other = (SceneV3) o; + String color = this.color; + String icon = this.icon; + + // TODO check roomIds as well ?? + return this.id == other.id && getName().equals(other.getName()) && (color != null && color.equals(other.color)) + && (icon != null && icon.equals(other.icon)); + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + + String color = this.color; + String icon = this.icon; + + // TODO hash roomIds as well ?? + result = prime * result + id; + result = prime * result + getName().hashCode(); + result = prime * result + (color == null ? 0 : color.hashCode()); + result = prime * result + (icon == null ? 0 : icon.hashCode()); + + return result; + } + + @Override + public String getName() { + return String.join(" ", super.getName(), ptName); + } + + @Override + public int compareTo(Scene other) throws IllegalArgumentException { + if (other.version() == version()) { + // TODO fix this code.. + return this.equals(other) ? 0 : Integer.MAX_VALUE; + } + throw new IllegalArgumentException("Cannot compare scenes from different hub generations"); + } + + @Override + public int version() { + return 3; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java new file mode 100644 index 0000000000000..ea1504a262a8b --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import java.time.DayOfWeek; +import java.util.EnumSet; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; + +/** + * class for scheduled event as returned by an HD PowerView Generation 3 hub. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ScheduledEventV3 extends ScheduledEvent { + // fields specific to Generation 3 + public @Nullable String days; + + private static final int MON = 0x01; + private static final int TUE = 0x02; + private static final int WED = 0x04; + private static final int THU = 0x08; + private static final int FRI = 0x10; + private static final int SAT = 0x20; + private static final int SUN = 0x40; + + private static final int CLOCK_BASED = 0; + private static final int BEFORE_SUNRISE = 2; + private static final int BEFORE_SUNSET = 6; + private static final int AFTER_SUNRISE = 10; + private static final int AFTER_SUNSET = 14; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ScheduledEventV3)) { + return false; + } + ScheduledEventV3 other = (ScheduledEventV3) o; + String days = this.days; + + return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId + && (days != null && days.equals(other.days)) && this.hour == other.hour && this.minute == other.minute; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + String days = this.days; + result = prime * result + id; + result = prime * result + (enabled ? 1 : 0); + result = prime * result + sceneId; + result = prime * result + (days != null ? days.hashCode() : 0); + result = prime * result + hour; + result = prime * result + minute; + return result; + } + + @Override + public EnumSet getDays() { + EnumSet daySet = EnumSet.noneOf(DayOfWeek.class); + String days = this.days; + if (days != null) { + try { + int daysInt = Integer.valueOf(days).intValue(); + if ((daysInt & MON) != 0) { + daySet.add(DayOfWeek.MONDAY); + } + if ((daysInt & TUE) != 0) { + daySet.add(DayOfWeek.TUESDAY); + } + if ((daysInt & WED) != 0) { + daySet.add(DayOfWeek.WEDNESDAY); + } + if ((daysInt & THU) != 0) { + daySet.add(DayOfWeek.THURSDAY); + } + if ((daysInt & FRI) != 0) { + daySet.add(DayOfWeek.FRIDAY); + } + if ((daysInt & SAT) != 0) { + daySet.add(DayOfWeek.SATURDAY); + } + if ((daysInt & SUN) != 0) { + daySet.add(DayOfWeek.SUNDAY); + } + } catch (NumberFormatException e) { + // fall through + } + } + return daySet; + } + + @Override + public int getEventType() { + switch (type) { + case CLOCK_BASED: + return ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME; + + case BEFORE_SUNRISE: + case AFTER_SUNRISE: + // TODO handle before and after sunrise cases separately + return ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE; + + case BEFORE_SUNSET: + case AFTER_SUNSET: + // TODO handle before and after sunset cases separately + return ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET; + } + return 0; + } + + @Override + public int version() { + return 3; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java new file mode 100644 index 0000000000000..492f499a17257 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Scene SSE event object as supplied an HD PowerView hub of Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class SseSceneV3 { + public @Nullable String evt; + public @Nullable String isoDate; + public int id; + public @Nullable SceneV3 scene; +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java new file mode 100644 index 0000000000000..f9454ff4ed0f5 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; + +/** + * Shade SSE event object as supplied an HD PowerView hub of Generation 3 + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class SseShadeV3 { + public @Nullable String evt; + public @Nullable String isoDate; + public int id; + public @Nullable ShadePositionV3 currentPositions; +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java index 10782b22b991c..00a07ea4de4fa 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java @@ -25,10 +25,11 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; @@ -63,7 +64,7 @@ private AutomationChannelBuilder(HDPowerViewTranslationProvider translationProvi /** * Creates an {@link AutomationChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and * {@link ChannelGroupUID}. - * + * * @param translationProvider the {@link HDPowerViewTranslationProvider} * @param channelGroupUid parent {@link ChannelGroupUID} for created channels * @return channel builder @@ -75,7 +76,7 @@ public static AutomationChannelBuilder create(HDPowerViewTranslationProvider tra /** * Adds created channels to existing list. - * + * * @param channels list that channels will be added to * @return channel builder */ @@ -86,7 +87,7 @@ public AutomationChannelBuilder withChannels(List channels) { /** * Sets the scenes. - * + * * @param scenes the scenes * @return channel builder */ @@ -97,7 +98,7 @@ public AutomationChannelBuilder withScenes(List scenes) { /** * Sets the scene collections. - * + * * @param sceneCollections the scene collections * @return channel builder */ @@ -109,7 +110,7 @@ public AutomationChannelBuilder withSceneCollections(List scene /** * Sets the scheduled events. - * + * * @param scheduledEvents the sceduled events * @return channel builder */ @@ -168,20 +169,21 @@ public List build() { } logger.warn("Scene '{}' was not found for scheduled event '{}'", scheduledEvent.sceneId, scheduledEvent.id); return null; - } else if (scheduledEvent.sceneCollectionId > 0) { + } else if (scheduledEvent.version() == 1 && ((ScheduledEventV1) scheduledEvent).sceneCollectionId > 0) { Map sceneCollections = this.sceneCollections; + ScheduledEventV1 scheduledEventV1 = (ScheduledEventV1) scheduledEvent; if (sceneCollections == null) { logger.warn( "Scheduled event '{}' references scene collection '{}', but no scene collections are loaded", - scheduledEvent.id, scheduledEvent.sceneCollectionId); + scheduledEvent.id, scheduledEventV1.sceneCollectionId); return null; } - SceneCollection sceneCollection = sceneCollections.get(scheduledEvent.sceneCollectionId); + SceneCollection sceneCollection = sceneCollections.get(scheduledEventV1.sceneCollectionId); if (sceneCollection != null) { return sceneCollection.getName(); } logger.warn("Scene collection '{}' was not found for scheduled event '{}'", - scheduledEvent.sceneCollectionId, scheduledEvent.id); + scheduledEventV1.sceneCollectionId, scheduledEvent.id); return null; } else { logger.warn("Scheduled event '{}'' not related to any scene or scene collection", scheduledEvent.id); @@ -191,8 +193,7 @@ public List build() { private String getScheduledEventName(String sceneName, ScheduledEvent scheduledEvent) { String timeString, daysString; - - switch (scheduledEvent.eventType) { + switch (scheduledEvent.getEventType()) { case ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME: timeString = LocalTime.of(scheduledEvent.hour, scheduledEvent.minute).toString(); break; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java index 01c05137e9ece..8d9624620223e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; @@ -45,7 +45,7 @@ private SceneChannelBuilder(HDPowerViewTranslationProvider translationProvider, /** * Creates a {@link SceneChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and * {@link ChannelGroupUID}. - * + * * @param translationProvider the {@link HDPowerViewTranslationProvider} * @param channelGroupUid parent {@link ChannelGroupUID} for created channels * @return channel builder @@ -57,7 +57,7 @@ public static SceneChannelBuilder create(HDPowerViewTranslationProvider translat /** * Adds created channels to existing list. - * + * * @param channels list that channels will be added to * @return channel builder */ @@ -68,7 +68,7 @@ public SceneChannelBuilder withChannels(List channels) { /** * Sets the scenes. - * + * * @param scenes the scenes * @return channel builder */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java index 98d108befcfb1..37d9b6f952a48 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java @@ -21,9 +21,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index b3991f3aad3bd..ee4e935f27942 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; @@ -30,17 +31,23 @@ import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; +import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; +import org.openhab.binding.hdpowerview.internal._v3.SseSinkV3; import org.openhab.binding.hdpowerview.internal.api.Firmware; import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder; @@ -79,7 +86,7 @@ * @author Jacob Laursen - Added support for scene groups and automations */ @NonNullByDefault -public class HDPowerViewHubHandler extends BaseBridgeHandler { +public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV3 { private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class); private final HttpClient httpClient; @@ -163,8 +170,14 @@ public void initialize() { return; } + try { + webTargets = newWebTargets(host); + } catch (InstantiationException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + return; + } + pendingShadeInitializations.clear(); - webTargets = new HDPowerViewWebTargets(httpClient, host); refreshInterval = config.refresh; hardRefreshPositionInterval = config.hardRefresh; hardRefreshBatteryLevelInterval = config.hardRefreshBatteryLevel; @@ -227,7 +240,12 @@ public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { } private void schedulePoll() { - scheduleSoftPoll(); + if (sseSubscribe(this)) { + scheduler.submit(this::poll); // do a single poll to fetch the initial state + } else { + scheduleSoftPoll(); + } + // do hard polls (even on generation 3) in case SSE subscriptions have dropped scheduleHardPoll(); } @@ -280,6 +298,8 @@ private synchronized void stopPoll() { future.cancel(true); } this.hardRefreshBatteryLevelFuture = null; + + sseSubscribe(null); } private synchronized void poll() { @@ -422,7 +442,7 @@ private void updateUnknownShadeThing(Thing thing) { HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler()); if (thingHandler == null) { logger.debug("Shade '{}' handler not initialized", shadeId); - pendingShadeInitializations.put(thing.getUID(), new ShadeData()); + pendingShadeInitializations.put(thing.getUID(), newShadeData()); return; } ThingStatus thingStatus = thingHandler.getThing().getStatus(); @@ -436,7 +456,7 @@ private void updateUnknownShadeThing(Thing thing) { case UNINITIALIZED: case INITIALIZING: logger.debug("Shade '{}' handler not yet ready; status: {}", shadeId, thingStatus); - pendingShadeInitializations.put(thing.getUID(), new ShadeData()); + pendingShadeInitializations.put(thing.getUID(), newShadeData()); break; case REMOVING: case REMOVED: @@ -655,6 +675,9 @@ private void requestRefreshShadePositions() { logger.debug("Shade '{}' handler not initialized", shadeId); } } + + // re-subscribe (in case SSE connections went down) + sseSubscribe(this); } private void requestRefreshShadeBatteryLevels() { @@ -675,4 +698,90 @@ private void requestRefreshShadeBatteryLevels() { } } } + + /** + * Instantiate the web targets. + * + * @param host the ip address + * @return instance of HDPowerViewWebTargets class (either V1 or V3). + * @throws InstantiationException if neither a V1 nor a V3 web target was instantiated. + */ + private HDPowerViewWebTargets newWebTargets(String host) throws InstantiationException { + HDPowerViewWebTargets webTargets = this.webTargets; + if (webTargets != null) { + return webTargets; + } + try { + // try communicating via V1 web targets + webTargets = new HDPowerViewWebTargetsV1(httpClient, host); + webTargets.getFirmwareVersions(); + this.webTargets = webTargets; + return webTargets; + } catch (HubProcessingException | HubMaintenanceException e) { + // fall through + } + try { + // try communicating via V3 web targets + webTargets = new HDPowerViewWebTargetsV3(httpClient, host); + webTargets.getFirmwareVersions(); + this.webTargets = webTargets; + return webTargets; + } catch (HubProcessingException | HubMaintenanceException e) { + // fall through + } + throw new InstantiationException("Unable to instantiate the web targets"); + } + + /** + * Check if gateway is generation 1 + * + * @return true if gateway is generation 1 + */ + private boolean isGeneration1() { + return webTargets instanceof HDPowerViewWebTargetsV1; + } + + /** + * Create a new ShadeData instance; either V1 or V3 depending on the gateway generation. + * + * @return new ShadeData instance. + */ + private ShadeData newShadeData() { + return isGeneration1() ? new ShadeDataV1() : new ShadeDataV3(); + } + + /** + * If the gateway is generation 3 try to (un)subscribe to SSE on it. If 'sseSinK' is not null, make the + * subscription, otherwise cancel it. + * + * @param sseSinK the sink for the SSE call backs (may be null). + * @return true if the subscription succeeded. + */ + private boolean sseSubscribe(@Nullable SseSinkV3 sseSinK) { + if (webTargets instanceof HDPowerViewWebTargetsV3) { + try { + return ((HDPowerViewWebTargetsV3) webTargets).sseSubscribe(sseSinK); + } catch (HubMaintenanceException | HubProcessingException e) { + logger.warn("Failed to {}subscribe for SSE '{}'", sseSinK == null ? "un-" : "", e.getMessage()); + } + } + return false; + } + + @Override + public void sseShade(String evt, int shadeId, ShadePosition shadePosition) { + Optional thing = getShadeThingIdMap().entrySet().stream() + .filter(e -> e.getValue().equals(Integer.valueOf(shadeId))).findFirst().map(Map.Entry::getKey); + if (thing.isPresent()) { + ThingHandler handler = thing.get().getHandler(); + if (handler instanceof HDPowerViewShadeHandler) { + ((HDPowerViewShadeHandler) handler).sseShadePosition(shadePosition); + } + } + } + + @Override + public void sseScene(String evt, int sceneId) { + // TODO (if anything) + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java index 53e77e0425cdb..51be5b55cc09c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java @@ -32,9 +32,12 @@ import org.openhab.binding.hdpowerview.internal.api.BatteryKind; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; @@ -95,6 +98,8 @@ private enum RefreshKind { private int shadeId; private boolean isDisposing; + private boolean isGeneration1 = true; + public HDPowerViewShadeHandler(Thing thing) { super(thing); } @@ -250,6 +255,7 @@ private void handleShadeCommand(String channelId, Command command, HDPowerViewWe * @param shadeData the ShadeData to be used. */ protected void onReceiveUpdate(ShadeData shadeData) { + isGeneration1 = shadeData.version() == 1; updateStatus(ThingStatus.ONLINE); updateCapabilities(shadeData); updateSoftProperties(shadeData); @@ -258,7 +264,7 @@ protected void onReceiveUpdate(ShadeData shadeData) { if (shadePosition != null) { updatePositionStates(shadePosition); } - updateBatteryStates(shadeData.batteryStatus, shadeData.batteryStrength); + updateBatteryStates(shadeData); updateSignalStrengthState(shadeData.signalStrength); } @@ -334,7 +340,7 @@ private void updateSoftProperties(ShadeData shadeData) { private void updateFirmwareProperties(ShadeData shadeData) { Map properties = editProperties(); Firmware shadeFirmware = shadeData.firmware; - Firmware motorFirmware = shadeData.motor; + Firmware motorFirmware = (shadeData.version() == 1) ? ((ShadeDataV1) shadeData).motor : null; if (shadeFirmware != null) { properties.put(Thing.PROPERTY_FIRMWARE_VERSION, shadeFirmware.toString()); } @@ -399,8 +405,9 @@ private void updatePositionStates(ShadePosition shadePos) { updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePos.getState(capabilities, SECONDARY_POSITION)); } - private void updateBatteryStates(int batteryStatus, double batteryStrength) { - updateBatteryLevelStates(batteryStatus); + private void updateBatteryStates(ShadeData shadeData) { + updateBatteryLevelStates(shadeData.batteryStatus); + double batteryStrength = shadeData.version() == 1 ? ((ShadeDataV1) shadeData).batteryStrength : 0; updateState(CHANNEL_SHADE_BATTERY_VOLTAGE, batteryStrength > 0 ? new QuantityType<>(batteryStrength / 10, Units.VOLT) : UnDefType.UNDEF); } @@ -441,7 +448,7 @@ private void moveShade(CoordinateSystem coordSys, int newPercent, HDPowerViewWeb newPosition = shadeData.positions; // if no positions returned, then create a new position if (newPosition == null) { - newPosition = new ShadePosition(); + newPosition = newShadePosition(); } Capabilities capabilities = getCapabilitiesOrDefault(); // set the new position value, and write the positions to the hub @@ -570,7 +577,7 @@ private void doRefreshShade(RefreshKind kind) { break; case BATTERY_LEVEL: shadeData = webTargets.refreshShadeBatteryLevel(shadeId); - updateBatteryStates(shadeData.batteryStatus, shadeData.batteryStrength); + updateBatteryStates(shadeData); break; default: throw new NotSupportedException("Unsupported refresh kind " + kind.toString()); @@ -648,4 +655,24 @@ private void updateDynamicChannels(Capabilities capabilities, ShadeData shade) { updateThing(editThing().withoutChannels(removeList).build()); } } + + private ShadePosition newShadePosition() { + return isGeneration1 ? new ShadePositionV1() : new ShadePositionV3(); + } + + /** + * Update position states with the new position provided by an SSE event. + * + * @param shadePosition the new position + */ + public void sseShadePosition(ShadePosition shadePosition) { + if (thing.getStatus() == ThingStatus.ONLINE) { + Capabilities capabilities = this.capabilities; + if (capabilities != null) { + updateState(CHANNEL_SHADE_POSITION, shadePosition.getState(capabilities, PRIMARY_POSITION)); + updateState(CHANNEL_SHADE_VANE, shadePosition.getState(capabilities, VANE_TILT_POSITION)); + updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePosition.getState(capabilities, SECONDARY_POSITION)); + } + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java index ba6cb99574fc4..8c92ec0fd366c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java @@ -24,10 +24,12 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; @@ -65,7 +67,7 @@ private void setUp() { logger.setLevel(Level.OFF); builder = AutomationChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID); - Scene scene = new Scene(); + Scene scene = new SceneV1(); scene.id = 1; scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes()); scenes = new ArrayList<>(List.of(scene)); @@ -78,7 +80,7 @@ private void setUp() { @Test public void sceneSunriseWeekends() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.daySaturday = true; scheduledEvent.daySunday = true; @@ -91,7 +93,7 @@ public void sceneSunriseWeekends() { @Test public void sceneSunsetWeekdays() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.dayWednesday = true; @@ -107,7 +109,7 @@ public void sceneSunsetWeekdays() { @Test public void sceneTimeAllDays() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.dayWednesday = true; @@ -127,7 +129,7 @@ public void sceneTimeAllDays() { @Test public void sceneMinutesBeforeSunriseMondayTuesday() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.minute = -15; @@ -141,7 +143,7 @@ public void sceneMinutesBeforeSunriseMondayTuesday() { @Test public void sceneHoursMinutesAfterSunriseMonday() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.dayMonday = true; scheduledEvent.minute = 61; @@ -154,7 +156,7 @@ public void sceneHoursMinutesAfterSunriseMonday() { @Test public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayWednesday = true; scheduledEvent.dayThursday = true; scheduledEvent.dayFriday = true; @@ -169,7 +171,7 @@ public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() { @Test public void sceneHourAfterSunsetFridaySaturdaySunday() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayFriday = true; scheduledEvent.daySaturday = true; scheduledEvent.daySunday = true; @@ -224,7 +226,7 @@ public void emptyListWhenNoScenesOrSceneCollections() { @Test public void emptyListWhenNoSceneForScheduledEvent() { - ScheduledEvent scheduledEvent = createScheduledEventWithSceneCollection( + ScheduledEventV1 scheduledEvent = createScheduledEventWithSceneCollection( ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build(); @@ -234,7 +236,7 @@ public void emptyListWhenNoSceneForScheduledEvent() { @Test public void emptyListWhenNoSceneCollectionForScheduledEvent() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents) @@ -245,7 +247,7 @@ public void emptyListWhenNoSceneCollectionForScheduledEvent() { @Test public void groupAndIdAreCorrect() { - ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.id = 42; List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build(); @@ -255,16 +257,16 @@ public void groupAndIdAreCorrect() { assertEquals(Integer.toString(scheduledEvent.id), channels.get(0).getUID().getIdWithoutGroup()); } - private ScheduledEvent createScheduledEventWithScene(int eventType) { - ScheduledEvent scheduledEvent = new ScheduledEvent(); + private ScheduledEventV1 createScheduledEventWithScene(int eventType) { + ScheduledEventV1 scheduledEvent = new ScheduledEventV1(); scheduledEvent.id = 1; scheduledEvent.sceneId = scenes.get(0).id; scheduledEvent.eventType = eventType; return scheduledEvent; } - private ScheduledEvent createScheduledEventWithSceneCollection(int eventType) { - ScheduledEvent scheduledEvent = new ScheduledEvent(); + private ScheduledEventV1 createScheduledEventWithSceneCollection(int eventType) { + ScheduledEventV1 scheduledEvent = new ScheduledEventV1(); scheduledEvent.id = 1; scheduledEvent.sceneCollectionId = sceneCollections.get(0).id; scheduledEvent.eventType = eventType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java new file mode 100644 index 0000000000000..f32f5ca731ecb --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; +import org.junit.jupiter.api.Test; +import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.UnDefType; + +/** + * Unit tests for Generation 3 DTO's. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class Generation3DtoTest { + + private final HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV3(new HttpClient(), ""); + private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); + + private String loadJson(String filename) throws IOException { + try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { + if (inputStream == null) { + throw new IOException("inputstream is null"); + } + byte[] bytes = inputStream.readAllBytes(); + if (bytes == null) { + throw new IOException("Resulting byte-array empty"); + } + return new String(bytes, StandardCharsets.UTF_8); + } + } + + /** + * Test JSON scenes response. + */ + @Test + public void testScenesParsing() throws IOException { + try { + Method method = webTargets.getClass().getDeclaredMethod("toScenes", String.class); + method.setAccessible(true); + String json = loadJson("_v3/scenes.json"); + Object result = method.invoke(webTargets, json); + assertTrue(result instanceof Scenes); + List sceneData = ((Scenes) result).sceneData; + assertNotNull(sceneData); + assertEquals(1, sceneData.size()); + Scene scene = sceneData.get(0); + assertEquals("Open All Office Shades\n ABC", scene.getName()); + assertEquals(234, scene.id); + assertEquals(3, scene.version()); + } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { + fail(e.getMessage()); + } + } + + /** + * Test JSON shades response. + */ + @Test + public void testShadesParsing() throws IOException { + try { + Method method = webTargets.getClass().getDeclaredMethod("toShades", String.class); + method.setAccessible(true); + String json = loadJson("_v3/shades.json"); + Object result = method.invoke(webTargets, json); + assertTrue(result instanceof Shades); + List shadeDataList = ((Shades) result).shadeData; + assertNotNull(shadeDataList); + assertEquals(1, shadeDataList.size()); + ShadeData shadeData = shadeDataList.get(0); + assertEquals("Shade 2 ABC", shadeData.getName()); + assertEquals(789, shadeData.id); + assertEquals(3, shadeData.version()); + } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { + fail(e.getMessage()); + } + } + + /** + * Test JSON automation response. + */ + @Test + public void testAutomationParsing() throws IOException { + try { + Method method = webTargets.getClass().getDeclaredMethod("toScheduledEvents", String.class); + method.setAccessible(true); + String json = loadJson("_v3/automations.json"); + Object result = method.invoke(webTargets, json); + assertTrue(result instanceof ScheduledEvents); + List scheduledEventList = ((ScheduledEvents) result).scheduledEventData; + assertNotNull(scheduledEventList); + assertEquals(1, scheduledEventList.size()); + ScheduledEvent scheduledEvent = scheduledEventList.get(0); + assertEquals(33, scheduledEvent.id); + assertTrue(scheduledEvent.enabled); + } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { + fail(e.getMessage()); + } + } + + /** + * Test JSON shade event response. + */ + @Test + public void testShadeEventParsing() throws IOException { + try { + Method method = webTargets.getClass().getDeclaredMethod("toShadeData2", String.class); + method.setAccessible(true); + String json = loadJson("_v3/shade-event.json"); + Object result = method.invoke(webTargets, json); + assertTrue(result instanceof ShadeData); + ShadeData shadeData = ((ShadeData) result); + assertEquals(3, shadeData.version()); + ShadePosition position = shadeData.positions; + assertNotNull(position); + assertEquals(PercentType.valueOf("99"), + position.getState(DB.getCapabilities(0), CoordinateSystem.PRIMARY_POSITION)); + assertEquals(PercentType.valueOf("98"), + position.getState(DB.getCapabilities(0), CoordinateSystem.SECONDARY_POSITION)); + assertEquals(PercentType.ZERO, + position.getState(DB.getCapabilities(0), CoordinateSystem.VANE_TILT_POSITION)); + } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException + | InvocationTargetException e) { + fail(e.getMessage()); + } + } + + /** + * Test JSON shade position setting. + */ + @Test + public void testShadePositions() { + ShadePositionV3 pos; + Capabilities caps; + + caps = DB.getCapabilities(0); // test with only primary support + pos = new ShadePositionV3(); + pos.setPosition(caps, CoordinateSystem.PRIMARY_POSITION, 11); + pos.setPosition(caps, CoordinateSystem.SECONDARY_POSITION, 22); + pos.setPosition(caps, CoordinateSystem.VANE_TILT_POSITION, 33); + assertEquals(PercentType.valueOf("11"), pos.getState(caps, CoordinateSystem.PRIMARY_POSITION)); + assertEquals(UnDefType.UNDEF, pos.getState(caps, CoordinateSystem.SECONDARY_POSITION)); + assertEquals(UnDefType.UNDEF, pos.getState(caps, CoordinateSystem.VANE_TILT_POSITION)); + + caps = DB.getCapabilities(9);// test with primary, secondary, and tilt support + pos = new ShadePositionV3(); + pos.setPosition(caps, CoordinateSystem.PRIMARY_POSITION, 11); + pos.setPosition(caps, CoordinateSystem.SECONDARY_POSITION, 22); + pos.setPosition(caps, CoordinateSystem.VANE_TILT_POSITION, 33); + assertEquals(PercentType.valueOf("11"), pos.getState(caps, CoordinateSystem.PRIMARY_POSITION)); + assertEquals(PercentType.valueOf("22"), pos.getState(caps, CoordinateSystem.SECONDARY_POSITION)); + assertEquals(PercentType.valueOf("33"), pos.getState(caps, CoordinateSystem.VANE_TILT_POSITION)); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java index 19cd35c7c33ee..737c932c46813 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java @@ -22,15 +22,17 @@ import java.util.Objects; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; +import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; import org.openhab.binding.hdpowerview.internal.api.BatteryKind; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; @@ -48,7 +50,7 @@ @NonNullByDefault public class HDPowerViewJUnitTests { - private Gson gson = new Gson(); + private final Gson gson = new HDPowerViewWebTargetsV1(new HttpClient(), "").getGson(); private T getObjectFromJson(String filename, Class clazz) throws IOException { try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { @@ -69,7 +71,7 @@ private T getObjectFromJson(String filename, Class clazz) throws IOExcept */ @Test public void shadeNameIsDecoded() throws IOException { - Shades shades = getObjectFromJson("shades.json", Shades.class); + Shades shades = getObjectFromJson("_v1/shades.json", Shades.class); List shadeData = shades.shadeData; assertNotNull(shadeData); assertEquals(3, shadeData.size()); @@ -82,7 +84,7 @@ public void shadeNameIsDecoded() throws IOException { */ @Test public void testBatteryKind() throws IOException { - Shades shades = getObjectFromJson("shades.json", Shades.class); + Shades shades = getObjectFromJson("_v1/shades.json", Shades.class); List shadeData = shades.shadeData; assertNotNull(shadeData); ShadeData shade = shadeData.get(0); @@ -96,7 +98,7 @@ public void testBatteryKind() throws IOException { */ @Test public void sceneNameIsDecoded() throws IOException { - Scenes scenes = getObjectFromJson("scenes.json", Scenes.class); + Scenes scenes = getObjectFromJson("_v1/scenes.json", Scenes.class); List sceneData = scenes.sceneData; assertNotNull(sceneData); assertEquals(4, sceneData.size()); @@ -109,7 +111,7 @@ public void sceneNameIsDecoded() throws IOException { */ @Test public void sceneCollectionNameIsDecoded() throws IOException { - SceneCollections sceneCollections = getObjectFromJson("sceneCollections.json", SceneCollections.class); + SceneCollections sceneCollections = getObjectFromJson("_v1/sceneCollections.json", SceneCollections.class); List sceneCollectionData = sceneCollections.sceneCollectionData; assertNotNull(sceneCollectionData); @@ -124,7 +126,7 @@ public void sceneCollectionNameIsDecoded() throws IOException { */ @Test public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws IOException { - Shades shades = getObjectFromJson("duette.json", Shades.class); + Shades shades = getObjectFromJson("_v1/duette.json", Shades.class); List shadesData = shades.shadeData; assertNotNull(shadesData); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java index b8670bb91ec92..cdb3660f01ee1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java @@ -13,7 +13,7 @@ package org.openhab.binding.hdpowerview; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.PRIMARY_POSITION; import java.util.List; import java.util.regex.Pattern; @@ -22,11 +22,13 @@ import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; +import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; @@ -83,7 +85,7 @@ public void testOnlineCommunication() { fail(e.getMessage()); } - HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress); + HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV1(client, hubIPAddress); assertNotNull(webTargets); int shadeId = 0; @@ -168,7 +170,7 @@ public void testOnlineCommunication() { int position = ((PercentType) pos).intValue(); position = position + ((position <= 10) ? 5 : -5); - ShadePosition targetPosition = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, + ShadePosition targetPosition = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, position); assertNotNull(targetPosition); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java index eca2a1f2e9ebb..2b96fa966540e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java @@ -24,7 +24,8 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; @@ -110,7 +111,7 @@ public void emptyListWhenNoScenes() { } private List createScenes() { - Scene scene = new Scene(); + Scene scene = new SceneV1(); scene.id = 1; scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes()); return new ArrayList<>(List.of(scene)); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java index 50dcd0e7ff4c2..55688bccf1c65 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java @@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; @@ -90,7 +91,7 @@ private void assertShadePosition(State position, int value) { @Test public void testCaps1ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -104,7 +105,7 @@ public void testCaps1ShadePositionParsingFullyUp() { @Test public void testCaps1ShadePositionParsingShadeFullyDown1() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -118,7 +119,7 @@ public void testCaps1ShadePositionParsingShadeFullyDown1() { @Test public void testCaps1ShadePositionParsingShadeFullyDown2() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 0); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -132,7 +133,7 @@ public void testCaps1ShadePositionParsingShadeFullyDown2() { @Test public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 88); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 88); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -147,7 +148,7 @@ public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() { @Test public void testDualRailConstraints() { Capabilities capabilities = db.getCapabilities(7); - ShadePosition test = new ShadePosition(); + ShadePosition test = new ShadePositionV1(); // ==== OK !! primary at bottom, secondary at top ==== test.setPosition(capabilities, PRIMARY_POSITION, 100).setPosition(capabilities, SECONDARY_POSITION, 0); @@ -207,42 +208,42 @@ public void testDuoliteShadePositionParsing() { ShadePosition test; // both shades up - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 50% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 50); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 50); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 50); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 0% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 0% down (ALTERNATE) - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 50% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 50); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 50); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 50); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 100% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 100); @@ -260,70 +261,70 @@ public void testDuoliteTiltShadePositionParsing() { ShadePosition test; // front shade up - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 30% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 30); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0); // tilt 0% - test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0); // tilt 30% - test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 30); + test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30); // tilt 100% - test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 0% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 0); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 30% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 30); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 30); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 100% down - test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 100); + test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 100); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // test constraints on impossible values: primary 30% => tilt 30% - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30).setPosition(capabilities, + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30).setPosition(capabilities, VANE_TILT_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); @@ -331,7 +332,7 @@ public void testDuoliteTiltShadePositionParsing() { assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30); // test constraints on impossible values: primary 30% => tilt 30% => back shade 30% down - test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30) + test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30) .setPosition(capabilities, VANE_TILT_POSITION, 30).setPosition(capabilities, SECONDARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); @@ -346,7 +347,7 @@ public void testDuoliteTiltShadePositionParsing() { @Test public void testCaps0ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(0); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -360,7 +361,7 @@ public void testCaps0ShadePositionParsingFullyUp() { @Test public void testCap0ShadePositionParsingShadeFullyDown() { Capabilities capabilities = db.getCapabilities(0); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -384,7 +385,7 @@ private void assertShadePosition(State actual, State target) { @Test public void testType44ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(44, null); - ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -398,7 +399,7 @@ public void testType44ShadePositionParsingFullyUp() { @Test public void testType44ShadePositionParsingShadeFullyDownVaneOpen() { Capabilities capabilities = db.getCapabilities(44, null); - ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 88); + ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 88); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/duette.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/duette.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/sceneCollections.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/sceneCollections.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/shades.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json new file mode 100644 index 0000000000000..1481512f41f60 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json @@ -0,0 +1,13 @@ +[ + { + "id": 33, + "type": 6, + "enabled": true, + "days": "24", + "hour": 4, + "min": 5, + "bleId": 83, + "sceneId": 116, + "errorShd_Ids": null + } +] diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json new file mode 100644 index 0000000000000..5a4c78d315a7e --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json @@ -0,0 +1,25 @@ +[ + { + "id": 789, + "type": 23, + "name": "Q2VudGVyCg==", + "ptName": "Center", + "capabilities": 0, + "powerType": "wand", + "batteryStatus": 2, + "roomId": 6833, + "signalStrength": -55, + "bleName": "PIR:5933", + "firmware": { + "revision": 3, + "subRevision": 0, + "build": 309 + }, + "positions": { + "primary": 0.99, + "secondary": 0.99, + "tilt": 0, + "velocity": 0.5 + } + } +] diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json new file mode 100644 index 0000000000000..6690715b23e44 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json @@ -0,0 +1,17 @@ +{ + "evt": "scene-activated", + "isoDate": "2021-12-06T20:01:11.934Z", + "id": 55, + "scene": { + "id": 234, + "name": "T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo=", + "ptName": "Open All Office Shades", + "color": "37", + "icon": "669", + "networkNumber": 7383, + "roomIds": [ + 55, + 66 + ] + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json new file mode 100644 index 0000000000000..3c131f1449736 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json @@ -0,0 +1,14 @@ +[ + { + "id": 234, + "name": "T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo=", + "ptName": "ABC", + "color": "37", + "icon": "669", + "networkNumber": 7383, + "roomIds": [ + 55, + 66 + ] + } +] diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json new file mode 100644 index 0000000000000..eb15d42a3b329 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json @@ -0,0 +1,17 @@ +{ + "evt": "motion-started", + "isoDate": "2021-12-06T20:01:11.934Z", + "bleName": "PIR:1233", + "id": 55, + "currentPositions": { + "primary": 0.99, + "secondary": 0.98, + "tilt": 0 + }, + "targetPositions": { + "etaInSeconds": 7, + "primary": 0.99, + "secondary": 0.99, + "tilt": 0 + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json new file mode 100644 index 0000000000000..a903771e23e37 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json @@ -0,0 +1,25 @@ +[ + { + "id": 789, + "type": 23, + "name": "U2hhZGUgMg==", + "ptName": "ABC", + "capabilities": 0, + "powerType": "wand", + "batteryStatus": 2, + "roomId": 6833, + "signalStrength": -55, + "bleName": "PIR:5933", + "firmware": { + "revision": 3, + "subRevision": 0, + "build": 309 + }, + "positions": { + "primary": 0.99, + "secondary": 0.99, + "tilt": 0, + "velocity": 0.5 + } + } +] From f0f3e2779eec0c75a8219b6ec14ff35a0ffe75b2 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 4 Sep 2022 18:47:09 +0100 Subject: [PATCH 24/64] [hdpowerview] add missing serializer Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/HDPowerViewWebTargets.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 4e9cf8ed2bf3d..7d336a3120127 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -56,6 +56,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; /** * Abstract class for JAX-RS targets for communicating with an HD PowerView hub. @@ -123,6 +125,16 @@ private class ShadePositionDeserializer implements JsonDeserializer { + @Override + public JsonElement serialize(ShadePosition src, Type typeOfT, JsonSerializationContext context) { + return context.serialize(src, shadePositionClass); + } + }; + /* * Private helper class for de-serialization of ScheduledEvent */ @@ -183,6 +195,7 @@ public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class s .registerTypeAdapter(Scene.class, new SceneDeserializer()) .registerTypeAdapter(ShadeData.class, new ShadeDataDeserializer()) .registerTypeAdapter(ShadePosition.class, new ShadePositionDeserializer()) + .registerTypeAdapter(ShadePosition.class, new ShadePositionSerializer()) .registerTypeAdapter(ScheduledEvent.class, new ScheduledEventDeserializer()) // @formatter:on .create(); From e2802a50db87a9f95ae8f12523f2212f5b39216f Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 4 Sep 2022 19:29:59 +0100 Subject: [PATCH 25/64] [hdpowerview] fix PUT command json Signed-off-by: Andrew Fiddian-Green --- .../internal/_v3/HDPowerViewWebTargetsV3.java | 10 +++++----- .../hdpowerview/internal/api/requests/ShadeMotion.java | 2 +- .../internal/api/requests/ShadePositions.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java index 18b0979f4c5a2..e43fc66e7ad2f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java @@ -34,8 +34,8 @@ import org.openhab.binding.hdpowerview.internal.api.UserData; import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadePositions; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; import org.openhab.binding.hdpowerview.internal.api.responses.Scene; @@ -163,7 +163,7 @@ public Shades getShades() throws HubInvalidResponseException, HubProcessingExcep public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), - gson.toJson(position)); + gson.toJson(new ShadePositions(position))); return getShade(shadeId); } @@ -177,7 +177,7 @@ public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubP @Override public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String json = gson.toJson(new ShadeJog()); + String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.JOG)); invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); return getShade(shadeId); } @@ -185,7 +185,7 @@ public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubPr @Override public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String json = gson.toJson(new ShadeCalibrate()); + String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.CALIBRATE)); invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); return getShade(shadeId); } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java index c6b16a000065e..13d3179c4841a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java @@ -20,7 +20,7 @@ * @author Jacob Laursen - Initial contribution */ @NonNullByDefault -class ShadeMotion { +public class ShadeMotion { public enum Type { STOP("stop"), diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java index 1c544c047e878..07b4c87e778a1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java @@ -21,7 +21,7 @@ * @author Andy Lintner - Initial contribution */ @NonNullByDefault -class ShadePositions { +public class ShadePositions { public ShadePosition positions; From dd13e96b11ebfdab053706202dd7f3a1ece326a6 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 4 Sep 2022 19:43:03 +0100 Subject: [PATCH 26/64] [hdpowerview] delete unnecessary json file Signed-off-by: Andrew Fiddian-Green --- .../binding/hdpowerview/_v3/motion.json | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json deleted file mode 100644 index 5a4c78d315a7e..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/motion.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "id": 789, - "type": 23, - "name": "Q2VudGVyCg==", - "ptName": "Center", - "capabilities": 0, - "powerType": "wand", - "batteryStatus": 2, - "roomId": 6833, - "signalStrength": -55, - "bleName": "PIR:5933", - "firmware": { - "revision": 3, - "subRevision": 0, - "build": 309 - }, - "positions": { - "primary": 0.99, - "secondary": 0.99, - "tilt": 0, - "velocity": 0.5 - } - } -] From d622022db1e56488e8f567a6123428f21eb32feb Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 5 Sep 2022 11:27:17 +0100 Subject: [PATCH 27/64] [hdpowerview] tweak serializer Signed-off-by: Andrew Fiddian-Green --- .../binding/hdpowerview/internal/HDPowerViewWebTargets.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 7d336a3120127..8360e3fdd742b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -130,8 +130,8 @@ private class ShadePositionDeserializer implements JsonDeserializer { @Override - public JsonElement serialize(ShadePosition src, Type typeOfT, JsonSerializationContext context) { - return context.serialize(src, shadePositionClass); + public JsonElement serialize(ShadePosition src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src, src.getClass()); } }; From d9c5c79ca85cdbfde03452dc734aae9596368bec Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 7 Sep 2022 16:43:16 +0100 Subject: [PATCH 28/64] [hpowerview] stronger type check on generics Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/HDPowerViewWebTargets.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 8360e3fdd742b..94480db4d8d7d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -81,10 +81,10 @@ public abstract class HDPowerViewWebTargets { /* * De-serializer target class types */ - private final Class sceneClass; - private final Class shadeDataClass; - private final Class shadePositionClass; - private final Class scheduledEventClass; + private final Class sceneClass; + private final Class shadeDataClass; + private final Class shadePositionClass; + private final Class scheduledEventClass; protected final Gson gson; protected final HttpClient httpClient; @@ -183,8 +183,9 @@ public String toString() { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class sceneClass, Class shadeDataClass, - Class shadePositionClass, Class scheduledEventClass) { + public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class sceneClass, + Class shadeDataClass, Class shadePositionClass, + Class scheduledEventClass) { this.httpClient = httpClient; this.sceneClass = sceneClass; this.shadeDataClass = shadeDataClass; From 29709e9b364276e36a20e9e580437e9168412daa Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 10 Sep 2022 12:46:42 +0100 Subject: [PATCH 29/64] [hdpowerview] implement battery kind on gen 3 Signed-off-by: Andrew Fiddian-Green --- .../internal/api/_v3/ShadeDataV3.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java index cadd5f62c4a56..ef6b3edde8b07 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java @@ -27,7 +27,6 @@ public class ShadeDataV3 extends ShadeData { public @Nullable String ptName; public @Nullable String powerType; public @Nullable String bleName; - // TODO: public @Nullable Motion motion; @Override public String getName() { @@ -36,8 +35,23 @@ public String getName() { @Override public BatteryKind getBatteryKind() { - // TODO Auto-generated method stub - // NOTE: the schema for powerType is not clear; is may be a string? or an integer? + // TODO the schema for powerType is not clear; is may be a string? or an integer? + String powerType = this.powerType; + if (powerType != null) { + try { + Integer powerTypeInt = Integer.valueOf(powerType); + switch (powerTypeInt) { + case 0: + return BatteryKind.BATTERY_WAND; + case 1: + return BatteryKind.HARDWIRED_POWER_SUPPLY; + case 2: + return BatteryKind.RECHARGEABLE_BATTERY_WAND; + } + } catch (NumberFormatException e) { + // fall through + } + } return BatteryKind.ERROR_UNKNOWN; } From f52593b918914506fa668be9c512daa955928a80 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 10 Sep 2022 12:47:44 +0100 Subject: [PATCH 30/64] [hdpowerview] implement generation configuration Signed-off-by: Andrew Fiddian-Green --- .../config/HDPowerViewHubConfiguration.java | 3 +++ .../handler/HDPowerViewHubHandler.java | 27 +++++++++---------- .../OH-INF/i18n/hdpowerview.properties | 2 ++ .../main/resources/OH-INF/thing/bridge.xml | 5 ++++ 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java index 14cd3b3424b72..156a4835fc07e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java @@ -24,10 +24,13 @@ public class HDPowerViewHubConfiguration { public static final String HOST = "host"; + public static final String GENERATION = "generation"; public @Nullable String host; public long refresh; public long hardRefresh; public long hardRefreshBatteryLevel; + + public int generation; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index ee4e935f27942..862bf0441b6b4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -711,23 +711,20 @@ private HDPowerViewWebTargets newWebTargets(String host) throws InstantiationExc if (webTargets != null) { return webTargets; } - try { - // try communicating via V1 web targets - webTargets = new HDPowerViewWebTargetsV1(httpClient, host); - webTargets.getFirmwareVersions(); - this.webTargets = webTargets; - return webTargets; - } catch (HubProcessingException | HubMaintenanceException e) { - // fall through + HDPowerViewHubConfiguration config = getConfigAs(HDPowerViewHubConfiguration.class); + int hubGeneration = config.generation; + switch (hubGeneration) { + case 0: // for non breaking of existing installations + case 1: // both generation 1 and 2 hubs use V1 web targets + case 2: + webTargets = new HDPowerViewWebTargetsV1(httpClient, host); + break; + case 3: // generation 3 hubs use V3 web targets + webTargets = new HDPowerViewWebTargetsV3(httpClient, host); } - try { - // try communicating via V3 web targets - webTargets = new HDPowerViewWebTargetsV3(httpClient, host); - webTargets.getFirmwareVersions(); + if (webTargets != null) { this.webTargets = webTargets; return webTargets; - } catch (HubProcessingException | HubMaintenanceException e) { - // fall through } throw new InstantiationException("Unable to instantiate the web targets"); } @@ -782,6 +779,6 @@ public void sseShade(String evt, int shadeId, ShadePosition shadePosition) { @Override public void sseScene(String evt, int sceneId) { - // TODO (if anything) + // TODO perhaps we don't need to do anything? } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index 6aeb6076f9582..cc4d76d24ddf4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -22,6 +22,8 @@ thing-type.hdpowerview.shade.channel.secondary.description = The secondary verti # thing types config +thing-type.config.hdpowerview.hub.generation.label = Hub Generation +thing-type.config.hdpowerview.hub.generation.description = The PowerView Hub Generation thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index 1c20f86e965d2..9458b79274956 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -27,6 +27,11 @@ The Host address of the PowerView Hub network-address + + + The PowerView Hub Generation + 0 + The number of milliseconds between fetches of the PowerView Hub shade state From 023ff8925fb1495306ea1696df5e0088f42629a3 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 10 Sep 2022 12:48:16 +0100 Subject: [PATCH 31/64] [hdpowerview] implement generation 1/2 and 3 mDNS Signed-off-by: Andrew Fiddian-Green --- .../HDPowerViewHubDiscoveryParticipant.java | 35 ++--------- .../HDPowerViewHubDiscoveryParticipantV1.java | 62 +++++++++++++++++++ .../HDPowerViewHubDiscoveryParticipantV3.java | 62 +++++++++++++++++++ 3 files changed, 130 insertions(+), 29 deletions(-) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java index 4ee2a8c4025b9..b118704083e38 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.hdpowerview.internal.discovery; -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; import java.util.Collections; import java.util.Set; @@ -22,28 +22,20 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; import org.openhab.core.config.discovery.DiscoveryResult; -import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; -import org.osgi.service.component.annotations.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * Discovers HD PowerView hubs by means of mDNS + * Abstract (component) class that discovers HD PowerView hubs by means of mDNS * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -@Component -public class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticipant { +public abstract class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticipant { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant.class); - - private static final Pattern VALID_IP_V4_ADDRESS = Pattern + protected static final Pattern VALID_IP_V4_ADDRESS = Pattern .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); @Override @@ -52,25 +44,10 @@ public Set getSupportedThingTypeUIDs() { } @Override - public String getServiceType() { - return "_powerview._tcp.local."; - } + public abstract String getServiceType(); @Override - public @Nullable DiscoveryResult createResult(ServiceInfo service) { - for (String host : service.getHostAddresses()) { - if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); - DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) - .withProperty(HDPowerViewHubConfiguration.HOST, host) - .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel("PowerView Hub (" + host + ")").build(); - logger.debug("mDNS discovered hub on host '{}'", host); - return hub; - } - } - return null; - } + public abstract @Nullable DiscoveryResult createResult(ServiceInfo service); @Override public @Nullable ThingUID getThingUID(ServiceInfo service) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java new file mode 100644 index 0000000000000..bb138c5eef52c --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.discovery._v1; + +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; + +import javax.jmdns.ServiceInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; +import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewHubDiscoveryParticipant; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Discovers HD PowerView generation 1/2 hubs by means of mDNS + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +@Component +public class HDPowerViewHubDiscoveryParticipantV1 extends HDPowerViewHubDiscoveryParticipant { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV1.class); + + @Override + public String getServiceType() { + return "_powerview._tcp.local."; + } + + @Override + public @Nullable DiscoveryResult createResult(ServiceInfo service) { + for (String host : service.getHostAddresses()) { + if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { + ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); + DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) + .withProperty(HDPowerViewHubConfiguration.HOST, host) + .withProperty(HDPowerViewHubConfiguration.GENERATION, 1) + .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) + .withLabel("PowerView Hub (" + host + ")").build(); + logger.debug("mDNS discovered generation 1/2 hub on host '{}'", host); + return hub; + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java new file mode 100644 index 0000000000000..be0b5a7577004 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.discovery._v3; + +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; + +import javax.jmdns.ServiceInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; +import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewHubDiscoveryParticipant; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Discovers HD PowerView generation 3 hubs by means of mDNS + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +@Component +public class HDPowerViewHubDiscoveryParticipantV3 extends HDPowerViewHubDiscoveryParticipant { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV3.class); + + @Override + public String getServiceType() { + return "_powerview-g3._tcp.local."; + } + + @Override + public @Nullable DiscoveryResult createResult(ServiceInfo service) { + for (String host : service.getHostAddresses()) { + if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { + ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); + DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) + .withProperty(HDPowerViewHubConfiguration.HOST, host) + .withProperty(HDPowerViewHubConfiguration.GENERATION, 3) + .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) + .withLabel("PowerView Hub (" + host + ")").build(); + logger.debug("mDNS discovered generation 3 hub on host '{}'", host); + return hub; + } + } + return null; + } +} From 0a8ecce013c3ea514d0afcd5c57151b0148eb0d9 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 10 Sep 2022 13:26:50 +0100 Subject: [PATCH 32/64] [hdpowerview] spotless:apply Signed-off-by: Andrew Fiddian-Green --- .../src/main/resources/OH-INF/thing/bridge.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index 9458b79274956..b752d79e370a7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -27,11 +27,11 @@ The Host address of the PowerView Hub network-address - - - The PowerView Hub Generation - 0 - + + + The PowerView Hub Generation + 0 + The number of milliseconds between fetches of the PowerView Hub shade state From 9eedcb8f6c53733579937bd3b8b5dab12e6f97de Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Thu, 15 Sep 2022 11:41:27 +0100 Subject: [PATCH 33/64] [hdpowerview] return empty repeaters object rather than throw exception Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java index e43fc66e7ad2f..8849e3c608b6d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java @@ -228,7 +228,7 @@ public void enableScheduledEvent(int scheduledEventId, boolean enable) @Override public Repeaters getRepeaters() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getRepeaters(): method not implemented"); + return new Repeaters(); } @Override From aafa8aa3fb44415bf7e316b26b99f98c390b1bc6 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Thu, 20 Oct 2022 13:59:50 +0100 Subject: [PATCH 34/64] [hdpowerview] use osgi ClientBuilder, SseEventSourceFactory instances Signed-off-by: Andrew Fiddian-Green --- .../internal/HDPowerViewHandlerFactory.java | 11 +++++++- .../internal/HDPowerViewWebTargets.java | 10 +++++++- .../internal/_v1/HDPowerViewWebTargetsV1.java | 9 +++++-- .../internal/_v3/HDPowerViewWebTargetsV3.java | 25 ++++++++++--------- .../handler/HDPowerViewHubHandler.java | 14 ++++++++--- .../hdpowerview/Generation3DtoTest.java | 7 +++++- .../hdpowerview/HDPowerViewJUnitTests.java | 7 +++++- .../hdpowerview/OnlineCommunicationTest.java | 7 +++++- 8 files changed, 68 insertions(+), 22 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index af8c201b99ff4..1fcaa336717ec 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -14,6 +14,8 @@ import java.util.Hashtable; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -35,6 +37,7 @@ import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; /** * The {@link HDPowerViewHandlerFactory} is responsible for creating things and thing @@ -48,15 +51,20 @@ public class HDPowerViewHandlerFactory extends BaseThingHandlerFactory { private final HttpClient httpClient; private final HDPowerViewTranslationProvider translationProvider; + private final ClientBuilder clientBuilder; + private final SseEventSourceFactory eventSourceFactory; @Activate public HDPowerViewHandlerFactory(@Reference HttpClientFactory httpClientFactory, final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider, + final @Reference ClientBuilder clientBuilder, final @Reference SseEventSourceFactory eventSourceFactory, ComponentContext componentContext) { super.activate(componentContext); this.httpClient = httpClientFactory.getCommonHttpClient(); this.translationProvider = new HDPowerViewTranslationProvider(getBundleContext().getBundle(), i18nProvider, localeProvider); + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; } @Override @@ -69,7 +77,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { - HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider); + HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider, + clientBuilder, eventSourceFactory); registerService(new HDPowerViewDeviceDiscoveryService(handler)); return handler; } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE.equals(thingTypeUID)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 94480db4d8d7d..ccad2af04eca8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -19,6 +19,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -46,6 +48,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -88,6 +91,8 @@ public abstract class HDPowerViewWebTargets { protected final Gson gson; protected final HttpClient httpClient; + protected final ClientBuilder clientBuilder; + protected final SseEventSourceFactory eventSourceFactory; /* * Private helper class for de-serialization of Scene @@ -183,10 +188,13 @@ public String toString() { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress, Class sceneClass, + public HDPowerViewWebTargets(HttpClient httpClient, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory, String ipAddress, Class sceneClass, Class shadeDataClass, Class shadePositionClass, Class scheduledEventClass) { this.httpClient = httpClient; + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; this.sceneClass = sceneClass; this.shadeDataClass = shadeDataClass; this.shadePositionClass = shadePositionClass; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java index 1eff3b0b31b32..5a47ce2b46b0a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java @@ -14,6 +14,8 @@ import java.util.List; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpMethod; @@ -52,6 +54,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -84,8 +87,10 @@ public class HDPowerViewWebTargetsV1 extends HDPowerViewWebTargets { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargetsV1(HttpClient httpClient, String ipAddress) { - super(httpClient, ipAddress, SceneV1.class, ShadeDataV1.class, ShadePositionV1.class, ScheduledEventV1.class); + public HDPowerViewWebTargetsV1(HttpClient httpClient, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory, String ipAddress) { + super(httpClient, clientBuilder, eventSourceFactory, ipAddress, SceneV1.class, ShadeDataV1.class, + ShadePositionV1.class, ScheduledEventV1.class); // initialize the urls String base = "http://" + ipAddress + "/api/"; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java index 8849e3c608b6d..c803f4e2c25cc 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java @@ -16,8 +16,9 @@ import java.util.ArrayList; import java.util.List; -import javax.ws.rs.client.Client; +import javax.net.ssl.SSLContext; import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; import javax.ws.rs.sse.InboundSseEvent; import javax.ws.rs.sse.SseEventSource; @@ -54,6 +55,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -109,7 +111,6 @@ private static class GatewayRegistration { private boolean isRegistered; private @Nullable SseSinkV3 sseSink; - private final Client sseClient = ClientBuilder.newClient(); private @Nullable SseEventSource shadeEventSource; private @Nullable SseEventSource sceneEventSource; @@ -119,8 +120,10 @@ private static class GatewayRegistration { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargetsV3(HttpClient httpClient, String ipAddress) { - super(httpClient, ipAddress, SceneV3.class, ShadeDataV3.class, ShadePositionV3.class, ScheduledEventV3.class); + public HDPowerViewWebTargetsV3(HttpClient httpClient, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory, String ipAddress) { + super(httpClient, clientBuilder, eventSourceFactory, ipAddress, SceneV3.class, ShadeDataV3.class, + ShadePositionV3.class, ScheduledEventV3.class); // initialize the urls String base = "http://" + ipAddress + "/"; @@ -314,24 +317,22 @@ public boolean sseSubscribe(@Nullable SseSinkV3 sseSink) throws HubMaintenanceEx // open SSE channel for shades SseEventSource shadeEventSource = this.shadeEventSource; if (shadeEventSource == null || !shadeEventSource.isOpen()) { - source = SseEventSource.target(sseClient.target(shadeEvents)).build(); + SSLContext context = httpClient.getSslContextFactory().getSslContext(); + WebTarget target = clientBuilder.sslContext(context).build().target(shadeEvents); + source = eventSourceFactory.newSource(target); source.register((event) -> onShadeEvent(event)); source.open(); - if (!source.isOpen()) { - throw new HubProcessingException("setEventSink(): failed to open SSE channel for shades"); - } this.shadeEventSource = source; } // open SSE channel for scenes SseEventSource sceneEventSource = this.sceneEventSource; if (sceneEventSource == null || !sceneEventSource.isOpen()) { - source = SseEventSource.target(sseClient.target(sceneEvents)).build(); + SSLContext context = httpClient.getSslContextFactory().getSslContext(); + WebTarget target = clientBuilder.sslContext(context).build().target(sceneEvents); + source = eventSourceFactory.newSource(target); source.register((event) -> onSceneEvent(event)); source.open(); - if (!source.isOpen()) { - throw new HubProcessingException("setEventSink(): failed to open SSE channel for scenes"); - } this.sceneEventSource = source; } return true; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index 862bf0441b6b4..db0e2f403e286 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -25,6 +25,8 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -74,6 +76,7 @@ import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,6 +96,8 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV private final HDPowerViewTranslationProvider translationProvider; private final ConcurrentHashMap pendingShadeInitializations = new ConcurrentHashMap<>(); private final Duration firmwareVersionValidityPeriod = Duration.ofDays(1); + private final ClientBuilder clientBuilder; + private final SseEventSourceFactory eventSourceFactory; private long refreshInterval; private long hardRefreshPositionInterval; @@ -119,10 +124,13 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED); public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient, - HDPowerViewTranslationProvider translationProvider) { + HDPowerViewTranslationProvider translationProvider, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory) { super(bridge); this.httpClient = httpClient; this.translationProvider = translationProvider; + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; } @Override @@ -717,10 +725,10 @@ private HDPowerViewWebTargets newWebTargets(String host) throws InstantiationExc case 0: // for non breaking of existing installations case 1: // both generation 1 and 2 hubs use V1 web targets case 2: - webTargets = new HDPowerViewWebTargetsV1(httpClient, host); + webTargets = new HDPowerViewWebTargetsV1(httpClient, clientBuilder, eventSourceFactory, host); break; case 3: // generation 3 hubs use V3 web targets - webTargets = new HDPowerViewWebTargetsV3(httpClient, host); + webTargets = new HDPowerViewWebTargetsV3(httpClient, clientBuilder, eventSourceFactory, host); } if (webTargets != null) { this.webTargets = webTargets; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java index f32f5ca731ecb..b7474b6c251b7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java @@ -21,9 +21,12 @@ import java.nio.charset.StandardCharsets; import java.util.List; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; @@ -39,6 +42,7 @@ import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.UnDefType; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; /** * Unit tests for Generation 3 DTO's. @@ -48,7 +52,8 @@ @NonNullByDefault public class Generation3DtoTest { - private final HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV3(new HttpClient(), ""); + private final HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV3(new HttpClient(), + Mockito.mock(ClientBuilder.class), Mockito.mock(SseEventSourceFactory.class), ""); private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); private String loadJson(String filename) throws IOException { diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java index 737c932c46813..e59f341cdefd1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java @@ -21,9 +21,12 @@ import java.util.List; import java.util.Objects; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; import org.openhab.binding.hdpowerview.internal.api.BatteryKind; import org.openhab.binding.hdpowerview.internal.api.ShadeData; @@ -38,6 +41,7 @@ import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; import com.google.gson.Gson; @@ -50,7 +54,8 @@ @NonNullByDefault public class HDPowerViewJUnitTests { - private final Gson gson = new HDPowerViewWebTargetsV1(new HttpClient(), "").getGson(); + private final Gson gson = new HDPowerViewWebTargetsV1(new HttpClient(), Mockito.mock(ClientBuilder.class), + Mockito.mock(SseEventSourceFactory.class), "").getGson(); private T getObjectFromJson(String filename, Class clazz) throws IOException { try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java index cdb3660f01ee1..588fcfa9ef23f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java @@ -18,9 +18,12 @@ import java.util.List; import java.util.regex.Pattern; +import javax.ws.rs.client.ClientBuilder; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; import org.openhab.binding.hdpowerview.internal.api.ShadeData; @@ -36,6 +39,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; /** * Unit tests for HD PowerView binding. @@ -85,7 +89,8 @@ public void testOnlineCommunication() { fail(e.getMessage()); } - HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV1(client, hubIPAddress); + HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV1(client, Mockito.mock(ClientBuilder.class), + Mockito.mock(SseEventSourceFactory.class), hubIPAddress); assertNotNull(webTargets); int shadeId = 0; From a3566b462c047a6bf0b91a5864cd6a85ee9bb8b0 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 26 Oct 2022 19:46:47 +0100 Subject: [PATCH 35/64] [hdpowerview] adopt reviewer request Signed-off-by: Andrew Fiddian-Green --- .../internal/HDPowerViewBindingConstants.java | 8 + .../internal/HDPowerViewHandlerFactory.java | 17 +- .../internal/HDPowerViewWebTargets.java | 626 ++++++++++------- .../internal/_v1/HDPowerViewWebTargetsV1.java | 629 ------------------ .../internal/_v3/HDPowerViewWebTargetsV3.java | 552 --------------- .../hdpowerview/internal/_v3/SseSinkV3.java | 42 -- .../hdpowerview/internal/api/ShadeData.java | 46 -- .../internal/api/ShadePosition.java | 338 +++++++++- .../internal/api/_v1/ShadeDataV1.java | 47 -- .../internal/api/_v1/ShadePositionV1.java | 387 ----------- .../internal/api/_v3/ShadeDataV3.java | 62 -- .../internal/api/requests/ShadePositions.java | 2 +- .../internal/api/responses/Scene.java | 40 -- .../internal/api/responses/Scenes.java | 56 ++ .../api/responses/ScheduledEvent.java | 40 -- .../api/responses/ScheduledEvents.java | 91 +++ .../internal/api/responses/Shade.java | 2 +- .../internal/api/responses/Shades.java | 41 +- .../internal/api/responses/_v1/SceneV1.java | 73 -- .../api/responses/_v1/ScheduledEventV1.java | 116 ---- .../internal/api/responses/_v3/InfoV3.java | 65 -- .../internal/api/responses/_v3/SceneV3.java | 85 --- .../builders/AutomationChannelBuilder.java | 27 +- .../builders/SceneChannelBuilder.java | 8 +- .../config/HDPowerViewHubConfiguration.java | 3 - .../HDPowerViewDeviceDiscoveryService.java | 2 +- .../HDPowerViewHubDiscoveryParticipant.java | 33 +- .../HDPowerViewHubDiscoveryParticipantV1.java | 62 -- .../HDPowerViewDeviceDiscoveryServiceV3.java | 112 ++++ .../HDPowerViewHubDiscoveryParticipantV3.java | 21 +- .../SseShadeV3.java => gen3/dto/Info3.java} | 22 +- .../hdpowerview/internal/gen3/dto/Scene3.java | 59 ++ .../dto/SceneEvent.java} | 23 +- .../dto/ScheduledEvent3.java} | 39 +- .../hdpowerview/internal/gen3/dto/Shade3.java | 158 +++++ .../internal/gen3/dto/ShadeEvent.java | 38 ++ .../dto/ShadePosition3.java} | 62 +- .../gen3/handler/HDPowerViewHubHandler3.java | 284 ++++++++ .../handler/HDPowerViewShadeHandler3.java | 285 ++++++++ .../webtargets/HDPowerViewWebTargets3.java | 445 +++++++++++++ .../handler/HDPowerViewHubHandler.java | 132 +--- .../handler/HDPowerViewShadeHandler.java | 41 +- .../OH-INF/i18n/hdpowerview.properties | 12 +- .../main/resources/OH-INF/thing/bridge.xml | 34 +- .../src/main/resources/OH-INF/thing/shade.xml | 30 + .../AutomationChannelBuilderTest.java | 36 +- .../hdpowerview/Generation3DtoTest.java | 196 ------ .../hdpowerview/HDPowerViewJUnitTests.java | 23 +- .../hdpowerview/OnlineCommunicationTest.java | 17 +- .../hdpowerview/SceneChannelBuilderTest.java | 5 +- .../hdpowerview/ShadePositionTest.java | 53 +- .../hdpowerview/gen3/Generation3DtoTest.java | 153 +++++ .../binding/hdpowerview/{_v1 => }/duette.json | 0 .../{_v3 => gen3}/automations.json | 0 .../{_v3 => gen3}/scene-event.json | 0 .../hdpowerview/{_v3 => gen3}/scenes.json | 0 .../{_v3 => gen3}/shade-event.json | 0 .../hdpowerview/{_v3 => gen3}/shades.json | 0 .../{_v1 => }/sceneCollections.json | 0 .../binding/hdpowerview/{_v1 => }/scenes.json | 0 .../binding/hdpowerview/{_v1 => }/shades.json | 0 61 files changed, 2732 insertions(+), 3048 deletions(-) delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{discovery/_v3 => gen3/discovery}/HDPowerViewHubDiscoveryParticipantV3.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api/responses/_v3/SseShadeV3.java => gen3/dto/Info3.java} (54%) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api/responses/_v3/SseSceneV3.java => gen3/dto/SceneEvent.java} (60%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api/responses/_v3/ScheduledEventV3.java => gen3/dto/ScheduledEvent3.java} (79%) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api/_v3/ShadePositionV3.java => gen3/dto/ShadePosition3.java} (56%) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v1 => }/duette.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/automations.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/scene-event.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/shade-event.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v3 => gen3}/shades.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v1 => }/sceneCollections.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v1 => }/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{_v1 => }/shades.json (100%) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java index e680de5d7f452..610e0d82d025f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java @@ -76,4 +76,12 @@ public class HDPowerViewBindingConstants { public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_HUB, THING_TYPE_SHADE, THING_TYPE_REPEATER); + + // generation 3 + public static final ThingTypeUID THING_TYPE_HUB_GEN3 = new ThingTypeUID(BINDING_ID, "gen3"); + public static final ThingTypeUID THING_TYPE_SHADE_GEN3 = new ThingTypeUID(BINDING_ID, "shade3"); + + public static final String PROPERTY_NAME = "name"; + public static final String PROPERTY_POWER_TYPE = "powerType"; + public static final String PROPERTY_BLE_NAME = "bleName"; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 1fcaa336717ec..c59011021297c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -20,6 +20,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewDeviceDiscoveryService; +import org.openhab.binding.hdpowerview.internal.gen3.discovery.HDPowerViewDeviceDiscoveryServiceV3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewShadeHandler3; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewRepeaterHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler; @@ -75,10 +78,18 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - - if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { - HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider, + // generation 3 + if (HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3.equals(thingTypeUID)) { + HDPowerViewHubHandler3 handler = new HDPowerViewHubHandler3((Bridge) thing, httpClient, translationProvider, clientBuilder, eventSourceFactory); + registerService(new HDPowerViewDeviceDiscoveryServiceV3(handler)); + return handler; + } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3.equals(thingTypeUID)) { + return new HDPowerViewShadeHandler3(thing); + } else + // generation 1/2 + if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { + HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider); registerService(new HDPowerViewDeviceDiscoveryService(handler)); return handler; } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE.equals(thingTypeUID)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index ccad2af04eca8..562d37bc504c7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -12,15 +12,12 @@ */ package org.openhab.binding.hdpowerview.internal; -import java.lang.reflect.Type; import java.time.Duration; import java.time.Instant; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; @@ -32,43 +29,52 @@ import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.api.Color; import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.SurveyData; import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; +import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; +import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; +import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Shade; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.responses.Survey; +import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; +import com.google.gson.JsonParser; /** - * Abstract class for JAX-RS targets for communicating with an HD PowerView hub. + * JAX-RS targets for communicating with an HD PowerView hub * - * @author Andrew Fiddian-Green - Initial contribution + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Added support for secondary rail positions + * @author Jacob Laursen - Added support for scene groups and automations */ @NonNullByDefault -public abstract class HDPowerViewWebTargets { +public class HDPowerViewWebTargets { private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets.class); @@ -81,81 +87,24 @@ public abstract class HDPowerViewWebTargets { private final Duration maintenancePeriod = Duration.ofMinutes(5); private Instant maintenanceScheduledEnd = Instant.MIN; - /* - * De-serializer target class types - */ - private final Class sceneClass; - private final Class shadeDataClass; - private final Class shadePositionClass; - private final Class scheduledEventClass; - - protected final Gson gson; - protected final HttpClient httpClient; - protected final ClientBuilder clientBuilder; - protected final SseEventSourceFactory eventSourceFactory; - - /* - * Private helper class for de-serialization of Scene - */ - private class SceneDeserializer implements JsonDeserializer { - @Override - public @Nullable Scene deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - JsonObject jsonObject = json.getAsJsonObject(); - return context.deserialize(jsonObject, sceneClass); - } - } - - /* - * Private helper class for de-serialization of ShadeData - */ - private class ShadeDataDeserializer implements JsonDeserializer { - @Override - public @Nullable ShadeData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - JsonObject jsonObject = json.getAsJsonObject(); - return context.deserialize(jsonObject, shadeDataClass); - } - } - - /* - * Private helper class for de-serialization of ShadePosition - */ - private class ShadePositionDeserializer implements JsonDeserializer { - @Override - public @Nullable ShadePosition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - JsonObject jsonObject = json.getAsJsonObject(); - return context.deserialize(jsonObject, shadePositionClass); - } - } + private final String base; + private final String firmwareVersion; + private final String shades; + private final String sceneActivate; + private final String scenes; + private final String sceneCollectionActivate; + private final String sceneCollections; + private final String scheduledEvents; + private final String repeaters; + private final String userData; - /* - * Private helper class for serialization of ShadePosition - */ - private class ShadePositionSerializer implements JsonSerializer { - @Override - public JsonElement serialize(ShadePosition src, Type typeOfSrc, JsonSerializationContext context) { - return context.serialize(src, src.getClass()); - } - }; - - /* - * Private helper class for de-serialization of ScheduledEvent - */ - private class ScheduledEventDeserializer implements JsonDeserializer { - @Override - public @Nullable ScheduledEvent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - JsonObject jsonObject = json.getAsJsonObject(); - return context.deserialize(jsonObject, scheduledEventClass); - } - } + private final Gson gson = new Gson(); + private final HttpClient httpClient; /** - * Protected helper class for passing http url query parameters + * private helper class for passing http url query parameters */ - protected static class Query { + public static class Query { private final String key; private final String value; @@ -188,101 +137,73 @@ public String toString() { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets(HttpClient httpClient, ClientBuilder clientBuilder, - SseEventSourceFactory eventSourceFactory, String ipAddress, Class sceneClass, - Class shadeDataClass, Class shadePositionClass, - Class scheduledEventClass) { + public HDPowerViewWebTargets(HttpClient httpClient, String ipAddress) { + base = "http://" + ipAddress + "/api/"; + shades = base + "shades/"; + firmwareVersion = base + "fwversion"; + sceneActivate = base + "scenes"; + scenes = base + "scenes/"; + + // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections". + sceneCollectionActivate = base + "scenecollections"; + sceneCollections = base + "scenecollections/"; + + scheduledEvents = base + "scheduledevents"; + repeaters = base + "repeaters/"; + userData = base + "userdata"; + this.httpClient = httpClient; - this.clientBuilder = clientBuilder; - this.eventSourceFactory = eventSourceFactory; - this.sceneClass = sceneClass; - this.shadeDataClass = shadeDataClass; - this.shadePositionClass = shadePositionClass; - this.scheduledEventClass = scheduledEventClass; - this.gson = new GsonBuilder() - // @formatter:off - .registerTypeAdapter(Scene.class, new SceneDeserializer()) - .registerTypeAdapter(ShadeData.class, new ShadeDataDeserializer()) - .registerTypeAdapter(ShadePosition.class, new ShadePositionDeserializer()) - .registerTypeAdapter(ShadePosition.class, new ShadePositionSerializer()) - .registerTypeAdapter(ScheduledEvent.class, new ScheduledEventDeserializer()) - // @formatter:on - .create(); } /** - * Get the gson de-serializer. + * Fetches a JSON package with firmware information for the hub. * - * @return the gson de-serializer. + * @return FirmwareVersions class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance */ - public Gson getGson() { - return gson; + public HubFirmware getFirmwareVersions() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, firmwareVersion, null, null); + try { + FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class); + if (firmwareVersion == null) { + throw new HubInvalidResponseException("Missing firmware response"); + } + HubFirmware firmwareVersions = firmwareVersion.firmware; + if (firmwareVersions == null) { + throw new HubInvalidResponseException("Missing 'firmware' element"); + } + return firmwareVersions; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing firmware response", e); + } } /** - * Common protected method to invoke a call on the hub server to retrieve information or send a command + * Fetches a JSON package with user data information for the hub. * - * @param method GET or PUT - * @param url the host url to be called - * @param query the http query parameter - * @param jsonCommand the request command content (as a json string) - * @return the response content (as a json string) - * @throws HubMaintenanceException - * @throws HubProcessingException + * @return {@link UserData} class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance */ - protected synchronized String invoke(HttpMethod method, String url, @Nullable Query query, - @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { - if (logger.isTraceEnabled()) { - if (query != null) { - logger.trace("API command {} {}{}", method, url, query); - } else { - logger.trace("API command {} {}", method, url); - } - if (jsonCommand != null) { - logger.trace("JSON command = {}", jsonCommand); - } - } - Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); - if (query != null) { - request.param(query.getKey(), query.getValue()); - } - if (jsonCommand != null) { - request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); - } - ContentResponse response; + public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, userData, null, null); try { - response = request.send(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); - } catch (TimeoutException | ExecutionException e) { - if (Instant.now().isBefore(maintenanceScheduledEnd)) { - // throw "softer" exception during maintenance window - logger.debug("Hub still undergoing maintenance"); - throw new HubMaintenanceException("Hub still undergoing maintenance"); + UserDataResponse userDataResponse = gson.fromJson(json, UserDataResponse.class); + if (userDataResponse == null) { + throw new HubInvalidResponseException("Missing userData response"); } - throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); - } - int statusCode = response.getStatus(); - if (statusCode == HttpStatus.LOCKED_423) { - // set end of maintenance window, and throw a "softer" exception - maintenanceScheduledEnd = Instant.now().plus(maintenancePeriod); - logger.debug("Hub undergoing maintenance"); - throw new HubMaintenanceException("Hub undergoing maintenance"); - } - if (statusCode != HttpStatus.OK_200) { - logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); - throw new HubProcessingException(String.format("HTTP %d error", statusCode)); - } - String jsonResponse = response.getContentAsString(); - if (logger.isTraceEnabled()) { - logger.trace("JSON response = {}", jsonResponse); - } - if (jsonResponse == null || jsonResponse.isEmpty()) { - logger.warn("Hub returned no content"); - throw new HubProcessingException("Missing response entity"); + UserData userData = userDataResponse.userData; + if (userData == null) { + throw new HubInvalidResponseException("Missing 'userData' element"); + } + return userData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing userData response", e); } - return jsonResponse; } /** @@ -294,42 +215,22 @@ protected synchronized String invoke(HttpMethod method, String url, @Nullable Qu * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract Shades getShades() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; - - /** - * Fetches a JSON package that describes all scenes in the hub, and wraps it in - * a Scenes class instance - * - * @return Scenes class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - public abstract Scenes getScenes() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; - - /** - * Fetches a JSON package with firmware information for the hub. - * - * @return FirmwareVersions class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - public abstract HubFirmware getFirmwareVersions() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; - - /** - * Fetches a JSON package with user data information for the hub. - * - * @return {@link UserData} class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - public abstract UserData getUserData() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, shades, null, null); + try { + Shades shades = gson.fromJson(json, Shades.class); + if (shades == null) { + throw new HubInvalidResponseException("Missing shades response"); + } + List shadeData = shades.shadeData; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); + } + return shades; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shades response", e); + } + } /** * Instructs the hub to move a specific shade @@ -342,8 +243,31 @@ public abstract UserData getUserData() * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; + public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, + HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeMove(position)); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + private ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException { + try { + Shade shade = gson.fromJson(json, Shade.class); + if (shade == null) { + throw new HubInvalidResponseException("Missing shade response"); + } + ShadeData shadeData = shade.shade; + if (shadeData == null) { + throw new HubInvalidResponseException("Missing 'shade.shade' element"); + } + if (Boolean.TRUE.equals(shadeData.timedOut)) { + throw new HubShadeTimeoutException("Timeout when sending request to the shade"); + } + return shadeData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing shade response", e); + } + } /** * Instructs the hub to stop movement of a specific shade @@ -355,8 +279,12 @@ public abstract ShadeData moveShade(int shadeId, ShadePosition position) throws * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException; + public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeStop()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } /** * Instructs the hub to jog a specific shade @@ -368,8 +296,12 @@ public abstract ShadeData stopShade(int shadeId) throws HubInvalidResponseExcept * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException; + public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeJog()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } /** * Instructs the hub to calibrate a specific shade @@ -381,8 +313,38 @@ public abstract ShadeData jogShade(int shadeId) throws HubInvalidResponseExcepti * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException; + public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonRequest = gson.toJson(new ShadeCalibrate()); + String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); + return shadeDataFromJson(jsonResponse); + } + + /** + * Fetches a JSON package that describes all scenes in the hub, and wraps it in + * a Scenes class instance + * + * @return Scenes class instance + * @throws HubInvalidResponseException if response is invalid + * @throws HubProcessingException if there is any processing error + * @throws HubMaintenanceException if the hub is down for maintenance + */ + public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scenes, null, null); + try { + Scenes scenes = gson.fromJson(json, Scenes.class); + if (scenes == null) { + throw new HubInvalidResponseException("Missing scenes response"); + } + List sceneData = scenes.sceneData; + if (sceneData == null) { + throw new HubInvalidResponseException("Missing 'scenes.sceneData' element"); + } + return scenes; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scenes response", e); + } + } /** * Instructs the hub to execute a specific scene @@ -391,7 +353,9 @@ public abstract ShadeData calibrateShade(int shadeId) throws HubInvalidResponseE * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException; + public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); + } /** * Fetches a JSON package that describes all scene collections in the hub, and wraps it in @@ -402,8 +366,23 @@ public abstract ShadeData calibrateShade(int shadeId) throws HubInvalidResponseE * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract SceneCollections getSceneCollections() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public SceneCollections getSceneCollections() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, sceneCollections, null, null); + try { + SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class); + if (sceneCollections == null) { + throw new HubInvalidResponseException("Missing sceneCollections response"); + } + List sceneCollectionData = sceneCollections.sceneCollectionData; + if (sceneCollectionData == null) { + throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element"); + } + return sceneCollections; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing sceneCollections response", e); + } + } /** * Instructs the hub to execute a specific scene collection @@ -412,8 +391,10 @@ public abstract SceneCollections getSceneCollections() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract void activateSceneCollection(int sceneCollectionId) - throws HubProcessingException, HubMaintenanceException; + public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { + invoke(HttpMethod.GET, sceneCollectionActivate, + Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null); + } /** * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in @@ -424,8 +405,23 @@ public abstract void activateSceneCollection(int sceneCollectionId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract ScheduledEvents getScheduledEvents() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public ScheduledEvents getScheduledEvents() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, scheduledEvents, null, null); + try { + ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class); + if (scheduledEvents == null) { + throw new HubInvalidResponseException("Missing scheduledEvents response"); + } + List scheduledEventData = scheduledEvents.scheduledEventData; + if (scheduledEventData == null) { + throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element"); + } + return scheduledEvents; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing scheduledEvents response", e); + } + } /** * Enables or disables a scheduled event in the hub. @@ -436,8 +432,23 @@ public abstract ScheduledEvents getScheduledEvents() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract void enableScheduledEvent(int scheduledEventId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public void enableScheduledEvent(int scheduledEventId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String uri = scheduledEvents + "/" + scheduledEventId; + String jsonResponse = invoke(HttpMethod.GET, uri, null, null); + try { + JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject(); + JsonElement scheduledEventElement = jsonObject.get("scheduledEvent"); + if (scheduledEventElement == null) { + throw new HubInvalidResponseException("Missing 'scheduledEvent' element"); + } + JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject(); + scheduledEventObject.addProperty("enabled", enable); + invoke(HttpMethod.PUT, uri, null, jsonObject.toString()); + } catch (JsonParseException | IllegalStateException e) { + throw new HubInvalidResponseException("Error parsing scheduledEvent response", e); + } + } /** * Fetches a JSON package that describes all repeaters in the hub, and wraps it in @@ -448,8 +459,23 @@ public abstract void enableScheduledEvent(int scheduledEventId, boolean enable) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract Repeaters getRepeaters() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public Repeaters getRepeaters() + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String json = invoke(HttpMethod.GET, repeaters, null, null); + try { + Repeaters repeaters = gson.fromJson(json, Repeaters.class); + if (repeaters == null) { + throw new HubInvalidResponseException("Missing repeaters response"); + } + List repeaterData = repeaters.repeaterData; + if (repeaterData == null) { + throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element"); + } + return repeaters; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing repeaters response", e); + } + } /** * Fetches a JSON package that describes a specific repeater in the hub, and wraps it @@ -461,8 +487,27 @@ public abstract Repeaters getRepeaters() * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract RepeaterData getRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public RepeaterData getRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null); + return repeaterDataFromJson(jsonResponse); + } + + private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException { + try { + Repeater repeater = gson.fromJson(json, Repeater.class); + if (repeater == null) { + throw new HubInvalidResponseException("Missing repeater response"); + } + RepeaterData repeaterData = repeater.repeater; + if (repeaterData == null) { + throw new HubInvalidResponseException("Missing 'repeater.repeater' element"); + } + return repeaterData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing repeater response", e); + } + } /** * Instructs the hub to identify a specific repeater by blinking @@ -473,8 +518,12 @@ public abstract RepeaterData getRepeater(int repeaterId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract RepeaterData identifyRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public RepeaterData identifyRepeater(int repeaterId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId, + Query.of("identify", Boolean.toString(true)), null); + return repeaterDataFromJson(jsonResponse); + } /** * Enables or disables blinking for a repeater @@ -486,8 +535,12 @@ public abstract RepeaterData identifyRepeater(int repeaterId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable)); + String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); + return repeaterDataFromJson(jsonResponse); + } /** * Sets color and brightness for a repeater @@ -498,8 +551,78 @@ public abstract RepeaterData enableRepeaterBlinking(int repeaterId, boolean enab * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract RepeaterData setRepeaterColor(int repeaterId, Color color) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public RepeaterData setRepeaterColor(int repeaterId, Color color) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color)); + String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); + return repeaterDataFromJson(jsonResponse); + } + + /** + * Invoke a call on the hub server to retrieve information or send a command + * + * @param method GET or PUT + * @param url the host url to be called + * @param query the http query parameter + * @param jsonCommand the request command content (as a json string) + * @return the response content (as a json string) + * @throws HubMaintenanceException + * @throws HubProcessingException + */ + private synchronized String invoke(HttpMethod method, String url, @Nullable Query query, + @Nullable String jsonCommand) throws HubMaintenanceException, HubProcessingException { + if (logger.isTraceEnabled()) { + if (query != null) { + logger.trace("API command {} {}{}", method, url, query); + } else { + logger.trace("API command {} {}", method, url); + } + if (jsonCommand != null) { + logger.trace("JSON command = {}", jsonCommand); + } + } + Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); + if (query != null) { + request.param(query.getKey(), query.getValue()); + } + if (jsonCommand != null) { + request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); + } + ContentResponse response; + try { + response = request.send(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } catch (TimeoutException | ExecutionException e) { + if (Instant.now().isBefore(maintenanceScheduledEnd)) { + // throw "softer" exception during maintenance window + logger.debug("Hub still undergoing maintenance"); + throw new HubMaintenanceException("Hub still undergoing maintenance"); + } + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } + int statusCode = response.getStatus(); + if (statusCode == HttpStatus.LOCKED_423) { + // set end of maintenance window, and throw a "softer" exception + maintenanceScheduledEnd = Instant.now().plus(maintenancePeriod); + logger.debug("Hub undergoing maintenance"); + throw new HubMaintenanceException("Hub undergoing maintenance"); + } + if (statusCode != HttpStatus.OK_200) { + logger.warn("Hub returned HTTP {} {}", statusCode, response.getReason()); + throw new HubProcessingException(String.format("HTTP %d error", statusCode)); + } + String jsonResponse = response.getContentAsString(); + if (logger.isTraceEnabled()) { + logger.trace("JSON response = {}", jsonResponse); + } + if (jsonResponse == null || jsonResponse.isEmpty()) { + logger.warn("Hub returned no content"); + throw new HubProcessingException("Missing response entity"); + } + return jsonResponse; + } /** * Fetches a JSON package that describes a specific shade in the hub, and wraps it @@ -512,8 +635,11 @@ public abstract RepeaterData setRepeaterColor(int repeaterId, Color color) * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException; + public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); + return shadeDataFromJson(jsonResponse); + } /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -527,8 +653,12 @@ public abstract ShadeData getShade(int shadeId) throws HubInvalidResponseExcepti * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData refreshShadePosition(int shadeId) - throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; + public ShadeData refreshShadePosition(int shadeId) + throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("refresh", Boolean.toString(true)), null); + return shadeDataFromJson(jsonResponse); + } /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -542,8 +672,24 @@ public abstract ShadeData refreshShadePosition(int shadeId) * @throws HubProcessingException if there is any processing error * @throws HubMaintenanceException if the hub is down for maintenance */ - public abstract List getShadeSurvey(int shadeId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException; + public List getShadeSurvey(int shadeId) + throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("survey", Boolean.toString(true)), null); + try { + Survey survey = gson.fromJson(jsonResponse, Survey.class); + if (survey == null) { + throw new HubInvalidResponseException("Missing survey response"); + } + List surveyData = survey.surveyData; + if (surveyData == null) { + throw new HubInvalidResponseException("Missing 'survey.surveyData' element"); + } + return surveyData; + } catch (JsonParseException e) { + throw new HubInvalidResponseException("Error parsing survey response", e); + } + } /** * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on @@ -557,6 +703,10 @@ public abstract List getShadeSurvey(int shadeId) * @throws HubMaintenanceException if the hub is down for maintenance * @throws HubShadeTimeoutException if the shade did not respond to a request */ - public abstract ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException; + public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, + HubMaintenanceException, HubShadeTimeoutException { + String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), + Query.of("updateBatteryLevel", Boolean.toString(true)), null); + return shadeDataFromJson(jsonResponse); + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java deleted file mode 100644 index 5a47ce2b46b0a..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v1/HDPowerViewWebTargetsV1.java +++ /dev/null @@ -1,629 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal._v1; - -import java.util.List; - -import javax.ws.rs.client.ClientBuilder; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.http.HttpMethod; -import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; -import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.Shade; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Survey; -import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; -import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; - -/** - * JAX-RS targets for communicating with an HD PowerView hub Generation 1/2 - * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Added support for secondary rail positions - * @author Jacob Laursen - Added support for scene groups and automations - */ -@NonNullByDefault -public class HDPowerViewWebTargetsV1 extends HDPowerViewWebTargets { - - private final String firmwareVersion; - private final String shades; - private final String sceneActivate; - private final String scenes; - private final String sceneCollectionActivate; - private final String sceneCollections; - private final String scheduledEvents; - private final String repeaters; - private final String userData; - - /** - * Initialize the web targets - * - * @param httpClient the HTTP client (the binding) - * @param ipAddress the IP address of the server (the hub) - */ - public HDPowerViewWebTargetsV1(HttpClient httpClient, ClientBuilder clientBuilder, - SseEventSourceFactory eventSourceFactory, String ipAddress) { - super(httpClient, clientBuilder, eventSourceFactory, ipAddress, SceneV1.class, ShadeDataV1.class, - ShadePositionV1.class, ScheduledEventV1.class); - - // initialize the urls - String base = "http://" + ipAddress + "/api/"; - shades = base + "shades/"; - firmwareVersion = base + "fwversion"; - sceneActivate = base + "scenes"; - scenes = base + "scenes/"; - - // Hub v1 only supports "scenecollections". Hub v2 will redirect to "sceneCollections". - sceneCollectionActivate = base + "scenecollections"; - sceneCollections = base + "scenecollections/"; - - scheduledEvents = base + "scheduledevents"; - repeaters = base + "repeaters/"; - userData = base + "userdata"; - } - - /** - * Protected method to create ShadeData instances from a JSON payload. - * - * @param json the json payload - * @return a ShadeData instance - * @throws HubInvalidResponseException in case od missing or invalid response - * @throws HubShadeTimeoutException in case of connection time out (in V1 implementation) - */ - protected ShadeData shadeDataFromJson(String json) throws HubInvalidResponseException, HubShadeTimeoutException { - try { - Shade shade = gson.fromJson(json, Shade.class); - if (shade == null) { - throw new HubInvalidResponseException("Missing shade response"); - } - ShadeData shadeData = shade.shade; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shade.shade' element"); - } - if (shadeData.version() == 1 && Boolean.TRUE.equals(((ShadeDataV1) shadeData).timedOut)) { - throw new HubShadeTimeoutException("Timeout when sending request to the shade"); - } - return shadeData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shade response", e); - } - } - - /** - * Fetches a JSON package with firmware information for the hub. - * - * @return FirmwareVersions class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public HubFirmware getFirmwareVersions() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, firmwareVersion, null, null); - try { - FirmwareVersion firmwareVersion = gson.fromJson(json, FirmwareVersion.class); - if (firmwareVersion == null) { - throw new HubInvalidResponseException("Missing firmware response"); - } - HubFirmware firmwareVersions = firmwareVersion.firmware; - if (firmwareVersions == null) { - throw new HubInvalidResponseException("Missing 'firmware' element"); - } - return firmwareVersions; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing firmware response", e); - } - } - - /** - * Fetches a JSON package with user data information for the hub. - * - * @return {@link UserData} class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, userData, null, null); - try { - UserDataResponse userDataResponse = gson.fromJson(json, UserDataResponse.class); - if (userDataResponse == null) { - throw new HubInvalidResponseException("Missing userData response"); - } - UserData userData = userDataResponse.userData; - if (userData == null) { - throw new HubInvalidResponseException("Missing 'userData' element"); - } - return userData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing userData response", e); - } - } - - /** - * Fetches a JSON package that describes all shades in the hub, and wraps it in - * a Shades class instance - * - * @return Shades class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, shades, null, null); - try { - Shades shades = gson.fromJson(json, Shades.class); - if (shades == null) { - throw new HubInvalidResponseException("Missing shades response"); - } - List shadeData = shades.shadeData; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); - } - return shades; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shades response", e); - } - } - - /** - * Instructs the hub to move a specific shade - * - * @param shadeId id of the shade to be moved - * @param position instance of ShadePosition containing the new position - * @return ShadeData class instance (with new position) - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeMove(position)); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to stop movement of a specific shade - * - * @param shadeId id of the shade to be stopped - * @return ShadeData class instance (new position cannot be relied upon) - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeStop()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to jog a specific shade - * - * @param shadeId id of the shade to be jogged - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeJog()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to calibrate a specific shade - * - * @param shadeId id of the shade to be calibrated - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonRequest = gson.toJson(new ShadeCalibrate()); - String jsonResponse = invoke(HttpMethod.PUT, shades + Integer.toString(shadeId), null, jsonRequest); - return shadeDataFromJson(jsonResponse); - } - - /** - * Fetches a JSON package that describes all scenes in the hub, and wraps it in - * a Scenes class instance - * - * @return Scenes class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, scenes, null, null); - try { - Scenes scenes = gson.fromJson(json, Scenes.class); - if (scenes == null) { - throw new HubInvalidResponseException("Missing scenes response"); - } - List sceneData = scenes.sceneData; - if (sceneData == null) { - throw new HubInvalidResponseException("Missing 'scenes.sceneData' element"); - } - return scenes; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scenes response", e); - } - } - - /** - * Instructs the hub to execute a specific scene - * - * @param sceneId id of the scene to be executed - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.GET, sceneActivate, Query.of("sceneId", Integer.toString(sceneId)), null); - } - - /** - * Fetches a JSON package that describes all scene collections in the hub, and wraps it in - * a SceneCollections class instance - * - * @return SceneCollections class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public SceneCollections getSceneCollections() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, sceneCollections, null, null); - try { - SceneCollections sceneCollections = gson.fromJson(json, SceneCollections.class); - if (sceneCollections == null) { - throw new HubInvalidResponseException("Missing sceneCollections response"); - } - List sceneCollectionData = sceneCollections.sceneCollectionData; - if (sceneCollectionData == null) { - throw new HubInvalidResponseException("Missing 'sceneCollections.sceneCollectionData' element"); - } - return sceneCollections; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing sceneCollections response", e); - } - } - - /** - * Instructs the hub to execute a specific scene collection - * - * @param sceneCollectionId id of the scene collection to be executed - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.GET, sceneCollectionActivate, - Query.of("sceneCollectionId", Integer.toString(sceneCollectionId)), null); - } - - /** - * Fetches a JSON package that describes all scheduled events in the hub, and wraps it in - * a ScheduledEvents class instance - * - * @return ScheduledEvents class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public ScheduledEvents getScheduledEvents() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, scheduledEvents, null, null); - try { - ScheduledEvents scheduledEvents = gson.fromJson(json, ScheduledEvents.class); - if (scheduledEvents == null) { - throw new HubInvalidResponseException("Missing scheduledEvents response"); - } - List scheduledEventData = scheduledEvents.scheduledEventData; - if (scheduledEventData == null) { - throw new HubInvalidResponseException("Missing 'scheduledEvents.scheduledEventData' element"); - } - return scheduledEvents; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scheduledEvents response", e); - } - } - - /** - * Enables or disables a scheduled event in the hub. - * - * @param scheduledEventId id of the scheduled event to be enabled or disabled - * @param enable true to enable scheduled event, false to disable - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public void enableScheduledEvent(int scheduledEventId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String uri = scheduledEvents + "/" + scheduledEventId; - String jsonResponse = invoke(HttpMethod.GET, uri, null, null); - try { - JsonObject jsonObject = JsonParser.parseString(jsonResponse).getAsJsonObject(); - JsonElement scheduledEventElement = jsonObject.get("scheduledEvent"); - if (scheduledEventElement == null) { - throw new HubInvalidResponseException("Missing 'scheduledEvent' element"); - } - JsonObject scheduledEventObject = scheduledEventElement.getAsJsonObject(); - scheduledEventObject.addProperty("enabled", enable); - invoke(HttpMethod.PUT, uri, null, jsonObject.toString()); - } catch (JsonParseException | IllegalStateException e) { - throw new HubInvalidResponseException("Error parsing scheduledEvent response", e); - } - } - - /** - * Fetches a JSON package that describes all repeaters in the hub, and wraps it in - * a Repeaters class instance - * - * @return Repeaters class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public Repeaters getRepeaters() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, repeaters, null, null); - try { - Repeaters repeaters = gson.fromJson(json, Repeaters.class); - if (repeaters == null) { - throw new HubInvalidResponseException("Missing repeaters response"); - } - List repeaterData = repeaters.repeaterData; - if (repeaterData == null) { - throw new HubInvalidResponseException("Missing 'repeaters.repeaterData' element"); - } - return repeaters; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing repeaters response", e); - } - } - - /** - * Fetches a JSON package that describes a specific repeater in the hub, and wraps it - * in a RepeaterData class instance - * - * @param repeaterId id of the repeater to be fetched - * @return RepeaterData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public RepeaterData getRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, repeaters + Integer.toString(repeaterId), null, null); - return repeaterDataFromJson(jsonResponse); - } - - private RepeaterData repeaterDataFromJson(String json) throws HubInvalidResponseException { - try { - Repeater repeater = gson.fromJson(json, Repeater.class); - if (repeater == null) { - throw new HubInvalidResponseException("Missing repeater response"); - } - RepeaterData repeaterData = repeater.repeater; - if (repeaterData == null) { - throw new HubInvalidResponseException("Missing 'repeater.repeater' element"); - } - return repeaterData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing repeater response", e); - } - } - - /** - * Instructs the hub to identify a specific repeater by blinking - * - * @param repeaterId id of the repeater to be identified - * @return RepeaterData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public RepeaterData identifyRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, repeaters + repeaterId, - Query.of("identify", Boolean.toString(true)), null); - return repeaterDataFromJson(jsonResponse); - } - - /** - * Enables or disables blinking for a repeater - * - * @param repeaterId id of the repeater for which to be enable or disable blinking - * @param enable true to enable blinking, false to disable - * @return RepeaterData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonRequest = gson.toJson(new RepeaterBlinking(repeaterId, enable)); - String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); - return repeaterDataFromJson(jsonResponse); - } - - /** - * Sets color and brightness for a repeater - * - * @param repeaterId id of the repeater for which to set color and brightness - * @return RepeaterData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public RepeaterData setRepeaterColor(int repeaterId, Color color) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonRequest = gson.toJson(new RepeaterColor(repeaterId, color)); - String jsonResponse = invoke(HttpMethod.PUT, repeaters + repeaterId, null, jsonRequest); - return repeaterDataFromJson(jsonResponse); - } - - /** - * Fetches a JSON package that describes a specific shade in the hub, and wraps it - * in a Shade class instance - * - * @param shadeId id of the shade to be fetched - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on - * a specific shade's position; fetches a JSON package that describes that shade, - * and wraps it in a Shade class instance - * - * @param shadeId id of the shade to be refreshed - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData refreshShadePosition(int shadeId) - throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("refresh", Boolean.toString(true)), null); - return shadeDataFromJson(jsonResponse); - } - - /** - * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on - * a specific shade's survey data, which will also refresh signal strength; - * fetches a JSON package that describes that survey, and wraps it in a Survey - * class instance - * - * @param shadeId id of the shade to be surveyed - * @return List of SurveyData class instances - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - */ - @Override - public List getShadeSurvey(int shadeId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("survey", Boolean.toString(true)), null); - try { - Survey survey = gson.fromJson(jsonResponse, Survey.class); - if (survey == null) { - throw new HubInvalidResponseException("Missing survey response"); - } - List surveyData = survey.surveyData; - if (surveyData == null) { - throw new HubInvalidResponseException("Missing 'survey.surveyData' element"); - } - return surveyData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing survey response", e); - } - } - - /** - * Instructs the hub to do a hard refresh (discovery on the hubs RF network) on - * a specific shade's battery level; fetches a JSON package that describes that shade, - * and wraps it in a Shade class instance - * - * @param shadeId id of the shade to be refreshed - * @return ShadeData class instance - * @throws HubInvalidResponseException if response is invalid - * @throws HubProcessingException if there is any processing error - * @throws HubMaintenanceException if the hub is down for maintenance - * @throws HubShadeTimeoutException if the shade did not respond to a request - */ - @Override - public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String jsonResponse = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), - Query.of("updateBatteryLevel", Boolean.toString(true)), null); - return shadeDataFromJson(jsonResponse); - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java deleted file mode 100644 index c803f4e2c25cc..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/HDPowerViewWebTargetsV3.java +++ /dev/null @@ -1,552 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal._v3; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; - -import javax.net.ssl.SSLContext; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.sse.InboundSseEvent; -import javax.ws.rs.sse.SseEventSource; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.http.HttpMethod; -import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadePositions; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.Shade; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.InfoV3; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.SceneV3; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.ScheduledEventV3; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.SseSceneV3; -import org.openhab.binding.hdpowerview.internal.api.responses._v3.SseShadeV3; -import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.exceptions.HubShadeTimeoutException; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonParseException; -import com.google.gson.reflect.TypeToken; - -/** - * JAX-RS targets for communicating with an HD PowerView hub Generation 3 - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class HDPowerViewWebTargetsV3 extends HDPowerViewWebTargets { - - private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargetsV3.class); - - private static final String IDS = "ids"; - - /** - * Simple DTO for registering the binding with the hub. - * - * @author AndrewFG - Initial contribution - */ - @SuppressWarnings("unused") - private static class GatewayRegistration { - public String todo = "org.openhab.binding.hdpowerview"; // TODO - } - - private final String shades; - private final String scenes; - private final String sceneActivate; - private final String shadeMotion; - private final String shadeStop; - private final String shadePositions; - private final String firmware; - private final String automations; - private final String register; - private final String shadeEvents; - private final String sceneEvents; - - // @formatter:off - private final Type shadeListType = new TypeToken>() {}.getType(); - // @formatter:on - - // @formatter:off - private final Type sceneListType = new TypeToken>() {}.getType(); - // @formatter:on - - // @formatter:off - private final Type scheduledEventListType = - new TypeToken>() {}.getType(); - // @formatter:on - - private boolean isRegistered; - private @Nullable SseSinkV3 sseSink; - private @Nullable SseEventSource shadeEventSource; - private @Nullable SseEventSource sceneEventSource; - - /** - * Initialize the web targets - * - * @param httpClient the HTTP client (the binding) - * @param ipAddress the IP address of the server (the hub) - */ - public HDPowerViewWebTargetsV3(HttpClient httpClient, ClientBuilder clientBuilder, - SseEventSourceFactory eventSourceFactory, String ipAddress) { - super(httpClient, clientBuilder, eventSourceFactory, ipAddress, SceneV3.class, ShadeDataV3.class, - ShadePositionV3.class, ScheduledEventV3.class); - - // initialize the urls - String base = "http://" + ipAddress + "/"; - - String home = base + "home/"; - shades = home + "shades"; - scenes = home + "scenes"; - sceneActivate = home + "scenes/%d/activate"; - shadeMotion = home + "shades/%d/motion"; - shadeStop = home + "shades/stop"; - shadePositions = home + "shades/positions"; - automations = home + "automations"; - shadeEvents = home + "shades/events"; - sceneEvents = home + "scenes/events"; - - String gateway = base + "gateway/"; - firmware = gateway + "info"; - register = gateway + "TBD"; // TODO - } - - @Override - public HubFirmware getFirmwareVersions() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, firmware, null, null); - return toFirmware(json); - } - - @Override - public UserData getUserData() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getUserData(): method not implemented"); - } - - @Override - public Shades getShades() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, shades, null, null); - return toShades(json); - } - - @Override - public ShadeData moveShade(int shadeId, ShadePosition position) throws HubInvalidResponseException, - HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), - gson.toJson(new ShadePositions(position))); - return getShade(shadeId); - } - - @Override - public ShadeData stopShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - invoke(HttpMethod.PUT, shadeStop, Query.of(IDS, Integer.valueOf(shadeId).toString()), null); - return getShade(shadeId); - } - - @Override - public ShadeData jogShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.JOG)); - invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); - return getShade(shadeId); - } - - @Override - public ShadeData calibrateShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.CALIBRATE)); - invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); - return getShade(shadeId); - } - - @Override - public Scenes getScenes() throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, this.scenes, null, null); - return toScenes(json); - } - - @Override - public void activateScene(int sceneId) throws HubProcessingException, HubMaintenanceException { - invoke(HttpMethod.PUT, String.format(sceneActivate, sceneId), null, null); - } - - @Override - public SceneCollections getSceneCollections() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getSceneCollections(): method not implemented"); - } - - @Override - public void activateSceneCollection(int sceneCollectionId) throws HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("activateSceneCollection(): method not implemented"); - } - - @Override - public ScheduledEvents getScheduledEvents() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - String json = invoke(HttpMethod.GET, automations, null, null); - return toScheduledEvents(json); - } - - @Override - public void enableScheduledEvent(int scheduledEventId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("enableScheduledEvent(): method not implemented"); - } - - @Override - public Repeaters getRepeaters() - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - return new Repeaters(); - } - - @Override - public RepeaterData getRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getRepeater(): method not implemented"); - } - - @Override - public RepeaterData identifyRepeater(int repeaterId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("identifyRepeater(): method not implemented"); - } - - @Override - public RepeaterData enableRepeaterBlinking(int repeaterId, boolean enable) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("enableRepeaterBlinking(): method not implemented"); - } - - @Override - public RepeaterData setRepeaterColor(int repeaterId, Color color) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("setRepeaterColor(): method not implemented"); - } - - @Override - public ShadeData getShade(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); - return toShadeData(json); - } - - @Override - public ShadeData refreshShadePosition(int shadeId) - throws JsonParseException, HubProcessingException, HubMaintenanceException, HubShadeTimeoutException { - return getShade(shadeId); - } - - @Override - public List getShadeSurvey(int shadeId) - throws HubInvalidResponseException, HubProcessingException, HubMaintenanceException { - throw new HubProcessingException("getShadeSurvey(): method not implemented"); - } - - @Override - public ShadeData refreshShadeBatteryLevel(int shadeId) throws HubInvalidResponseException, HubProcessingException, - HubMaintenanceException, HubShadeTimeoutException { - return getShade(shadeId); - } - - /** - * Register the binding with the hub (if not already registered). - * - * @throws HubProcessingException - * @throws HubMaintenanceException - */ - private void gatewayRegister() throws HubMaintenanceException, HubProcessingException { - if (!isRegistered) { - String json = gson.toJson(new GatewayRegistration()); - invoke(HttpMethod.PUT, register, null, json); - isRegistered = true; - } - } - - /** - * Set the sink for SSE events. - * - * @param sseSink a class that implements the SseSinkV3 interface. - * @return true if registered for SSE events. - * @throws HubProcessingException - * @throws HubMaintenanceException - */ - public boolean sseSubscribe(@Nullable SseSinkV3 sseSink) throws HubMaintenanceException, HubProcessingException { - SseEventSource source; - - this.sseSink = sseSink; - - if (sseSink != null) { - // register ourself with the gateway (if necessary) - gatewayRegister(); - - // open SSE channel for shades - SseEventSource shadeEventSource = this.shadeEventSource; - if (shadeEventSource == null || !shadeEventSource.isOpen()) { - SSLContext context = httpClient.getSslContextFactory().getSslContext(); - WebTarget target = clientBuilder.sslContext(context).build().target(shadeEvents); - source = eventSourceFactory.newSource(target); - source.register((event) -> onShadeEvent(event)); - source.open(); - this.shadeEventSource = source; - } - - // open SSE channel for scenes - SseEventSource sceneEventSource = this.sceneEventSource; - if (sceneEventSource == null || !sceneEventSource.isOpen()) { - SSLContext context = httpClient.getSslContextFactory().getSslContext(); - WebTarget target = clientBuilder.sslContext(context).build().target(sceneEvents); - source = eventSourceFactory.newSource(target); - source.register((event) -> onSceneEvent(event)); - source.open(); - this.sceneEventSource = source; - } - return true; - } - - // close SSE channel for shades - source = shadeEventSource; - if (source != null) { - source.close(); - shadeEventSource = null; - } - - // close SSE channel for scenes - source = sceneEventSource; - if (source != null) { - source.close(); - sceneEventSource = null; - } - return false; - } - - /** - * Handle inbound SSE events for a shade. - * - * @param sseEvent the inbound event - */ - private void onShadeEvent(InboundSseEvent sseEvent) { - SseSinkV3 eventSink = this.sseSink; - if (eventSink != null) { - String json = sseEvent.readData(); - logger.trace("onShadeEvent(): {}", json); - try { - ShadeData shadeData = toShadeData2(json); - ShadePosition shadePosition = shadeData.positions; - if (shadePosition != null) { - String evt = toEvt(json); - eventSink.sseShade(evt, shadeData.id, shadePosition); - } - } catch (HubInvalidResponseException e) { - // swallow the exception; don't pass back to caller - } - } - } - - /** - * Handle inbound SSE events for a scene. - * - * @param sseEvent the inbound event - */ - private void onSceneEvent(InboundSseEvent sseEvent) { - SseSinkV3 eventSink = this.sseSink; - if (eventSink != null) { - String json = sseEvent.readData(); - logger.trace("onSceneEvent(): {}", json); - try { - Scene scene = toScene(json); - String evt = toEvt(json); - eventSink.sseScene(evt, scene.id); - } catch (HubInvalidResponseException e) { - // swallow the exception; don't pass back to caller - } - } - } - - /** - * Create ShadeData instance from a JSON payload. - * - * @param json the json payload - * @return a ShadeData instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private ShadeData toShadeData(String json) throws HubInvalidResponseException { - try { - Shade shade = gson.fromJson(json, Shade.class); - if (shade == null) { - throw new HubInvalidResponseException("Missing shade response"); - } - ShadeData shadeData = shade.shade; - if (shadeData == null) { - throw new HubInvalidResponseException("Missing 'shade.shade' element"); - } - return shadeData; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shade response", e); - } - } - - /** - * Create Shades instance from a JSON payload. - * - * @param json the json payload - * @return a Shades instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private Shades toShades(String json) throws HubInvalidResponseException { - try { - Shades shades = new Shades(); - shades.shadeData = gson.fromJson(json, shadeListType); - if (shades.shadeData == null) { - throw new HubInvalidResponseException("Missing 'shades.shadeData' element"); - } - return shades; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing shades response", e); - } - } - - /** - * Create HubFirmware instance from a JSON payload. - * - * @param json the json payload - * @return a HubFirmware instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private HubFirmware toFirmware(String json) throws HubProcessingException { - try { - InfoV3 gatewayInfo = gson.fromJson(json, InfoV3.class); - if (gatewayInfo == null) { - throw new HubProcessingException("getFirmwareVersions(): missing gatewayInfo"); - } - return gatewayInfo.toHubFirmware(); - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing gateway info response", e); - } - } - - /** - * Create Scenes instance from a JSON payload. - * - * @param json the json payload - * @return a Scenes instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private Scenes toScenes(String json) throws HubInvalidResponseException { - try { - Scenes scenes = new Scenes(); - scenes.sceneData = gson.fromJson(json, sceneListType); - return scenes; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing scenes response", e); - } - } - - /** - * Create ScheduledEvents instance from a JSON payload. - * - * @param json the json payload - * @return a ScheduledEvents instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private ScheduledEvents toScheduledEvents(String json) throws HubInvalidResponseException { - try { - ScheduledEvents scheduledEvents = new ScheduledEvents(); - scheduledEvents.scheduledEventData = gson.fromJson(json, scheduledEventListType); - return scheduledEvents; - } catch (JsonParseException e) { - throw new HubInvalidResponseException("Error parsing automation response", e); - } - } - - /** - * Get the 'evt' message from an event JSON payload. - * - * @param json the json payload - * @return the message - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private String toEvt(String json) throws HubInvalidResponseException { - SseShadeV3 sseShade = gson.fromJson(json, SseShadeV3.class); - if (sseShade != null) { - String evt = sseShade.evt; - if (evt != null) { - return evt; - } - } - throw new HubInvalidResponseException("Error parsing event"); - } - - /** - * Create ShadeData instance from a JSON shade event payload. - * - * @param json the json payload - * @return a ShadeData instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private ShadeData toShadeData2(String json) throws HubInvalidResponseException { - SseShadeV3 sseShade = gson.fromJson(json, SseShadeV3.class); - if (sseShade != null) { - ShadePosition shadePosition = sseShade.currentPositions; - if (shadePosition != null) { - ShadeData shadeData = new ShadeDataV3(); - shadeData.id = sseShade.id; - shadeData.positions = sseShade.currentPositions; - return shadeData; - } - } - throw new HubInvalidResponseException("Error parsing shade event"); - } - - /** - * Create Scene instance from a JSON scene event payload. - * - * @param json the json payload - * @return a Scene instance - * @throws HubInvalidResponseException in case of missing or invalid response - */ - private Scene toScene(String json) throws HubInvalidResponseException { - SseSceneV3 sceneEvent = gson.fromJson(json, SseSceneV3.class); - if (sceneEvent != null) { - Scene scene = sceneEvent.scene; - if (scene != null) { - return scene; - } - } - throw new HubInvalidResponseException("Error parsing scene event"); - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java deleted file mode 100644 index bf827dfe17580..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/_v3/SseSinkV3.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal._v3; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; - -/** - * Interface for receiving SSE events from Generation 3 hubs. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public interface SseSinkV3 { - - /** - * Method that is called when a shade changes state. - * - * @param evt contents of json evt element. - * @param shadeId the id of the shade that changed. - * @param shadePosition the shade's new position. - */ - public void sseShade(String evt, int shadeId, ShadePosition shadePosition); - - /** - * Method that is called when a scene changes state. - * - * @param evt contents of json 'evt' element. - * @param sceneId the id of the scene that changed. - */ - public void sseScene(String evt, int sceneId); -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java deleted file mode 100644 index 4d240e7aec367..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadeData.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api; - -import java.util.Base64; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Abstract class for state of a Shade as returned by an HD PowerView hub. - * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Refactored into separate class - */ -@NonNullByDefault -public abstract class ShadeData { - // fields common to Generation 1/2 and 3 hubs - public int id; - public @Nullable String name; - public int roomId; - public int type; - public int batteryStatus; - public @Nullable ShadePosition positions; - public int signalStrength; - public @Nullable Integer capabilities; - public @Nullable Firmware firmware; - - public String getName() { - return new String(Base64.getDecoder().decode(name)); - } - - public abstract BatteryKind getBatteryKind(); - - public abstract int version(); -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java index 08a0ec1a2308d..9b66e798bbcbc 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java @@ -12,17 +12,45 @@ */ package org.openhab.binding.hdpowerview.internal.api; +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Abstract class for position of a Shade as returned by an HD PowerView hub. + * The position of a single shade, as returned by the HD PowerView hub * - * @author Andrew Fiddian-Green - Initial contribution + * @author Andy Lintner - Initial contribution + * @author Andrew Fiddian-Green - Added support for secondary rail positions */ @NonNullByDefault -public abstract class ShadePosition { +public class ShadePosition { + + private final transient Logger logger = LoggerFactory.getLogger(ShadePosition.class); + + /** + * Primary actuator position. + */ + private int posKind1; + private int position1; + + /** + * Secondary actuator position. + * + * Here we have to use Integer objects rather than just int primitives because these are secondary optional position + * elements in the JSON payload, so the GSON de-serializer might leave them as null. + */ + private @Nullable Integer posKind2 = null; + private @Nullable Integer position2 = null; + + public ShadePosition() { + } /** * Get the shade's State for the given actuator class resp. coordinate system. @@ -31,14 +59,250 @@ public abstract class ShadePosition { * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. * @return the current state. */ - public abstract State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords); + public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + State result = getPosition1(shadeCapabilities, posKindCoords); + if (result == UnDefType.UNDEF) { + result = getPosition2(shadeCapabilities, posKindCoords); + } + logger.trace("getState(): capabilities={}, coords={} => result={}", shadeCapabilities, posKindCoords, result); + return result; + } + + /** + * Set the shade's position1 value for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + */ + private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percentArg) { + int percent = percentArg; + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { + // on dual rail shades constrain percent to not move the lower rail above the upper + State secondary = getState(shadeCapabilities, SECONDARY_POSITION); + if (secondary instanceof PercentType) { + int secPercent = ((PercentType) secondary).intValue(); + if (percent < secPercent) { + percent = secPercent; + } + } + } + posKind1 = posKindCoords.ordinal(); + position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); + break; + + case SECONDARY_POSITION: + /* + * Secondary, blackout shade a 'Duolite' shade: => INVERTED + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + posKind1 = posKindCoords.ordinal(); + if (shadeCapabilities.supportsSecondaryOverlapped()) { + position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); + } else { + position1 = (int) Math.round((double) percent / 100 * MAX_SHADE); + } + break; + + case VANE_TILT_POSITION: + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + */ + posKind1 = posKindCoords.ordinal(); + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + position1 = (int) Math.round((double) percent / 100 * max); + break; + + default: + posKind1 = CoordinateSystem.NONE.ordinal(); + position1 = 0; + } + } + + /** + * Get the shade's position1 State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the State (or UNDEF if not available). + */ + private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (posKindCoords.equals(posKind1)) { + return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); + } + if (VANE_TILT_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { + return PercentType.HUNDRED; + } + if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { + return PercentType.HUNDRED; + } + break; + + case SECONDARY_POSITION: + /* + * Secondary, blackout shade a 'Duolite' shade: => INVERTED + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (posKindCoords.equals(posKind1)) { + if (shadeCapabilities.supportsSecondaryOverlapped()) { + return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); + } + return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100)); + } + if (!SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { + return PercentType.ZERO; + } + break; + + case VANE_TILT_POSITION: + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + * + * If the shades are not open, the vane position is undefined; if the the shades + * are exactly open then the vanes are at zero; otherwise return the actual vane + * position itself + * + * note: sometimes the hub may return a value of position1 > MAX_VANE (seems to + * be a bug in the hub) so we avoid an out of range exception via the Math.min() + * function below.. + */ + if (posKindCoords.equals(posKind1)) { + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + return new PercentType((int) Math.round((double) Math.min(position1, max) / max * 100)); + } + if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { + return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO; + } + if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped() + && shadeCapabilities.supportsTiltOnClosed()) { + return PercentType.HUNDRED; + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, return UNDEF + } + return UnDefType.UNDEF; + } + + /** + * Set the shade's position2 value for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. + * @param percent the new position value. + */ + private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percentArg) { + int percent = percentArg; + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + posKind2 = posKindCoords.ordinal(); + position2 = Integer.valueOf(MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE)); + break; + + case SECONDARY_POSITION: + /* + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { + // on dual rail shades constrain percent to not move the upper rail below the lower + State primary = getState(shadeCapabilities, PRIMARY_POSITION); + if (primary instanceof PercentType) { + int primaryPercent = ((PercentType) primary).intValue(); + if (percent > primaryPercent) { + percent = primaryPercent; + } + } + } + posKind2 = posKindCoords.ordinal(); + position2 = Integer.valueOf((int) Math.round((double) percent / 100 * MAX_SHADE)); + break; + + case VANE_TILT_POSITION: + posKind2 = posKindCoords.ordinal(); + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + position2 = Integer.valueOf((int) Math.round((double) percent / 100 * max)); + break; + + default: + posKind2 = null; + position2 = null; + } + } + + /** + * Get the shade's position2 State for the given actuator class resp. coordinate system. + * + * @param shadeCapabilities the shade Thing capabilities. + * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. + * @return the State (or UNDEF if not available). + */ + private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + Integer posKind2 = this.posKind2; + Integer position2 = this.position2; + + if (position2 == null || posKind2 == null) { + return UnDefType.UNDEF; + } + + switch (posKindCoords) { + case PRIMARY_POSITION: + /* + * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED + */ + if (posKindCoords.equals(posKind2)) { + return new PercentType(100 - (int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); + } + break; + + case SECONDARY_POSITION: + /* + * Secondary, upper rail of a dual action shade: => NOT INVERTED + */ + if (posKindCoords.equals(posKind2)) { + return new PercentType((int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); + } + break; + + /* + * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED + */ + case VANE_TILT_POSITION: + if (posKindCoords.equals(posKind2)) { + int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; + return new PercentType((int) Math.round((double) Math.min(position2.intValue(), max) / max * 100)); + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, return UNDEF + } + return UnDefType.UNDEF; + } /** * Detect if the ShadePosition has a posKindN value indicating potential support for a secondary rail. * * @return true if the ShadePosition supports a secondary rail. */ - public abstract boolean secondaryRailDetected(); + public boolean secondaryRailDetected() { + return SECONDARY_POSITION.equals(posKind1) || SECONDARY_POSITION.equals(posKind2); + } /** * Detect if the ShadePosition has both a posKindN value indicating potential support for tilt, AND a posKindN @@ -46,7 +310,10 @@ public abstract class ShadePosition { * * @return true if potential support for tilt anywhere functionality was detected. */ - public abstract boolean tiltAnywhereDetected(); + public boolean tiltAnywhereDetected() { + return ((PRIMARY_POSITION.equals(posKind1)) && (VANE_TILT_POSITION.equals(posKind2)) + || ((PRIMARY_POSITION.equals(posKind2) && (VANE_TILT_POSITION.equals(posKind1))))); + } /** * Set the shade's position for the given actuator class resp. coordinate system. @@ -56,6 +323,61 @@ public abstract class ShadePosition { * @param percent the new position value. * @return this object. */ - public abstract ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, - int percent); + public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + logger.trace("setPosition(): capabilities={}, coords={}, percent={}", shadeCapabilities, posKindCoords, + percent); + // if necessary swap the order of position1 and position2 + if (PRIMARY_POSITION.equals(posKind2) && !PRIMARY_POSITION.equals(posKind1)) { + final Integer posKind2Temp = posKind2; + final Integer position2Temp = position2; + posKind2 = Integer.valueOf(posKind1); + position2 = Integer.valueOf(position1); + posKind1 = posKind2Temp != null ? posKind2Temp.intValue() : NONE.ordinal(); + position1 = position2Temp != null ? position2Temp.intValue() : 0; + } + + // delete position2 if it has an invalid position kind + if (ERROR_UNKNOWN.equals(posKind2) || NONE.equals(posKind2)) { + posKind2 = null; + position2 = null; + } + + // logic to set either position1 or position2 + switch (posKindCoords) { + case PRIMARY_POSITION: + if (shadeCapabilities.supportsPrimary()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case SECONDARY_POSITION: + if (shadeCapabilities.supportsSecondary()) { + if (shadeCapabilities.supportsPrimary()) { + setPosition2(shadeCapabilities, posKindCoords, percent); + } else { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + } else if (shadeCapabilities.supportsSecondaryOverlapped()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case VANE_TILT_POSITION: + if (shadeCapabilities.supportsPrimary()) { + if (shadeCapabilities.supportsTiltOnClosed()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } else if (shadeCapabilities.supportsTiltAnywhere()) { + setPosition2(shadeCapabilities, posKindCoords, percent); + } + } else if (shadeCapabilities.supportsTiltAnywhere()) { + setPosition1(shadeCapabilities, posKindCoords, percent); + } + break; + + case ERROR_UNKNOWN: + case NONE: + // fall through, do nothing + } + return this; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java deleted file mode 100644 index ebe97bed29f71..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadeDataV1.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v1; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; - -/** - * State of a Shade as returned by an HD PowerView hub of Generation 1 or 2. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class ShadeDataV1 extends ShadeData { - // fields for Generation 1/2 hubs only; NOTE: all these are Nullable - public int groupId; - public int order; - public double batteryStrength; - public boolean batteryIsLow; - public @Nullable Boolean timedOut; - public @Nullable Firmware motor; - // note: in old JSON batteryKind was a string but now it's a number; fortunately GSON string accepts either - public @Nullable String batteryKind; - - @Override - public BatteryKind getBatteryKind() { - return BatteryKind.fromString(batteryKind); - } - - @Override - public int version() { - return 1; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java deleted file mode 100644 index 3a7371f9587a7..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v1/ShadePositionV1.java +++ /dev/null @@ -1,387 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v1; - -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; -import org.openhab.core.library.types.PercentType; -import org.openhab.core.types.State; -import org.openhab.core.types.UnDefType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The position of a single shade, as returned by an HD PowerView hub of Generation 1/2 - * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Added support for secondary rail positions - */ -@NonNullByDefault -public class ShadePositionV1 extends ShadePosition { - - private final transient Logger logger = LoggerFactory.getLogger(ShadePositionV1.class); - - /** - * Primary actuator position. - */ - private int posKind1; - private int position1; - - /** - * Secondary actuator position. - * - * Here we have to use Integer objects rather than just int primitives because these are secondary optional position - * elements in the JSON payload, so the GSON de-serializer might leave them as null. - */ - private @Nullable Integer posKind2 = null; - private @Nullable Integer position2 = null; - - public ShadePositionV1() { - } - - /** - * Get the shade's State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the current state. - */ - @Override - public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - State result = getPosition1(shadeCapabilities, posKindCoords); - if (result == UnDefType.UNDEF) { - result = getPosition2(shadeCapabilities, posKindCoords); - } - logger.trace("getState(): capabilities={}, coords={} => result={}", shadeCapabilities, posKindCoords, result); - return result; - } - - /** - * Set the shade's position1 value for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - */ - private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { - // on dual rail shades constrain percent to not move the lower rail above the upper - State secondary = getState(shadeCapabilities, SECONDARY_POSITION); - if (secondary instanceof PercentType) { - int secPercent = ((PercentType) secondary).intValue(); - if (percent < secPercent) { - percent = secPercent; - } - } - } - posKind1 = posKindCoords.ordinal(); - position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); - break; - - case SECONDARY_POSITION: - /* - * Secondary, blackout shade a 'Duolite' shade: => INVERTED - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - posKind1 = posKindCoords.ordinal(); - if (shadeCapabilities.supportsSecondaryOverlapped()) { - position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE); - } else { - position1 = (int) Math.round((double) percent / 100 * MAX_SHADE); - } - break; - - case VANE_TILT_POSITION: - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - */ - posKind1 = posKindCoords.ordinal(); - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - position1 = (int) Math.round((double) percent / 100 * max); - break; - - default: - posKind1 = CoordinateSystem.NONE.ordinal(); - position1 = 0; - } - } - - /** - * Get the shade's position1 State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the State (or UNDEF if not available). - */ - private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (posKindCoords.equals(posKind1)) { - return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); - } - if (VANE_TILT_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { - return PercentType.HUNDRED; - } - if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { - return PercentType.HUNDRED; - } - break; - - case SECONDARY_POSITION: - /* - * Secondary, blackout shade a 'Duolite' shade: => INVERTED - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (posKindCoords.equals(posKind1)) { - if (shadeCapabilities.supportsSecondaryOverlapped()) { - return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100)); - } - return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100)); - } - if (!SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped()) { - return PercentType.ZERO; - } - break; - - case VANE_TILT_POSITION: - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - * - * If the shades are not open, the vane position is undefined; if the the shades - * are exactly open then the vanes are at zero; otherwise return the actual vane - * position itself - * - * note: sometimes the hub may return a value of position1 > MAX_VANE (seems to - * be a bug in the hub) so we avoid an out of range exception via the Math.min() - * function below.. - */ - if (posKindCoords.equals(posKind1)) { - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - return new PercentType((int) Math.round((double) Math.min(position1, max) / max * 100)); - } - if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) { - return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO; - } - if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsSecondaryOverlapped() - && shadeCapabilities.supportsTiltOnClosed()) { - return PercentType.HUNDRED; - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, return UNDEF - } - return UnDefType.UNDEF; - } - - /** - * Set the shade's position2 value for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - */ - private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - posKind2 = posKindCoords.ordinal(); - position2 = Integer.valueOf(MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE)); - break; - - case SECONDARY_POSITION: - /* - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (shadeCapabilities.supportsPrimary() && shadeCapabilities.supportsSecondary()) { - // on dual rail shades constrain percent to not move the upper rail below the lower - State primary = getState(shadeCapabilities, PRIMARY_POSITION); - if (primary instanceof PercentType) { - int primaryPercent = ((PercentType) primary).intValue(); - if (percent > primaryPercent) { - percent = primaryPercent; - } - } - } - posKind2 = posKindCoords.ordinal(); - position2 = Integer.valueOf((int) Math.round((double) percent / 100 * MAX_SHADE)); - break; - - case VANE_TILT_POSITION: - posKind2 = posKindCoords.ordinal(); - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - position2 = Integer.valueOf((int) Math.round((double) percent / 100 * max)); - break; - - default: - posKind2 = null; - position2 = null; - } - } - - /** - * Get the shade's position2 State for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be returned. - * @return the State (or UNDEF if not available). - */ - private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { - Integer posKind2 = this.posKind2; - Integer position2 = this.position2; - - if (position2 == null || posKind2 == null) { - return UnDefType.UNDEF; - } - - switch (posKindCoords) { - case PRIMARY_POSITION: - /* - * Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED - */ - if (posKindCoords.equals(posKind2)) { - return new PercentType(100 - (int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); - } - break; - - case SECONDARY_POSITION: - /* - * Secondary, upper rail of a dual action shade: => NOT INVERTED - */ - if (posKindCoords.equals(posKind2)) { - return new PercentType((int) Math.round(position2.doubleValue() / MAX_SHADE * 100)); - } - break; - - /* - * Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED - */ - case VANE_TILT_POSITION: - if (posKindCoords.equals(posKind2)) { - int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE; - return new PercentType((int) Math.round((double) Math.min(position2.intValue(), max) / max * 100)); - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, return UNDEF - } - return UnDefType.UNDEF; - } - - /** - * Detect if the ShadePosition has a posKindN value indicating potential support for a secondary rail. - * - * @return true if the ShadePosition supports a secondary rail. - */ - @Override - public boolean secondaryRailDetected() { - return SECONDARY_POSITION.equals(posKind1) || SECONDARY_POSITION.equals(posKind2); - } - - /** - * Detect if the ShadePosition has both a posKindN value indicating potential support for tilt, AND a posKindN - * indicating support for a primary rail. i.e. it potentially supports tilt anywhere functionality. - * - * @return true if potential support for tilt anywhere functionality was detected. - */ - @Override - public boolean tiltAnywhereDetected() { - return ((PRIMARY_POSITION.equals(posKind1)) && (VANE_TILT_POSITION.equals(posKind2)) - || ((PRIMARY_POSITION.equals(posKind2) && (VANE_TILT_POSITION.equals(posKind1))))); - } - - /** - * Set the shade's position for the given actuator class resp. coordinate system. - * - * @param shadeCapabilities the shade Thing capabilities. - * @param posKindCoords the actuator class (coordinate system) whose state is to be changed. - * @param percent the new position value. - * @return this object. - */ - @Override - public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { - logger.trace("setPosition(): capabilities={}, coords={}, percent={}", shadeCapabilities, posKindCoords, - percent); - // if necessary swap the order of position1 and position2 - if (PRIMARY_POSITION.equals(posKind2) && !PRIMARY_POSITION.equals(posKind1)) { - final Integer posKind2Temp = posKind2; - final Integer position2Temp = position2; - posKind2 = Integer.valueOf(posKind1); - position2 = Integer.valueOf(position1); - posKind1 = posKind2Temp != null ? posKind2Temp.intValue() : NONE.ordinal(); - position1 = position2Temp != null ? position2Temp.intValue() : 0; - } - - // delete position2 if it has an invalid position kind - if (ERROR_UNKNOWN.equals(posKind2) || NONE.equals(posKind2)) { - posKind2 = null; - position2 = null; - } - - // logic to set either position1 or position2 - switch (posKindCoords) { - case PRIMARY_POSITION: - if (shadeCapabilities.supportsPrimary()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case SECONDARY_POSITION: - if (shadeCapabilities.supportsSecondary()) { - if (shadeCapabilities.supportsPrimary()) { - setPosition2(shadeCapabilities, posKindCoords, percent); - } else { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - } else if (shadeCapabilities.supportsSecondaryOverlapped()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case VANE_TILT_POSITION: - if (shadeCapabilities.supportsPrimary()) { - if (shadeCapabilities.supportsTiltOnClosed()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } else if (shadeCapabilities.supportsTiltAnywhere()) { - setPosition2(shadeCapabilities, posKindCoords, percent); - } - } else if (shadeCapabilities.supportsTiltAnywhere()) { - setPosition1(shadeCapabilities, posKindCoords, percent); - } - break; - - case ERROR_UNKNOWN: - case NONE: - // fall through, do nothing - } - return this; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java deleted file mode 100644 index ef6b3edde8b07..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadeDataV3.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api._v3; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; - -/** - * State of a Shade as returned by an HD PowerView hub of Generation 3. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class ShadeDataV3 extends ShadeData { - public @Nullable String ptName; - public @Nullable String powerType; - public @Nullable String bleName; - - @Override - public String getName() { - return String.join(" ", super.getName(), ptName); - } - - @Override - public BatteryKind getBatteryKind() { - // TODO the schema for powerType is not clear; is may be a string? or an integer? - String powerType = this.powerType; - if (powerType != null) { - try { - Integer powerTypeInt = Integer.valueOf(powerType); - switch (powerTypeInt) { - case 0: - return BatteryKind.BATTERY_WAND; - case 1: - return BatteryKind.HARDWIRED_POWER_SUPPLY; - case 2: - return BatteryKind.RECHARGEABLE_BATTERY_WAND; - } - } catch (NumberFormatException e) { - // fall through - } - } - return BatteryKind.ERROR_UNKNOWN; - } - - @Override - public int version() { - return 3; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java index 07b4c87e778a1..1c544c047e878 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java @@ -21,7 +21,7 @@ * @author Andy Lintner - Initial contribution */ @NonNullByDefault -public class ShadePositions { +class ShadePositions { public ShadePosition positions; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java deleted file mode 100644 index 4191c5bd96b95..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scene.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Scene object as returned by an HD PowerView hub - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public abstract class Scene implements Comparable { - // fields that are common to Generation 1/2 and 3 hubs - public int id; - public @Nullable String name; - - @Override - public abstract int compareTo(Scene other); - - public String getName() { - return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8); - } - - public abstract int version(); -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java index 4fa5a5fe4264a..5cb25a8aab0b4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.hdpowerview.internal.api.responses; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -27,4 +29,58 @@ public class Scenes { public @Nullable List sceneData; public @Nullable List sceneIds; + + /* + * the following SuppressWarnings annotation is because the Eclipse compiler + * does NOT expect a NonNullByDefault annotation on the inner class, since it is + * implicitly inherited from the outer class, whereas the Maven compiler always + * requires an explicit NonNullByDefault annotation on all classes + */ + @SuppressWarnings("null") + @NonNullByDefault + public static class Scene implements Comparable { + public int id; + public @Nullable String name; + public int roomId; + public int order; + public int colorId; + public int iconId; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof Scene)) { + return false; + } + Scene other = (Scene) o; + + return this.id == other.id && this.name.equals(other.name) && this.roomId == other.roomId + && this.order == other.order && this.colorId == other.colorId && this.iconId == other.iconId; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + (name == null ? 0 : name.hashCode()); + result = prime * result + roomId; + result = prime * result + order; + result = prime * result + colorId; + result = prime * result + iconId; + + return result; + } + + @Override + public int compareTo(Scene other) { + return Integer.compare(order, other.order); + } + + public String getName() { + return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8); + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java deleted file mode 100644 index 7033358730002..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses; - -import java.time.DayOfWeek; -import java.util.EnumSet; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * Abstract class for scheduled event as returned by an HD PowerView hub. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public abstract class ScheduledEvent { - // fields common to Generation 1/2 and 3 hubs - public int id; - public int type; - public boolean enabled; - public int hour; - public int minute; - public int sceneId; - - public abstract EnumSet getDays(); - - public abstract int getEventType(); - - public abstract int version(); -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java index 5a595cd206a25..e7ae6ac1ae440 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java @@ -38,4 +38,95 @@ public class ScheduledEvents { public @Nullable List scheduledEventData; public @Nullable List scheduledEventIds; + + /* + * the following SuppressWarnings annotation is because the Eclipse compiler + * does NOT expect a NonNullByDefault annotation on the inner class, since it is + * implicitly inherited from the outer class, whereas the Maven compiler always + * requires an explicit NonNullByDefault annotation on all classes + */ + @SuppressWarnings("null") + @NonNullByDefault + public static class ScheduledEvent { + public int id; + public boolean enabled; + public int sceneId; + public int sceneCollectionId; + public boolean daySunday; + public boolean dayMonday; + public boolean dayTuesday; + public boolean dayWednesday; + public boolean dayThursday; + public boolean dayFriday; + public boolean daySaturday; + public int eventType; + public int hour; + public int minute; + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ScheduledEvent)) { + return false; + } + ScheduledEvent other = (ScheduledEvent) o; + + return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId + && this.sceneCollectionId == other.sceneCollectionId && this.daySunday == other.daySunday + && this.dayMonday == other.dayMonday && this.dayTuesday == other.dayTuesday + && this.dayWednesday == other.dayWednesday && this.dayThursday == other.dayThursday + && this.dayFriday == other.dayFriday && this.daySaturday == other.daySaturday + && this.eventType == other.eventType && this.hour == other.hour && this.minute == other.minute; + } + + @Override + public final int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + (enabled ? 1 : 0); + result = prime * result + sceneId; + result = prime * result + sceneCollectionId; + result = prime * result + (daySunday ? 1 : 0); + result = prime * result + (dayMonday ? 1 : 0); + result = prime * result + (dayTuesday ? 1 : 0); + result = prime * result + (dayWednesday ? 1 : 0); + result = prime * result + (dayThursday ? 1 : 0); + result = prime * result + (dayFriday ? 1 : 0); + result = prime * result + (daySaturday ? 1 : 0); + result = prime * result + eventType; + result = prime * result + hour; + result = prime * result + minute; + + return result; + } + + public EnumSet getDays() { + EnumSet days = EnumSet.noneOf(DayOfWeek.class); + if (daySunday) { + days.add(DayOfWeek.SUNDAY); + } + if (dayMonday) { + days.add(DayOfWeek.MONDAY); + } + if (dayTuesday) { + days.add(DayOfWeek.TUESDAY); + } + if (dayWednesday) { + days.add(DayOfWeek.WEDNESDAY); + } + if (dayThursday) { + days.add(DayOfWeek.THURSDAY); + } + if (dayFriday) { + days.add(DayOfWeek.FRIDAY); + } + if (daySaturday) { + days.add(DayOfWeek.SATURDAY); + } + return days; + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java index f73635e929c27..69da9378395b7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java @@ -14,7 +14,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; /** * State of a single Shade, as returned by an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java index 72d820d1ee7db..2746523b47a97 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.hdpowerview.internal.api.responses; +import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; +import org.openhab.binding.hdpowerview.internal.api.BatteryKind; +import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.api.ShadePosition; /** * State of all Shades, as returned by an HD PowerView hub @@ -28,4 +31,40 @@ public class Shades { public @Nullable List shadeData; public @Nullable List shadeIds; + + /* + * the following SuppressWarnings annotation is because the Eclipse compiler + * does NOT expect a NonNullByDefault annotation on the inner class, since it is + * implicitly inherited from the outer class, whereas the Maven compiler always + * requires an explicit NonNullByDefault annotation on all classes + */ + @SuppressWarnings("null") + @NonNullByDefault + public static class ShadeData { + public int id; + public @Nullable String name; + public int roomId; + public int groupId; + public int order; + public int type; + public double batteryStrength; + public int batteryStatus; + public boolean batteryIsLow; + public @Nullable ShadePosition positions; + public @Nullable Boolean timedOut; + public int signalStrength; + public @Nullable Integer capabilities; + public @Nullable Firmware firmware; + public @Nullable Firmware motor; + // note: in old JSON batteryKind was a string but now it's a number; fortunately GSON string accepts either + public @Nullable String batteryKind; + + public String getName() { + return new String(Base64.getDecoder().decode(name)); + } + + public BatteryKind getBatteryKind() { + return BatteryKind.fromString(batteryKind); + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java deleted file mode 100644 index f4efb2805964a..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/SceneV1.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v1; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; - -/** - * Scene object as returned by an HD PowerView hub of Generation 3 - * - * @author Andy Lintner - Initial contribution - * @author Andrew Fiddian-Green - Refactored into separate class - */ -@NonNullByDefault -public class SceneV1 extends Scene { - public int roomId; - public int order; - public int colorId; - public int iconId; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof SceneV1)) { - return false; - } - SceneV1 other = (SceneV1) o; - - return this.id == other.id && getName().equals(other.getName()) && this.roomId == other.roomId - && this.order == other.order && this.colorId == other.colorId && this.iconId == other.iconId; - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + id; - result = prime * result + getName().hashCode(); - result = prime * result + roomId; - result = prime * result + order; - result = prime * result + colorId; - result = prime * result + iconId; - - return result; - } - - @Override - public int compareTo(Scene other) throws IllegalArgumentException { - if (other.version() == version()) { - return Integer.compare(order, ((SceneV1) other).order); - } - throw new IllegalArgumentException("Cannot compare scenes from different hub generations"); - } - - @Override - public int version() { - return 1; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java deleted file mode 100644 index b7ef3c878288c..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v1/ScheduledEventV1.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v1; - -import java.time.DayOfWeek; -import java.util.EnumSet; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; - -/** - * class for scheduled event as returned by an HD PowerView Generation 1/2 hub. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class ScheduledEventV1 extends ScheduledEvent { - // fields specific to Generation 1/2 - public int sceneCollectionId; - public boolean daySunday; - public boolean dayMonday; - public boolean dayTuesday; - public boolean dayWednesday; - public boolean dayThursday; - public boolean dayFriday; - public boolean daySaturday; - public int eventType; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof ScheduledEventV1)) { - return false; - } - ScheduledEventV1 other = (ScheduledEventV1) o; - - return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId - && this.sceneCollectionId == other.sceneCollectionId && this.daySunday == other.daySunday - && this.dayMonday == other.dayMonday && this.dayTuesday == other.dayTuesday - && this.dayWednesday == other.dayWednesday && this.dayThursday == other.dayThursday - && this.dayFriday == other.dayFriday && this.daySaturday == other.daySaturday - && this.eventType == other.eventType && this.hour == other.hour && this.minute == other.minute; - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + (enabled ? 1 : 0); - result = prime * result + sceneId; - result = prime * result + sceneCollectionId; - result = prime * result + (daySunday ? 1 : 0); - result = prime * result + (dayMonday ? 1 : 0); - result = prime * result + (dayTuesday ? 1 : 0); - result = prime * result + (dayWednesday ? 1 : 0); - result = prime * result + (dayThursday ? 1 : 0); - result = prime * result + (dayFriday ? 1 : 0); - result = prime * result + (daySaturday ? 1 : 0); - result = prime * result + eventType; - result = prime * result + hour; - result = prime * result + minute; - - return result; - } - - @Override - public EnumSet getDays() { - EnumSet days = EnumSet.noneOf(DayOfWeek.class); - if (daySunday) { - days.add(DayOfWeek.SUNDAY); - } - if (dayMonday) { - days.add(DayOfWeek.MONDAY); - } - if (dayTuesday) { - days.add(DayOfWeek.TUESDAY); - } - if (dayWednesday) { - days.add(DayOfWeek.WEDNESDAY); - } - if (dayThursday) { - days.add(DayOfWeek.THURSDAY); - } - if (dayFriday) { - days.add(DayOfWeek.FRIDAY); - } - if (daySaturday) { - days.add(DayOfWeek.SATURDAY); - } - return days; - } - - @Override - public int getEventType() { - return eventType; - } - - @Override - public int version() { - return 1; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java deleted file mode 100644 index 9302a29782404..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/InfoV3.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; - -/** - * DTO for the Generation 3 gateway information. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class InfoV3 { - public @Nullable String fwVersion; - public @Nullable String serialNumber; - - public HubFirmware toHubFirmware() { - Firmware firmware = new Firmware(); - String fwVersion = this.fwVersion; - if (fwVersion != null) { - String[] parts = fwVersion.split("\\."); - if (parts.length > 0) { - try { - firmware.revision = Integer.valueOf(parts[0]); - } catch (NumberFormatException e) { - } - } - if (parts.length > 1) { - try { - firmware.subRevision = Integer.valueOf(parts[1]); - } catch (NumberFormatException e) { - } - } - if (parts.length > 2) { - try { - firmware.build = Integer.valueOf(parts[2]); - } catch (NumberFormatException e) { - } - } - } - - String name = "Generation 3 Hub"; - if (serialNumber != null) { - name = String.format("%s (serial: %s)", name, serialNumber); - } - firmware.name = name; - - HubFirmware result = new HubFirmware(); - result.mainProcessor = firmware; - return result; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java deleted file mode 100644 index 5756f1a9a7e52..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SceneV3.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.api.responses._v3; - -import java.util.List; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; - -/** - * Scene object as returned by an HD PowerView hub of Generation 3 - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class SceneV3 extends Scene { - public @Nullable String ptName; - public @Nullable String color; - public @Nullable String icon; - public @Nullable List roomIds; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof SceneV3)) { - return false; - } - SceneV3 other = (SceneV3) o; - String color = this.color; - String icon = this.icon; - - // TODO check roomIds as well ?? - return this.id == other.id && getName().equals(other.getName()) && (color != null && color.equals(other.color)) - && (icon != null && icon.equals(other.icon)); - } - - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - - String color = this.color; - String icon = this.icon; - - // TODO hash roomIds as well ?? - result = prime * result + id; - result = prime * result + getName().hashCode(); - result = prime * result + (color == null ? 0 : color.hashCode()); - result = prime * result + (icon == null ? 0 : icon.hashCode()); - - return result; - } - - @Override - public String getName() { - return String.join(" ", super.getName(), ptName); - } - - @Override - public int compareTo(Scene other) throws IllegalArgumentException { - if (other.version() == version()) { - // TODO fix this code.. - return this.equals(other) ? 0 : Integer.MAX_VALUE; - } - throw new IllegalArgumentException("Cannot compare scenes from different hub generations"); - } - - @Override - public int version() { - return 3; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java index 00a07ea4de4fa..10782b22b991c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java @@ -25,11 +25,10 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; @@ -64,7 +63,7 @@ private AutomationChannelBuilder(HDPowerViewTranslationProvider translationProvi /** * Creates an {@link AutomationChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and * {@link ChannelGroupUID}. - * + * * @param translationProvider the {@link HDPowerViewTranslationProvider} * @param channelGroupUid parent {@link ChannelGroupUID} for created channels * @return channel builder @@ -76,7 +75,7 @@ public static AutomationChannelBuilder create(HDPowerViewTranslationProvider tra /** * Adds created channels to existing list. - * + * * @param channels list that channels will be added to * @return channel builder */ @@ -87,7 +86,7 @@ public AutomationChannelBuilder withChannels(List channels) { /** * Sets the scenes. - * + * * @param scenes the scenes * @return channel builder */ @@ -98,7 +97,7 @@ public AutomationChannelBuilder withScenes(List scenes) { /** * Sets the scene collections. - * + * * @param sceneCollections the scene collections * @return channel builder */ @@ -110,7 +109,7 @@ public AutomationChannelBuilder withSceneCollections(List scene /** * Sets the scheduled events. - * + * * @param scheduledEvents the sceduled events * @return channel builder */ @@ -169,21 +168,20 @@ public List build() { } logger.warn("Scene '{}' was not found for scheduled event '{}'", scheduledEvent.sceneId, scheduledEvent.id); return null; - } else if (scheduledEvent.version() == 1 && ((ScheduledEventV1) scheduledEvent).sceneCollectionId > 0) { + } else if (scheduledEvent.sceneCollectionId > 0) { Map sceneCollections = this.sceneCollections; - ScheduledEventV1 scheduledEventV1 = (ScheduledEventV1) scheduledEvent; if (sceneCollections == null) { logger.warn( "Scheduled event '{}' references scene collection '{}', but no scene collections are loaded", - scheduledEvent.id, scheduledEventV1.sceneCollectionId); + scheduledEvent.id, scheduledEvent.sceneCollectionId); return null; } - SceneCollection sceneCollection = sceneCollections.get(scheduledEventV1.sceneCollectionId); + SceneCollection sceneCollection = sceneCollections.get(scheduledEvent.sceneCollectionId); if (sceneCollection != null) { return sceneCollection.getName(); } logger.warn("Scene collection '{}' was not found for scheduled event '{}'", - scheduledEventV1.sceneCollectionId, scheduledEvent.id); + scheduledEvent.sceneCollectionId, scheduledEvent.id); return null; } else { logger.warn("Scheduled event '{}'' not related to any scene or scene collection", scheduledEvent.id); @@ -193,7 +191,8 @@ public List build() { private String getScheduledEventName(String sceneName, ScheduledEvent scheduledEvent) { String timeString, daysString; - switch (scheduledEvent.getEventType()) { + + switch (scheduledEvent.eventType) { case ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME: timeString = LocalTime.of(scheduledEvent.hour, scheduledEvent.minute).toString(); break; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java index 8d9624620223e..01c05137e9ece 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; @@ -45,7 +45,7 @@ private SceneChannelBuilder(HDPowerViewTranslationProvider translationProvider, /** * Creates a {@link SceneChannelBuilder} for the given {@link HDPowerViewTranslationProvider} and * {@link ChannelGroupUID}. - * + * * @param translationProvider the {@link HDPowerViewTranslationProvider} * @param channelGroupUid parent {@link ChannelGroupUID} for created channels * @return channel builder @@ -57,7 +57,7 @@ public static SceneChannelBuilder create(HDPowerViewTranslationProvider translat /** * Adds created channels to existing list. - * + * * @param channels list that channels will be added to * @return channel builder */ @@ -68,7 +68,7 @@ public SceneChannelBuilder withChannels(List channels) { /** * Sets the scenes. - * + * * @param scenes the scenes * @return channel builder */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java index 156a4835fc07e..14cd3b3424b72 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/config/HDPowerViewHubConfiguration.java @@ -24,13 +24,10 @@ public class HDPowerViewHubConfiguration { public static final String HOST = "host"; - public static final String GENERATION = "generation"; public @Nullable String host; public long refresh; public long hardRefresh; public long hardRefreshBatteryLevel; - - public int generation; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java index 37d9b6f952a48..98d108befcfb1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java @@ -21,9 +21,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java index b118704083e38..d9c9bf063e795 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java @@ -22,20 +22,28 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Abstract (component) class that discovers HD PowerView hubs by means of mDNS + * Discovers HD PowerView hubs by means of mDNS * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public abstract class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticipant { +@Component +public class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticipant { - protected static final Pattern VALID_IP_V4_ADDRESS = Pattern + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant.class); + + public static final Pattern VALID_IP_V4_ADDRESS = Pattern .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); @Override @@ -44,10 +52,25 @@ public Set getSupportedThingTypeUIDs() { } @Override - public abstract String getServiceType(); + public String getServiceType() { + return "_powerview._tcp.local."; + } @Override - public abstract @Nullable DiscoveryResult createResult(ServiceInfo service); + public @Nullable DiscoveryResult createResult(ServiceInfo service) { + for (String host : service.getHostAddresses()) { + if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { + ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); + DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) + .withProperty(HDPowerViewHubConfiguration.HOST, host) + .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) + .withLabel("PowerView Hub (" + host + ")").build(); + logger.debug("mDNS discovered hub on host '{}'", host); + return hub; + } + } + return null; + } @Override public @Nullable ThingUID getThingUID(ServiceInfo service) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java deleted file mode 100644 index bb138c5eef52c..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v1/HDPowerViewHubDiscoveryParticipantV1.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.discovery._v1; - -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; - -import javax.jmdns.ServiceInfo; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; -import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewHubDiscoveryParticipant; -import org.openhab.core.config.discovery.DiscoveryResult; -import org.openhab.core.config.discovery.DiscoveryResultBuilder; -import org.openhab.core.thing.ThingUID; -import org.osgi.service.component.annotations.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Discovers HD PowerView generation 1/2 hubs by means of mDNS - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -@Component -public class HDPowerViewHubDiscoveryParticipantV1 extends HDPowerViewHubDiscoveryParticipant { - - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV1.class); - - @Override - public String getServiceType() { - return "_powerview._tcp.local."; - } - - @Override - public @Nullable DiscoveryResult createResult(ServiceInfo service) { - for (String host : service.getHostAddresses()) { - if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); - DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) - .withProperty(HDPowerViewHubConfiguration.HOST, host) - .withProperty(HDPowerViewHubConfiguration.GENERATION, 1) - .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel("PowerView Hub (" + host + ")").build(); - logger.debug("mDNS discovered generation 1/2 hub on host '{}'", host); - return hub; - } - } - return null; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java new file mode 100644 index 0000000000000..f7051465b6e20 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.discovery; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.core.config.discovery.AbstractDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.ThingUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Discovers HD PowerView Shades and Repeaters from an existing hub + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewDeviceDiscoveryServiceV3 extends AbstractDiscoveryService { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewDeviceDiscoveryServiceV3.class); + private final HDPowerViewHubHandler3 hub; + private final Runnable scanner; + private @Nullable ScheduledFuture backgroundFuture; + private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase(); + + public HDPowerViewDeviceDiscoveryServiceV3(HDPowerViewHubHandler3 hub) { + super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3), 600, true); + this.hub = hub; + this.scanner = createScanner(); + } + + @Override + protected void startScan() { + scheduler.execute(scanner); + } + + @Override + protected void startBackgroundDiscovery() { + ScheduledFuture backgroundFuture = this.backgroundFuture; + if (backgroundFuture != null && !backgroundFuture.isDone()) { + backgroundFuture.cancel(true); + } + this.backgroundFuture = scheduler.scheduleWithFixedDelay(scanner, 0, 60, TimeUnit.SECONDS); + } + + @Override + protected void stopBackgroundDiscovery() { + ScheduledFuture backgroundFuture = this.backgroundFuture; + if (backgroundFuture != null && !backgroundFuture.isDone()) { + backgroundFuture.cancel(true); + this.backgroundFuture = null; + } + super.stopBackgroundDiscovery(); + } + + private Runnable createScanner() { + return () -> { + HDPowerViewWebTargets3 webTargets = hub.getWebTargets(); + try { + discoverShades(webTargets); + } catch (HubProcessingException e) { + logger.warn("Unexpected exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + } + stopScan(); + }; + } + + private void discoverShades(HDPowerViewWebTargets3 webTargets) throws HubProcessingException { + List shades = webTargets.getShades(); + ThingUID bridgeUid = hub.getThing().getUID(); + for (Shade3 shadeData : shades) { + if (shadeData.getId() == 0) { + continue; + } + String id = Integer.toString(shadeData.getId()); + ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3, bridgeUid, id); + + DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shadeData.getName()) + .withBridge(bridgeUid).withProperty(HDPowerViewShadeConfiguration.ID, id) + .withRepresentationProperty(HDPowerViewShadeConfiguration.ID); + String type = shadeData.getTypeString(); + if (type != null) { + builder.withProperty(HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, type); + } + logger.debug("Hub discovered shade '{}'", id); + thingDiscovered(builder.build()); + } + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java index be0b5a7577004..8f2273250a328 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/_v3/HDPowerViewHubDiscoveryParticipantV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java @@ -10,9 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.discovery._v3; +package org.openhab.binding.hdpowerview.internal.gen3.discovery; -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3; import javax.jmdns.ServiceInfo; @@ -28,9 +28,9 @@ import org.slf4j.LoggerFactory; /** - * Discovers HD PowerView generation 3 hubs by means of mDNS + * Discovers HD PowerView generation 3 hubs by means of mDNS. * - * @author Andrew Fiddian-Green - Initial contribution + * @author Andrew Fiddian-Green - Initial contribution. */ @NonNullByDefault @Component @@ -38,19 +38,13 @@ public class HDPowerViewHubDiscoveryParticipantV3 extends HDPowerViewHubDiscover private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV3.class); - @Override - public String getServiceType() { - return "_powerview-g3._tcp.local."; - } - @Override public @Nullable DiscoveryResult createResult(ServiceInfo service) { for (String host : service.getHostAddresses()) { if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_HUB, host.replace('.', '_')); + ThingUID thingUID = new ThingUID(THING_TYPE_HUB_GEN3, host.replace('.', '_')); DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) - .withProperty(HDPowerViewHubConfiguration.GENERATION, 3) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) .withLabel("PowerView Hub (" + host + ")").build(); logger.debug("mDNS discovered generation 3 hub on host '{}'", host); @@ -59,4 +53,9 @@ public String getServiceType() { } return null; } + + @Override + public String getServiceType() { + return "_powerview-g3._tcp.local."; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java similarity index 54% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java index f9454ff4ed0f5..e0d97be96d655 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseShadeV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java @@ -10,21 +10,25 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses._v3; +package org.openhab.binding.hdpowerview.internal.gen3.dto; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; /** - * Shade SSE event object as supplied an HD PowerView hub of Generation 3 + * DTO for the Generation 3 gateway information. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class SseShadeV3 { - public @Nullable String evt; - public @Nullable String isoDate; - public int id; - public @Nullable ShadePositionV3 currentPositions; +public class Info3 { + private @NonNullByDefault({}) String fwVersion; + private @NonNullByDefault({}) String serialNumber; + + public String getFwVersion() { + return fwVersion; + } + + public String getSerialNumber() { + return serialNumber; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java new file mode 100644 index 0000000000000..8d07cdd7c6342 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.dto; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Scene object as returned by an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution. + */ +@NonNullByDefault +public class Scene3 { + private int id; + private @NonNullByDefault({}) String name; + private @NonNullByDefault({}) String ptName; + private @NonNullByDefault({}) String color; + private @NonNullByDefault({}) String icon; + private @NonNullByDefault({}) List roomIds; + + public String getColor() { + return color; + } + + public String getIcon() { + return icon; + } + + public int getId() { + return id; + } + + public String getName() { + return String.join(" ", new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8), ptName); + } + + public String getPtName() { + return ptName; + } + + public List getRoomIds() { + List roomIds = this.roomIds; + return roomIds != null ? roomIds : List.of(); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java similarity index 60% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java index 492f499a17257..cb8fb302b6d75 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/SseSceneV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java @@ -10,20 +10,27 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses._v3; +package org.openhab.binding.hdpowerview.internal.gen3.dto; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; /** - * Scene SSE event object as supplied an HD PowerView hub of Generation 3 + * Scene SSE event object as supplied an HD PowerView hub of Generation 3. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class SseSceneV3 { - public @Nullable String evt; - public @Nullable String isoDate; - public int id; - public @Nullable SceneV3 scene; +public class SceneEvent { + private int id; + // private @NonNullByDefault({}) String evt; + // private @NonNullByDefault({}) String isoDate; + private @NonNullByDefault({}) Scene3 scene; + + public int getId() { + return id; + } + + public Scene3 getScene() { + return scene; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java similarity index 79% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java index ea1504a262a8b..74cd526170f06 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/_v3/ScheduledEventV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java @@ -10,14 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses._v3; +package org.openhab.binding.hdpowerview.internal.gen3.dto; import java.time.DayOfWeek; import java.util.EnumSet; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; /** @@ -26,9 +25,14 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ScheduledEventV3 extends ScheduledEvent { - // fields specific to Generation 3 - public @Nullable String days; +public class ScheduledEvent3 { + public int id; + public int type; + public boolean enabled; + public int hour; + public int minute; + public int sceneId; + public @NonNullByDefault({}) String days; private static final int MON = 0x01; private static final int TUE = 0x02; @@ -49,31 +53,16 @@ public boolean equals(@Nullable Object o) { if (o == this) { return true; } - if (!(o instanceof ScheduledEventV3)) { + if (!(o instanceof ScheduledEvent3)) { return false; } - ScheduledEventV3 other = (ScheduledEventV3) o; + ScheduledEvent3 other = (ScheduledEvent3) o; String days = this.days; return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId && (days != null && days.equals(other.days)) && this.hour == other.hour && this.minute == other.minute; } - @Override - public final int hashCode() { - final int prime = 31; - int result = 1; - String days = this.days; - result = prime * result + id; - result = prime * result + (enabled ? 1 : 0); - result = prime * result + sceneId; - result = prime * result + (days != null ? days.hashCode() : 0); - result = prime * result + hour; - result = prime * result + minute; - return result; - } - - @Override public EnumSet getDays() { EnumSet daySet = EnumSet.noneOf(DayOfWeek.class); String days = this.days; @@ -108,7 +97,6 @@ public EnumSet getDays() { return daySet; } - @Override public int getEventType() { switch (type) { case CLOCK_BASED: @@ -126,9 +114,4 @@ public int getEventType() { } return 0; } - - @Override - public int version() { - return 3; - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java new file mode 100644 index 0000000000000..f71fe9d498075 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java @@ -0,0 +1,158 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.dto; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * State of a Shade as returned by an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class Shade3 { + private int id; + private @Nullable Integer type; + private @Nullable String name; + private @Nullable String ptName; + private @Nullable Integer capabilities; + private @Nullable String powerType; // TODO unclear if this is String or Integer + private @Nullable Integer batteryStatus; + // private @Nullable Integer roomId; + private @Nullable Integer signalStrength; + private @Nullable String bleName; + private @Nullable Firmware firmware; + private @Nullable ShadePosition3 positions; + + private transient boolean partialState; + + public State getBatteryLevel() { + Integer batteryStatus = this.batteryStatus; + return batteryStatus == null ? UnDefType.UNDEF + : new PercentType(Math.max(0, Math.min(100, (100 * batteryStatus) / 3))); + } + + public @Nullable String getBleName() { + return bleName; + } + + public @Nullable Integer getCapabilities() { + return capabilities; + } + + public @Nullable String getCapabilitieString() { + Integer capabilities = this.capabilities; + return capabilities == null ? null : Integer.toString(capabilities); + } + + public @Nullable String getFirmware() { + Firmware firmware = this.firmware; + return firmware == null ? null + : String.format("%d.%d.%d", firmware.revision, firmware.subRevision, firmware.build); + } + + public int getId() { + return id; + } + + public State getLowBattery() { + Integer batteryStatus = this.batteryStatus; + return batteryStatus == null ? UnDefType.UNDEF : OnOffType.from(batteryStatus == 0); + } + + public String getName() { + return String.join(" ", new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8), ptName); + } + + public State getPosition(CoordinateSystem posKindCoords) { + ShadePosition3 positions = this.positions; + return positions == null ? UnDefType.UNDEF : positions.getState(posKindCoords); + } + + public @Nullable String getPowerType() { + return powerType; + } + + public @Nullable ShadePosition3 getShadePositions() { + return positions; + } + + public State getSignalStrength() { + Integer signalStrength = this.signalStrength; + return signalStrength == null ? UnDefType.UNDEF : new DecimalType(signalStrength); + } + + public @Nullable Integer getType() { + return type; + } + + public @Nullable String getTypeString() { + Integer type = this.type; + return type == null ? null : Integer.toString(type); + } + + public boolean hasFullState() { + return !partialState; + } + + public boolean isMainsPowered() { + // check powerType and return true or false + return false; + } + + public Shade3 setCapabilities(int capabilities) { + this.capabilities = capabilities; + return this; + } + + public Shade3 setId(int id) { + this.id = id; + return this; + } + + public Shade3 setPartialState() { + this.partialState = true; + return this; + } + + public Shade3 setPosition(CoordinateSystem coordinates, int percent) { + ShadePosition3 positions = this.positions; + if (positions == null) { + positions = new ShadePosition3(); + this.positions = positions; + } + positions.setPosition(coordinates, percent); + return this; + } + + public Shade3 setShadePosition(ShadePosition3 position) { + this.positions = position; + return this; + } + + public Shade3 setType(int type) { + this.type = type; + return this; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java new file mode 100644 index 0000000000000..af38fc9c4d96b --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.dto; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Shade SSE event object as supplied an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class ShadeEvent { + private int id; + // private @NonNullByDefault({}) String evt; + // private @NonNullByDefault({}) String isoDate; + // private @NonNullByDefault({}) String bleName; + private @NonNullByDefault({}) ShadePosition3 currentPositions; + // private @NonNullByDefault({}) ShadePosition3 targetPositions; + + public ShadePosition3 getCurrentPositions() { + return currentPositions; + } + + public int getId() { + return id; + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java similarity index 56% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java index 516d00df121dc..93800309a5546 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/_v3/ShadePositionV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java @@ -10,13 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api._v3; +package org.openhab.binding.hdpowerview.internal.gen3.dto; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; @@ -27,15 +24,13 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadePositionV3 extends ShadePosition { +public class ShadePosition3 { + private @NonNullByDefault({}) Double primary; + private @NonNullByDefault({}) Double secondary; + private @NonNullByDefault({}) Double tilt; + // private @NonNullByDefault({}) Double velocity; - private @Nullable Double primary; - private @Nullable Double secondary; - private @Nullable Double tilt; - // private @Nullable Double velocity; - - @Override - public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) { + public State getState(CoordinateSystem posKindCoords) { Double value; switch (posKindCoords) { case PRIMARY_POSITION: @@ -53,34 +48,39 @@ public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCo return value != null ? new PercentType((int) (value.doubleValue() * 100)) : UnDefType.UNDEF; } - @Override - public boolean secondaryRailDetected() { - return secondary != null; - } - - @Override - public boolean tiltAnywhereDetected() { - return tilt != null; - } - - @Override - public ShadePositionV3 setPosition(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) { + /** + * Set a new position value for this object based on the given coordinates, and the given new value. + * + * @param coordinates which of the position fields shall be set. + * @param percent the new value in percent. + * @return this object. + */ + public ShadePosition3 setPosition(CoordinateSystem coordinates, int percent) { Double value = Double.valueOf(percent) / 100.0; - switch (posKindCoords) { + switch (coordinates) { case PRIMARY_POSITION: - primary = shadeCapabilities.supportsPrimary() ? value : null; + primary = value; break; case SECONDARY_POSITION: - secondary = shadeCapabilities.supportsSecondary() || shadeCapabilities.supportsSecondaryOverlapped() - ? value - : null; + secondary = value; break; case VANE_TILT_POSITION: - tilt = shadeCapabilities.supportsTiltOnClosed() || shadeCapabilities.supportsTiltAnywhere() ? value - : null; + tilt = value; break; default: } return this; } + + public boolean supportsPrimary() { + return primary != null; + } + + public boolean supportsSecondary() { + return secondary != null; + } + + public boolean supportsTilt() { + return tilt != null; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java new file mode 100644 index 0000000000000..fbfe1f8cf3352 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java @@ -0,0 +1,284 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.handler; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.client.ClientBuilder; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.thing.type.AutoUpdatePolicy; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Bridge handler for an HD PowerView hub of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewHubHandler3 extends BaseBridgeHandler { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler3.class); + private final String channelTypeId = HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE; + private final String channelGroupId = HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES; + + private final HttpClient httpClient; + private final HDPowerViewTranslationProvider translationProvider; + private final ClientBuilder clientBuilder; + private final SseEventSourceFactory eventSourceFactory; + + private @Nullable HDPowerViewWebTargets3 webTargets; + private @Nullable ScheduledFuture refreshTask; + + private boolean scenesLoaded; + private boolean propertiesLoaded; + private boolean isDisposing; + + public HDPowerViewHubHandler3(Bridge bridge, HttpClient httpClient, + HDPowerViewTranslationProvider translationProvider, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory) { + super(bridge); + this.httpClient = httpClient; + this.translationProvider = translationProvider; + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; + } + + @Override + public void dispose() { + isDisposing = true; + ScheduledFuture future = this.refreshTask; + if (future != null) { + future.cancel(true); + } + this.refreshTask = null; + + HDPowerViewWebTargets3 webTargets = this.webTargets; + if (webTargets != null) { + try { + webTargets.close(); + } catch (IOException e) { + } + this.webTargets = null; + } + } + + /** + * Refresh the state of all things. Normally the thing's position state is updated by SSE. However we must do a + * refresh once on start up in order to get the initial state. Also the other properties (battery, signal strength + * etc.) are not updated by SSE. Furthermore we need to do periodic refreshes just in case the SSE connection may + * have been lost. + */ + private void doRefresh() { + try { + getWebTargets().openSSE(); + refreshProperties(); + refreshShades(); + refreshScenes(); + } catch (IllegalStateException | HubProcessingException e) { + logger.warn("doRefresh() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + } + } + + /** + * Getter for the list of all HDPowerViewShadeHandler3 child thing handlers. + * + * @return the list of handlers. + * @throws IllegalStateException if the bridge is not properly initialized. + */ + private List getThingHandlers() throws IllegalStateException { + Bridge bridge = getBridge(); + if (bridge != null) { + List result = new ArrayList<>(); + bridge.getThings().stream().map(thing -> thing.getHandler()).forEach(handler -> { + if (handler instanceof HDPowerViewShadeHandler3) { + result.add((HDPowerViewShadeHandler3) handler); + } + }); + return result; + } + throw new IllegalStateException("Bridge not initialized."); + } + + /** + * Getter for the webTargets. + * + * @return the webTargets. + * @throws IllegalStateException if webTargets is not initialized. + */ + public HDPowerViewWebTargets3 getWebTargets() throws IllegalStateException { + HDPowerViewWebTargets3 webTargets = this.webTargets; + if (webTargets != null) { + return webTargets; + } + throw new IllegalStateException("WebTargets not initialized."); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (RefreshType.REFRESH == command) { + scheduler.submit(() -> doRefresh()); + return; + } + Channel channel = getThing().getChannel(channelUID.getId()); + if (channel == null) { + return; + } + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + if (channelTypeUID == null) { + return; + } + if (channelTypeId.equals(channelTypeUID.getId()) && OnOffType.ON == command) { + try { + getWebTargets().activateScene(Integer.parseInt(channelUID.getIdWithoutGroup())); + } catch (HubProcessingException | IllegalStateException e) { + logger.warn("handleCommand() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + } + } + } + + @Override + public void initialize() { + HDPowerViewHubConfiguration config = getConfigAs(HDPowerViewHubConfiguration.class); + String host = config.host; + + if (host == null || host.isEmpty()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error.no-host-address"); + return; + } + + webTargets = new HDPowerViewWebTargets3(this, httpClient, clientBuilder, eventSourceFactory, host); + scenesLoaded = false; + propertiesLoaded = false; + isDisposing = false; + + /* + * Normally the thing's position state is updated by SSE. However we must do a refresh once on start up in order + * to get the initial state. Also the other properties (battery, signal strength etc.) are not updated by SSE. + * Furthermore we need to do periodic refreshes just in case the SSE connection may have been lost. So we + * schedule the refresh at the 'hardRefresh' interval. + */ + ScheduledFuture refreshTask = this.refreshTask; + if (refreshTask != null) { + refreshTask.cancel(false); + } + this.refreshTask = scheduler.scheduleWithFixedDelay(() -> doRefresh(), 10, config.hardRefresh, + TimeUnit.MINUTES); + + updateStatus(ThingStatus.UNKNOWN); + } + + /** + * Method that is called when a scene changes state. + * + * @param scene the one that changed. + */ + public void onSceneEvent(Scene3 scene) { + // TODO perhaps we should trigger an OH core event here ?? + } + + /** + * Method that is called when a shade changes state. + * + * @param shade the one that changed. + */ + public void onShadeEvent(Shade3 shade) { + try { + for (HDPowerViewShadeHandler3 handler : getThingHandlers()) { + if (isDisposing || handler.notify(shade)) { + break; + } + } + } catch (IllegalStateException e) { + logger.warn("onShadeEvent() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + } + } + + private void refreshProperties() throws HubProcessingException, IllegalStateException { + if (propertiesLoaded || isDisposing) { + return; + } + thing.setProperties(getWebTargets().getInformation()); + propertiesLoaded = true; + } + + /** + * Create the dynamic list of scene channels. + * + * @throws HubProcessingException if the web target connection caused an error. + * @throws IllegalStateException if this handler is in an illegal state. + */ + private void refreshScenes() throws HubProcessingException, IllegalStateException { + if (scenesLoaded || isDisposing) { + return; + } + ChannelTypeUID typeUID = new ChannelTypeUID(channelTypeId); + ChannelGroupUID groupUID = new ChannelGroupUID(thing.getUID(), channelGroupId); + List channels = new ArrayList<>(); + for (Scene3 scene : getWebTargets().getScenes()) { + ChannelUID channelUID = new ChannelUID(groupUID, Integer.toString(scene.getId())); + String name = scene.getName(); + String description = translationProvider.getText("dynamic-channel.scene-activate.description", name); + channels.add(ChannelBuilder.create(channelUID, CoreItemFactory.SWITCH).withType(typeUID).withLabel(name) + .withDescription(description).withAutoUpdatePolicy(AutoUpdatePolicy.VETO).build()); + } + updateThing(editThing().withChannels(channels).build()); + scenesLoaded = true; + } + + /** + * Get the full list of shades data and notify each of the thing handlers. + * + * @throws HubProcessingException if the web target connection caused an error. + * @throws IllegalStateException if this handler is in an illegal state. + */ + private void refreshShades() throws HubProcessingException, IllegalStateException { + List handlers = getThingHandlers(); + for (Shade3 shade : getWebTargets().getShades()) { + for (HDPowerViewShadeHandler3 handler : handlers) { + if (isDisposing || handler.notify(shade)) { + break; + } + } + } + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java new file mode 100644 index 0000000000000..19fd295d80e2c --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java @@ -0,0 +1,285 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.handler; + +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.BridgeHandler; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Thing handler for an HD PowerView shade of Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewShadeHandler3 extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewShadeHandler3.class); + + private static final String INVALID_CHANNEL = "invalid channel"; + private static final String INVALID_COMMAND = "invalid command"; + private static final String COMMAND_CALIBRATE = "CALIBRATE"; + private static final String COMMAND_IDENTIFY = "IDENTIFY"; + + private final Shade3 thisShade = new Shade3(); + private boolean isInitialized; + + public HDPowerViewShadeHandler3(Thing thing) { + super(thing); + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + } + + /** + * Getter for the hub handler. + * + * @return the hub handler. + * @throws IllegalStateException if the bridge or its handler are not initialized. + */ + private HDPowerViewHubHandler3 getHandler() throws IllegalStateException { + Bridge bridge = this.getBridge(); + if (bridge == null) { + throw new IllegalStateException("Bridge not initialised."); + } + BridgeHandler handler = bridge.getHandler(); + if (!(handler instanceof HDPowerViewHubHandler3)) { + throw new IllegalStateException("Bridge handler not initialised."); + } + return (HDPowerViewHubHandler3) handler; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (RefreshType.REFRESH == command) { + getHandler().handleCommand(channelUID, command); + return; + } + + HDPowerViewWebTargets3 webTargets = getHandler().getWebTargets(); + ShadePosition3 position = new ShadePosition3(); + int shadeId = thisShade.getId(); + try { + switch (channelUID.getId()) { + case CHANNEL_SHADE_POSITION: + if (command instanceof PercentType) { + position.setPosition(PRIMARY_POSITION, ((PercentType) command).intValue()); + webTargets.moveShade(shadeId, position); + break; + } else if (command instanceof UpDownType) { + position.setPosition(PRIMARY_POSITION, UpDownType.UP == command ? 0 : 100); + webTargets.moveShade(shadeId, position); + break; + } else if (StopMoveType.STOP == command) { + webTargets.stopShade(shadeId); + break; + } + throw new IllegalArgumentException(INVALID_COMMAND); + + case CHANNEL_SHADE_SECONDARY_POSITION: + if (command instanceof PercentType) { + position.setPosition(SECONDARY_POSITION, ((PercentType) command).intValue()); + webTargets.moveShade(shadeId, position); + break; + } else if (command instanceof UpDownType) { + position.setPosition(SECONDARY_POSITION, UpDownType.UP == command ? 0 : 100); + webTargets.moveShade(shadeId, position); + break; + } else if (StopMoveType.STOP == command) { + webTargets.stopShade(shadeId); + break; + } + throw new IllegalArgumentException(INVALID_COMMAND); + + case CHANNEL_SHADE_VANE: + if (command instanceof PercentType) { + position.setPosition(VANE_TILT_POSITION, ((PercentType) command).intValue()); + webTargets.moveShade(shadeId, position); + break; + } else if (command instanceof UpDownType) { + position.setPosition(VANE_TILT_POSITION, UpDownType.UP == command ? 0 : 100); + webTargets.moveShade(shadeId, position); + break; + } + throw new IllegalArgumentException(INVALID_COMMAND); + + case CHANNEL_SHADE_COMMAND: + if (command instanceof StringType) { + if (COMMAND_IDENTIFY.equals(((StringType) command).toString())) { + webTargets.jogShade(shadeId); + break; + } else if (COMMAND_CALIBRATE.equals(((StringType) command).toString())) { + webTargets.calibrateShade(shadeId); + break; + } + } + throw new IllegalArgumentException(INVALID_COMMAND); + + default: + throw new IllegalArgumentException(INVALID_CHANNEL); + } + } catch (HubProcessingException | IllegalArgumentException e) { + logger.warn("handleCommand() shadeId:{}, channelUID:{}, command:{}, exception:{}, message:{}", shadeId, + channelUID, command, e.getClass().getSimpleName(), e.getMessage()); + } + } + + @Override + public void initialize() { + thisShade.setId(getConfigAs(HDPowerViewShadeConfiguration.class).id); + Bridge bridge = getBridge(); + if (bridge == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error.invalid-bridge-handler"); + return; + } + isInitialized = false; + updateStatus(ThingStatus.UNKNOWN); + } + + /** + * Handle shade state change notifications. + * + * @param shade the new shade state. + * @return true if we handled the call. + */ + public boolean notify(Shade3 shade) { + if (thisShade.getId() == shade.getId()) { + updateStatus(ThingStatus.ONLINE); + if (!isInitialized) { + ShadePosition3 position = shade.getShadePositions(); + if (position != null) { + thisShade.setShadePosition(position); + } + updateProperties(shade); + updateDynamicChannels(shade); + } + isInitialized = true; + updateChannels(shade); + return true; + } + return false; + } + + /** + * Update channels based on the data in the passed shade instance. + * + * @param shade containing the channel data. + */ + private void updateChannels(Shade3 shade) { + updateState(CHANNEL_SHADE_POSITION, shade.getPosition(PRIMARY_POSITION)); + updateState(CHANNEL_SHADE_VANE, shade.getPosition(VANE_TILT_POSITION)); + updateState(CHANNEL_SHADE_SECONDARY_POSITION, shade.getPosition(SECONDARY_POSITION)); + if (shade.hasFullState()) { + updateState(CHANNEL_SHADE_LOW_BATTERY, shade.getLowBattery()); + updateState(CHANNEL_SHADE_BATTERY_LEVEL, shade.getBatteryLevel()); + updateState(CHANNEL_SHADE_SIGNAL_STRENGTH, shade.getSignalStrength()); + } + } + + /** + * If the given channel exists in the thing, but is NOT required in the thing, then add it to a list of channels to + * be removed. Or if the channel does NOT exist in the thing, but is required in the thing, then log a warning. + * + * @param removeList the list of channels to be removed from the thing. + * @param channelId the id of the channel to be (eventually) removed. + * @param channelRequired true if the thing requires this channel. + */ + private void updateDynamicChannel(List removeList, String channelId, boolean channelRequired) { + Channel channel = thing.getChannel(channelId); + if (!channelRequired && channel != null) { + removeList.add(channel); + } else if (channelRequired && channel == null) { + logger.warn("updateDynamicChannel() shadeId:{} is missing channel:{} => please recreate the thing", + thisShade.getId(), channelId); + } + } + + /** + * Remove previously statically created channels if the shade does not support them or they are not relevant. + * + * @param shade containing the channel data. + */ + private void updateDynamicChannels(Shade3 shade) { + List removeChannels = new ArrayList<>(); + + ShadePosition3 positions = shade.getShadePositions(); + if (positions != null) { + updateDynamicChannel(removeChannels, CHANNEL_SHADE_POSITION, positions.supportsPrimary()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_SECONDARY_POSITION, positions.supportsSecondary()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_VANE, positions.supportsTilt()); + } + + updateDynamicChannel(removeChannels, CHANNEL_SHADE_BATTERY_LEVEL, shade.isMainsPowered()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_LOW_BATTERY, shade.isMainsPowered()); + + if (!removeChannels.isEmpty()) { + if (logger.isDebugEnabled()) { + StringJoiner joiner = new StringJoiner(", "); + removeChannels.forEach(c -> joiner.add(c.getUID().getId())); + logger.debug("updateDynamicChannels() shadeId:{}, removing unsupported channels:{}", thisShade.getId(), + joiner.toString()); + } + updateThing(editThing().withoutChannels(removeChannels).build()); + } + } + + /** + * Update thing properties based on the data in the passed shade instance. + * + * @param shade containing the property data. + */ + private void updateProperties(Shade3 shade) { + if (shade.hasFullState()) { + thing.setProperties(Stream.of(new String[][] { // + { HDPowerViewBindingConstants.PROPERTY_NAME, shade.getName() }, + { HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, shade.getTypeString() }, + { HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES, shade.getCapabilitieString() }, + { HDPowerViewBindingConstants.PROPERTY_POWER_TYPE, shade.getPowerType() }, + { HDPowerViewBindingConstants.PROPERTY_BLE_NAME, shade.getBleName() }, + { Thing.PROPERTY_FIRMWARE_VERSION, shade.getFirmware() } // + }).collect(Collectors.toMap(data -> data[0], data -> data[1]))); + } + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java new file mode 100644 index 0000000000000..3c3c540d13cc6 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java @@ -0,0 +1,445 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.webtargets; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.net.ssl.SSLContext; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.sse.InboundSseEvent; +import javax.ws.rs.sse.SseEventSource; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets.Query; +import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Info3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; +import org.openhab.core.thing.Thing; +import org.osgi.service.jaxrs.client.SseEventSourceFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +/** + * JAX-RS targets for communicating with an HD PowerView hub Generation 3. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class HDPowerViewWebTargets3 implements Closeable { + + private static final String IDS = "ids"; + + // @formatter:off + public static final Type LIST_SHADES = new TypeToken>() {}.getType(); + public static final Type LIST_SCENES = new TypeToken>() {}.getType(); + public static final Type LIST_EVENTS =new TypeToken>() {}.getType(); + // @formatter:on + + private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets3.class); + private final String shades; + private final String scenes; + private final String sceneActivate; + private final String shadeMotion; + private final String shadeStop; + private final String shadePositions; + private final String info; + private final String automations; + + private final String register; + private final String shadeEvents; + private final String sceneEvents; + private final Gson gson = new Gson(); + private final HttpClient httpClient; + + private final ClientBuilder clientBuilder; + private final SseEventSourceFactory eventSourceFactory; + private final HDPowerViewHubHandler3 hubHandler; + + private boolean isRegistered; + + private @Nullable SseEventSource shadeEventSource; + private @Nullable SseEventSource sceneEventSource; + + /** + * Simple DTO for registering the binding with the hub. + * + * @author Andrew Fiddian-Green - Initial contribution + */ + @SuppressWarnings("unused") + private static class GatewayRegistration { + public String todo = "org.openhab.binding.hdpowerview"; // TODO + } + + /** + * Initialize the web targets + * + * @param httpClient the HTTP client (the binding) + * @param ipAddress the IP address of the server (the hub) + */ + public HDPowerViewWebTargets3(HDPowerViewHubHandler3 hubHandler, HttpClient httpClient, ClientBuilder clientBuilder, + SseEventSourceFactory eventSourceFactory, String ipAddress) { + String base = "http://" + ipAddress + "/"; + String home = base + "home/"; + + shades = home + "shades"; + scenes = home + "scenes"; + sceneActivate = home + "scenes/%d/activate"; + shadeMotion = home + "shades/%d/motion"; + shadeStop = home + "shades/stop"; + shadePositions = home + "shades/positions"; + automations = home + "automations"; + shadeEvents = home + "shades/events"; + sceneEvents = home + "scenes/events"; + + info = base + "gateway/info"; + register = "TBD"; // TODO + + this.httpClient = httpClient; + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; + this.hubHandler = hubHandler; + } + + /** + * Issue a command a activate a scene. + * + * @param sceneId the scene to be activated. + * @throws HubProcessingException if any error occurs. + */ + public void activateScene(int sceneId) throws HubProcessingException { + invoke(HttpMethod.PUT, String.format(sceneActivate, sceneId), null, null); + } + + /** + * Issue a calibrate command to a shade. + * + * @param shadeId the shade to be calibrated. + * @throws HubProcessingException if any error occurs. + */ + public void calibrateShade(int shadeId) throws HubProcessingException { + String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.CALIBRATE)); + invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); + } + + @Override + public void close() throws IOException { + SseEventSource source; + source = this.shadeEventSource; + if (source != null) { + source.close(); + this.shadeEventSource = null; + } + source = this.sceneEventSource; + if (source != null) { + source.close(); + this.sceneEventSource = null; + } + } + + /** + * Register the binding with the hub (if not already registered). + * + * @throws HubProcessingException if any error occurs. + */ + private void gatewayRegister() throws HubProcessingException { + if (!isRegistered) { + String json = gson.toJson(new GatewayRegistration()); + invoke(HttpMethod.PUT, register, null, json); + isRegistered = true; + } + } + + /** + * Get hub properties. + * + * @return a map containing the hub properties. + * @throws HubProcessingException if any error occurs. + */ + public Map getInformation() throws HubProcessingException { + String json = invoke(HttpMethod.GET, info, null, null); + try { + Info3 result = gson.fromJson(json, Info3.class); + if (result == null) { + throw new HubProcessingException("getInformation(): missing response"); + } + return Map.of( // + Thing.PROPERTY_FIRMWARE_VERSION, result.getFwVersion(), // + Thing.PROPERTY_SERIAL_NUMBER, result.getSerialNumber()); + } catch (JsonParseException e) { + throw new HubProcessingException("getFirmwareVersions(): JsonParseException"); + } + } + + /** + * Get the list of scenes. + * + * @return the list of scenes. + * @throws HubProcessingException if any error occurs. + */ + public List getScenes() throws HubProcessingException { + String json = invoke(HttpMethod.GET, scenes, null, null); + try { + List result = gson.fromJson(json, LIST_SCENES); + if (result == null) { + throw new HubProcessingException("getScenes() missing response"); + } + return result; + } catch (JsonParseException e) { + throw new HubProcessingException("getScenes() JsonParseException"); + } + } + + /** + * Get the list of scheduled events. + * + * @return the list of scheduled events. + * @throws HubProcessingException if any error occurs. + */ + public List getScheduledEvents() throws HubProcessingException { + String json = invoke(HttpMethod.GET, automations, null, null); + try { + List result = gson.fromJson(json, LIST_EVENTS); + if (result == null) { + throw new HubProcessingException("getScheduledEvents() missing response"); + } + return result; + } catch (JsonParseException e) { + throw new HubProcessingException("getScheduledEvents() JsonParseException"); + } + } + + /** + * Get the data for a single shade. + * + * @param shadeId the id of the shade to get. + * @return the shade. + * @throws HubProcessingException if any error occurs. + */ + public Shade3 getShade(int shadeId) throws HubProcessingException { + String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); + try { + Shade3 result = gson.fromJson(json, Shade3.class); + if (result == null) { + throw new HubProcessingException("getShade() missing response"); + } + return result; + } catch (JsonParseException e) { + throw new HubProcessingException("getShade() JsonParseException"); + } + } + + /** + * Get the list of shades. + * + * @return the list of shades. + * @throws HubProcessingException if any error occurs. + */ + public List getShades() throws HubProcessingException { + String json = invoke(HttpMethod.GET, shades, null, null); + try { + List result = gson.fromJson(json, LIST_SHADES); + if (result == null) { + throw new HubProcessingException("getShades() missing response"); + } + return result; + } catch (JsonParseException e) { + throw new HubProcessingException("getShades() JsonParseException"); + } + } + + /** + * Invoke a call on the hub server to retrieve information or send a command. + * + * @param method GET or PUT. + * @param url the host URL to be called. + * @param query the HTTP query parameter. + * @param jsonCommand the request command content (as a JSON string). + * @return the response content (as a JSON string). + * @throws HubProcessingException if something goes wrong. + */ + protected synchronized String invoke(HttpMethod method, String url, @Nullable Query query, + @Nullable String jsonCommand) throws HubProcessingException { + if (logger.isTraceEnabled()) { + if (query != null) { + logger.trace("invoke() method:{}, url:{}, query:{}", method, url, query); + } else { + logger.trace("invoke() method:{}, url:{}", method, url); + } + if (jsonCommand != null) { + logger.trace("invoke() request JSON:{}", jsonCommand); + } + } + Request request = httpClient.newRequest(url).method(method).header("Connection", "close").accept("*/*"); + if (query != null) { + request.param(query.getKey(), query.getValue()); + } + if (jsonCommand != null) { + request.header(HttpHeader.CONTENT_TYPE, "application/json").content(new StringContentProvider(jsonCommand)); + } + ContentResponse response; + try { + response = request.send(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } catch (TimeoutException | ExecutionException e) { + throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); + } + int statusCode = response.getStatus(); + if (statusCode != HttpStatus.OK_200) { + logger.warn("invoke() HTTP status:{}, reason:{}", statusCode, response.getReason()); + throw new HubProcessingException(String.format("HTTP %d error", statusCode)); + } + String jsonResponse = response.getContentAsString(); + if (logger.isTraceEnabled()) { + logger.trace("invoke() response JSON:{}", jsonResponse); + } + if (jsonResponse == null || jsonResponse.isEmpty()) { + logger.warn("invoke() hub returned no content"); + throw new HubProcessingException("Missing response entity"); + } + return jsonResponse; + } + + /** + * Issue a jog command to a shade. + * + * @param shadeId the shade to be jogged. + * @throws HubProcessingException if any error occurs. + */ + public void jogShade(int shadeId) throws HubProcessingException { + String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.JOG)); + invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); + } + + /** + * Issue a ccommand to move a shade. + * + * @param shadeId the shade to be moved. + * @param position the new position. + * @throws HubProcessingException if any error occurs. + */ + public void moveShade(int shadeId, ShadePosition3 position) throws HubProcessingException { + invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), + gson.toJson(position)); + } + + /** + * Handle inbound SSE events for a scene. + * + * @param sseEvent the inbound event. + */ + private void onSceneEvent(InboundSseEvent sseEvent) { + String json = sseEvent.readData(); + logger.trace("onSceneEvent() json:{}", json); + SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); + if (sceneEvent != null) { + Scene3 scene = sceneEvent.getScene(); + hubHandler.onSceneEvent(scene); + } + } + + /** + * Handle inbound SSE events for a shade. + * + * @param sseEvent the inbound event. + */ + private void onShadeEvent(InboundSseEvent sseEvent) { + String json = sseEvent.readData(); + logger.trace("onShadeEvent() json:{}", json); + ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); + if (shadeEvent != null) { + ShadePosition3 positions = shadeEvent.getCurrentPositions(); + hubHandler + .onShadeEvent(new Shade3().setId(shadeEvent.getId()).setShadePosition(positions).setPartialState()); + } + } + + /** + * Open the SSE subscriptions. + * + * @return true if registered for SSE events. + * @throws HubProcessingException if any error occurs. + */ + public void openSSE() throws HubProcessingException { + SseEventSource shadeEventSource = this.shadeEventSource; + SseEventSource sceneEventSource = this.sceneEventSource; + + if (shadeEventSource == null || !shadeEventSource.isOpen() || sceneEventSource == null + || !sceneEventSource.isOpen()) { + + try { + close(); + } catch (IOException e) { + } + + // register ourself with the gateway (if necessary) + gatewayRegister(); + + SSLContext context = httpClient.getSslContextFactory().getSslContext(); + WebTarget target; + + // open SSE channel for shades + target = clientBuilder.sslContext(context).build().target(shadeEvents); + shadeEventSource = eventSourceFactory.newSource(target); + shadeEventSource.register((event) -> onShadeEvent(event)); + shadeEventSource.open(); + this.shadeEventSource = shadeEventSource; + + // open SSE channel for scenes + target = clientBuilder.sslContext(context).build().target(sceneEvents); + sceneEventSource = eventSourceFactory.newSource(target); + sceneEventSource.register((event) -> onSceneEvent(event)); + sceneEventSource.open(); + this.sceneEventSource = sceneEventSource; + } + } + + /** + * Issue a stop command to a shade. + * + * @param shadeId the shade to be stopped. + * @throws HubProcessingException if any error occurs. + */ + public void stopShade(int shadeId) throws HubProcessingException { + invoke(HttpMethod.PUT, shadeStop, Query.of(IDS, Integer.valueOf(shadeId).toString()), null); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index db0e2f403e286..b3991f3aad3bd 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -19,37 +19,28 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; -import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; -import org.openhab.binding.hdpowerview.internal._v3.SseSinkV3; import org.openhab.binding.hdpowerview.internal.api.Firmware; import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadeDataV3; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder; @@ -76,7 +67,6 @@ import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,15 +79,13 @@ * @author Jacob Laursen - Added support for scene groups and automations */ @NonNullByDefault -public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV3 { +public class HDPowerViewHubHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler.class); private final HttpClient httpClient; private final HDPowerViewTranslationProvider translationProvider; private final ConcurrentHashMap pendingShadeInitializations = new ConcurrentHashMap<>(); private final Duration firmwareVersionValidityPeriod = Duration.ofDays(1); - private final ClientBuilder clientBuilder; - private final SseEventSourceFactory eventSourceFactory; private long refreshInterval; private long hardRefreshPositionInterval; @@ -124,13 +112,10 @@ public class HDPowerViewHubHandler extends BaseBridgeHandler implements SseSinkV HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED); public HDPowerViewHubHandler(Bridge bridge, HttpClient httpClient, - HDPowerViewTranslationProvider translationProvider, ClientBuilder clientBuilder, - SseEventSourceFactory eventSourceFactory) { + HDPowerViewTranslationProvider translationProvider) { super(bridge); this.httpClient = httpClient; this.translationProvider = translationProvider; - this.clientBuilder = clientBuilder; - this.eventSourceFactory = eventSourceFactory; } @Override @@ -178,14 +163,8 @@ public void initialize() { return; } - try { - webTargets = newWebTargets(host); - } catch (InstantiationException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); - return; - } - pendingShadeInitializations.clear(); + webTargets = new HDPowerViewWebTargets(httpClient, host); refreshInterval = config.refresh; hardRefreshPositionInterval = config.hardRefresh; hardRefreshBatteryLevelInterval = config.hardRefreshBatteryLevel; @@ -248,12 +227,7 @@ public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { } private void schedulePoll() { - if (sseSubscribe(this)) { - scheduler.submit(this::poll); // do a single poll to fetch the initial state - } else { - scheduleSoftPoll(); - } - // do hard polls (even on generation 3) in case SSE subscriptions have dropped + scheduleSoftPoll(); scheduleHardPoll(); } @@ -306,8 +280,6 @@ private synchronized void stopPoll() { future.cancel(true); } this.hardRefreshBatteryLevelFuture = null; - - sseSubscribe(null); } private synchronized void poll() { @@ -450,7 +422,7 @@ private void updateUnknownShadeThing(Thing thing) { HDPowerViewShadeHandler thingHandler = ((HDPowerViewShadeHandler) thing.getHandler()); if (thingHandler == null) { logger.debug("Shade '{}' handler not initialized", shadeId); - pendingShadeInitializations.put(thing.getUID(), newShadeData()); + pendingShadeInitializations.put(thing.getUID(), new ShadeData()); return; } ThingStatus thingStatus = thingHandler.getThing().getStatus(); @@ -464,7 +436,7 @@ private void updateUnknownShadeThing(Thing thing) { case UNINITIALIZED: case INITIALIZING: logger.debug("Shade '{}' handler not yet ready; status: {}", shadeId, thingStatus); - pendingShadeInitializations.put(thing.getUID(), newShadeData()); + pendingShadeInitializations.put(thing.getUID(), new ShadeData()); break; case REMOVING: case REMOVED: @@ -683,9 +655,6 @@ private void requestRefreshShadePositions() { logger.debug("Shade '{}' handler not initialized", shadeId); } } - - // re-subscribe (in case SSE connections went down) - sseSubscribe(this); } private void requestRefreshShadeBatteryLevels() { @@ -706,87 +675,4 @@ private void requestRefreshShadeBatteryLevels() { } } } - - /** - * Instantiate the web targets. - * - * @param host the ip address - * @return instance of HDPowerViewWebTargets class (either V1 or V3). - * @throws InstantiationException if neither a V1 nor a V3 web target was instantiated. - */ - private HDPowerViewWebTargets newWebTargets(String host) throws InstantiationException { - HDPowerViewWebTargets webTargets = this.webTargets; - if (webTargets != null) { - return webTargets; - } - HDPowerViewHubConfiguration config = getConfigAs(HDPowerViewHubConfiguration.class); - int hubGeneration = config.generation; - switch (hubGeneration) { - case 0: // for non breaking of existing installations - case 1: // both generation 1 and 2 hubs use V1 web targets - case 2: - webTargets = new HDPowerViewWebTargetsV1(httpClient, clientBuilder, eventSourceFactory, host); - break; - case 3: // generation 3 hubs use V3 web targets - webTargets = new HDPowerViewWebTargetsV3(httpClient, clientBuilder, eventSourceFactory, host); - } - if (webTargets != null) { - this.webTargets = webTargets; - return webTargets; - } - throw new InstantiationException("Unable to instantiate the web targets"); - } - - /** - * Check if gateway is generation 1 - * - * @return true if gateway is generation 1 - */ - private boolean isGeneration1() { - return webTargets instanceof HDPowerViewWebTargetsV1; - } - - /** - * Create a new ShadeData instance; either V1 or V3 depending on the gateway generation. - * - * @return new ShadeData instance. - */ - private ShadeData newShadeData() { - return isGeneration1() ? new ShadeDataV1() : new ShadeDataV3(); - } - - /** - * If the gateway is generation 3 try to (un)subscribe to SSE on it. If 'sseSinK' is not null, make the - * subscription, otherwise cancel it. - * - * @param sseSinK the sink for the SSE call backs (may be null). - * @return true if the subscription succeeded. - */ - private boolean sseSubscribe(@Nullable SseSinkV3 sseSinK) { - if (webTargets instanceof HDPowerViewWebTargetsV3) { - try { - return ((HDPowerViewWebTargetsV3) webTargets).sseSubscribe(sseSinK); - } catch (HubMaintenanceException | HubProcessingException e) { - logger.warn("Failed to {}subscribe for SSE '{}'", sseSinK == null ? "un-" : "", e.getMessage()); - } - } - return false; - } - - @Override - public void sseShade(String evt, int shadeId, ShadePosition shadePosition) { - Optional thing = getShadeThingIdMap().entrySet().stream() - .filter(e -> e.getValue().equals(Integer.valueOf(shadeId))).findFirst().map(Map.Entry::getKey); - if (thing.isPresent()) { - ThingHandler handler = thing.get().getHandler(); - if (handler instanceof HDPowerViewShadeHandler) { - ((HDPowerViewShadeHandler) handler).sseShadePosition(shadePosition); - } - } - } - - @Override - public void sseScene(String evt, int sceneId) { - // TODO perhaps we don't need to do anything? - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java index 51be5b55cc09c..53e77e0425cdb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java @@ -32,12 +32,9 @@ import org.openhab.binding.hdpowerview.internal.api.BatteryKind; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadeDataV1; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; @@ -98,8 +95,6 @@ private enum RefreshKind { private int shadeId; private boolean isDisposing; - private boolean isGeneration1 = true; - public HDPowerViewShadeHandler(Thing thing) { super(thing); } @@ -255,7 +250,6 @@ private void handleShadeCommand(String channelId, Command command, HDPowerViewWe * @param shadeData the ShadeData to be used. */ protected void onReceiveUpdate(ShadeData shadeData) { - isGeneration1 = shadeData.version() == 1; updateStatus(ThingStatus.ONLINE); updateCapabilities(shadeData); updateSoftProperties(shadeData); @@ -264,7 +258,7 @@ protected void onReceiveUpdate(ShadeData shadeData) { if (shadePosition != null) { updatePositionStates(shadePosition); } - updateBatteryStates(shadeData); + updateBatteryStates(shadeData.batteryStatus, shadeData.batteryStrength); updateSignalStrengthState(shadeData.signalStrength); } @@ -340,7 +334,7 @@ private void updateSoftProperties(ShadeData shadeData) { private void updateFirmwareProperties(ShadeData shadeData) { Map properties = editProperties(); Firmware shadeFirmware = shadeData.firmware; - Firmware motorFirmware = (shadeData.version() == 1) ? ((ShadeDataV1) shadeData).motor : null; + Firmware motorFirmware = shadeData.motor; if (shadeFirmware != null) { properties.put(Thing.PROPERTY_FIRMWARE_VERSION, shadeFirmware.toString()); } @@ -405,9 +399,8 @@ private void updatePositionStates(ShadePosition shadePos) { updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePos.getState(capabilities, SECONDARY_POSITION)); } - private void updateBatteryStates(ShadeData shadeData) { - updateBatteryLevelStates(shadeData.batteryStatus); - double batteryStrength = shadeData.version() == 1 ? ((ShadeDataV1) shadeData).batteryStrength : 0; + private void updateBatteryStates(int batteryStatus, double batteryStrength) { + updateBatteryLevelStates(batteryStatus); updateState(CHANNEL_SHADE_BATTERY_VOLTAGE, batteryStrength > 0 ? new QuantityType<>(batteryStrength / 10, Units.VOLT) : UnDefType.UNDEF); } @@ -448,7 +441,7 @@ private void moveShade(CoordinateSystem coordSys, int newPercent, HDPowerViewWeb newPosition = shadeData.positions; // if no positions returned, then create a new position if (newPosition == null) { - newPosition = newShadePosition(); + newPosition = new ShadePosition(); } Capabilities capabilities = getCapabilitiesOrDefault(); // set the new position value, and write the positions to the hub @@ -577,7 +570,7 @@ private void doRefreshShade(RefreshKind kind) { break; case BATTERY_LEVEL: shadeData = webTargets.refreshShadeBatteryLevel(shadeId); - updateBatteryStates(shadeData); + updateBatteryStates(shadeData.batteryStatus, shadeData.batteryStrength); break; default: throw new NotSupportedException("Unsupported refresh kind " + kind.toString()); @@ -655,24 +648,4 @@ private void updateDynamicChannels(Capabilities capabilities, ShadeData shade) { updateThing(editThing().withoutChannels(removeList).build()); } } - - private ShadePosition newShadePosition() { - return isGeneration1 ? new ShadePositionV1() : new ShadePositionV3(); - } - - /** - * Update position states with the new position provided by an SSE event. - * - * @param shadePosition the new position - */ - public void sseShadePosition(ShadePosition shadePosition) { - if (thing.getStatus() == ThingStatus.ONLINE) { - Capabilities capabilities = this.capabilities; - if (capabilities != null) { - updateState(CHANNEL_SHADE_POSITION, shadePosition.getState(capabilities, PRIMARY_POSITION)); - updateState(CHANNEL_SHADE_VANE, shadePosition.getState(capabilities, VANE_TILT_POSITION)); - updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePosition.getState(capabilities, SECONDARY_POSITION)); - } - } - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index cc4d76d24ddf4..c118891fa9855 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -5,6 +5,8 @@ binding.hdpowerview.description = The Hunter Douglas PowerView binding provides # thing types +thing-type.hdpowerview.gen3.label = PowerView Hub Generation 3 +thing-type.hdpowerview.gen3.description = Hunter Douglas (Luxaflex) PowerView Hub Generation 3 thing-type.hdpowerview.hub.label = PowerView Hub thing-type.hdpowerview.hub.description = Hunter Douglas (Luxaflex) PowerView Hub thing-type.hdpowerview.repeater.label = PowerView Repeater @@ -19,11 +21,17 @@ thing-type.hdpowerview.shade.channel.repeaterRssi.label = Repeater RSSI thing-type.hdpowerview.shade.channel.repeaterRssi.description = Received Signal Strength Indicator for Repeater thing-type.hdpowerview.shade.channel.secondary.label = Secondary Position thing-type.hdpowerview.shade.channel.secondary.description = The secondary vertical position (on top-down/bottom-up shades) +thing-type.hdpowerview.shade3.label = PowerView Shade +thing-type.hdpowerview.shade3.description = Hunter Douglas (Luxaflex) PowerView Shade +thing-type.hdpowerview.shade3.channel.secondary.label = Secondary Position +thing-type.hdpowerview.shade3.channel.secondary.description = The secondary vertical position (on top-down/bottom-up shades) # thing types config -thing-type.config.hdpowerview.hub.generation.label = Hub Generation -thing-type.config.hdpowerview.hub.generation.description = The PowerView Hub Generation +thing-type.config.hdpowerview.gen3.hardRefresh.label = Hard Refresh Interval +thing-type.config.hdpowerview.gen3.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Hub +thing-type.config.hdpowerview.gen3.host.label = Host +thing-type.config.hdpowerview.gen3.host.description = The Host address of the PowerView Hub thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index b752d79e370a7..d438d8646ebea 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -27,11 +27,6 @@ The Host address of the PowerView Hub network-address - - - The PowerView Hub Generation - 0 - The number of milliseconds between fetches of the PowerView Hub shade state @@ -52,4 +47,33 @@ + + + Hunter Douglas (Luxaflex) PowerView Hub Generation 3 + + + + + + + Hunter Douglas (Luxaflex) + PowerView Hub + + + host + + + + + The Host address of the PowerView Hub + network-address + + + + The number of minutes between hard refreshes from the PowerView Hub + 180 + + + + diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml index a8dd168767b77..53d64243d74a8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml @@ -43,4 +43,34 @@ + + + + + + Hunter Douglas (Luxaflex) PowerView Shade + + + + + + The secondary vertical position (on top-down/bottom-up shades) + + + + + + + + + + Hunter Douglas (Luxaflex) + PowerView Motorized Shade + + + id + + + + diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java index 8c92ec0fd366c..ba6cb99574fc4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java @@ -24,12 +24,10 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.ScheduledEventV1; +import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; @@ -67,7 +65,7 @@ private void setUp() { logger.setLevel(Level.OFF); builder = AutomationChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID); - Scene scene = new SceneV1(); + Scene scene = new Scene(); scene.id = 1; scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes()); scenes = new ArrayList<>(List.of(scene)); @@ -80,7 +78,7 @@ private void setUp() { @Test public void sceneSunriseWeekends() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.daySaturday = true; scheduledEvent.daySunday = true; @@ -93,7 +91,7 @@ public void sceneSunriseWeekends() { @Test public void sceneSunsetWeekdays() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.dayWednesday = true; @@ -109,7 +107,7 @@ public void sceneSunsetWeekdays() { @Test public void sceneTimeAllDays() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.dayWednesday = true; @@ -129,7 +127,7 @@ public void sceneTimeAllDays() { @Test public void sceneMinutesBeforeSunriseMondayTuesday() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.dayMonday = true; scheduledEvent.dayTuesday = true; scheduledEvent.minute = -15; @@ -143,7 +141,7 @@ public void sceneMinutesBeforeSunriseMondayTuesday() { @Test public void sceneHoursMinutesAfterSunriseMonday() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.dayMonday = true; scheduledEvent.minute = 61; @@ -156,7 +154,7 @@ public void sceneHoursMinutesAfterSunriseMonday() { @Test public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayWednesday = true; scheduledEvent.dayThursday = true; scheduledEvent.dayFriday = true; @@ -171,7 +169,7 @@ public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() { @Test public void sceneHourAfterSunsetFridaySaturdaySunday() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET); scheduledEvent.dayFriday = true; scheduledEvent.daySaturday = true; scheduledEvent.daySunday = true; @@ -226,7 +224,7 @@ public void emptyListWhenNoScenesOrSceneCollections() { @Test public void emptyListWhenNoSceneForScheduledEvent() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithSceneCollection( + ScheduledEvent scheduledEvent = createScheduledEventWithSceneCollection( ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build(); @@ -236,7 +234,7 @@ public void emptyListWhenNoSceneForScheduledEvent() { @Test public void emptyListWhenNoSceneCollectionForScheduledEvent() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents) @@ -247,7 +245,7 @@ public void emptyListWhenNoSceneCollectionForScheduledEvent() { @Test public void groupAndIdAreCorrect() { - ScheduledEventV1 scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); + ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE); scheduledEvent.id = 42; List scheduledEvents = new ArrayList<>(List.of(scheduledEvent)); List channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build(); @@ -257,16 +255,16 @@ public void groupAndIdAreCorrect() { assertEquals(Integer.toString(scheduledEvent.id), channels.get(0).getUID().getIdWithoutGroup()); } - private ScheduledEventV1 createScheduledEventWithScene(int eventType) { - ScheduledEventV1 scheduledEvent = new ScheduledEventV1(); + private ScheduledEvent createScheduledEventWithScene(int eventType) { + ScheduledEvent scheduledEvent = new ScheduledEvent(); scheduledEvent.id = 1; scheduledEvent.sceneId = scenes.get(0).id; scheduledEvent.eventType = eventType; return scheduledEvent; } - private ScheduledEventV1 createScheduledEventWithSceneCollection(int eventType) { - ScheduledEventV1 scheduledEvent = new ScheduledEventV1(); + private ScheduledEvent createScheduledEventWithSceneCollection(int eventType) { + ScheduledEvent scheduledEvent = new ScheduledEvent(); scheduledEvent.id = 1; scheduledEvent.sceneCollectionId = sceneCollections.get(0).id; scheduledEvent.eventType = eventType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java deleted file mode 100644 index b7474b6c251b7..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/Generation3DtoTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview; - -import static org.junit.jupiter.api.Assertions.*; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; -import java.util.List; - -import javax.ws.rs.client.ClientBuilder; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jetty.client.HttpClient; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal._v3.HDPowerViewWebTargetsV3; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api._v3.ShadePositionV3; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; -import org.openhab.core.library.types.PercentType; -import org.openhab.core.types.UnDefType; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; - -/** - * Unit tests for Generation 3 DTO's. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class Generation3DtoTest { - - private final HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV3(new HttpClient(), - Mockito.mock(ClientBuilder.class), Mockito.mock(SseEventSourceFactory.class), ""); - private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); - - private String loadJson(String filename) throws IOException { - try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { - if (inputStream == null) { - throw new IOException("inputstream is null"); - } - byte[] bytes = inputStream.readAllBytes(); - if (bytes == null) { - throw new IOException("Resulting byte-array empty"); - } - return new String(bytes, StandardCharsets.UTF_8); - } - } - - /** - * Test JSON scenes response. - */ - @Test - public void testScenesParsing() throws IOException { - try { - Method method = webTargets.getClass().getDeclaredMethod("toScenes", String.class); - method.setAccessible(true); - String json = loadJson("_v3/scenes.json"); - Object result = method.invoke(webTargets, json); - assertTrue(result instanceof Scenes); - List sceneData = ((Scenes) result).sceneData; - assertNotNull(sceneData); - assertEquals(1, sceneData.size()); - Scene scene = sceneData.get(0); - assertEquals("Open All Office Shades\n ABC", scene.getName()); - assertEquals(234, scene.id); - assertEquals(3, scene.version()); - } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException - | InvocationTargetException e) { - fail(e.getMessage()); - } - } - - /** - * Test JSON shades response. - */ - @Test - public void testShadesParsing() throws IOException { - try { - Method method = webTargets.getClass().getDeclaredMethod("toShades", String.class); - method.setAccessible(true); - String json = loadJson("_v3/shades.json"); - Object result = method.invoke(webTargets, json); - assertTrue(result instanceof Shades); - List shadeDataList = ((Shades) result).shadeData; - assertNotNull(shadeDataList); - assertEquals(1, shadeDataList.size()); - ShadeData shadeData = shadeDataList.get(0); - assertEquals("Shade 2 ABC", shadeData.getName()); - assertEquals(789, shadeData.id); - assertEquals(3, shadeData.version()); - } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException - | InvocationTargetException e) { - fail(e.getMessage()); - } - } - - /** - * Test JSON automation response. - */ - @Test - public void testAutomationParsing() throws IOException { - try { - Method method = webTargets.getClass().getDeclaredMethod("toScheduledEvents", String.class); - method.setAccessible(true); - String json = loadJson("_v3/automations.json"); - Object result = method.invoke(webTargets, json); - assertTrue(result instanceof ScheduledEvents); - List scheduledEventList = ((ScheduledEvents) result).scheduledEventData; - assertNotNull(scheduledEventList); - assertEquals(1, scheduledEventList.size()); - ScheduledEvent scheduledEvent = scheduledEventList.get(0); - assertEquals(33, scheduledEvent.id); - assertTrue(scheduledEvent.enabled); - } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException - | InvocationTargetException e) { - fail(e.getMessage()); - } - } - - /** - * Test JSON shade event response. - */ - @Test - public void testShadeEventParsing() throws IOException { - try { - Method method = webTargets.getClass().getDeclaredMethod("toShadeData2", String.class); - method.setAccessible(true); - String json = loadJson("_v3/shade-event.json"); - Object result = method.invoke(webTargets, json); - assertTrue(result instanceof ShadeData); - ShadeData shadeData = ((ShadeData) result); - assertEquals(3, shadeData.version()); - ShadePosition position = shadeData.positions; - assertNotNull(position); - assertEquals(PercentType.valueOf("99"), - position.getState(DB.getCapabilities(0), CoordinateSystem.PRIMARY_POSITION)); - assertEquals(PercentType.valueOf("98"), - position.getState(DB.getCapabilities(0), CoordinateSystem.SECONDARY_POSITION)); - assertEquals(PercentType.ZERO, - position.getState(DB.getCapabilities(0), CoordinateSystem.VANE_TILT_POSITION)); - } catch (SecurityException | NoSuchMethodException | IllegalArgumentException | IllegalAccessException - | InvocationTargetException e) { - fail(e.getMessage()); - } - } - - /** - * Test JSON shade position setting. - */ - @Test - public void testShadePositions() { - ShadePositionV3 pos; - Capabilities caps; - - caps = DB.getCapabilities(0); // test with only primary support - pos = new ShadePositionV3(); - pos.setPosition(caps, CoordinateSystem.PRIMARY_POSITION, 11); - pos.setPosition(caps, CoordinateSystem.SECONDARY_POSITION, 22); - pos.setPosition(caps, CoordinateSystem.VANE_TILT_POSITION, 33); - assertEquals(PercentType.valueOf("11"), pos.getState(caps, CoordinateSystem.PRIMARY_POSITION)); - assertEquals(UnDefType.UNDEF, pos.getState(caps, CoordinateSystem.SECONDARY_POSITION)); - assertEquals(UnDefType.UNDEF, pos.getState(caps, CoordinateSystem.VANE_TILT_POSITION)); - - caps = DB.getCapabilities(9);// test with primary, secondary, and tilt support - pos = new ShadePositionV3(); - pos.setPosition(caps, CoordinateSystem.PRIMARY_POSITION, 11); - pos.setPosition(caps, CoordinateSystem.SECONDARY_POSITION, 22); - pos.setPosition(caps, CoordinateSystem.VANE_TILT_POSITION, 33); - assertEquals(PercentType.valueOf("11"), pos.getState(caps, CoordinateSystem.PRIMARY_POSITION)); - assertEquals(PercentType.valueOf("22"), pos.getState(caps, CoordinateSystem.SECONDARY_POSITION)); - assertEquals(PercentType.valueOf("33"), pos.getState(caps, CoordinateSystem.VANE_TILT_POSITION)); - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java index e59f341cdefd1..19cd35c7c33ee 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java @@ -21,27 +21,21 @@ import java.util.List; import java.util.Objects; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; import com.google.gson.Gson; @@ -54,8 +48,7 @@ @NonNullByDefault public class HDPowerViewJUnitTests { - private final Gson gson = new HDPowerViewWebTargetsV1(new HttpClient(), Mockito.mock(ClientBuilder.class), - Mockito.mock(SseEventSourceFactory.class), "").getGson(); + private Gson gson = new Gson(); private T getObjectFromJson(String filename, Class clazz) throws IOException { try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { @@ -76,7 +69,7 @@ private T getObjectFromJson(String filename, Class clazz) throws IOExcept */ @Test public void shadeNameIsDecoded() throws IOException { - Shades shades = getObjectFromJson("_v1/shades.json", Shades.class); + Shades shades = getObjectFromJson("shades.json", Shades.class); List shadeData = shades.shadeData; assertNotNull(shadeData); assertEquals(3, shadeData.size()); @@ -89,7 +82,7 @@ public void shadeNameIsDecoded() throws IOException { */ @Test public void testBatteryKind() throws IOException { - Shades shades = getObjectFromJson("_v1/shades.json", Shades.class); + Shades shades = getObjectFromJson("shades.json", Shades.class); List shadeData = shades.shadeData; assertNotNull(shadeData); ShadeData shade = shadeData.get(0); @@ -103,7 +96,7 @@ public void testBatteryKind() throws IOException { */ @Test public void sceneNameIsDecoded() throws IOException { - Scenes scenes = getObjectFromJson("_v1/scenes.json", Scenes.class); + Scenes scenes = getObjectFromJson("scenes.json", Scenes.class); List sceneData = scenes.sceneData; assertNotNull(sceneData); assertEquals(4, sceneData.size()); @@ -116,7 +109,7 @@ public void sceneNameIsDecoded() throws IOException { */ @Test public void sceneCollectionNameIsDecoded() throws IOException { - SceneCollections sceneCollections = getObjectFromJson("_v1/sceneCollections.json", SceneCollections.class); + SceneCollections sceneCollections = getObjectFromJson("sceneCollections.json", SceneCollections.class); List sceneCollectionData = sceneCollections.sceneCollectionData; assertNotNull(sceneCollectionData); @@ -131,7 +124,7 @@ public void sceneCollectionNameIsDecoded() throws IOException { */ @Test public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws IOException { - Shades shades = getObjectFromJson("_v1/duette.json", Shades.class); + Shades shades = getObjectFromJson("duette.json", Shades.class); List shadesData = shades.shadeData; assertNotNull(shadesData); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java index 588fcfa9ef23f..b8670bb91ec92 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java @@ -13,25 +13,20 @@ package org.openhab.binding.hdpowerview; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.PRIMARY_POSITION; +import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; import java.util.List; import java.util.regex.Pattern; -import javax.ws.rs.client.ClientBuilder; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal._v1.HDPowerViewWebTargetsV1; -import org.openhab.binding.hdpowerview.internal.api.ShadeData; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.api.responses.Shades; +import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; @@ -39,7 +34,6 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; -import org.osgi.service.jaxrs.client.SseEventSourceFactory; /** * Unit tests for HD PowerView binding. @@ -89,8 +83,7 @@ public void testOnlineCommunication() { fail(e.getMessage()); } - HDPowerViewWebTargets webTargets = new HDPowerViewWebTargetsV1(client, Mockito.mock(ClientBuilder.class), - Mockito.mock(SseEventSourceFactory.class), hubIPAddress); + HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress); assertNotNull(webTargets); int shadeId = 0; @@ -175,7 +168,7 @@ public void testOnlineCommunication() { int position = ((PercentType) pos).intValue(); position = position + ((position <= 10) ? 5 : -5); - ShadePosition targetPosition = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, + ShadePosition targetPosition = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, position); assertNotNull(targetPosition); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java index 2b96fa966540e..eca2a1f2e9ebb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java @@ -24,8 +24,7 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses._v1.SceneV1; +import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; @@ -111,7 +110,7 @@ public void emptyListWhenNoScenes() { } private List createScenes() { - Scene scene = new SceneV1(); + Scene scene = new Scene(); scene.id = 1; scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes()); return new ArrayList<>(List.of(scene)); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java index 55688bccf1c65..50dcd0e7ff4c2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java @@ -18,7 +18,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api._v1.ShadePositionV1; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.core.library.types.PercentType; @@ -91,7 +90,7 @@ private void assertShadePosition(State position, int value) { @Test public void testCaps1ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -105,7 +104,7 @@ public void testCaps1ShadePositionParsingFullyUp() { @Test public void testCaps1ShadePositionParsingShadeFullyDown1() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -119,7 +118,7 @@ public void testCaps1ShadePositionParsingShadeFullyDown1() { @Test public void testCaps1ShadePositionParsingShadeFullyDown2() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 0); + ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -133,7 +132,7 @@ public void testCaps1ShadePositionParsingShadeFullyDown2() { @Test public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() { Capabilities capabilities = db.getCapabilities(1); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 88); + ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 88); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -148,7 +147,7 @@ public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() { @Test public void testDualRailConstraints() { Capabilities capabilities = db.getCapabilities(7); - ShadePosition test = new ShadePositionV1(); + ShadePosition test = new ShadePosition(); // ==== OK !! primary at bottom, secondary at top ==== test.setPosition(capabilities, PRIMARY_POSITION, 100).setPosition(capabilities, SECONDARY_POSITION, 0); @@ -208,42 +207,42 @@ public void testDuoliteShadePositionParsing() { ShadePosition test; // both shades up - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 50% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 50); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 50); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 50); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 0% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 0% down (ALTERNATE) - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 50% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 50); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 50); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 50); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down, back shade 100% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 100); @@ -261,70 +260,70 @@ public void testDuoliteTiltShadePositionParsing() { ShadePosition test; // front shade up - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 30% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 30); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF); // front shade 100% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0); // tilt 0% - test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0); // tilt 30% - test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 30); + test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30); // tilt 100% - test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 0% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 0); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 30% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 30); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 30); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // back shade 100% down - test = new ShadePositionV1().setPosition(capabilities, SECONDARY_POSITION, 100); + test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 100); assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100); // test constraints on impossible values: primary 30% => tilt 30% - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30).setPosition(capabilities, + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30).setPosition(capabilities, VANE_TILT_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); @@ -332,7 +331,7 @@ public void testDuoliteTiltShadePositionParsing() { assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30); // test constraints on impossible values: primary 30% => tilt 30% => back shade 30% down - test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 30) + test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30) .setPosition(capabilities, VANE_TILT_POSITION, 30).setPosition(capabilities, SECONDARY_POSITION, 30); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); @@ -347,7 +346,7 @@ public void testDuoliteTiltShadePositionParsing() { @Test public void testCaps0ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(0); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -361,7 +360,7 @@ public void testCaps0ShadePositionParsingFullyUp() { @Test public void testCap0ShadePositionParsingShadeFullyDown() { Capabilities capabilities = db.getCapabilities(0); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 100); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -385,7 +384,7 @@ private void assertShadePosition(State actual, State target) { @Test public void testType44ShadePositionParsingFullyUp() { Capabilities capabilities = db.getCapabilities(44, null); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, PRIMARY_POSITION, 0); + ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); @@ -399,7 +398,7 @@ public void testType44ShadePositionParsingFullyUp() { @Test public void testType44ShadePositionParsingShadeFullyDownVaneOpen() { Capabilities capabilities = db.getCapabilities(44, null); - ShadePosition test = new ShadePositionV1().setPosition(capabilities, VANE_TILT_POSITION, 88); + ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 88); assertNotNull(test); assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100); assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), UnDefType.UNDEF); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java new file mode 100644 index 0000000000000..d6985fd9894d3 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2010-2022 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.hdpowerview.gen3; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; +import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.UnDefType; + +import com.google.gson.Gson; + +/** + * Unit tests for Generation 3 DTO's. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +@NonNullByDefault +public class Generation3DtoTest { + + private final Gson gson = new Gson(); + + private String loadJson(String filename) throws IOException { + try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { + if (inputStream == null) { + throw new IOException("inputstream is null"); + } + byte[] bytes = inputStream.readAllBytes(); + if (bytes == null) { + throw new IOException("Resulting byte-array empty"); + } + return new String(bytes, StandardCharsets.UTF_8); + } + } + + /** + * Test JSON automation response. + */ + @Test + public void testAutomationParsing() throws IOException { + String json = loadJson("gen3/automations.json"); + List scheduledEventList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_EVENTS); + assertNotNull(scheduledEventList); + assertEquals(1, scheduledEventList.size()); + ScheduledEvent3 scheduledEvent = scheduledEventList.get(0); + assertEquals(33, scheduledEvent.id); + assertTrue(scheduledEvent.enabled); + } + + /** + * Test JSON scene event response. + */ + @Test + public void testSceneEventParsing() throws IOException { + String json = loadJson("gen3/scene-event.json"); + SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); + assertNotNull(sceneEvent); + Scene3 scene = sceneEvent.getScene(); + assertNotNull(scene); + assertEquals("Open All Office Shades\n Open All Office Shades", scene.getName()); + assertEquals(234, scene.getId()); + } + + /** + * Test JSON scenes response. + */ + @Test + public void testScenesParsing() throws IOException { + String json = loadJson("gen3/scenes.json"); + List sceneList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_SCENES); + assertNotNull(sceneList); + assertEquals(1, sceneList.size()); + Scene3 scene = sceneList.get(0); + assertEquals("Open All Office Shades\n ABC", scene.getName()); + assertEquals(234, scene.getId()); + } + + /** + * Test JSON shade event response. + */ + @Test + public void testShadeEventParsing() throws IOException { + String json = loadJson("gen3/shade-event.json"); + ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); + assertNotNull(shadeEvent); + ShadePosition3 position = shadeEvent.getCurrentPositions(); + assertNotNull(position); + assertEquals(PercentType.valueOf("99"), position.getState(CoordinateSystem.PRIMARY_POSITION)); + assertEquals(PercentType.valueOf("98"), position.getState(CoordinateSystem.SECONDARY_POSITION)); + assertEquals(PercentType.ZERO, position.getState(CoordinateSystem.VANE_TILT_POSITION)); + } + + /** + * Test JSON shade position setting. + */ + @Test + public void testShadePositions() { + ShadePosition3 pos; + + pos = new ShadePosition3(); + pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); + assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION)); + assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.SECONDARY_POSITION)); + assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.VANE_TILT_POSITION)); + + pos = new ShadePosition3(); + pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); + pos.setPosition(CoordinateSystem.SECONDARY_POSITION, 22); + pos.setPosition(CoordinateSystem.VANE_TILT_POSITION, 33); + assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION)); + assertEquals(PercentType.valueOf("22"), pos.getState(CoordinateSystem.SECONDARY_POSITION)); + assertEquals(PercentType.valueOf("33"), pos.getState(CoordinateSystem.VANE_TILT_POSITION)); + } + + /** + * Test JSON shades response. + */ + @Test + public void testShadesParsing() throws IOException { + String json = loadJson("gen3/shades.json"); + List shadeList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_SHADES); + assertNotNull(shadeList); + assertEquals(1, shadeList.size()); + Shade3 shadeData = shadeList.get(0); + assertEquals("Shade 2 ABC", shadeData.getName()); + assertEquals(789, shadeData.getId()); + } +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/duette.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/duette.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/automations.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/automations.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/automations.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scene-event.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scene-event.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scene-event.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shade-event.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shade-event.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shade-event.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v3/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shades.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/sceneCollections.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/sceneCollections.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/_v1/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json From 152330fa8fbae1eb139e9244a486deefbc50f399 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 26 Oct 2022 19:51:04 +0100 Subject: [PATCH 36/64] [hdpowerview] cosmetics Signed-off-by: Andrew Fiddian-Green --- .../HDPowerViewDeviceDiscoveryServiceV3.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java index f7051465b6e20..b736015a46aeb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java @@ -13,7 +13,6 @@ package org.openhab.binding.hdpowerview.internal.gen3.discovery; import java.util.Collections; -import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -89,19 +88,19 @@ private Runnable createScanner() { } private void discoverShades(HDPowerViewWebTargets3 webTargets) throws HubProcessingException { - List shades = webTargets.getShades(); ThingUID bridgeUid = hub.getThing().getUID(); - for (Shade3 shadeData : shades) { - if (shadeData.getId() == 0) { + for (Shade3 shade : webTargets.getShades()) { + if (shade.getId() == 0) { continue; } - String id = Integer.toString(shadeData.getId()); + + String id = Integer.toString(shade.getId()); ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3, bridgeUid, id); - DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shadeData.getName()) + DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shade.getName()) .withBridge(bridgeUid).withProperty(HDPowerViewShadeConfiguration.ID, id) .withRepresentationProperty(HDPowerViewShadeConfiguration.ID); - String type = shadeData.getTypeString(); + String type = shade.getTypeString(); if (type != null) { builder.withProperty(HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, type); } From 7bd2c0e4a22a03dc57dd60d986763649a0337460 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 26 Oct 2022 19:56:38 +0100 Subject: [PATCH 37/64] [hdpowerview] rename classes Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/HDPowerViewHandlerFactory.java | 4 ++-- ...iceV3.java => HDPowerViewDeviceDiscoveryService3.java} | 6 +++--- ...ntV3.java => HDPowerViewHubDiscoveryParticipant3.java} | 4 ++-- .../gen3/dto/{SceneEvent.java => SceneEvent3.java} | 2 +- .../gen3/dto/{ShadeEvent.java => ShadeEvent3.java} | 2 +- .../internal/gen3/webtargets/HDPowerViewWebTargets3.java | 8 ++++---- .../binding/hdpowerview/gen3/Generation3DtoTest.java | 8 ++++---- 7 files changed, 17 insertions(+), 17 deletions(-) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/{HDPowerViewDeviceDiscoveryServiceV3.java => HDPowerViewDeviceDiscoveryService3.java} (95%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/{HDPowerViewHubDiscoveryParticipantV3.java => HDPowerViewHubDiscoveryParticipant3.java} (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/{SceneEvent.java => SceneEvent3.java} (97%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/{ShadeEvent.java => ShadeEvent3.java} (97%) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index c59011021297c..799f05196ac58 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -20,7 +20,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewDeviceDiscoveryService; -import org.openhab.binding.hdpowerview.internal.gen3.discovery.HDPowerViewDeviceDiscoveryServiceV3; +import org.openhab.binding.hdpowerview.internal.gen3.discovery.HDPowerViewDeviceDiscoveryService3; import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewShadeHandler3; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; @@ -82,7 +82,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3.equals(thingTypeUID)) { HDPowerViewHubHandler3 handler = new HDPowerViewHubHandler3((Bridge) thing, httpClient, translationProvider, clientBuilder, eventSourceFactory); - registerService(new HDPowerViewDeviceDiscoveryServiceV3(handler)); + registerService(new HDPowerViewDeviceDiscoveryService3(handler)); return handler; } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3.equals(thingTypeUID)) { return new HDPowerViewShadeHandler3(thing); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java similarity index 95% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java index b736015a46aeb..532369088d594 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryServiceV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java @@ -37,15 +37,15 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewDeviceDiscoveryServiceV3 extends AbstractDiscoveryService { +public class HDPowerViewDeviceDiscoveryService3 extends AbstractDiscoveryService { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewDeviceDiscoveryServiceV3.class); + private final Logger logger = LoggerFactory.getLogger(HDPowerViewDeviceDiscoveryService3.class); private final HDPowerViewHubHandler3 hub; private final Runnable scanner; private @Nullable ScheduledFuture backgroundFuture; private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase(); - public HDPowerViewDeviceDiscoveryServiceV3(HDPowerViewHubHandler3 hub) { + public HDPowerViewDeviceDiscoveryService3(HDPowerViewHubHandler3 hub) { super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3), 600, true); this.hub = hub; this.scanner = createScanner(); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java index 8f2273250a328..acaecd0f15247 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipantV3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java @@ -34,9 +34,9 @@ */ @NonNullByDefault @Component -public class HDPowerViewHubDiscoveryParticipantV3 extends HDPowerViewHubDiscoveryParticipant { +public class HDPowerViewHubDiscoveryParticipant3 extends HDPowerViewHubDiscoveryParticipant { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipantV3.class); + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant3.class); @Override public @Nullable DiscoveryResult createResult(ServiceInfo service) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java index cb8fb302b6d75..bc54624dc2298 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java @@ -20,7 +20,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class SceneEvent { +public class SceneEvent3 { private int id; // private @NonNullByDefault({}) String evt; // private @NonNullByDefault({}) String isoDate; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java index af38fc9c4d96b..4956dbcc99f1d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java @@ -20,7 +20,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadeEvent { +public class ShadeEvent3 { private int id; // private @NonNullByDefault({}) String evt; // private @NonNullByDefault({}) String isoDate; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java index 3c3c540d13cc6..97ec14353001b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java @@ -42,10 +42,10 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Info3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; import org.openhab.core.thing.Thing; @@ -370,7 +370,7 @@ public void moveShade(int shadeId, ShadePosition3 position) throws HubProcessing private void onSceneEvent(InboundSseEvent sseEvent) { String json = sseEvent.readData(); logger.trace("onSceneEvent() json:{}", json); - SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); + SceneEvent3 sceneEvent = gson.fromJson(json, SceneEvent3.class); if (sceneEvent != null) { Scene3 scene = sceneEvent.getScene(); hubHandler.onSceneEvent(scene); @@ -385,7 +385,7 @@ private void onSceneEvent(InboundSseEvent sseEvent) { private void onShadeEvent(InboundSseEvent sseEvent) { String json = sseEvent.readData(); logger.trace("onShadeEvent() json:{}", json); - ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); + ShadeEvent3 shadeEvent = gson.fromJson(json, ShadeEvent3.class); if (shadeEvent != null) { ShadePosition3 positions = shadeEvent.getCurrentPositions(); hubHandler diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java index d6985fd9894d3..49c04df2363e9 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java @@ -24,10 +24,10 @@ import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; import org.openhab.core.library.types.PercentType; @@ -78,7 +78,7 @@ public void testAutomationParsing() throws IOException { @Test public void testSceneEventParsing() throws IOException { String json = loadJson("gen3/scene-event.json"); - SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); + SceneEvent3 sceneEvent = gson.fromJson(json, SceneEvent3.class); assertNotNull(sceneEvent); Scene3 scene = sceneEvent.getScene(); assertNotNull(scene); @@ -106,7 +106,7 @@ public void testScenesParsing() throws IOException { @Test public void testShadeEventParsing() throws IOException { String json = loadJson("gen3/shade-event.json"); - ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); + ShadeEvent3 shadeEvent = gson.fromJson(json, ShadeEvent3.class); assertNotNull(shadeEvent); ShadePosition3 position = shadeEvent.getCurrentPositions(); assertNotNull(position); From 55a04f6c1938ece0a026ebaf55f18b35364b84cb Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Thu, 27 Oct 2022 13:06:36 +0100 Subject: [PATCH 38/64] [hdpowerview] (interim) adopt reviewer suggestions Signed-off-by: Andrew Fiddian-Green --- .../internal/HDPowerViewBindingConstants.java | 8 ++++++-- .../internal/HDPowerViewHandlerFactory.java | 4 ++-- .../HDPowerViewHubDiscoveryParticipant.java | 6 +----- .../HDPowerViewDeviceDiscoveryService3.java | 8 +++----- .../HDPowerViewHubDiscoveryParticipant3.java | 6 +++--- .../hdpowerview/internal/gen3/dto/Info3.java | 2 +- .../hdpowerview/internal/gen3/dto/Scene3.java | 2 +- .../hdpowerview/internal/gen3/dto/SceneEvent3.java | 4 +--- .../internal/gen3/dto/ScheduledEvent3.java | 2 +- .../hdpowerview/internal/gen3/dto/Shade3.java | 3 +-- .../hdpowerview/internal/gen3/dto/ShadeEvent3.java | 6 +----- .../internal/gen3/dto/ShadePosition3.java | 3 +-- .../gen3/handler/HDPowerViewHubHandler3.java | 2 +- .../gen3/handler/HDPowerViewShadeHandler3.java | 2 +- .../gen3/webtargets/HDPowerViewWebTargets3.java | 2 +- .../resources/OH-INF/i18n/hdpowerview.properties | 14 +++++++------- .../src/main/resources/OH-INF/thing/bridge.xml | 12 ++++++------ 17 files changed, 38 insertions(+), 48 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java index 610e0d82d025f..6b6ccc3fefb45 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingTypeUID; @@ -77,9 +78,12 @@ public class HDPowerViewBindingConstants { public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_HUB, THING_TYPE_SHADE, THING_TYPE_REPEATER); + public static final Pattern VALID_IP_V4_ADDRESS = Pattern + .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); + // generation 3 - public static final ThingTypeUID THING_TYPE_HUB_GEN3 = new ThingTypeUID(BINDING_ID, "gen3"); - public static final ThingTypeUID THING_TYPE_SHADE_GEN3 = new ThingTypeUID(BINDING_ID, "shade3"); + public static final ThingTypeUID THING_TYPE_GATEWAY3 = new ThingTypeUID(BINDING_ID, "gateway3"); + public static final ThingTypeUID THING_TYPE_SHADE3 = new ThingTypeUID(BINDING_ID, "shade3"); public static final String PROPERTY_NAME = "name"; public static final String PROPERTY_POWER_TYPE = "powerType"; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 799f05196ac58..6787e1e861f97 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -79,12 +79,12 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); // generation 3 - if (HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3.equals(thingTypeUID)) { + if (HDPowerViewBindingConstants.THING_TYPE_GATEWAY3.equals(thingTypeUID)) { HDPowerViewHubHandler3 handler = new HDPowerViewHubHandler3((Bridge) thing, httpClient, translationProvider, clientBuilder, eventSourceFactory); registerService(new HDPowerViewDeviceDiscoveryService3(handler)); return handler; - } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3.equals(thingTypeUID)) { + } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE3.equals(thingTypeUID)) { return new HDPowerViewShadeHandler3(thing); } else // generation 1/2 diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java index d9c9bf063e795..924ef4c2b35b6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java @@ -12,11 +12,10 @@ */ package org.openhab.binding.hdpowerview.internal.discovery; -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB; +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; import java.util.Collections; import java.util.Set; -import java.util.regex.Pattern; import javax.jmdns.ServiceInfo; @@ -43,9 +42,6 @@ public class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticip private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant.class); - public static final Pattern VALID_IP_V4_ADDRESS = Pattern - .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); - @Override public Set getSupportedThingTypeUIDs() { return Collections.singleton(THING_TYPE_HUB); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java index 532369088d594..fd456543ea11a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java @@ -20,7 +20,6 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; -import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; @@ -32,7 +31,7 @@ import org.slf4j.LoggerFactory; /** - * Discovers HD PowerView Shades and Repeaters from an existing hub + * Discovers shades in an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @@ -43,10 +42,9 @@ public class HDPowerViewDeviceDiscoveryService3 extends AbstractDiscoveryService private final HDPowerViewHubHandler3 hub; private final Runnable scanner; private @Nullable ScheduledFuture backgroundFuture; - private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase(); public HDPowerViewDeviceDiscoveryService3(HDPowerViewHubHandler3 hub) { - super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3), 600, true); + super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE3), 600, true); this.hub = hub; this.scanner = createScanner(); } @@ -95,7 +93,7 @@ private void discoverShades(HDPowerViewWebTargets3 webTargets) throws HubProcess } String id = Integer.toString(shade.getId()); - ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE_GEN3, bridgeUid, id); + ThingUID thingUID = new ThingUID(HDPowerViewBindingConstants.THING_TYPE_SHADE3, bridgeUid, id); DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withLabel(shade.getName()) .withBridge(bridgeUid).withProperty(HDPowerViewShadeConfiguration.ID, id) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java index acaecd0f15247..4985bfb733119 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.hdpowerview.internal.gen3.discovery; -import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.THING_TYPE_HUB_GEN3; +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; import javax.jmdns.ServiceInfo; @@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory; /** - * Discovers HD PowerView generation 3 hubs by means of mDNS. + * Discovers HD PowerView Generation 3 Gateways by means of mDNS. * * @author Andrew Fiddian-Green - Initial contribution. */ @@ -42,7 +42,7 @@ public class HDPowerViewHubDiscoveryParticipant3 extends HDPowerViewHubDiscovery public @Nullable DiscoveryResult createResult(ServiceInfo service) { for (String host : service.getHostAddresses()) { if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_HUB_GEN3, host.replace('.', '_')); + ThingUID thingUID = new ThingUID(THING_TYPE_GATEWAY3, host.replace('.', '_')); DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java index e0d97be96d655..8b520ebe0a3e8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * DTO for the Generation 3 gateway information. + * DTO for the Generation 3 Gateway information. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java index 8d07cdd7c6342..5a167fcd926d2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java @@ -19,7 +19,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Scene object as returned by an HD PowerView hub of Generation 3. + * Scene object as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution. */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java index bc54624dc2298..c1db4851332af 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java @@ -15,15 +15,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Scene SSE event object as supplied an HD PowerView hub of Generation 3. + * Scene SSE event object as supplied an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault public class SceneEvent3 { private int id; - // private @NonNullByDefault({}) String evt; - // private @NonNullByDefault({}) String isoDate; private @NonNullByDefault({}) Scene3 scene; public int getId() { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java index 74cd526170f06..a7e3d8afb7d93 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java @@ -20,7 +20,7 @@ import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; /** - * class for scheduled event as returned by an HD PowerView Generation 3 hub. + * class for scheduled event as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java index f71fe9d498075..8e70d6a0ca8ad 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java @@ -26,7 +26,7 @@ import org.openhab.core.types.UnDefType; /** - * State of a Shade as returned by an HD PowerView hub of Generation 3. + * State of a Shade as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @@ -39,7 +39,6 @@ public class Shade3 { private @Nullable Integer capabilities; private @Nullable String powerType; // TODO unclear if this is String or Integer private @Nullable Integer batteryStatus; - // private @Nullable Integer roomId; private @Nullable Integer signalStrength; private @Nullable String bleName; private @Nullable Firmware firmware; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java index 4956dbcc99f1d..8db10da33e7c1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java @@ -15,18 +15,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Shade SSE event object as supplied an HD PowerView hub of Generation 3. + * Shade SSE event object as supplied an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault public class ShadeEvent3 { private int id; - // private @NonNullByDefault({}) String evt; - // private @NonNullByDefault({}) String isoDate; - // private @NonNullByDefault({}) String bleName; private @NonNullByDefault({}) ShadePosition3 currentPositions; - // private @NonNullByDefault({}) ShadePosition3 targetPositions; public ShadePosition3 getCurrentPositions() { return currentPositions; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java index 93800309a5546..06866f5341823 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java @@ -19,7 +19,7 @@ import org.openhab.core.types.UnDefType; /** - * The position of a single shade, as returned by an HD PowerView hub of Generation 3 + * The position of a shade as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @@ -28,7 +28,6 @@ public class ShadePosition3 { private @NonNullByDefault({}) Double primary; private @NonNullByDefault({}) Double secondary; private @NonNullByDefault({}) Double tilt; - // private @NonNullByDefault({}) Double velocity; public State getState(CoordinateSystem posKindCoords) { Double value; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java index fbfe1f8cf3352..0fcaeefcd1028 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java @@ -49,7 +49,7 @@ import org.slf4j.LoggerFactory; /** - * Bridge handler for an HD PowerView hub of Generation 3. + * Bridge handler for an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java index 19fd295d80e2c..1f1db93f7d536 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java @@ -46,7 +46,7 @@ import org.slf4j.LoggerFactory; /** - * Thing handler for an HD PowerView shade of Generation 3. + * Thing handler for shades in an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java index 97ec14353001b..0e1d21d485dc4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java @@ -58,7 +58,7 @@ import com.google.gson.reflect.TypeToken; /** - * JAX-RS targets for communicating with an HD PowerView hub Generation 3. + * JAX-RS targets for communicating with an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index c118891fa9855..b7251366e41fb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -5,8 +5,8 @@ binding.hdpowerview.description = The Hunter Douglas PowerView binding provides # thing types -thing-type.hdpowerview.gen3.label = PowerView Hub Generation 3 -thing-type.hdpowerview.gen3.description = Hunter Douglas (Luxaflex) PowerView Hub Generation 3 +thing-type.hdpowerview.gateway3.label = PowerView Gen3 Gateway +thing-type.hdpowerview.gateway3.description = Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro thing-type.hdpowerview.hub.label = PowerView Hub thing-type.hdpowerview.hub.description = Hunter Douglas (Luxaflex) PowerView Hub thing-type.hdpowerview.repeater.label = PowerView Repeater @@ -28,10 +28,10 @@ thing-type.hdpowerview.shade3.channel.secondary.description = The secondary vert # thing types config -thing-type.config.hdpowerview.gen3.hardRefresh.label = Hard Refresh Interval -thing-type.config.hdpowerview.gen3.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Hub -thing-type.config.hdpowerview.gen3.host.label = Host -thing-type.config.hdpowerview.gen3.host.description = The Host address of the PowerView Hub +thing-type.config.hdpowerview.gateway3.hardRefresh.label = Hard Refresh Interval +thing-type.config.hdpowerview.gateway3.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Gateway +thing-type.config.hdpowerview.gateway3.host.label = Host +thing-type.config.hdpowerview.gateway3.host.description = The Host address of the PowerView Hub thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval @@ -43,7 +43,7 @@ thing-type.config.hdpowerview.hub.refresh.description = The number of millisecon thing-type.config.hdpowerview.repeater.id.label = ID thing-type.config.hdpowerview.repeater.id.description = The numeric ID of the PowerView Repeater in the Hub thing-type.config.hdpowerview.shade.id.label = ID -thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Hub +thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Gateway/Hub # channel group types diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index d438d8646ebea..50a610faa5694 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -47,9 +47,9 @@ - - - Hunter Douglas (Luxaflex) PowerView Hub Generation 3 + + + Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro @@ -57,7 +57,7 @@ Hunter Douglas (Luxaflex) - PowerView Hub + PowerView Gateway host @@ -65,12 +65,12 @@ - The Host address of the PowerView Hub + The Host address of the PowerView Gateway network-address - The number of minutes between hard refreshes from the PowerView Hub + The number of minutes between hard refreshes from the PowerView Gateway 180 From 82577d0bee5a95254e5387c7c11d7094a4e0ec4f Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 14:24:20 +0100 Subject: [PATCH 39/64] [hdpowerview] readme, class names, minor bugs Signed-off-by: Andrew Fiddian-Green --- .../org.openhab.binding.hdpowerview/README.md | 152 +++++++++++------- .../internal/HDPowerViewBindingConstants.java | 2 +- .../internal/HDPowerViewHandlerFactory.java | 14 +- ....java => GatewayDiscoveryParticipant.java} | 31 +++- ...rvice3.java => ShadeDiscoveryService.java} | 16 +- ...{ScheduledEvent3.java => Automation3.java} | 8 +- .../hdpowerview/internal/gen3/dto/Scene3.java | 2 +- .../internal/gen3/dto/SceneEvent3.java | 2 +- .../hdpowerview/internal/gen3/dto/Shade3.java | 2 +- .../internal/gen3/dto/ShadeEvent3.java | 2 +- .../internal/gen3/dto/ShadePosition3.java | 2 +- ...andler3.java => GatewayBridgeHandler.java} | 36 ++--- ...deHandler3.java => ShadeThingHandler.java} | 16 +- ...ebTargets3.java => GatewayWebTargets.java} | 14 +- .../OH-INF/i18n/hdpowerview.properties | 12 +- .../main/resources/OH-INF/thing/bridge.xml | 4 +- .../src/main/resources/OH-INF/thing/shade.xml | 2 +- .../hdpowerview/gen3/Generation3DtoTest.java | 12 +- 18 files changed, 196 insertions(+), 133 deletions(-) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/{HDPowerViewHubDiscoveryParticipant3.java => GatewayDiscoveryParticipant.java} (65%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/{HDPowerViewDeviceDiscoveryService3.java => ShadeDiscoveryService.java} (86%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/{ScheduledEvent3.java => Automation3.java} (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/{HDPowerViewHubHandler3.java => GatewayBridgeHandler.java} (88%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/{HDPowerViewShadeHandler3.java => ShadeThingHandler.java} (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/{HDPowerViewWebTargets3.java => GatewayWebTargets.java} (96%) diff --git a/bundles/org.openhab.binding.hdpowerview/README.md b/bundles/org.openhab.binding.hdpowerview/README.md index 41e1d5ffe1ff5..512257afc9092 100644 --- a/bundles/org.openhab.binding.hdpowerview/README.md +++ b/bundles/org.openhab.binding.hdpowerview/README.md @@ -1,7 +1,13 @@ # Hunter Douglas (Luxaflex) PowerView Binding This is an openHAB binding for [Hunter Douglas PowerView](https://www.hunterdouglas.com/operating-systems/motorized/powerview-motorization/overview) motorized shades via their PowerView hub. -In some countries the PowerView system is sold under the brand name [Luxaflex](https://www.luxaflex.com/) +In some countries the PowerView system is sold under the brand name [Luxaflex](https://www.luxaflex.com/). + +This binding supports hubs/gateways of all generations. +Hubs of Generation 1 or 2 are handled commonly and their generation specific features are identified with the *'Generation 1/2 only'* annotation and/or via the [1/2] mark. +Gateways of Generation 3 have generation specific features which are identified with the *'Generation 3 only'* annotation and/or via the [3] mark. +Features that are common to all generations are not annoted or marked. + ![PowerView](doc/hdpowerview.png) @@ -13,48 +19,56 @@ By using a scene to control multiple shades at once, the shades will all begin m ## Supported Things -| Thing | Thing Type | Description | -|----------|------------|-------------| -| hub | Bridge | The PowerView hub provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. | -| shade | Thing | A motorized shade. | -| repeater | Thing | A PowerView signal repeater. | +| Thing | Thing Type | Description | +|--------------------------|------------|-------------| +| hub[1/2] | Bridge | The PowerView hub provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. | +| shade[1/2] | Thing | A motorized shade. | +| repeater[1/2] | Thing | A PowerView signal repeater. | +| gateway[3] | Bridge | Powerview Generation 3 gateway that provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. | +| shade3[3] | Thing | A Powerview Generation 3 motorized shade. | + +[1/2] Generation 1/2 hubs only. [3] Generation 3 gateways only. ## Discovery Make sure your shades are visible in the PowerView app before attempting discovery. -The binding can automatically discover the PowerView hub. +The binding can automatically discover the PowerView hub and/or the PowerView Generation 3 gateway. The discovery process can be started by pressing the refresh button in the Main Configuration UI Inbox. However you can also manually create a (bridge) thing for the hub, and enter the required configuration parameters (see Thing Configuration below). -If the configuration parameters are all valid, the binding will then automatically attempt to connect to the hub. +If the configuration parameters are all valid, the binding will then automatically attempt to connect to the hub/gateway. If the connection succeeds, the hub will indicate its status as Online, otherwise it will show an error status. Once the hub thing has been created and successfully connected, the binding will automatically discover all shades and scenes that are in it. - For each shade discovered: the binding will create a new dedicated thing with its own channels. -- For each repeater discovered: the binding will create a new dedicated thing with its own channels. -- For each scene discovered: the binding will create a new channel dynamically within the hub thing. -- For each scene group discovered: the binding will create a new channel dynamically within the hub thing. -- For each automation discovered: the binding will create a new channel dynamically within the hub thing. +- For each scene discovered: the binding will create a new channel dynamically within the hub/gateway thing. +- [1/2] For each repeater discovered: the binding will create a new dedicated thing with its own channels. +- [1/2] For each scene group discovered: the binding will create a new channel dynamically within the hub thing. +- [1/2] For each automation discovered: the binding will create a new channel dynamically within the hub thing. -If in the future, you add additional shades, repeaters, scenes, scene groups or automations to your system, the binding will discover them too. +[1/2] Generation 1/2 hubs only. + +If in the future, you add additional shades, scenes, repeaters, scene groups or automations to your system, the binding will discover them too. ## Thing Configuration -### Thing Configuration for PowerView Hub +### Thing Configuration for PowerView Hub / Gateway (Thing type `hub`[1/2] or `gateway`[3]) + +| Configuration Parameter | Description | +|-----------------------------------------|---------------| +| host | The host name or IP address of the hub on your network. | +| refresh[1/2] | The number of milli-seconds between fetches of the PowerView hub's shade state (default 60'000 one minute). | +| hardRefresh | The number of minutes between hard refreshes of the PowerView hub's shade state (default 180 three hours). See [Hard Refreshes](#Hard-Refreshes). | +| hardRefreshBatteryLevel[1/2] | The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, defaulting to weekly). See [Hard Refreshes](#Hard-Refreshes). | -| Configuration Parameter | Description | -|-------------------------|---------------| -| host | The host name or IP address of the hub on your network. | -| refresh | The number of milli-seconds between fetches of the PowerView hub's shade state (default 60'000 one minute). | -| hardRefresh | The number of minutes between hard refreshes of the PowerView hub's shade state (default 180 three hours). See [Refreshing the PowerView Hub Cache](#Refreshing-the-PowerView-Hub-Cache). | -| hardRefreshBatteryLevel | The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, defaulting to weekly). See [Refreshing the PowerView Hub Cache](#Refreshing-the-PowerView-Hub-Cache). | +[1/2] Generation 1/2 hubs only. -### Thing Configuration for PowerView Shades and Accessories +### Thing Configuration for PowerView Shades and Accessories[3] -PowerView shades and repeaters should preferably be configured via the automatic discovery process. -However, for manual configuration of shades and repeaters, the console command `openhab:hdpowerview showIds` can be used to identify the IDs of all connected equipment. -This can be used for the `id` parameters described below. +PowerView shades and repeaters[3] should preferably be configured via the automatic discovery process. +It is quite difficult to configure manually as the `id` of the shade or repeater is not exposed in the +PowerView app. However, the configuration parameters are described below. #### Thing Configuration for PowerView Shades @@ -62,7 +76,7 @@ This can be used for the `id` parameters described below. |-------------------------|-------------| | id | The ID of the PowerView shade in the app. Must be an integer. | -#### Thing Configuration for PowerView Repeaters +#### Thing Configuration for PowerView Repeaters[1/2] | Configuration Parameter | Description | |-------------------------|-------------| @@ -70,46 +84,48 @@ This can be used for the `id` parameters described below. ## Channels -### Channels for Hub (Thing type `hub`) +### Channels for Hub[1/2] (Thing type `hub`) or Gateway[3] (Thing type `gateway`) Scene, scene group and automation channels will be added dynamically to the binding as they are discovered in the hub. -Each will have an entry in the hub as shown below, whereby different scenes, scene groups and automations -have different `id` values: +Each will have an entry in the hub as shown below, whereby different scenes, scene groups and automations have different `id` values: -| Channel Group | Channel | Item Type | Description | -|---------------|---------|-----------|-------------| -| scenes | id | Switch | Setting this to ON will activate the scene. Scenes are stateless in the PowerView hub; they have no on/off state. | -| sceneGroups | id | Switch | Setting this to ON will activate the scene group. Scene groups are stateless in the PowerView hub; they have no on/off state. | -| automations | id | Switch | Setting this to ON will enable the automation, while OFF will disable it. | +| Channel Group | Channel | Item Type | Description | +|-----------------------------|---------|-----------|-------------| +| scenes | id | Switch | Setting this to ON will activate the scene. Scenes are stateless in the PowerView hub; they have no on/off state. | +| sceneGroups[1/2] | id | Switch | Setting this to ON will activate the scene group. Scene groups are stateless in the PowerView hub; they have no on/off state. | +| automations[1/2] | id | Switch | Setting this to ON will enable the automation, while OFF will disable it. | -### Channels for Shades (Thing type `shade`) +[1/2] Generation 1/2 hubs only. + +### Channels for Shades (Thing type `shade`[1/2] or `shade3`[3]) A shade always implements a roller shutter channel `position` which controls the vertical position of the shade's (primary) rail. If the shade has slats or rotatable vanes, there is also a dimmer channel `vane` which controls the slat / vane position. If it is a dual action (top-down plus bottom-up) shade, there is also a roller shutter channel `secondary` which controls the vertical position of the secondary rail. All of these channels appear in the binding, but only those which have a physical implementation in the shade, will have any physical effect. -| Channel | Item Type | Description | -|----------------|--------------------------|-------------| -| position | Rollershutter | The vertical position of the shade's rail (if any). -- See [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). Up/Down commands will move the rail completely up or completely down. Percentage commands will move the rail to an intermediate position. Stop commands will halt any current movement of the rail. | -| secondary | Rollershutter | The vertical position of the secondary rail (if any). Its function is similar to the `position` channel above. -- But see [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). | -| vane | Dimmer | The degree of opening of the slats or vanes (if any). On some shade types, setting this to a non-zero value might first move the shade `position` fully down, since the slats or vanes can only have a defined state if the shade is in its down position. See [Interdependency between Channel positions](#Interdependency-between-Channel-positions). | -| command | String | Send a command to the shade. Valid values are: `CALIBRATE`, `IDENTIFY` | -| lowBattery | Switch | Indicates ON when the battery level of the shade is low, as determined by the hub's internal rules. | -| batteryLevel | Number | Battery level (10% = low, 50% = medium, 100% = high) | -| batteryVoltage | Number:ElectricPotential | Battery (resp. mains power supply) voltage reported by the shade. | -| signalStrength | Number | Signal strength (0 for no or unknown signal, 1 for weak, 2 for average, 3 for good or 4 for excellent) | -| hubRssi | Number:Power | Received Signal Strength Indicator for Hub | -| repeaterRssi | Number:Power | Received Signal Strength Indicator for Repeater | +| Channel | Item Type | Description | +|--------------------------------|--------------------------|-------------| +| position | Rollershutter | The vertical position of the shade's rail (if any). -- See [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). Up/Down commands will move the rail completely up or completely down. Percentage commands will move the rail to an intermediate position. Stop commands will halt any current movement of the rail. | +| secondary | Rollershutter | The vertical position of the secondary rail (if any). Its function is similar to the `position` channel above. -- But see [next chapter](#Roller-Shutter-Up/Down-Position-vs.-Open/Close-State). | +| vane | Dimmer | The degree of opening of the slats or vanes (if any). On some shade types, setting this to a non-zero value might first move the shade `position` fully down, since the slats or vanes can only have a defined state if the shade is in its down position. See [Interdependency between Channel positions](#Interdependency-between-Channel-positions). | +| command | String | Send a command to the shade. Valid values are: `CALIBRATE`, `IDENTIFY` | +| lowBattery | Switch | Indicates ON when the battery level of the shade is low, as determined by the hub's internal rules. | +| batteryLevel | Number | Battery level (10% = low, 50% = medium, 100% = high) | +| batteryVoltage[1/2] | Number:ElectricPotential | Battery (resp. mains power supply) voltage reported by the shade. | +| signalStrength | Number | Signal strength (0 for no or unknown signal, 1 for weak, 2 for average, 3 for good or 4 for excellent) | +| hubRssi[1/2] | Number:Power | Received Signal Strength Indicator for Hub | +| repeaterRssi[1/2] | Number:Power | Received Signal Strength Indicator for Repeater | Notes: - - The channels `position`, `secondary` and `vane` exist if the shade physically supports such channels. - The shade's Power Option is set via the PowerView app with possible values 'Battery Wand', 'Rechargeable Battery Wand' or 'Hardwired Power Supply'. The channels `lowBattery` and `batteryLevel` exist if you have _not_ selected 'Hardwired Power Supply' in the app. -- The RSSI values will only be updated upon manual request by a `REFRESH` command (e.g. in a rule). +- [1/2] The RSSI values will only be updated upon manual request by a `REFRESH` command (e.g. in a rule). -### Channels for Repeaters (Thing type `repeater`) +[1/2] Generation 1/2 hubs only. + +### Channels for Repeaters (Thing type `repeater`)[1/2] | Channel | Item Type | Description | |-----------------|-----------|-------------------------------| @@ -177,7 +193,15 @@ And the value of `position` is constrained by the prior value of `secondary`. On shades with a secondary blackout panel 'DuoLite', the secondary blackout panel cannot be moved unless the main shade panel is already down. In this case, the position of the secondary blackout panel is reported as 0%. -## Refreshing the PowerView Hub Cache +## Hard Refreshes + +### Hard Refresh on Generation 3 Gateways[3] + +In Generation 3 systems, whenever the state of a shade or scene changes, it immediately notifies the gateway, and the gateway immediately notifies the openHAB binding. +However in case that any notifications may have been lost, (e.g. due to a network error), the binding occasionally requests a full update (i.e. a 'hard refresh') of all shade and scene states from the gateway. +The time interval between hard refreshes is set in the `hardRefresh` configuration parameter. + +### Hard Refresh on Generation 1/2 Hubs[1/2] The hub maintains a cache of the last known state of its shades, and this binding delivers those values. Usually the shades will be moved by this binding, so since the hub is always involved in the moving process, it updates this cache accordingly. @@ -226,10 +250,16 @@ For single shades the refresh takes the item's channel into consideration: ### `demo.things` File ``` +// generation 1/2 Bridge hdpowerview:hub:home "Luxaflex Hub" @ "Living Room" [host="192.168.1.123"] { Thing shade s50150 "Living Room Shade" @ "Living Room" [id="50150"] Thing repeater r16384 "Bedroom Repeater" @ "Bedroom" [id="16384"] } + +// generation 3 +Bridge hdpowerview:gateway:home "Luxaflex Hub" @ "Living Room" [host="192.168.1.123"] { + Thing shade3 s50150 "Living Room Shade" @ "Living Room" [id="50150"] +} ``` ### `demo.items` File @@ -237,6 +267,7 @@ Bridge hdpowerview:hub:home "Luxaflex Hub" @ "Living Room" [host="192.168.1.123" Shade items: ``` +// generation 1/2 Rollershutter Living_Room_Shade_Position "Living Room Shade Position [%.0f %%]" {channel="hdpowerview:shade:home:s50150:position"} Rollershutter Living_Room_Shade_Secondary "Living Room Shade Secondary Position [%.0f %%]" {channel="hdpowerview:shade:home:s50150:secondary"} Dimmer Living_Room_Shade_Vane "Living Room Shade Vane [%.0f %%]" {channel="hdpowerview:shade:home:s50150:vane"} @@ -245,9 +276,17 @@ Number Living_Room_Shade_Battery_Level "Battery Level" {channel="hdpowerview:sha Number:ElectricPotential Living_Room_Shade_Battery_Voltage "Battery Voltage" {channel="hdpowerview:shade:home:s50150:batteryVoltage"} String Living_Room_Shade_Command "Living Room Shade Command" {channel="hdpowerview:shade:home:s50150:command"} Number Living_Room_Shade_SignalStrength "Living Room Shade Signal Strength" {channel="hdpowerview:shade:home:s50150:signalStrength"} + +// generation 3 +Rollershutter Living_Room_Shade_Position "Living Room Shade Position [%.0f %%]" {channel="hdpowerview:shade3:home:s50150:position"} +Rollershutter Living_Room_Shade_Secondary "Living Room Shade Secondary Position [%.0f %%]" {channel="hdpowerview:shade3:home:s50150:secondary"} +Dimmer Living_Room_Shade_Vane "Living Room Shade Vane [%.0f %%]" {channel="hdpowerview:shade3:home:s50150:vane"} +Switch Living_Room_Shade_Battery_Low_Alarm "Living Room Shade Battery Low Alarm [%s]" {channel="hdpowerview:shade3:home:s50150:lowBattery"} +Number Living_Room_Shade_Battery_Level "Battery Level" {channel="hdpowerview:shade3:home:s50150:batteryLevel"} +String Living_Room_Shade_Command "Living Room Shade Command" {channel="hdpowerview:shade3:home:s50150:command"} ``` -Repeater items: +Repeater items[1/2]: ``` Color Bedroom_Repeater_Color "Bedroom Repeater Color" {channel="hdpowerview:repeater:home:r16384:color"} @@ -259,16 +298,20 @@ Switch Bedroom_Repeater_BlinkingEnabled "Bedroom Repeater Blinking Enabled [%s]" Scene items: ``` +// generation 1/2 Switch Living_Room_Shades_Scene_Heart "Living Room Shades Scene Heart" (g_Shades_Scene_Trigger) {channel="hdpowerview:hub:home:scenes#22663"} + +// generation 3 +Switch Living_Room_Shades_Scene_Heart "Living Room Shades Scene Heart" (g_Shades_Scene_Trigger) {channel="hdpowerview:gateway:home:scenes#22663"} ``` -Scene Group items: +Scene Group items[1/2]: ``` Switch Children_Rooms_Shades_Up "Good Morning Children" {channel="hdpowerview:hub:home:sceneGroups#27119"} ``` -Automation items: +Automation items[1/2]: ``` Switch Automation_Children_Up_Sun "Children Up At Sunrise" {channel="hdpowerview:hub:home:automations#1262"} @@ -283,8 +326,9 @@ Frame label="Living Room" { Slider item=Living_Room_Shade_Position Switch item=Living_Room_Shade_Command mappings=[CALIBRATE="Calibrate"] Text item=Living_Room_Shade_SignalStrength - Text item=Living_Room_Shade_Battery_Voltage } + +// generation 1/2 only Frame label="Bedroom" { Colorpicker item=PowerViewRepeater_Color Switch item=PowerViewRepeater_Color diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java index 6b6ccc3fefb45..5a19a9f1438c6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java @@ -82,7 +82,7 @@ public class HDPowerViewBindingConstants { .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); // generation 3 - public static final ThingTypeUID THING_TYPE_GATEWAY3 = new ThingTypeUID(BINDING_ID, "gateway3"); + public static final ThingTypeUID THING_TYPE_GATEWAY = new ThingTypeUID(BINDING_ID, "gateway"); public static final ThingTypeUID THING_TYPE_SHADE3 = new ThingTypeUID(BINDING_ID, "shade3"); public static final String PROPERTY_NAME = "name"; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 6787e1e861f97..0539e64dfe539 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -20,9 +20,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewDeviceDiscoveryService; -import org.openhab.binding.hdpowerview.internal.gen3.discovery.HDPowerViewDeviceDiscoveryService3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewShadeHandler3; +import org.openhab.binding.hdpowerview.internal.gen3.discovery.ShadeDiscoveryService; +import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; +import org.openhab.binding.hdpowerview.internal.gen3.handler.ShadeThingHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewRepeaterHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler; @@ -79,13 +79,13 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); // generation 3 - if (HDPowerViewBindingConstants.THING_TYPE_GATEWAY3.equals(thingTypeUID)) { - HDPowerViewHubHandler3 handler = new HDPowerViewHubHandler3((Bridge) thing, httpClient, translationProvider, + if (HDPowerViewBindingConstants.THING_TYPE_GATEWAY.equals(thingTypeUID)) { + GatewayBridgeHandler handler = new GatewayBridgeHandler((Bridge) thing, httpClient, translationProvider, clientBuilder, eventSourceFactory); - registerService(new HDPowerViewDeviceDiscoveryService3(handler)); + registerService(new ShadeDiscoveryService(handler)); return handler; } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE3.equals(thingTypeUID)) { - return new HDPowerViewShadeHandler3(thing); + return new ShadeThingHandler(thing); } else // generation 1/2 if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java similarity index 65% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java index 4985bfb733119..f2a062825fa63 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewHubDiscoveryParticipant3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java @@ -14,14 +14,18 @@ import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; +import java.util.Collections; +import java.util.Set; + import javax.jmdns.ServiceInfo; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; -import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewHubDiscoveryParticipant; import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; +import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; @@ -34,20 +38,20 @@ */ @NonNullByDefault @Component -public class HDPowerViewHubDiscoveryParticipant3 extends HDPowerViewHubDiscoveryParticipant { +public class GatewayDiscoveryParticipant implements MDNSDiscoveryParticipant { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant3.class); + private final Logger logger = LoggerFactory.getLogger(GatewayDiscoveryParticipant.class); @Override public @Nullable DiscoveryResult createResult(ServiceInfo service) { for (String host : service.getHostAddresses()) { if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { - ThingUID thingUID = new ThingUID(THING_TYPE_GATEWAY3, host.replace('.', '_')); + ThingUID thingUID = new ThingUID(THING_TYPE_GATEWAY, host.replace('.', '_')); DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel("PowerView Hub (" + host + ")").build(); - logger.debug("mDNS discovered generation 3 hub on host '{}'", host); + .withLabel("PowerView Gateway (" + host + ")").build(); + logger.debug("mDNS discovered Generation 3 Gateway on host '{}'", host); return hub; } } @@ -58,4 +62,19 @@ public class HDPowerViewHubDiscoveryParticipant3 extends HDPowerViewHubDiscovery public String getServiceType() { return "_powerview-g3._tcp.local."; } + + @Override + public Set getSupportedThingTypeUIDs() { + return Collections.singleton(THING_TYPE_GATEWAY); + } + + @Override + public @Nullable ThingUID getThingUID(ServiceInfo service) { + for (String host : service.getHostAddresses()) { + if (VALID_IP_V4_ADDRESS.matcher(host).matches()) { + return new ThingUID(THING_TYPE_GATEWAY, host.replace('.', '_')); + } + } + return null; + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java similarity index 86% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java index fd456543ea11a..7a2d1bf13ff42 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/HDPowerViewDeviceDiscoveryService3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java @@ -22,8 +22,8 @@ import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.thing.ThingUID; @@ -36,14 +36,14 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewDeviceDiscoveryService3 extends AbstractDiscoveryService { +public class ShadeDiscoveryService extends AbstractDiscoveryService { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewDeviceDiscoveryService3.class); - private final HDPowerViewHubHandler3 hub; + private final Logger logger = LoggerFactory.getLogger(ShadeDiscoveryService.class); + private final GatewayBridgeHandler hub; private final Runnable scanner; private @Nullable ScheduledFuture backgroundFuture; - public HDPowerViewDeviceDiscoveryService3(HDPowerViewHubHandler3 hub) { + public ShadeDiscoveryService(GatewayBridgeHandler hub) { super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE3), 600, true); this.hub = hub; this.scanner = createScanner(); @@ -75,7 +75,7 @@ protected void stopBackgroundDiscovery() { private Runnable createScanner() { return () -> { - HDPowerViewWebTargets3 webTargets = hub.getWebTargets(); + GatewayWebTargets webTargets = hub.getWebTargets(); try { discoverShades(webTargets); } catch (HubProcessingException e) { @@ -85,7 +85,7 @@ private Runnable createScanner() { }; } - private void discoverShades(HDPowerViewWebTargets3 webTargets) throws HubProcessingException { + private void discoverShades(GatewayWebTargets webTargets) throws HubProcessingException { ThingUID bridgeUid = hub.getThing().getUID(); for (Shade3 shade : webTargets.getShades()) { if (shade.getId() == 0) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java index a7e3d8afb7d93..1753a59672739 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ScheduledEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java @@ -20,12 +20,12 @@ import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; /** - * class for scheduled event as returned by an HD PowerView Generation 3 Gateway. + * DTO for automation object as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ScheduledEvent3 { +public class Automation3 { public int id; public int type; public boolean enabled; @@ -53,10 +53,10 @@ public boolean equals(@Nullable Object o) { if (o == this) { return true; } - if (!(o instanceof ScheduledEvent3)) { + if (!(o instanceof Automation3)) { return false; } - ScheduledEvent3 other = (ScheduledEvent3) o; + Automation3 other = (Automation3) o; String days = this.days; return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java index 5a167fcd926d2..3e4a56eb84719 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java @@ -19,7 +19,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Scene object as returned by an HD PowerView Generation 3 Gateway. + * DTO for a scene as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution. */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java index c1db4851332af..47c306af7a8d6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Scene SSE event object as supplied an HD PowerView Generation 3 Gateway. + * DTO for scene SSE event object as supplied an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java index 8e70d6a0ca8ad..073870a71974c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java @@ -26,7 +26,7 @@ import org.openhab.core.types.UnDefType; /** - * State of a Shade as returned by an HD PowerView Generation 3 Gateway. + * DTO for a shade as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java index 8db10da33e7c1..e3fda1a402572 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java @@ -15,7 +15,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Shade SSE event object as supplied an HD PowerView Generation 3 Gateway. + * DTO for a shade SSE event object as supplied an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java index 06866f5341823..98f0003e868b0 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java @@ -19,7 +19,7 @@ import org.openhab.core.types.UnDefType; /** - * The position of a shade as returned by an HD PowerView Generation 3 Gateway. + * DTO for the position of a shade as returned by an HD PowerView Generation 3 Gateway. * * @author Andrew Fiddian-Green - Initial contribution */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java similarity index 88% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java index 0fcaeefcd1028..72b47443b943b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewHubHandler3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java @@ -29,7 +29,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.Bridge; @@ -54,9 +54,9 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewHubHandler3 extends BaseBridgeHandler { +public class GatewayBridgeHandler extends BaseBridgeHandler { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubHandler3.class); + private final Logger logger = LoggerFactory.getLogger(GatewayBridgeHandler.class); private final String channelTypeId = HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE; private final String channelGroupId = HDPowerViewBindingConstants.CHANNEL_GROUP_SCENES; @@ -65,14 +65,14 @@ public class HDPowerViewHubHandler3 extends BaseBridgeHandler { private final ClientBuilder clientBuilder; private final SseEventSourceFactory eventSourceFactory; - private @Nullable HDPowerViewWebTargets3 webTargets; + private @Nullable GatewayWebTargets webTargets; private @Nullable ScheduledFuture refreshTask; private boolean scenesLoaded; private boolean propertiesLoaded; private boolean isDisposing; - public HDPowerViewHubHandler3(Bridge bridge, HttpClient httpClient, + public GatewayBridgeHandler(Bridge bridge, HttpClient httpClient, HDPowerViewTranslationProvider translationProvider, ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory) { super(bridge); @@ -91,7 +91,7 @@ public void dispose() { } this.refreshTask = null; - HDPowerViewWebTargets3 webTargets = this.webTargets; + GatewayWebTargets webTargets = this.webTargets; if (webTargets != null) { try { webTargets.close(); @@ -119,18 +119,18 @@ private void doRefresh() { } /** - * Getter for the list of all HDPowerViewShadeHandler3 child thing handlers. + * Getter for the list of all child shade thing handlers. * - * @return the list of handlers. + * @return the list of shade handlers. * @throws IllegalStateException if the bridge is not properly initialized. */ - private List getThingHandlers() throws IllegalStateException { + private List getShadeThingHandlers() throws IllegalStateException { Bridge bridge = getBridge(); if (bridge != null) { - List result = new ArrayList<>(); + List result = new ArrayList<>(); bridge.getThings().stream().map(thing -> thing.getHandler()).forEach(handler -> { - if (handler instanceof HDPowerViewShadeHandler3) { - result.add((HDPowerViewShadeHandler3) handler); + if (handler instanceof ShadeThingHandler) { + result.add((ShadeThingHandler) handler); } }); return result; @@ -144,8 +144,8 @@ private List getThingHandlers() throws IllegalStateExc * @return the webTargets. * @throws IllegalStateException if webTargets is not initialized. */ - public HDPowerViewWebTargets3 getWebTargets() throws IllegalStateException { - HDPowerViewWebTargets3 webTargets = this.webTargets; + public GatewayWebTargets getWebTargets() throws IllegalStateException { + GatewayWebTargets webTargets = this.webTargets; if (webTargets != null) { return webTargets; } @@ -186,7 +186,7 @@ public void initialize() { return; } - webTargets = new HDPowerViewWebTargets3(this, httpClient, clientBuilder, eventSourceFactory, host); + webTargets = new GatewayWebTargets(this, httpClient, clientBuilder, eventSourceFactory, host); scenesLoaded = false; propertiesLoaded = false; isDisposing = false; @@ -223,7 +223,7 @@ public void onSceneEvent(Scene3 scene) { */ public void onShadeEvent(Shade3 shade) { try { - for (HDPowerViewShadeHandler3 handler : getThingHandlers()) { + for (ShadeThingHandler handler : getShadeThingHandlers()) { if (isDisposing || handler.notify(shade)) { break; } @@ -272,9 +272,9 @@ private void refreshScenes() throws HubProcessingException, IllegalStateExceptio * @throws IllegalStateException if this handler is in an illegal state. */ private void refreshShades() throws HubProcessingException, IllegalStateException { - List handlers = getThingHandlers(); + List handlers = getShadeThingHandlers(); for (Shade3 shade : getWebTargets().getShades()) { - for (HDPowerViewShadeHandler3 handler : handlers) { + for (ShadeThingHandler handler : handlers) { if (isDisposing || handler.notify(shade)) { break; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java index 1f1db93f7d536..97e368ad66e65 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/HDPowerViewShadeHandler3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java @@ -27,7 +27,7 @@ import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StopMoveType; import org.openhab.core.library.types.StringType; @@ -51,9 +51,9 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewShadeHandler3 extends BaseThingHandler { +public class ShadeThingHandler extends BaseThingHandler { - private final Logger logger = LoggerFactory.getLogger(HDPowerViewShadeHandler3.class); + private final Logger logger = LoggerFactory.getLogger(ShadeThingHandler.class); private static final String INVALID_CHANNEL = "invalid channel"; private static final String INVALID_COMMAND = "invalid command"; @@ -63,7 +63,7 @@ public class HDPowerViewShadeHandler3 extends BaseThingHandler { private final Shade3 thisShade = new Shade3(); private boolean isInitialized; - public HDPowerViewShadeHandler3(Thing thing) { + public ShadeThingHandler(Thing thing) { super(thing); } @@ -78,16 +78,16 @@ public void dispose() { * @return the hub handler. * @throws IllegalStateException if the bridge or its handler are not initialized. */ - private HDPowerViewHubHandler3 getHandler() throws IllegalStateException { + private GatewayBridgeHandler getHandler() throws IllegalStateException { Bridge bridge = this.getBridge(); if (bridge == null) { throw new IllegalStateException("Bridge not initialised."); } BridgeHandler handler = bridge.getHandler(); - if (!(handler instanceof HDPowerViewHubHandler3)) { + if (!(handler instanceof GatewayBridgeHandler)) { throw new IllegalStateException("Bridge handler not initialised."); } - return (HDPowerViewHubHandler3) handler; + return (GatewayBridgeHandler) handler; } @Override @@ -97,7 +97,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { return; } - HDPowerViewWebTargets3 webTargets = getHandler().getWebTargets(); + GatewayWebTargets webTargets = getHandler().getWebTargets(); ShadePosition3 position = new ShadePosition3(); int shadeId = thisShade.getId(); try { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java index 0e1d21d485dc4..8ff69c80fdd38 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/HDPowerViewWebTargets3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java @@ -40,14 +40,14 @@ import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Automation3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Info3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.HDPowerViewHubHandler3; +import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; import org.openhab.core.thing.Thing; import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; @@ -63,17 +63,17 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class HDPowerViewWebTargets3 implements Closeable { +public class GatewayWebTargets implements Closeable { private static final String IDS = "ids"; // @formatter:off public static final Type LIST_SHADES = new TypeToken>() {}.getType(); public static final Type LIST_SCENES = new TypeToken>() {}.getType(); - public static final Type LIST_EVENTS =new TypeToken>() {}.getType(); + public static final Type LIST_EVENTS =new TypeToken>() {}.getType(); // @formatter:on - private final Logger logger = LoggerFactory.getLogger(HDPowerViewWebTargets3.class); + private final Logger logger = LoggerFactory.getLogger(GatewayWebTargets.class); private final String shades; private final String scenes; private final String sceneActivate; @@ -91,7 +91,7 @@ public class HDPowerViewWebTargets3 implements Closeable { private final ClientBuilder clientBuilder; private final SseEventSourceFactory eventSourceFactory; - private final HDPowerViewHubHandler3 hubHandler; + private final GatewayBridgeHandler hubHandler; private boolean isRegistered; @@ -114,7 +114,7 @@ private static class GatewayRegistration { * @param httpClient the HTTP client (the binding) * @param ipAddress the IP address of the server (the hub) */ - public HDPowerViewWebTargets3(HDPowerViewHubHandler3 hubHandler, HttpClient httpClient, ClientBuilder clientBuilder, + public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory, String ipAddress) { String base = "http://" + ipAddress + "/"; String home = base + "home/"; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index b7251366e41fb..8ab9e1e1e2889 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -5,8 +5,8 @@ binding.hdpowerview.description = The Hunter Douglas PowerView binding provides # thing types -thing-type.hdpowerview.gateway3.label = PowerView Gen3 Gateway -thing-type.hdpowerview.gateway3.description = Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro +thing-type.hdpowerview.gateway.label = PowerView Gen3 Gateway +thing-type.hdpowerview.gateway.description = Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro thing-type.hdpowerview.hub.label = PowerView Hub thing-type.hdpowerview.hub.description = Hunter Douglas (Luxaflex) PowerView Hub thing-type.hdpowerview.repeater.label = PowerView Repeater @@ -28,10 +28,10 @@ thing-type.hdpowerview.shade3.channel.secondary.description = The secondary vert # thing types config -thing-type.config.hdpowerview.gateway3.hardRefresh.label = Hard Refresh Interval -thing-type.config.hdpowerview.gateway3.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Gateway -thing-type.config.hdpowerview.gateway3.host.label = Host -thing-type.config.hdpowerview.gateway3.host.description = The Host address of the PowerView Hub +thing-type.config.hdpowerview.gateway.hardRefresh.label = Hard Refresh Interval +thing-type.config.hdpowerview.gateway.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Gateway +thing-type.config.hdpowerview.gateway.host.label = Host +thing-type.config.hdpowerview.gateway.host.description = The Host address of the PowerView Hub thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index 50a610faa5694..08a7f28890a5f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -47,7 +47,7 @@ - + Hunter Douglas (Luxaflex) PowerView Generation 3 Gateway/Gateway Pro @@ -57,7 +57,7 @@ Hunter Douglas (Luxaflex) - PowerView Gateway + PowerView Gen 3 Gateway host diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml index 53d64243d74a8..2c46638868a42 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml @@ -45,7 +45,7 @@ - + Hunter Douglas (Luxaflex) PowerView Shade diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java index 49c04df2363e9..863a7e0bce08a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java @@ -23,13 +23,13 @@ import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.gen3.dto.Automation3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ScheduledEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.HDPowerViewWebTargets3; +import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.UnDefType; @@ -64,10 +64,10 @@ private String loadJson(String filename) throws IOException { @Test public void testAutomationParsing() throws IOException { String json = loadJson("gen3/automations.json"); - List scheduledEventList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_EVENTS); + List scheduledEventList = gson.fromJson(json, GatewayWebTargets.LIST_EVENTS); assertNotNull(scheduledEventList); assertEquals(1, scheduledEventList.size()); - ScheduledEvent3 scheduledEvent = scheduledEventList.get(0); + Automation3 scheduledEvent = scheduledEventList.get(0); assertEquals(33, scheduledEvent.id); assertTrue(scheduledEvent.enabled); } @@ -92,7 +92,7 @@ public void testSceneEventParsing() throws IOException { @Test public void testScenesParsing() throws IOException { String json = loadJson("gen3/scenes.json"); - List sceneList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_SCENES); + List sceneList = gson.fromJson(json, GatewayWebTargets.LIST_SCENES); assertNotNull(sceneList); assertEquals(1, sceneList.size()); Scene3 scene = sceneList.get(0); @@ -143,7 +143,7 @@ public void testShadePositions() { @Test public void testShadesParsing() throws IOException { String json = loadJson("gen3/shades.json"); - List shadeList = gson.fromJson(json, HDPowerViewWebTargets3.LIST_SHADES); + List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); assertNotNull(shadeList); assertEquals(1, shadeList.size()); Shade3 shadeData = shadeList.get(0); From 33307088bcef6b4b46a4bb4ba931cb643dbc2b94 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 14:54:25 +0100 Subject: [PATCH 40/64] [hdpowerview] extend console extension for gen 3 Signed-off-by: Andrew Fiddian-Green --- bundles/org.openhab.binding.hdpowerview/README.md | 5 +++-- .../binding/hdpowerview/internal/gen3/dto/Shade3.java | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/README.md b/bundles/org.openhab.binding.hdpowerview/README.md index 512257afc9092..c442f6fe75491 100644 --- a/bundles/org.openhab.binding.hdpowerview/README.md +++ b/bundles/org.openhab.binding.hdpowerview/README.md @@ -67,8 +67,8 @@ If in the future, you add additional shades, scenes, repeaters, scene groups or ### Thing Configuration for PowerView Shades and Accessories[3] PowerView shades and repeaters[3] should preferably be configured via the automatic discovery process. -It is quite difficult to configure manually as the `id` of the shade or repeater is not exposed in the -PowerView app. However, the configuration parameters are described below. +However, for manual configuration of shades and repeaters, the console command `openhab:hdpowerview showIds` can be used to identify the IDs of all connected equipment. +This can be used for the `id` parameters described below. #### Thing Configuration for PowerView Shades @@ -118,6 +118,7 @@ All of these channels appear in the binding, but only those which have a physica | repeaterRssi[1/2] | Number:Power | Received Signal Strength Indicator for Repeater | Notes: + - The channels `position`, `secondary` and `vane` exist if the shade physically supports such channels. - The shade's Power Option is set via the PowerView app with possible values 'Battery Wand', 'Rechargeable Battery Wand' or 'Hardwired Power Supply'. The channels `lowBattery` and `batteryLevel` exist if you have _not_ selected 'Hardwired Power Supply' in the app. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java index 073870a71974c..de085e1e2ae82 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java @@ -81,7 +81,8 @@ public State getLowBattery() { } public String getName() { - return String.join(" ", new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8), ptName); + return String.join(" ", new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8), ptName) + .replaceAll("\n", "").replaceAll("\r", ""); } public State getPosition(CoordinateSystem posKindCoords) { From f08c0530e8042982df3273e4387ef860c0227f58 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 20:09:45 +0100 Subject: [PATCH 41/64] [hdpowerview] implement class naming convention Signed-off-by: Andrew Fiddian-Green --- .../webtargets => }/GatewayWebTargets.java | 70 ++++------- .../internal/HDPowerViewHandlerFactory.java | 6 +- .../dto/Info3.java => api/gen3/Info.java} | 4 +- .../dto/Scene3.java => api/gen3/Scene.java} | 4 +- .../gen3/SceneEvent.java} | 8 +- .../dto/Shade3.java => api/gen3/Shade.java} | 26 ++-- .../gen3/ShadeEvent.java} | 8 +- .../gen3/ShadePosition.java} | 6 +- .../GatewayDiscoveryParticipant.java | 2 +- .../discovery/ShadeDiscoveryService.java | 10 +- .../internal/gen3/dto/Automation3.java | 117 ------------------ .../handler/GatewayBridgeHandler.java | 16 +-- .../{gen3 => }/handler/ShadeThingHandler.java | 24 ++-- .../hdpowerview/gen3/Generation3DtoTest.java | 49 +++----- 14 files changed, 97 insertions(+), 253 deletions(-) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/webtargets => }/GatewayWebTargets.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/Info3.java => api/gen3/Info.java} (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/Scene3.java => api/gen3/Scene.java} (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/SceneEvent3.java => api/gen3/SceneEvent.java} (81%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/Shade3.java => api/gen3/Shade.java} (86%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/ShadeEvent3.java => api/gen3/ShadeEvent.java} (77%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3/dto/ShadePosition3.java => api/gen3/ShadePosition.java} (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3 => }/discovery/GatewayDiscoveryParticipant.java (97%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3 => }/discovery/ShadeDiscoveryService.java (91%) delete mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3 => }/handler/GatewayBridgeHandler.java (95%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{gen3 => }/handler/ShadeThingHandler.java (94%) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 8ff69c80fdd38..12300adc1746e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/webtargets/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.webtargets; +package org.openhab.binding.hdpowerview.internal; import java.io.Closeable; import java.io.IOException; @@ -37,17 +37,15 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets.Query; +import org.openhab.binding.hdpowerview.internal.api.gen3.Info; +import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.api.gen3.SceneEvent; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Automation3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Info3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; +import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.core.thing.Thing; import org.osgi.service.jaxrs.client.SseEventSourceFactory; import org.slf4j.Logger; @@ -68,9 +66,8 @@ public class GatewayWebTargets implements Closeable { private static final String IDS = "ids"; // @formatter:off - public static final Type LIST_SHADES = new TypeToken>() {}.getType(); - public static final Type LIST_SCENES = new TypeToken>() {}.getType(); - public static final Type LIST_EVENTS =new TypeToken>() {}.getType(); + public static final Type LIST_SHADES = new TypeToken>() {}.getType(); + public static final Type LIST_SCENES = new TypeToken>() {}.getType(); // @formatter:on private final Logger logger = LoggerFactory.getLogger(GatewayWebTargets.class); @@ -81,7 +78,6 @@ public class GatewayWebTargets implements Closeable { private final String shadeStop; private final String shadePositions; private final String info; - private final String automations; private final String register; private final String shadeEvents; @@ -125,7 +121,6 @@ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, shadeMotion = home + "shades/%d/motion"; shadeStop = home + "shades/stop"; shadePositions = home + "shades/positions"; - automations = home + "automations"; shadeEvents = home + "shades/events"; sceneEvents = home + "scenes/events"; @@ -196,7 +191,7 @@ private void gatewayRegister() throws HubProcessingException { public Map getInformation() throws HubProcessingException { String json = invoke(HttpMethod.GET, info, null, null); try { - Info3 result = gson.fromJson(json, Info3.class); + Info result = gson.fromJson(json, Info.class); if (result == null) { throw new HubProcessingException("getInformation(): missing response"); } @@ -214,10 +209,10 @@ public Map getInformation() throws HubProcessingException { * @return the list of scenes. * @throws HubProcessingException if any error occurs. */ - public List getScenes() throws HubProcessingException { + public List getScenes() throws HubProcessingException { String json = invoke(HttpMethod.GET, scenes, null, null); try { - List result = gson.fromJson(json, LIST_SCENES); + List result = gson.fromJson(json, LIST_SCENES); if (result == null) { throw new HubProcessingException("getScenes() missing response"); } @@ -227,25 +222,6 @@ public List getScenes() throws HubProcessingException { } } - /** - * Get the list of scheduled events. - * - * @return the list of scheduled events. - * @throws HubProcessingException if any error occurs. - */ - public List getScheduledEvents() throws HubProcessingException { - String json = invoke(HttpMethod.GET, automations, null, null); - try { - List result = gson.fromJson(json, LIST_EVENTS); - if (result == null) { - throw new HubProcessingException("getScheduledEvents() missing response"); - } - return result; - } catch (JsonParseException e) { - throw new HubProcessingException("getScheduledEvents() JsonParseException"); - } - } - /** * Get the data for a single shade. * @@ -253,10 +229,10 @@ public List getScheduledEvents() throws HubProcessingException * @return the shade. * @throws HubProcessingException if any error occurs. */ - public Shade3 getShade(int shadeId) throws HubProcessingException { + public Shade getShade(int shadeId) throws HubProcessingException { String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); try { - Shade3 result = gson.fromJson(json, Shade3.class); + Shade result = gson.fromJson(json, Shade.class); if (result == null) { throw new HubProcessingException("getShade() missing response"); } @@ -272,10 +248,10 @@ public Shade3 getShade(int shadeId) throws HubProcessingException { * @return the list of shades. * @throws HubProcessingException if any error occurs. */ - public List getShades() throws HubProcessingException { + public List getShades() throws HubProcessingException { String json = invoke(HttpMethod.GET, shades, null, null); try { - List result = gson.fromJson(json, LIST_SHADES); + List result = gson.fromJson(json, LIST_SHADES); if (result == null) { throw new HubProcessingException("getShades() missing response"); } @@ -357,7 +333,7 @@ public void jogShade(int shadeId) throws HubProcessingException { * @param position the new position. * @throws HubProcessingException if any error occurs. */ - public void moveShade(int shadeId, ShadePosition3 position) throws HubProcessingException { + public void moveShade(int shadeId, ShadePosition position) throws HubProcessingException { invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), gson.toJson(position)); } @@ -370,9 +346,9 @@ public void moveShade(int shadeId, ShadePosition3 position) throws HubProcessing private void onSceneEvent(InboundSseEvent sseEvent) { String json = sseEvent.readData(); logger.trace("onSceneEvent() json:{}", json); - SceneEvent3 sceneEvent = gson.fromJson(json, SceneEvent3.class); + SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); if (sceneEvent != null) { - Scene3 scene = sceneEvent.getScene(); + Scene scene = sceneEvent.getScene(); hubHandler.onSceneEvent(scene); } } @@ -385,11 +361,11 @@ private void onSceneEvent(InboundSseEvent sseEvent) { private void onShadeEvent(InboundSseEvent sseEvent) { String json = sseEvent.readData(); logger.trace("onShadeEvent() json:{}", json); - ShadeEvent3 shadeEvent = gson.fromJson(json, ShadeEvent3.class); + ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); if (shadeEvent != null) { - ShadePosition3 positions = shadeEvent.getCurrentPositions(); + ShadePosition positions = shadeEvent.getCurrentPositions(); hubHandler - .onShadeEvent(new Shade3().setId(shadeEvent.getId()).setShadePosition(positions).setPartialState()); + .onShadeEvent(new Shade().setId(shadeEvent.getId()).setShadePosition(positions).setPartialState()); } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index 0539e64dfe539..c7554fe85681d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -20,12 +20,12 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.hdpowerview.internal.discovery.HDPowerViewDeviceDiscoveryService; -import org.openhab.binding.hdpowerview.internal.gen3.discovery.ShadeDiscoveryService; -import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; -import org.openhab.binding.hdpowerview.internal.gen3.handler.ShadeThingHandler; +import org.openhab.binding.hdpowerview.internal.discovery.ShadeDiscoveryService; +import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewRepeaterHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewShadeHandler; +import org.openhab.binding.hdpowerview.internal.handler.ShadeThingHandler; import org.openhab.core.config.discovery.DiscoveryService; import org.openhab.core.i18n.LocaleProvider; import org.openhab.core.i18n.TranslationProvider; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java index 8b520ebe0a3e8..19c221a4aa5d2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Info3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -20,7 +20,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class Info3 { +public class Info { private @NonNullByDefault({}) String fwVersion; private @NonNullByDefault({}) String serialNumber; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java index 3e4a56eb84719..36c021676a406 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Scene3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -24,7 +24,7 @@ * @author Andrew Fiddian-Green - Initial contribution. */ @NonNullByDefault -public class Scene3 { +public class Scene { private int id; private @NonNullByDefault({}) String name; private @NonNullByDefault({}) String ptName; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java similarity index 81% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java index 47c306af7a8d6..b96ca16bc5615 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/SceneEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -20,15 +20,15 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class SceneEvent3 { +public class SceneEvent { private int id; - private @NonNullByDefault({}) Scene3 scene; + private @NonNullByDefault({}) Scene scene; public int getId() { return id; } - public Scene3 getScene() { + public Scene getScene() { return scene; } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java similarity index 86% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java index de085e1e2ae82..78e1a852aa0d7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Shade3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -31,7 +31,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class Shade3 { +public class Shade { private int id; private @Nullable Integer type; private @Nullable String name; @@ -42,7 +42,7 @@ public class Shade3 { private @Nullable Integer signalStrength; private @Nullable String bleName; private @Nullable Firmware firmware; - private @Nullable ShadePosition3 positions; + private @Nullable ShadePosition positions; private transient boolean partialState; @@ -86,7 +86,7 @@ public String getName() { } public State getPosition(CoordinateSystem posKindCoords) { - ShadePosition3 positions = this.positions; + ShadePosition positions = this.positions; return positions == null ? UnDefType.UNDEF : positions.getState(posKindCoords); } @@ -94,7 +94,7 @@ public State getPosition(CoordinateSystem posKindCoords) { return powerType; } - public @Nullable ShadePosition3 getShadePositions() { + public @Nullable ShadePosition getShadePositions() { return positions; } @@ -121,37 +121,37 @@ public boolean isMainsPowered() { return false; } - public Shade3 setCapabilities(int capabilities) { + public Shade setCapabilities(int capabilities) { this.capabilities = capabilities; return this; } - public Shade3 setId(int id) { + public Shade setId(int id) { this.id = id; return this; } - public Shade3 setPartialState() { + public Shade setPartialState() { this.partialState = true; return this; } - public Shade3 setPosition(CoordinateSystem coordinates, int percent) { - ShadePosition3 positions = this.positions; + public Shade setPosition(CoordinateSystem coordinates, int percent) { + ShadePosition positions = this.positions; if (positions == null) { - positions = new ShadePosition3(); + positions = new ShadePosition(); this.positions = positions; } positions.setPosition(coordinates, percent); return this; } - public Shade3 setShadePosition(ShadePosition3 position) { + public Shade setShadePosition(ShadePosition position) { this.positions = position; return this; } - public Shade3 setType(int type) { + public Shade setType(int type) { this.type = type; return this; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java similarity index 77% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java index e3fda1a402572..20cade4c9f61a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadeEvent3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -20,11 +20,11 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadeEvent3 { +public class ShadeEvent { private int id; - private @NonNullByDefault({}) ShadePosition3 currentPositions; + private @NonNullByDefault({}) ShadePosition currentPositions; - public ShadePosition3 getCurrentPositions() { + public ShadePosition getCurrentPositions() { return currentPositions; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java index 98f0003e868b0..b6e7ccaee8359 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/ShadePosition3.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.dto; +package org.openhab.binding.hdpowerview.internal.api.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; @@ -24,7 +24,7 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class ShadePosition3 { +public class ShadePosition { private @NonNullByDefault({}) Double primary; private @NonNullByDefault({}) Double secondary; private @NonNullByDefault({}) Double tilt; @@ -54,7 +54,7 @@ public State getState(CoordinateSystem posKindCoords) { * @param percent the new value in percent. * @return this object. */ - public ShadePosition3 setPosition(CoordinateSystem coordinates, int percent) { + public ShadePosition setPosition(CoordinateSystem coordinates, int percent) { Double value = Double.valueOf(percent) / 100.0; switch (coordinates) { case PRIMARY_POSITION: diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java index f2a062825fa63..b14425f8ce233 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/GatewayDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.discovery; +package org.openhab.binding.hdpowerview.internal.discovery; import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java index 7a2d1bf13ff42..76087bcfda9fc 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/discovery/ShadeDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.discovery; +package org.openhab.binding.hdpowerview.internal.discovery; import java.util.Collections; import java.util.concurrent.ScheduledFuture; @@ -18,12 +18,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.handler.GatewayBridgeHandler; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; +import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.thing.ThingUID; @@ -87,7 +87,7 @@ private Runnable createScanner() { private void discoverShades(GatewayWebTargets webTargets) throws HubProcessingException { ThingUID bridgeUid = hub.getThing().getUID(); - for (Shade3 shade : webTargets.getShades()) { + for (Shade shade : webTargets.getShades()) { if (shade.getId() == 0) { continue; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java deleted file mode 100644 index 1753a59672739..0000000000000 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/dto/Automation3.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.hdpowerview.internal.gen3.dto; - -import java.time.DayOfWeek; -import java.util.EnumSet; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; - -/** - * DTO for automation object as returned by an HD PowerView Generation 3 Gateway. - * - * @author Andrew Fiddian-Green - Initial contribution - */ -@NonNullByDefault -public class Automation3 { - public int id; - public int type; - public boolean enabled; - public int hour; - public int minute; - public int sceneId; - public @NonNullByDefault({}) String days; - - private static final int MON = 0x01; - private static final int TUE = 0x02; - private static final int WED = 0x04; - private static final int THU = 0x08; - private static final int FRI = 0x10; - private static final int SAT = 0x20; - private static final int SUN = 0x40; - - private static final int CLOCK_BASED = 0; - private static final int BEFORE_SUNRISE = 2; - private static final int BEFORE_SUNSET = 6; - private static final int AFTER_SUNRISE = 10; - private static final int AFTER_SUNSET = 14; - - @Override - public boolean equals(@Nullable Object o) { - if (o == this) { - return true; - } - if (!(o instanceof Automation3)) { - return false; - } - Automation3 other = (Automation3) o; - String days = this.days; - - return this.id == other.id && this.enabled == other.enabled && this.sceneId == other.sceneId - && (days != null && days.equals(other.days)) && this.hour == other.hour && this.minute == other.minute; - } - - public EnumSet getDays() { - EnumSet daySet = EnumSet.noneOf(DayOfWeek.class); - String days = this.days; - if (days != null) { - try { - int daysInt = Integer.valueOf(days).intValue(); - if ((daysInt & MON) != 0) { - daySet.add(DayOfWeek.MONDAY); - } - if ((daysInt & TUE) != 0) { - daySet.add(DayOfWeek.TUESDAY); - } - if ((daysInt & WED) != 0) { - daySet.add(DayOfWeek.WEDNESDAY); - } - if ((daysInt & THU) != 0) { - daySet.add(DayOfWeek.THURSDAY); - } - if ((daysInt & FRI) != 0) { - daySet.add(DayOfWeek.FRIDAY); - } - if ((daysInt & SAT) != 0) { - daySet.add(DayOfWeek.SATURDAY); - } - if ((daysInt & SUN) != 0) { - daySet.add(DayOfWeek.SUNDAY); - } - } catch (NumberFormatException e) { - // fall through - } - } - return daySet; - } - - public int getEventType() { - switch (type) { - case CLOCK_BASED: - return ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME; - - case BEFORE_SUNRISE: - case AFTER_SUNRISE: - // TODO handle before and after sunrise cases separately - return ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE; - - case BEFORE_SUNSET: - case AFTER_SUNSET: - // TODO handle before and after sunset cases separately - return ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET; - } - return 0; - } -} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java similarity index 95% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index 72b47443b943b..02753e2ff925d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.handler; +package org.openhab.binding.hdpowerview.internal.handler; import java.io.IOException; import java.util.ArrayList; @@ -23,13 +23,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jetty.client.HttpClient; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; +import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.Bridge; @@ -212,7 +212,7 @@ public void initialize() { * * @param scene the one that changed. */ - public void onSceneEvent(Scene3 scene) { + public void onSceneEvent(Scene scene) { // TODO perhaps we should trigger an OH core event here ?? } @@ -221,7 +221,7 @@ public void onSceneEvent(Scene3 scene) { * * @param shade the one that changed. */ - public void onShadeEvent(Shade3 shade) { + public void onShadeEvent(Shade shade) { try { for (ShadeThingHandler handler : getShadeThingHandlers()) { if (isDisposing || handler.notify(shade)) { @@ -254,7 +254,7 @@ private void refreshScenes() throws HubProcessingException, IllegalStateExceptio ChannelTypeUID typeUID = new ChannelTypeUID(channelTypeId); ChannelGroupUID groupUID = new ChannelGroupUID(thing.getUID(), channelGroupId); List channels = new ArrayList<>(); - for (Scene3 scene : getWebTargets().getScenes()) { + for (Scene scene : getWebTargets().getScenes()) { ChannelUID channelUID = new ChannelUID(groupUID, Integer.toString(scene.getId())); String name = scene.getName(); String description = translationProvider.getText("dynamic-channel.scene-activate.description", name); @@ -273,7 +273,7 @@ private void refreshScenes() throws HubProcessingException, IllegalStateExceptio */ private void refreshShades() throws HubProcessingException, IllegalStateException { List handlers = getShadeThingHandlers(); - for (Shade3 shade : getWebTargets().getShades()) { + for (Shade shade : getWebTargets().getShades()) { for (ShadeThingHandler handler : handlers) { if (isDisposing || handler.notify(shade)) { break; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index 97e368ad66e65..21c75263923df 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/gen3/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.gen3.handler; +package org.openhab.binding.hdpowerview.internal.handler; import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; @@ -22,12 +22,12 @@ import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StopMoveType; import org.openhab.core.library.types.StringType; @@ -60,7 +60,7 @@ public class ShadeThingHandler extends BaseThingHandler { private static final String COMMAND_CALIBRATE = "CALIBRATE"; private static final String COMMAND_IDENTIFY = "IDENTIFY"; - private final Shade3 thisShade = new Shade3(); + private final Shade thisShade = new Shade(); private boolean isInitialized; public ShadeThingHandler(Thing thing) { @@ -98,7 +98,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } GatewayWebTargets webTargets = getHandler().getWebTargets(); - ShadePosition3 position = new ShadePosition3(); + ShadePosition position = new ShadePosition(); int shadeId = thisShade.getId(); try { switch (channelUID.getId()) { @@ -184,11 +184,11 @@ public void initialize() { * @param shade the new shade state. * @return true if we handled the call. */ - public boolean notify(Shade3 shade) { + public boolean notify(Shade shade) { if (thisShade.getId() == shade.getId()) { updateStatus(ThingStatus.ONLINE); if (!isInitialized) { - ShadePosition3 position = shade.getShadePositions(); + ShadePosition position = shade.getShadePositions(); if (position != null) { thisShade.setShadePosition(position); } @@ -207,7 +207,7 @@ public boolean notify(Shade3 shade) { * * @param shade containing the channel data. */ - private void updateChannels(Shade3 shade) { + private void updateChannels(Shade shade) { updateState(CHANNEL_SHADE_POSITION, shade.getPosition(PRIMARY_POSITION)); updateState(CHANNEL_SHADE_VANE, shade.getPosition(VANE_TILT_POSITION)); updateState(CHANNEL_SHADE_SECONDARY_POSITION, shade.getPosition(SECONDARY_POSITION)); @@ -241,10 +241,10 @@ private void updateDynamicChannel(List removeList, String channelId, bo * * @param shade containing the channel data. */ - private void updateDynamicChannels(Shade3 shade) { + private void updateDynamicChannels(Shade shade) { List removeChannels = new ArrayList<>(); - ShadePosition3 positions = shade.getShadePositions(); + ShadePosition positions = shade.getShadePositions(); if (positions != null) { updateDynamicChannel(removeChannels, CHANNEL_SHADE_POSITION, positions.supportsPrimary()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_SECONDARY_POSITION, positions.supportsSecondary()); @@ -270,7 +270,7 @@ private void updateDynamicChannels(Shade3 shade) { * * @param shade containing the property data. */ - private void updateProperties(Shade3 shade) { + private void updateProperties(Shade shade) { if (shade.hasFullState()) { thing.setProperties(Stream.of(new String[][] { // { HDPowerViewBindingConstants.PROPERTY_NAME, shade.getName() }, diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java index 863a7e0bce08a..bd36cfeacf808 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java @@ -22,14 +22,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Automation3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Scene3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.SceneEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.Shade3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadeEvent3; -import org.openhab.binding.hdpowerview.internal.gen3.dto.ShadePosition3; -import org.openhab.binding.hdpowerview.internal.gen3.webtargets.GatewayWebTargets; +import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.api.gen3.SceneEvent; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.UnDefType; @@ -58,29 +57,15 @@ private String loadJson(String filename) throws IOException { } } - /** - * Test JSON automation response. - */ - @Test - public void testAutomationParsing() throws IOException { - String json = loadJson("gen3/automations.json"); - List scheduledEventList = gson.fromJson(json, GatewayWebTargets.LIST_EVENTS); - assertNotNull(scheduledEventList); - assertEquals(1, scheduledEventList.size()); - Automation3 scheduledEvent = scheduledEventList.get(0); - assertEquals(33, scheduledEvent.id); - assertTrue(scheduledEvent.enabled); - } - /** * Test JSON scene event response. */ @Test public void testSceneEventParsing() throws IOException { String json = loadJson("gen3/scene-event.json"); - SceneEvent3 sceneEvent = gson.fromJson(json, SceneEvent3.class); + SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); assertNotNull(sceneEvent); - Scene3 scene = sceneEvent.getScene(); + Scene scene = sceneEvent.getScene(); assertNotNull(scene); assertEquals("Open All Office Shades\n Open All Office Shades", scene.getName()); assertEquals(234, scene.getId()); @@ -92,10 +77,10 @@ public void testSceneEventParsing() throws IOException { @Test public void testScenesParsing() throws IOException { String json = loadJson("gen3/scenes.json"); - List sceneList = gson.fromJson(json, GatewayWebTargets.LIST_SCENES); + List sceneList = gson.fromJson(json, GatewayWebTargets.LIST_SCENES); assertNotNull(sceneList); assertEquals(1, sceneList.size()); - Scene3 scene = sceneList.get(0); + Scene scene = sceneList.get(0); assertEquals("Open All Office Shades\n ABC", scene.getName()); assertEquals(234, scene.getId()); } @@ -106,9 +91,9 @@ public void testScenesParsing() throws IOException { @Test public void testShadeEventParsing() throws IOException { String json = loadJson("gen3/shade-event.json"); - ShadeEvent3 shadeEvent = gson.fromJson(json, ShadeEvent3.class); + ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); assertNotNull(shadeEvent); - ShadePosition3 position = shadeEvent.getCurrentPositions(); + ShadePosition position = shadeEvent.getCurrentPositions(); assertNotNull(position); assertEquals(PercentType.valueOf("99"), position.getState(CoordinateSystem.PRIMARY_POSITION)); assertEquals(PercentType.valueOf("98"), position.getState(CoordinateSystem.SECONDARY_POSITION)); @@ -120,15 +105,15 @@ public void testShadeEventParsing() throws IOException { */ @Test public void testShadePositions() { - ShadePosition3 pos; + ShadePosition pos; - pos = new ShadePosition3(); + pos = new ShadePosition(); pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION)); assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.SECONDARY_POSITION)); assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.VANE_TILT_POSITION)); - pos = new ShadePosition3(); + pos = new ShadePosition(); pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); pos.setPosition(CoordinateSystem.SECONDARY_POSITION, 22); pos.setPosition(CoordinateSystem.VANE_TILT_POSITION, 33); @@ -143,10 +128,10 @@ public void testShadePositions() { @Test public void testShadesParsing() throws IOException { String json = loadJson("gen3/shades.json"); - List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); + List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); assertNotNull(shadeList); assertEquals(1, shadeList.size()); - Shade3 shadeData = shadeList.get(0); + Shade shadeData = shadeList.get(0); assertEquals("Shade 2 ABC", shadeData.getName()); assertEquals(789, shadeData.getId()); } From 40011e5a063e1dc025f866ff61f79f503e457fd1 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 20:29:18 +0100 Subject: [PATCH 42/64] [hdpowerview] console changes Signed-off-by: Andrew Fiddian-Green --- .../console/HDPowerViewCommandExtension.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java index 4252f8d57bb2b..78dc10aa1cf07 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java @@ -17,11 +17,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; +import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; +import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; import org.openhab.core.io.console.Console; import org.openhab.core.io.console.ConsoleCommandCompleter; @@ -65,7 +68,7 @@ public void execute(String[] args, Console console) { for (Thing thing : thingRegistry.getAll()) { ThingHandler thingHandler = thing.getHandler(); if (thingHandler instanceof HDPowerViewHubHandler) { - console.println("API bridge: " + thing.getLabel()); + console.println("Generation 1/2 API bridge: " + thing.getLabel()); HDPowerViewWebTargets webTargets = ((HDPowerViewHubHandler) thingHandler).getWebTargets(); try { @@ -87,6 +90,21 @@ public void execute(String[] args, Console console) { } catch (HubException e) { console.println("Error retrieving ID's: " + e.getMessage()); } + } else if (thingHandler instanceof GatewayBridgeHandler) { + console.println("Generation 3 API bridge: " + thing.getLabel()); + GatewayWebTargets webTargets = ((GatewayBridgeHandler) thingHandler).getWebTargets(); + + try { + List shades = webTargets.getShades(); + if (!shades.isEmpty()) { + console.println(" - Shades:"); + for (Shade shade : shades) { + console.println(" - ID: " + shade.getId() + " (" + shade.getName() + ")"); + } + } + } catch (HubException e) { + console.println("Error retrieving ID's: " + e.getMessage()); + } } } } From c1077746ccecd01d586056b9fc59439325b934b0 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 28 Oct 2022 23:39:42 +0100 Subject: [PATCH 43/64] [hdpowerview] console texts Signed-off-by: Andrew Fiddian-Green --- .../internal/console/HDPowerViewCommandExtension.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java index 78dc10aa1cf07..91240155bfa60 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java @@ -68,7 +68,7 @@ public void execute(String[] args, Console console) { for (Thing thing : thingRegistry.getAll()) { ThingHandler thingHandler = thing.getHandler(); if (thingHandler instanceof HDPowerViewHubHandler) { - console.println("Generation 1/2 API bridge: " + thing.getLabel()); + console.println("Generation 1/2 API hub: " + thing.getLabel()); HDPowerViewWebTargets webTargets = ((HDPowerViewHubHandler) thingHandler).getWebTargets(); try { @@ -91,7 +91,7 @@ public void execute(String[] args, Console console) { console.println("Error retrieving ID's: " + e.getMessage()); } } else if (thingHandler instanceof GatewayBridgeHandler) { - console.println("Generation 3 API bridge: " + thing.getLabel()); + console.println("Generation 3 API gateway: " + thing.getLabel()); GatewayWebTargets webTargets = ((GatewayBridgeHandler) thingHandler).getWebTargets(); try { @@ -111,7 +111,7 @@ public void execute(String[] args, Console console) { @Override public List getUsages() { - return Arrays.asList(buildCommandUsage(SHOW_IDS, "list all shades and repeaters")); + return Arrays.asList(buildCommandUsage(SHOW_IDS, "list all shades and eventually repeaters")); } @Override From 624b6b30dab091a04b50f14b731c42b2399b0e8e Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Tue, 1 Nov 2022 14:25:11 +0000 Subject: [PATCH 44/64] [hdpowerview] move modules Signed-off-by: Andrew Fiddian-Green --- .../internal/GatewayWebTargets.java | 14 ++--- .../internal/HDPowerViewWebTargets.java | 52 +++++++++---------- .../builders/AutomationChannelBuilder.java | 8 +-- .../builders/SceneChannelBuilder.java | 2 +- .../builders/SceneGroupChannelBuilder.java | 2 +- .../console/HDPowerViewCommandExtension.java | 6 +-- .../HDPowerViewDeviceDiscoveryService.java | 6 +-- .../discovery/ShadeDiscoveryService.java | 2 +- .../internal/{api => dto}/BatteryKind.java | 2 +- .../internal/{api => dto}/Color.java | 2 +- .../{api => dto}/CoordinateSystem.java | 2 +- .../internal/{api => dto}/Firmware.java | 2 +- .../internal/{api => dto}/HubFirmware.java | 2 +- .../internal/{api => dto}/ShadePosition.java | 4 +- .../internal/{api => dto}/SurveyData.java | 2 +- .../internal/{api => dto}/Times.java | 2 +- .../internal/{api => dto}/UserData.java | 2 +- .../internal/{api => dto}/gen3/Info.java | 2 +- .../internal/{api => dto}/gen3/Scene.java | 2 +- .../{api => dto}/gen3/SceneEvent.java | 2 +- .../internal/{api => dto}/gen3/Shade.java | 6 +-- .../{api => dto}/gen3/ShadeEvent.java | 2 +- .../{api => dto}/gen3/ShadePosition.java | 4 +- .../requests/RepeaterBlinking.java | 2 +- .../{api => dto}/requests/RepeaterColor.java | 4 +- .../{api => dto}/requests/ShadeCalibrate.java | 2 +- .../{api => dto}/requests/ShadeJog.java | 2 +- .../{api => dto}/requests/ShadeMotion.java | 2 +- .../{api => dto}/requests/ShadeMove.java | 4 +- .../{api => dto}/requests/ShadePositions.java | 4 +- .../{api => dto}/requests/ShadeStop.java | 2 +- .../responses/FirmwareVersion.java | 4 +- .../{api => dto}/responses/Repeater.java | 2 +- .../{api => dto}/responses/RepeaterData.java | 6 +-- .../{api => dto}/responses/Repeaters.java | 2 +- .../responses/SceneCollections.java | 2 +- .../{api => dto}/responses/Scenes.java | 2 +- .../responses/ScheduledEvents.java | 2 +- .../{api => dto}/responses/Shade.java | 4 +- .../{api => dto}/responses/Shades.java | 8 +-- .../{api => dto}/responses/Survey.java | 4 +- .../responses/UserDataResponse.java | 4 +- .../handler/GatewayBridgeHandler.java | 4 +- .../handler/HDPowerViewHubHandler.java | 22 ++++---- .../handler/HDPowerViewRepeaterHandler.java | 6 +-- .../handler/HDPowerViewShadeHandler.java | 14 ++--- .../internal/handler/ShadeThingHandler.java | 6 +-- .../AutomationChannelBuilderTest.java | 16 +++--- .../{ => internal}/HDPowerViewJUnitTests.java | 20 +++---- .../OnlineCommunicationTest.java | 15 +++--- .../SceneChannelBuilderTest.java | 10 ++-- .../SceneGroupChannelBuilderTest.java | 10 ++-- .../{ => internal}/ShadePositionTest.java | 6 +-- .../gen3/Generation3DtoTest.java | 16 +++--- .../providers/MockedLocaleProvider.java | 2 +- .../providers/MockedTranslationProvider.java | 2 +- .../hdpowerview/{ => internal}/duette.json | 0 .../{ => internal}/gen3/automations.json | 0 .../{ => internal}/gen3/scene-event.json | 0 .../{ => internal}/gen3/scenes.json | 0 .../{ => internal}/gen3/shade-event.json | 0 .../{ => internal}/gen3/shades.json | 0 .../{ => internal}/sceneCollections.json | 0 .../hdpowerview/{ => internal}/scenes.json | 0 .../hdpowerview/{ => internal}/shades.json | 0 65 files changed, 167 insertions(+), 174 deletions(-) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/BatteryKind.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/Color.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/CoordinateSystem.java (98%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/Firmware.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/HubFirmware.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/ShadePosition.java (99%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/SurveyData.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/Times.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/UserData.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/Info.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/Scene.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/SceneEvent.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/Shade.java (96%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/ShadeEvent.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/gen3/ShadePosition.java (95%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/RepeaterBlinking.java (93%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/RepeaterColor.java (88%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeCalibrate.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeJog.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeMotion.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeMove.java (85%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadePositions.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/requests/ShadeStop.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/FirmwareVersion.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Repeater.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/RepeaterData.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Repeaters.java (92%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/SceneCollections.java (97%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Scenes.java (97%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/ScheduledEvents.java (98%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Shade.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Shades.java (89%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/Survey.java (87%) rename bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/{api => dto}/responses/UserDataResponse.java (84%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/AutomationChannelBuilderTest.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/HDPowerViewJUnitTests.java (92%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/OnlineCommunicationTest.java (94%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/SceneChannelBuilderTest.java (90%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/SceneGroupChannelBuilderTest.java (91%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/ShadePositionTest.java (99%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/gen3/Generation3DtoTest.java (90%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/providers/MockedLocaleProvider.java (92%) rename bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/{ => internal}/providers/MockedTranslationProvider.java (97%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/duette.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/automations.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/scene-event.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/shade-event.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/gen3/shades.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/sceneCollections.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/scenes.json (100%) rename bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/{ => internal}/shades.json (100%) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 12300adc1746e..783bad3dda974 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -37,13 +37,13 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets.Query; -import org.openhab.binding.hdpowerview.internal.api.gen3.Info; -import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; -import org.openhab.binding.hdpowerview.internal.api.gen3.SceneEvent; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadeEvent; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMotion; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Info; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.dto.gen3.SceneEvent; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeMotion; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.core.thing.Thing; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 562d37bc504c7..2ab79180bde13 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -27,32 +27,32 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterBlinking; -import org.openhab.binding.hdpowerview.internal.api.requests.RepeaterColor; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeCalibrate; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeJog; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeMove; -import org.openhab.binding.hdpowerview.internal.api.requests.ShadeStop; -import org.openhab.binding.hdpowerview.internal.api.responses.FirmwareVersion; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeater; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Repeaters; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.Shade; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; -import org.openhab.binding.hdpowerview.internal.api.responses.Survey; -import org.openhab.binding.hdpowerview.internal.api.responses.UserDataResponse; +import org.openhab.binding.hdpowerview.internal.dto.Color; +import org.openhab.binding.hdpowerview.internal.dto.HubFirmware; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.SurveyData; +import org.openhab.binding.hdpowerview.internal.dto.UserData; +import org.openhab.binding.hdpowerview.internal.dto.requests.RepeaterBlinking; +import org.openhab.binding.hdpowerview.internal.dto.requests.RepeaterColor; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeCalibrate; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeJog; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeMove; +import org.openhab.binding.hdpowerview.internal.dto.requests.ShadeStop; +import org.openhab.binding.hdpowerview.internal.dto.responses.FirmwareVersion; +import org.openhab.binding.hdpowerview.internal.dto.responses.Repeater; +import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Repeaters; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shade; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Survey; +import org.openhab.binding.hdpowerview.internal.dto.responses.UserDataResponse; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java index 10782b22b991c..f48e722c7bebe 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/AutomationChannelBuilder.java @@ -25,10 +25,10 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents.ScheduledEvent; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java index 01c05137e9ece..7c866458f3bf7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneChannelBuilder.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneGroupChannelBuilder.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneGroupChannelBuilder.java index 4cc4436ea2f32..54d9c8a0cfc1d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneGroupChannelBuilder.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/builders/SceneGroupChannelBuilder.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java index 91240155bfa60..d5f633d80338e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java @@ -20,9 +20,9 @@ import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java index 98d108befcfb1..9e74c946e2a62 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java @@ -21,13 +21,13 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java index 76087bcfda9fc..68ced751e154c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java @@ -20,8 +20,8 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; import org.openhab.core.config.discovery.AbstractDiscoveryService; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/BatteryKind.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/BatteryKind.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/BatteryKind.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/BatteryKind.java index 229d0abce377c..a3b79bf47f998 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/BatteryKind.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/BatteryKind.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Color.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Color.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Color.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Color.java index 39f1d0f47b24f..7179690638457 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Color.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Color.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.library.types.HSBType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/CoordinateSystem.java similarity index 98% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/CoordinateSystem.java index 236e468947820..47c4a1bb7d2b6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/CoordinateSystem.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Firmware.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Firmware.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Firmware.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Firmware.java index fd627c8b9b146..c17d76e99eff2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Firmware.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Firmware.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/HubFirmware.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/HubFirmware.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/HubFirmware.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/HubFirmware.java index 0a625b2f160ff..5deb23bd566b9 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/HubFirmware.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/HubFirmware.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/ShadePosition.java similarity index 99% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/ShadePosition.java index 9b66e798bbcbc..96d8bea648729 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/ShadePosition.java @@ -10,9 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/SurveyData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/SurveyData.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/SurveyData.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/SurveyData.java index bfb5301bac539..7f3da5ff9c372 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/SurveyData.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/SurveyData.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Times.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Times.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Times.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Times.java index 0dbc911bfd94f..33a9bc99f3b91 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/Times.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/Times.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/UserData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/UserData.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/UserData.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/UserData.java index 70c33a9189e31..fd36992246cb7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/UserData.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/UserData.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api; +package org.openhab.binding.hdpowerview.internal.dto; import java.util.Base64; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java index 19c221a4aa5d2..0d98838a67f81 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Info.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java index 36c021676a406..144878f2eaa88 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Scene.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import java.nio.charset.StandardCharsets; import java.util.Base64; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java index b96ca16bc5615..58d557886c06a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/SceneEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java similarity index 96% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java index 78e1a852aa0d7..aa64287c90722 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import java.nio.charset.StandardCharsets; import java.util.Base64; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java index 20cade4c9f61a..7ac276735fdeb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadeEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java similarity index 95% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java index b6e7ccaee8359..85316f893bf73 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/gen3/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.gen3; +package org.openhab.binding.hdpowerview.internal.dto.gen3; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterBlinking.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterBlinking.java similarity index 93% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterBlinking.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterBlinking.java index ea3886af1f1c4..e50a308448b90 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterBlinking.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterBlinking.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterColor.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterColor.java similarity index 88% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterColor.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterColor.java index 960e95f5878c9..fc317e7aa1dab 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/RepeaterColor.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/RepeaterColor.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.Color; +import org.openhab.binding.hdpowerview.internal.dto.Color; /** * Color state of a single Repeater for being updated by an HD PowerView Hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeCalibrate.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeCalibrate.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeCalibrate.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeCalibrate.java index e1ec80e48743c..0d1d50ec9dc19 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeCalibrate.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeCalibrate.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeJog.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeJog.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeJog.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeJog.java index 71fb0477a09ec..b1317a7eabb32 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeJog.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeJog.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMotion.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMotion.java index 13d3179c4841a..c78fc1dca421f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMotion.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMotion.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMove.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMove.java similarity index 85% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMove.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMove.java index fe688f4f75b58..4f87fdf0c3358 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeMove.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeMove.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; /** * A request to set the position of a shade diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadePositions.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadePositions.java index 1c544c047e878..fd98950f5d816 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadePositions.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadePositions.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; /** * The position of a shade to set diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeStop.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeStop.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeStop.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeStop.java index ed7ab4c4b0a8a..34f47f038ca7a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/requests/ShadeStop.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/requests/ShadeStop.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.requests; +package org.openhab.binding.hdpowerview.internal.dto.requests; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/FirmwareVersion.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/FirmwareVersion.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/FirmwareVersion.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/FirmwareVersion.java index d046f06a8d760..34b9f202eb942 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/FirmwareVersion.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/FirmwareVersion.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; +import org.openhab.binding.hdpowerview.internal.dto.HubFirmware; /** * Firmware information for an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeater.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeater.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeater.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeater.java index 55ba66665b14f..d743626d9605c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeater.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeater.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/RepeaterData.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/RepeaterData.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/RepeaterData.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/RepeaterData.java index a083f91a35a76..1b789637ddded 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/RepeaterData.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/RepeaterData.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.util.Base64; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.Color; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; /** * Repeater data for a single Repeater, as returned by an HD PowerView Hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeaters.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeaters.java similarity index 92% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeaters.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeaters.java index 8b5f15b12fd69..e1aa699b49484 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Repeaters.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Repeaters.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.util.List; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/SceneCollections.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/SceneCollections.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/SceneCollections.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/SceneCollections.java index bef9e94b93329..bcf87c9671497 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/SceneCollections.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/SceneCollections.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.nio.charset.StandardCharsets; import java.util.Base64; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Scenes.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Scenes.java index 5cb25a8aab0b4..72de7c35d6cb4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Scenes.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Scenes.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.nio.charset.StandardCharsets; import java.util.Base64; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/ScheduledEvents.java similarity index 98% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/ScheduledEvents.java index e7ae6ac1ae440..41a64b240ace0 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/ScheduledEvents.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/ScheduledEvents.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.time.DayOfWeek; import java.util.EnumSet; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shade.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shade.java index 69da9378395b7..f585b98ebad06 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shade.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; /** * State of a single Shade, as returned by an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shades.java similarity index 89% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shades.java index 2746523b47a97..868f87ff38892 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Shades.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Shades.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.util.Base64; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.BatteryKind; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; /** * State of all Shades, as returned by an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Survey.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Survey.java similarity index 87% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Survey.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Survey.java index 21709fe1a0b9a..e55a1f99f84b4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/Survey.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/Survey.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import java.util.List; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; +import org.openhab.binding.hdpowerview.internal.dto.SurveyData; import com.google.gson.annotations.SerializedName; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/UserDataResponse.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/UserDataResponse.java similarity index 84% rename from bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/UserDataResponse.java rename to bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/UserDataResponse.java index 3509ffdb85c6f..15c4cbeae7b06 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/responses/UserDataResponse.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/responses/UserDataResponse.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.internal.api.responses; +package org.openhab.binding.hdpowerview.internal.dto.responses; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hdpowerview.internal.api.UserData; +import org.openhab.binding.hdpowerview.internal.dto.UserData; /** * Response with {@link UserData} for an HD PowerView hub diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index 02753e2ff925d..6110f16dc7207 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -26,9 +26,9 @@ import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.OnOffType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java index b3991f3aad3bd..f5c9b89bdf0af 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewHubHandler.java @@ -30,22 +30,22 @@ import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.HubFirmware; -import org.openhab.binding.hdpowerview.internal.api.UserData; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewHubConfiguration; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.HubFirmware; +import org.openhab.binding.hdpowerview.internal.dto.UserData; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewRepeaterHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewRepeaterHandler.java index 037c85184d167..c8080fa2c354b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewRepeaterHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewRepeaterHandler.java @@ -20,10 +20,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.Color; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewRepeaterConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.Color; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java index 53e77e0425cdb..66d7056200683 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java @@ -13,7 +13,7 @@ package org.openhab.binding.hdpowerview.internal.handler; import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import java.util.ArrayList; import java.util.HashMap; @@ -29,15 +29,15 @@ import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.Firmware; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.SurveyData; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.BatteryKind; +import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.dto.Firmware; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.SurveyData; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index 21c75263923df..16074faa05992 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -13,7 +13,7 @@ package org.openhab.binding.hdpowerview.internal.handler; import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import java.util.ArrayList; import java.util.List; @@ -24,9 +24,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.StopMoveType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/AutomationChannelBuilderTest.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/AutomationChannelBuilderTest.java index ba6cb99574fc4..fcadf7a03593f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/AutomationChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/AutomationChannelBuilderTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -22,15 +22,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents; -import org.openhab.binding.hdpowerview.internal.api.responses.ScheduledEvents.ScheduledEvent; import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder; -import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; -import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents; +import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents.ScheduledEvent; +import org.openhab.binding.hdpowerview.internal.providers.MockedLocaleProvider; +import org.openhab.binding.hdpowerview.internal.providers.MockedTranslationProvider; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; import org.openhab.core.thing.ThingUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/HDPowerViewJUnitTests.java similarity index 92% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/HDPowerViewJUnitTests.java index 19cd35c7c33ee..e7b05686c3b70 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/HDPowerViewJUnitTests.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import java.io.IOException; import java.io.InputStream; @@ -23,16 +23,16 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.api.BatteryKind; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.BatteryKind; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/OnlineCommunicationTest.java similarity index 94% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/OnlineCommunicationTest.java index b8670bb91ec92..7aca650e833e2 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/OnlineCommunicationTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/OnlineCommunicationTest.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import java.util.List; import java.util.regex.Pattern; @@ -21,14 +21,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades; -import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades; +import org.openhab.binding.hdpowerview.internal.dto.responses.Shades.ShadeData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneChannelBuilderTest.java similarity index 90% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneChannelBuilderTest.java index eca2a1f2e9ebb..859f30cb0bf6b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneChannelBuilderTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -22,12 +22,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.Scenes.Scene; import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder; -import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; -import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; +import org.openhab.binding.hdpowerview.internal.dto.responses.Scenes.Scene; +import org.openhab.binding.hdpowerview.internal.providers.MockedLocaleProvider; +import org.openhab.binding.hdpowerview.internal.providers.MockedTranslationProvider; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; import org.openhab.core.thing.ThingUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneGroupChannelBuilderTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneGroupChannelBuilderTest.java similarity index 91% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneGroupChannelBuilderTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneGroupChannelBuilderTest.java index e2dc8b1b7a0da..d1a4f8c91e90d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/SceneGroupChannelBuilderTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/SceneGroupChannelBuilderTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; @@ -22,12 +22,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; -import org.openhab.binding.hdpowerview.internal.HDPowerViewTranslationProvider; -import org.openhab.binding.hdpowerview.internal.api.responses.SceneCollections.SceneCollection; import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder; -import org.openhab.binding.hdpowerview.providers.MockedLocaleProvider; -import org.openhab.binding.hdpowerview.providers.MockedTranslationProvider; +import org.openhab.binding.hdpowerview.internal.dto.responses.SceneCollections.SceneCollection; +import org.openhab.binding.hdpowerview.internal.providers.MockedLocaleProvider; +import org.openhab.binding.hdpowerview.internal.providers.MockedTranslationProvider; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; import org.openhab.core.thing.ThingUID; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/ShadePositionTest.java similarity index 99% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/ShadePositionTest.java index 50dcd0e7ff4c2..5ced717f203b0 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/ShadePositionTest.java @@ -10,16 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview; +package org.openhab.binding.hdpowerview.internal; import static org.junit.jupiter.api.Assertions.*; -import static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*; +import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*; import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.api.ShadePosition; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.dto.ShadePosition; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java similarity index 90% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java index bd36cfeacf808..beb0c4d9778ba 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.gen3; +package org.openhab.binding.hdpowerview.internal.gen3; import static org.junit.jupiter.api.Assertions.*; @@ -21,14 +21,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.HDPowerViewJUnitTests; import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; -import org.openhab.binding.hdpowerview.internal.api.CoordinateSystem; -import org.openhab.binding.hdpowerview.internal.api.gen3.Scene; -import org.openhab.binding.hdpowerview.internal.api.gen3.SceneEvent; -import org.openhab.binding.hdpowerview.internal.api.gen3.Shade; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadeEvent; -import org.openhab.binding.hdpowerview.internal.api.gen3.ShadePosition; +import org.openhab.binding.hdpowerview.internal.HDPowerViewJUnitTests; +import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Scene; +import org.openhab.binding.hdpowerview.internal.dto.gen3.SceneEvent; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadeEvent; +import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition; import org.openhab.core.library.types.PercentType; import org.openhab.core.types.UnDefType; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedLocaleProvider.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedLocaleProvider.java similarity index 92% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedLocaleProvider.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedLocaleProvider.java index 8161cc8e28274..d95879f454ea6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedLocaleProvider.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedLocaleProvider.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.providers; +package org.openhab.binding.hdpowerview.internal.providers; import java.util.Locale; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedTranslationProvider.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedTranslationProvider.java similarity index 97% rename from bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedTranslationProvider.java rename to bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedTranslationProvider.java index da7152ad7504c..9c9503ef1eee7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/providers/MockedTranslationProvider.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/providers/MockedTranslationProvider.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.hdpowerview.providers; +package org.openhab.binding.hdpowerview.internal.providers; import static java.util.Map.entry; diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/duette.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/duette.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/duette.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/automations.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/automations.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/automations.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/automations.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scene-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/scene-event.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scene-event.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/scene-event.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shade-event.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shade-event.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shade-event.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shade-event.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/gen3/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/sceneCollections.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/sceneCollections.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/sceneCollections.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/scenes.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/scenes.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/scenes.json diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/shades.json similarity index 100% rename from bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/shades.json rename to bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/shades.json From 62120f8fb5b0c9570ab0ecab28904d0cfd6a0829 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 12 Nov 2022 11:37:32 +0000 Subject: [PATCH 45/64] [hdpowerview] add import Signed-off-by: Andrew Fiddian-Green --- .../internal/console/HDPowerViewCommandExtension.java | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java index 60f3f90008a41..c163b6412f549 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java @@ -21,6 +21,7 @@ import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets; import org.openhab.binding.hdpowerview.internal.dto.ShadeData; +import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData; import org.openhab.binding.hdpowerview.internal.exceptions.HubException; import org.openhab.binding.hdpowerview.internal.handler.GatewayBridgeHandler; From 13c969912ca60b2972c024e12df328ea38b42288 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 18 Nov 2022 16:39:32 +0000 Subject: [PATCH 46/64] [hdpowerview] adopt SSE learnings from hue clip 2 testing Signed-off-by: Andrew Fiddian-Green --- .../internal/GatewayWebTargets.java | 258 +++++++++++++----- .../handler/GatewayBridgeHandler.java | 8 +- 2 files changed, 191 insertions(+), 75 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 783bad3dda974..05b5c5a4f2535 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -18,12 +18,16 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import javax.net.ssl.SSLContext; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; +import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.WebTarget; import javax.ws.rs.sse.InboundSseEvent; import javax.ws.rs.sse.SseEventSource; @@ -61,9 +65,10 @@ * @author Andrew Fiddian-Green - Initial contribution */ @NonNullByDefault -public class GatewayWebTargets implements Closeable { +public class GatewayWebTargets implements Closeable, HostnameVerifier { private static final String IDS = "ids"; + private static final int SLEEP_SECONDS = 360; // @formatter:off public static final Type LIST_SHADES = new TypeToken>() {}.getType(); @@ -78,31 +83,23 @@ public class GatewayWebTargets implements Closeable { private final String shadeStop; private final String shadePositions; private final String info; - private final String register; private final String shadeEvents; private final String sceneEvents; - private final Gson gson = new Gson(); + private final Gson jsonParser = new Gson(); private final HttpClient httpClient; - private final ClientBuilder clientBuilder; private final SseEventSourceFactory eventSourceFactory; private final GatewayBridgeHandler hubHandler; + private final String hostName; private boolean isRegistered; + private boolean closing; private @Nullable SseEventSource shadeEventSource; private @Nullable SseEventSource sceneEventSource; - /** - * Simple DTO for registering the binding with the hub. - * - * @author Andrew Fiddian-Green - Initial contribution - */ - @SuppressWarnings("unused") - private static class GatewayRegistration { - public String todo = "org.openhab.binding.hdpowerview"; // TODO - } + private @Nullable ScheduledFuture sseQuietCheck; /** * Initialize the web targets @@ -115,6 +112,8 @@ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, String base = "http://" + ipAddress + "/"; String home = base + "home/"; + hostName = ipAddress; + shades = home + "shades"; scenes = home + "scenes"; sceneActivate = home + "scenes/%d/activate"; @@ -125,7 +124,8 @@ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, sceneEvents = home + "scenes/events"; info = base + "gateway/info"; - register = "TBD"; // TODO + + register = "TODO"; // TODO waiting for Hunter Douglas to provide the end point URL this.httpClient = httpClient; this.clientBuilder = clientBuilder; @@ -150,23 +150,14 @@ public void activateScene(int sceneId) throws HubProcessingException { * @throws HubProcessingException if any error occurs. */ public void calibrateShade(int shadeId) throws HubProcessingException { - String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.CALIBRATE)); + String json = jsonParser.toJson(new ShadeMotion(ShadeMotion.Type.CALIBRATE)); invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); } @Override public void close() throws IOException { - SseEventSource source; - source = this.shadeEventSource; - if (source != null) { - source.close(); - this.shadeEventSource = null; - } - source = this.sceneEventSource; - if (source != null) { - source.close(); - this.sceneEventSource = null; - } + closing = true; + sseClose(); } /** @@ -174,9 +165,14 @@ public void close() throws IOException { * * @throws HubProcessingException if any error occurs. */ - private void gatewayRegister() throws HubProcessingException { + public void gatewayRegister() throws HubProcessingException { + // TODO waiting for Hunter Douglas to provide registration details if (!isRegistered) { - String json = gson.toJson(new GatewayRegistration()); + final class GatewayRegistration { + @SuppressWarnings("unused") + public String todo = "org.openhab.binding.hdpowerview"; + } + String json = jsonParser.toJson(new GatewayRegistration()); invoke(HttpMethod.PUT, register, null, json); isRegistered = true; } @@ -191,7 +187,7 @@ private void gatewayRegister() throws HubProcessingException { public Map getInformation() throws HubProcessingException { String json = invoke(HttpMethod.GET, info, null, null); try { - Info result = gson.fromJson(json, Info.class); + Info result = jsonParser.fromJson(json, Info.class); if (result == null) { throw new HubProcessingException("getInformation(): missing response"); } @@ -212,7 +208,7 @@ public Map getInformation() throws HubProcessingException { public List getScenes() throws HubProcessingException { String json = invoke(HttpMethod.GET, scenes, null, null); try { - List result = gson.fromJson(json, LIST_SCENES); + List result = jsonParser.fromJson(json, LIST_SCENES); if (result == null) { throw new HubProcessingException("getScenes() missing response"); } @@ -232,7 +228,7 @@ public List getScenes() throws HubProcessingException { public Shade getShade(int shadeId) throws HubProcessingException { String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); try { - Shade result = gson.fromJson(json, Shade.class); + Shade result = jsonParser.fromJson(json, Shade.class); if (result == null) { throw new HubProcessingException("getShade() missing response"); } @@ -251,7 +247,7 @@ public Shade getShade(int shadeId) throws HubProcessingException { public List getShades() throws HubProcessingException { String json = invoke(HttpMethod.GET, shades, null, null); try { - List result = gson.fromJson(json, LIST_SHADES); + List result = jsonParser.fromJson(json, LIST_SHADES); if (result == null) { throw new HubProcessingException("getShades() missing response"); } @@ -322,7 +318,7 @@ protected synchronized String invoke(HttpMethod method, String url, @Nullable Qu * @throws HubProcessingException if any error occurs. */ public void jogShade(int shadeId) throws HubProcessingException { - String json = gson.toJson(new ShadeMotion(ShadeMotion.Type.JOG)); + String json = jsonParser.toJson(new ShadeMotion(ShadeMotion.Type.JOG)); invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); } @@ -335,7 +331,19 @@ public void jogShade(int shadeId) throws HubProcessingException { */ public void moveShade(int shadeId, ShadePosition position) throws HubProcessingException { invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), - gson.toJson(position)); + jsonParser.toJson(position)); + } + + /** + * Handle SSE errors. + * For the time being just log them, because the framework should automatically recover itself. + * + * @param e the error that was thrown. + */ + private void onSseSceneError(Throwable e) { + if (!closing) { + logger.debug("onSseSceneError() {}", e.getMessage(), e); + } } /** @@ -343,25 +351,65 @@ public void moveShade(int shadeId, ShadePosition position) throws HubProcessingE * * @param sseEvent the inbound event. */ - private void onSceneEvent(InboundSseEvent sseEvent) { + private void onSseSceneEvent(InboundSseEvent sseEvent) { + if (closing) { + return; + } + ScheduledFuture task = sseQuietCheck; + if (task != null && !task.isCancelled()) { + task.cancel(true); + } + sseQuietCheck = hubHandler.getScheduler().schedule(this::onSseQuiet, SLEEP_SECONDS, TimeUnit.SECONDS); + if (sseEvent.isEmpty()) { + return; + } String json = sseEvent.readData(); - logger.trace("onSceneEvent() json:{}", json); - SceneEvent sceneEvent = gson.fromJson(json, SceneEvent.class); + if (json == null) { + return; + } + logger.trace("onSseSceneEvent() json:{}", json); + SceneEvent sceneEvent = jsonParser.fromJson(json, SceneEvent.class); if (sceneEvent != null) { Scene scene = sceneEvent.getScene(); hubHandler.onSceneEvent(scene); } } + /** + * Handle SSE errors. + * For the time being just log them, because the framework should automatically recover itself. + * + * @param e the error that was thrown. + */ + private void onSseShadeError(Throwable e) { + if (!closing) { + logger.debug("onSseShadeError() {}", e.getMessage(), e); + } + } + /** * Handle inbound SSE events for a shade. * * @param sseEvent the inbound event. */ - private void onShadeEvent(InboundSseEvent sseEvent) { + private void onSSeShadeEvent(InboundSseEvent sseEvent) { + if (closing) { + return; + } + ScheduledFuture task = sseQuietCheck; + if (task != null && !task.isCancelled()) { + task.cancel(true); + } + sseQuietCheck = hubHandler.getScheduler().schedule(this::onSseQuiet, SLEEP_SECONDS, TimeUnit.SECONDS); + if (sseEvent.isEmpty()) { + return; + } String json = sseEvent.readData(); - logger.trace("onShadeEvent() json:{}", json); - ShadeEvent shadeEvent = gson.fromJson(json, ShadeEvent.class); + if (json == null) { + return; + } + logger.trace("onSseShadeEvent() json:{}", json); + ShadeEvent shadeEvent = jsonParser.fromJson(json, ShadeEvent.class); if (shadeEvent != null) { ShadePosition positions = shadeEvent.getCurrentPositions(); hubHandler @@ -370,43 +418,93 @@ private void onShadeEvent(InboundSseEvent sseEvent) { } /** - * Open the SSE subscriptions. - * - * @return true if registered for SSE events. - * @throws HubProcessingException if any error occurs. + * Called when the SSE event channel has not received any events for a long time. This could mean that the event + * source socket has dropped. So restart the SSE connection. */ - public void openSSE() throws HubProcessingException { - SseEventSource shadeEventSource = this.shadeEventSource; - SseEventSource sceneEventSource = this.sceneEventSource; + public void onSseQuiet() { + if (!closing) { + sseReOpen(); + } + } - if (shadeEventSource == null || !shadeEventSource.isOpen() || sceneEventSource == null - || !sceneEventSource.isOpen()) { + /** + * Close the SSE links. + */ + private synchronized void sseClose() { + logger.debug("sseClose() called"); + ScheduledFuture task = sseQuietCheck; + if (task != null && !task.isCancelled()) { + task.cancel(true); + sseQuietCheck = null; + } + SseEventSource source; + source = this.shadeEventSource; + if (source != null) { + source.close(); + this.shadeEventSource = null; + } + source = this.sceneEventSource; + if (source != null) { + source.close(); + this.sceneEventSource = null; + } + } - try { - close(); - } catch (IOException e) { - } + /** + * Open the SSE links. + */ + public synchronized void sseOpen() { + sseClose(); + + logger.debug("sseOpen() called"); + Client client = clientBuilder.sslContext(httpClient.getSslContextFactory().getSslContext()) + .hostnameVerifier(null).hostnameVerifier(this).readTimeout(0, TimeUnit.SECONDS).build(); + + // open SSE channel for shades + SseEventSource shadeEventSource = eventSourceFactory.newSource(client.target(shadeEvents)); + shadeEventSource.register(this::onSSeShadeEvent, this::onSseShadeError); + shadeEventSource.open(); + this.shadeEventSource = shadeEventSource; + + // open SSE channel for scenes + SseEventSource sceneEventSource = eventSourceFactory.newSource(client.target(sceneEvents)); + sceneEventSource.register(this::onSseSceneEvent, this::onSseSceneError); + sceneEventSource.open(); + this.sceneEventSource = sceneEventSource; + } - // register ourself with the gateway (if necessary) - gatewayRegister(); - - SSLContext context = httpClient.getSslContextFactory().getSslContext(); - WebTarget target; - - // open SSE channel for shades - target = clientBuilder.sslContext(context).build().target(shadeEvents); - shadeEventSource = eventSourceFactory.newSource(target); - shadeEventSource.register((event) -> onShadeEvent(event)); - shadeEventSource.open(); - this.shadeEventSource = shadeEventSource; - - // open SSE channel for scenes - target = clientBuilder.sslContext(context).build().target(sceneEvents); - sceneEventSource = eventSourceFactory.newSource(target); - sceneEventSource.register((event) -> onSceneEvent(event)); - sceneEventSource.open(); - this.sceneEventSource = sceneEventSource; + /** + * Reopen the SSE links. If the eventSources already exist, try first to simply close and reopen them, but if that + * fails, then completely destroy and re-create the eventSources. + */ + private synchronized void sseReOpen() { + logger.debug("sseReOpen() called"); + + SseEventSource shadeEventSource = this.shadeEventSource; + SseEventSource sceneEventSource = this.sceneEventSource; + if (shadeEventSource != null && sceneEventSource != null) { + boolean exception = false; + for (SseEventSource eventSource : Set.of(shadeEventSource, sceneEventSource)) { + if (eventSource != null) { + try { + if (eventSource.isOpen()) { + eventSource.close(); + } + if (!eventSource.isOpen()) { + eventSource.open(); + } + } catch (Exception e) { + // SSE documentation does not say what exceptions may be thrown, so catch anything + logger.warn("sseReOpen() {}", e.getMessage(), e); + exception = true; + } + } + } + if (!exception) { + return; + } } + sseOpen(); } /** @@ -418,4 +516,16 @@ public void openSSE() throws HubProcessingException { public void stopShade(int shadeId) throws HubProcessingException { invoke(HttpMethod.PUT, shadeStop, Query.of(IDS, Integer.valueOf(shadeId).toString()), null); } + + /** + * HostnameVerifier method implementation that validates the host name when opening SSE connections. + * + * @param hostName the host name to be verified. + * @param sslSession (not used). + * @return true if the host name matches our own. + */ + @Override + public boolean verify(@Nullable String hostName, @Nullable SSLSession sslSession) { + return this.hostName.equals(hostName); + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index 6110f16dc7207..98b76b6edef0f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -15,6 +15,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -109,7 +110,8 @@ public void dispose() { */ private void doRefresh() { try { - getWebTargets().openSSE(); + getWebTargets().gatewayRegister(); + getWebTargets().sseOpen(); refreshProperties(); refreshShades(); refreshScenes(); @@ -281,4 +283,8 @@ private void refreshShades() throws HubProcessingException, IllegalStateExceptio } } } + + public ScheduledExecutorService getScheduler() { + return scheduler; + } } From dd0924450f9eea47395b3a2ac988225b7ee74997 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Tue, 22 Nov 2022 11:49:55 +0000 Subject: [PATCH 47/64] [hdpowerview] add exception handling Signed-off-by: Andrew Fiddian-Green --- .../internal/GatewayWebTargets.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 05b5c5a4f2535..6c4ded86a570d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -460,17 +460,22 @@ public synchronized void sseOpen() { Client client = clientBuilder.sslContext(httpClient.getSslContextFactory().getSslContext()) .hostnameVerifier(null).hostnameVerifier(this).readTimeout(0, TimeUnit.SECONDS).build(); - // open SSE channel for shades - SseEventSource shadeEventSource = eventSourceFactory.newSource(client.target(shadeEvents)); - shadeEventSource.register(this::onSSeShadeEvent, this::onSseShadeError); - shadeEventSource.open(); - this.shadeEventSource = shadeEventSource; - - // open SSE channel for scenes - SseEventSource sceneEventSource = eventSourceFactory.newSource(client.target(sceneEvents)); - sceneEventSource.register(this::onSseSceneEvent, this::onSseSceneError); - sceneEventSource.open(); - this.sceneEventSource = sceneEventSource; + try { + // open SSE channel for shades + SseEventSource shadeEventSource = eventSourceFactory.newSource(client.target(shadeEvents)); + shadeEventSource.register(this::onSSeShadeEvent, this::onSseShadeError); + shadeEventSource.open(); + this.shadeEventSource = shadeEventSource; + + // open SSE channel for scenes + SseEventSource sceneEventSource = eventSourceFactory.newSource(client.target(sceneEvents)); + sceneEventSource.register(this::onSseSceneEvent, this::onSseSceneError); + sceneEventSource.open(); + this.sceneEventSource = sceneEventSource; + } catch (Exception e) { + // SSE documentation does not say what exceptions may be thrown, so catch everything + logger.warn("sseOpen() {}", e.getMessage(), e); + } } /** @@ -494,7 +499,7 @@ private synchronized void sseReOpen() { eventSource.open(); } } catch (Exception e) { - // SSE documentation does not say what exceptions may be thrown, so catch anything + // SSE documentation does not say what exceptions may be thrown, so catch everything logger.warn("sseReOpen() {}", e.getMessage(), e); exception = true; } From c90a8c1bc41a9e6fc6daf4f8b7c2b1449b5ec0fd Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Thu, 23 Feb 2023 15:50:16 +0000 Subject: [PATCH 48/64] [hdpowerview] update copyright date Signed-off-by: Andrew Fiddian-Green --- .../openhab/binding/hdpowerview/internal/GatewayWebTargets.java | 2 +- .../internal/discovery/GatewayDiscoveryParticipant.java | 2 +- .../hdpowerview/internal/discovery/ShadeDiscoveryService.java | 2 +- .../org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java | 2 +- .../openhab/binding/hdpowerview/internal/dto/gen3/Scene.java | 2 +- .../binding/hdpowerview/internal/dto/gen3/SceneEvent.java | 2 +- .../openhab/binding/hdpowerview/internal/dto/gen3/Shade.java | 2 +- .../binding/hdpowerview/internal/dto/gen3/ShadeEvent.java | 2 +- .../binding/hdpowerview/internal/dto/gen3/ShadePosition.java | 2 +- .../hdpowerview/internal/handler/GatewayBridgeHandler.java | 2 +- .../binding/hdpowerview/internal/handler/ShadeThingHandler.java | 2 +- .../binding/hdpowerview/internal/gen3/Generation3DtoTest.java | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 6c4ded86a570d..4a00423be72da 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java index b14425f8ce233..a1f2daacd6375 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java index 68ced751e154c..ddb2461389579 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java index 0d98838a67f81..99a8210a22a77 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Info.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java index 144878f2eaa88..9b884e927ca1a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Scene.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java index 58d557886c06a..22947d059fd68 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/SceneEvent.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java index aa64287c90722..cc628ff58e437 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java index 7ac276735fdeb..f4c66f49c4e8c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadeEvent.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java index 85316f893bf73..ff9388ee7eca1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index 98b76b6edef0f..d845948701815 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index 16074faa05992..3172a87b96f2d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java index beb0c4d9778ba..4a2b1f37cec6c 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2022 Contributors to the openHAB project + * Copyright (c) 2010-2023 Contributors to the openHAB project * * See the NOTICE file(s) distributed with this work for additional * information. From dc9fe6311c4e80aa495cc706211a0a7d68b4a426 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 5 Mar 2023 17:17:22 +0000 Subject: [PATCH 49/64] [hdpowerview] add yaml file Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/gen3/openapi.yaml | 12912 ++++++++++++++++ 1 file changed, 12912 insertions(+) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/openapi.yaml diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/openapi.yaml b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/openapi.yaml new file mode 100644 index 0000000000000..1441861242625 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/openapi.yaml @@ -0,0 +1,12912 @@ +swaggerDoc: + openapi: 3.0.3 + info: + title: PowerView Gen3 Gateway + description: >- + The Gateway can be accessed and controlled via HTTP requests over a local + area network via this API. + version: 1.10.0 + servers: + - url: http://10.0.0.127:80 + tags: + - name: Home + description: >- + A Home consists of a number of related objects such as Rooms, Shades, + Scenes, Automations, Gateways, and Remotes. The Home APIs access the + Home in the following way: + * listing objects and their properties + * mutating object relationships and properties + * performing actions such as moving Shades and activating Scenes + + Note: If the Home does not have a HomeDoc, all /home APIs return + statusCode 503/SERVICE_UNAVAILABLE until the Home is correctly + configured and a HomeDoc is successfully retrieved from the Cloud + - name: Events + description: Provide Home Shade and Scene Events over an HTTP Streaming interface + - name: Gateway + description: >- + APIs providing ways to configure, provision, manage and diagnose the + Gateway + - name: HomeKit + description: APIs to manage HomeKit running on the Gateway + - name: HD Internal + description: >- + APIs for the sole use of Hunter Douglas. These APIs are internal hooks + for Hunter Douglas specific Apps and for troubleshooting + paths: + /gateway/: + get: + summary: Retrieve general Gateway information + tags: + - Gateway + responses: + '200': + description: The Gateway information object + content: + application/json: + schema: + type: object + properties: + config: + properties: + rev: + title: Config.rev + description: Config data structure version + type: number + hwVersion: + title: Config.hwVersion + description: Hardware version + type: string + model: + title: Config.model + description: Hardware model + type: string + brand: + title: Config.brand + description: Hardware brand + type: string + serialNumber: + title: Config.serialNumber + description: Unique serial number across all products + type: string + firmware: + title: Config.firmware + description: Software version + properties: + mainProcessor: + title: BuildVersions.mainProcessor + description: Version number + properties: + build: + title: TypescriptAppBuildInfo.build + description: Build number + type: number + name: + title: TypescriptAppBuildInfo.name + description: '???' + type: string + revision: + title: TypescriptAppBuildInfo.revision + description: Major version number + type: number + subRevision: + title: TypescriptAppBuildInfo.subRevision + description: Minor version number + type: number + required: + - build + - name + - revision + - subRevision + additionalProperties: false + type: object + radios: + items: + title: BuildVersions.radios.[] + properties: + build: + title: TransceiverFirmwareBuildInfo.build + description: Build number + type: number + revision: + title: TransceiverFirmwareBuildInfo.revision + description: Major version number + type: number + subRevision: + title: TransceiverFirmwareBuildInfo.subRevision + description: Minor version number + type: number + required: + - build + - revision + - subRevision + additionalProperties: false + description: Transceiver/Radio firmware information + type: object + title: BuildVersions.radios + description: Transceiver/Radio firmware versions + type: array + required: + - mainProcessor + - radios + additionalProperties: false + type: object + homeAssoc: + title: Config.homeAssoc + description: >- + 2-byte Hash of the HomeSecurityKey (AKA: + ShadeSecurityKey) + type: number + color: + title: Config.color + description: >- + User idle color - the color the user chose for the + Idle state + properties: + brightness: + title: LedColor.brightness + description: LED brightness percentage between 0 and 100 + type: number + red: + title: LedColor.red + description: Red color value between 0 and 255 + type: number + green: + title: LedColor.green + description: Green color value between 0 and 255 + type: number + blue: + title: LedColor.blue + description: Blue color value between 0 and 255 + type: number + required: + - brightness + - red + - green + - blue + additionalProperties: false + type: object + cloudConfig: + title: Config.cloudConfig + properties: + cloudEnv: + title: FirestoreConfig.cloudEnv + description: >- + What cloud environment to access (i.e, what + credentials to load) + type: number + collection: + title: FirestoreConfig.collection + description: Cloud collection name for homeDocs + type: string + homeId: + title: FirestoreConfig.homeId + description: Home identifier for this household + type: string + gatewayId: + title: FirestoreConfig.gatewayId + description: Gateway id for THIS gateway + type: number + required: + - cloudEnv + - collection + - homeId + - gatewayId + additionalProperties: false + description: >- + Cloud Config + + The properties required to uniquely identify a Home + and Gateway pairing. + type: object + rangeMapper: + title: Config.rangeMapper + description: Radio scan information + properties: + minRSSI: + title: RangeMapperDb.minRSSI + description: The minimum RSSI (of Shades) to report back on + type: number + levelingSeconds: + title: RangeMapperDb.levelingSeconds + description: >- + The number of seconds over that the algorithm + averages over + type: number + required: + - minRSSI + - levelingSeconds + additionalProperties: false + type: object + networkConfig: + title: Config.networkConfig + description: Persistent network configuration + properties: + staticIpEnabled: + title: NetworkConfig.staticIpEnabled + description: Is static IP configuration enabled? + type: boolean + staticIp: + properties: + ip_address: + title: NetworkConfig.staticIp.ip_address + description: Static IP address + type: string + netmask: + title: NetworkConfig.staticIp.netmask + description: Static IP network mask + type: string + gateway_ip: + title: NetworkConfig.staticIp.gateway_ip + description: >- + Home gateway IP address (i.e., the home + router + type: string + dns: + items: + type: string + title: NetworkConfig.staticIp.dns + description: DNS IP values + type: array + required: + - ip_address + - netmask + - gateway_ip + - dns + additionalProperties: false + title: NetworkConfig.staticIp + description: Static IP values if enabled + type: object + required: + - staticIpEnabled + - staticIp + additionalProperties: false + type: object + networkStatus: + title: Config.networkStatus + description: Transient network information + properties: + ipAddress: + title: NetworkStatus.ipAddress + description: Currently active IP address + type: string + activeInterface: + title: NetworkStatus.activeInterface + description: Current active network interface + type: string + primaryMacAddress: + title: NetworkStatus.primaryMacAddress + description: Primary MAC address to identify device + type: string + dns: + items: + type: string + title: NetworkStatus.dns + description: DNS entries + type: array + eth0: + title: NetworkStatus.eth0 + description: Wired network interface information + wlan0: + title: NetworkStatus.wlan0 + description: Wireless network interface information + internetState: + title: NetworkStatus.internetState + description: Internet connectivity state + type: string + enum: + - Connected + - Disconnected + ssid: + title: NetworkStatus.ssid + description: Wireless SSID + type: string + required: + - ipAddress + - activeInterface + - primaryMacAddress + - dns + - eth0 + - wlan0 + - internetState + - ssid + additionalProperties: false + type: object + mgwConfig: + title: Config.mgwConfig + description: Persistent multi-gateway configuration + properties: + primary: + title: MultiGatewayConfig.primary + description: This gateway is the primary in multi-gateway + type: boolean + required: + - primary + additionalProperties: false + type: object + mgwStatus: + title: Config.mgwStatus + description: Transient multi-gateway information + properties: + running: + title: MultiGatewayStatus.running + description: Multi-gateway is operational + type: boolean + required: + - running + additionalProperties: false + type: object + required: + - rev + - hwVersion + - model + - brand + - serialNumber + - firmware + - homeAssoc + - color + - cloudConfig + - rangeMapper + - networkConfig + - networkStatus + - mgwConfig + - mgwStatus + additionalProperties: false + title: Config + type: object + /gateway/balancer: + get: + summary: Retrieve general Gateway balancer information + tags: + - HD Internal + responses: + '200': + description: The Gateway balancer information object + content: + application/json: + schema: + type: object + properties: + maxShadesPerChannel: + type: number + seqNum: + type: number + balancingAlg: + type: string + example: 0 = BALANCE_BY_GROUP + transactionNum: + type: number + /gateway/cloud/config: + get: + summary: Get the Gateway Cloud configuration + tags: + - Gateway + responses: + '200': + description: The Gateway Cloud configuration + content: + application/json: + schema: + type: object + description: >- + Cloud configuration information used by the Gateway to + access the Cloud + properties: + homeId: + type: string + description: Home identifier as a hexadecimal string + example: Xffeg05RhmhUQez88Qyv + gatewayId: + type: number + description: >- + Numerical identifier of the Gateway as registered in the + Cloud + example: 103 + collection: + type: string + description: Name of the Home collection + default: homes + example: homes + clouddEnv: + type: number + description: >- + Select the Cloud environment to connect to: Dev = 0, + Production = 1 + default: 0 + example: 1 + required: + - homeId + - gatewayId + put: + summary: Set the Gateway Cloud configuration + tags: + - Gateway + requestBody: + description: Parameters for connection to the Cloud database + required: true + content: + application/json: + schema: + type: object + description: >- + Cloud configuration information used by the Gateway to access + the Cloud + properties: + homeId: + type: string + description: Home identifier as a hexadecimal string + example: Xffeg05RhmhUQez88Qyv + gatewayId: + type: number + description: >- + Numerical identifier of the Gateway as registered in the + Cloud + example: 103 + collection: + type: string + description: Name of the Home collection + default: homes + example: homes + clouddEnv: + type: number + description: >- + Select the Cloud environment to connect to: Dev = 0, + Production = 1 + default: 0 + example: 1 + required: + - homeId + - gatewayId + responses: + '200': + description: The Gateway Cloud configuration + content: + application/json: + schema: + type: object + description: >- + Cloud configuration information used by the Gateway to + access the Cloud + properties: + homeId: + type: string + description: Home identifier as a hexadecimal string + example: Xffeg05RhmhUQez88Qyv + gatewayId: + type: number + description: >- + Numerical identifier of the Gateway as registered in the + Cloud + example: 103 + collection: + type: string + description: Name of the Home collection + default: homes + example: homes + clouddEnv: + type: number + description: >- + Select the Cloud environment to connect to: Dev = 0, + Production = 1 + default: 0 + example: 1 + required: + - homeId + - gatewayId + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /gateway/cloud/config/push: + put: + summary: Push the individual Cloud configurations to each SPS + tags: + - Gateway + requestBody: + description: Parameters for connection to the Cloud database + required: true + content: + application/json: + schema: + type: object + description: >- + Cloud configuration information used by the Gateway to access + the Cloud + properties: + homeId: + type: string + description: Home identifier as a hexadecimal string + example: Xffeg05RhmhUQez88Qyv + gatewayId: + type: number + description: >- + Numerical identifier of the Gateway as registered in the + Cloud + example: 103 + collection: + type: string + description: Name of the Home collection + default: homes + example: homes + clouddEnv: + type: number + description: >- + Select the Cloud environment to connect to: Dev = 0, + Production = 1 + default: 0 + example: 1 + required: + - homeId + - gatewayId + responses: + '200': + description: The Gateway Cloud configuration + content: + application/json: + schema: + type: object + description: >- + Cloud configuration information used by the Gateway to + access the Cloud + properties: + homeId: + type: string + description: Home identifier as a hexadecimal string + example: Xffeg05RhmhUQez88Qyv + gatewayId: + type: number + description: >- + Numerical identifier of the Gateway as registered in the + Cloud + example: 103 + collection: + type: string + description: Name of the Home collection + default: homes + example: homes + clouddEnv: + type: number + description: >- + Select the Cloud environment to connect to: Dev = 0, + Production = 1 + default: 0 + example: 1 + required: + - homeId + - gatewayId + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /gateway/cloud/config/sps: + get: + summary: Allow a SPS to retrieve its Cloud configuration + tags: + - HD Internal + parameters: + - name: mac + in: query + required: true + description: Primary MAC address + schema: + type: string + example: F8:DC:7A:3D:FB:7A + responses: + '200': + description: The Gateway Cloud configuration + content: + application/json: + schema: + type: object + description: >- + Cloud configuration information used by the Gateway to + access the Cloud + properties: + homeId: + type: string + description: Home identifier as a hexadecimal string + example: Xffeg05RhmhUQez88Qyv + gatewayId: + type: number + description: >- + Numerical identifier of the Gateway as registered in the + Cloud + example: 103 + collection: + type: string + description: Name of the Home collection + default: homes + example: homes + clouddEnv: + type: number + description: >- + Select the Cloud environment to connect to: Dev = 0, + Production = 1 + default: 0 + example: 1 + required: + - homeId + - gatewayId + '400': + description: The request parameters were incorrect + content: + application/json: + schema: + type: object + properties: + errMsg: + type: string + description: Describes which parameters were incorrect + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /gateway/factory-reset: + post: + summary: >- + Reset the Gateway by clearing all the settings and restoring them to + the factory defaults + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/homekit: + get: + summary: Returns the homekit information + tags: + - HomeKit + responses: + '200': + description: HomeKit information + content: + application/json: + schema: + type: object + properties: + hapBridge: + type: object + properties: + pid: + type: number + connected: + type: boolean + exitCode: + type: string + killed: + type: boolean + started: + type: string + lastStoppedEvent: + type: string + stoppedMessage: + type: string + respawnDisabled: + type: boolean + debugEnabled: + type: boolean + closeEvents: + type: array + items: + type: string + lastAID: + type: number + accessoryMap: + type: object + /gateway/homekit/setup: + get: + summary: HomeKit setup data + tags: + - HomeKit + responses: + '200': + description: The HomeKit setup data + content: + application/json: + schema: + type: object + properties: + setupCode: + type: string + example: 383-67-992 + setupPayload: + type: string + example: X-HM://0E38KYF3CHSCC03FBKDP6RPW4CM4J5S + required: + - setupCode + - setupPayload + /gateway/homekit/pairing/clear: + put: + summary: Clears pairing signal + tags: + - HomeKit + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/homekit/pairing/restart: + put: + summary: Restarts Homekit pairing mode + tags: + - HomeKit + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/homekit/hdbridge/reset: + put: + summary: Factory resets HomeKit HD Bridge process + tags: + - HomeKit + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/homekit/hdbridge/restart: + put: + summary: Restarts HomeKit HD Bridge process + tags: + - HomeKit + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/homekit/reset: + post: + summary: >- + Factory reset HomeKit only. The response indicates HomeKit reset + completed + tags: + - HomeKit + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/identify: + get: + summary: Identify the Gateway by flashing the LED + tags: + - Gateway + parameters: + - name: time + in: query + required: false + description: The flash time in seconds + schema: + type: string + default: 10 + example: 15 + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + put: + summary: Identify the Gateway by flashing the LED + deprecated: true + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/info: + get: + summary: >- + Retrieve some Gateway info including the serial number and the + firmware version + tags: + - Gateway + responses: + '200': + description: Limited information about the Gateway + content: + application/json: + schema: + type: object + properties: + fwVersion: + type: string + description: Gateway firmware version string + example: 3.1.376 + serialNumber: + type: string + description: Gateway serial number + example: 3b4149d4f24d7bd7 + required: + - fwVersion + - serialNumber + /gateway/info/gateways: + get: + summary: Retrieve the known and missing Gateways + tags: + - Gateway + responses: + '200': + description: Known and missing Gateways + content: + application/json: + schema: + type: object + properties: + known: + type: object + description: Known HomeDoc Gateway identifier + additionalProperties: + type: object + properties: + st: + type: integer + description: >- + The current operating state, which is one of: + 0=STARTUP, 1=IDLE, 2=NEW_GW_NO_NETWORK, + 3=NO_INTERNET, 4=NO_HOME + example: 1 + pri: + type: boolean + description: >- + Optional: true indicates this Gateway is the + primary Gateway. Not set indicates this Gateway is + an auxiliary (aux) Gateway + self: + type: boolean + description: >- + Optional: true indicates this Gateway. Not set + indicates other Gateways in a multi-gateway system + required: + - st + missing: + type: array + description: >- + Gateway identifiers of HomeDoc listed Gateways who have + not reported recently + items: + type: number + description: HomeDoc Gateway identifier + example: 7643 + required: + - known + - missing + /gateway/internet/status: + get: + summary: Test the Gateway connection to the internet + tags: + - Gateway + responses: + '200': + description: Conformation of internet connection + content: + application/json: + schema: + type: object + properties: + HealthCheckResponse: + type: object + properties: + success: + type: boolean + type: + type: string + message: + type: string + required: + - success + - type + - message + required: + - HealthCheckResponse + /gateway/led: + get: + summary: Get the Gateway LED color and brightness + tags: + - Gateway + responses: + '200': + description: The LED Color + content: + application/json: + schema: + description: The Gateway LED color and brightness + type: object + properties: + red: + type: number + description: Red + maximum: 0 + minimum: 255 + green: + type: number + description: Green + maximum: 0 + minimum: 255 + blue: + type: number + description: Blue + maximum: 0 + minimum: 255 + brightness: + type: number + description: Brightness + maximum: 0 + minimum: 100 + required: + - red + - green + - blue + - brightness + put: + summary: Set the Gateway LED color and brightness + tags: + - Gateway + requestBody: + $ref: '#components/requestBodies/LedColor' + responses: + '200': + description: The LED Color + content: + application/json: + schema: + description: The Gateway LED color and brightness + type: object + properties: + red: + type: number + description: Red + maximum: 0 + minimum: 255 + green: + type: number + description: Green + maximum: 0 + minimum: 255 + blue: + type: number + description: Blue + maximum: 0 + minimum: 255 + brightness: + type: number + description: Brightness + maximum: 0 + minimum: 100 + required: + - red + - green + - blue + - brightness + /gateway/log: + get: + summary: Download the Gateway log from this Gateway + tags: + - Gateway + responses: + '200': + description: The current Gateway.log + /gateway/log/level: + get: + summary: Get the current Gateway log level + tags: + - Gateway + responses: + '200': + description: The Gateway current log level + content: + application/json: + schema: + type: object + properties: + level: + type: string + description: >- + The current root log level, which is one of: error, + warn, info, verbose, debug, or silly. The recommended + value is info. + example: info + transports: + type: array + items: + properties: + level: + type: string + description: >- + The current transport log level, which is one of: + error, warn, info, verbose, debug, or silly. The + recommended value is info. + example: silly + required: + - level + required: + - level + - transports + put: + summary: Set the Gateway log level and if it persists across Gateway restarts + tags: + - Gateway + parameters: + - name: level + in: query + required: true + description: >- + The new log level, which must be one of: error, warn, info, + verbose, debug, or silly. The recommended value is info. + schema: + type: string + example: debug + - name: persist + in: query + required: false + description: >- + If the new log level is to persist (be sticky) across application + restarts. Values are string 'true' or 'false' + schema: + type: string + example: 'true' + responses: + '204': + description: The log level was successfully set + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /gateway/log/upload: + get: + summary: Upload the Gateway log to the Cloud + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + put: + summary: Upload the Gateway log to the Cloud + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/network: + get: + summary: Retrieve the current Gateway network information + tags: + - Gateway + responses: + '200': + description: The Gateway's network information + content: + application/json: + schema: + type: object + properties: + networkConfig: + description: The Gateway's network configuration information + type: object + properties: + staticIpEnabled: + type: boolean + description: >- + Indicates if the below staticIp object information + is to be applied. The below staticIp object is + optional if staticIpEnabled is false + example: true + staticIp: + type: object + description: >- + The optional static IP configuration information. If + staticIpEnabled is true, this object must be present + in its entirety. If staticIpEnabled is false, this + object may be present and if present, it must be + fully provided + properties: + ip_address: + type: string + description: The IP address to utilize as the static IP + example: 10.0.0.101 + netwask: + type: string + description: >- + The subnet mask to apply to the static IP + address value + example: 255.255.255.0 + gateway_ip: + type: string + description: >- + The static internet Gateway IP address to + utilize + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + networkStatus: + description: The Gateway's network status information + type: object + properties: + ipAddress: + type: string + description: The IP address of the active interface + example: 10.0.0.101 + activeInterface: + type: string + description: >- + The active network interface identifier. Either + 'eth0' (wired) or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: eth0 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: 8.8.8.8 + eth0: + type: object + description: >- + The wired (eth0) network information, which may be + empty if not active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' + (wired) or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' + (eth0) or 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: >- + The network interface's internet Gateway IP + address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + wlan0: + type: object + description: >- + The wireless (wlan0) network information, which may + be empty if not active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' + (wired) or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' + (eth0) or 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: >- + The network interface's internet Gateway IP + address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + internetState: + type: string + description: The IP network's internet connectivity state + enum: + - Connected + - Disconnected + example: Connected + ssid: + type: string + description: >- + The wireless network's SSID value. The value is an + empty string if not configured + example: HunterDouglasWifi + required: + - ipAddress + - activeInterface + - dns + - eth0 + - wlan0 + - internetState + - ssid + required: + - networkConfig + - networkStatus + /gateway/network/config: + get: + summary: Retrieve the Gateway's current network configuration information + tags: + - Gateway + responses: + '200': + description: The Gateway's current network configuration information + content: + application/json: + schema: + type: object + properties: + networkConfig: + description: The Gateway's network configuration information + type: object + properties: + staticIpEnabled: + type: boolean + description: >- + Indicates if the below staticIp object information + is to be applied. The below staticIp object is + optional if staticIpEnabled is false + example: true + staticIp: + type: object + description: >- + The optional static IP configuration information. If + staticIpEnabled is true, this object must be present + in its entirety. If staticIpEnabled is false, this + object may be present and if present, it must be + fully provided + properties: + ip_address: + type: string + description: The IP address to utilize as the static IP + example: 10.0.0.101 + netwask: + type: string + description: >- + The subnet mask to apply to the static IP + address value + example: 255.255.255.0 + gateway_ip: + type: string + description: >- + The static internet Gateway IP address to + utilize + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + linuxStaticIpValues: + description: >- + The Gateway operating system's configured static IP + values These values should match the networkConfig + staticIp information + type: object + properties: + staticIpEnabled: + type: boolean + description: Indicates if the staticIp is enabled + example: true + interface: + type: string + description: >- + The network interface currently configured with the + static IP values + example: wlan0 + ip_address: + type: string + description: The IP address being utilized as the static IP + example: 10.0.0.101 + netwask: + type: string + description: >- + The subnet mask applied to the static IP address + value + example: 255.255.255.0 + gateway_ip: + type: string + description: >- + The static internet Gateway IP address being + utilized + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + required: + - networkConfig + put: + summary: Modify the Gateway's network configuration + tags: + - Gateway + requestBody: + description: Update the network configuration + content: + application/json: + schema: + description: The Gateway's network configuration information + type: object + properties: + staticIpEnabled: + type: boolean + description: >- + Indicates if the below staticIp object information is to + be applied. The below staticIp object is optional if + staticIpEnabled is false + example: true + staticIp: + type: object + description: >- + The optional static IP configuration information. If + staticIpEnabled is true, this object must be present in + its entirety. If staticIpEnabled is false, this object may + be present and if present, it must be fully provided + properties: + ip_address: + type: string + description: The IP address to utilize as the static IP + example: 10.0.0.101 + netwask: + type: string + description: >- + The subnet mask to apply to the static IP address + value + example: 255.255.255.0 + gateway_ip: + type: string + description: The static internet Gateway IP address to utilize + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + responses: + '200': + description: The Gateway's current network configuration information + content: + application/json: + schema: + type: object + properties: + networkConfig: + description: The Gateway's network configuration information + type: object + properties: + staticIpEnabled: + type: boolean + description: >- + Indicates if the below staticIp object information + is to be applied. The below staticIp object is + optional if staticIpEnabled is false + example: true + staticIp: + type: object + description: >- + The optional static IP configuration information. If + staticIpEnabled is true, this object must be present + in its entirety. If staticIpEnabled is false, this + object may be present and if present, it must be + fully provided + properties: + ip_address: + type: string + description: The IP address to utilize as the static IP + example: 10.0.0.101 + netwask: + type: string + description: >- + The subnet mask to apply to the static IP + address value + example: 255.255.255.0 + gateway_ip: + type: string + description: >- + The static internet Gateway IP address to + utilize + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + linuxStaticIpValues: + description: >- + The Gateway operating system's configured static IP + values These values should match the networkConfig + staticIp information + type: object + properties: + staticIpEnabled: + type: boolean + description: Indicates if the staticIp is enabled + example: true + interface: + type: string + description: >- + The network interface currently configured with the + static IP values + example: wlan0 + ip_address: + type: string + description: The IP address being utilized as the static IP + example: 10.0.0.101 + netwask: + type: string + description: >- + The subnet mask applied to the static IP address + value + example: 255.255.255.0 + gateway_ip: + type: string + description: >- + The static internet Gateway IP address being + utilized + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + required: + - networkConfig + /gateway/network/sddp: + get: + summary: Retrieve the Gateway's current SDDP configuration information + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + put: + summary: Configure the Gateway's current SDDP debug configuration + tags: + - Gateway + parameters: + - name: debug + in: query + required: true + description: true enables debug output; false disables debug output + schema: + type: boolean + example: true + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + '400': + description: The request parameters were incorrect + content: + application/json: + schema: + type: object + properties: + errMsg: + type: string + description: Describes which parameters were incorrect + required: + - errMsg + /gateway/network/sddp/online: + put: + summary: Force the Gateway to emit a SDDP online notification message + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + '400': + description: The request parameters were incorrect + content: + application/json: + schema: + type: object + properties: + errMsg: + type: string + description: Describes which parameters were incorrect + required: + - errMsg + /gateway/network/sddp/offline: + put: + summary: Force the Gateway to emit a SDDP offline notification message + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + '400': + description: The request parameters were incorrect + content: + application/json: + schema: + type: object + properties: + errMsg: + type: string + description: Describes which parameters were incorrect + required: + - errMsg + /gateway/network/sddp/identify: + put: + summary: Force the Gateway to emit a SDDP identify message + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + '400': + description: The request parameters were incorrect + content: + application/json: + schema: + type: object + properties: + errMsg: + type: string + description: Describes which parameters were incorrect + required: + - errMsg + /gateway/network/status: + get: + summary: Retrieve the Gateway's current network status information + tags: + - Gateway + responses: + '200': + description: The Gateway's current network status information + content: + application/json: + schema: + type: object + properties: + networkStatus: + description: The Gateway's network status information + type: object + properties: + ipAddress: + type: string + description: The IP address of the active interface + example: 10.0.0.101 + activeInterface: + type: string + description: >- + The active network interface identifier. Either + 'eth0' (wired) or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: eth0 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: 8.8.8.8 + eth0: + type: object + description: >- + The wired (eth0) network information, which may be + empty if not active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' + (wired) or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' + (eth0) or 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: >- + The network interface's internet Gateway IP + address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + wlan0: + type: object + description: >- + The wireless (wlan0) network information, which may + be empty if not active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' + (wired) or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' + (eth0) or 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: >- + The network interface's internet Gateway IP + address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + internetState: + type: string + description: The IP network's internet connectivity state + enum: + - Connected + - Disconnected + example: Connected + ssid: + type: string + description: >- + The wireless network's SSID value. The value is an + empty string if not configured + example: HunterDouglasWifi + required: + - ipAddress + - activeInterface + - dns + - eth0 + - wlan0 + - internetState + - ssid + required: + - networkStatus + /gateway/radios: + get: + summary: Retrieve radio build information + tags: + - Gateway + responses: + '200': + description: Gateway radios + content: + application/json: + schema: + properties: + serialNum: + type: string + appVersion: + type: string + softdeviceVersion: + type: string + bootloaderVersion: + type: string + type: object + /gateway/radios/clear: + post: + summary: Clear the channel maps of a radio + tags: + - HD Internal + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/radios/shades: + get: + summary: List Shades on a radio or communications channel + tags: + - HD Internal + responses: + '200': + description: Gateway radio shades + content: + application/json: + schema: + type: array + items: + type: array + items: + type: object + properties: + index: + type: number + rssi: + type: number + example: -256 + ready: + type: number + example: 1 + bleName: + type: string + mac: + type: string + /gateway/reboot: + post: + summary: Reboot the Gateway + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/shades/discover: + get: + summary: Returns a list of the Home's Shades with Bluetooth RSSI + description: >- + Returns a list of the Home's Shades and each Shade's Bluetooth RSSI as + observed from the Gateway. The list is sorted by decreasing RSSI + (stronger to weaker signal). Note: In multi-gateway systems, Shades + visible to the Gateway from the entire home are returned, which is + intended to facilitate optimising Gateway placement and + Shade-to-Gateway assignment. + + The list is filtered by the optional minimum RSSI threshold. When + omitted, the saved minimum RSSI level is used. + tags: + - Gateway + parameters: + - name: rssiMinValue + in: query + required: false + description: Minimum RSSI filter. Range is -20 to -100 + schema: + type: number + default: -90 + example: -80 + responses: + '200': + description: List of discovered Shades + content: + application/json: + schema: + type: object + properties: + scan: + type: array + description: Shades discovered List + items: + type: object + description: Discovered Shade entry + properties: + bleName: + type: string + description: Shade name + example: PIR:1E37 + filteredRssi: + type: number + description: Shade RSSI + example: -37 + required: + - bleName + - filteredRssi + required: + - scan + '400': + description: The request parameters were incorrect + content: + application/json: + schema: + type: object + properties: + errMsg: + type: string + description: Describes which parameters were incorrect + required: + - errMsg + post: + summary: Returns a list of the Home's Shades with Bluetooth RSSI + deprecated: true + description: >- + Returns a list of the Home's Shades and each Shade's Bluetooth RSSI as + observed from the Gateway. The list is sorted by decreasing RSSI + (stronger to weaker signal). Note: In multi-gateway systems, Shades + visible to the Gateway from the entire home are returned, which is + intended to facilitate optimising Gateway placement and + Shade-to-Gateway assignment. + + The list is filtered by the optional minimum RSSI threshold. When + omitted, the saved minimum RSSI level is used. + tags: + - Gateway + requestBody: + description: Minimum RSSI filter + required: false + content: + application/json: + schema: + type: object + properties: + rssiMinValue: + type: number + description: Mimimum RSSI value + example: -66 + required: + - rssiMinValue + responses: + '200': + description: List of discovered Shades + content: + application/json: + schema: + type: object + properties: + scan: + type: array + description: Shades discovered List + items: + type: object + description: Discovered Shade entry + properties: + bleName: + type: string + description: Shade name + example: PIR:1E37 + filteredRssi: + type: number + description: Shade RSSI + example: -37 + required: + - bleName + - filteredRssi + required: + - scan + '400': + description: The request parameters were incorrect + content: + application/json: + schema: + type: object + properties: + errMsg: + type: string + description: Describes which parameters were incorrect + required: + - errMsg + /gateway/sshd: + put: + summary: Request permission to SSH into the Gateway + tags: + - HD Internal + parameters: + - name: enable + in: query + required: true + description: >- + Boolean set to 'true' to start and permanently persist SSH. + 'false' stops and permanently disables SSH. + schema: + type: boolean + example: true + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/shades/fab/info: + get: + summary: Returns the current fab Shade information details + tags: + - HD Internal + responses: + '200': + description: Gateway Fab Info response + content: + application/json: + schema: + type: object + properties: + shadesFabInfoAllShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + shadesFabInfoLocalShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + cachedPrimary: + type: boolean + periodicRequesterTimerNextRunTime: + type: string + put: + summary: Force all the fab Shade information data to update + tags: + - HD Internal + responses: + '200': + description: Gateway Fab Info response + content: + application/json: + schema: + type: object + properties: + shadesFabInfoAllShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + shadesFabInfoLocalShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + periodicCheckIntervalMs: + type: number + periodicCheckTimerNextRunTime: + type: string + cachedPrimary: + type: boolean + periodicRequesterTimerNextRunTime: + type: string + delete: + summary: Deletes all the fab Shade information + tags: + - HD Internal + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/shades/fab/info/aux: + put: + summary: Request the Shades fab information from all auxiliary devices + tags: + - HD Internal + responses: + '200': + description: Gateway Fab Info response + content: + application/json: + schema: + type: object + properties: + shadesFabInfoAllShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + shadesFabInfoLocalShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + periodicCheckIntervalMs: + type: number + periodicCheckTimerNextRunTime: + type: string + cachedPrimary: + type: boolean + periodicRequesterTimerNextRunTime: + type: string + /gateway/shades/fab/info/local: + put: + summary: Force the local Shades fab information to update + tags: + - HD Internal + responses: + '200': + description: Gateway Fab Info response + content: + application/json: + schema: + type: object + properties: + shadesFabInfoAllShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + shadesFabInfoLocalShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + periodicCheckIntervalMs: + type: number + periodicCheckTimerNextRunTime: + type: string + cachedPrimary: + type: boolean + periodicRequesterTimerNextRunTime: + type: string + /gateway/shades/fab/info/all-map: + delete: + summary: Deletes the all shade fab information map + tags: + - HD Internal + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/shades/fab/info/local-map: + delete: + summary: Deletes the local shade fab information map + tags: + - HD Internal + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/shades/fab/info/maps: + delete: + summary: Deletes both the local and the all Shade fab information maps + tags: + - HD Internal + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/shades/fab/info/lds: + delete: + summary: Deletes the Shades fab information local data store + tags: + - HD Internal + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/shades/time/sync: + put: + summary: >- + Sync all times including sunrise, sunset, and current local time to + all Shades + tags: + - HD Internal + responses: + '200': + description: Gateway Shade Time Information + content: + application/json: + schema: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + setSunTimes: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + successfullyUpdated: + type: array + items: + type: string + failedToUpdate: + type: array + items: + type: string + setCurrentTime: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + successfullyUpdated: + type: array + items: + type: string + failedToUpdate: + type: array + items: + type: string + /gateway/shades/time/sync/current: + put: + summary: Sync the current local time to all Shades + tags: + - HD Internal + responses: + '200': + description: Gateway Shade Time Information + content: + application/json: + schema: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + setSunTimes: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + successfullyUpdated: + type: array + items: + type: string + failedToUpdate: + type: array + items: + type: string + setCurrentTime: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + successfullyUpdated: + type: array + items: + type: string + failedToUpdate: + type: array + items: + type: string + /gateway/shades/time/sync/info: + get: + summary: Retrieve the Shade Time Sync Manager sync information + tags: + - HD Internal + responses: + '200': + description: Shade Time Sync Manager Info + content: + application/json: + schema: + type: object + properties: + cachedHomeDocInfo: + type: object + properties: + gwId: + type: number + geoPoint: + type: object + properties: + latitude: + type: number + example: 29.9 + longitude: + type: number + example: -100 + tz: + type: string + example: America/Denver + localDstAdjustedOffset: + type: number + nextAllShadesSyncDate: + type: string + lastSuccessfulAllShadesSyncDate: + type: string + nextBleNamesSyncDate: + type: string + bleNamesNeedingSync: + type: array + items: + type: string + delayTimes: + type: object + properties: + homeDocUpdateDelayMs: + type: number + shadesAddedDelayMs: + type: number + syncAllRetryDelayMs: + type: number + syncListRetryDelayMs: + type: number + syncInProgressDelayMs: + type: number + syncInProgress: + type: boolean + /gateway/shades/time/sync/sun: + get: + summary: Sync the sunrise and sunset local time to all Shades + tags: + - HD Internal + responses: + '200': + description: Gateway Shade Time Information + content: + application/json: + schema: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + setSunTimes: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + successfullyUpdated: + type: array + items: + type: string + failedToUpdate: + type: array + items: + type: string + setCurrentTime: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + successfullyUpdated: + type: array + items: + type: string + failedToUpdate: + type: array + items: + type: string + /gateway/shades/time/sync/delays: + put: + summary: Override the factory set delay times + tags: + - HD Internal + parameters: + - name: persist + in: query + required: false + description: Boolean set to 'true' to make delay times sticky + schema: + type: boolean + example: true + - name: delayTimes + in: query + required: true + description: Delay times object + schema: + type: object + properties: + homeDocUpdateDelayMs: + type: number + shadesAddedDelayMs: + type: number + syncAllRetryDelayMs: + type: number + syncListRetryDelayMs: + type: number + syncInProgressDelayMs: + type: number + responses: + '200': + description: Shade Time Sync Manager Info + content: + application/json: + schema: + type: object + properties: + cachedHomeDocInfo: + type: object + properties: + gwId: + type: number + geoPoint: + type: object + properties: + latitude: + type: number + example: 29.9 + longitude: + type: number + example: -100 + tz: + type: string + example: America/Denver + localDstAdjustedOffset: + type: number + nextAllShadesSyncDate: + type: string + lastSuccessfulAllShadesSyncDate: + type: string + nextBleNamesSyncDate: + type: string + bleNamesNeedingSync: + type: array + items: + type: string + delayTimes: + type: object + properties: + homeDocUpdateDelayMs: + type: number + shadesAddedDelayMs: + type: number + syncAllRetryDelayMs: + type: number + syncListRetryDelayMs: + type: number + syncInProgressDelayMs: + type: number + syncInProgress: + type: boolean + delete: + summary: Restores the delay times to their factory defaults + tags: + - HD Internal + responses: + '200': + description: Shade Time Sync Manager Info + content: + application/json: + schema: + type: object + properties: + cachedHomeDocInfo: + type: object + properties: + gwId: + type: number + geoPoint: + type: object + properties: + latitude: + type: number + example: 29.9 + longitude: + type: number + example: -100 + tz: + type: string + example: America/Denver + localDstAdjustedOffset: + type: number + nextAllShadesSyncDate: + type: string + lastSuccessfulAllShadesSyncDate: + type: string + nextBleNamesSyncDate: + type: string + bleNamesNeedingSync: + type: array + items: + type: string + delayTimes: + type: object + properties: + homeDocUpdateDelayMs: + type: number + shadesAddedDelayMs: + type: number + syncAllRetryDelayMs: + type: number + syncListRetryDelayMs: + type: number + syncInProgressDelayMs: + type: number + syncInProgress: + type: boolean + /gateway/update: + get: + summary: Trigger a Gateway firmware update check + tags: + - Gateway + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/update/artifact-info: + get: + summary: Retrieve the Gateway's artifact information string + tags: + - Gateway + responses: + '200': + description: Build artifact information + content: + applicaiton/json: + schema: + type: object + properties: + artifact: + type: string + description: Detailed artifact information + example: gw_image-3.1.394-os.77add-ts.60717-hk.13c6a + required: + - artifact + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /gateway/update/channel: + get: + summary: Get the current Gateway firmware update channel + tags: + - Gateway + responses: + '200': + description: The current Gateway firmware update channel + content: + application/json: + schema: + type: object + properties: + channel: + type: string + description: The current Gateway firmware update channel + example: production + required: + - channel + put: + summary: Set the Gateway firmware update channel + tags: + - Gateway + parameters: + - name: channel + in: query + required: true + description: >- + The new update/upgrade channel, which must be one of: [ coredev, + nightly, qa, beta, production ]. The recommended value is + 'production' + schema: + type: string + example: qa + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /gateway/wifi: + put: + summary: Set Gateway Wifi parameters + tags: + - Gateway + requestBody: + description: Set Wifi Parameters + content: + application/json: + schema: + type: object + properties: + ssid: + type: string + description: The WiFi SSID + example: Woodland Hills + password: + type: string + description: The WiFi password. Must be 8..63 characters in length. + minLength: 8 + maxLength: 63 + format: password + example: password + required: + - ssid + - password + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + delete: + summary: Remove the Wifi credentials from the Gateway + tags: + - Gateway + responses: + '204': + description: Success, no content + /gateway/wifi/scan: + get: + summary: Retrieve the Wifi networks observable by the Gateway + tags: + - Gateway + parameters: + - name: delay + in: query + required: false + description: | + The number of seconds to scan before returning the results. + + Minimum is 3 seconds; Maximum is 20 seconds + schema: + type: integer + default: 10 + responses: + '200': + description: Wireless network scan list + content: + application/json: + schema: + type: object + properties: + ssidScanResults: + type: array + items: + type: object + description: Wireless network scan entry + properties: + ssid: + type: string + description: Wireless network name + example: HunterDouglasWiFi + rssi: + type: string + description: Wireless network RSSI value + example: '-81' + required: + - ssid + - rssi + required: + - ssidScanResults + /home: + get: + summary: >- + Get the entire home graph with all the Rooms, Shades, Scenes, + Automations, Gateways, and Remotes. + tags: + - HD Internal + responses: + '200': + description: Web HomeDoc Object + content: + application/json: + schema: + type: object + properties: + id: + type: string + _schemaVersion: + type: number + owner: + type: string + users?: + type: array + items: + type: string + modApp: + type: string + modDate: + type: string + home: + properties: + name: + title: Home.name + type: string + autosEnabled: + title: Home.autosEnabled + description: Are automations enabled globally + type: boolean + key: + title: Home.key + type: string + loc: + title: Home.loc + power: + title: Home.power + type: number + tz: + title: Home.tz + description: TimeZone - available in v24+ + type: string + required: + - name + - autosEnabled + additionalProperties: false + title: Home + type: object + rooms: + type: array + items: + type: object + properties: + id: + type: string + _id: + type: number + type: + type: number + name: + type: string + icon: + type: string + color: + type: string + shades: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter + Douglas use only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's + capabilities. In PowerView Gen 3, + + there are over 20 different types of Hunter + Douglas Shades available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, + all can be represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either + from the bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot + operate independently - the + + front shade must be down before the rear shade + can deploy. In other cases, + + they are independent with two motors and two + tubes. Where they are + + dependent, the shade firmware will force the + appropriate front shade + + position when the rear shade is controlled - + there is no need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom + up shade that also tilts plus a rear blackout + (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: >- + Approximate gauge of remaining Shade battery + power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: >- + Unique numerical identifier for a particular + Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully + open position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully + open position of the secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully + open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + scenes: + type: array + items: + properties: + id: + title: Scene.id + description: '@deprecated in v23' + type: string + _id: + title: Scene._id + type: number + name: + title: Scene.name + type: string + icon: + title: Scene.icon + type: string + color: + title: Scene.color + type: string + bleId: + title: Scene.bleId + type: number + modDate: + title: Scene.modDate + type: string + roomId: + title: Scene.roomId + description: '@deprecated in v23' + type: string + room_Id: + title: Scene.room_Id + type: number + members: + items: + properties: + id: + title: SceneMember.id + description: '@deprecated in v23' + type: string + _id: + title: SceneMember._id + type: number + pos: + title: SceneMember.pos + properties: + pos1: + title: ShadePosition.pos1 + type: number + pos2: + title: ShadePosition.pos2 + type: number + pos3: + title: ShadePosition.pos3 + type: number + tilt: + title: ShadePosition.tilt + type: number + vel: + title: ShadePosition.vel + type: number + required: + - pos1 + - pos2 + - pos3 + - tilt + - vel + additionalProperties: false + type: object + shdId: + title: SceneMember.shdId + description: '@deprecated in v23' + type: string + shd_Id: + title: SceneMember.shd_Id + type: number + required: + - id + - _id + - pos + - shdId + - shd_Id + additionalProperties: false + title: SceneMember + type: object + title: Scene.members + type: array + required: + - id + - _id + - name + - icon + - color + - bleId + - modDate + additionalProperties: false + title: Scene + type: object + automations: + type: array + items: + properties: + id: + title: Automation.id + description: '@deprecated in v23' + type: string + _id: + title: Automation._id + type: number + type: + title: Automation.type + type: number + enabled: + title: Automation.enabled + type: boolean + days: + title: Automation.days + type: number + hour: + title: Automation.hour + type: number + min: + title: Automation.min + type: number + bleId: + title: Automation.bleId + type: number + sceneId: + title: Automation.sceneId + description: '@deprecated in v23' + type: string + scene_Id: + title: Automation.scene_Id + type: number + errorShdIds: + items: + type: string + title: Automation.errorShdIds + description: '@deprecated in v23' + type: array + errorShd_Ids: + items: + type: number + title: Automation.errorShd_Ids + type: array + required: + - id + - _id + - type + - enabled + - days + - hour + - min + - bleId + - sceneId + - scene_Id + - errorShdIds + - errorShd_Ids + additionalProperties: false + title: Automation + type: object + remotes?: + type: array + items: + properties: + id: + title: Remote.id + description: '@deprecated in v23' + type: string + _id: + title: Remote._id + type: number + name: + title: Remote.name + type: string + v: + title: Remote.v + type: number + mac: + title: Remote.mac + type: string + bleName: + title: Remote.bleName + type: string + members: + items: + properties: + id: + title: RemoteMember.id + description: '@deprecated in v23' + type: string + _id: + title: RemoteMember._id + type: number + group: + title: RemoteMember.group + type: number + shdId: + title: RemoteMember.shdId + description: '@deprecated in v23' + type: string + shd_Id: + title: RemoteMember.shd_Id + type: number + required: + - id + - _id + - group + - shdId + - shd_Id + additionalProperties: false + title: RemoteMember + type: object + title: Remote.members + type: array + required: + - id + - _id + - name + - v + - mac + - bleName + additionalProperties: false + title: Remote + type: object + gateways: + type: array + items: + properties: + id: + title: Gateway.id + description: '@deprecated in v23' + type: string + _id: + title: Gateway._id + type: number + name: + title: Gateway.name + type: string + v: + title: Gateway.v + type: string + radio1V: + title: Gateway.radio1V + type: string + radio2V: + title: Gateway.radio2V + type: string + serial: + title: Gateway.serial + type: string + aux: + title: Gateway.aux + type: boolean + ip: + title: Gateway.ip + type: string + ssid: + title: Gateway.ssid + type: string + shdIds: + items: + type: string + title: Gateway.shdIds + description: '@deprecated in v23' + type: array + shd_Ids: + items: + type: number + title: Gateway.shd_Ids + type: array + required: + - id + - _id + - name + - v + - radio1V + - radio2V + - serial + - ip + - ssid + additionalProperties: false + title: Gateway + type: object + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/automations: + get: + summary: List the Automations for the Home + tags: + - Home + responses: + '200': + description: Web Automation List + content: + application/json: + schema: + type: array + items: + type: object + description: Web Automation Object + properties: + id: + type: number + description: Unique numerical Automation identifier + example: 33 + type: + type: number + description: | + Time-base for the automation + * 0 = Clock-based + * 2 = Before sunrise + * 6 = Before sunset + * 10 = After sunrise + * 14 = After sunset + example: 6 + enabled: + type: boolean + description: True indicates the Automation is enabled + example: true + days: + type: number + description: | + Bitmask of days when the automation is to run + * 0x00 = Reserved + * 0x01 = Monday + * 0x02 = Tuesday + * 0x04 = Wednesday + * 0x08 = Thursay + * 0x10 = Friday + * 0x20 = Saturday + * 0x40 = Sunday + * 0x80 = Reserved + example: 24 (i.e., 0x18, which is Thursday and Friday) + hour: + type: number + description: > + For clock-based automations, the 'hour' field is the + + exact hour the automation will occur. For sunrise & + sunset + + based automations, the 'hour' field is the hour offset + + before/after a sunrise/sunset automation is to run. + example: 4 + min: + type: number + description: > + For clock-based automations, the 'min' field is the + + exact min the automation will occur. For sunrise & + sunset + + based automations, the 'min' field is the minutes + offset + + before/after a sunrise/sunset automation is to run. + example: 5 + bleId: + type: number + description: The automation ID stored on the Shade itself. + example: 83 + sceneId: + type: number + description: >- + The unique numerical Scene identifier associated with + this Automation + example: 116 + errorShd_Ids: + type: array + description: >- + The Shade list where the Automation was unsuccessfully + provisioned. The value can be null + example: null + items: + type: number + description: The failing Shade identifier + example: 55 + required: + - id + - type + - enabled + - days + - hour + - min + - bleId + - sceneId + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/automations/{id}: + get: + summary: Get information for a Automation + tags: + - Home + parameters: + - name: id + in: path + description: Numerical identifier of the Automation + required: true + schema: + type: string + description: The numerical identifier for a particular Automation + example: 456 + responses: + '200': + description: Web Automation Object + content: + application/json: + schema: + type: object + description: Web Automation Object + properties: + id: + type: number + description: Unique numerical Automation identifier + example: 33 + type: + type: number + description: | + Time-base for the automation + * 0 = Clock-based + * 2 = Before sunrise + * 6 = Before sunset + * 10 = After sunrise + * 14 = After sunset + example: 6 + enabled: + type: boolean + description: True indicates the Automation is enabled + example: true + days: + type: number + description: | + Bitmask of days when the automation is to run + * 0x00 = Reserved + * 0x01 = Monday + * 0x02 = Tuesday + * 0x04 = Wednesday + * 0x08 = Thursay + * 0x10 = Friday + * 0x20 = Saturday + * 0x40 = Sunday + * 0x80 = Reserved + example: 24 (i.e., 0x18, which is Thursday and Friday) + hour: + type: number + description: > + For clock-based automations, the 'hour' field is the + + exact hour the automation will occur. For sunrise & + sunset + + based automations, the 'hour' field is the hour offset + + before/after a sunrise/sunset automation is to run. + example: 4 + min: + type: number + description: > + For clock-based automations, the 'min' field is the + + exact min the automation will occur. For sunrise & + sunset + + based automations, the 'min' field is the minutes offset + + before/after a sunrise/sunset automation is to run. + example: 5 + bleId: + type: number + description: The automation ID stored on the Shade itself. + example: 83 + sceneId: + type: number + description: >- + The unique numerical Scene identifier associated with + this Automation + example: 116 + errorShd_Ids: + type: array + description: >- + The Shade list where the Automation was unsuccessfully + provisioned. The value can be null + example: null + items: + type: number + description: The failing Shade identifier + example: 55 + required: + - id + - type + - enabled + - days + - hour + - min + - bleId + - sceneId + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/id: + get: + summary: Get the Cloud identifier of the Home + tags: + - Home + responses: + '200': + description: The cloud identifier of the Home + content: + application/json: + schema: + type: object + properties: + id: + type: string + description: The Home identifier + required: + - id + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/id/silent: + get: + summary: Get the Home Cloud identifier without logging the operation + tags: + - HD Internal + responses: + '200': + description: The cloud identifier of the Home + content: + application/json: + schema: + type: object + properties: + id: + type: string + description: The Home identifier + required: + - id + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/integration/{integrationName}: + put: + summary: >- + Store the supplied 'integrationName' in the HomeDoc's integrations + list + tags: + - Home + parameters: + - name: integrationName + in: path + description: >- + Unique 3rd party ASCII integration identifier that must be all + lower case, can be a maximum of 12 characters in length, and must + only contain the characters [a-z], [0-9], or the special + characters '.' (dot/period), '@', '_' (underscore) and '-' + (hyphen) + required: true + schema: + type: string + example: hd.com + responses: + '204': + description: Success, no content + delete: + summary: >- + Remove the supplied 'integrationName' from the HomeDoc's integrations + list + tags: + - Home + parameters: + - name: integrationName + in: path + description: >- + Unique 3rd party ASCII integration identifier that must be all + lower case, can be a maximum of 12 characters in length, and must + only contain the characters [a-z], [0-9], or the special + characters '.' (dot/period), '@', '_' (underscore) and '-' + (hyphen) + required: true + schema: + type: string + example: hd.com + responses: + '204': + description: Success, no content + /home/rooms: + get: + summary: List the Rooms for the Home + tags: + - Home + parameters: + - name: shades + in: query + description: >- + Optional query param to return Shades as part of the Room object. + Shades will not be returned if this query param is omitted or + false. + schema: + type: boolean + responses: + '200': + description: Web Room List + content: + application/json: + schema: + type: array + items: + type: object + description: Room information + properties: + id: + type: number + description: Unique numerical Room identifier + example: 674 + name: + type: string + description: Base64 encoded Room name + example: T2ZmaWNlCg== + ptName: + type: string + description: Room name + example: Office + color: + type: string + description: > + Unique identifier for a Room color from HomeDoc Room + object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Room object + + Note: The numeric value is supplied as a string + example: 669 + type: + type: number + description: Room type enumeration value + example: 3 + shades: + type: array + description: >- + Optional list of Shades in a Room, which can be an + empty list + example: [] + items: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter + Douglas use only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's + capabilities. In PowerView Gen 3, + + there are over 20 different types of Hunter + Douglas Shades available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, all + can be represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either + from the bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot + operate independently - the + + front shade must be down before the rear shade + can deploy. In other cases, + + they are independent with two motors and two + tubes. Where they are + + dependent, the shade firmware will force the + appropriate front shade + + position when the rear shade is controlled - + there is no need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom + up shade that also tilts plus a rear blackout + (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: >- + Approximate gauge of remaining Shade battery + power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: >- + Unique numerical identifier for a particular + Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully open + position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully open + position of the secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully open + tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + required: + - id + - name + - ptName + - color + - icon + - type + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/rooms/{id}: + get: + summary: Get information for a Room + tags: + - Home + parameters: + - name: id + in: path + description: Numerical identifier of the Room + required: true + schema: + type: string + description: The numerical identifier for a particular Room + example: 2 + responses: + '200': + description: Web Room Object + content: + application/json: + schema: + type: object + description: Room information + properties: + id: + type: number + description: Unique numerical Room identifier + example: 674 + name: + type: string + description: Base64 encoded Room name + example: T2ZmaWNlCg== + ptName: + type: string + description: Room name + example: Office + color: + type: string + description: > + Unique identifier for a Room color from HomeDoc Room + object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Room object + + Note: The numeric value is supplied as a string + example: 669 + type: + type: number + description: Room type enumeration value + example: 3 + shades: + type: array + description: >- + Optional list of Shades in a Room, which can be an empty + list + example: [] + items: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter + Douglas use only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. + In PowerView Gen 3, + + there are over 20 different types of Hunter + Douglas Shades available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, all + can be represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either + from the bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot + operate independently - the + + front shade must be down before the rear shade can + deploy. In other cases, + + they are independent with two motors and two + tubes. Where they are + + dependent, the shade firmware will force the + appropriate front shade + + position when the rear shade is controlled - there + is no need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up + shade that also tilts plus a rear blackout + (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully open + position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully open + position of the secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully open + tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + required: + - id + - name + - ptName + - color + - icon + - type + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/scenes: + get: + summary: List the Scenes for the Home + tags: + - Home + responses: + '200': + description: Web Scene List + content: + application/json: + schema: + type: array + items: + type: object + description: Scene information + properties: + id: + type: number + description: Unique numerical Scene identifier + example: 234 + name: + type: string + description: Base64 encoded Scene name + example: T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo= + ptName: + type: string + description: The Scene name + example: Open All Office Shades + color: + type: string + description: > + Unique identifier for a Scene color from HomeDoc Scene + object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 669 + networkNumber: + type: number + description: Numerical network identifier + example: 7383 + roomIds: + type: array + description: >- + List of unique numerical Room identifiers associated + with this Scene + example: + - 55 + - 66 + items: + type: number + required: + - id + - name + - ptName + - color + - icon + - networkNumber + - roomIds + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/scenes/active: + get: + summary: List the active Scenes for the Home + tags: + - Home + responses: + '200': + description: Web Scene List + content: + application/json: + schema: + type: array + items: + type: object + description: Scene information + properties: + id: + type: number + description: Unique numerical Scene identifier + example: 234 + name: + type: string + description: Base64 encoded Scene name + example: T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo= + ptName: + type: string + description: The Scene name + example: Open All Office Shades + color: + type: string + description: > + Unique identifier for a Scene color from HomeDoc Scene + object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 669 + networkNumber: + type: number + description: Numerical network identifier + example: 7383 + roomIds: + type: array + description: >- + List of unique numerical Room identifiers associated + with this Scene + example: + - 55 + - 66 + items: + type: number + required: + - id + - name + - ptName + - color + - icon + - networkNumber + - roomIds + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/scenes/{id}: + get: + summary: Get information for a Scene + tags: + - Home + parameters: + - name: id + in: path + description: Numerical identifier of the Scene + required: true + schema: + type: string + description: The numerical identifier for a particular Scene + example: 22 + responses: + '200': + description: Web Scene Object + content: + application/json: + schema: + type: object + description: Scene information + properties: + id: + type: number + description: Unique numerical Scene identifier + example: 234 + name: + type: string + description: Base64 encoded Scene name + example: T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo= + ptName: + type: string + description: The Scene name + example: Open All Office Shades + color: + type: string + description: > + Unique identifier for a Scene color from HomeDoc Scene + object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 669 + networkNumber: + type: number + description: Numerical network identifier + example: 7383 + roomIds: + type: array + description: >- + List of unique numerical Room identifiers associated + with this Scene + example: + - 55 + - 66 + items: + type: number + required: + - id + - name + - ptName + - color + - icon + - networkNumber + - roomIds + '404': + description: The resource was not found + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/scenes/{id}/activate: + put: + summary: Activate a particular Scene in the Home + tags: + - Home + parameters: + - name: id + in: path + description: Numerical identifier of the Scene + required: true + schema: + type: string + description: The numerical identifier for a particular Scene + example: 22 + responses: + '200': + description: List of Shade identifiers associated with a Scene + content: + application/json: + schema: + type: array + description: Array of numerical Shade identifiers + example: + - 22 + - 33 + - 44 + items: + type: number + '404': + description: The resource was not found + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/shades: + get: + summary: List the Shades for the Home + tags: + - Home + responses: + '200': + description: Web Shade List + content: + application/json: + schema: + type: array + items: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter Douglas + use only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. In + PowerView Gen 3, + + there are over 20 different types of Hunter Douglas + Shades available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, all can be + represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from + the bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot operate + independently - the + + front shade must be down before the rear shade can + deploy. In other cases, + + they are independent with two motors and two tubes. + Where they are + + dependent, the shade firmware will force the + appropriate front shade + + position when the rear shade is controlled - there is + no need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up + shade that also tilts plus a rear blackout + (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the fully closed to fully open position of the + primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the fully closed to fully open position of the + secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the fully closed to fully open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/shades/{id}: + get: + summary: Get information for a Shade + tags: + - Home + parameters: + - name: id + in: path + description: Numerical identifier of the Shade + required: true + schema: + type: string + description: The numerical identifier for a particular Shade + example: 21 + responses: + '200': + description: Web Shade Object + content: + application/json: + schema: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter Douglas use + only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. In + PowerView Gen 3, + + there are over 20 different types of Hunter Douglas + Shades available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, all can be + represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from the + bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot operate + independently - the + + front shade must be down before the rear shade can + deploy. In other cases, + + they are independent with two motors and two tubes. + Where they are + + dependent, the shade firmware will force the appropriate + front shade + + position when the rear shade is controlled - there is no + need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up shade + that also tilts plus a rear blackout (non-tilting) + shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary + rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/shades/{id}/motion: + put: + summary: Perform the specified motion operation for a Shade + tags: + - Home + parameters: + - name: id + in: path + description: Numerical identifier of the Shade + required: true + schema: + type: string + description: The numerical identifier for a particular Shade + example: 21 + requestBody: + description: Shade motion command + content: + application/json: + schema: + type: object + properties: + motion: + description: Motion command + type: string + enum: + - jog + example: jog + required: + - motion + responses: + '200': + description: Aggregate response with status for each requested Shade + content: + application/json: + schema: + type: object + properties: + err: + type: number + responses: + type: array + items: + type: object + properties: + id: + type: number + bleName: + type: string + err: + description: Only present on error condition + type: number + hex: + description: >- + WC-API encoded response. Only present on success + condition + type: string + required: + - id + - bleName + required: + - err + - responses + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/shades/{id}/positions: + put: + summary: Set the positions for a Shade + deprecated: true + tags: + - Home + parameters: + - name: id + in: path + description: Numerical identifier of the Shade + required: true + schema: + type: string + description: The numerical identifier for a particular Shade + example: 21 + requestBody: + description: Shade positions command + content: + application/json: + schema: + type: object + properties: + positions: + type: object + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open position of the primary + rail. When omitted, the position of the primary rail + is not changed. + minimum: 0 + maximum: 1 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 that the represents + fully closed to fully open position of the secondary + rail. When omitted, the position of the secondary + rail. is not changed. + minimum: 0 + maximum: 1 + servo3: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open position of the third rail. + When omitted, the position of the third rail is not + changed. + minimum: 0 + maximum: 1 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open tilt position When omitted, + the tilt position is not changed. + minimum: 0 + maximum: 1 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + minimum to maximum velocity of the shade motion. When + omitted, the default velocity is used. + minimum: 0 + maximum: 1 + required: + - positions + examples: + Open: + value: '{"positions": { "primary" : 1}}' + summary: Open Shade(s) - 100% light transmission + Close: + value: '{"positions": { "primary" : 0}}' + summary: Close Shade(s) - 0% light transmission + TiltOpen: + value: '{"positions": { "tilt" : 1}}' + summary: Tilt Open Shade(s) + TiltClose: + value: '{"positions": { "tilt" : 0}}' + summary: Tilt Close Shade(s) + Goto25Percent: + value: '{"positions": { "primary" : 0.25} }' + summary: Move primary rail to 25% light transmission + responses: + '200': + description: Shade event + content: + text/event-stream: + schema: + type: object + description: Shade event + properties: + evt: + type: string + description: Event name + enum: + - shade-offline + - shade-online + - motion-started + - motion-stopped + - battery-alert + example: motion-started + isoDate: + type: string + description: ISO timestamp + example: '2021-12-06T20:01:11.934Z' + bleName: + type: string + description: Shade name + example: PIR:1233 + id: + type: number + description: Unique Shade identifier + example: 55 + currentPositions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary + rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + targetPositions: + type: object + description: > + Optional target Shade position values and estimate + completion time + + that may only present when the Event name is + 'motion-started'. + + The property may be omitted even with the Event + 'motion-started' + + if the values cannot be obtained or computed + properties: + etaInSeconds: + type: number + description: >- + Estimated time in seconds for the Shade to reach its + target positions + example: 7 + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary + rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + required: + - evt + - isoDate + - bleName + - id + - currentPositions + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/shades/exec: + post: + summary: >- + Execute an arbitrary hexadecimal command on a list of shades (For + Hunter Douglas internal use only) + description: >- + The list of Shades is specified by either of the "ids" or "shades" + parameters. Only one of these should and must be utilized. + + - The ``ids`` parameter accepts a list of numerical shade identifiers. + - The ``shades`` parameter accepts a list of strings like ``PIR:6521`` + tags: + - HD Internal + parameters: + - name: ids + in: query + required: false + description: | + One or more Shade identifiers separated by commas. + * _**Preferred over the 'shades' query parameter**_ + * _Note: Shade identifiers remain constant in the eventuality of a motor replacement_ + schema: + type: array + items: + type: number + style: form + explode: false + example: 12,42,133 + - name: shades + in: query + required: false + description: One or more Shade names (i.e., BleNames) separated by commas + schema: + type: array + items: + type: string + style: form + explode: false + example: SON:1234,PIR:9AE2 + requestBody: + description: Hexadecimal representation of a Shade command + content: + application/json: + schema: + type: object + properties: + hex: + type: string + description: Hex Shade command + example: f711990101 + required: + - hex + responses: + '200': + description: Aggregate response with status for each requested Shade + content: + application/json: + schema: + type: object + properties: + err: + type: number + responses: + type: array + items: + type: object + properties: + id: + type: number + bleName: + type: string + err: + description: Only present on error condition + type: number + hex: + description: >- + WC-API encoded response. Only present on success + condition + type: string + required: + - id + - bleName + required: + - err + - responses + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/shades/positions: + put: + summary: Set the positions of a Shade or set of Shades + description: >- + The list of Shades is specified by either of the "ids" or "shades" + parameters. Only one of these should and must be utilized. + + - The ``ids`` parameter accepts a list of numerical shade identifiers. + - The ``shades`` parameter accepts a list of strings like ``PIR:6521`` + tags: + - Home + parameters: + - name: ids + in: query + required: false + description: | + One or more Shade identifiers separated by commas. + * _**Preferred over the 'shades' query parameter**_ + * _Note: Shade identifiers remain constant in the eventuality of a motor replacement_ + schema: + type: array + items: + type: number + style: form + explode: false + example: 12,42,133 + - name: shades + in: query + required: false + description: One or more Shade names (i.e., BleNames) separated by commas + schema: + type: array + items: + type: string + style: form + explode: false + example: SON:1234,PIR:9AE2 + requestBody: + description: Shade positions command + content: + application/json: + schema: + type: object + properties: + positions: + type: object + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open position of the primary + rail. When omitted, the position of the primary rail + is not changed. + minimum: 0 + maximum: 1 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 that the represents + fully closed to fully open position of the secondary + rail. When omitted, the position of the secondary + rail. is not changed. + minimum: 0 + maximum: 1 + servo3: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open position of the third rail. + When omitted, the position of the third rail is not + changed. + minimum: 0 + maximum: 1 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open tilt position When omitted, + the tilt position is not changed. + minimum: 0 + maximum: 1 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + minimum to maximum velocity of the shade motion. When + omitted, the default velocity is used. + minimum: 0 + maximum: 1 + required: + - positions + examples: + Open: + value: '{"positions": { "primary" : 1}}' + summary: Open Shade(s) - 100% light transmission + Close: + value: '{"positions": { "primary" : 0}}' + summary: Close Shade(s) - 0% light transmission + TiltOpen: + value: '{"positions": { "tilt" : 1}}' + summary: Tilt Open Shade(s) + TiltClose: + value: '{"positions": { "tilt" : 0}}' + summary: Tilt Close Shade(s) + Goto25Percent: + value: '{"positions": { "primary" : 0.25} }' + summary: Move primary rail to 25% light transmission + responses: + '200': + description: Aggregate response with status for each requested Shade + content: + application/json: + schema: + type: object + properties: + err: + type: number + responses: + type: array + items: + type: object + properties: + id: + type: number + bleName: + type: string + err: + description: Only present on error condition + type: number + hex: + description: >- + WC-API encoded response. Only present on success + condition + type: string + required: + - id + - bleName + required: + - err + - responses + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/shades/stop: + put: + summary: Stop any current motion on a given list of Shades + description: >- + The list of Shades is specified by either of the "ids" or "shades" + parameters. Only one of these should and must be utilized. + + - The ``ids`` parameter accepts a list of numerical shade identifiers. + - The ``shades`` parameter accepts a list of strings like ``PIR:6521`` + tags: + - Home + parameters: + - name: ids + in: query + required: false + description: | + One or more Shade identifiers separated by commas. + * _**Preferred over the 'shades' query parameter**_ + * _Note: Shade identifiers remain constant in the eventuality of a motor replacement_ + schema: + type: array + items: + type: number + style: form + explode: false + example: 12,42,133 + - name: shades + in: query + required: false + description: One or more Shade names (i.e., BleNames) separated by commas + schema: + type: array + items: + type: string + style: form + explode: false + example: SON:1234,PIR:9AE2 + responses: + '200': + description: Aggregate response with status for each requested Shade + content: + application/json: + schema: + type: object + properties: + err: + type: number + responses: + type: array + items: + type: object + properties: + id: + type: number + bleName: + type: string + err: + description: Only present on error condition + type: number + hex: + description: >- + WC-API encoded response. Only present on success + condition + type: string + required: + - id + - bleName + required: + - err + - responses + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/shades/time: + get: + summary: Get the clock time from the Shade for the given list of Shades + description: >- + The list of Shades is specified by either of the "ids" or "shades" + parameters. Only one of these should and must be utilized. + + - The ``ids`` parameter accepts a list of numerical shade identifiers. + - The ``shades`` parameter accepts a list of strings like ``PIR:6521`` + tags: + - Home + parameters: + - name: ids + in: query + required: false + description: | + One or more Shade identifiers separated by commas. + * _**Preferred over the 'shades' query parameter**_ + * _Note: Shade identifiers remain constant in the eventuality of a motor replacement_ + schema: + type: array + items: + type: number + style: form + explode: false + example: 12,42,133 + - name: shades + in: query + required: false + description: One or more Shade names (i.e., BleNames) separated by commas + schema: + type: array + items: + type: string + style: form + explode: false + example: SON:1234,PIR:9AE2 + responses: + '200': + description: Aggregate response with status for each requested Shade + content: + application/json: + schema: + type: object + properties: + err: + type: number + responses: + type: array + items: + type: object + properties: + id: + type: number + bleName: + type: string + err: + description: Only present on error condition + type: number + hex: + description: >- + WC-API encoded response. Only present on success + condition + type: string + required: + - id + - bleName + required: + - err + - responses + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + put: + summary: Update the Shade clock time to now for the given list of Shades + description: >- + The list of Shades is specified by either of the "ids" or "shades" + parameters. Only one of these should and must be utilized. + + - The ``ids`` parameter accepts a list of numerical shade identifiers. + - The ``shades`` parameter accepts a list of strings like ``PIR:6521`` + tags: + - Home + parameters: + - name: ids + in: query + required: false + description: | + One or more Shade identifiers separated by commas. + * _**Preferred over the 'shades' query parameter**_ + * _Note: Shade identifiers remain constant in the eventuality of a motor replacement_ + schema: + type: array + items: + type: number + style: form + explode: false + example: 12,42,133 + - name: shades + in: query + required: false + description: One or more Shade names (i.e., BleNames) separated by commas + schema: + type: array + items: + type: string + style: form + explode: false + example: SON:1234,PIR:9AE2 + responses: + '200': + description: Aggregate response with status for each requested Shade + content: + application/json: + schema: + type: object + properties: + err: + type: number + responses: + type: array + items: + type: object + properties: + id: + type: number + bleName: + type: string + err: + description: Only present on error condition + type: number + hex: + description: >- + WC-API encoded response. Only present on success + condition + type: string + required: + - id + - bleName + required: + - err + - responses + '503': + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/events: + get: + summary: >- + Provides an aggregate stream of ALL events via server-sent events + (SSE). + description: This event stream does not work in Swagger. Use the Chrome browser + tags: + - Events + responses: + '100': + description: > + Stream of JSON Scene and Shade event objects. + + + See the /home/scenes/events and /home/shades/events APIs for + object shapes + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/events/listeners: + get: + summary: Returns a list of all active event stream listeners + tags: + - HD Internal + responses: + '200': + description: List of Event Listeners + content: + application/json: + schema: + type: object + properties: + slots: + type: array + items: + type: string + activeListeners: + type: array + items: + type: string + required: + - slots + - activeListeners + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + delete: + summary: Deletes all active event stream listeners + tags: + - HD Internal + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/scenes/events: + get: + summary: Provides a stream of Shade events via server-sent events (SSE). + description: >- + This event stream does not work in Swagger. Use the Chrome browser. + Scene events are not reported until Shade motion stops + tags: + - Events + responses: + '100': + description: Stream of JSON Scene event objects + content: + text/event-stream: + schema: + type: object + description: Scene event + properties: + evt: + type: string + description: Event name + enum: + - scene-add + - scene-del + - scene-activated + - scene-deactivated + example: scene-activated + isoDate: + type: string + description: ISO timestamp + example: '2021-12-06T20:01:11.934Z' + id: + type: number + description: Unique Scene identifier + example: 55 + scene: + type: object + description: Scene information + properties: + id: + type: number + description: Unique numerical Scene identifier + example: 234 + name: + type: string + description: Base64 encoded Scene name + example: T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo= + ptName: + type: string + description: The Scene name + example: Open All Office Shades + color: + type: string + description: > + Unique identifier for a Scene color from HomeDoc + Scene object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: > + Unique identifier for a icon from HomeDoc Scene + object + + + Note: The numeric value is supplied as a string + example: 669 + networkNumber: + type: number + description: Numerical network identifier + example: 7383 + roomIds: + type: array + description: >- + List of unique numerical Room identifiers associated + with this Scene + example: + - 55 + - 66 + items: + type: number + required: + - id + - name + - ptName + - color + - icon + - networkNumber + - roomIds + required: + - evt + - isoDate + - id + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /home/shades/events: + get: + summary: Provides a stream of Shade events via server-sent events (SSE). + description: This event stream does not work in Swagger. Use the Chrome browser + tags: + - Events + responses: + '100': + description: Shade event + content: + text/event-stream: + schema: + type: object + description: Shade event + properties: + evt: + type: string + description: Event name + enum: + - shade-offline + - shade-online + - motion-started + - motion-stopped + - battery-alert + example: motion-started + isoDate: + type: string + description: ISO timestamp + example: '2021-12-06T20:01:11.934Z' + bleName: + type: string + description: Shade name + example: PIR:1233 + id: + type: number + description: Unique Shade identifier + example: 55 + currentPositions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary + rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + targetPositions: + type: object + description: > + Optional target Shade position values and estimate + completion time + + that may only present when the Event name is + 'motion-started'. + + The property may be omitted even with the Event + 'motion-started' + + if the values cannot be obtained or computed + properties: + etaInSeconds: + type: number + description: >- + Estimated time in seconds for the Shade to reach its + target positions + example: 7 + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary + rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + required: + - evt + - isoDate + - bleName + - id + - currentPositions + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /sps/channels/check: + get: + summary: SPS only - Verifies the SPS comms channels by sending echo messages + tags: + - HD Internal + responses: + '200': + description: >- + Comms channels that are offline, and an empty array means all + operational + content: + application/json: + schema: + type: object + properties: + failedChannels: + type: array + items: + type: string + description: name of downed channel + example: comms01 + required: + - failedChannels + /sps/comms/log/level: + put: + summary: SPS only - Sets the comms layer log level + tags: + - HD Internal + parameters: + - name: level + in: query + required: true + description: >- + The new log level, which must be one of: none, error, warn, info, + debug, or silly. The recommended value is info. + schema: + type: string + example: debug + - name: persist + in: query + required: false + description: >- + If the new log level is to persist (be sticky) across application + restarts. Values are string 'true' or 'false' + schema: + type: string + example: 'true' + responses: + '200': + description: Comms set log level response + content: + application/json: + schema: + type: object + properties: + responseIsOk: + type: boolean + description: The log level set was successful or not + returnedLogLevel: + type: number + description: >- + The new log level. 0=none, 1=error, 2=warn, 3=info, + 4=debug, 5=silly + returnedPersist: + type: boolean + description: The new persistence setting + required: + - responseIsOk + - returnedLogLevel + - returnedPersist + /sps/shades/comparison: + get: + summary: >- + SPS only - Compare the HomeDoc SPS PV+ Shades list against the actual + SPS PV+ Shades + tags: + - HD Internal + responses: + '200': + description: HomeDoc vs actual PV+ Shade comparison results + content: + application/json: + schema: + type: object + properties: + perfectMatch: + type: boolean + description: >- + Indicates if the HomeDoc and the SPS are perfectly + matched + differences: + type: object + spsHomeDocShades: + type: array + items: + type: object + description: Unique Shade information + properties: + bleName: + type: string + description: Shade BleName + example: SON:1234 + required: + - bleName + spsActualShades: + type: array + items: + type: object + description: Unique Shade information + properties: + bleName: + type: string + description: Shade BleName + example: SON:1234 + required: + - bleName + required: + - perfectMatch + - differences + - spsHomeDocShades + - spsActualShades + /sps/shades/enumerated: + get: + summary: SPS only - Returns the list of PV+ Shades on an SPS + tags: + - HD Internal + responses: + '200': + description: PV+ Shades found on the SPS + content: + application/json: + schema: + type: array + items: + type: object + description: SPS port object + properties: + port: + type: number + description: Port/channel number from 1 to 16 inclusive + example: 1 + shades: + type: array + description: Information about PV+ Shades on this port + items: + type: object + description: SPS Shade description + properties: + slot: + type: number + description: >- + Shade port/channel number between 0 and 2 + inclusive + example: 1 + rssi: + type: number + description: Perfect RSSI value + example: -1 + ready: + type: number + description: Shade is ready + example: 1 + bleName: + type: string + description: Shade's BleName + example: SON:1234 + mac: + type: string + description: Shade's MAC address + example: '00:12:34:56:78:90' + pcaVersion: + type: string + description: Shade PCA version + example: 0.5.0 + pcaChecksum: + type: string + description: Shade PCA release checksum + example: '0x54321ABC' + pcaUpdateNeeded: + type: boolean + description: >- + The Shade PCA version is mismatched with the + version in the release. Time to do a upgrade + example: false + required: + - slot + - rssi + - ready + - bleName + - mac + - pcaVersion + - pcaChecksum + - pcaUpdateNeeded + required: + - port + - shades + /sps/shades/exec: + post: + summary: >- + SPS only - Execute an arbitrary hexadecimal command on a list of SPS + Shades (For Hunter Douglas internal use only) + description: >- + The list of Shades is specified by one of the following: + "coordinates", "ids", or "shades" parameters. Only one of these should + and must be utilized. + + - The ``coordinates`` parameter accepts a comma seperated list of channel:slot coordinates like ``1:0`` + - The ``ids`` parameter accepts a list of numerical shade identifiers. + - The ``shades`` parameter accepts a list of strings like ``PIR:6521`` + tags: + - HD Internal + parameters: + - name: coordinates + in: query + required: false + description: > + One or more Shade "channel:slot" coordinate pairs with each pair + separated by a comma + * The "channel" value is an integer in the range from 1 to 16 + * The "slot" value is an integer in the range of 0 to 2 + * The "channel" and "slot" pair values are separated by a colon character (i.e., a ':' character) + schema: + type: array + items: + type: string + style: form + explode: false + example: 1:0,2:1,14:2 + - name: ids + in: query + required: false + description: | + One or more Shade identifiers separated by commas. + * _**Preferred over the 'shades' query parameter**_ + * _Note: Shade identifiers remain constant in the eventuality of a motor replacement_ + schema: + type: array + items: + type: number + style: form + explode: false + example: 12,42,133 + - name: shades + in: query + required: false + description: One or more Shade names (i.e., BleNames) separated by commas + schema: + type: array + items: + type: string + style: form + explode: false + example: SON:1234,PIR:9AE2 + - name: failUnresolved + in: query + description: >- + Query param controlling if the request is to fail if any of the + coorinates query parameter pairs fail to resolve to a Shade + required: false + schema: + type: boolean + requestBody: + description: Hexadecimal representation of a Shade command + content: + application/json: + schema: + type: object + properties: + hex: + type: string + description: Hex Shade command + example: f711990101 + required: + - hex + responses: + '200': + description: >- + Aggregate response with status for each requested Shade and + optionally a list of unresolvedCoordinates + content: + application/json: + schema: + type: object + properties: + err: + type: number + unresolvedCoordinates: + type: array + description: Any coordinate pairs that could not be resolved + items: + type: string + description: A unresolved coordinate pair + example: '1:2' + responses: + type: array + items: + type: object + properties: + id: + type: number + bleName: + type: string + err: + description: Only present on error condition + type: number + hex: + description: >- + WC-API encoded response. Only present on success + condition + type: string + required: + - id + - bleName + required: + - err + - responses + '400': + description: The request parameters were incorrect + content: + application/json: + schema: + type: object + properties: + errMsg: + type: string + description: Describes which parameters were incorrect + required: + - errMsg + default: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + /sps/pca/upgrade: + get: + summary: >- + SPS only - Upgrade the Shade PCA modules using the built in binary + image + tags: + - HD Internal + responses: + '200': + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + /sps/pca/versions: + get: + summary: SPS only - Retrieve the Shade PCA modules version information + tags: + - HD Internal + responses: + '200': + description: List of SPS PV+ Shade PCA versions + content: + application/json: + schema: + type: array + description: List of channels containing arrays of PV+ Shades + items: + type: array + description: >- + PCA information for a list of PV+ Shades on a single + channel + items: + type: object + description: SPS PCA version information + properties: + slot: + type: number + description: Shade port/channel number between 0 and 2 inclusive + example: 1 + bleName: + type: string + description: Shade's BleName + example: SON:1234 + mac: + type: string + description: Shade's MAC address + example: '00:12:34:56:78:90' + pcaVersion: + type: string + description: Shade PCA version + example: 0.5.0 + pcaChecksum: + type: string + description: Shade PCA release checksum + example: '0x54321ABC' + pcaUpdateNeeded: + type: boolean + description: >- + The Shade PCA version is mismatched with the version + in the release. Time to do a upgrade + example: false + required: + - slot + - bleName + - mac + - pcaVersion + - pcaChecksum + - pcaUpdateNeeded + components: + parameters: + RoomShades: + name: shades + in: query + description: >- + Optional query param to return Shades as part of the Room object. + Shades will not be returned if this query param is omitted or false. + schema: + type: boolean + AutomationId: + name: id + in: path + description: Numerical identifier of the Automation + required: true + schema: + type: string + description: The numerical identifier for a particular Automation + example: 456 + AutomationEnable: + name: enable + in: query + description: >- + Query param to enable or disable a Automation. 'true' enables. 'false' + disables + required: true + schema: + type: boolean + FailUnresolved: + name: failUnresolved + in: query + description: >- + Query param controlling if the request is to fail if any of the + coorinates query parameter pairs fail to resolve to a Shade + required: false + schema: + type: boolean + IntegrationName: + name: integrationName + in: path + description: >- + Unique 3rd party ASCII integration identifier that must be all lower + case, can be a maximum of 12 characters in length, and must only + contain the characters [a-z], [0-9], or the special characters '.' + (dot/period), '@', '_' (underscore) and '-' (hyphen) + required: true + schema: + type: string + example: hd.com + RoomId: + name: id + in: path + description: Numerical identifier of the Room + required: true + schema: + type: string + description: The numerical identifier for a particular Room + example: 2 + SceneId: + name: id + in: path + description: Numerical identifier of the Scene + required: true + schema: + type: string + description: The numerical identifier for a particular Scene + example: 22 + ShadeId: + name: id + in: path + description: Numerical identifier of the Shade + required: true + schema: + type: string + description: The numerical identifier for a particular Shade + example: 21 + ShadeCoordinateList: + name: coordinates + in: query + required: false + description: > + One or more Shade "channel:slot" coordinate pairs with each pair + separated by a comma + * The "channel" value is an integer in the range from 1 to 16 + * The "slot" value is an integer in the range of 0 to 2 + * The "channel" and "slot" pair values are separated by a colon character (i.e., a ':' character) + schema: + type: array + items: + type: string + style: form + explode: false + example: 1:0,2:1,14:2 + ShadeNameList: + name: shades + in: query + required: false + description: One or more Shade names (i.e., BleNames) separated by commas + schema: + type: array + items: + type: string + style: form + explode: false + example: SON:1234,PIR:9AE2 + ShadeIdList: + name: ids + in: query + required: false + description: | + One or more Shade identifiers separated by commas. + * _**Preferred over the 'shades' query parameter**_ + * _Note: Shade identifiers remain constant in the eventuality of a motor replacement_ + schema: + type: array + items: + type: number + style: form + explode: false + example: 12,42,133 + requestBodies: + CloudConfig: + description: Parameters for connection to the Cloud database + required: true + content: + application/json: + schema: + type: object + description: >- + Cloud configuration information used by the Gateway to access + the Cloud + properties: + homeId: + type: string + description: Home identifier as a hexadecimal string + example: Xffeg05RhmhUQez88Qyv + gatewayId: + type: number + description: >- + Numerical identifier of the Gateway as registered in the + Cloud + example: 103 + collection: + type: string + description: Name of the Home collection + default: homes + example: homes + clouddEnv: + type: number + description: >- + Select the Cloud environment to connect to: Dev = 0, + Production = 1 + default: 0 + example: 1 + required: + - homeId + - gatewayId + DiscoveryMinRssi: + description: Minimum RSSI filter + required: false + content: + application/json: + schema: + type: object + properties: + rssiMinValue: + type: number + description: Mimimum RSSI value + example: -66 + required: + - rssiMinValue + Hexadecimal: + description: Hexadecimal representation of a Shade command + content: + application/json: + schema: + type: object + properties: + hex: + type: string + description: Hex Shade command + example: f711990101 + required: + - hex + LedColor: + description: The color and brightness of the Gateway LED + content: + application/json: + schema: + description: The Gateway LED color and brightness + type: object + properties: + red: + type: number + description: Red + maximum: 0 + minimum: 255 + green: + type: number + description: Green + maximum: 0 + minimum: 255 + blue: + type: number + description: Blue + maximum: 0 + minimum: 255 + brightness: + type: number + description: Brightness + maximum: 0 + minimum: 100 + required: + - red + - green + - blue + - brightness + NetworkConfig: + description: Update the network configuration + content: + application/json: + schema: + description: The Gateway's network configuration information + type: object + properties: + staticIpEnabled: + type: boolean + description: >- + Indicates if the below staticIp object information is to be + applied. The below staticIp object is optional if + staticIpEnabled is false + example: true + staticIp: + type: object + description: >- + The optional static IP configuration information. If + staticIpEnabled is true, this object must be present in its + entirety. If staticIpEnabled is false, this object may be + present and if present, it must be fully provided + properties: + ip_address: + type: string + description: The IP address to utilize as the static IP + example: 10.0.0.101 + netwask: + type: string + description: The subnet mask to apply to the static IP address value + example: 255.255.255.0 + gateway_ip: + type: string + description: The static internet Gateway IP address to utilize + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + Motion: + description: Shade motion command + content: + application/json: + schema: + type: object + properties: + motion: + description: Motion command + type: string + enum: + - jog + example: jog + required: + - motion + Positions: + description: Shade positions command + content: + application/json: + schema: + type: object + properties: + positions: + type: object + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open position of the primary rail. + When omitted, the position of the primary rail is not + changed. + minimum: 0 + maximum: 1 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 that the represents + fully closed to fully open position of the secondary + rail. When omitted, the position of the secondary rail. + is not changed. + minimum: 0 + maximum: 1 + servo3: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open position of the third rail. + When omitted, the position of the third rail is not + changed. + minimum: 0 + maximum: 1 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open tilt position When omitted, + the tilt position is not changed. + minimum: 0 + maximum: 1 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + minimum to maximum velocity of the shade motion. When + omitted, the default velocity is used. + minimum: 0 + maximum: 1 + required: + - positions + examples: + Open: + value: '{"positions": { "primary" : 1}}' + summary: Open Shade(s) - 100% light transmission + Close: + value: '{"positions": { "primary" : 0}}' + summary: Close Shade(s) - 0% light transmission + TiltOpen: + value: '{"positions": { "tilt" : 1}}' + summary: Tilt Open Shade(s) + TiltClose: + value: '{"positions": { "tilt" : 0}}' + summary: Tilt Close Shade(s) + Goto25Percent: + value: '{"positions": { "primary" : 0.25} }' + summary: Move primary rail to 25% light transmission + PositionSingular: + description: Shade position command + content: + application/json: + schema: + type: object + properties: + position: + type: object + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open position of the primary rail. + When omitted, the position of the primary rail is not + changed. + minimum: 0 + maximum: 1 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 that the represents + fully closed to fully open position of the secondary + rail. When omitted, the position of the secondary rail. + is not changed. + minimum: 0 + maximum: 1 + servo3: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open position of the third rail. + When omitted, the position of the third rail is not + changed. + minimum: 0 + maximum: 1 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + fully closed to fully open tilt position When omitted, + the tilt position is not changed. + minimum: 0 + maximum: 1 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 that represents the + minimum to maximum velocity of the shade motion. When + omitted, the default velocity is used. + minimum: 0 + maximum: 1 + required: + - position + examples: + Open: + value: '{"position": { "primary" : 1}}' + summary: Open Shade(s) - 100% light transmission + Close: + value: '{"position": { "primary" : 0}}' + summary: Close Shade(s) - 0% light transmission + TiltOpen: + value: '{"position": { "tilt" : 1}}' + summary: Tilt Open Shade(s) + TiltClose: + value: '{"position": { "tilt" : 0}}' + summary: Tilt Close Shade(s) + Goto25Percent: + value: '{"position": { "primary" : 0.25} }' + summary: Move primary rail to 25% light transmission + ProvisionWifi: + description: Set Wifi Parameters + content: + application/json: + schema: + type: object + properties: + ssid: + type: string + description: The WiFi SSID + example: Woodland Hills + password: + type: string + description: The WiFi password. Must be 8..63 characters in length. + minLength: 8 + maxLength: 63 + format: password + example: password + required: + - ssid + - password + responses: + ArtifactInfo: + description: Build artifact information + content: + applicaiton/json: + schema: + type: object + properties: + artifact: + type: string + description: Detailed artifact information + example: gw_image-3.1.394-os.77add-ts.60717-hk.13c6a + required: + - artifact + AggregateResponse: + description: Aggregate response with status for each requested Shade + content: + application/json: + schema: + type: object + properties: + err: + type: number + responses: + type: array + items: + type: object + properties: + id: + type: number + bleName: + type: string + err: + description: Only present on error condition + type: number + hex: + description: >- + WC-API encoded response. Only present on success + condition + type: string + required: + - id + - bleName + required: + - err + - responses + AggregateResponseSpsTestExtended: + description: >- + Aggregate response with status for each requested Shade and optionally + a list of unresolvedCoordinates + content: + application/json: + schema: + type: object + properties: + err: + type: number + unresolvedCoordinates: + type: array + description: Any coordinate pairs that could not be resolved + items: + type: string + description: A unresolved coordinate pair + example: '1:2' + responses: + type: array + items: + type: object + properties: + id: + type: number + bleName: + type: string + err: + description: Only present on error condition + type: number + hex: + description: >- + WC-API encoded response. Only present on success + condition + type: string + required: + - id + - bleName + required: + - err + - responses + CloudConfig: + description: The Gateway Cloud configuration + content: + application/json: + schema: + type: object + description: >- + Cloud configuration information used by the Gateway to access + the Cloud + properties: + homeId: + type: string + description: Home identifier as a hexadecimal string + example: Xffeg05RhmhUQez88Qyv + gatewayId: + type: number + description: >- + Numerical identifier of the Gateway as registered in the + Cloud + example: 103 + collection: + type: string + description: Name of the Home collection + default: homes + example: homes + clouddEnv: + type: number + description: >- + Select the Cloud environment to connect to: Dev = 0, + Production = 1 + default: 0 + example: 1 + required: + - homeId + - gatewayId + ConnectionTest: + description: Conformation of internet connection + content: + application/json: + schema: + type: object + properties: + HealthCheckResponse: + type: object + properties: + success: + type: boolean + type: + type: string + message: + type: string + required: + - success + - type + - message + required: + - HealthCheckResponse + DiscoveryShadeList: + description: List of discovered Shades + content: + application/json: + schema: + type: object + properties: + scan: + type: array + description: Shades discovered List + items: + type: object + description: Discovered Shade entry + properties: + bleName: + type: string + description: Shade name + example: PIR:1E37 + filteredRssi: + type: number + description: Shade RSSI + example: -37 + required: + - bleName + - filteredRssi + required: + - scan + GatewayConfig: + description: The Gateway information object + content: + application/json: + schema: + type: object + properties: + config: + properties: + rev: + title: Config.rev + description: Config data structure version + type: number + hwVersion: + title: Config.hwVersion + description: Hardware version + type: string + model: + title: Config.model + description: Hardware model + type: string + brand: + title: Config.brand + description: Hardware brand + type: string + serialNumber: + title: Config.serialNumber + description: Unique serial number across all products + type: string + firmware: + title: Config.firmware + description: Software version + properties: + mainProcessor: + title: BuildVersions.mainProcessor + description: Version number + properties: + build: + title: TypescriptAppBuildInfo.build + description: Build number + type: number + name: + title: TypescriptAppBuildInfo.name + description: '???' + type: string + revision: + title: TypescriptAppBuildInfo.revision + description: Major version number + type: number + subRevision: + title: TypescriptAppBuildInfo.subRevision + description: Minor version number + type: number + required: + - build + - name + - revision + - subRevision + additionalProperties: false + type: object + radios: + items: + title: BuildVersions.radios.[] + properties: + build: + title: TransceiverFirmwareBuildInfo.build + description: Build number + type: number + revision: + title: TransceiverFirmwareBuildInfo.revision + description: Major version number + type: number + subRevision: + title: TransceiverFirmwareBuildInfo.subRevision + description: Minor version number + type: number + required: + - build + - revision + - subRevision + additionalProperties: false + description: Transceiver/Radio firmware information + type: object + title: BuildVersions.radios + description: Transceiver/Radio firmware versions + type: array + required: + - mainProcessor + - radios + additionalProperties: false + type: object + homeAssoc: + title: Config.homeAssoc + description: >- + 2-byte Hash of the HomeSecurityKey (AKA: + ShadeSecurityKey) + type: number + color: + title: Config.color + description: >- + User idle color - the color the user chose for the Idle + state + properties: + brightness: + title: LedColor.brightness + description: LED brightness percentage between 0 and 100 + type: number + red: + title: LedColor.red + description: Red color value between 0 and 255 + type: number + green: + title: LedColor.green + description: Green color value between 0 and 255 + type: number + blue: + title: LedColor.blue + description: Blue color value between 0 and 255 + type: number + required: + - brightness + - red + - green + - blue + additionalProperties: false + type: object + cloudConfig: + title: Config.cloudConfig + properties: + cloudEnv: + title: FirestoreConfig.cloudEnv + description: >- + What cloud environment to access (i.e, what + credentials to load) + type: number + collection: + title: FirestoreConfig.collection + description: Cloud collection name for homeDocs + type: string + homeId: + title: FirestoreConfig.homeId + description: Home identifier for this household + type: string + gatewayId: + title: FirestoreConfig.gatewayId + description: Gateway id for THIS gateway + type: number + required: + - cloudEnv + - collection + - homeId + - gatewayId + additionalProperties: false + description: >- + Cloud Config + + The properties required to uniquely identify a Home and + Gateway pairing. + type: object + rangeMapper: + title: Config.rangeMapper + description: Radio scan information + properties: + minRSSI: + title: RangeMapperDb.minRSSI + description: The minimum RSSI (of Shades) to report back on + type: number + levelingSeconds: + title: RangeMapperDb.levelingSeconds + description: >- + The number of seconds over that the algorithm + averages over + type: number + required: + - minRSSI + - levelingSeconds + additionalProperties: false + type: object + networkConfig: + title: Config.networkConfig + description: Persistent network configuration + properties: + staticIpEnabled: + title: NetworkConfig.staticIpEnabled + description: Is static IP configuration enabled? + type: boolean + staticIp: + properties: + ip_address: + title: NetworkConfig.staticIp.ip_address + description: Static IP address + type: string + netmask: + title: NetworkConfig.staticIp.netmask + description: Static IP network mask + type: string + gateway_ip: + title: NetworkConfig.staticIp.gateway_ip + description: Home gateway IP address (i.e., the home router + type: string + dns: + items: + type: string + title: NetworkConfig.staticIp.dns + description: DNS IP values + type: array + required: + - ip_address + - netmask + - gateway_ip + - dns + additionalProperties: false + title: NetworkConfig.staticIp + description: Static IP values if enabled + type: object + required: + - staticIpEnabled + - staticIp + additionalProperties: false + type: object + networkStatus: + title: Config.networkStatus + description: Transient network information + properties: + ipAddress: + title: NetworkStatus.ipAddress + description: Currently active IP address + type: string + activeInterface: + title: NetworkStatus.activeInterface + description: Current active network interface + type: string + primaryMacAddress: + title: NetworkStatus.primaryMacAddress + description: Primary MAC address to identify device + type: string + dns: + items: + type: string + title: NetworkStatus.dns + description: DNS entries + type: array + eth0: + title: NetworkStatus.eth0 + description: Wired network interface information + wlan0: + title: NetworkStatus.wlan0 + description: Wireless network interface information + internetState: + title: NetworkStatus.internetState + description: Internet connectivity state + type: string + enum: + - Connected + - Disconnected + ssid: + title: NetworkStatus.ssid + description: Wireless SSID + type: string + required: + - ipAddress + - activeInterface + - primaryMacAddress + - dns + - eth0 + - wlan0 + - internetState + - ssid + additionalProperties: false + type: object + mgwConfig: + title: Config.mgwConfig + description: Persistent multi-gateway configuration + properties: + primary: + title: MultiGatewayConfig.primary + description: This gateway is the primary in multi-gateway + type: boolean + required: + - primary + additionalProperties: false + type: object + mgwStatus: + title: Config.mgwStatus + description: Transient multi-gateway information + properties: + running: + title: MultiGatewayStatus.running + description: Multi-gateway is operational + type: boolean + required: + - running + additionalProperties: false + type: object + required: + - rev + - hwVersion + - model + - brand + - serialNumber + - firmware + - homeAssoc + - color + - cloudConfig + - rangeMapper + - networkConfig + - networkStatus + - mgwConfig + - mgwStatus + additionalProperties: false + title: Config + type: object + GatewayBalancer: + description: The Gateway balancer information object + content: + application/json: + schema: + type: object + properties: + maxShadesPerChannel: + type: number + seqNum: + type: number + balancingAlg: + type: string + example: 0 = BALANCE_BY_GROUP + transactionNum: + type: number + GatewayInfo: + description: Limited information about the Gateway + content: + application/json: + schema: + type: object + properties: + fwVersion: + type: string + description: Gateway firmware version string + example: 3.1.376 + serialNumber: + type: string + description: Gateway serial number + example: 3b4149d4f24d7bd7 + required: + - fwVersion + - serialNumber + GatewayRadios: + description: Gateway radios + content: + application/json: + schema: + properties: + serialNum: + type: string + appVersion: + type: string + softdeviceVersion: + type: string + bootloaderVersion: + type: string + type: object + GatewayRadioShades: + description: Gateway radio shades + content: + application/json: + schema: + type: array + items: + type: array + items: + type: object + properties: + index: + type: number + rssi: + type: number + example: -256 + ready: + type: number + example: 1 + bleName: + type: string + mac: + type: string + GatewayFabInfo: + description: Gateway Fab Info response + content: + application/json: + schema: + type: object + properties: + shadesFabInfoAllShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + shadesFabInfoLocalShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + cachedPrimary: + type: boolean + periodicRequesterTimerNextRunTime: + type: string + GatewayFabInfoPut: + description: Gateway Fab Info response + content: + application/json: + schema: + type: object + properties: + shadesFabInfoAllShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + shadesFabInfoLocalShades: + type: array + items: + type: object + properties: + bleName: + type: string + id: + type: number + appIndex: + type: number + modelId: + type: number + sourceGwName: + type: string + shadeLengthTickCnt: + type: number + periodicCheckIntervalMs: + type: number + periodicCheckTimerNextRunTime: + type: string + cachedPrimary: + type: boolean + periodicRequesterTimerNextRunTime: + type: string + GatewayShadeTime: + description: Gateway Shade Time Information + content: + application/json: + schema: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + setSunTimes: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + successfullyUpdated: + type: array + items: + type: string + failedToUpdate: + type: array + items: + type: string + setCurrentTime: + type: object + properties: + err: + type: number + errMsg: + type: string + example: '' + successfullyUpdated: + type: array + items: + type: string + failedToUpdate: + type: array + items: + type: string + GatewayShadeTimeInfo: + description: Shade Time Sync Manager Info + content: + application/json: + schema: + type: object + properties: + cachedHomeDocInfo: + type: object + properties: + gwId: + type: number + geoPoint: + type: object + properties: + latitude: + type: number + example: 29.9 + longitude: + type: number + example: -100 + tz: + type: string + example: America/Denver + localDstAdjustedOffset: + type: number + nextAllShadesSyncDate: + type: string + lastSuccessfulAllShadesSyncDate: + type: string + nextBleNamesSyncDate: + type: string + bleNamesNeedingSync: + type: array + items: + type: string + delayTimes: + type: object + properties: + homeDocUpdateDelayMs: + type: number + shadesAddedDelayMs: + type: number + syncAllRetryDelayMs: + type: number + syncListRetryDelayMs: + type: number + syncInProgressDelayMs: + type: number + syncInProgress: + type: boolean + HomedocRooms: + description: The rooms in the Home + content: + application/json: + schema: + type: array + items: + properties: + id: + title: Room.id + description: '@deprecated in v23' + type: string + _id: + title: Room._id + type: number + type: + title: Room.type + type: number + name: + title: Room.name + type: string + icon: + title: Room.icon + type: string + color: + title: Room.color + type: string + shades: + items: + properties: + id: + title: Shade.id + description: '@deprecated in v23' + type: string + _id: + title: Shade._id + type: number + name: + title: Shade.name + type: string + v: + title: Shade.v + type: number + index: + title: Shade.index + type: number + serial: + title: Shade.serial + type: string + mac: + title: Shade.mac + type: string + bleName: + title: Shade.bleName + type: string + power: + title: Shade.power + type: number + modelId: + title: Shade.modelId + type: number + required: + - id + - _id + - name + - v + - index + - serial + - mac + - bleName + additionalProperties: false + title: Shade + type: object + title: Room.shades + type: array + required: + - id + - _id + - type + - name + - icon + - color + additionalProperties: false + title: Room + type: object + HomeId: + description: The cloud identifier of the Home + content: + application/json: + schema: + type: object + properties: + id: + type: string + description: The Home identifier + required: + - id + HomeKit: + description: HomeKit information + content: + application/json: + schema: + type: object + properties: + hapBridge: + type: object + properties: + pid: + type: number + connected: + type: boolean + exitCode: + type: string + killed: + type: boolean + started: + type: string + lastStoppedEvent: + type: string + stoppedMessage: + type: string + respawnDisabled: + type: boolean + debugEnabled: + type: boolean + closeEvents: + type: array + items: + type: string + lastAID: + type: number + accessoryMap: + type: object + HomeKitSetup: + description: The HomeKit setup data + content: + application/json: + schema: + type: object + properties: + setupCode: + type: string + example: 383-67-992 + setupPayload: + type: string + example: X-HM://0E38KYF3CHSCC03FBKDP6RPW4CM4J5S + required: + - setupCode + - setupPayload + InfoKnownMissingGateways: + description: Known and missing Gateways + content: + application/json: + schema: + type: object + properties: + known: + type: object + description: Known HomeDoc Gateway identifier + additionalProperties: + type: object + properties: + st: + type: integer + description: >- + The current operating state, which is one of: + 0=STARTUP, 1=IDLE, 2=NEW_GW_NO_NETWORK, 3=NO_INTERNET, + 4=NO_HOME + example: 1 + pri: + type: boolean + description: >- + Optional: true indicates this Gateway is the primary + Gateway. Not set indicates this Gateway is an + auxiliary (aux) Gateway + self: + type: boolean + description: >- + Optional: true indicates this Gateway. Not set + indicates other Gateways in a multi-gateway system + required: + - st + missing: + type: array + description: >- + Gateway identifiers of HomeDoc listed Gateways who have not + reported recently + items: + type: number + description: HomeDoc Gateway identifier + example: 7643 + required: + - known + - missing + LedColor: + description: The LED Color + content: + application/json: + schema: + description: The Gateway LED color and brightness + type: object + properties: + red: + type: number + description: Red + maximum: 0 + minimum: 255 + green: + type: number + description: Green + maximum: 0 + minimum: 255 + blue: + type: number + description: Blue + maximum: 0 + minimum: 255 + brightness: + type: number + description: Brightness + maximum: 0 + minimum: 100 + required: + - red + - green + - blue + - brightness + LogLevel: + description: The Gateway current log level + content: + application/json: + schema: + type: object + properties: + level: + type: string + description: >- + The current root log level, which is one of: error, warn, + info, verbose, debug, or silly. The recommended value is + info. + example: info + transports: + type: array + items: + properties: + level: + type: string + description: >- + The current transport log level, which is one of: + error, warn, info, verbose, debug, or silly. The + recommended value is info. + example: silly + required: + - level + required: + - level + - transports + Network: + description: The Gateway's network information + content: + application/json: + schema: + type: object + properties: + networkConfig: + description: The Gateway's network configuration information + type: object + properties: + staticIpEnabled: + type: boolean + description: >- + Indicates if the below staticIp object information is to + be applied. The below staticIp object is optional if + staticIpEnabled is false + example: true + staticIp: + type: object + description: >- + The optional static IP configuration information. If + staticIpEnabled is true, this object must be present in + its entirety. If staticIpEnabled is false, this object + may be present and if present, it must be fully provided + properties: + ip_address: + type: string + description: The IP address to utilize as the static IP + example: 10.0.0.101 + netwask: + type: string + description: >- + The subnet mask to apply to the static IP address + value + example: 255.255.255.0 + gateway_ip: + type: string + description: The static internet Gateway IP address to utilize + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + networkStatus: + description: The Gateway's network status information + type: object + properties: + ipAddress: + type: string + description: The IP address of the active interface + example: 10.0.0.101 + activeInterface: + type: string + description: >- + The active network interface identifier. Either 'eth0' + (wired) or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: eth0 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: 8.8.8.8 + eth0: + type: object + description: >- + The wired (eth0) network information, which may be empty + if not active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' (wired) + or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' (eth0) + or 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: The network interface's internet Gateway IP address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + wlan0: + type: object + description: >- + The wireless (wlan0) network information, which may be + empty if not active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' (wired) + or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' (eth0) + or 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: The network interface's internet Gateway IP address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + internetState: + type: string + description: The IP network's internet connectivity state + enum: + - Connected + - Disconnected + example: Connected + ssid: + type: string + description: >- + The wireless network's SSID value. The value is an empty + string if not configured + example: HunterDouglasWifi + required: + - ipAddress + - activeInterface + - dns + - eth0 + - wlan0 + - internetState + - ssid + required: + - networkConfig + - networkStatus + NetworkConfig: + description: The Gateway's current network configuration information + content: + application/json: + schema: + type: object + properties: + networkConfig: + description: The Gateway's network configuration information + type: object + properties: + staticIpEnabled: + type: boolean + description: >- + Indicates if the below staticIp object information is to + be applied. The below staticIp object is optional if + staticIpEnabled is false + example: true + staticIp: + type: object + description: >- + The optional static IP configuration information. If + staticIpEnabled is true, this object must be present in + its entirety. If staticIpEnabled is false, this object + may be present and if present, it must be fully provided + properties: + ip_address: + type: string + description: The IP address to utilize as the static IP + example: 10.0.0.101 + netwask: + type: string + description: >- + The subnet mask to apply to the static IP address + value + example: 255.255.255.0 + gateway_ip: + type: string + description: The static internet Gateway IP address to utilize + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + linuxStaticIpValues: + description: >- + The Gateway operating system's configured static IP values + These values should match the networkConfig staticIp + information + type: object + properties: + staticIpEnabled: + type: boolean + description: Indicates if the staticIp is enabled + example: true + interface: + type: string + description: >- + The network interface currently configured with the + static IP values + example: wlan0 + ip_address: + type: string + description: The IP address being utilized as the static IP + example: 10.0.0.101 + netwask: + type: string + description: The subnet mask applied to the static IP address value + example: 255.255.255.0 + gateway_ip: + type: string + description: The static internet Gateway IP address being utilized + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + required: + - networkConfig + NetworkStatus: + description: The Gateway's current network status information + content: + application/json: + schema: + type: object + properties: + networkStatus: + description: The Gateway's network status information + type: object + properties: + ipAddress: + type: string + description: The IP address of the active interface + example: 10.0.0.101 + activeInterface: + type: string + description: >- + The active network interface identifier. Either 'eth0' + (wired) or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: eth0 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: 8.8.8.8 + eth0: + type: object + description: >- + The wired (eth0) network information, which may be empty + if not active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' (wired) + or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' (eth0) + or 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: The network interface's internet Gateway IP address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + wlan0: + type: object + description: >- + The wireless (wlan0) network information, which may be + empty if not active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' (wired) + or 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' (eth0) + or 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: The network interface's internet Gateway IP address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + internetState: + type: string + description: The IP network's internet connectivity state + enum: + - Connected + - Disconnected + example: Connected + ssid: + type: string + description: >- + The wireless network's SSID value. The value is an empty + string if not configured + example: HunterDouglasWifi + required: + - ipAddress + - activeInterface + - dns + - eth0 + - wlan0 + - internetState + - ssid + required: + - networkStatus + Scene: + description: Describes a scene + content: + application/json: + schema: + properties: + id: + title: Scene.id + description: '@deprecated in v23' + type: string + _id: + title: Scene._id + type: number + name: + title: Scene.name + type: string + icon: + title: Scene.icon + type: string + color: + title: Scene.color + type: string + bleId: + title: Scene.bleId + type: number + modDate: + title: Scene.modDate + type: string + roomId: + title: Scene.roomId + description: '@deprecated in v23' + type: string + room_Id: + title: Scene.room_Id + type: number + members: + items: + properties: + id: + title: SceneMember.id + description: '@deprecated in v23' + type: string + _id: + title: SceneMember._id + type: number + pos: + title: SceneMember.pos + properties: + pos1: + title: ShadePosition.pos1 + type: number + pos2: + title: ShadePosition.pos2 + type: number + pos3: + title: ShadePosition.pos3 + type: number + tilt: + title: ShadePosition.tilt + type: number + vel: + title: ShadePosition.vel + type: number + required: + - pos1 + - pos2 + - pos3 + - tilt + - vel + additionalProperties: false + type: object + shdId: + title: SceneMember.shdId + description: '@deprecated in v23' + type: string + shd_Id: + title: SceneMember.shd_Id + type: number + required: + - id + - _id + - pos + - shdId + - shd_Id + additionalProperties: false + title: SceneMember + type: object + title: Scene.members + type: array + required: + - id + - _id + - name + - icon + - color + - bleId + - modDate + additionalProperties: false + title: Scene + type: object + SceneEvent: + description: Scene event + content: + text/event-stream: + schema: + type: object + description: Scene event + properties: + evt: + type: string + description: Event name + enum: + - scene-add + - scene-del + - scene-activated + - scene-deactivated + example: scene-activated + isoDate: + type: string + description: ISO timestamp + example: '2021-12-06T20:01:11.934Z' + id: + type: number + description: Unique Scene identifier + example: 55 + scene: + type: object + description: Scene information + properties: + id: + type: number + description: Unique numerical Scene identifier + example: 234 + name: + type: string + description: Base64 encoded Scene name + example: T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo= + ptName: + type: string + description: The Scene name + example: Open All Office Shades + color: + type: string + description: > + Unique identifier for a Scene color from HomeDoc Scene + object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 669 + networkNumber: + type: number + description: Numerical network identifier + example: 7383 + roomIds: + type: array + description: >- + List of unique numerical Room identifiers associated + with this Scene + example: + - 55 + - 66 + items: + type: number + required: + - id + - name + - ptName + - color + - icon + - networkNumber + - roomIds + required: + - evt + - isoDate + - id + SceneShadeList: + description: List of Shade identifiers associated with a Scene + content: + application/json: + schema: + type: array + description: Array of numerical Shade identifiers + example: + - 22 + - 33 + - 44 + items: + type: number + ShadeEvent: + description: Shade event + content: + text/event-stream: + schema: + type: object + description: Shade event + properties: + evt: + type: string + description: Event name + enum: + - shade-offline + - shade-online + - motion-started + - motion-stopped + - battery-alert + example: motion-started + isoDate: + type: string + description: ISO timestamp + example: '2021-12-06T20:01:11.934Z' + bleName: + type: string + description: Shade name + example: PIR:1233 + id: + type: number + description: Unique Shade identifier + example: 55 + currentPositions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + targetPositions: + type: object + description: > + Optional target Shade position values and estimate + completion time + + that may only present when the Event name is + 'motion-started'. + + The property may be omitted even with the Event + 'motion-started' + + if the values cannot be obtained or computed + properties: + etaInSeconds: + type: number + description: >- + Estimated time in seconds for the Shade to reach its + target positions + example: 7 + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + required: + - evt + - isoDate + - bleName + - id + - currentPositions + EventListeners: + description: List of Event Listeners + content: + application/json: + schema: + type: object + properties: + slots: + type: array + items: + type: string + activeListeners: + type: array + items: + type: string + required: + - slots + - activeListeners + ShadeIDList: + description: List of Shade identifiers + content: + application/json: + schema: + type: array + items: + properties: + id: + title: Shade.id + description: '@deprecated in v23' + type: string + _id: + title: Shade._id + type: number + name: + title: Shade.name + type: string + v: + title: Shade.v + type: number + index: + title: Shade.index + type: number + serial: + title: Shade.serial + type: string + mac: + title: Shade.mac + type: string + bleName: + title: Shade.bleName + type: string + power: + title: Shade.power + type: number + modelId: + title: Shade.modelId + type: number + required: + - id + - _id + - name + - v + - index + - serial + - mac + - bleName + additionalProperties: false + title: Shade + type: object + SpsChannelsCheckResponse: + description: >- + Comms channels that are offline, and an empty array means all + operational + content: + application/json: + schema: + type: object + properties: + failedChannels: + type: array + items: + type: string + description: name of downed channel + example: comms01 + required: + - failedChannels + SpsPcaVersions: + description: List of SPS PV+ Shade PCA versions + content: + application/json: + schema: + type: array + description: List of channels containing arrays of PV+ Shades + items: + type: array + description: PCA information for a list of PV+ Shades on a single channel + items: + type: object + description: SPS PCA version information + properties: + slot: + type: number + description: Shade port/channel number between 0 and 2 inclusive + example: 1 + bleName: + type: string + description: Shade's BleName + example: SON:1234 + mac: + type: string + description: Shade's MAC address + example: '00:12:34:56:78:90' + pcaVersion: + type: string + description: Shade PCA version + example: 0.5.0 + pcaChecksum: + type: string + description: Shade PCA release checksum + example: '0x54321ABC' + pcaUpdateNeeded: + type: boolean + description: >- + The Shade PCA version is mismatched with the version in + the release. Time to do a upgrade + example: false + required: + - slot + - bleName + - mac + - pcaVersion + - pcaChecksum + - pcaUpdateNeeded + SpsSetLogLevelResponse: + description: Comms set log level response + content: + application/json: + schema: + type: object + properties: + responseIsOk: + type: boolean + description: The log level set was successful or not + returnedLogLevel: + type: number + description: >- + The new log level. 0=none, 1=error, 2=warn, 3=info, 4=debug, + 5=silly + returnedPersist: + type: boolean + description: The new persistence setting + required: + - responseIsOk + - returnedLogLevel + - returnedPersist + SpsShadeComparisonResults: + description: HomeDoc vs actual PV+ Shade comparison results + content: + application/json: + schema: + type: object + properties: + perfectMatch: + type: boolean + description: Indicates if the HomeDoc and the SPS are perfectly matched + differences: + type: object + spsHomeDocShades: + type: array + items: + type: object + description: Unique Shade information + properties: + bleName: + type: string + description: Shade BleName + example: SON:1234 + required: + - bleName + spsActualShades: + type: array + items: + type: object + description: Unique Shade information + properties: + bleName: + type: string + description: Shade BleName + example: SON:1234 + required: + - bleName + required: + - perfectMatch + - differences + - spsHomeDocShades + - spsActualShades + SpsShadesEnumeration: + description: PV+ Shades found on the SPS + content: + application/json: + schema: + type: array + items: + type: object + description: SPS port object + properties: + port: + type: number + description: Port/channel number from 1 to 16 inclusive + example: 1 + shades: + type: array + description: Information about PV+ Shades on this port + items: + type: object + description: SPS Shade description + properties: + slot: + type: number + description: Shade port/channel number between 0 and 2 inclusive + example: 1 + rssi: + type: number + description: Perfect RSSI value + example: -1 + ready: + type: number + description: Shade is ready + example: 1 + bleName: + type: string + description: Shade's BleName + example: SON:1234 + mac: + type: string + description: Shade's MAC address + example: '00:12:34:56:78:90' + pcaVersion: + type: string + description: Shade PCA version + example: 0.5.0 + pcaChecksum: + type: string + description: Shade PCA release checksum + example: '0x54321ABC' + pcaUpdateNeeded: + type: boolean + description: >- + The Shade PCA version is mismatched with the version + in the release. Time to do a upgrade + example: false + required: + - slot + - rssi + - ready + - bleName + - mac + - pcaVersion + - pcaChecksum + - pcaUpdateNeeded + required: + - port + - shades + Standard200: + description: The request was successfully completed + content: + application/json: + schema: + type: object + properties: + msg: + type: string + required: + - msg + Standard400: + description: The request parameters were incorrect + content: + application/json: + schema: + type: object + properties: + errMsg: + type: string + description: Describes which parameters were incorrect + required: + - errMsg + Standard404: + description: The resource was not found + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + Standard503: + description: The resource is currently unavailable (i.e., no HomeDoc) + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + StandardGeneralError: + description: General unexpected error response + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + StandardUnimplemented: + description: The resource has not been implemented + content: + application/json: + schema: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + UpdateChannel: + description: The current Gateway firmware update channel + content: + application/json: + schema: + type: object + properties: + channel: + type: string + description: The current Gateway firmware update channel + example: production + required: + - channel + WebAutomation: + description: Web Automation Object + content: + application/json: + schema: + type: object + description: Web Automation Object + properties: + id: + type: number + description: Unique numerical Automation identifier + example: 33 + type: + type: number + description: | + Time-base for the automation + * 0 = Clock-based + * 2 = Before sunrise + * 6 = Before sunset + * 10 = After sunrise + * 14 = After sunset + example: 6 + enabled: + type: boolean + description: True indicates the Automation is enabled + example: true + days: + type: number + description: | + Bitmask of days when the automation is to run + * 0x00 = Reserved + * 0x01 = Monday + * 0x02 = Tuesday + * 0x04 = Wednesday + * 0x08 = Thursay + * 0x10 = Friday + * 0x20 = Saturday + * 0x40 = Sunday + * 0x80 = Reserved + example: 24 (i.e., 0x18, which is Thursday and Friday) + hour: + type: number + description: | + For clock-based automations, the 'hour' field is the + exact hour the automation will occur. For sunrise & sunset + based automations, the 'hour' field is the hour offset + before/after a sunrise/sunset automation is to run. + example: 4 + min: + type: number + description: | + For clock-based automations, the 'min' field is the + exact min the automation will occur. For sunrise & sunset + based automations, the 'min' field is the minutes offset + before/after a sunrise/sunset automation is to run. + example: 5 + bleId: + type: number + description: The automation ID stored on the Shade itself. + example: 83 + sceneId: + type: number + description: >- + The unique numerical Scene identifier associated with this + Automation + example: 116 + errorShd_Ids: + type: array + description: >- + The Shade list where the Automation was unsuccessfully + provisioned. The value can be null + example: null + items: + type: number + description: The failing Shade identifier + example: 55 + required: + - id + - type + - enabled + - days + - hour + - min + - bleId + - sceneId + WebAutomationList: + description: Web Automation List + content: + application/json: + schema: + type: array + items: + type: object + description: Web Automation Object + properties: + id: + type: number + description: Unique numerical Automation identifier + example: 33 + type: + type: number + description: | + Time-base for the automation + * 0 = Clock-based + * 2 = Before sunrise + * 6 = Before sunset + * 10 = After sunrise + * 14 = After sunset + example: 6 + enabled: + type: boolean + description: True indicates the Automation is enabled + example: true + days: + type: number + description: | + Bitmask of days when the automation is to run + * 0x00 = Reserved + * 0x01 = Monday + * 0x02 = Tuesday + * 0x04 = Wednesday + * 0x08 = Thursay + * 0x10 = Friday + * 0x20 = Saturday + * 0x40 = Sunday + * 0x80 = Reserved + example: 24 (i.e., 0x18, which is Thursday and Friday) + hour: + type: number + description: > + For clock-based automations, the 'hour' field is the + + exact hour the automation will occur. For sunrise & + sunset + + based automations, the 'hour' field is the hour offset + + before/after a sunrise/sunset automation is to run. + example: 4 + min: + type: number + description: | + For clock-based automations, the 'min' field is the + exact min the automation will occur. For sunrise & sunset + based automations, the 'min' field is the minutes offset + before/after a sunrise/sunset automation is to run. + example: 5 + bleId: + type: number + description: The automation ID stored on the Shade itself. + example: 83 + sceneId: + type: number + description: >- + The unique numerical Scene identifier associated with this + Automation + example: 116 + errorShd_Ids: + type: array + description: >- + The Shade list where the Automation was unsuccessfully + provisioned. The value can be null + example: null + items: + type: number + description: The failing Shade identifier + example: 55 + required: + - id + - type + - enabled + - days + - hour + - min + - bleId + - sceneId + WebHomeDoc: + description: Web HomeDoc Object + content: + application/json: + schema: + type: object + properties: + id: + type: string + _schemaVersion: + type: number + owner: + type: string + users?: + type: array + items: + type: string + modApp: + type: string + modDate: + type: string + home: + properties: + name: + title: Home.name + type: string + autosEnabled: + title: Home.autosEnabled + description: Are automations enabled globally + type: boolean + key: + title: Home.key + type: string + loc: + title: Home.loc + power: + title: Home.power + type: number + tz: + title: Home.tz + description: TimeZone - available in v24+ + type: string + required: + - name + - autosEnabled + additionalProperties: false + title: Home + type: object + rooms: + type: array + items: + type: object + properties: + id: + type: string + _id: + type: number + type: + type: number + name: + type: string + icon: + type: string + color: + type: string + shades: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter + Douglas use only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. + In PowerView Gen 3, + + there are over 20 different types of Hunter + Douglas Shades available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, all + can be represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either + from the bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot + operate independently - the + + front shade must be down before the rear shade can + deploy. In other cases, + + they are independent with two motors and two + tubes. Where they are + + dependent, the shade firmware will force the + appropriate front shade + + position when the rear shade is controlled - there + is no need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up + shade that also tilts plus a rear blackout + (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully open + position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully open + position of the secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the fully closed to fully open + tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 + representing the Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + scenes: + type: array + items: + properties: + id: + title: Scene.id + description: '@deprecated in v23' + type: string + _id: + title: Scene._id + type: number + name: + title: Scene.name + type: string + icon: + title: Scene.icon + type: string + color: + title: Scene.color + type: string + bleId: + title: Scene.bleId + type: number + modDate: + title: Scene.modDate + type: string + roomId: + title: Scene.roomId + description: '@deprecated in v23' + type: string + room_Id: + title: Scene.room_Id + type: number + members: + items: + properties: + id: + title: SceneMember.id + description: '@deprecated in v23' + type: string + _id: + title: SceneMember._id + type: number + pos: + title: SceneMember.pos + properties: + pos1: + title: ShadePosition.pos1 + type: number + pos2: + title: ShadePosition.pos2 + type: number + pos3: + title: ShadePosition.pos3 + type: number + tilt: + title: ShadePosition.tilt + type: number + vel: + title: ShadePosition.vel + type: number + required: + - pos1 + - pos2 + - pos3 + - tilt + - vel + additionalProperties: false + type: object + shdId: + title: SceneMember.shdId + description: '@deprecated in v23' + type: string + shd_Id: + title: SceneMember.shd_Id + type: number + required: + - id + - _id + - pos + - shdId + - shd_Id + additionalProperties: false + title: SceneMember + type: object + title: Scene.members + type: array + required: + - id + - _id + - name + - icon + - color + - bleId + - modDate + additionalProperties: false + title: Scene + type: object + automations: + type: array + items: + properties: + id: + title: Automation.id + description: '@deprecated in v23' + type: string + _id: + title: Automation._id + type: number + type: + title: Automation.type + type: number + enabled: + title: Automation.enabled + type: boolean + days: + title: Automation.days + type: number + hour: + title: Automation.hour + type: number + min: + title: Automation.min + type: number + bleId: + title: Automation.bleId + type: number + sceneId: + title: Automation.sceneId + description: '@deprecated in v23' + type: string + scene_Id: + title: Automation.scene_Id + type: number + errorShdIds: + items: + type: string + title: Automation.errorShdIds + description: '@deprecated in v23' + type: array + errorShd_Ids: + items: + type: number + title: Automation.errorShd_Ids + type: array + required: + - id + - _id + - type + - enabled + - days + - hour + - min + - bleId + - sceneId + - scene_Id + - errorShdIds + - errorShd_Ids + additionalProperties: false + title: Automation + type: object + remotes?: + type: array + items: + properties: + id: + title: Remote.id + description: '@deprecated in v23' + type: string + _id: + title: Remote._id + type: number + name: + title: Remote.name + type: string + v: + title: Remote.v + type: number + mac: + title: Remote.mac + type: string + bleName: + title: Remote.bleName + type: string + members: + items: + properties: + id: + title: RemoteMember.id + description: '@deprecated in v23' + type: string + _id: + title: RemoteMember._id + type: number + group: + title: RemoteMember.group + type: number + shdId: + title: RemoteMember.shdId + description: '@deprecated in v23' + type: string + shd_Id: + title: RemoteMember.shd_Id + type: number + required: + - id + - _id + - group + - shdId + - shd_Id + additionalProperties: false + title: RemoteMember + type: object + title: Remote.members + type: array + required: + - id + - _id + - name + - v + - mac + - bleName + additionalProperties: false + title: Remote + type: object + gateways: + type: array + items: + properties: + id: + title: Gateway.id + description: '@deprecated in v23' + type: string + _id: + title: Gateway._id + type: number + name: + title: Gateway.name + type: string + v: + title: Gateway.v + type: string + radio1V: + title: Gateway.radio1V + type: string + radio2V: + title: Gateway.radio2V + type: string + serial: + title: Gateway.serial + type: string + aux: + title: Gateway.aux + type: boolean + ip: + title: Gateway.ip + type: string + ssid: + title: Gateway.ssid + type: string + shdIds: + items: + type: string + title: Gateway.shdIds + description: '@deprecated in v23' + type: array + shd_Ids: + items: + type: number + title: Gateway.shd_Ids + type: array + required: + - id + - _id + - name + - v + - radio1V + - radio2V + - serial + - ip + - ssid + additionalProperties: false + title: Gateway + type: object + WebRoom: + description: Web Room Object + content: + application/json: + schema: + type: object + description: Room information + properties: + id: + type: number + description: Unique numerical Room identifier + example: 674 + name: + type: string + description: Base64 encoded Room name + example: T2ZmaWNlCg== + ptName: + type: string + description: Room name + example: Office + color: + type: string + description: | + Unique identifier for a Room color from HomeDoc Room object + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Room object + + Note: The numeric value is supplied as a string + example: 669 + type: + type: number + description: Room type enumeration value + example: 3 + shades: + type: array + description: >- + Optional list of Shades in a Room, which can be an empty + list + example: [] + items: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter Douglas + use only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. In + PowerView Gen 3, + + there are over 20 different types of Hunter Douglas + Shades available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, all can be + represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from + the bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot operate + independently - the + + front shade must be down before the rear shade can + deploy. In other cases, + + they are independent with two motors and two tubes. + Where they are + + dependent, the shade firmware will force the + appropriate front shade + + position when the rear shade is controlled - there is + no need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up + shade that also tilts plus a rear blackout + (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the fully closed to fully open position of the + primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the fully closed to fully open position of the + secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the fully closed to fully open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + required: + - id + - name + - ptName + - color + - icon + - type + WebRoomList: + description: Web Room List + content: + application/json: + schema: + type: array + items: + type: object + description: Room information + properties: + id: + type: number + description: Unique numerical Room identifier + example: 674 + name: + type: string + description: Base64 encoded Room name + example: T2ZmaWNlCg== + ptName: + type: string + description: Room name + example: Office + color: + type: string + description: > + Unique identifier for a Room color from HomeDoc Room + object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Room object + + Note: The numeric value is supplied as a string + example: 669 + type: + type: number + description: Room type enumeration value + example: 3 + shades: + type: array + description: >- + Optional list of Shades in a Room, which can be an empty + list + example: [] + items: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter Douglas + use only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. + In PowerView Gen 3, + + there are over 20 different types of Hunter Douglas + Shades available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, all can + be represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from + the bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot + operate independently - the + + front shade must be down before the rear shade can + deploy. In other cases, + + they are independent with two motors and two tubes. + Where they are + + dependent, the shade firmware will force the + appropriate front shade + + position when the rear shade is controlled - there + is no need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up + shade that also tilts plus a rear blackout + (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the fully closed to fully open position of the + primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the fully closed to fully open position of the + secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the fully closed to fully open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing + the Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + required: + - id + - name + - ptName + - color + - icon + - type + WebScene: + description: Web Scene Object + content: + application/json: + schema: + type: object + description: Scene information + properties: + id: + type: number + description: Unique numerical Scene identifier + example: 234 + name: + type: string + description: Base64 encoded Scene name + example: T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo= + ptName: + type: string + description: The Scene name + example: Open All Office Shades + color: + type: string + description: > + Unique identifier for a Scene color from HomeDoc Scene + object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 669 + networkNumber: + type: number + description: Numerical network identifier + example: 7383 + roomIds: + type: array + description: >- + List of unique numerical Room identifiers associated with + this Scene + example: + - 55 + - 66 + items: + type: number + required: + - id + - name + - ptName + - color + - icon + - networkNumber + - roomIds + WebSceneList: + description: Web Scene List + content: + application/json: + schema: + type: array + items: + type: object + description: Scene information + properties: + id: + type: number + description: Unique numerical Scene identifier + example: 234 + name: + type: string + description: Base64 encoded Scene name + example: T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo= + ptName: + type: string + description: The Scene name + example: Open All Office Shades + color: + type: string + description: > + Unique identifier for a Scene color from HomeDoc Scene + object + + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 669 + networkNumber: + type: number + description: Numerical network identifier + example: 7383 + roomIds: + type: array + description: >- + List of unique numerical Room identifiers associated with + this Scene + example: + - 55 + - 66 + items: + type: number + required: + - id + - name + - ptName + - color + - icon + - networkNumber + - roomIds + WebShade: + description: Web Shade Object + content: + application/json: + schema: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter Douglas use + only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. In + PowerView Gen 3, + + there are over 20 different types of Hunter Douglas Shades + available. + + These Shade have a variety of different motion capabilities. + While each + + Shade has its own set of unique properties, all can be + represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from the + bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot operate + independently - the + + front shade must be down before the rear shade can deploy. + In other cases, + + they are independent with two motors and two tubes. Where + they are + + dependent, the shade firmware will force the appropriate + front shade + + position when the rear shade is controlled - there is no + need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up shade that + also tilts plus a rear blackout (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + WebShadeList: + description: Web Shade List + content: + application/json: + schema: + type: array + items: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter Douglas use + only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. In + PowerView Gen 3, + + there are over 20 different types of Hunter Douglas Shades + available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, all can be + represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from the + bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot operate + independently - the + + front shade must be down before the rear shade can deploy. + In other cases, + + they are independent with two motors and two tubes. Where + they are + + dependent, the shade firmware will force the appropriate + front shade + + position when the rear shade is controlled - there is no + need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up shade + that also tilts plus a rear blackout (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary + rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + WiFiScanList: + description: Wireless network scan list + content: + application/json: + schema: + type: object + properties: + ssidScanResults: + type: array + items: + type: object + description: Wireless network scan entry + properties: + ssid: + type: string + description: Wireless network name + example: HunterDouglasWiFi + rssi: + type: string + description: Wireless network RSSI value + example: '-81' + required: + - ssid + - rssi + required: + - ssidScanResults + schemas: + BatteryLevel: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + CloudConfig: + type: object + description: >- + Cloud configuration information used by the Gateway to access the + Cloud + properties: + homeId: + type: string + description: Home identifier as a hexadecimal string + example: Xffeg05RhmhUQez88Qyv + gatewayId: + type: number + description: Numerical identifier of the Gateway as registered in the Cloud + example: 103 + collection: + type: string + description: Name of the Home collection + default: homes + example: homes + clouddEnv: + type: number + description: >- + Select the Cloud environment to connect to: Dev = 0, Production = + 1 + default: 0 + example: 1 + required: + - homeId + - gatewayId + ComparableShade: + type: object + description: Unique Shade information + properties: + bleName: + type: string + description: Shade BleName + example: SON:1234 + required: + - bleName + Error: + type: object + description: Error response + properties: + errMsg: + type: string + description: Error description + required: + - errMsg + Hexadecimal: + type: object + description: Shade command payload + properties: + hex: + type: string + required: + - hex + LedColor: + description: The Gateway LED color and brightness + type: object + properties: + red: + type: number + description: Red + maximum: 0 + minimum: 255 + green: + type: number + description: Green + maximum: 0 + minimum: 255 + blue: + type: number + description: Blue + maximum: 0 + minimum: 255 + brightness: + type: number + description: Brightness + maximum: 0 + minimum: 100 + required: + - red + - green + - blue + - brightness + LinuxStaticIp: + description: >- + The Gateway operating system's configured static IP values These + values should match the networkConfig staticIp information + type: object + properties: + staticIpEnabled: + type: boolean + description: Indicates if the staticIp is enabled + example: true + interface: + type: string + description: >- + The network interface currently configured with the static IP + values + example: wlan0 + ip_address: + type: string + description: The IP address being utilized as the static IP + example: 10.0.0.101 + netwask: + type: string + description: The subnet mask applied to the static IP address value + example: 255.255.255.0 + gateway_ip: + type: string + description: The static internet Gateway IP address being utilized + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + NetworkConfig: + description: The Gateway's network configuration information + type: object + properties: + staticIpEnabled: + type: boolean + description: >- + Indicates if the below staticIp object information is to be + applied. The below staticIp object is optional if staticIpEnabled + is false + example: true + staticIp: + type: object + description: >- + The optional static IP configuration information. If + staticIpEnabled is true, this object must be present in its + entirety. If staticIpEnabled is false, this object may be present + and if present, it must be fully provided + properties: + ip_address: + type: string + description: The IP address to utilize as the static IP + example: 10.0.0.101 + netwask: + type: string + description: The subnet mask to apply to the static IP address value + example: 255.255.255.0 + gateway_ip: + type: string + description: The static internet Gateway IP address to utilize + example: 10.0.0.1 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: + - 8.8.8.8 + - 9.9.9.9 + required: + - staticIpEnabled + NetworkStatus: + description: The Gateway's network status information + type: object + properties: + ipAddress: + type: string + description: The IP address of the active interface + example: 10.0.0.101 + activeInterface: + type: string + description: >- + The active network interface identifier. Either 'eth0' (wired) or + 'wlan0' (wireless) + enum: + - eth0 + - wlan0 + example: eth0 + dns: + type: array + items: + type: string + description: The list of DNS server IP addresses + example: 8.8.8.8 + eth0: + type: object + description: >- + The wired (eth0) network information, which may be empty if not + active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' (wired) or 'wlan0' + (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' (eth0) or + 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: The network interface's internet Gateway IP address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + wlan0: + type: object + description: >- + The wireless (wlan0) network information, which may be empty if + not active/configured + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' (wired) or 'wlan0' + (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' (eth0) or + 'Wireless' (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: The network interface's internet Gateway IP address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + internetState: + type: string + description: The IP network's internet connectivity state + enum: + - Connected + - Disconnected + example: Connected + ssid: + type: string + description: >- + The wireless network's SSID value. The value is an empty string if + not configured + example: HunterDouglasWifi + required: + - ipAddress + - activeInterface + - dns + - eth0 + - wlan0 + - internetState + - ssid + NICType: + description: The Gateway's network interface information + type: object + properties: + name: + type: string + description: >- + The network interface's name either 'eth0' (wired) or 'wlan0' + (wireless) + enum: + - eth0 + - wlan0 + example: wlan0 + type: + type: string + description: >- + The network interface's type either 'Wired' (eth0) or 'Wireless' + (wlan0) + enum: + - Wired + - Wireless + example: Wireless + ip_address: + type: string + description: The network interface's IP address + example: 10.0.0.101 + mac_address: + type: string + description: The network interface's MAC address + example: 00:26:74:be:ef:66 + gateway_ip: + type: string + description: The network interface's internet Gateway IP address + example: 10.0.0.1 + netwask: + type: string + description: The network interface's subnet mask + example: 255.255.255.0 + AutomationId: + type: string + description: The numerical identifier for a particular Automation + example: 456 + PositionsPrimary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully closed to + fully open position of the primary rail + example: 0.99 + PositionsSecondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully closed to + fully open position of the secondary rail + example: 0.99 + PositionsTilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully closed to + fully open tilt position + example: 0 + PositionsVelocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the Shade operation + speed + example: 0.5 + PowerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + RoomId: + type: string + description: The numerical identifier for a particular Room + example: 2 + SceneEvent: + type: object + description: Scene event + properties: + evt: + type: string + description: Event name + enum: + - scene-add + - scene-del + - scene-activated + - scene-deactivated + example: scene-activated + isoDate: + type: string + description: ISO timestamp + example: '2021-12-06T20:01:11.934Z' + id: + type: number + description: Unique Scene identifier + example: 55 + scene: + type: object + description: Scene information + properties: + id: + type: number + description: Unique numerical Scene identifier + example: 234 + name: + type: string + description: Base64 encoded Scene name + example: T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo= + ptName: + type: string + description: The Scene name + example: Open All Office Shades + color: + type: string + description: | + Unique identifier for a Scene color from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 669 + networkNumber: + type: number + description: Numerical network identifier + example: 7383 + roomIds: + type: array + description: >- + List of unique numerical Room identifiers associated with this + Scene + example: + - 55 + - 66 + items: + type: number + required: + - id + - name + - ptName + - color + - icon + - networkNumber + - roomIds + required: + - evt + - isoDate + - id + SceneId: + type: string + description: The numerical identifier for a particular Scene + example: 22 + ShadeEvent: + type: object + description: Shade event + properties: + evt: + type: string + description: Event name + enum: + - shade-offline + - shade-online + - motion-started + - motion-stopped + - battery-alert + example: motion-started + isoDate: + type: string + description: ISO timestamp + example: '2021-12-06T20:01:11.934Z' + bleName: + type: string + description: Shade name + example: PIR:1233 + id: + type: number + description: Unique Shade identifier + example: 55 + currentPositions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully + closed to fully open position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully + closed to fully open position of the secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully + closed to fully open tilt position + example: 0 + targetPositions: + type: object + description: | + Optional target Shade position values and estimate completion time + that may only present when the Event name is 'motion-started'. + The property may be omitted even with the Event 'motion-started' + if the values cannot be obtained or computed + properties: + etaInSeconds: + type: number + description: >- + Estimated time in seconds for the Shade to reach its target + positions + example: 7 + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully + closed to fully open position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully + closed to fully open position of the secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully + closed to fully open tilt position + example: 0 + required: + - evt + - isoDate + - bleName + - id + - currentPositions + ShadeId: + type: string + description: The numerical identifier for a particular Shade + example: 21 + ShadeCapabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. In PowerView Gen + 3, + + there are over 20 different types of Hunter Douglas Shades available. + + These Shade have a variety of different motion capabilities. While + each + + Shade has its own set of unique properties, all can be represented by + the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from the bottom or from + the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot operate independently - + the + + front shade must be down before the rear shade can deploy. In other + cases, + + they are independent with two motors and two tubes. Where they are + + dependent, the shade firmware will force the appropriate front shade + + position when the rear shade is controlled - there is no need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up shade that also + tilts plus a rear blackout (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + SpsPcaVersionInfo: + type: object + description: SPS PCA version information + properties: + slot: + type: number + description: Shade port/channel number between 0 and 2 inclusive + example: 1 + bleName: + type: string + description: Shade's BleName + example: SON:1234 + mac: + type: string + description: Shade's MAC address + example: '00:12:34:56:78:90' + pcaVersion: + type: string + description: Shade PCA version + example: 0.5.0 + pcaChecksum: + type: string + description: Shade PCA release checksum + example: '0x54321ABC' + pcaUpdateNeeded: + type: boolean + description: >- + The Shade PCA version is mismatched with the version in the + release. Time to do a upgrade + example: false + required: + - slot + - bleName + - mac + - pcaVersion + - pcaChecksum + - pcaUpdateNeeded + SpsPort: + type: object + description: SPS port object + properties: + port: + type: number + description: Port/channel number from 1 to 16 inclusive + example: 1 + shades: + type: array + description: Information about PV+ Shades on this port + items: + type: object + description: SPS Shade description + properties: + slot: + type: number + description: Shade port/channel number between 0 and 2 inclusive + example: 1 + rssi: + type: number + description: Perfect RSSI value + example: -1 + ready: + type: number + description: Shade is ready + example: 1 + bleName: + type: string + description: Shade's BleName + example: SON:1234 + mac: + type: string + description: Shade's MAC address + example: '00:12:34:56:78:90' + pcaVersion: + type: string + description: Shade PCA version + example: 0.5.0 + pcaChecksum: + type: string + description: Shade PCA release checksum + example: '0x54321ABC' + pcaUpdateNeeded: + type: boolean + description: >- + The Shade PCA version is mismatched with the version in the + release. Time to do a upgrade + example: false + required: + - slot + - rssi + - ready + - bleName + - mac + - pcaVersion + - pcaChecksum + - pcaUpdateNeeded + required: + - port + - shades + SpsShadeEntry: + type: object + description: SPS Shade description + properties: + slot: + type: number + description: Shade port/channel number between 0 and 2 inclusive + example: 1 + rssi: + type: number + description: Perfect RSSI value + example: -1 + ready: + type: number + description: Shade is ready + example: 1 + bleName: + type: string + description: Shade's BleName + example: SON:1234 + mac: + type: string + description: Shade's MAC address + example: '00:12:34:56:78:90' + pcaVersion: + type: string + description: Shade PCA version + example: 0.5.0 + pcaChecksum: + type: string + description: Shade PCA release checksum + example: '0x54321ABC' + pcaUpdateNeeded: + type: boolean + description: >- + The Shade PCA version is mismatched with the version in the + release. Time to do a upgrade + example: false + required: + - slot + - rssi + - ready + - bleName + - mac + - pcaVersion + - pcaChecksum + - pcaUpdateNeeded + WebAutomation: + type: object + description: Web Automation Object + properties: + id: + type: number + description: Unique numerical Automation identifier + example: 33 + type: + type: number + description: | + Time-base for the automation + * 0 = Clock-based + * 2 = Before sunrise + * 6 = Before sunset + * 10 = After sunrise + * 14 = After sunset + example: 6 + enabled: + type: boolean + description: True indicates the Automation is enabled + example: true + days: + type: number + description: | + Bitmask of days when the automation is to run + * 0x00 = Reserved + * 0x01 = Monday + * 0x02 = Tuesday + * 0x04 = Wednesday + * 0x08 = Thursay + * 0x10 = Friday + * 0x20 = Saturday + * 0x40 = Sunday + * 0x80 = Reserved + example: 24 (i.e., 0x18, which is Thursday and Friday) + hour: + type: number + description: | + For clock-based automations, the 'hour' field is the + exact hour the automation will occur. For sunrise & sunset + based automations, the 'hour' field is the hour offset + before/after a sunrise/sunset automation is to run. + example: 4 + min: + type: number + description: | + For clock-based automations, the 'min' field is the + exact min the automation will occur. For sunrise & sunset + based automations, the 'min' field is the minutes offset + before/after a sunrise/sunset automation is to run. + example: 5 + bleId: + type: number + description: The automation ID stored on the Shade itself. + example: 83 + sceneId: + type: number + description: >- + The unique numerical Scene identifier associated with this + Automation + example: 116 + errorShd_Ids: + type: array + description: >- + The Shade list where the Automation was unsuccessfully + provisioned. The value can be null + example: null + items: + type: number + description: The failing Shade identifier + example: 55 + required: + - id + - type + - enabled + - days + - hour + - min + - bleId + - sceneId + WebHomeDoc: + type: object + properties: + id: + type: string + _schemaVersion: + type: number + owner: + type: string + users?: + type: array + items: + type: string + modApp: + type: string + modDate: + type: string + home: + properties: + name: + title: Home.name + type: string + autosEnabled: + title: Home.autosEnabled + description: Are automations enabled globally + type: boolean + key: + title: Home.key + type: string + loc: + title: Home.loc + power: + title: Home.power + type: number + tz: + title: Home.tz + description: TimeZone - available in v24+ + type: string + required: + - name + - autosEnabled + additionalProperties: false + title: Home + type: object + rooms: + type: array + items: + type: object + properties: + id: + type: string + _id: + type: number + type: + type: number + name: + type: string + icon: + type: string + color: + type: string + shades: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter Douglas use + only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. In + PowerView Gen 3, + + there are over 20 different types of Hunter Douglas + Shades available. + + These Shade have a variety of different motion + capabilities. While each + + Shade has its own set of unique properties, all can be + represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from the + bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot operate + independently - the + + front shade must be down before the rear shade can + deploy. In other cases, + + they are independent with two motors and two tubes. + Where they are + + dependent, the shade firmware will force the appropriate + front shade + + position when the rear shade is controlled - there is no + need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up shade + that also tilts plus a rear blackout (non-tilting) + shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary + rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + scenes: + type: array + items: + properties: + id: + title: Scene.id + description: '@deprecated in v23' + type: string + _id: + title: Scene._id + type: number + name: + title: Scene.name + type: string + icon: + title: Scene.icon + type: string + color: + title: Scene.color + type: string + bleId: + title: Scene.bleId + type: number + modDate: + title: Scene.modDate + type: string + roomId: + title: Scene.roomId + description: '@deprecated in v23' + type: string + room_Id: + title: Scene.room_Id + type: number + members: + items: + properties: + id: + title: SceneMember.id + description: '@deprecated in v23' + type: string + _id: + title: SceneMember._id + type: number + pos: + title: SceneMember.pos + properties: + pos1: + title: ShadePosition.pos1 + type: number + pos2: + title: ShadePosition.pos2 + type: number + pos3: + title: ShadePosition.pos3 + type: number + tilt: + title: ShadePosition.tilt + type: number + vel: + title: ShadePosition.vel + type: number + required: + - pos1 + - pos2 + - pos3 + - tilt + - vel + additionalProperties: false + type: object + shdId: + title: SceneMember.shdId + description: '@deprecated in v23' + type: string + shd_Id: + title: SceneMember.shd_Id + type: number + required: + - id + - _id + - pos + - shdId + - shd_Id + additionalProperties: false + title: SceneMember + type: object + title: Scene.members + type: array + required: + - id + - _id + - name + - icon + - color + - bleId + - modDate + additionalProperties: false + title: Scene + type: object + automations: + type: array + items: + properties: + id: + title: Automation.id + description: '@deprecated in v23' + type: string + _id: + title: Automation._id + type: number + type: + title: Automation.type + type: number + enabled: + title: Automation.enabled + type: boolean + days: + title: Automation.days + type: number + hour: + title: Automation.hour + type: number + min: + title: Automation.min + type: number + bleId: + title: Automation.bleId + type: number + sceneId: + title: Automation.sceneId + description: '@deprecated in v23' + type: string + scene_Id: + title: Automation.scene_Id + type: number + errorShdIds: + items: + type: string + title: Automation.errorShdIds + description: '@deprecated in v23' + type: array + errorShd_Ids: + items: + type: number + title: Automation.errorShd_Ids + type: array + required: + - id + - _id + - type + - enabled + - days + - hour + - min + - bleId + - sceneId + - scene_Id + - errorShdIds + - errorShd_Ids + additionalProperties: false + title: Automation + type: object + remotes?: + type: array + items: + properties: + id: + title: Remote.id + description: '@deprecated in v23' + type: string + _id: + title: Remote._id + type: number + name: + title: Remote.name + type: string + v: + title: Remote.v + type: number + mac: + title: Remote.mac + type: string + bleName: + title: Remote.bleName + type: string + members: + items: + properties: + id: + title: RemoteMember.id + description: '@deprecated in v23' + type: string + _id: + title: RemoteMember._id + type: number + group: + title: RemoteMember.group + type: number + shdId: + title: RemoteMember.shdId + description: '@deprecated in v23' + type: string + shd_Id: + title: RemoteMember.shd_Id + type: number + required: + - id + - _id + - group + - shdId + - shd_Id + additionalProperties: false + title: RemoteMember + type: object + title: Remote.members + type: array + required: + - id + - _id + - name + - v + - mac + - bleName + additionalProperties: false + title: Remote + type: object + gateways: + type: array + items: + properties: + id: + title: Gateway.id + description: '@deprecated in v23' + type: string + _id: + title: Gateway._id + type: number + name: + title: Gateway.name + type: string + v: + title: Gateway.v + type: string + radio1V: + title: Gateway.radio1V + type: string + radio2V: + title: Gateway.radio2V + type: string + serial: + title: Gateway.serial + type: string + aux: + title: Gateway.aux + type: boolean + ip: + title: Gateway.ip + type: string + ssid: + title: Gateway.ssid + type: string + shdIds: + items: + type: string + title: Gateway.shdIds + description: '@deprecated in v23' + type: array + shd_Ids: + items: + type: number + title: Gateway.shd_Ids + type: array + required: + - id + - _id + - name + - v + - radio1V + - radio2V + - serial + - ip + - ssid + additionalProperties: false + title: Gateway + type: object + WebRoom: + type: object + description: Room information + properties: + id: + type: number + description: Unique numerical Room identifier + example: 674 + name: + type: string + description: Base64 encoded Room name + example: T2ZmaWNlCg== + ptName: + type: string + description: Room name + example: Office + color: + type: string + description: | + Unique identifier for a Room color from HomeDoc Room object + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Room object + + Note: The numeric value is supplied as a string + example: 669 + type: + type: number + description: Room type enumeration value + example: 3 + shades: + type: array + description: Optional list of Shades in a Room, which can be an empty list + example: [] + items: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: >- + Hunter Douglas unique 'App Index' for Hunter Douglas use + only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. In + PowerView Gen 3, + + there are over 20 different types of Hunter Douglas Shades + available. + + These Shade have a variety of different motion capabilities. + While each + + Shade has its own set of unique properties, all can be + represented by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from the + bottom or from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot operate + independently - the + + front shade must be down before the rear shade can deploy. + In other cases, + + they are independent with two motors and two tubes. Where + they are + + dependent, the shade firmware will force the appropriate + front shade + + position when the rear shade is controlled - there is no + need for the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up shade that + also tilts plus a rear blackout (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open position of the secondary + rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + fully closed to fully open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the + Shade operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + required: + - id + - name + - ptName + - color + - icon + - type + WebScene: + type: object + description: Scene information + properties: + id: + type: number + description: Unique numerical Scene identifier + example: 234 + name: + type: string + description: Base64 encoded Scene name + example: T3BlbiBBbGwgT2ZmaWNlIFNoYWRlcwo= + ptName: + type: string + description: The Scene name + example: Open All Office Shades + color: + type: string + description: | + Unique identifier for a Scene color from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 37 + icon: + type: string + description: | + Unique identifier for a icon from HomeDoc Scene object + + Note: The numeric value is supplied as a string + example: 669 + networkNumber: + type: number + description: Numerical network identifier + example: 7383 + roomIds: + type: array + description: >- + List of unique numerical Room identifiers associated with this + Scene + example: + - 55 + - 66 + items: + type: number + required: + - id + - name + - ptName + - color + - icon + - networkNumber + - roomIds + WebShade: + type: object + description: Shade information + properties: + id: + type: number + description: Unique numerical Shade identifier + example: 789 + type: + type: number + description: Hunter Douglas unique 'App Index' for Hunter Douglas use only + example: 23 + name: + type: string + description: Base64 encoded Shade name + example: Q2VudGVyCg== + ptName: + type: string + description: Shade label + example: Center + capabilities: + type: number + description: > + Describes the Hunter Douglas Shade's capabilities. In PowerView + Gen 3, + + there are over 20 different types of Hunter Douglas Shades + available. + + These Shade have a variety of different motion capabilities. While + each + + Shade has its own set of unique properties, all can be represented + by the following + + motion type capabilities: + + + * Type 0 - Bottom Up + * Examples: Standard roller/screen shades, Duette bottom up + * Uses the “primary” control type + + * Type 1 - Bottom Up w/ 90° Tilt + * Examples: Silhouette, Pirouette + * Uses the “primary” and “tilt” control types + + * Type 2 - Bottom Up w/ 180° Tilt + * Example: Silhouette Halo + * Uses the “primary” and “tilt” control types + + * Type 3 - Vertical (Traversing) + * Examples: Skyline, Duette Vertiglide, Design Studio Drapery + * Uses the “primary” control type + + * Type 4 - Vertical (Traversing) w/ 180° Tilt + * Example: Luminette + * Uses the “primary” and “tilt” control types + + * Type 5 - Tilt Only 180° + * Examples: Palm Beach Shutters, Parkland Wood Blinds + * Uses the “tilt” control type + + * Type 6 - Top Down + * Example: Duette Top Down + * Uses the “primary” control type + + * Type 7 - Top-Down/Bottom-Up (can open either from the bottom or + from the top) + * Examples: Duette TDBU, Vignette TDBU + * Uses the “primary” and “secondary” control types + + * Type 8 - Duolite (front and rear shades) + * Examples: Roller Duolite, Vignette Duolite, Dual Roller + * Uses the “primary” and “secondary” control types + * Note: In some cases the front and rear shades are controlled by a single + motor and are on a single tube so they cannot operate + independently - the + + front shade must be down before the rear shade can deploy. In + other cases, + + they are independent with two motors and two tubes. Where they are + + dependent, the shade firmware will force the appropriate front + shade + + position when the rear shade is controlled - there is no need for + the + + control system to take this into account. + + + * Type 9 - Duolite with 90° Tilt (front bottom up shade that also + tilts plus a rear blackout (non-tilting) shade) + * Example: Silhouette Duolite, Silhouette Adeux + * Uses the “primary,” “secondary,” and “tilt” control types + * Note: Like with Type 8, these can be either dependent or independent. + + * Type 10 - Duolite with 180° Tilt + * Example: Silhouette Halo Duolite + * Uses the “primary,” “secondary,” and “tilt” control types + example: 0 + powerType: + description: | + Identifies the Shade power source + + * 0 = Battery + * 1 = Hardwired + * 2 = Rechargeable + type: number + example: wand + batteryStatus: + description: Approximate gauge of remaining Shade battery power + type: integer + minimum: 0 + maximum: 3 + example: 2 + roomId: + type: number + description: Unique numerical identifier for a particular Room + example: 6833 + signalStrength: + type: number + description: Shade's average RSSI value + example: -55 + bleName: + type: string + description: Shade name + example: PIR:5933 + firmware: + type: object + description: Shade firmware version + properties: + revision: + type: number + description: Firmware major number + example: 3 + subRevision: + type: number + description: Firmware minor number + example: 0 + build: + type: number + description: Firmware build number + example: 309 + positions: + type: object + description: Current Shade position values + properties: + primary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully + closed to fully open position of the primary rail + example: 0.99 + secondary: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully + closed to fully open position of the secondary rail + example: 0.99 + tilt: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the fully + closed to fully open tilt position + example: 0 + velocity: + type: number + description: >- + Decimal number between 0.0 and 1.0 representing the Shade + operation speed + example: 0.5 + required: + - id + - type + - name + - ptName + - capabilities + - powerType + - batteryStatus + - roomId + - bleName + WiFiScanEntry: + type: object + description: Wireless network scan entry + properties: + ssid: + type: string + description: Wireless network name + example: HunterDouglasWiFi + rssi: + type: string + description: Wireless network RSSI value + example: '-81' + required: + - ssid + - rssi + x-descriptions: + shadeListQuery: >- + The list of Shades is specified by either of the "ids" or "shades" + parameters. Only one of these should and must be utilized. + + - The ``ids`` parameter accepts a list of numerical shade identifiers. + - The ``shades`` parameter accepts a list of strings like ``PIR:6521`` + shadeListQuerySpsTest: >- + The list of Shades is specified by one of the following: "coordinates", + "ids", or "shades" parameters. Only one of these should and must be + utilized. + + - The ``coordinates`` parameter accepts a comma seperated list of channel:slot coordinates like ``1:0`` + - The ``ids`` parameter accepts a list of numerical shade identifiers. + - The ``shades`` parameter accepts a list of strings like ``PIR:6521`` +customOptions: {} From 5447bde0a9794bf859bf94dbbf54a9676771ef14 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 5 Mar 2023 17:18:38 +0000 Subject: [PATCH 50/64] [hdpowerview] add integration registration Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/GatewayWebTargets.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 4a00423be72da..6d76a3ab511e4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -75,6 +75,8 @@ public class GatewayWebTargets implements Closeable, HostnameVerifier { public static final Type LIST_SCENES = new TypeToken>() {}.getType(); // @formatter:on + private static final Set HTTP_OK_CODES = Set.of(HttpStatus.OK_200, HttpStatus.NO_CONTENT_204); + private final Logger logger = LoggerFactory.getLogger(GatewayWebTargets.class); private final String shades; private final String scenes; @@ -122,11 +124,10 @@ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, shadePositions = home + "shades/positions"; shadeEvents = home + "shades/events"; sceneEvents = home + "scenes/events"; + register = home + "integration/openhab.org"; info = base + "gateway/info"; - register = "TODO"; // TODO waiting for Hunter Douglas to provide the end point URL - this.httpClient = httpClient; this.clientBuilder = clientBuilder; this.eventSourceFactory = eventSourceFactory; @@ -166,14 +167,8 @@ public void close() throws IOException { * @throws HubProcessingException if any error occurs. */ public void gatewayRegister() throws HubProcessingException { - // TODO waiting for Hunter Douglas to provide registration details if (!isRegistered) { - final class GatewayRegistration { - @SuppressWarnings("unused") - public String todo = "org.openhab.binding.hdpowerview"; - } - String json = jsonParser.toJson(new GatewayRegistration()); - invoke(HttpMethod.PUT, register, null, json); + invoke(HttpMethod.PUT, register, null, null); isRegistered = true; } } @@ -296,7 +291,7 @@ protected synchronized String invoke(HttpMethod method, String url, @Nullable Qu throw new HubProcessingException(String.format("%s: \"%s\"", e.getClass().getName(), e.getMessage())); } int statusCode = response.getStatus(); - if (statusCode != HttpStatus.OK_200) { + if (!HTTP_OK_CODES.contains(statusCode)) { logger.warn("invoke() HTTP status:{}, reason:{}", statusCode, response.getReason()); throw new HubProcessingException(String.format("HTTP %d error", statusCode)); } @@ -304,7 +299,7 @@ protected synchronized String invoke(HttpMethod method, String url, @Nullable Qu if (logger.isTraceEnabled()) { logger.trace("invoke() response JSON:{}", jsonResponse); } - if (jsonResponse == null || jsonResponse.isEmpty()) { + if ((HttpStatus.OK_200 == statusCode) && ((jsonResponse == null) || (jsonResponse.isEmpty()))) { logger.warn("invoke() hub returned no content"); throw new HubProcessingException("Missing response entity"); } From 4db576db15e556317257dfdfd549bb73f626410a Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 5 Mar 2023 17:19:11 +0000 Subject: [PATCH 51/64] [hdpowerview] fix refresh Signed-off-by: Andrew Fiddian-Green --- .../hdpowerview/internal/handler/GatewayBridgeHandler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index d845948701815..cd157a1c76fec 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -203,10 +203,11 @@ public void initialize() { if (refreshTask != null) { refreshTask.cancel(false); } - this.refreshTask = scheduler.scheduleWithFixedDelay(() -> doRefresh(), 10, config.hardRefresh, - TimeUnit.MINUTES); + this.refreshTask = scheduler.scheduleWithFixedDelay(() -> doRefresh(), 0, config.hardRefresh, TimeUnit.MINUTES); updateStatus(ThingStatus.UNKNOWN); + + scheduler.submit(() -> doRefresh()); } /** From ef97dc03c73e2a5ce054b7f73b6c91aa93432941 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Wed, 8 Mar 2023 18:21:31 +0000 Subject: [PATCH 52/64] [hdpowerview] bug fixes based on Mockoon simulator Signed-off-by: Andrew Fiddian-Green --- .../internal/GatewayWebTargets.java | 16 +++-- .../discovery/ShadeDiscoveryService.java | 4 +- .../handler/GatewayBridgeHandler.java | 63 +++++++++++++------ .../internal/handler/ShadeThingHandler.java | 6 +- 4 files changed, 61 insertions(+), 28 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 6d76a3ab511e4..e8010795aad19 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -82,8 +82,9 @@ public class GatewayWebTargets implements Closeable, HostnameVerifier { private final String scenes; private final String sceneActivate; private final String shadeMotion; - private final String shadeStop; private final String shadePositions; + private final String shadeSingle; + private final String shadeStop; private final String info; private final String register; private final String shadeEvents; @@ -118,12 +119,16 @@ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, shades = home + "shades"; scenes = home + "scenes"; + sceneActivate = home + "scenes/%d/activate"; shadeMotion = home + "shades/%d/motion"; + shadePositions = home + "shades/%d/positions"; + shadeSingle = home + "shades/%d"; shadeStop = home + "shades/stop"; - shadePositions = home + "shades/positions"; + shadeEvents = home + "shades/events"; sceneEvents = home + "scenes/events"; + register = home + "integration/openhab.org"; info = base + "gateway/info"; @@ -221,7 +226,7 @@ public List getScenes() throws HubProcessingException { * @throws HubProcessingException if any error occurs. */ public Shade getShade(int shadeId) throws HubProcessingException { - String json = invoke(HttpMethod.GET, shades + Integer.toString(shadeId), null, null); + String json = invoke(HttpMethod.GET, String.format(shadeSingle, shadeId), null, null); try { Shade result = jsonParser.fromJson(json, Shade.class); if (result == null) { @@ -318,15 +323,14 @@ public void jogShade(int shadeId) throws HubProcessingException { } /** - * Issue a ccommand to move a shade. + * Issue a command to move a shade. * * @param shadeId the shade to be moved. * @param position the new position. * @throws HubProcessingException if any error occurs. */ public void moveShade(int shadeId, ShadePosition position) throws HubProcessingException { - invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.valueOf(shadeId).toString()), - jsonParser.toJson(position)); + invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, jsonParser.toJson(position)); } /** diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java index ddb2461389579..e79b10f8b9d3d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java @@ -75,11 +75,13 @@ protected void stopBackgroundDiscovery() { private Runnable createScanner() { return () -> { - GatewayWebTargets webTargets = hub.getWebTargets(); try { + GatewayWebTargets webTargets = hub.getWebTargets(); discoverShades(webTargets); } catch (HubProcessingException e) { logger.warn("Unexpected exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + } catch (IllegalStateException e) { + // ignore } stopScan(); }; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index cd157a1c76fec..c135946f12eaf 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -109,17 +109,24 @@ public void dispose() { * have been lost. */ private void doRefresh() { + logger.debug("doRefresh()"); try { getWebTargets().gatewayRegister(); getWebTargets().sseOpen(); refreshProperties(); refreshShades(); refreshScenes(); + updateStatus(ThingStatus.ONLINE); } catch (IllegalStateException | HubProcessingException e) { - logger.warn("doRefresh() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + logger.debug("doRefresh() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage(), e); } } + public ScheduledExecutorService getScheduler() { + return scheduler; + } + /** * Getter for the list of all child shade thing handlers. * @@ -127,17 +134,10 @@ private void doRefresh() { * @throws IllegalStateException if the bridge is not properly initialized. */ private List getShadeThingHandlers() throws IllegalStateException { - Bridge bridge = getBridge(); - if (bridge != null) { - List result = new ArrayList<>(); - bridge.getThings().stream().map(thing -> thing.getHandler()).forEach(handler -> { - if (handler instanceof ShadeThingHandler) { - result.add((ShadeThingHandler) handler); - } - }); - return result; - } - throw new IllegalStateException("Bridge not initialized."); + logger.debug("getShadeThingHandlers()"); + return getThing().getThings().stream().map(thing -> thing.getHandler()) + .filter(handler -> (handler instanceof ShadeThingHandler)).map(handler -> (ShadeThingHandler) handler) + .toList(); } /** @@ -225,18 +225,22 @@ public void onSceneEvent(Scene scene) { * @param shade the one that changed. */ public void onShadeEvent(Shade shade) { + if (isDisposing) { + return; + } try { for (ShadeThingHandler handler : getShadeThingHandlers()) { - if (isDisposing || handler.notify(shade)) { + if (handler.notify(shade)) { break; } } } catch (IllegalStateException e) { - logger.warn("onShadeEvent() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage()); + logger.warn("onShadeEvent() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage(), e); } } private void refreshProperties() throws HubProcessingException, IllegalStateException { + logger.debug("refreshProperties()"); if (propertiesLoaded || isDisposing) { return; } @@ -251,10 +255,11 @@ private void refreshProperties() throws HubProcessingException, IllegalStateExce * @throws IllegalStateException if this handler is in an illegal state. */ private void refreshScenes() throws HubProcessingException, IllegalStateException { + logger.debug("refreshScenes()"); if (scenesLoaded || isDisposing) { return; } - ChannelTypeUID typeUID = new ChannelTypeUID(channelTypeId); + ChannelTypeUID typeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID, channelTypeId); ChannelGroupUID groupUID = new ChannelGroupUID(thing.getUID(), channelGroupId); List channels = new ArrayList<>(); for (Scene scene : getWebTargets().getScenes()) { @@ -268,6 +273,24 @@ private void refreshScenes() throws HubProcessingException, IllegalStateExceptio scenesLoaded = true; } + /** + * Refresh a single shade. + * + * @param shadeId the id of the shade to be refreshed. + */ + public void refreshShade(int shadeId) { + try { + Shade shade = getWebTargets().getShade(shadeId); + for (ShadeThingHandler handler : getShadeThingHandlers()) { + if (handler.notify(shade)) { + break; + } + } + } catch (HubProcessingException | IllegalStateException e) { + logger.warn("refreshShade() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage(), e); + } + } + /** * Get the full list of shades data and notify each of the thing handlers. * @@ -275,17 +298,17 @@ private void refreshScenes() throws HubProcessingException, IllegalStateExceptio * @throws IllegalStateException if this handler is in an illegal state. */ private void refreshShades() throws HubProcessingException, IllegalStateException { + logger.debug("refreshShades()"); + if (isDisposing) { + return; + } List handlers = getShadeThingHandlers(); for (Shade shade : getWebTargets().getShades()) { for (ShadeThingHandler handler : handlers) { - if (isDisposing || handler.notify(shade)) { + if (handler.notify(shade)) { break; } } } } - - public ScheduledExecutorService getScheduler() { - return scheduler; - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index 3172a87b96f2d..6139793114660 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; import java.util.StringJoiner; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -169,13 +170,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { public void initialize() { thisShade.setId(getConfigAs(HDPowerViewShadeConfiguration.class).id); Bridge bridge = getBridge(); - if (bridge == null) { + BridgeHandler bridgeHandler = bridge != null ? bridge.getHandler() : null; + if (!(bridgeHandler instanceof GatewayBridgeHandler)) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error.invalid-bridge-handler"); return; } isInitialized = false; updateStatus(ThingStatus.UNKNOWN); + scheduler.schedule(() -> ((GatewayBridgeHandler) bridgeHandler).refreshShade(thisShade.getId()), 5, + TimeUnit.SECONDS); } /** From 77386bd23213eaa6f440402ab5baee728371b43f Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Fri, 10 Mar 2023 19:22:59 +0000 Subject: [PATCH 53/64] [hdpowerview] more bug fixes based on Mockoon simulator Signed-off-by: Andrew Fiddian-Green --- .../internal/GatewayWebTargets.java | 2 +- .../internal/HDPowerViewBindingConstants.java | 6 +- .../internal/HDPowerViewHandlerFactory.java | 6 +- .../HDPowerViewDeviceDiscoveryService.java | 2 +- .../HDPowerViewHubDiscoveryService.java | 2 +- .../discovery/ShadeDiscoveryService.java | 2 +- .../hdpowerview/internal/dto/gen3/Shade.java | 6 +- .../internal/handler/ShadeThingHandler.java | 22 ++- .../internal/gen3/Generation3DtoTest.java | 145 +++++++++++++++++- .../hdpowerview/internal/gen3/shades.json | 21 +++ 10 files changed, 193 insertions(+), 21 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index e8010795aad19..6b0cc76f5e150 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -330,7 +330,7 @@ public void jogShade(int shadeId) throws HubProcessingException { * @throws HubProcessingException if any error occurs. */ public void moveShade(int shadeId, ShadePosition position) throws HubProcessingException { - invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, jsonParser.toJson(position)); + invoke(HttpMethod.PUT, String.format(shadePositions, shadeId), null, jsonParser.toJson(position)); } /** diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java index c78a45479361d..7436cd05005a1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewBindingConstants.java @@ -75,9 +75,6 @@ public class HDPowerViewBindingConstants { public static final List NETBIOS_NAMES = Arrays.asList("PDBU-Hub3.0", "PowerView-Hub"); - public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_HUB, THING_TYPE_SHADE, - THING_TYPE_REPEATER); - public static final Pattern VALID_IP_V4_ADDRESS = Pattern .compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b"); @@ -85,6 +82,9 @@ public class HDPowerViewBindingConstants { public static final ThingTypeUID THING_TYPE_GATEWAY = new ThingTypeUID(BINDING_ID, "gateway"); public static final ThingTypeUID THING_TYPE_SHADE3 = new ThingTypeUID(BINDING_ID, "shade3"); + public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_HUB, THING_TYPE_SHADE, + THING_TYPE_REPEATER, THING_TYPE_GATEWAY, THING_TYPE_SHADE3); + public static final String PROPERTY_NAME = "name"; public static final String PROPERTY_POWER_TYPE = "powerType"; public static final String PROPERTY_BLE_NAME = "bleName"; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java index d8039fd8aa043..47359a89a9d7d 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewHandlerFactory.java @@ -78,7 +78,6 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - // generation 3 if (HDPowerViewBindingConstants.THING_TYPE_GATEWAY.equals(thingTypeUID)) { GatewayBridgeHandler handler = new GatewayBridgeHandler((Bridge) thing, httpClient, translationProvider, clientBuilder, eventSourceFactory); @@ -86,9 +85,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { return handler; } else if (HDPowerViewBindingConstants.THING_TYPE_SHADE3.equals(thingTypeUID)) { return new ShadeThingHandler(thing); - } else - // generation 1/2 - if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { + } else if (HDPowerViewBindingConstants.THING_TYPE_HUB.equals(thingTypeUID)) { HDPowerViewHubHandler handler = new HDPowerViewHubHandler((Bridge) thing, httpClient, translationProvider); registerService(new HDPowerViewDeviceDiscoveryService(handler)); return handler; @@ -97,7 +94,6 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } else if (HDPowerViewBindingConstants.THING_TYPE_REPEATER.equals(thingTypeUID)) { return new HDPowerViewRepeaterHandler(thing); } - return null; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java index 156d70492c4b4..c8c1b83128eb9 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewDeviceDiscoveryService.java @@ -55,7 +55,7 @@ public class HDPowerViewDeviceDiscoveryService extends AbstractDiscoveryService private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase(); public HDPowerViewDeviceDiscoveryService(HDPowerViewHubHandler hub) { - super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE), 600, true); + super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE), 60, true); this.hub = hub; this.scanner = createScanner(); } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryService.java index d6edb949a880b..6defbf71a93d8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryService.java @@ -48,7 +48,7 @@ public class HDPowerViewHubDiscoveryService extends AbstractDiscoveryService { private @Nullable ScheduledFuture backgroundFuture; public HDPowerViewHubDiscoveryService() { - super(Collections.singleton(THING_TYPE_HUB), 600, true); + super(Collections.singleton(THING_TYPE_HUB), 60, true); scanner = createScanner(); } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java index e79b10f8b9d3d..80ae30968d610 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/ShadeDiscoveryService.java @@ -44,7 +44,7 @@ public class ShadeDiscoveryService extends AbstractDiscoveryService { private @Nullable ScheduledFuture backgroundFuture; public ShadeDiscoveryService(GatewayBridgeHandler hub) { - super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE3), 600, true); + super(Collections.singleton(HDPowerViewBindingConstants.THING_TYPE_SHADE3), 60, true); this.hub = hub; this.scanner = createScanner(); } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java index cc628ff58e437..a898f278b5bb1 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java @@ -21,7 +21,6 @@ import org.openhab.binding.hdpowerview.internal.dto.Firmware; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; -import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; @@ -49,7 +48,7 @@ public class Shade { public State getBatteryLevel() { Integer batteryStatus = this.batteryStatus; return batteryStatus == null ? UnDefType.UNDEF - : new PercentType(Math.max(0, Math.min(100, (100 * batteryStatus) / 3))); + : new DecimalType(Math.max(0, Math.min(100, (100 * batteryStatus) / 3))); } public @Nullable String getBleName() { @@ -117,8 +116,7 @@ public boolean hasFullState() { } public boolean isMainsPowered() { - // check powerType and return true or false - return false; + return "hardwired".equalsIgnoreCase(powerType) || "1".equals(powerType); } public Shade setCapabilities(int capabilities) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index 6139793114660..0a5cdc504b30b 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -43,6 +43,7 @@ import org.openhab.core.thing.binding.BridgeHandler; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; +import org.openhab.core.types.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,12 +55,12 @@ @NonNullByDefault public class ShadeThingHandler extends BaseThingHandler { - private final Logger logger = LoggerFactory.getLogger(ShadeThingHandler.class); - private static final String INVALID_CHANNEL = "invalid channel"; + private static final String INVALID_COMMAND = "invalid command"; private static final String COMMAND_CALIBRATE = "CALIBRATE"; private static final String COMMAND_IDENTIFY = "IDENTIFY"; + private final Logger logger = LoggerFactory.getLogger(ShadeThingHandler.class); private final Shade thisShade = new Shade(); private boolean isInitialized; @@ -255,8 +256,8 @@ private void updateDynamicChannels(Shade shade) { updateDynamicChannel(removeChannels, CHANNEL_SHADE_VANE, positions.supportsTilt()); } - updateDynamicChannel(removeChannels, CHANNEL_SHADE_BATTERY_LEVEL, shade.isMainsPowered()); - updateDynamicChannel(removeChannels, CHANNEL_SHADE_LOW_BATTERY, shade.isMainsPowered()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_BATTERY_LEVEL, !shade.isMainsPowered()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_LOW_BATTERY, !shade.isMainsPowered()); if (!removeChannels.isEmpty()) { if (logger.isDebugEnabled()) { @@ -286,4 +287,17 @@ private void updateProperties(Shade shade) { }).collect(Collectors.toMap(data -> data[0], data -> data[1]))); } } + + /** + * Override base method to only update the channel if it actually exists. + * + * @param channelID id of the channel, which was updated + * @param state new state + */ + @Override + protected void updateState(String channelID, State state) { + if (thing.getChannel(channelID) != null) { + super.updateState(channelID, state); + } + } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java index 4a2b1f37cec6c..9620d469e4c8f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java @@ -13,11 +13,15 @@ package org.openhab.binding.hdpowerview.internal.gen3; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.List; +import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; @@ -29,7 +33,18 @@ import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadeEvent; import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition; +import org.openhab.binding.hdpowerview.internal.handler.ShadeThingHandler; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandlerCallback; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.thing.binding.builder.ThingBuilder; import org.openhab.core.types.UnDefType; import com.google.gson.Gson; @@ -130,9 +145,137 @@ public void testShadesParsing() throws IOException { String json = loadJson("gen3/shades.json"); List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); assertNotNull(shadeList); - assertEquals(1, shadeList.size()); + assertEquals(2, shadeList.size()); Shade shadeData = shadeList.get(0); assertEquals("Shade 2 ABC", shadeData.getName()); assertEquals(789, shadeData.getId()); + assertFalse(shadeData.isMainsPowered()); + assertEquals(new DecimalType(66), shadeData.getBatteryLevel()); + assertEquals(OnOffType.OFF, shadeData.getLowBattery()); + ShadePosition positions = shadeData.getShadePositions(); + assertNotNull(positions); + assertTrue(positions.supportsPrimary()); + assertTrue(positions.supportsSecondary()); + assertTrue(positions.supportsTilt()); + + shadeData = shadeList.get(1); + assertEquals(123, shadeData.getId()); + assertTrue(shadeData.isMainsPowered()); + positions = shadeData.getShadePositions(); + assertNotNull(positions); + assertTrue(positions.supportsPrimary()); + assertFalse(positions.supportsSecondary()); + assertFalse(positions.supportsTilt()); + } + + /** + * Test sending properties and dynamic channel values to a shade handler. + */ + @Test + public void testShadeHandlerPropertiesAndChannels() throws IOException { + ThingTypeUID thingTypeUID = new ThingTypeUID("hdpowerview:shade"); + ThingUID thingUID = new ThingUID(thingTypeUID, "test"); + + List channels = new ArrayList(); + for (String channelId : Set.of(CHANNEL_SHADE_POSITION, CHANNEL_SHADE_SECONDARY_POSITION, CHANNEL_SHADE_VANE, + CHANNEL_SHADE_BATTERY_LEVEL, CHANNEL_SHADE_LOW_BATTERY, CHANNEL_SHADE_SIGNAL_STRENGTH)) { + ChannelUID channelUID = new ChannelUID(thingUID, channelId); + channels.add(ChannelBuilder.create(channelUID).build()); + } + + String json = loadJson("gen3/shades.json"); + List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); + assertNotNull(shadeList); + assertEquals(2, shadeList.size()); + + Thing thing = ThingBuilder.create(thingTypeUID, thingUID).withChannels(channels).build(); + ShadeThingHandler shadeThingHandler; + Shade shadeData; + + /* + * Use the first JSON Shade entry. + * It should support the full 6 dynamic channels. + */ + shadeThingHandler = new ShadeThingHandler(thing); + shadeThingHandler.setCallback(mock(ThingHandlerCallback.class)); + shadeData = shadeList.get(0).setId(0); + assertTrue(shadeData.hasFullState()); + shadeThingHandler.notify(shadeData); + Thing handlerThing = shadeThingHandler.getThing(); + assertEquals("23", handlerThing.getProperties().get("type")); + assertEquals("wand", handlerThing.getProperties().get("powerType")); + assertEquals("3.0.309", handlerThing.getProperties().get("firmwareVersion")); + assertEquals(new DecimalType(-55), shadeData.getSignalStrength()); + assertEquals(6, handlerThing.getChannels().size()); + + /* + * Reuse the first JSON Shade entry and delete its 3 positions. + * So it should now only support 3 dynamic channels. + */ + shadeThingHandler = new ShadeThingHandler(thing); + shadeThingHandler.setCallback(mock(ThingHandlerCallback.class)); + shadeData.setShadePosition(new ShadePosition()); + shadeThingHandler.notify(shadeData); + handlerThing = shadeThingHandler.getThing(); + assertEquals(3, handlerThing.getChannels().size()); + + /* + * Use the second JSON Shade entry. + * It should support only 2 dynamic channel. + */ + shadeThingHandler = new ShadeThingHandler(thing); + shadeThingHandler.setCallback(mock(ThingHandlerCallback.class)); + shadeData = shadeList.get(1).setId(0); + assertTrue(shadeData.hasFullState()); + shadeThingHandler.notify(shadeData); + handlerThing = shadeThingHandler.getThing(); + assertEquals("44", handlerThing.getProperties().get("type")); + assertEquals("hardwired", handlerThing.getProperties().get("powerType")); + assertEquals("3.0.123", handlerThing.getProperties().get("firmwareVersion")); + assertEquals(new DecimalType(-44), shadeData.getSignalStrength()); + assertEquals(2, handlerThing.getChannels().size()); + } + + /** + * Test sending state change events to shade handler. + */ + @Test + public void testShadeHandlerEvents() throws IOException { + ThingTypeUID thingTypeUID = new ThingTypeUID("hdpowerview:shade"); + ThingUID thingUID = new ThingUID(thingTypeUID, "test"); + + List channels = new ArrayList(); + for (String channelId : Set.of(CHANNEL_SHADE_POSITION, CHANNEL_SHADE_SECONDARY_POSITION, CHANNEL_SHADE_VANE, + CHANNEL_SHADE_BATTERY_LEVEL, CHANNEL_SHADE_LOW_BATTERY, CHANNEL_SHADE_SIGNAL_STRENGTH)) { + ChannelUID channelUID = new ChannelUID(thingUID, channelId); + channels.add(ChannelBuilder.create(channelUID).build()); + } + + String json = loadJson("gen3/shades.json"); + List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); + assertNotNull(shadeList); + assertEquals(2, shadeList.size()); + + Thing thing = ThingBuilder.create(thingTypeUID, thingUID).withChannels(channels).build(); + ShadeThingHandler shadeThingHandler; + Shade shadeData; + + /* + * Use the second JSON Shade entry, which only has a primary channel. + */ + shadeThingHandler = new ShadeThingHandler(thing); + shadeThingHandler.setCallback(mock(ThingHandlerCallback.class)); + shadeData = shadeList.get(1).setId(0); + shadeThingHandler.notify(shadeData); + + /* + * And try to update it with an event that has all 3 channels. + */ + json = loadJson("gen3/shade-event.json"); + ShadeEvent event = gson.fromJson(json, ShadeEvent.class); + assertNotNull(event); + shadeData = new Shade().setId(0).setShadePosition(event.getCurrentPositions()).setPartialState(); + assertFalse(shadeData.hasFullState()); + shadeThingHandler.notify(shadeData); } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json index a903771e23e37..4950a28c4cc78 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json @@ -21,5 +21,26 @@ "tilt": 0, "velocity": 0.5 } + }, + { + "id": 123, + "type": 44, + "name": "U2hhZGUgMg==", + "ptName": "ABC", + "capabilities": 0, + "powerType": "hardwired", + "batteryStatus": 2, + "roomId": 6833, + "signalStrength": -44, + "bleName": "PIR:5933", + "firmware": { + "revision": 3, + "subRevision": 0, + "build": 123 + }, + "positions": { + "primary": 0.99, + "velocity": 0.5 + } } ] From f803078f43ae5ae653796ab339bc1f91f7429f4d Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 20 May 2023 18:29:30 +0100 Subject: [PATCH 54/64] [hdpowerview] initial fixes from live testing Signed-off-by: Andrew Fiddian-Green --- .../internal/GatewayWebTargets.java | 78 ++++++---------- .../database/ShadeCapabilitiesDatabase.java | 2 +- .../internal/dto/gen3/PowerType.java | 24 +++++ .../hdpowerview/internal/dto/gen3/Shade.java | 17 ++-- .../internal/dto/gen3/ShadePosition.java | 24 ++--- .../handler/GatewayBridgeHandler.java | 19 ++-- .../internal/handler/ShadeThingHandler.java | 70 +++++++++----- .../internal/gen3/Generation3DtoTest.java | 81 ++++++++-------- .../hdpowerview/internal/gen3/shades.json | 92 ++++++++++--------- 9 files changed, 221 insertions(+), 186 deletions(-) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/PowerType.java diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 6b0cc76f5e150..4391f9d140e62 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -14,8 +14,6 @@ import java.io.Closeable; import java.io.IOException; -import java.lang.reflect.Type; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; @@ -57,7 +55,6 @@ import com.google.gson.Gson; import com.google.gson.JsonParseException; -import com.google.gson.reflect.TypeToken; /** * JAX-RS targets for communicating with an HD PowerView Generation 3 Gateway. @@ -69,12 +66,6 @@ public class GatewayWebTargets implements Closeable, HostnameVerifier { private static final String IDS = "ids"; private static final int SLEEP_SECONDS = 360; - - // @formatter:off - public static final Type LIST_SHADES = new TypeToken>() {}.getType(); - public static final Type LIST_SCENES = new TypeToken>() {}.getType(); - // @formatter:on - private static final Set HTTP_OK_CODES = Set.of(HttpStatus.OK_200, HttpStatus.NO_CONTENT_204); private final Logger logger = LoggerFactory.getLogger(GatewayWebTargets.class); @@ -112,31 +103,25 @@ public class GatewayWebTargets implements Closeable, HostnameVerifier { */ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory, String ipAddress) { + this.hostName = ipAddress; + this.httpClient = httpClient; + this.clientBuilder = clientBuilder; + this.eventSourceFactory = eventSourceFactory; + this.hubHandler = hubHandler; + String base = "http://" + ipAddress + "/"; String home = base + "home/"; - - hostName = ipAddress; - shades = home + "shades"; scenes = home + "scenes"; - sceneActivate = home + "scenes/%d/activate"; shadeMotion = home + "shades/%d/motion"; - shadePositions = home + "shades/%d/positions"; + shadePositions = home + "shades/positions"; shadeSingle = home + "shades/%d"; shadeStop = home + "shades/stop"; - - shadeEvents = home + "shades/events"; - sceneEvents = home + "scenes/events"; - + shadeEvents = home + "shades/events?sse=true"; + sceneEvents = home + "scenes/events?sse=true"; register = home + "integration/openhab.org"; - info = base + "gateway/info"; - - this.httpClient = httpClient; - this.clientBuilder = clientBuilder; - this.eventSourceFactory = eventSourceFactory; - this.hubHandler = hubHandler; } /** @@ -171,11 +156,12 @@ public void close() throws IOException { * * @throws HubProcessingException if any error occurs. */ - public void gatewayRegister() throws HubProcessingException { + public boolean gatewayRegister() throws HubProcessingException { if (!isRegistered) { invoke(HttpMethod.PUT, register, null, null); isRegistered = true; } + return isRegistered; } /** @@ -208,11 +194,9 @@ public Map getInformation() throws HubProcessingException { public List getScenes() throws HubProcessingException { String json = invoke(HttpMethod.GET, scenes, null, null); try { - List result = jsonParser.fromJson(json, LIST_SCENES); - if (result == null) { - throw new HubProcessingException("getScenes() missing response"); - } - return result; + return List.of(jsonParser.fromJson(json, Scene[].class)); + } catch (NullPointerException e) { + throw new HubProcessingException("getScenes() missing response"); } catch (JsonParseException e) { throw new HubProcessingException("getScenes() JsonParseException"); } @@ -247,11 +231,9 @@ public Shade getShade(int shadeId) throws HubProcessingException { public List getShades() throws HubProcessingException { String json = invoke(HttpMethod.GET, shades, null, null); try { - List result = jsonParser.fromJson(json, LIST_SHADES); - if (result == null) { - throw new HubProcessingException("getShades() missing response"); - } - return result; + return List.of(jsonParser.fromJson(json, Shade[].class)); + } catch (NullPointerException e) { + throw new HubProcessingException("getScenes() missing response"); } catch (JsonParseException e) { throw new HubProcessingException("getShades() JsonParseException"); } @@ -326,11 +308,21 @@ public void jogShade(int shadeId) throws HubProcessingException { * Issue a command to move a shade. * * @param shadeId the shade to be moved. - * @param position the new position. + * @param shade DTO with the new position. * @throws HubProcessingException if any error occurs. */ - public void moveShade(int shadeId, ShadePosition position) throws HubProcessingException { - invoke(HttpMethod.PUT, String.format(shadePositions, shadeId), null, jsonParser.toJson(position)); + public void moveShade(int shadeId, Shade shade) throws HubProcessingException { + invoke(HttpMethod.PUT, shadePositions, Query.of(IDS, Integer.toString(shadeId)), jsonParser.toJson(shade)); + } + + /** + * Called when the SSE event channel has not received any events for a long time. This could mean that the event + * source socket has dropped. So restart the SSE connection. + */ + public void onSseQuiet() { + if (!closing) { + sseReOpen(); + } } /** @@ -416,16 +408,6 @@ private void onSSeShadeEvent(InboundSseEvent sseEvent) { } } - /** - * Called when the SSE event channel has not received any events for a long time. This could mean that the event - * source socket has dropped. So restart the SSE connection. - */ - public void onSseQuiet() { - if (!closing) { - sseReOpen(); - } - } - /** * Close the SSE links. */ diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/database/ShadeCapabilitiesDatabase.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/database/ShadeCapabilitiesDatabase.java index 35b233d0bad0b..70dd23308813a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/database/ShadeCapabilitiesDatabase.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/database/ShadeCapabilitiesDatabase.java @@ -129,7 +129,7 @@ public static class Type extends Base { protected Type() { } - protected Type(int type) { + public Type(int type) { intValue = type; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/PowerType.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/PowerType.java new file mode 100644 index 0000000000000..62a85c4a697c9 --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/PowerType.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2023 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.hdpowerview.internal.dto.gen3; + +/** + * Enum for Generation 3 shade power types. + * + * @author Andrew Fiddian-Green - Initial contribution + */ +public enum PowerType { + BATTERY, + HARDWIRED, + RECHARGEABLE; +} diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java index a898f278b5bb1..c2c237c2e661a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java @@ -21,6 +21,7 @@ import org.openhab.binding.hdpowerview.internal.dto.Firmware; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; @@ -31,12 +32,12 @@ */ @NonNullByDefault public class Shade { - private int id; + private @Nullable Integer id; private @Nullable Integer type; private @Nullable String name; private @Nullable String ptName; private @Nullable Integer capabilities; - private @Nullable String powerType; // TODO unclear if this is String or Integer + private @Nullable Integer powerType; private @Nullable Integer batteryStatus; private @Nullable Integer signalStrength; private @Nullable String bleName; @@ -71,7 +72,8 @@ public State getBatteryLevel() { } public int getId() { - return id; + Integer id = this.id; + return id != null ? id.intValue() : 0; } public State getLowBattery() { @@ -89,8 +91,9 @@ public State getPosition(CoordinateSystem posKindCoords) { return positions == null ? UnDefType.UNDEF : positions.getState(posKindCoords); } - public @Nullable String getPowerType() { - return powerType; + public PowerType getPowerType() { + Integer powerType = this.powerType; + return PowerType.values()[powerType != null ? powerType.intValue() : 0]; } public @Nullable ShadePosition getShadePositions() { @@ -116,7 +119,7 @@ public boolean hasFullState() { } public boolean isMainsPowered() { - return "hardwired".equalsIgnoreCase(powerType) || "1".equals(powerType); + return getPowerType() == PowerType.HARDWIRED; } public Shade setCapabilities(int capabilities) { @@ -134,7 +137,7 @@ public Shade setPartialState() { return this; } - public Shade setPosition(CoordinateSystem coordinates, int percent) { + public Shade setPosition(CoordinateSystem coordinates, PercentType percent) { ShadePosition positions = this.positions; if (positions == null) { positions = new ShadePosition(); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java index ff9388ee7eca1..cc838d75ef518 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/ShadePosition.java @@ -12,6 +12,10 @@ */ package org.openhab.binding.hdpowerview.internal.dto.gen3; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; import org.openhab.core.library.types.PercentType; @@ -25,6 +29,8 @@ */ @NonNullByDefault public class ShadePosition { + private static final MathContext MATH_CONTEXT = new MathContext(4, RoundingMode.HALF_UP); + private @NonNullByDefault({}) Double primary; private @NonNullByDefault({}) Double secondary; private @NonNullByDefault({}) Double tilt; @@ -44,7 +50,7 @@ public State getState(CoordinateSystem posKindCoords) { default: value = null; } - return value != null ? new PercentType((int) (value.doubleValue() * 100)) : UnDefType.UNDEF; + return value != null ? new PercentType(new BigDecimal(value * 100f, MATH_CONTEXT)) : UnDefType.UNDEF; } /** @@ -54,8 +60,8 @@ public State getState(CoordinateSystem posKindCoords) { * @param percent the new value in percent. * @return this object. */ - public ShadePosition setPosition(CoordinateSystem coordinates, int percent) { - Double value = Double.valueOf(percent) / 100.0; + public ShadePosition setPosition(CoordinateSystem coordinates, PercentType percent) { + Double value = percent.doubleValue() / 100f; switch (coordinates) { case PRIMARY_POSITION: primary = value; @@ -70,16 +76,4 @@ public ShadePosition setPosition(CoordinateSystem coordinates, int percent) { } return this; } - - public boolean supportsPrimary() { - return primary != null; - } - - public boolean supportsSecondary() { - return secondary != null; - } - - public boolean supportsTilt() { - return tilt != null; - } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index c135946f12eaf..dd898cbd15d51 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -18,6 +18,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import javax.ws.rs.client.ClientBuilder; @@ -111,12 +112,13 @@ public void dispose() { private void doRefresh() { logger.debug("doRefresh()"); try { - getWebTargets().gatewayRegister(); - getWebTargets().sseOpen(); - refreshProperties(); - refreshShades(); - refreshScenes(); - updateStatus(ThingStatus.ONLINE); + if (getWebTargets().gatewayRegister()) { + updateStatus(ThingStatus.ONLINE); + getWebTargets().sseOpen(); + refreshProperties(); + refreshShades(); + refreshScenes(); + } } catch (IllegalStateException | HubProcessingException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); logger.debug("doRefresh() exception:{}, message:{}", e.getClass().getSimpleName(), e.getMessage(), e); @@ -137,7 +139,7 @@ private List getShadeThingHandlers() throws IllegalStateExcep logger.debug("getShadeThingHandlers()"); return getThing().getThings().stream().map(thing -> thing.getHandler()) .filter(handler -> (handler instanceof ShadeThingHandler)).map(handler -> (ShadeThingHandler) handler) - .toList(); + .collect(Collectors.toList()); } /** @@ -204,10 +206,7 @@ public void initialize() { refreshTask.cancel(false); } this.refreshTask = scheduler.scheduleWithFixedDelay(() -> doRefresh(), 0, config.hardRefresh, TimeUnit.MINUTES); - updateStatus(ThingStatus.UNKNOWN); - - scheduler.submit(() -> doRefresh()); } /** diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index 0a5cdc504b30b..ffdc6d51cbdd9 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -23,9 +23,13 @@ import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants; import org.openhab.binding.hdpowerview.internal.config.HDPowerViewShadeConfiguration; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Type; import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition; import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException; @@ -60,10 +64,13 @@ public class ShadeThingHandler extends BaseThingHandler { private static final String INVALID_COMMAND = "invalid command"; private static final String COMMAND_CALIBRATE = "CALIBRATE"; private static final String COMMAND_IDENTIFY = "IDENTIFY"; - private final Logger logger = LoggerFactory.getLogger(ShadeThingHandler.class); + private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); + private final Logger logger = LoggerFactory.getLogger(ShadeThingHandler.class); private final Shade thisShade = new Shade(); + private boolean isInitialized; + private @Nullable Capabilities capabilities; public ShadeThingHandler(Thing thing) { super(thing); @@ -74,6 +81,15 @@ public void dispose() { // TODO Auto-generated method stub } + private Capabilities getCapabilities(Shade shade) { + Capabilities capabilities = this.capabilities; + if (capabilities == null) { + capabilities = DB.getCapabilities(shade.getCapabilities()); + this.capabilities = capabilities; + } + return capabilities; + } + /** * Getter for the hub handler. * @@ -92,6 +108,11 @@ private GatewayBridgeHandler getHandler() throws IllegalStateException { return (GatewayBridgeHandler) handler; } + private Type getType(Shade shade) { + Integer type = shade.getType(); + return type != null ? DB.getType(type) : new ShadeCapabilitiesDatabase.Type(0); + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { if (RefreshType.REFRESH == command) { @@ -106,12 +127,15 @@ public void handleCommand(ChannelUID channelUID, Command command) { switch (channelUID.getId()) { case CHANNEL_SHADE_POSITION: if (command instanceof PercentType) { - position.setPosition(PRIMARY_POSITION, ((PercentType) command).intValue()); - webTargets.moveShade(shadeId, position); + position.setPosition(PRIMARY_POSITION, ((PercentType) command)); + webTargets.moveShade(shadeId, new Shade().setShadePosition(position)); break; } else if (command instanceof UpDownType) { - position.setPosition(PRIMARY_POSITION, UpDownType.UP == command ? 0 : 100); - webTargets.moveShade(shadeId, position); + position.setPosition(PRIMARY_POSITION, + (UpDownType.UP == command) && !getCapabilities(thisShade).isPrimaryInverted() + ? PercentType.HUNDRED + : PercentType.ZERO); + webTargets.moveShade(shadeId, new Shade().setShadePosition(position)); break; } else if (StopMoveType.STOP == command) { webTargets.stopShade(shadeId); @@ -121,12 +145,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { case CHANNEL_SHADE_SECONDARY_POSITION: if (command instanceof PercentType) { - position.setPosition(SECONDARY_POSITION, ((PercentType) command).intValue()); - webTargets.moveShade(shadeId, position); + position.setPosition(SECONDARY_POSITION, ((PercentType) command)); + webTargets.moveShade(shadeId, new Shade().setShadePosition(position)); break; } else if (command instanceof UpDownType) { - position.setPosition(SECONDARY_POSITION, UpDownType.UP == command ? 0 : 100); - webTargets.moveShade(shadeId, position); + position.setPosition(SECONDARY_POSITION, + UpDownType.UP == command ? PercentType.ZERO : PercentType.HUNDRED); + webTargets.moveShade(shadeId, new Shade().setShadePosition(position)); break; } else if (StopMoveType.STOP == command) { webTargets.stopShade(shadeId); @@ -136,12 +161,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { case CHANNEL_SHADE_VANE: if (command instanceof PercentType) { - position.setPosition(VANE_TILT_POSITION, ((PercentType) command).intValue()); - webTargets.moveShade(shadeId, position); + position.setPosition(VANE_TILT_POSITION, ((PercentType) command)); + webTargets.moveShade(shadeId, new Shade().setShadePosition(position)); break; } else if (command instanceof UpDownType) { - position.setPosition(VANE_TILT_POSITION, UpDownType.UP == command ? 0 : 100); - webTargets.moveShade(shadeId, position); + position.setPosition(VANE_TILT_POSITION, + UpDownType.UP == command ? PercentType.HUNDRED : PercentType.ZERO); + webTargets.moveShade(shadeId, new Shade().setShadePosition(position)); break; } throw new IllegalArgumentException(INVALID_COMMAND); @@ -249,13 +275,11 @@ private void updateDynamicChannel(List removeList, String channelId, bo private void updateDynamicChannels(Shade shade) { List removeChannels = new ArrayList<>(); - ShadePosition positions = shade.getShadePositions(); - if (positions != null) { - updateDynamicChannel(removeChannels, CHANNEL_SHADE_POSITION, positions.supportsPrimary()); - updateDynamicChannel(removeChannels, CHANNEL_SHADE_SECONDARY_POSITION, positions.supportsSecondary()); - updateDynamicChannel(removeChannels, CHANNEL_SHADE_VANE, positions.supportsTilt()); - } - + Capabilities capabilities = getCapabilities(shade); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_POSITION, capabilities.supportsPrimary()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_SECONDARY_POSITION, capabilities.supportsSecondary()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_VANE, capabilities.supportsTilt180() + || capabilities.supportsTiltAnywhere() | capabilities.supportsTiltOnClosed()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_BATTERY_LEVEL, !shade.isMainsPowered()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_LOW_BATTERY, !shade.isMainsPowered()); @@ -279,9 +303,9 @@ private void updateProperties(Shade shade) { if (shade.hasFullState()) { thing.setProperties(Stream.of(new String[][] { // { HDPowerViewBindingConstants.PROPERTY_NAME, shade.getName() }, - { HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, shade.getTypeString() }, - { HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES, shade.getCapabilitieString() }, - { HDPowerViewBindingConstants.PROPERTY_POWER_TYPE, shade.getPowerType() }, + { HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, getType(shade).toString() }, + { HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES, getCapabilities(shade).toString() }, + { HDPowerViewBindingConstants.PROPERTY_POWER_TYPE, shade.getPowerType().name().toLowerCase() }, { HDPowerViewBindingConstants.PROPERTY_BLE_NAME, shade.getBleName() }, { Thing.PROPERTY_FIRMWARE_VERSION, shade.getFirmware() } // }).collect(Collectors.toMap(data -> data[0], data -> data[1]))); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java index 9620d469e4c8f..1eb09673362dc 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java @@ -25,8 +25,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.jupiter.api.Test; -import org.openhab.binding.hdpowerview.internal.GatewayWebTargets; import org.openhab.binding.hdpowerview.internal.HDPowerViewJUnitTests; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase; +import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities; import org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem; import org.openhab.binding.hdpowerview.internal.dto.gen3.Scene; import org.openhab.binding.hdpowerview.internal.dto.gen3.SceneEvent; @@ -58,6 +59,7 @@ public class Generation3DtoTest { private final Gson gson = new Gson(); + private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase(); private String loadJson(String filename) throws IOException { try (InputStream inputStream = HDPowerViewJUnitTests.class.getResourceAsStream(filename)) { @@ -92,7 +94,7 @@ public void testSceneEventParsing() throws IOException { @Test public void testScenesParsing() throws IOException { String json = loadJson("gen3/scenes.json"); - List sceneList = gson.fromJson(json, GatewayWebTargets.LIST_SCENES); + List sceneList = List.of(gson.fromJson(json, Scene[].class)); assertNotNull(sceneList); assertEquals(1, sceneList.size()); Scene scene = sceneList.get(0); @@ -123,15 +125,15 @@ public void testShadePositions() { ShadePosition pos; pos = new ShadePosition(); - pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); + pos.setPosition(CoordinateSystem.PRIMARY_POSITION, new PercentType(11)); assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION)); assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.SECONDARY_POSITION)); assertEquals(UnDefType.UNDEF, pos.getState(CoordinateSystem.VANE_TILT_POSITION)); pos = new ShadePosition(); - pos.setPosition(CoordinateSystem.PRIMARY_POSITION, 11); - pos.setPosition(CoordinateSystem.SECONDARY_POSITION, 22); - pos.setPosition(CoordinateSystem.VANE_TILT_POSITION, 33); + pos.setPosition(CoordinateSystem.PRIMARY_POSITION, new PercentType(11)); + pos.setPosition(CoordinateSystem.SECONDARY_POSITION, new PercentType(22)); + pos.setPosition(CoordinateSystem.VANE_TILT_POSITION, new PercentType(33)); assertEquals(PercentType.valueOf("11"), pos.getState(CoordinateSystem.PRIMARY_POSITION)); assertEquals(PercentType.valueOf("22"), pos.getState(CoordinateSystem.SECONDARY_POSITION)); assertEquals(PercentType.valueOf("33"), pos.getState(CoordinateSystem.VANE_TILT_POSITION)); @@ -143,29 +145,39 @@ public void testShadePositions() { @Test public void testShadesParsing() throws IOException { String json = loadJson("gen3/shades.json"); - List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); + List shadeList = List.of(gson.fromJson(json, Shade[].class)); assertNotNull(shadeList); assertEquals(2, shadeList.size()); Shade shadeData = shadeList.get(0); - assertEquals("Shade 2 ABC", shadeData.getName()); - assertEquals(789, shadeData.getId()); + assertEquals("Upper Left Upper Left", shadeData.getName()); + assertEquals(2, shadeData.getId()); assertFalse(shadeData.isMainsPowered()); assertEquals(new DecimalType(66), shadeData.getBatteryLevel()); assertEquals(OnOffType.OFF, shadeData.getLowBattery()); ShadePosition positions = shadeData.getShadePositions(); assertNotNull(positions); - assertTrue(positions.supportsPrimary()); - assertTrue(positions.supportsSecondary()); - assertTrue(positions.supportsTilt()); + Integer caps = shadeData.getCapabilities(); + assertNotNull(caps); + Capabilities capabilities = db.getCapabilities(caps); + assertTrue(capabilities.supportsPrimary()); + assertFalse(capabilities.supportsSecondary()); + assertFalse(capabilities.supportsTilt180()); + assertFalse(capabilities.supportsTiltAnywhere()); + assertFalse(capabilities.supportsTiltOnClosed()); shadeData = shadeList.get(1); - assertEquals(123, shadeData.getId()); + assertEquals(3, shadeData.getId()); assertTrue(shadeData.isMainsPowered()); positions = shadeData.getShadePositions(); assertNotNull(positions); - assertTrue(positions.supportsPrimary()); - assertFalse(positions.supportsSecondary()); - assertFalse(positions.supportsTilt()); + caps = shadeData.getCapabilities(); + assertNotNull(caps); + capabilities = db.getCapabilities(caps); + assertTrue(capabilities.supportsPrimary()); + assertFalse(capabilities.supportsSecondary()); + assertFalse(capabilities.supportsTilt180()); + assertFalse(capabilities.supportsTiltAnywhere()); + assertTrue(capabilities.supportsTiltOnClosed()); } /** @@ -184,7 +196,7 @@ public void testShadeHandlerPropertiesAndChannels() throws IOException { } String json = loadJson("gen3/shades.json"); - List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); + List shadeList = List.of(gson.fromJson(json, Shade[].class)); assertNotNull(shadeList); assertEquals(2, shadeList.size()); @@ -194,7 +206,7 @@ public void testShadeHandlerPropertiesAndChannels() throws IOException { /* * Use the first JSON Shade entry. - * It should support the full 6 dynamic channels. + * It should support 4 dynamic channels. */ shadeThingHandler = new ShadeThingHandler(thing); shadeThingHandler.setCallback(mock(ThingHandlerCallback.class)); @@ -202,26 +214,15 @@ public void testShadeHandlerPropertiesAndChannels() throws IOException { assertTrue(shadeData.hasFullState()); shadeThingHandler.notify(shadeData); Thing handlerThing = shadeThingHandler.getThing(); - assertEquals("23", handlerThing.getProperties().get("type")); - assertEquals("wand", handlerThing.getProperties().get("powerType")); - assertEquals("3.0.309", handlerThing.getProperties().get("firmwareVersion")); - assertEquals(new DecimalType(-55), shadeData.getSignalStrength()); - assertEquals(6, handlerThing.getChannels().size()); - - /* - * Reuse the first JSON Shade entry and delete its 3 positions. - * So it should now only support 3 dynamic channels. - */ - shadeThingHandler = new ShadeThingHandler(thing); - shadeThingHandler.setCallback(mock(ThingHandlerCallback.class)); - shadeData.setShadePosition(new ShadePosition()); - shadeThingHandler.notify(shadeData); - handlerThing = shadeThingHandler.getThing(); - assertEquals(3, handlerThing.getChannels().size()); + assertEquals("Duette (6)", handlerThing.getProperties().get("type")); + assertEquals("battery", handlerThing.getProperties().get("powerType")); + assertEquals("3.0.359", handlerThing.getProperties().get("firmwareVersion")); + assertEquals(new DecimalType(-50), shadeData.getSignalStrength()); + assertEquals(4, handlerThing.getChannels().size()); /* * Use the second JSON Shade entry. - * It should support only 2 dynamic channel. + * It should support only 3 dynamic channels. */ shadeThingHandler = new ShadeThingHandler(thing); shadeThingHandler.setCallback(mock(ThingHandlerCallback.class)); @@ -229,11 +230,11 @@ public void testShadeHandlerPropertiesAndChannels() throws IOException { assertTrue(shadeData.hasFullState()); shadeThingHandler.notify(shadeData); handlerThing = shadeThingHandler.getThing(); - assertEquals("44", handlerThing.getProperties().get("type")); + assertEquals("Silhouette (23)", handlerThing.getProperties().get("type")); assertEquals("hardwired", handlerThing.getProperties().get("powerType")); - assertEquals("3.0.123", handlerThing.getProperties().get("firmwareVersion")); - assertEquals(new DecimalType(-44), shadeData.getSignalStrength()); - assertEquals(2, handlerThing.getChannels().size()); + assertEquals("3.0.359", handlerThing.getProperties().get("firmwareVersion")); + assertEquals(new DecimalType(-51), shadeData.getSignalStrength()); + assertEquals(3, handlerThing.getChannels().size()); } /** @@ -252,7 +253,7 @@ public void testShadeHandlerEvents() throws IOException { } String json = loadJson("gen3/shades.json"); - List shadeList = gson.fromJson(json, GatewayWebTargets.LIST_SHADES); + List shadeList = List.of(gson.fromJson(json, Shade[].class)); assertNotNull(shadeList); assertEquals(2, shadeList.size()); diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json index 4950a28c4cc78..bd40c2ddb5122 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json +++ b/bundles/org.openhab.binding.hdpowerview/src/test/resources/org/openhab/binding/hdpowerview/internal/gen3/shades.json @@ -1,46 +1,54 @@ [ - { - "id": 789, - "type": 23, - "name": "U2hhZGUgMg==", - "ptName": "ABC", - "capabilities": 0, - "powerType": "wand", - "batteryStatus": 2, - "roomId": 6833, - "signalStrength": -55, - "bleName": "PIR:5933", - "firmware": { - "revision": 3, - "subRevision": 0, - "build": 309 + { + "id": 2, + "type": 6, + "name": "VXBwZXIgTGVmdA==", + "ptName": "Upper Left", + "motion": null, + "capabilities": 0, + "powerType": 0, + "batteryStatus": 2, + "roomId": 1, + "firmware": { + "revision": 3, + "subRevision": 0, + "build": 359 + }, + "positions": { + "primary": 0.5, + "secondary": 0, + "tilt": 0, + "velocity": 0 + }, + "signalStrength": -50, + "bleName": "DUE:1444", + "shadeGroupIds": [ + ] }, - "positions": { - "primary": 0.99, - "secondary": 0.99, - "tilt": 0, - "velocity": 0.5 + { + "id": 3, + "type": 23, + "name": "VXBwZXIgUmlnaHQ=", + "ptName": "Upper Right", + "motion": null, + "capabilities": 1, + "powerType": 1, + "batteryStatus": 3, + "roomId": 1, + "firmware": { + "revision": 3, + "subRevision": 0, + "build": 359 + }, + "positions": { + "primary": 0, + "secondary": 0, + "tilt": 0, + "velocity": 0 + }, + "signalStrength": -51, + "bleName": "SIL:5AA5", + "shadeGroupIds": [ + ] } - }, - { - "id": 123, - "type": 44, - "name": "U2hhZGUgMg==", - "ptName": "ABC", - "capabilities": 0, - "powerType": "hardwired", - "batteryStatus": 2, - "roomId": 6833, - "signalStrength": -44, - "bleName": "PIR:5933", - "firmware": { - "revision": 3, - "subRevision": 0, - "build": 123 - }, - "positions": { - "primary": 0.99, - "velocity": 0.5 - } - } ] From d125de150cdf615d524503100044df6ddf01061c Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 20 May 2023 18:58:37 +0100 Subject: [PATCH 55/64] [hdpowerview] tweaks Signed-off-by: Andrew Fiddian-Green --- .../internal/handler/ShadeThingHandler.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index ffdc6d51cbdd9..5f4ada55d3f36 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.List; import java.util.StringJoiner; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -150,7 +149,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; } else if (command instanceof UpDownType) { position.setPosition(SECONDARY_POSITION, - UpDownType.UP == command ? PercentType.ZERO : PercentType.HUNDRED); + (UpDownType.UP == command) && !getCapabilities(thisShade).supportsSecondaryOverlapped() + ? PercentType.ZERO + : PercentType.HUNDRED); webTargets.moveShade(shadeId, new Shade().setShadePosition(position)); break; } else if (StopMoveType.STOP == command) { @@ -205,8 +206,7 @@ public void initialize() { } isInitialized = false; updateStatus(ThingStatus.UNKNOWN); - scheduler.schedule(() -> ((GatewayBridgeHandler) bridgeHandler).refreshShade(thisShade.getId()), 5, - TimeUnit.SECONDS); + scheduler.submit(() -> ((GatewayBridgeHandler) bridgeHandler).refreshShade(thisShade.getId())); } /** @@ -279,7 +279,7 @@ private void updateDynamicChannels(Shade shade) { updateDynamicChannel(removeChannels, CHANNEL_SHADE_POSITION, capabilities.supportsPrimary()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_SECONDARY_POSITION, capabilities.supportsSecondary()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_VANE, capabilities.supportsTilt180() - || capabilities.supportsTiltAnywhere() | capabilities.supportsTiltOnClosed()); + || capabilities.supportsTiltAnywhere() || capabilities.supportsTiltOnClosed()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_BATTERY_LEVEL, !shade.isMainsPowered()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_LOW_BATTERY, !shade.isMainsPowered()); From eca336e03e372ce72286c55e1a87bceb4f00b7c1 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sat, 20 May 2023 19:22:27 +0100 Subject: [PATCH 56/64] [hdpowerview] thing xml and i18n Signed-off-by: Andrew Fiddian-Green --- .../src/main/resources/OH-INF/config/config.xml | 7 +++++++ .../main/resources/OH-INF/i18n/hdpowerview.properties | 10 ++++++---- .../src/main/resources/OH-INF/thing/bridge.xml | 2 +- .../src/main/resources/OH-INF/thing/shade.xml | 6 +++--- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/config/config.xml index 9945d63e8e68f..a655fa3493de8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/config/config.xml @@ -18,4 +18,11 @@ + + + + The numeric ID of the PowerView Shade in the Gateway + + + diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index 3f3e3df9bd2e9..9d0205720b1c6 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -14,7 +14,7 @@ thing-type.hdpowerview.repeater.description = Hunter Douglas (Luxaflex) PowerVie thing-type.hdpowerview.repeater.channel.brightness.description = Controls the brightness of the LED ring thing-type.hdpowerview.repeater.channel.color.description = Controls the color of the LED ring thing-type.hdpowerview.shade.label = PowerView Shade -thing-type.hdpowerview.shade.description = Hunter Douglas (Luxaflex) PowerView Shade +thing-type.hdpowerview.shade.description = Hunter Douglas (Luxaflex) PowerView Gen 1/2 Shade thing-type.hdpowerview.shade.channel.hubRssi.label = Hub RSSI thing-type.hdpowerview.shade.channel.hubRssi.description = Received Signal Strength Indicator for Hub thing-type.hdpowerview.shade.channel.repeaterRssi.label = Repeater RSSI @@ -22,7 +22,7 @@ thing-type.hdpowerview.shade.channel.repeaterRssi.description = Received Signal thing-type.hdpowerview.shade.channel.secondary.label = Secondary Position thing-type.hdpowerview.shade.channel.secondary.description = The secondary vertical position (on top-down/bottom-up shades) thing-type.hdpowerview.shade3.label = PowerView Shade -thing-type.hdpowerview.shade3.description = Hunter Douglas (Luxaflex) PowerView Shade +thing-type.hdpowerview.shade3.description = Hunter Douglas (Luxaflex) PowerView Gen3 Shade thing-type.hdpowerview.shade3.channel.secondary.label = Secondary Position thing-type.hdpowerview.shade3.channel.secondary.description = The secondary vertical position (on top-down/bottom-up shades) @@ -31,7 +31,7 @@ thing-type.hdpowerview.shade3.channel.secondary.description = The secondary vert thing-type.config.hdpowerview.gateway.hardRefresh.label = Hard Refresh Interval thing-type.config.hdpowerview.gateway.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Gateway thing-type.config.hdpowerview.gateway.host.label = Host -thing-type.config.hdpowerview.gateway.host.description = The Host address of the PowerView Hub +thing-type.config.hdpowerview.gateway.host.description = The Host address of the PowerView Gateway thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval @@ -43,7 +43,9 @@ thing-type.config.hdpowerview.hub.refresh.description = The number of millisecon thing-type.config.hdpowerview.repeater.id.label = ID thing-type.config.hdpowerview.repeater.id.description = The numeric ID of the PowerView Repeater in the Hub thing-type.config.hdpowerview.shade.id.label = ID -thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Gateway/Hub +thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Hub +thing-type.config.hdpowerview.shade3.id.label = ID +thing-type.config.hdpowerview.shade3.id.description = The numeric ID of the PowerView Shade in the Gateway # channel group types diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index 08a7f28890a5f..f0cd3aab798cd 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -70,7 +70,7 @@ - The number of minutes between hard refreshes from the PowerView Gateway + The number of minutes between hard refreshes of the PowerView Gateway 180 diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml index 2c46638868a42..968faa3b3c719 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml @@ -9,7 +9,7 @@ - Hunter Douglas (Luxaflex) PowerView Shade + Hunter Douglas (Luxaflex) PowerView Gen 1/2 Shade @@ -48,7 +48,7 @@ - Hunter Douglas (Luxaflex) PowerView Shade + Hunter Douglas (Luxaflex) PowerView Gen3 Shade @@ -70,7 +70,7 @@ id - + From 2a484ab823676deb6340f3ab8240067bf37e7c3f Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 21 May 2023 16:13:33 +0100 Subject: [PATCH 57/64] [hdpowerview] uom, thing xml, start up, and documentation Signed-off-by: Andrew Fiddian-Green --- .../org.openhab.binding.hdpowerview/README.md | 120 ++++++++--------- .../hdpowerview/internal/dto/gen3/Shade.java | 7 +- .../handler/GatewayBridgeHandler.java | 16 ++- .../internal/handler/ShadeThingHandler.java | 122 ++++++++++-------- .../OH-INF/i18n/hdpowerview.properties | 5 + .../main/resources/OH-INF/thing/channels.xml | 20 +++ .../src/main/resources/OH-INF/thing/shade.xml | 4 +- .../internal/gen3/Generation3DtoTest.java | 8 +- 8 files changed, 177 insertions(+), 125 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/README.md b/bundles/org.openhab.binding.hdpowerview/README.md index 08274c5c29a52..e50c297e1cb69 100644 --- a/bundles/org.openhab.binding.hdpowerview/README.md +++ b/bundles/org.openhab.binding.hdpowerview/README.md @@ -19,15 +19,16 @@ By using a scene to control multiple shades at once, the shades will all begin m ## Supported Things -| Thing | Thing Type | Description | -|--------------------------|------------|-------------| -| hub[1/2] | Bridge | The PowerView hub provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. | -| shade[1/2] | Thing | A motorized shade. | -| repeater[1/2] | Thing | A PowerView signal repeater. | -| gateway[3] | Bridge | Powerview Generation 3 gateway that provides the interface between your network and the shade's radio network. It also contains channels used to interact with scenes. | -| shade3[3] | Thing | A Powerview Generation 3 motorized shade. | +| Thing | Thing Type | Description | +|--------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------| +| hub[1/2] | Bridge | The PowerView hub interfaces between the LAN and the shade's radio network. Contains channels to interact with scenes. | +| shade[1/2] | Thing | A motorized shade. | +| repeater[1/2] | Thing | A PowerView signal repeater. | +| gateway[3] | Bridge | Generation 3 gateway interfaces between the LAN and the shade's Bluetooth network. Contains channels to interact with scenes. | +| shade3[3] | Thing | A Powerview Generation 3 motorized shade. | -[1/2] Generation 1/2 hubs only. [3] Generation 3 gateways only. +[1/2] Generation 1/2 hubs only. +[3] Generation 3 gateways only. ## Discovery @@ -55,12 +56,12 @@ If in the future, you add additional shades, scenes, repeaters, scene groups or ### Thing Configuration for PowerView Hub / Gateway (Thing type `hub`[1/2] or `gateway`[3]) -| Configuration Parameter | Description | -|-----------------------------------------|---------------| -| host | The host name or IP address of the hub on your network. | -| refresh[1/2] | The number of milli-seconds between fetches of the PowerView hub's shade state (default 60'000 one minute). | -| hardRefresh | The number of minutes between hard refreshes of the PowerView hub's shade state (default 180 three hours). See [Hard Refreshes](#hard-refreshes). | -| hardRefreshBatteryLevel[1/2] | The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, defaulting to weekly). See [Hard Refreshes](#hard-refreshes). | +| Configuration Parameter | Description | +|-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------| +| host | The host name or IP address of the hub on your network. | +| refresh[1/2] | The number of milli-seconds between fetches of the PowerView hub's shade state (default 60'000 one minute). | +| hardRefresh | The number of minutes between hard refreshes of the PowerView hub's shade state (default 180 three hours). See [Hard Refreshes](#hard-refreshes). | +| hardRefreshBatteryLevel[1/2] | The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, defaulting to weekly). See [Hard Refreshes](#hard-refreshes). | [1/2] Generation 1/2 hubs only. @@ -72,14 +73,14 @@ This can be used for the `id` parameters described below. #### Thing Configuration for PowerView Shades -| Configuration Parameter | Description | -|-------------------------|-------------| +| Configuration Parameter | Description | +|-------------------------|---------------------------------------------------------------| | id | The ID of the PowerView shade in the app. Must be an integer. | #### Thing Configuration for PowerView Repeaters[1/2] -| Configuration Parameter | Description | -|-------------------------|-------------| +| Configuration Parameter | Description | +|-------------------------|------------------------------------------------------------------| | id | The ID of the PowerView repeater in the app. Must be an integer. | ## Channels @@ -89,11 +90,11 @@ This can be used for the `id` parameters described below. Scene, scene group and automation channels will be added dynamically to the binding as they are discovered in the hub. Each will have an entry in the hub as shown below, whereby different scenes, scene groups and automations have different `id` values: -| Channel Group | Channel | Item Type | Description | -|-----------------------------|---------|-----------|-------------| -| scenes | id | Switch | Setting this to ON will activate the scene. Scenes are stateless in the PowerView hub; they have no on/off state. | -| sceneGroups[1/2] | id | Switch | Setting this to ON will activate the scene group. Scene groups are stateless in the PowerView hub; they have no on/off state. | -| automations[1/2] | id | Switch | Setting this to ON will enable the automation, while OFF will disable it. | +| Channel Group | Channel | Item Type | Description | +|-----------------------------|---------|-----------|------------------------------------------------------------------------------------------------------------------------| +| scenes | id | Switch | Setting this to ON will activate the scene. Scenes are stateless in the PowerView hub; they have no on/off state. | +| sceneGroups[1/2] | id | Switch | Setting this to ON will activate the scene group. Scene groups are stateless in the PowerView hub; they have no on/off state. | +| automations[1/2] | id | Switch | Setting this to ON will enable the automation, while OFF will disable it. | [1/2] Generation 1/2 hubs only. @@ -104,36 +105,39 @@ If the shade has slats or rotatable vanes, there is also a dimmer channel `vane` If it is a dual action (top-down plus bottom-up) shade, there is also a roller shutter channel `secondary` which controls the vertical position of the secondary rail. All of these channels appear in the binding, but only those which have a physical implementation in the shade, will have any physical effect. -| Channel | Item Type | Description | -|--------------------------------|--------------------------|-------------| -| position | Rollershutter | The vertical position of the shade's rail (if any). -- See [next chapter](#roller-shutter-updown-position-vs-openclose-state). Up/Down commands will move the rail completely up or completely down. Percentage commands will move the rail to an intermediate position. Stop commands will halt any current movement of the rail. | -| secondary | Rollershutter | The vertical position of the secondary rail (if any). Its function is similar to the `position` channel above. -- But see [next chapter](#roller-shutter-updown-position-vs-openclose-state). | -| vane | Dimmer | The degree of opening of the slats or vanes (if any). On some shade types, setting this to a non-zero value might first move the shade `position` fully down, since the slats or vanes can only have a defined state if the shade is in its down position. See [Interdependency between Channel positions](#interdependency-between-channel-positions). | -| command | String | Send a command to the shade. Valid values are: `CALIBRATE`, `IDENTIFY` | -| lowBattery | Switch | Indicates ON when the battery level of the shade is low, as determined by the hub's internal rules. | -| batteryLevel | Number | Battery level (10% = low, 50% = medium, 100% = high) | -| batteryVoltage[1/2] | Number:ElectricPotential | Battery (resp. mains power supply) voltage reported by the shade. | -| signalStrength | Number | Signal strength (0 for no or unknown signal, 1 for weak, 2 for average, 3 for good or 4 for excellent) | -| hubRssi[1/2] | Number:Power | Received Signal Strength Indicator for Hub | -| repeaterRssi[1/2] | Number:Power | Received Signal Strength Indicator for Repeater | +| Channel | Item Type | Description | +|--------------------------------|--------------------------|----------------------------------------------------------------------------------------------------------------| +| position | Rollershutter | The vertical position of the shade's rail (if any). -- See [next chapter](#roller-shutter-updown-position-vs-openclose-state). Up/Down commands will move the rail completely up or completely down. Percentage commands will move the rail to an intermediate position. Stop commands will halt any current movement of the rail. | +| secondary | Rollershutter | The vertical position of the secondary rail (if any). Its function is similar to the `position` channel above. -- But see [next chapter](#roller-shutter-updown-position-vs-openclose-state). | +| vane | Dimmer | The degree of opening of the slats or vanes (if any). On some shade types, setting this to a non-zero value might first move the shade `position` fully down, since the slats or vanes can only have a defined state if the shade is in its down position. See [Interdependency between Channel positions](#interdependency-between-channel-positions). | +| command | String | Advanced: Send a command to the shade. | +| lowBattery | Switch | Indicates ON when the battery level of the shade is low, as determined by the hub's internal rules. | +| batteryLevel | Number | Battery level (10% = low, 50% = medium, 100% = high) | +| batteryVoltage[1/2] | Number:ElectricPotential | Battery (resp. mains power supply) voltage reported by the shade. | +| signalStrength | Number / Number:Power | Shade radio signal strength. | +| hubRssi[1/2] | Number:Power | Received Signal Strength Indicator for Hub. | +| repeaterRssi[1/2] | Number:Power | Received Signal Strength Indicator for Repeater. | Notes: - The channels `position`, `secondary` and `vane` exist if the shade physically supports such channels. - The shade's Power Option is set via the PowerView app with possible values 'Battery Wand', 'Rechargeable Battery Wand' or 'Hardwired Power Supply'. The channels `lowBattery` and `batteryLevel` exist if you have _not_ selected 'Hardwired Power Supply' in the app. +- On Generation 1/2 hubs, the valid values for the `command` channel are: `CALIBRATE`, `IDENTIFY`. +On Generation 3 gateways, the only valid value is: `IDENTIFY`. +- On Generation 1/2 hubs the signal strength is 0 for no or unknown signal, 1 for weak, 2 for average, 3 for good, or 4 for excellent. +On Generation 3 gateways the signal strength is displayed in dBm (deciBel-milliWatt). - [1/2] The RSSI values will only be updated upon manual request by a `REFRESH` command (e.g. in a rule). - -[1/2] Generation 1/2 hubs only. +- [1/2] Repeaters are supported on Generation 1/2 hubs only. ### Channels for Repeaters (Thing type `repeater`)[1/2] -| Channel | Item Type | Description | -|-----------------|-----------|-------------------------------| +| Channel | Item Type | Description | +|-----------------|-----------|---------------------------------------------------------------------------------------------| | color | Color | Controls the color of the LED ring. A switch item can be linked: ON = white, OFF = turn off | -| brightness | Dimmer | Controls the brightness of the LED ring. | -| identify | String | Flash repeater to identify. Valid values are: `IDENTIFY` | -| blinkingEnabled | Switch | Blink during commands. | +| brightness | Dimmer | Controls the brightness of the LED ring. | +| identify | String | Flash repeater to identify. Valid values are: `IDENTIFY` | +| blinkingEnabled | Switch | Blink during commands. | ### Roller Shutter Up/Down Position vs. Open/Close State @@ -143,22 +147,22 @@ And for horizontal shades, it maps the horizontal position of the "truck" to the Depending on whether the shade is a top-down, bottom-up, left-right, right-left, dual action shade, or, a shade with a secondary blackout panel, the `OPEN` and `CLOSED` position of the shades may differ from the ▲ / ▼ commands follows.. -| Type of Shade | Channel | Rollershutter Command | Motion direction | Shade State | Percent | Pebble Remote Button | -|-----------------------------|-------------------|-----------------------|------------------|----------------|-------------------|----------------------| -| Single action
bottom-up | `position` | ▲ | Up | `OPEN` | 0% | ▲ | -| | | ▼ | Down | `CLOSED` | 100% | ▼ | -| Single action
top-down | `position` | ▲ | Up | **`CLOSED`** | 0% | ▲ | -| | | ▼ | Down | **`OPEN`** | 100% | ▼ | -| Single action
right-left | `position` | ▲ | _**Left**_ | `OPEN` | 0% | ▲ | -| | | ▼ | _**Right**_ | `CLOSED` | 100% | ▼ | -| Single action
left-right | `position` | ▲ | _**Right**_ | `OPEN` | 0% | ▲ | -| | | ▼ | _**Left**_ | `CLOSED` | 100% | ▼ | -| Dual action
(lower rail) | `position` | ▲ | Up | `OPEN` | 0% | ▲ | -| | | ▼ | Down | `CLOSED` | 100% | ▼ | -| Dual action
(upper rail) | _**`secondary`**_ | ▲ | Up | **`CLOSED`** | 0%1) | ![dual_action arrow_right](doc/right.png) | -| | | ▼ | Down | **`OPEN`** | 100%1) | ![dual_action arrow_left](doc/left.png) | -| Blackout panel ('DuoLite') | _**`secondary`**_ | ▲ | Up | `OPEN` | 0% | ▲ | -| | | ▼ | Down | `CLOSED` | 100% | ▼ | +| Type of Shade | Channel | Rollershutter Command | Motion direction | Shade State | Percent | Pebble Remote Button | +|-----------------------------|-------------------|-----------------------|------------------|----------------|-------------------|--------------------------------------------| +| Single action
bottom-up | `position` | ▲ | Up | `OPEN` | 0% | ▲ | +| | | ▼ | Down | `CLOSED` | 100% | ▼ | +| Single action
top-down | `position` | ▲ | Up | **`CLOSED`** | 0% | ▲ | +| | | ▼ | Down | **`OPEN`** | 100% | ▼ | +| Single action
right-left | `position` | ▲ | _**Left**_ | `OPEN` | 0% | ▲ | +| | | ▼ | _**Right**_ | `CLOSED` | 100% | ▼ | +| Single action
left-right | `position` | ▲ | _**Right**_ | `OPEN` | 0% | ▲ | +| | | ▼ | _**Left**_ | `CLOSED` | 100% | ▼ | +| Dual action
(lower rail) | `position` | ▲ | Up | `OPEN` | 0% | ▲ | +| | | ▼ | Down | `CLOSED` | 100% | ▼ | +| Dual action
(upper rail) | _**`secondary`**_ | ▲ | Up | **`CLOSED`** | 0%1) | ![dual_action arrow_right](doc/right.png) | +| | | ▼ | Down | **`OPEN`** | 100%1) | ![dual_action arrow_left](doc/left.png) | +| Blackout panel ('DuoLite') | _**`secondary`**_ | ▲ | Up | `OPEN` | 0% | ▲ | +| | | ▼ | Down | `CLOSED` | 100% | ▼ | _**1) BUG NOTE**_: In openHAB versions v3.1.x and earlier, there was a bug in the handling of the position percent value of the `secondary` shade. Although the RollerShutter Up/Down commands functioned properly as described in the table above, the percent state values (e.g. displayed on a slider control), did not. diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java index c2c237c2e661a..4658297520ae3 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java @@ -22,6 +22,8 @@ import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; @@ -82,8 +84,7 @@ public State getLowBattery() { } public String getName() { - return String.join(" ", new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8), ptName) - .replaceAll("\n", "").replaceAll("\r", ""); + return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8); } public State getPosition(CoordinateSystem posKindCoords) { @@ -102,7 +103,7 @@ public PowerType getPowerType() { public State getSignalStrength() { Integer signalStrength = this.signalStrength; - return signalStrength == null ? UnDefType.UNDEF : new DecimalType(signalStrength); + return signalStrength != null ? new QuantityType<>(signalStrength, Units.DECIBEL_MILLIWATTS) : UnDefType.UNDEF; } public @Nullable Integer getType() { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index dd898cbd15d51..c3969388520d8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -38,9 +38,11 @@ import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandler; import org.openhab.core.thing.binding.builder.ChannelBuilder; import org.openhab.core.thing.type.AutoUpdatePolicy; import org.openhab.core.thing.type.ChannelTypeUID; @@ -84,6 +86,13 @@ public GatewayBridgeHandler(Bridge bridge, HttpClient httpClient, this.eventSourceFactory = eventSourceFactory; } + @Override + public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { + if (childHandler instanceof ShadeThingHandler shadeHandler) { + refreshShade(shadeHandler.getShadeId()); + } + } + @Override public void dispose() { isDisposing = true; @@ -137,9 +146,8 @@ public ScheduledExecutorService getScheduler() { */ private List getShadeThingHandlers() throws IllegalStateException { logger.debug("getShadeThingHandlers()"); - return getThing().getThings().stream().map(thing -> thing.getHandler()) - .filter(handler -> (handler instanceof ShadeThingHandler)).map(handler -> (ShadeThingHandler) handler) - .collect(Collectors.toList()); + return getThing().getThings().stream().map(Thing::getHandler).filter(ShadeThingHandler.class::isInstance) + .map(ShadeThingHandler.class::cast).collect(Collectors.toList()); } /** @@ -277,7 +285,7 @@ private void refreshScenes() throws HubProcessingException, IllegalStateExceptio * * @param shadeId the id of the shade to be refreshed. */ - public void refreshShade(int shadeId) { + private void refreshShade(int shadeId) { try { Shade shade = getWebTargets().getShade(shadeId); for (ShadeThingHandler handler : getShadeThingHandlers()) { diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index 5f4ada55d3f36..c325abf1b2ceb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.StringJoiner; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -47,6 +48,7 @@ import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,8 +68,8 @@ public class ShadeThingHandler extends BaseThingHandler { private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); private final Logger logger = LoggerFactory.getLogger(ShadeThingHandler.class); - private final Shade thisShade = new Shade(); + private int shadeId; private boolean isInitialized; private @Nullable Capabilities capabilities; @@ -75,27 +77,13 @@ public ShadeThingHandler(Thing thing) { super(thing); } - @Override - public void dispose() { - // TODO Auto-generated method stub - } - - private Capabilities getCapabilities(Shade shade) { - Capabilities capabilities = this.capabilities; - if (capabilities == null) { - capabilities = DB.getCapabilities(shade.getCapabilities()); - this.capabilities = capabilities; - } - return capabilities; - } - /** * Getter for the hub handler. * * @return the hub handler. * @throws IllegalStateException if the bridge or its handler are not initialized. */ - private GatewayBridgeHandler getHandler() throws IllegalStateException { + private GatewayBridgeHandler getBridgeHandler() throws IllegalStateException { Bridge bridge = this.getBridge(); if (bridge == null) { throw new IllegalStateException("Bridge not initialised."); @@ -107,6 +95,10 @@ private GatewayBridgeHandler getHandler() throws IllegalStateException { return (GatewayBridgeHandler) handler; } + public int getShadeId() { + return shadeId; + } + private Type getType(Shade shade) { Integer type = shade.getType(); return type != null ? DB.getType(type) : new ShadeCapabilitiesDatabase.Type(0); @@ -115,13 +107,12 @@ private Type getType(Shade shade) { @Override public void handleCommand(ChannelUID channelUID, Command command) { if (RefreshType.REFRESH == command) { - getHandler().handleCommand(channelUID, command); + getBridgeHandler().handleCommand(channelUID, command); return; } - GatewayWebTargets webTargets = getHandler().getWebTargets(); + GatewayWebTargets webTargets = getBridgeHandler().getWebTargets(); ShadePosition position = new ShadePosition(); - int shadeId = thisShade.getId(); try { switch (channelUID.getId()) { case CHANNEL_SHADE_POSITION: @@ -131,7 +122,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; } else if (command instanceof UpDownType) { position.setPosition(PRIMARY_POSITION, - (UpDownType.UP == command) && !getCapabilities(thisShade).isPrimaryInverted() + (UpDownType.UP == command) && !Objects.requireNonNull(capabilities).isPrimaryInverted() ? PercentType.HUNDRED : PercentType.ZERO); webTargets.moveShade(shadeId, new Shade().setShadePosition(position)); @@ -149,9 +140,10 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; } else if (command instanceof UpDownType) { position.setPosition(SECONDARY_POSITION, - (UpDownType.UP == command) && !getCapabilities(thisShade).supportsSecondaryOverlapped() - ? PercentType.ZERO - : PercentType.HUNDRED); + (UpDownType.UP == command) + && !Objects.requireNonNull(capabilities).supportsSecondaryOverlapped() + ? PercentType.ZERO + : PercentType.HUNDRED); webTargets.moveShade(shadeId, new Shade().setShadePosition(position)); break; } else if (StopMoveType.STOP == command) { @@ -194,9 +186,24 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } + private boolean hasPrimary() { + return Objects.requireNonNull(capabilities).supportsPrimary(); + } + + private boolean hasSecondary() { + Capabilities capabilities = Objects.requireNonNull(this.capabilities); + return capabilities.supportsSecondary() || capabilities.supportsSecondaryOverlapped(); + } + + private boolean hasVane() { + Capabilities capabilities = Objects.requireNonNull(this.capabilities); + return capabilities.supportsTilt180() || capabilities.supportsTiltAnywhere() + || capabilities.supportsTiltOnClosed(); + } + @Override public void initialize() { - thisShade.setId(getConfigAs(HDPowerViewShadeConfiguration.class).id); + shadeId = getConfigAs(HDPowerViewShadeConfiguration.class).id; Bridge bridge = getBridge(); BridgeHandler bridgeHandler = bridge != null ? bridge.getHandler() : null; if (!(bridgeHandler instanceof GatewayBridgeHandler)) { @@ -206,7 +213,6 @@ public void initialize() { } isInitialized = false; updateStatus(ThingStatus.UNKNOWN); - scheduler.submit(() -> ((GatewayBridgeHandler) bridgeHandler).refreshShade(thisShade.getId())); } /** @@ -216,32 +222,43 @@ public void initialize() { * @return true if we handled the call. */ public boolean notify(Shade shade) { - if (thisShade.getId() == shade.getId()) { + if (shadeId == shade.getId()) { updateStatus(ThingStatus.ONLINE); - if (!isInitialized) { - ShadePosition position = shade.getShadePositions(); - if (position != null) { - thisShade.setShadePosition(position); - } + if (!isInitialized && shade.hasFullState()) { + updateCapabilities(shade); updateProperties(shade); updateDynamicChannels(shade); + isInitialized = true; } - isInitialized = true; updateChannels(shade); return true; } return false; } + /** + * Update the capabilities object based on the data in the passed shade instance. + * + * @param shade containing the channel data. + */ + private void updateCapabilities(Shade shade) { + Capabilities capabilities = this.capabilities; + if (capabilities == null) { + capabilities = DB.getCapabilities(shade.getCapabilities()); + this.capabilities = capabilities; + } + } + /** * Update channels based on the data in the passed shade instance. * * @param shade containing the channel data. */ private void updateChannels(Shade shade) { - updateState(CHANNEL_SHADE_POSITION, shade.getPosition(PRIMARY_POSITION)); - updateState(CHANNEL_SHADE_VANE, shade.getPosition(VANE_TILT_POSITION)); - updateState(CHANNEL_SHADE_SECONDARY_POSITION, shade.getPosition(SECONDARY_POSITION)); + updateState(CHANNEL_SHADE_POSITION, hasPrimary() ? shade.getPosition(PRIMARY_POSITION) : UnDefType.UNDEF); + updateState(CHANNEL_SHADE_VANE, hasVane() ? shade.getPosition(VANE_TILT_POSITION) : UnDefType.UNDEF); + updateState(CHANNEL_SHADE_SECONDARY_POSITION, + hasSecondary() ? shade.getPosition(SECONDARY_POSITION) : UnDefType.UNDEF); if (shade.hasFullState()) { updateState(CHANNEL_SHADE_LOW_BATTERY, shade.getLowBattery()); updateState(CHANNEL_SHADE_BATTERY_LEVEL, shade.getBatteryLevel()); @@ -262,8 +279,8 @@ private void updateDynamicChannel(List removeList, String channelId, bo if (!channelRequired && channel != null) { removeList.add(channel); } else if (channelRequired && channel == null) { - logger.warn("updateDynamicChannel() shadeId:{} is missing channel:{} => please recreate the thing", - thisShade.getId(), channelId); + logger.warn("updateDynamicChannel() shadeId:{} is missing channel:{} => please recreate the thing", shadeId, + channelId); } } @@ -274,20 +291,16 @@ private void updateDynamicChannel(List removeList, String channelId, bo */ private void updateDynamicChannels(Shade shade) { List removeChannels = new ArrayList<>(); - - Capabilities capabilities = getCapabilities(shade); - updateDynamicChannel(removeChannels, CHANNEL_SHADE_POSITION, capabilities.supportsPrimary()); - updateDynamicChannel(removeChannels, CHANNEL_SHADE_SECONDARY_POSITION, capabilities.supportsSecondary()); - updateDynamicChannel(removeChannels, CHANNEL_SHADE_VANE, capabilities.supportsTilt180() - || capabilities.supportsTiltAnywhere() || capabilities.supportsTiltOnClosed()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_POSITION, hasPrimary()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_SECONDARY_POSITION, hasSecondary()); + updateDynamicChannel(removeChannels, CHANNEL_SHADE_VANE, hasVane()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_BATTERY_LEVEL, !shade.isMainsPowered()); updateDynamicChannel(removeChannels, CHANNEL_SHADE_LOW_BATTERY, !shade.isMainsPowered()); - if (!removeChannels.isEmpty()) { if (logger.isDebugEnabled()) { StringJoiner joiner = new StringJoiner(", "); removeChannels.forEach(c -> joiner.add(c.getUID().getId())); - logger.debug("updateDynamicChannels() shadeId:{}, removing unsupported channels:{}", thisShade.getId(), + logger.debug("updateDynamicChannels() shadeId:{}, removing unsupported channels:{}", shadeId, joiner.toString()); } updateThing(editThing().withoutChannels(removeChannels).build()); @@ -300,16 +313,15 @@ private void updateDynamicChannels(Shade shade) { * @param shade containing the property data. */ private void updateProperties(Shade shade) { - if (shade.hasFullState()) { - thing.setProperties(Stream.of(new String[][] { // - { HDPowerViewBindingConstants.PROPERTY_NAME, shade.getName() }, - { HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, getType(shade).toString() }, - { HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES, getCapabilities(shade).toString() }, - { HDPowerViewBindingConstants.PROPERTY_POWER_TYPE, shade.getPowerType().name().toLowerCase() }, - { HDPowerViewBindingConstants.PROPERTY_BLE_NAME, shade.getBleName() }, - { Thing.PROPERTY_FIRMWARE_VERSION, shade.getFirmware() } // - }).collect(Collectors.toMap(data -> data[0], data -> data[1]))); - } + thing.setProperties(Stream.of(new String[][] { // + { HDPowerViewBindingConstants.PROPERTY_NAME, shade.getName() }, + { HDPowerViewBindingConstants.PROPERTY_SHADE_TYPE, getType(shade).toString() }, + { HDPowerViewBindingConstants.PROPERTY_SHADE_CAPABILITIES, + Objects.requireNonNull(capabilities).toString() }, + { HDPowerViewBindingConstants.PROPERTY_POWER_TYPE, shade.getPowerType().name().toLowerCase() }, + { HDPowerViewBindingConstants.PROPERTY_BLE_NAME, shade.getBleName() }, + { Thing.PROPERTY_FIRMWARE_VERSION, shade.getFirmware() } // + }).collect(Collectors.toMap(data -> data[0], data -> data[1]))); } /** diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index 9d0205720b1c6..f30b489a90603 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -58,6 +58,8 @@ channel-group-type.hdpowerview.scenes.label = Scenes channel-type.hdpowerview.automation-enabled.label = Enable channel-type.hdpowerview.battery-voltage.label = Battery Voltage channel-type.hdpowerview.battery-voltage.description = Battery voltage reported by the shade +channel-type.hdpowerview.ble-signal-strength.label = Signal strength +channel-type.hdpowerview.ble-signal-strength.description = Signal strength of Bluetooth Low Energy communication channel-type.hdpowerview.repeater-blinking-enabled.label = Blinking Enabled channel-type.hdpowerview.repeater-blinking-enabled.description = Blink during commands channel-type.hdpowerview.repeater-identify.label = Identify @@ -75,6 +77,9 @@ channel-type.hdpowerview.shade-position.label = Position channel-type.hdpowerview.shade-position.description = The vertical position of the shade channel-type.hdpowerview.shade-vane.label = Vane channel-type.hdpowerview.shade-vane.description = The opening of the slats in the shade +channel-type.hdpowerview.shade3-command.label = Command +channel-type.hdpowerview.shade3-command.description = Send a command to identify the shade +channel-type.hdpowerview.shade3-command.command.option.IDENTIFY = Identify # thing status descriptions diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml index 914bedbb35ae5..6e37c0dc2ee42 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml @@ -30,6 +30,18 @@ veto + + String + + Send a command to identify the shade + + + + + + veto + + Switch @@ -77,6 +89,14 @@ Blink during commands + + Number:Power + + Signal strength of Bluetooth Low Energy communication + QualityOfService + + + diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml index 968faa3b3c719..992d1af0587fb 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml @@ -57,10 +57,10 @@ The secondary vertical position (on top-down/bottom-up shades) - + - +
diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java index 1eb09673362dc..911f7cc9ba4f9 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java +++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/internal/gen3/Generation3DtoTest.java @@ -38,6 +38,8 @@ import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; @@ -149,7 +151,7 @@ public void testShadesParsing() throws IOException { assertNotNull(shadeList); assertEquals(2, shadeList.size()); Shade shadeData = shadeList.get(0); - assertEquals("Upper Left Upper Left", shadeData.getName()); + assertEquals("Upper Left", shadeData.getName()); assertEquals(2, shadeData.getId()); assertFalse(shadeData.isMainsPowered()); assertEquals(new DecimalType(66), shadeData.getBatteryLevel()); @@ -217,7 +219,7 @@ public void testShadeHandlerPropertiesAndChannels() throws IOException { assertEquals("Duette (6)", handlerThing.getProperties().get("type")); assertEquals("battery", handlerThing.getProperties().get("powerType")); assertEquals("3.0.359", handlerThing.getProperties().get("firmwareVersion")); - assertEquals(new DecimalType(-50), shadeData.getSignalStrength()); + assertEquals(new QuantityType<>(-50, Units.DECIBEL_MILLIWATTS), shadeData.getSignalStrength()); assertEquals(4, handlerThing.getChannels().size()); /* @@ -233,7 +235,7 @@ public void testShadeHandlerPropertiesAndChannels() throws IOException { assertEquals("Silhouette (23)", handlerThing.getProperties().get("type")); assertEquals("hardwired", handlerThing.getProperties().get("powerType")); assertEquals("3.0.359", handlerThing.getProperties().get("firmwareVersion")); - assertEquals(new DecimalType(-51), shadeData.getSignalStrength()); + assertEquals(new QuantityType<>(-51, Units.DECIBEL_MILLIWATTS), shadeData.getSignalStrength()); assertEquals(3, handlerThing.getChannels().size()); } From f9e359b6b5e429d11c9e6df141d907a09a21bd24 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 21 May 2023 16:58:45 +0100 Subject: [PATCH 58/64] [hdpowerview] shutdown, state, etc. Signed-off-by: Andrew Fiddian-Green --- .../binding/hdpowerview/internal/dto/gen3/Shade.java | 2 +- .../internal/handler/GatewayBridgeHandler.java | 12 ++++++++---- .../src/main/resources/OH-INF/thing/channels.xml | 4 +++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java index 4658297520ae3..9552eea959cb4 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/dto/gen3/Shade.java @@ -37,7 +37,7 @@ public class Shade { private @Nullable Integer id; private @Nullable Integer type; private @Nullable String name; - private @Nullable String ptName; + private @Nullable @SuppressWarnings("unused") String ptName; private @Nullable Integer capabilities; private @Nullable Integer powerType; private @Nullable Integer batteryStatus; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index c3969388520d8..78e6a2653dc46 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -104,14 +104,18 @@ public void dispose() { GatewayWebTargets webTargets = this.webTargets; if (webTargets != null) { - try { - webTargets.close(); - } catch (IOException e) { - } + scheduler.submit(() -> disposeWebTargets(webTargets)); this.webTargets = null; } } + private void disposeWebTargets(GatewayWebTargets webTargets) { + try { + webTargets.close(); + } catch (IOException e) { + } + } + /** * Refresh the state of all things. Normally the thing's position state is updated by SSE. However we must do a * refresh once on start up in order to get the initial state. Also the other properties (battery, signal strength diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml index 6e37c0dc2ee42..8365ffce60923 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml @@ -8,13 +8,15 @@ Rollershutter The vertical position of the shade - blinds + Blinds + Dimmer The opening of the slats in the shade + From 49b9bca323229f2ea1945aa4ec9f64904d084d7b Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 22 May 2023 11:26:16 +0100 Subject: [PATCH 59/64] [hdpowerview] fix pattern Signed-off-by: Andrew Fiddian-Green --- .../src/main/resources/OH-INF/thing/channels.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml index 8365ffce60923..419c00ad36915 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml @@ -96,7 +96,7 @@ Signal strength of Bluetooth Low Energy communication QualityOfService - + From 8d522f01869c6ab0e74f80bc5a2e5055ee7da2ba Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 22 May 2023 17:30:12 +0100 Subject: [PATCH 60/64] [hdpowerview] shortcut logging Signed-off-by: Andrew Fiddian-Green --- .../handler/GatewayBridgeHandler.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index 78e6a2653dc46..1a4c1de01bede 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -74,7 +74,7 @@ public class GatewayBridgeHandler extends BaseBridgeHandler { private boolean scenesLoaded; private boolean propertiesLoaded; - private boolean isDisposing; + private boolean disposing; public GatewayBridgeHandler(Bridge bridge, HttpClient httpClient, HDPowerViewTranslationProvider translationProvider, ClientBuilder clientBuilder, @@ -88,14 +88,14 @@ public GatewayBridgeHandler(Bridge bridge, HttpClient httpClient, @Override public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { - if (childHandler instanceof ShadeThingHandler shadeHandler) { - refreshShade(shadeHandler.getShadeId()); + if (childHandler instanceof ShadeThingHandler) { + refreshShade(((ShadeThingHandler) childHandler).getShadeId()); } } @Override public void dispose() { - isDisposing = true; + disposing = true; ScheduledFuture future = this.refreshTask; if (future != null) { future.cancel(true); @@ -205,7 +205,7 @@ public void initialize() { webTargets = new GatewayWebTargets(this, httpClient, clientBuilder, eventSourceFactory, host); scenesLoaded = false; propertiesLoaded = false; - isDisposing = false; + disposing = false; /* * Normally the thing's position state is updated by SSE. However we must do a refresh once on start up in order @@ -236,7 +236,7 @@ public void onSceneEvent(Scene scene) { * @param shade the one that changed. */ public void onShadeEvent(Shade shade) { - if (isDisposing) { + if (disposing) { return; } try { @@ -251,10 +251,10 @@ public void onShadeEvent(Shade shade) { } private void refreshProperties() throws HubProcessingException, IllegalStateException { - logger.debug("refreshProperties()"); - if (propertiesLoaded || isDisposing) { + if (propertiesLoaded || disposing) { return; } + logger.debug("refreshProperties()"); thing.setProperties(getWebTargets().getInformation()); propertiesLoaded = true; } @@ -266,10 +266,10 @@ private void refreshProperties() throws HubProcessingException, IllegalStateExce * @throws IllegalStateException if this handler is in an illegal state. */ private void refreshScenes() throws HubProcessingException, IllegalStateException { - logger.debug("refreshScenes()"); - if (scenesLoaded || isDisposing) { + if (scenesLoaded || disposing) { return; } + logger.debug("refreshScenes()"); ChannelTypeUID typeUID = new ChannelTypeUID(HDPowerViewBindingConstants.BINDING_ID, channelTypeId); ChannelGroupUID groupUID = new ChannelGroupUID(thing.getUID(), channelGroupId); List channels = new ArrayList<>(); @@ -309,10 +309,10 @@ private void refreshShade(int shadeId) { * @throws IllegalStateException if this handler is in an illegal state. */ private void refreshShades() throws HubProcessingException, IllegalStateException { - logger.debug("refreshShades()"); - if (isDisposing) { + if (disposing) { return; } + logger.debug("refreshShades()"); List handlers = getShadeThingHandlers(); for (Shade shade : getWebTargets().getShades()) { for (ShadeThingHandler handler : handlers) { From 070c91d2ccc9198347716f0cb65746f26353db13 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Mon, 22 May 2023 19:08:09 +0100 Subject: [PATCH 61/64] [hdpowerview] comment for maintainers Signed-off-by: Andrew Fiddian-Green --- .../binding/hdpowerview/internal/GatewayWebTargets.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index 4391f9d140e62..ac72f5a08e974 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -120,8 +120,14 @@ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, shadeStop = home + "shades/stop"; shadeEvents = home + "shades/events?sse=true"; sceneEvents = home + "scenes/events?sse=true"; - register = home + "integration/openhab.org"; info = base + "gateway/info"; + + /* + * Hunter Douglas keeps a statistical count of systems (e.g. openHAB, Home Assistant, Amazon etc.) that are + * using their Generation 3 REST API to connect to their gateways. So we are asked to register with the gateway + * on startup. => So do not change the 'openhab.org' tag below !! + */ + register = home + "integration/openhab.org"; } /** From 9e3a28005e276c24f8f9bcc75991e6391d6c0204 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Tue, 23 May 2023 14:22:04 +0100 Subject: [PATCH 62/64] [hdpowerview] adopt reviewer requests Signed-off-by: Andrew Fiddian-Green --- .../org.openhab.binding.hdpowerview/README.md | 27 ++- .../internal/GatewayWebTargets.java | 155 +++++------------- .../internal/HDPowerViewWebTargets.java | 2 +- .../console/HDPowerViewCommandExtension.java | 2 +- .../GatewayDiscoveryParticipant.java | 24 ++- .../HDPowerViewHubDiscoveryParticipant.java | 21 ++- .../handler/GatewayBridgeHandler.java | 6 +- .../internal/handler/ShadeThingHandler.java | 15 +- .../main/resources/OH-INF/config/config.xml | 9 +- .../OH-INF/i18n/hdpowerview.properties | 17 +- .../main/resources/OH-INF/thing/bridge.xml | 4 +- .../main/resources/OH-INF/thing/channels.xml | 2 +- .../src/main/resources/OH-INF/thing/shade.xml | 2 +- .../main/resources/OH-INF/update/update.xml | 22 +++ 14 files changed, 139 insertions(+), 169 deletions(-) create mode 100644 bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/update/update.xml diff --git a/bundles/org.openhab.binding.hdpowerview/README.md b/bundles/org.openhab.binding.hdpowerview/README.md index e50c297e1cb69..cda450a74d1d4 100644 --- a/bundles/org.openhab.binding.hdpowerview/README.md +++ b/bundles/org.openhab.binding.hdpowerview/README.md @@ -6,7 +6,7 @@ In some countries the PowerView system is sold under the brand name [Luxaflex](h This binding supports hubs/gateways of all generations. Hubs of Generation 1 or 2 are handled commonly and their generation specific features are identified with the *'Generation 1/2 only'* annotation and/or via the [1/2] mark. Gateways of Generation 3 have generation specific features which are identified with the *'Generation 3 only'* annotation and/or via the [3] mark. -Features that are common to all generations are not annoted or marked. +Features that are common to all generations are not annotated or marked. ![PowerView](doc/hdpowerview.png) @@ -19,26 +19,23 @@ By using a scene to control multiple shades at once, the shades will all begin m ## Supported Things -| Thing | Thing Type | Description | -|--------------------------|------------|-------------------------------------------------------------------------------------------------------------------------------| -| hub[1/2] | Bridge | The PowerView hub interfaces between the LAN and the shade's radio network. Contains channels to interact with scenes. | -| shade[1/2] | Thing | A motorized shade. | -| repeater[1/2] | Thing | A PowerView signal repeater. | -| gateway[3] | Bridge | Generation 3 gateway interfaces between the LAN and the shade's Bluetooth network. Contains channels to interact with scenes. | -| shade3[3] | Thing | A Powerview Generation 3 motorized shade. | - -[1/2] Generation 1/2 hubs only. -[3] Generation 3 gateways only. +| Thing | Generation | Thing Type | Description | +|----------|------------|------------|-------------------------------------------------------------------------------------------------------------------------------| +| hub | 1, 2 | Bridge | The PowerView hub interfaces between the LAN and the shade's radio network. Contains channels to interact with scenes. | +| shade | 1, 2 | Thing | A motorized shade. | +| repeater | 1, 2 | Thing | A PowerView signal repeater. | +| gateway | 3 | Bridge | Generation 3 gateway interfaces between the LAN and the shade's Bluetooth network. Contains channels to interact with scenes. | +| shade3 | 3 | Thing | A Powerview Generation 3 motorized shade. | ## Discovery Make sure your shades are visible in the PowerView app before attempting discovery. -The binding can automatically discover the PowerView hub and/or the PowerView Generation 3 gateway. +The binding can automatically discover PowerView hubs and gateways. The discovery process can be started by pressing the refresh button in the Main Configuration UI Inbox. However you can also manually create a (bridge) thing for the hub, and enter the required configuration parameters (see Thing Configuration below). If the configuration parameters are all valid, the binding will then automatically attempt to connect to the hub/gateway. -If the connection succeeds, the hub will indicate its status as Online, otherwise it will show an error status. +If the connection succeeds, the hub will indicate its status as Online, otherwise it will show an error status. Once the hub thing has been created and successfully connected, the binding will automatically discover all shades and scenes that are in it. @@ -203,7 +200,7 @@ In this case, the position of the secondary blackout panel is reported as 0%. ### Hard Refresh on Generation 3 Gateways[3] In Generation 3 systems, whenever the state of a shade or scene changes, it immediately notifies the gateway, and the gateway immediately notifies the openHAB binding. -However in case that any notifications may have been lost, (e.g. due to a network error), the binding occasionally requests a full update (i.e. a 'hard refresh') of all shade and scene states from the gateway. +However, in case that any notifications have been lost, (e.g. due to a network error), the binding occasionally requests a full update (i.e. a 'hard refresh') of all shade and scene states from the gateway. The time interval between hard refreshes is set in the `hardRefresh` configuration parameter. ### Hard Refresh on Generation 1/2 Hubs[1/2] @@ -262,7 +259,7 @@ Bridge hdpowerview:hub:home "Luxaflex Hub" @ "Living Room" [host="192.168.1.123" } // generation 3 -Bridge hdpowerview:gateway:home "Luxaflex Hub" @ "Living Room" [host="192.168.1.123"] { +Bridge hdpowerview:gateway:home "Luxaflex Gateway" @ "Living Room" [host="192.168.1.123"] { Thing shade3 s50150 "Living Room Shade" @ "Living Room" [id="50150"] } ``` diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index ac72f5a08e974..bfec5d90eebe7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -14,8 +14,10 @@ import java.io.Closeable; import java.io.IOException; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; @@ -41,7 +43,6 @@ import org.openhab.binding.hdpowerview.internal.HDPowerViewWebTargets.Query; import org.openhab.binding.hdpowerview.internal.dto.gen3.Info; import org.openhab.binding.hdpowerview.internal.dto.gen3.Scene; -import org.openhab.binding.hdpowerview.internal.dto.gen3.SceneEvent; import org.openhab.binding.hdpowerview.internal.dto.gen3.Shade; import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadeEvent; import org.openhab.binding.hdpowerview.internal.dto.gen3.ShadePosition; @@ -69,6 +70,8 @@ public class GatewayWebTargets implements Closeable, HostnameVerifier { private static final Set HTTP_OK_CODES = Set.of(HttpStatus.OK_200, HttpStatus.NO_CONTENT_204); private final Logger logger = LoggerFactory.getLogger(GatewayWebTargets.class); + private final Gson jsonParser = new Gson(); + private final String shades; private final String scenes; private final String sceneActivate; @@ -79,20 +82,16 @@ public class GatewayWebTargets implements Closeable, HostnameVerifier { private final String info; private final String register; private final String shadeEvents; - private final String sceneEvents; - private final Gson jsonParser = new Gson(); private final HttpClient httpClient; private final ClientBuilder clientBuilder; private final SseEventSourceFactory eventSourceFactory; private final GatewayBridgeHandler hubHandler; + private final String ipAddress; - private final String hostName; - private boolean isRegistered; - + private boolean registered; private boolean closing; - private @Nullable SseEventSource shadeEventSource; - private @Nullable SseEventSource sceneEventSource; + private @Nullable SseEventSource shadeEventSource; private @Nullable ScheduledFuture sseQuietCheck; /** @@ -103,7 +102,7 @@ public class GatewayWebTargets implements Closeable, HostnameVerifier { */ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, ClientBuilder clientBuilder, SseEventSourceFactory eventSourceFactory, String ipAddress) { - this.hostName = ipAddress; + this.ipAddress = ipAddress; this.httpClient = httpClient; this.clientBuilder = clientBuilder; this.eventSourceFactory = eventSourceFactory; @@ -119,7 +118,6 @@ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, shadeSingle = home + "shades/%d"; shadeStop = home + "shades/stop"; shadeEvents = home + "shades/events?sse=true"; - sceneEvents = home + "scenes/events?sse=true"; info = base + "gateway/info"; /* @@ -131,7 +129,7 @@ public GatewayWebTargets(GatewayBridgeHandler hubHandler, HttpClient httpClient, } /** - * Issue a command a activate a scene. + * Issue a command to activate a scene. * * @param sceneId the scene to be activated. * @throws HubProcessingException if any error occurs. @@ -140,17 +138,6 @@ public void activateScene(int sceneId) throws HubProcessingException { invoke(HttpMethod.PUT, String.format(sceneActivate, sceneId), null, null); } - /** - * Issue a calibrate command to a shade. - * - * @param shadeId the shade to be calibrated. - * @throws HubProcessingException if any error occurs. - */ - public void calibrateShade(int shadeId) throws HubProcessingException { - String json = jsonParser.toJson(new ShadeMotion(ShadeMotion.Type.CALIBRATE)); - invoke(HttpMethod.PUT, String.format(shadeMotion, shadeId), null, json); - } - @Override public void close() throws IOException { closing = true; @@ -163,11 +150,11 @@ public void close() throws IOException { * @throws HubProcessingException if any error occurs. */ public boolean gatewayRegister() throws HubProcessingException { - if (!isRegistered) { + if (!registered) { invoke(HttpMethod.PUT, register, null, null); - isRegistered = true; + registered = true; } - return isRegistered; + return registered; } /** @@ -200,9 +187,11 @@ public Map getInformation() throws HubProcessingException { public List getScenes() throws HubProcessingException { String json = invoke(HttpMethod.GET, scenes, null, null); try { - return List.of(jsonParser.fromJson(json, Scene[].class)); - } catch (NullPointerException e) { - throw new HubProcessingException("getScenes() missing response"); + Scene[] scenes = jsonParser.fromJson(json, Scene[].class); + if (scenes == null || Arrays.stream(scenes).anyMatch(Objects::isNull)) { + throw new HubProcessingException("getScenes() response is null or contains null(s)"); + } + return List.of(scenes); } catch (JsonParseException e) { throw new HubProcessingException("getScenes() JsonParseException"); } @@ -237,9 +226,11 @@ public Shade getShade(int shadeId) throws HubProcessingException { public List getShades() throws HubProcessingException { String json = invoke(HttpMethod.GET, shades, null, null); try { - return List.of(jsonParser.fromJson(json, Shade[].class)); - } catch (NullPointerException e) { - throw new HubProcessingException("getScenes() missing response"); + Shade[] shades = jsonParser.fromJson(json, Shade[].class); + if (shades == null || Arrays.stream(shades).anyMatch(Objects::isNull)) { + throw new HubProcessingException("getShades() response is null or contains null(s)"); + } + return List.of(shades); } catch (JsonParseException e) { throw new HubProcessingException("getShades() JsonParseException"); } @@ -293,7 +284,6 @@ protected synchronized String invoke(HttpMethod method, String url, @Nullable Qu logger.trace("invoke() response JSON:{}", jsonResponse); } if ((HttpStatus.OK_200 == statusCode) && ((jsonResponse == null) || (jsonResponse.isEmpty()))) { - logger.warn("invoke() hub returned no content"); throw new HubProcessingException("Missing response entity"); } return jsonResponse; @@ -332,64 +322,22 @@ public void onSseQuiet() { } /** - * Handle SSE errors. - * For the time being just log them, because the framework should automatically recover itself. + * Handle SSE errors. For the time being just log them, because the framework should automatically recover itself. * * @param e the error that was thrown. */ - private void onSseSceneError(Throwable e) { - if (!closing) { - logger.debug("onSseSceneError() {}", e.getMessage(), e); - } - } - - /** - * Handle inbound SSE events for a scene. - * - * @param sseEvent the inbound event. - */ - private void onSseSceneEvent(InboundSseEvent sseEvent) { - if (closing) { - return; - } - ScheduledFuture task = sseQuietCheck; - if (task != null && !task.isCancelled()) { - task.cancel(true); - } - sseQuietCheck = hubHandler.getScheduler().schedule(this::onSseQuiet, SLEEP_SECONDS, TimeUnit.SECONDS); - if (sseEvent.isEmpty()) { - return; - } - String json = sseEvent.readData(); - if (json == null) { - return; - } - logger.trace("onSseSceneEvent() json:{}", json); - SceneEvent sceneEvent = jsonParser.fromJson(json, SceneEvent.class); - if (sceneEvent != null) { - Scene scene = sceneEvent.getScene(); - hubHandler.onSceneEvent(scene); - } - } - - /** - * Handle SSE errors. - * For the time being just log them, because the framework should automatically recover itself. - * - * @param e the error that was thrown. - */ - private void onSseShadeError(Throwable e) { + private void onSseError(Throwable e) { if (!closing) { logger.debug("onSseShadeError() {}", e.getMessage(), e); } } /** - * Handle inbound SSE events for a shade. + * Handle inbound shade SSE events. * * @param sseEvent the inbound event. */ - private void onSSeShadeEvent(InboundSseEvent sseEvent) { + private void onSSeEvent(InboundSseEvent sseEvent) { if (closing) { return; } @@ -420,7 +368,7 @@ private void onSSeShadeEvent(InboundSseEvent sseEvent) { private synchronized void sseClose() { logger.debug("sseClose() called"); ScheduledFuture task = sseQuietCheck; - if (task != null && !task.isCancelled()) { + if (task != null) { task.cancel(true); sseQuietCheck = null; } @@ -430,11 +378,6 @@ private synchronized void sseClose() { source.close(); this.shadeEventSource = null; } - source = this.sceneEventSource; - if (source != null) { - source.close(); - this.sceneEventSource = null; - } } /** @@ -450,15 +393,9 @@ public synchronized void sseOpen() { try { // open SSE channel for shades SseEventSource shadeEventSource = eventSourceFactory.newSource(client.target(shadeEvents)); - shadeEventSource.register(this::onSSeShadeEvent, this::onSseShadeError); + shadeEventSource.register(this::onSSeEvent, this::onSseError); shadeEventSource.open(); this.shadeEventSource = shadeEventSource; - - // open SSE channel for scenes - SseEventSource sceneEventSource = eventSourceFactory.newSource(client.target(sceneEvents)); - sceneEventSource.register(this::onSseSceneEvent, this::onSseSceneError); - sceneEventSource.open(); - this.sceneEventSource = sceneEventSource; } catch (Exception e) { // SSE documentation does not say what exceptions may be thrown, so catch everything logger.warn("sseOpen() {}", e.getMessage(), e); @@ -466,34 +403,24 @@ public synchronized void sseOpen() { } /** - * Reopen the SSE links. If the eventSources already exist, try first to simply close and reopen them, but if that - * fails, then completely destroy and re-create the eventSources. + * Reopen the SSE links. If the event source already exists, try first to simply close and re-open it, but if that + * fails, then completely destroy and re-create it. */ private synchronized void sseReOpen() { logger.debug("sseReOpen() called"); - SseEventSource shadeEventSource = this.shadeEventSource; - SseEventSource sceneEventSource = this.sceneEventSource; - if (shadeEventSource != null && sceneEventSource != null) { - boolean exception = false; - for (SseEventSource eventSource : Set.of(shadeEventSource, sceneEventSource)) { - if (eventSource != null) { - try { - if (eventSource.isOpen()) { - eventSource.close(); - } - if (!eventSource.isOpen()) { - eventSource.open(); - } - } catch (Exception e) { - // SSE documentation does not say what exceptions may be thrown, so catch everything - logger.warn("sseReOpen() {}", e.getMessage(), e); - exception = true; - } + if (shadeEventSource != null) { + try { + if (shadeEventSource.isOpen()) { + shadeEventSource.close(); + } + if (!shadeEventSource.isOpen()) { + shadeEventSource.open(); } - } - if (!exception) { return; + } catch (Exception e) { + // SSE documentation does not say what exceptions may be thrown, so catch everything + logger.warn("sseReOpen() {}", e.getMessage(), e); } } sseOpen(); @@ -518,6 +445,6 @@ public void stopShade(int shadeId) throws HubProcessingException { */ @Override public boolean verify(@Nullable String hostName, @Nullable SSLSession sslSession) { - return this.hostName.equals(hostName); + return this.ipAddress.equals(hostName); } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java index 9a57f101bd891..1b0a78d5d60d8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/HDPowerViewWebTargets.java @@ -102,7 +102,7 @@ public class HDPowerViewWebTargets { private final HttpClient httpClient; /** - * private helper class for passing http url query parameters + * helper class for passing http url query parameters */ public static class Query { private final String key; diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java index d7cbbecae4890..3a257db258217 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/console/HDPowerViewCommandExtension.java @@ -111,7 +111,7 @@ public void execute(String[] args, Console console) { @Override public List getUsages() { - return Arrays.asList(buildCommandUsage(SHOW_IDS, "list all shades and eventually repeaters")); + return Arrays.asList(buildCommandUsage(SHOW_IDS, "list all shades and repeaters")); } @Override diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java index a1f2daacd6375..88dc5d722a606 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java @@ -14,7 +14,6 @@ import static org.openhab.binding.hdpowerview.internal.HDPowerViewBindingConstants.*; -import java.util.Collections; import java.util.Set; import javax.jmdns.ServiceInfo; @@ -25,9 +24,14 @@ import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,8 +44,20 @@ @Component public class GatewayDiscoveryParticipant implements MDNSDiscoveryParticipant { + private static final String LABEL_KEY = "discovery.gateway.label"; + private final Logger logger = LoggerFactory.getLogger(GatewayDiscoveryParticipant.class); + private final TranslationProvider i18nProvider; + private final LocaleProvider localeProvider; + + @Activate + public GatewayDiscoveryParticipant(final @Reference TranslationProvider i18nProvider, + final @Reference LocaleProvider localeProvider) { + this.i18nProvider = i18nProvider; + this.localeProvider = localeProvider; + } + @Override public @Nullable DiscoveryResult createResult(ServiceInfo service) { for (String host : service.getHostAddresses()) { @@ -50,7 +66,9 @@ public class GatewayDiscoveryParticipant implements MDNSDiscoveryParticipant { DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel("PowerView Gateway (" + host + ")").build(); + .withLabel(i18nProvider.getText(FrameworkUtil.getBundle(getClass()), LABEL_KEY, LABEL_KEY, + localeProvider.getLocale(), host)) + .build(); logger.debug("mDNS discovered Generation 3 Gateway on host '{}'", host); return hub; } @@ -65,7 +83,7 @@ public String getServiceType() { @Override public Set getSupportedThingTypeUIDs() { - return Collections.singleton(THING_TYPE_GATEWAY); + return Set.of(THING_TYPE_GATEWAY); } @Override diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java index 1665d4ac5ffd2..49a2bd3034f3e 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java @@ -25,9 +25,14 @@ import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,8 +45,20 @@ @Component public class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticipant { + private static final String LABEL_KEY = "discovery.hub.label"; + private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant.class); + private final TranslationProvider i18nProvider; + private final LocaleProvider localeProvider; + + @Activate + public HDPowerViewHubDiscoveryParticipant(final @Reference TranslationProvider i18nProvider, + final @Reference LocaleProvider localeProvider) { + this.i18nProvider = i18nProvider; + this.localeProvider = localeProvider; + } + @Override public Set getSupportedThingTypeUIDs() { return Collections.singleton(THING_TYPE_HUB); @@ -60,7 +77,9 @@ public String getServiceType() { DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel("PowerView Hub (" + host + ")").build(); + .withLabel(i18nProvider.getText(FrameworkUtil.getBundle(getClass()), LABEL_KEY, LABEL_KEY, + localeProvider.getLocale(), host)) + .build(); logger.debug("mDNS discovered hub on host '{}'", host); return hub; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java index 1a4c1de01bede..b8c01af68f497 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/GatewayBridgeHandler.java @@ -213,11 +213,7 @@ public void initialize() { * Furthermore we need to do periodic refreshes just in case the SSE connection may have been lost. So we * schedule the refresh at the 'hardRefresh' interval. */ - ScheduledFuture refreshTask = this.refreshTask; - if (refreshTask != null) { - refreshTask.cancel(false); - } - this.refreshTask = scheduler.scheduleWithFixedDelay(() -> doRefresh(), 0, config.hardRefresh, TimeUnit.MINUTES); + refreshTask = scheduler.scheduleWithFixedDelay(() -> doRefresh(), 0, config.hardRefresh, TimeUnit.MINUTES); updateStatus(ThingStatus.UNKNOWN); } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java index c325abf1b2ceb..a106c52d99cfd 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/ShadeThingHandler.java @@ -61,10 +61,10 @@ public class ShadeThingHandler extends BaseThingHandler { private static final String INVALID_CHANNEL = "invalid channel"; - private static final String INVALID_COMMAND = "invalid command"; - private static final String COMMAND_CALIBRATE = "CALIBRATE"; + private static final String COMMAND_IDENTIFY = "IDENTIFY"; + private static final ShadeCapabilitiesDatabase DB = new ShadeCapabilitiesDatabase(); private final Logger logger = LoggerFactory.getLogger(ShadeThingHandler.class); @@ -166,14 +166,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { throw new IllegalArgumentException(INVALID_COMMAND); case CHANNEL_SHADE_COMMAND: - if (command instanceof StringType) { - if (COMMAND_IDENTIFY.equals(((StringType) command).toString())) { - webTargets.jogShade(shadeId); - break; - } else if (COMMAND_CALIBRATE.equals(((StringType) command).toString())) { - webTargets.calibrateShade(shadeId); - break; - } + if ((command instanceof StringType) && COMMAND_IDENTIFY.equals(((StringType) command).toString())) { + webTargets.jogShade(shadeId); + break; } throw new IllegalArgumentException(INVALID_COMMAND); diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/config/config.xml index a655fa3493de8..21acc75050282 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/config/config.xml @@ -14,14 +14,7 @@ - The numeric ID of the PowerView Shade in the Hub - - - - - - - The numeric ID of the PowerView Shade in the Gateway + The numeric ID of the PowerView Shade in the Hub/Gateway diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties index f30b489a90603..fc10667c8eb4a 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/i18n/hdpowerview.properties @@ -31,21 +31,19 @@ thing-type.hdpowerview.shade3.channel.secondary.description = The secondary vert thing-type.config.hdpowerview.gateway.hardRefresh.label = Hard Refresh Interval thing-type.config.hdpowerview.gateway.hardRefresh.description = The number of minutes between hard refreshes of the PowerView Gateway thing-type.config.hdpowerview.gateway.host.label = Host -thing-type.config.hdpowerview.gateway.host.description = The Host address of the PowerView Gateway +thing-type.config.hdpowerview.gateway.host.description = The host address of the PowerView Gateway thing-type.config.hdpowerview.hub.hardRefresh.label = Hard Position Refresh Interval thing-type.config.hdpowerview.hub.hardRefresh.description = The number of minutes between hard refreshes of positions from the PowerView Hub (or 0 to disable) thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.label = Hard Battery Level Refresh Interval thing-type.config.hdpowerview.hub.hardRefreshBatteryLevel.description = The number of hours between hard refreshes of battery levels from the PowerView Hub (or 0 to disable, default is weekly) thing-type.config.hdpowerview.hub.host.label = Host -thing-type.config.hdpowerview.hub.host.description = The Host address of the PowerView Hub +thing-type.config.hdpowerview.hub.host.description = The host address of the PowerView Hub thing-type.config.hdpowerview.hub.refresh.label = Refresh Interval thing-type.config.hdpowerview.hub.refresh.description = The number of milliseconds between fetches of the PowerView Hub shade state thing-type.config.hdpowerview.repeater.id.label = ID thing-type.config.hdpowerview.repeater.id.description = The numeric ID of the PowerView Repeater in the Hub thing-type.config.hdpowerview.shade.id.label = ID -thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Hub -thing-type.config.hdpowerview.shade3.id.label = ID -thing-type.config.hdpowerview.shade3.id.description = The numeric ID of the PowerView Shade in the Gateway +thing-type.config.hdpowerview.shade.id.description = The numeric ID of the PowerView Shade in the Hub/Gateway # channel group types @@ -58,7 +56,7 @@ channel-group-type.hdpowerview.scenes.label = Scenes channel-type.hdpowerview.automation-enabled.label = Enable channel-type.hdpowerview.battery-voltage.label = Battery Voltage channel-type.hdpowerview.battery-voltage.description = Battery voltage reported by the shade -channel-type.hdpowerview.ble-signal-strength.label = Signal strength +channel-type.hdpowerview.ble-signal-strength.label = Signal Strength channel-type.hdpowerview.ble-signal-strength.description = Signal strength of Bluetooth Low Energy communication channel-type.hdpowerview.repeater-blinking-enabled.label = Blinking Enabled channel-type.hdpowerview.repeater-blinking-enabled.description = Blink during commands @@ -85,7 +83,12 @@ channel-type.hdpowerview.shade3-command.command.option.IDENTIFY = Identify offline.conf-error.no-host-address = Host address must be set offline.conf-error.invalid-bridge-handler = Invalid bridge handler -offline.gone.shade-unknown-to-hub = Shade is unknown to Hub +offline.gone.shade-unknown-to-hub = Shade is unknown to Hub/Gateway + +# discovery + +discovery.hub.label = PowerView Gen 1/2 Hub ({0}) +discovery.gateway.label = PowerView Gen 3 Gateway ({0}) # dynamic channels diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml index f0cd3aab798cd..44db4ac1545a8 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/bridge.xml @@ -24,7 +24,7 @@ - The Host address of the PowerView Hub + The host address of the PowerView Hub network-address @@ -65,7 +65,7 @@ - The Host address of the PowerView Gateway + The host address of the PowerView Gateway network-address diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml index 419c00ad36915..71264b04532aa 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/channels.xml @@ -93,7 +93,7 @@ Number:Power - + Signal strength of Bluetooth Low Energy communication QualityOfService diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml index 992d1af0587fb..de18d3b353968 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/thing/shade.xml @@ -70,7 +70,7 @@ id - + diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/update/update.xml b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/update/update.xml new file mode 100644 index 0000000000000..70a692f2f238d --- /dev/null +++ b/bundles/org.openhab.binding.hdpowerview/src/main/resources/OH-INF/update/update.xml @@ -0,0 +1,22 @@ + + + + + + + powerview:shade-position + + + powerview:shade-position + + The secondary vertical position (on top-down/bottom-up shades) + + + powerview:shade-vane + + + + + From d23a2e9d2868174cefcb24e98fddfccc502258c0 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Tue, 23 May 2023 22:54:57 +0100 Subject: [PATCH 63/64] [hdpowerview] adopt reviewer requests Signed-off-by: Andrew Fiddian-Green --- .../internal/GatewayWebTargets.java | 18 ++++------------ .../GatewayDiscoveryParticipant.java | 21 ++----------------- .../HDPowerViewHubDiscoveryParticipant.java | 21 ++----------------- 3 files changed, 8 insertions(+), 52 deletions(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index bfec5d90eebe7..d6cd37c5436ac 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -14,10 +14,8 @@ import java.io.Closeable; import java.io.IOException; -import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; @@ -187,11 +185,7 @@ public Map getInformation() throws HubProcessingException { public List getScenes() throws HubProcessingException { String json = invoke(HttpMethod.GET, scenes, null, null); try { - Scene[] scenes = jsonParser.fromJson(json, Scene[].class); - if (scenes == null || Arrays.stream(scenes).anyMatch(Objects::isNull)) { - throw new HubProcessingException("getScenes() response is null or contains null(s)"); - } - return List.of(scenes); + return List.of(jsonParser.fromJson(json, Scene[].class)); } catch (JsonParseException e) { throw new HubProcessingException("getScenes() JsonParseException"); } @@ -226,11 +220,7 @@ public Shade getShade(int shadeId) throws HubProcessingException { public List getShades() throws HubProcessingException { String json = invoke(HttpMethod.GET, shades, null, null); try { - Shade[] shades = jsonParser.fromJson(json, Shade[].class); - if (shades == null || Arrays.stream(shades).anyMatch(Objects::isNull)) { - throw new HubProcessingException("getShades() response is null or contains null(s)"); - } - return List.of(shades); + return List.of(jsonParser.fromJson(json, Shade[].class)); } catch (JsonParseException e) { throw new HubProcessingException("getShades() JsonParseException"); } @@ -283,8 +273,8 @@ protected synchronized String invoke(HttpMethod method, String url, @Nullable Qu if (logger.isTraceEnabled()) { logger.trace("invoke() response JSON:{}", jsonResponse); } - if ((HttpStatus.OK_200 == statusCode) && ((jsonResponse == null) || (jsonResponse.isEmpty()))) { - throw new HubProcessingException("Missing response entity"); + if (method == HttpMethod.GET && jsonResponse.isEmpty()) { + throw new HubProcessingException("Empty response entity"); } return jsonResponse; } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java index 88dc5d722a606..a79d1559904bf 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/GatewayDiscoveryParticipant.java @@ -24,14 +24,9 @@ import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; -import org.openhab.core.i18n.LocaleProvider; -import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; -import org.osgi.framework.FrameworkUtil; -import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,16 +43,6 @@ public class GatewayDiscoveryParticipant implements MDNSDiscoveryParticipant { private final Logger logger = LoggerFactory.getLogger(GatewayDiscoveryParticipant.class); - private final TranslationProvider i18nProvider; - private final LocaleProvider localeProvider; - - @Activate - public GatewayDiscoveryParticipant(final @Reference TranslationProvider i18nProvider, - final @Reference LocaleProvider localeProvider) { - this.i18nProvider = i18nProvider; - this.localeProvider = localeProvider; - } - @Override public @Nullable DiscoveryResult createResult(ServiceInfo service) { for (String host : service.getHostAddresses()) { @@ -66,10 +51,8 @@ public GatewayDiscoveryParticipant(final @Reference TranslationProvider i18nProv DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel(i18nProvider.getText(FrameworkUtil.getBundle(getClass()), LABEL_KEY, LABEL_KEY, - localeProvider.getLocale(), host)) - .build(); - logger.debug("mDNS discovered Generation 3 Gateway on host '{}'", host); + .withLabel(String.format("@text/%s [\"%s\"]", LABEL_KEY, host)).build(); + logger.debug("mDNS discovered Gen 3 gateway on host '{}'", host); return hub; } } diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java index 49a2bd3034f3e..a4c98977e0d4f 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/discovery/HDPowerViewHubDiscoveryParticipant.java @@ -25,14 +25,9 @@ import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant; -import org.openhab.core.i18n.LocaleProvider; -import org.openhab.core.i18n.TranslationProvider; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; -import org.osgi.framework.FrameworkUtil; -import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,16 +44,6 @@ public class HDPowerViewHubDiscoveryParticipant implements MDNSDiscoveryParticip private final Logger logger = LoggerFactory.getLogger(HDPowerViewHubDiscoveryParticipant.class); - private final TranslationProvider i18nProvider; - private final LocaleProvider localeProvider; - - @Activate - public HDPowerViewHubDiscoveryParticipant(final @Reference TranslationProvider i18nProvider, - final @Reference LocaleProvider localeProvider) { - this.i18nProvider = i18nProvider; - this.localeProvider = localeProvider; - } - @Override public Set getSupportedThingTypeUIDs() { return Collections.singleton(THING_TYPE_HUB); @@ -77,10 +62,8 @@ public String getServiceType() { DiscoveryResult hub = DiscoveryResultBuilder.create(thingUID) .withProperty(HDPowerViewHubConfiguration.HOST, host) .withRepresentationProperty(HDPowerViewHubConfiguration.HOST) - .withLabel(i18nProvider.getText(FrameworkUtil.getBundle(getClass()), LABEL_KEY, LABEL_KEY, - localeProvider.getLocale(), host)) - .build(); - logger.debug("mDNS discovered hub on host '{}'", host); + .withLabel(String.format("@text/%s [\"%s\"]", LABEL_KEY, host)).build(); + logger.debug("mDNS discovered Gen 1/2 hub on host '{}'", host); return hub; } } From 137f987bcd130528877f7c08b9ba148473c6d8ee Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Wed, 24 May 2023 12:49:46 +0200 Subject: [PATCH 64/64] Remove redundant logging Signed-off-by: Jacob Laursen --- .../openhab/binding/hdpowerview/internal/GatewayWebTargets.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java index d6cd37c5436ac..1ee0b0bcf95f7 100644 --- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java +++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/GatewayWebTargets.java @@ -266,7 +266,6 @@ protected synchronized String invoke(HttpMethod method, String url, @Nullable Qu } int statusCode = response.getStatus(); if (!HTTP_OK_CODES.contains(statusCode)) { - logger.warn("invoke() HTTP status:{}, reason:{}", statusCode, response.getReason()); throw new HubProcessingException(String.format("HTTP %d error", statusCode)); } String jsonResponse = response.getContentAsString();