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.Scene;
+import org.openhab.binding.hdpowerview.internal.dto.SceneCollection;
+import org.openhab.binding.hdpowerview.internal.dto.ScheduledEvent;
+import org.openhab.binding.hdpowerview.internal.dto.ShadeData;
+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.Scenes;
+import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents;
+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.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;
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * An enum for the type of power supply in a shade.
- *
- * @author Andrew Fiddian-Green - Initial contribution
- */
-@NonNullByDefault
-public enum BatteryKind {
- ERROR_UNKNOWN(-1),
- HARDWIRED_POWER_SUPPLY(1),
- BATTERY_WAND(2),
- RECHARGEABLE_BATTERY_WAND(3);
-
- private int batteryKind;
-
- private BatteryKind(int i) {
- this.batteryKind = i;
- }
-
- /**
- * Determine the BatteryKind by parsing the given string value.
- *
- * @param value the string to parse, or null.
- * @return the BatteryKind or ERROR_UNKNOWN in case of error.
- */
- public static BatteryKind fromString(@Nullable String value) {
- if (value != null) {
- try {
- int intValue = Integer.parseInt(value);
- for (BatteryKind e : values()) {
- if (e.batteryKind == intValue) {
- return e;
- }
- }
- } catch (NumberFormatException e) {
- // fall through
- }
- }
- return ERROR_UNKNOWN;
- }
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.library.types.HSBType;
-
-/**
- * Color and brightness information for HD PowerView repeater
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class Color {
- public int brightness;
- public int red;
- public int green;
- public int blue;
-
- public Color(int brightness, HSBType hsbType) {
- this.brightness = brightness;
- int rgb = hsbType.getRGB();
- java.awt.Color color = new java.awt.Color(rgb);
- red = color.getRed();
- green = color.getGreen();
- blue = color.getBlue();
- }
-
- public Color(int brightness, java.awt.Color color) {
- this.brightness = brightness;
- red = color.getRed();
- green = color.getGreen();
- blue = color.getBlue();
- }
-
- public Color(int brightness, int red, int green, int blue) {
- this.brightness = brightness;
- this.red = red;
- this.green = green;
- this.blue = blue;
- }
-
- @Override
- public String toString() {
- return String.format("%d.%d.%d/%d%%", red, green, blue, brightness);
- }
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Shade coordinate system (a.k.a. position kind), as returned by the HD PowerView hub.
- *
- * @param NONE a coordinate system that does not refer to any type of physical rail.
- * @param PRIMARY_POSITION primary rail, whose coordinate value 0 means shade is closed.
- * @param SECONDARY_POSITION secondary rail, whose coordinate value 0 means shade is open.
- * @param VANE_TILT_POSITION vane/tilt operator, whose coordinate system is for vanes.
- * @param ERROR_UNKNOWN unsupported coordinate system.
- *
- * @author Andy Lintner - Initial contribution of the original enum called
- * ShadePositionKind
- *
- * @author Andrew Fiddian-Green - Rewritten as a new enum called
- * CoordinateSystem to support secondary rail positions and be more
- * explicit on coordinate directions and ranges
- */
-@NonNullByDefault
-public enum CoordinateSystem {
- /*-
- * Specifies the coordinate system used for the position of the shade. Top-down
- * shades are in the same coordinate space as bottom-up shades. Shade position
- * values for top-down shades would be reversed for bottom-up shades. For
- * example, since 65535 is the open value for a bottom-up shade, it is the
- * closed value for a top-down shade. The top-down/bottom-up shade is different
- * in that instead of the top and bottom rail operating in one coordinate space
- * like the top-down and the bottom-up, it operates in two where the top
- * (middle) rail closed value is 0 and the bottom (primary) rail closed position
- * is also 0 and fully open for both is 65535
- *
- * The position element can take on multiple states depending on the family of
- * shade under control.
- *
- * The ranges of position integer values are
- * shades: 0..65535
- * vanes: 0..32767
- *
- * Shade fully up: (top-down: open, bottom-up: closed)
- * posKind: 1 {ZERO_IS_CLOSED}
- * position: 65535
- *
- * Shade and vane fully down: (top-down: closed, bottom-up: open)
- * posKind: 1 {ZERO_IS_CLOSED}
- * position1: 0
- *
- * ALTERNATE: Shade and vane fully down: (top-down: closed, bottom-up: open)
- * posKind: 3 {VANE_COORDS}
- * position: 0
- *
- * Shade fully down (closed) and vane fully up (open):
- * posKind: 3 {VANE_COORDS}
- * position: 32767
- *
- * Dual action, secondary top-down shade fully up (closed):
- * posKind: 2 {ZERO_IS_OPEN}
- * position: 0
- *
- * Dual action, secondary top-down shade fully down (open):
- * posKind: 2 {ZERO_IS_OPEN}
- * position: 65535
- *
- */
- NONE,
- PRIMARY_POSITION,
- SECONDARY_POSITION,
- VANE_TILT_POSITION,
- ERROR_UNKNOWN;
-
- public static final int MAX_SHADE = 65535;
- public static final int MAX_VANE = 32767;
-
- /**
- * Converts an HD PowerView posKind integer value to a CoordinateSystem enum value.
- *
- * @param posKind input integer value.
- * @return corresponding CoordinateSystem enum.
- */
- public static CoordinateSystem fromPosKind(int posKind) {
- try {
- return CoordinateSystem.values()[posKind];
- } catch (ArrayIndexOutOfBoundsException e) {
- return ERROR_UNKNOWN;
- }
- }
-
- /**
- * Check if the coordinate system matches the given posKind.
- *
- * @param posKind
- * @return true if equal.
- */
- public boolean equals(@Nullable Integer posKind) {
- return (posKind != null) && (posKind.intValue() == ordinal());
- }
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Firmware version information for HD PowerView components
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class Firmware {
- @Nullable
- public String name;
- public int revision;
- public int subRevision;
- public int build;
-
- @Override
- public String toString() {
- return String.format("%d.%d.%d", revision, subRevision, build);
- }
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Firmware information for an HD PowerView hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class HubFirmware {
- @Nullable
- public Firmware mainProcessor;
- @Nullable
- public Firmware radio;
-}
+++ /dev/null
-/**
- * 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 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
- *
- * @author Andy Lintner - Initial contribution
- * @author Andrew Fiddian-Green - Added support for secondary rail positions
- */
-@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() {
- }
-
- /**
- * 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.
- */
- 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.
- */
- 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.
- */
- 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.
- */
- 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;
- }
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Survey data of a single Shade, as returned by an HD PowerView hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class SurveyData {
- @SerializedName("neighbor_id")
- public int neighborId;
- public int rssi;
-
- @Override
- public String toString() {
- return String.format("{neighbor id:%d, rssi:%d}", neighborId, rssi);
- }
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Times as part of {@link UserData} for an HD PowerView hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class Times {
- public @Nullable String timezone;
- public int localSunriseTimeInMinutes;
- public int localSunsetTimeInMinutes;
- public int currentOffset;
- public double longitude;
- public double latitude;
-}
+++ /dev/null
-/**
- * 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;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * User data for an HD PowerView hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class UserData {
- public @Nullable String hubName;
- public boolean localTimeDataSet;
- public boolean enableScheduledEvents;
- public boolean editingEnabled;
- public boolean setupCompleted;
- public @Nullable String gateway;
- public @Nullable String dns;
- public boolean staticIp;
- @SerializedName("_id")
- public @Nullable String id;
- public @Nullable Color color;
- public boolean autoBackup;
- public @Nullable String ip;
- public @Nullable String macAddress;
- public @Nullable String mask;
- public boolean wireless;
- public @Nullable HubFirmware firmware;
- public @Nullable String serialNumber;
- public @Nullable String rfIDInt;
- public @Nullable String rfID;
- public int rfStatus;
- public @Nullable Times times;
- public @Nullable String brand;
- public boolean rcUp;
- public boolean remoteConnectEnabled;
-
- public String getHubName() {
- return hubName != null ? new String(Base64.getDecoder().decode(hubName)) : "";
- }
-}
+++ /dev/null
-/**
- * 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.requests;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * State of a single Repeater for being updated by an HD PowerView Hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class RepeaterBlinking {
- public Repeater repeater;
-
- public class Repeater {
- public int id;
- public boolean blinkEnabled;
-
- public Repeater(int id, boolean enable) {
- this.id = id;
- this.blinkEnabled = enable;
- }
- }
-
- public RepeaterBlinking(int id, boolean enable) {
- repeater = new Repeater(id, enable);
- }
-}
+++ /dev/null
-/**
- * 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.requests;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.hdpowerview.internal.api.Color;
-
-/**
- * Color state of a single Repeater for being updated by an HD PowerView Hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class RepeaterColor {
- public Repeater repeater;
-
- public class Repeater {
- public int id;
- public Color color;
-
- public Repeater(int id, Color color) {
- this.id = id;
- this.color = color;
- }
- }
-
- public RepeaterColor(int id, Color color) {
- repeater = new Repeater(id, color);
- }
-}
+++ /dev/null
-/**
- * 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.requests;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * A request to calibrate a shade
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class ShadeCalibrate {
-
- public ShadeMotion shade;
-
- public ShadeCalibrate() {
- this.shade = new ShadeMotion(ShadeMotion.Type.CALIBRATE);
- }
-}
+++ /dev/null
-/**
- * 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.requests;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * A request to jog a shade for identification
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class ShadeJog {
-
- public ShadeMotion shade;
-
- public ShadeJog() {
- this.shade = new ShadeMotion(ShadeMotion.Type.JOG);
- }
-}
+++ /dev/null
-/**
- * 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.requests;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * A motion directive for a shade
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-class ShadeMotion {
-
- public enum Type {
- STOP("stop"),
- JOG("jog"),
- CALIBRATE("calibrate");
-
- private String motion;
-
- Type(String motion) {
- this.motion = motion;
- }
-
- public String getMotion() {
- return this.motion;
- }
- }
-
- public String motion;
-
- public ShadeMotion(Type motionType) {
- this.motion = motionType.getMotion();
- }
-}
+++ /dev/null
-/**
- * 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.requests;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
-
-/**
- * A request to set the position of a shade
- *
- * @author Andy Lintner - Initial contribution
- */
-@NonNullByDefault
-public class ShadeMove {
-
- public ShadePositions shade;
-
- public ShadeMove(ShadePosition position) {
- this.shade = new ShadePositions(position);
- }
-}
+++ /dev/null
-/**
- * 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.requests;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.hdpowerview.internal.api.ShadePosition;
-
-/**
- * The position of a shade to set
- *
- * @author Andy Lintner - Initial contribution
- */
-@NonNullByDefault
-class ShadePositions {
-
- public ShadePosition positions;
-
- public ShadePositions(ShadePosition position) {
- this.positions = position;
- }
-}
+++ /dev/null
-/**
- * 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.requests;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * A request to stop the movement of a shade
- *
- * @author Andrew Fiddian-Green - Initial contribution
- */
-@NonNullByDefault
-public class ShadeStop {
-
- public ShadeMotion shade;
-
- public ShadeStop() {
- this.shade = new ShadeMotion(ShadeMotion.Type.STOP);
- }
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.hdpowerview.internal.api.HubFirmware;
-
-/**
- * Firmware information for an HD PowerView hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class FirmwareVersion {
- @Nullable
- public HubFirmware firmware;
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * State of a single Repeater, as returned by an HD PowerView Hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class Repeater {
- public @Nullable RepeaterData repeater;
-}
+++ /dev/null
-/**
- * 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.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;
-
-/**
- * Repeater data for a single Repeater, as returned by an HD PowerView Hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class RepeaterData {
- public int id;
- public @Nullable String name;
- public int roomId;
- public int groupId;
- public boolean blinkEnabled;
- public @Nullable Firmware firmware;
- public @Nullable Color color;
-
- public String getName() {
- return new String(Base64.getDecoder().decode(name));
- }
-}
+++ /dev/null
-/**
- * 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.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * Repeaters connected to an HD PowerView Hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class Repeaters {
- public @Nullable List<RepeaterData> repeaterData;
- public @Nullable List<Integer> repeaterIds;
-}
+++ /dev/null
-/**
- * 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 java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * State of all Scene Collections in an HD PowerView hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class SceneCollections {
-
- public @Nullable List<SceneCollection> sceneCollectionData;
- public @Nullable List<Integer> sceneCollectionIds;
-
- /*
- * 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 SceneCollection implements Comparable<SceneCollection> {
- public int id;
- public @Nullable String name;
- public int order;
- public int colorId;
- public int iconId;
-
- @Override
- public boolean equals(@Nullable Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof SceneCollection)) {
- return false;
- }
- SceneCollection other = (SceneCollection) o;
-
- return this.id == other.id && this.name.equals(other.name) && 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 + order;
- result = prime * result + colorId;
- result = prime * result + iconId;
-
- return result;
- }
-
- @Override
- public int compareTo(SceneCollection other) {
- return Integer.compare(order, other.order);
- }
-
- public String getName() {
- return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8);
- }
- }
-}
+++ /dev/null
-/**
- * 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 java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * State of all Scenes in an HD PowerView hub
- *
- * @author Andy Lintner - Initial contribution
- */
-@NonNullByDefault
-public class Scenes {
-
- public @Nullable List<Scene> sceneData;
- public @Nullable List<Integer> 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<Scene> {
- 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);
- }
- }
-}
+++ /dev/null
-/**
- * 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 java.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-
-/**
- * State of all Scheduled Events in an HD PowerView hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class ScheduledEvents {
-
- public static final EnumSet<DayOfWeek> WEEKDAYS = EnumSet.of(DayOfWeek.MONDAY, DayOfWeek.TUESDAY,
- DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY);
-
- public static final EnumSet<DayOfWeek> WEEKENDS = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
-
- public static final int SCHEDULED_EVENT_TYPE_TIME = 0;
- public static final int SCHEDULED_EVENT_TYPE_SUNRISE = 1;
- public static final int SCHEDULED_EVENT_TYPE_SUNSET = 2;
-
- public @Nullable List<ScheduledEvent> scheduledEventData;
- public @Nullable List<Integer> 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<DayOfWeek> getDays() {
- EnumSet<DayOfWeek> 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;
- }
- }
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.hdpowerview.internal.api.responses.Shades.ShadeData;
-
-/**
- * State of a single Shade, as returned by an HD PowerView hub
- *
- * @author Andrew Fiddian-Green - Initial contribution
- */
-@NonNullByDefault
-public class Shade {
-
- public @Nullable ShadeData shade;
-}
+++ /dev/null
-/**
- * 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.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;
-
-/**
- * State of all Shades, as returned by an HD PowerView hub
- *
- * @author Andy Lintner - Initial contribution
- */
-@NonNullByDefault
-public class Shades {
-
- public @Nullable List<ShadeData> shadeData;
- public @Nullable List<Integer> 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);
- }
- }
-}
+++ /dev/null
-/**
- * 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.util.List;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.hdpowerview.internal.api.SurveyData;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Survey data of a single Shade, as returned by an HD PowerView hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class Survey {
- @SerializedName("shade_id")
- public int shadeId;
- @SerializedName("survey")
- public @Nullable List<SurveyData> surveyData;
-}
+++ /dev/null
-/**
- * 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 org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.hdpowerview.internal.api.UserData;
-
-/**
- * Response with {@link UserData} for an HD PowerView hub
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class UserDataResponse {
- public @Nullable UserData userData;
-}
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.Scene;
+import org.openhab.binding.hdpowerview.internal.dto.SceneCollection;
+import org.openhab.binding.hdpowerview.internal.dto.ScheduledEvent;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelGroupUID;
String timeString, daysString;
switch (scheduledEvent.eventType) {
- case ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME:
+ case ScheduledEvent.SCHEDULED_EVENT_TYPE_TIME:
timeString = LocalTime.of(scheduledEvent.hour, scheduledEvent.minute).toString();
break;
- case ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE:
+ case ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE:
if (scheduledEvent.minute == 0) {
timeString = translationProvider.getText("dynamic-channel.automation.at-sunrise");
} else if (scheduledEvent.minute < 0) {
getFormattedTimeOffset(scheduledEvent.minute));
}
break;
- case ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET:
+ case ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNSET:
if (scheduledEvent.minute == 0) {
timeString = translationProvider.getText("dynamic-channel.automation.at-sunset");
} else if (scheduledEvent.minute < 0) {
EnumSet<DayOfWeek> days = scheduledEvent.getDays();
if (EnumSet.allOf(DayOfWeek.class).equals(days)) {
daysString = translationProvider.getText("dynamic-channel.automation.all-days");
- } else if (ScheduledEvents.WEEKDAYS.equals(days)) {
+ } else if (ScheduledEvent.WEEKDAYS.equals(days)) {
daysString = translationProvider.getText("dynamic-channel.automation.weekdays");
- } else if (ScheduledEvents.WEEKENDS.equals(days)) {
+ } else if (ScheduledEvent.WEEKENDS.equals(days)) {
daysString = translationProvider.getText("dynamic-channel.automation.weekends");
} else {
StringJoiner joiner = new StringJoiner(", ");
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.Scene;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelGroupUID;
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.SceneCollection;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelGroupUID;
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.ShadeData;
+import org.openhab.binding.hdpowerview.internal.dto.ShadeData;
+import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData;
import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
import org.openhab.binding.hdpowerview.internal.handler.HDPowerViewHubHandler;
import org.openhab.core.io.console.Console;
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.ShadeData;
+import org.openhab.binding.hdpowerview.internal.dto.responses.RepeaterData;
+import org.openhab.binding.hdpowerview.internal.dto.responses.Shades;
import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
--- /dev/null
+/**
+ * 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.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * An enum for the type of power supply in a shade.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+@NonNullByDefault
+public enum BatteryKind {
+ ERROR_UNKNOWN(-1),
+ HARDWIRED_POWER_SUPPLY(1),
+ BATTERY_WAND(2),
+ RECHARGEABLE_BATTERY_WAND(3);
+
+ private int batteryKind;
+
+ private BatteryKind(int i) {
+ this.batteryKind = i;
+ }
+
+ /**
+ * Determine the BatteryKind by parsing the given string value.
+ *
+ * @param value the string to parse, or null.
+ * @return the BatteryKind or ERROR_UNKNOWN in case of error.
+ */
+ public static BatteryKind fromString(@Nullable String value) {
+ if (value != null) {
+ try {
+ int intValue = Integer.parseInt(value);
+ for (BatteryKind e : values()) {
+ if (e.batteryKind == intValue) {
+ return e;
+ }
+ }
+ } catch (NumberFormatException e) {
+ // fall through
+ }
+ }
+ return ERROR_UNKNOWN;
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.library.types.HSBType;
+
+/**
+ * Color and brightness information for HD PowerView repeater
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class Color {
+ public int brightness;
+ public int red;
+ public int green;
+ public int blue;
+
+ public Color(int brightness, HSBType hsbType) {
+ this.brightness = brightness;
+ int rgb = hsbType.getRGB();
+ java.awt.Color color = new java.awt.Color(rgb);
+ red = color.getRed();
+ green = color.getGreen();
+ blue = color.getBlue();
+ }
+
+ public Color(int brightness, java.awt.Color color) {
+ this.brightness = brightness;
+ red = color.getRed();
+ green = color.getGreen();
+ blue = color.getBlue();
+ }
+
+ public Color(int brightness, int red, int green, int blue) {
+ this.brightness = brightness;
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%d.%d.%d/%d%%", red, green, blue, brightness);
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Shade coordinate system (a.k.a. position kind), as returned by the HD PowerView hub.
+ *
+ * @param NONE a coordinate system that does not refer to any type of physical rail.
+ * @param PRIMARY_POSITION primary rail, whose coordinate value 0 means shade is closed.
+ * @param SECONDARY_POSITION secondary rail, whose coordinate value 0 means shade is open.
+ * @param VANE_TILT_POSITION vane/tilt operator, whose coordinate system is for vanes.
+ * @param ERROR_UNKNOWN unsupported coordinate system.
+ *
+ * @author Andy Lintner - Initial contribution of the original enum called
+ * ShadePositionKind
+ *
+ * @author Andrew Fiddian-Green - Rewritten as a new enum called
+ * CoordinateSystem to support secondary rail positions and be more
+ * explicit on coordinate directions and ranges
+ */
+@NonNullByDefault
+public enum CoordinateSystem {
+ /*-
+ * Specifies the coordinate system used for the position of the shade. Top-down
+ * shades are in the same coordinate space as bottom-up shades. Shade position
+ * values for top-down shades would be reversed for bottom-up shades. For
+ * example, since 65535 is the open value for a bottom-up shade, it is the
+ * closed value for a top-down shade. The top-down/bottom-up shade is different
+ * in that instead of the top and bottom rail operating in one coordinate space
+ * like the top-down and the bottom-up, it operates in two where the top
+ * (middle) rail closed value is 0 and the bottom (primary) rail closed position
+ * is also 0 and fully open for both is 65535
+ *
+ * The position element can take on multiple states depending on the family of
+ * shade under control.
+ *
+ * The ranges of position integer values are
+ * shades: 0..65535
+ * vanes: 0..32767
+ *
+ * Shade fully up: (top-down: open, bottom-up: closed)
+ * posKind: 1 {ZERO_IS_CLOSED}
+ * position: 65535
+ *
+ * Shade and vane fully down: (top-down: closed, bottom-up: open)
+ * posKind: 1 {ZERO_IS_CLOSED}
+ * position1: 0
+ *
+ * ALTERNATE: Shade and vane fully down: (top-down: closed, bottom-up: open)
+ * posKind: 3 {VANE_COORDS}
+ * position: 0
+ *
+ * Shade fully down (closed) and vane fully up (open):
+ * posKind: 3 {VANE_COORDS}
+ * position: 32767
+ *
+ * Dual action, secondary top-down shade fully up (closed):
+ * posKind: 2 {ZERO_IS_OPEN}
+ * position: 0
+ *
+ * Dual action, secondary top-down shade fully down (open):
+ * posKind: 2 {ZERO_IS_OPEN}
+ * position: 65535
+ *
+ */
+ NONE,
+ PRIMARY_POSITION,
+ SECONDARY_POSITION,
+ VANE_TILT_POSITION,
+ ERROR_UNKNOWN;
+
+ public static final int MAX_SHADE = 65535;
+ public static final int MAX_VANE = 32767;
+
+ /**
+ * Converts an HD PowerView posKind integer value to a CoordinateSystem enum value.
+ *
+ * @param posKind input integer value.
+ * @return corresponding CoordinateSystem enum.
+ */
+ public static CoordinateSystem fromPosKind(int posKind) {
+ try {
+ return CoordinateSystem.values()[posKind];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return ERROR_UNKNOWN;
+ }
+ }
+
+ /**
+ * Check if the coordinate system matches the given posKind.
+ *
+ * @param posKind
+ * @return true if equal.
+ */
+ public boolean equals(@Nullable Integer posKind) {
+ return (posKind != null) && (posKind.intValue() == ordinal());
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Firmware version information for HD PowerView components
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class Firmware {
+ @Nullable
+ public String name;
+ public int revision;
+ public int subRevision;
+ public int build;
+
+ @Override
+ public String toString() {
+ return String.format("%d.%d.%d", revision, subRevision, build);
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Firmware information for an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class HubFirmware {
+ @Nullable
+ public Firmware mainProcessor;
+ @Nullable
+ public Firmware radio;
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * State of a single Scene, as returned by an HD PowerView Hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class Scene implements Comparable<Scene> {
+ 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 && Objects.equals(name, 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;
+ String name = this.name;
+ 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);
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * State of a single Scene Collection, as returned by an HD PowerView Hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class SceneCollection implements Comparable<SceneCollection> {
+ public int id;
+ public @Nullable String name;
+ public int order;
+ public int colorId;
+ public int iconId;
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof SceneCollection)) {
+ return false;
+ }
+ SceneCollection other = (SceneCollection) o;
+
+ return this.id == other.id && Objects.equals(name, other.name) && this.order == other.order
+ && this.colorId == other.colorId && this.iconId == other.iconId;
+ }
+
+ @Override
+ public final int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ String name = this.name;
+ result = prime * result + id;
+ result = prime * result + (name == null ? 0 : name.hashCode());
+ result = prime * result + order;
+ result = prime * result + colorId;
+ result = prime * result + iconId;
+
+ return result;
+ }
+
+ @Override
+ public int compareTo(SceneCollection other) {
+ return Integer.compare(order, other.order);
+ }
+
+ public String getName() {
+ return new String(Base64.getDecoder().decode(name), StandardCharsets.UTF_8);
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import java.time.DayOfWeek;
+import java.util.EnumSet;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * State of a single Scheduled Event, as returned by an HD PowerView Hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class ScheduledEvent {
+ public static final EnumSet<DayOfWeek> WEEKDAYS = EnumSet.of(DayOfWeek.MONDAY, DayOfWeek.TUESDAY,
+ DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY);
+
+ public static final EnumSet<DayOfWeek> WEEKENDS = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
+
+ public static final int SCHEDULED_EVENT_TYPE_TIME = 0;
+ public static final int SCHEDULED_EVENT_TYPE_SUNRISE = 1;
+ public static final int SCHEDULED_EVENT_TYPE_SUNSET = 2;
+
+ 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<DayOfWeek> getDays() {
+ EnumSet<DayOfWeek> 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;
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import java.util.Base64;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Shade data for a single Shade, as returned by an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public 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);
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import static org.openhab.binding.hdpowerview.internal.dto.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
+ *
+ * @author Andy Lintner - Initial contribution
+ * @author Andrew Fiddian-Green - Added support for secondary rail positions
+ */
+@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() {
+ }
+
+ /**
+ * 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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;
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Survey data of a single Shade, as returned by an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class SurveyData {
+ @SerializedName("neighbor_id")
+ public int neighborId;
+ public int rssi;
+
+ @Override
+ public String toString() {
+ return String.format("{neighbor id:%d, rssi:%d}", neighborId, rssi);
+ }
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Times as part of {@link UserData} for an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class Times {
+ public @Nullable String timezone;
+ public int localSunriseTimeInMinutes;
+ public int localSunsetTimeInMinutes;
+ public int currentOffset;
+ public double longitude;
+ public double latitude;
+}
--- /dev/null
+/**
+ * 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.dto;
+
+import java.util.Base64;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * User data for an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class UserData {
+ public @Nullable String hubName;
+ public boolean localTimeDataSet;
+ public boolean enableScheduledEvents;
+ public boolean editingEnabled;
+ public boolean setupCompleted;
+ public @Nullable String gateway;
+ public @Nullable String dns;
+ public boolean staticIp;
+ @SerializedName("_id")
+ public @Nullable String id;
+ public @Nullable Color color;
+ public boolean autoBackup;
+ public @Nullable String ip;
+ public @Nullable String macAddress;
+ public @Nullable String mask;
+ public boolean wireless;
+ public @Nullable HubFirmware firmware;
+ public @Nullable String serialNumber;
+ public @Nullable String rfIDInt;
+ public @Nullable String rfID;
+ public int rfStatus;
+ public @Nullable Times times;
+ public @Nullable String brand;
+ public boolean rcUp;
+ public boolean remoteConnectEnabled;
+
+ public String getHubName() {
+ return hubName != null ? new String(Base64.getDecoder().decode(hubName)) : "";
+ }
+}
--- /dev/null
+/**
+ * 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.dto.requests;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * State of a single Repeater for being updated by an HD PowerView Hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class RepeaterBlinking {
+ public Repeater repeater;
+
+ public class Repeater {
+ public int id;
+ public boolean blinkEnabled;
+
+ public Repeater(int id, boolean enable) {
+ this.id = id;
+ this.blinkEnabled = enable;
+ }
+ }
+
+ public RepeaterBlinking(int id, boolean enable) {
+ repeater = new Repeater(id, enable);
+ }
+}
--- /dev/null
+/**
+ * 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.dto.requests;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hdpowerview.internal.dto.Color;
+
+/**
+ * Color state of a single Repeater for being updated by an HD PowerView Hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class RepeaterColor {
+ public Repeater repeater;
+
+ public class Repeater {
+ public int id;
+ public Color color;
+
+ public Repeater(int id, Color color) {
+ this.id = id;
+ this.color = color;
+ }
+ }
+
+ public RepeaterColor(int id, Color color) {
+ repeater = new Repeater(id, color);
+ }
+}
--- /dev/null
+/**
+ * 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.dto.requests;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A request to calibrate a shade
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class ShadeCalibrate {
+
+ public ShadeMotion shade;
+
+ public ShadeCalibrate() {
+ this.shade = new ShadeMotion(ShadeMotion.Type.CALIBRATE);
+ }
+}
--- /dev/null
+/**
+ * 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.dto.requests;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A request to jog a shade for identification
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class ShadeJog {
+
+ public ShadeMotion shade;
+
+ public ShadeJog() {
+ this.shade = new ShadeMotion(ShadeMotion.Type.JOG);
+ }
+}
--- /dev/null
+/**
+ * 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.dto.requests;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A motion directive for a shade
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+class ShadeMotion {
+
+ public enum Type {
+ STOP("stop"),
+ JOG("jog"),
+ CALIBRATE("calibrate");
+
+ private String motion;
+
+ Type(String motion) {
+ this.motion = motion;
+ }
+
+ public String getMotion() {
+ return this.motion;
+ }
+ }
+
+ public String motion;
+
+ public ShadeMotion(Type motionType) {
+ this.motion = motionType.getMotion();
+ }
+}
--- /dev/null
+/**
+ * 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.dto.requests;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hdpowerview.internal.dto.ShadePosition;
+
+/**
+ * A request to set the position of a shade
+ *
+ * @author Andy Lintner - Initial contribution
+ */
+@NonNullByDefault
+public class ShadeMove {
+
+ public ShadePositions shade;
+
+ public ShadeMove(ShadePosition position) {
+ this.shade = new ShadePositions(position);
+ }
+}
--- /dev/null
+/**
+ * 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.dto.requests;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.hdpowerview.internal.dto.ShadePosition;
+
+/**
+ * The position of a shade to set
+ *
+ * @author Andy Lintner - Initial contribution
+ */
+@NonNullByDefault
+class ShadePositions {
+
+ public ShadePosition positions;
+
+ public ShadePositions(ShadePosition position) {
+ this.positions = position;
+ }
+}
--- /dev/null
+/**
+ * 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.dto.requests;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A request to stop the movement of a shade
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+@NonNullByDefault
+public class ShadeStop {
+
+ public ShadeMotion shade;
+
+ public ShadeStop() {
+ this.shade = new ShadeMotion(ShadeMotion.Type.STOP);
+ }
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hdpowerview.internal.dto.HubFirmware;
+
+/**
+ * Firmware information for an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class FirmwareVersion {
+ @Nullable
+ public HubFirmware firmware;
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * State of a single Repeater, as returned by an HD PowerView Hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class Repeater {
+ public @Nullable RepeaterData repeater;
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import java.util.Base64;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+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
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class RepeaterData {
+ public int id;
+ public @Nullable String name;
+ public int roomId;
+ public int groupId;
+ public boolean blinkEnabled;
+ public @Nullable Firmware firmware;
+ public @Nullable Color color;
+
+ public String getName() {
+ return new String(Base64.getDecoder().decode(name));
+ }
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Repeaters connected to an HD PowerView Hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class Repeaters {
+ public @Nullable List<RepeaterData> repeaterData;
+ public @Nullable List<Integer> repeaterIds;
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hdpowerview.internal.dto.SceneCollection;
+
+/**
+ * State of all Scene Collections in an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class SceneCollections {
+ public @Nullable List<SceneCollection> sceneCollectionData;
+ public @Nullable List<Integer> sceneCollectionIds;
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hdpowerview.internal.dto.Scene;
+
+/**
+ * State of all Scenes in an HD PowerView hub
+ *
+ * @author Andy Lintner - Initial contribution
+ */
+@NonNullByDefault
+public class Scenes {
+ public @Nullable List<Scene> sceneData;
+ public @Nullable List<Integer> sceneIds;
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hdpowerview.internal.dto.ScheduledEvent;
+
+/**
+ * State of all Scheduled Events in an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class ScheduledEvents {
+ public @Nullable List<ScheduledEvent> scheduledEventData;
+ public @Nullable List<Integer> scheduledEventIds;
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hdpowerview.internal.dto.ShadeData;
+
+/**
+ * State of a single Shade, as returned by an HD PowerView hub
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+@NonNullByDefault
+public class Shade {
+ public @Nullable ShadeData shade;
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hdpowerview.internal.dto.ShadeData;
+
+/**
+ * State of all Shades, as returned by an HD PowerView hub
+ *
+ * @author Andy Lintner - Initial contribution
+ */
+@NonNullByDefault
+public class Shades {
+ public @Nullable List<ShadeData> shadeData;
+ public @Nullable List<Integer> shadeIds;
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hdpowerview.internal.dto.SurveyData;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Survey data of a single Shade, as returned by an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class Survey {
+ @SerializedName("shade_id")
+ public int shadeId;
+ @SerializedName("survey")
+ public @Nullable List<SurveyData> surveyData;
+}
--- /dev/null
+/**
+ * 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.dto.responses;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.hdpowerview.internal.dto.UserData;
+
+/**
+ * Response with {@link UserData} for an HD PowerView hub
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class UserDataResponse {
+ public @Nullable UserData userData;
+}
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.Scene;
+import org.openhab.binding.hdpowerview.internal.dto.SceneCollection;
+import org.openhab.binding.hdpowerview.internal.dto.ScheduledEvent;
+import org.openhab.binding.hdpowerview.internal.dto.ShadeData;
+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.Scenes;
+import org.openhab.binding.hdpowerview.internal.dto.responses.ScheduledEvents;
+import org.openhab.binding.hdpowerview.internal.dto.responses.Shades;
import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
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;
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;
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.ShadeData;
+import org.openhab.binding.hdpowerview.internal.dto.ShadePosition;
+import org.openhab.binding.hdpowerview.internal.dto.SurveyData;
import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
import org.openhab.binding.hdpowerview.internal.exceptions.HubInvalidResponseException;
import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
+++ /dev/null
-/**
- * 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.assertEquals;
-import static org.mockito.Mockito.mock;
-
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.List;
-
-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.core.thing.Channel;
-import org.openhab.core.thing.ChannelGroupUID;
-import org.openhab.core.thing.ThingUID;
-import org.osgi.framework.Bundle;
-import org.slf4j.LoggerFactory;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.Logger;
-
-/**
- * Unit tests for {@link AutomationChannelBuilder}.
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class AutomationChannelBuilderTest {
-
- private static final ChannelGroupUID CHANNEL_GROUP_UID = new ChannelGroupUID(
- new ThingUID(HDPowerViewBindingConstants.BINDING_ID, AutomationChannelBuilderTest.class.getSimpleName()),
- HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED);
-
- private static final HDPowerViewTranslationProvider TRANSLATION_PROVIDER = new HDPowerViewTranslationProvider(
- mock(Bundle.class), new MockedTranslationProvider(), new MockedLocaleProvider());
-
- private AutomationChannelBuilder builder = AutomationChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
- private List<Scene> scenes = new ArrayList<>();
- private List<SceneCollection> sceneCollections = new ArrayList<>();
-
- @BeforeEach
- private void setUp() {
- final Logger logger = (Logger) LoggerFactory.getLogger(AutomationChannelBuilder.class);
- logger.setLevel(Level.OFF);
- builder = AutomationChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
-
- Scene scene = new Scene();
- scene.id = 1;
- scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes());
- scenes = new ArrayList<>(List.of(scene));
-
- SceneCollection sceneCollection = new SceneCollection();
- sceneCollection.id = 2;
- sceneCollection.name = Base64.getEncoder().encodeToString(("TestSceneCollection").getBytes());
- sceneCollections = new ArrayList<>(List.of(sceneCollection));
- }
-
- @Test
- public void sceneSunriseWeekends() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE);
- scheduledEvent.daySaturday = true;
- scheduledEvent.daySunday = true;
-
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
-
- assertEquals(1, channels.size());
- assertEquals("TestScene, At sunrise, Weekends", channels.get(0).getLabel());
- }
-
- @Test
- public void sceneSunsetWeekdays() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET);
- scheduledEvent.dayMonday = true;
- scheduledEvent.dayTuesday = true;
- scheduledEvent.dayWednesday = true;
- scheduledEvent.dayThursday = true;
- scheduledEvent.dayFriday = true;
-
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
-
- assertEquals(1, channels.size());
- assertEquals("TestScene, At sunset, Weekdays", channels.get(0).getLabel());
- }
-
- @Test
- public void sceneTimeAllDays() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_TIME);
- scheduledEvent.dayMonday = true;
- scheduledEvent.dayTuesday = true;
- scheduledEvent.dayWednesday = true;
- scheduledEvent.dayThursday = true;
- scheduledEvent.dayFriday = true;
- scheduledEvent.daySaturday = true;
- scheduledEvent.daySunday = true;
- scheduledEvent.hour = 6;
- scheduledEvent.minute = 30;
-
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
-
- assertEquals(1, channels.size());
- assertEquals("TestScene, 06:30, All days", channels.get(0).getLabel());
- }
-
- @Test
- public void sceneMinutesBeforeSunriseMondayTuesday() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE);
- scheduledEvent.dayMonday = true;
- scheduledEvent.dayTuesday = true;
- scheduledEvent.minute = -15;
-
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
-
- assertEquals(1, channels.size());
- assertEquals("TestScene, 15m before sunrise, Mon, Tue", channels.get(0).getLabel());
- }
-
- @Test
- public void sceneHoursMinutesAfterSunriseMonday() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE);
- scheduledEvent.dayMonday = true;
- scheduledEvent.minute = 61;
-
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
-
- assertEquals(1, channels.size());
- assertEquals("TestScene, 1hr 1m after sunrise, Mon", channels.get(0).getLabel());
- }
-
- @Test
- public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET);
- scheduledEvent.dayWednesday = true;
- scheduledEvent.dayThursday = true;
- scheduledEvent.dayFriday = true;
- scheduledEvent.minute = -59;
-
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
-
- assertEquals(1, channels.size());
- assertEquals("TestScene, 59m before sunset, Wed, Thu, Fri", channels.get(0).getLabel());
- }
-
- @Test
- public void sceneHourAfterSunsetFridaySaturdaySunday() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNSET);
- scheduledEvent.dayFriday = true;
- scheduledEvent.daySaturday = true;
- scheduledEvent.daySunday = true;
- scheduledEvent.minute = 60;
-
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
-
- assertEquals(1, channels.size());
- assertEquals("TestScene, 1hr after sunset, Fri, Sat, Sun", channels.get(0).getLabel());
- }
-
- @Test
- public void sceneCollection() {
- ScheduledEvent scheduledEvent = createScheduledEventWithSceneCollection(
- ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE);
-
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents)
- .build();
-
- assertEquals(1, channels.size());
- assertEquals("TestSceneCollection, At sunrise, ", channels.get(0).getLabel());
- }
-
- @Test
- public void suppliedListIsUsed() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE);
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> existingChannels = new ArrayList<>(0);
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents)
- .withChannels(existingChannels).build();
-
- assertEquals(existingChannels, channels);
- }
-
- @Test
- public void emptyListWhenNoScheduledEvents() {
- List<Channel> channels = builder.build();
-
- assertEquals(0, channels.size());
- }
-
- @Test
- public void emptyListWhenNoScenesOrSceneCollections() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE);
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScheduledEvents(scheduledEvents).build();
-
- assertEquals(0, channels.size());
- }
-
- @Test
- public void emptyListWhenNoSceneForScheduledEvent() {
- ScheduledEvent scheduledEvent = createScheduledEventWithSceneCollection(
- ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE);
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
-
- assertEquals(0, channels.size());
- }
-
- @Test
- public void emptyListWhenNoSceneCollectionForScheduledEvent() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE);
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
-
- List<Channel> channels = builder.withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents)
- .build();
-
- assertEquals(0, channels.size());
- }
-
- @Test
- public void groupAndIdAreCorrect() {
- ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvents.SCHEDULED_EVENT_TYPE_SUNRISE);
- scheduledEvent.id = 42;
- List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
- List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
-
- assertEquals(1, channels.size());
- assertEquals(CHANNEL_GROUP_UID.getId(), channels.get(0).getUID().getGroupId());
- assertEquals(Integer.toString(scheduledEvent.id), channels.get(0).getUID().getIdWithoutGroup());
- }
-
- private ScheduledEvent createScheduledEventWithScene(int eventType) {
- ScheduledEvent scheduledEvent = new ScheduledEvent();
- scheduledEvent.id = 1;
- scheduledEvent.sceneId = scenes.get(0).id;
- scheduledEvent.eventType = eventType;
- return scheduledEvent;
- }
-
- private ScheduledEvent createScheduledEventWithSceneCollection(int eventType) {
- ScheduledEvent scheduledEvent = new ScheduledEvent();
- scheduledEvent.id = 1;
- scheduledEvent.sceneCollectionId = sceneCollections.get(0).id;
- scheduledEvent.eventType = eventType;
- return scheduledEvent;
- }
-}
+++ /dev/null
-/**
- * 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 static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.Objects;
-
-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.core.library.types.PercentType;
-import org.openhab.core.types.State;
-import org.openhab.core.types.UnDefType;
-
-import com.google.gson.Gson;
-
-/**
- * Unit tests for HD PowerView binding.
- *
- * @author Andrew Fiddian-Green - Initial contribution
- * @author Jacob Laursen - Add support for scene groups
- */
-@NonNullByDefault
-public class HDPowerViewJUnitTests {
-
- private Gson gson = new Gson();
-
- private <T> T getObjectFromJson(String filename, Class<T> clazz) 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");
- }
- String json = new String(bytes, StandardCharsets.UTF_8);
- return Objects.requireNonNull(gson.fromJson(json, clazz));
- }
- }
-
- /**
- * Test generic JSON shades response.
- */
- @Test
- public void shadeNameIsDecoded() throws IOException {
- Shades shades = getObjectFromJson("shades.json", Shades.class);
- List<ShadeData> shadeData = shades.shadeData;
- assertNotNull(shadeData);
- assertEquals(3, shadeData.size());
- ShadeData shade = shadeData.get(0);
- assertEquals("Shade 2", shade.getName());
- }
-
- /**
- * Test the BatteryKind decoding.
- */
- @Test
- public void testBatteryKind() throws IOException {
- Shades shades = getObjectFromJson("shades.json", Shades.class);
- List<ShadeData> shadeData = shades.shadeData;
- assertNotNull(shadeData);
- ShadeData shade = shadeData.get(0);
- assertEquals(BatteryKind.HARDWIRED_POWER_SUPPLY, shade.getBatteryKind());
- shade = shadeData.get(1);
- assertEquals(BatteryKind.ERROR_UNKNOWN, shade.getBatteryKind());
- }
-
- /**
- * Test generic JSON scene response.
- */
- @Test
- public void sceneNameIsDecoded() throws IOException {
- Scenes scenes = getObjectFromJson("scenes.json", Scenes.class);
- List<Scene> sceneData = scenes.sceneData;
- assertNotNull(sceneData);
- assertEquals(4, sceneData.size());
- Scene scene = sceneData.get(0);
- assertEquals("Door Open", scene.getName());
- }
-
- /**
- * Test generic JSON scene collection response.
- */
- @Test
- public void sceneCollectionNameIsDecoded() throws IOException {
- SceneCollections sceneCollections = getObjectFromJson("sceneCollections.json", SceneCollections.class);
-
- List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
- assertNotNull(sceneCollectionData);
- assertEquals(1, sceneCollectionData.size());
-
- SceneCollection sceneCollection = sceneCollectionData.get(0);
- assertEquals("Børn op", sceneCollection.getName());
- }
-
- /**
- * Test the JSON parsing for a duette top down bottom up shade.
- */
- @Test
- public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws IOException {
- Shades shades = getObjectFromJson("duette.json", Shades.class);
- List<ShadeData> shadesData = shades.shadeData;
- assertNotNull(shadesData);
-
- assertEquals(1, shadesData.size());
- ShadeData shadeData = shadesData.get(0);
- assertNotNull(shadeData);
-
- assertEquals("Gardin 1", shadeData.getName());
- assertEquals(63778, shadeData.id);
-
- ShadePosition shadePos = shadeData.positions;
- assertNotNull(shadePos);
-
- Integer capabilitiesValue = shadeData.capabilities;
- assertNotNull(capabilitiesValue);
- assertEquals(7, capabilitiesValue.intValue());
- ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
- Capabilities capabilities = db.getCapabilities(capabilitiesValue);
-
- State pos = shadePos.getState(capabilities, PRIMARY_POSITION);
- assertEquals(PercentType.class, pos.getClass());
- assertEquals(59, ((PercentType) pos).intValue());
-
- pos = shadePos.getState(capabilities, SECONDARY_POSITION);
- assertEquals(PercentType.class, pos.getClass());
- assertEquals(35, ((PercentType) pos).intValue());
-
- pos = shadePos.getState(capabilities, VANE_TILT_POSITION);
- assertEquals(UnDefType.class, pos.getClass());
-
- assertEquals(3, shadeData.batteryStatus);
-
- assertEquals(4, shadeData.signalStrength);
-
- assertEquals(8, shadeData.type);
-
- assertTrue(db.isTypeInDatabase(shadeData.type));
- assertTrue(db.isCapabilitiesInDatabase(capabilitiesValue.intValue()));
-
- assertEquals(db.getType(shadeData.type).getCapabilities(), capabilitiesValue.intValue());
-
- assertTrue(db.getCapabilities(capabilitiesValue.intValue()).supportsSecondary());
- assertNotEquals(db.getType(shadeData.type).getCapabilities(), capabilitiesValue.intValue() + 1);
-
- // ==== when changing position1, position2 value is not changed (vice-versa) ====
- ShadePosition shadePosition = shadeData.positions;
- assertNotNull(shadePosition);
- // ==== position2 ====
- State position2Old = shadePosition.getState(capabilities, SECONDARY_POSITION);
- shadePosition.setPosition(capabilities, PRIMARY_POSITION, 99);
- State position2New = shadePosition.getState(capabilities, SECONDARY_POSITION);
- assertEquals(PercentType.class, position2Old.getClass());
- assertEquals(PercentType.class, position2New.getClass());
- assertEquals(((PercentType) position2Old).intValue(), ((PercentType) position2New).intValue());
-
- // ==== position2 ====
- State position1Old = shadePosition.getState(capabilities, PRIMARY_POSITION);
- shadePosition.setPosition(capabilities, SECONDARY_POSITION, 99);
- State position1New = shadePosition.getState(capabilities, PRIMARY_POSITION);
- assertEquals(PercentType.class, position1Old.getClass());
- assertEquals(PercentType.class, position1New.getClass());
- assertEquals(((PercentType) position1Old).intValue(), ((PercentType) position1New).intValue());
- }
-}
+++ /dev/null
-/**
- * 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 static org.openhab.binding.hdpowerview.internal.api.CoordinateSystem.*;
-
-import java.util.List;
-import java.util.regex.Pattern;
-
-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.exceptions.HubException;
-import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
-import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
-import org.openhab.core.library.types.PercentType;
-import org.openhab.core.types.State;
-
-/**
- * Unit tests for HD PowerView binding.
- *
- * @author Andrew Fiddian-Green - Initial contribution
- */
-@NonNullByDefault
-public class OnlineCommunicationTest {
-
- private 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");
-
- /**
- * Run a series of ONLINE tests on the communication with a hub.
- *
- * @param hubIPAddress must be a valid hub IP address to run the
- * tests on; or an INVALID IP address to
- * suppress the tests
- * @param allowShadeMovementCommands set to true if you accept that the tests
- * shall physically move the shades
- */
- @Test
- public void testOnlineCommunication() {
- /*
- * NOTE: in order to actually run these tests you must have a hub physically
- * available, and its IP address must be correctly configured in the
- * "hubIPAddress" string constant e.g. "192.168.1.123"
- */
- String hubIPAddress = "192.168.1.xxx";
-
- /*
- * NOTE: set allowShadeMovementCommands = true if you accept physically moving
- * the shades during these tests
- */
- boolean allowShadeMovementCommands = false;
-
- if (VALID_IP_V4_ADDRESS.matcher(hubIPAddress).matches()) {
- // ==== initialize stuff ====
- HttpClient client = new HttpClient();
- assertNotNull(client);
-
- // ==== start the client ====
- try {
- client.start();
- assertTrue(client.isStarted());
- } catch (Exception e) {
- fail(e.getMessage());
- }
-
- HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress);
- assertNotNull(webTargets);
-
- int shadeId = 0;
- ShadePosition shadePos = null;
- Shades shadesX = null;
-
- // ==== get all shades ====
- try {
- shadesX = webTargets.getShades();
- assertNotNull(shadesX);
- List<ShadeData> shadesData = shadesX.shadeData;
- assertNotNull(shadesData);
-
- assertTrue(!shadesData.isEmpty());
- ShadeData shadeData;
- shadeData = shadesData.get(0);
- assertNotNull(shadeData);
- assertTrue(shadeData.getName().length() > 0);
- shadePos = shadeData.positions;
- assertNotNull(shadePos);
- ShadeData shadeZero = shadesData.get(0);
- assertNotNull(shadeZero);
- shadeId = shadeZero.id;
- assertNotEquals(0, shadeId);
-
- for (ShadeData shadexData : shadesData) {
- String shadeName = shadexData.getName();
- assertNotNull(shadeName);
- }
- } catch (HubException e) {
- fail(e.getMessage());
- }
-
- // ==== get all scenes ====
- int sceneId = 0;
- try {
- Scenes scenes = webTargets.getScenes();
- assertNotNull(scenes);
-
- List<Scene> scenesData = scenes.sceneData;
- assertNotNull(scenesData);
-
- assertTrue(!scenesData.isEmpty());
- Scene sceneZero = scenesData.get(0);
- assertNotNull(sceneZero);
- sceneId = sceneZero.id;
- assertTrue(sceneId > 0);
-
- for (Scene scene : scenesData) {
- String sceneName = scene.getName();
- assertNotNull(sceneName);
- }
- } catch (HubException e) {
- fail(e.getMessage());
- }
-
- // ==== refresh a specific shade ====
- ShadeData shadeData = null;
- try {
- assertNotEquals(0, shadeId);
- shadeData = webTargets.refreshShadePosition(shadeId);
- } catch (HubException e) {
- fail(e.getMessage());
- }
-
- // ==== move a specific shade ====
- try {
- assertNotEquals(0, shadeId);
-
- if (shadeData != null) {
- ShadePosition positions = shadeData.positions;
- assertNotNull(positions);
- Integer capabilitiesValue = shadeData.capabilities;
- assertNotNull(capabilitiesValue);
-
- Capabilities capabilities = new ShadeCapabilitiesDatabase()
- .getCapabilities(capabilitiesValue.intValue());
-
- State pos = positions.getState(capabilities, PRIMARY_POSITION);
- assertEquals(PercentType.class, pos.getClass());
-
- int position = ((PercentType) pos).intValue();
- position = position + ((position <= 10) ? 5 : -5);
-
- ShadePosition targetPosition = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION,
- position);
- assertNotNull(targetPosition);
-
- if (allowShadeMovementCommands) {
- webTargets.moveShade(shadeId, targetPosition);
-
- ShadeData newData = webTargets.getShade(shadeId);
- ShadePosition actualPosition = newData.positions;
- assertNotNull(actualPosition);
- assertEquals(targetPosition.getState(capabilities, PRIMARY_POSITION),
- actualPosition.getState(capabilities, PRIMARY_POSITION));
- }
- }
- } catch (HubException e) {
- fail(e.getMessage());
- }
-
- // ==== activate a specific scene ====
- if (allowShadeMovementCommands) {
- try {
- assertNotNull(sceneId);
- webTargets.activateScene(sceneId);
- } catch (HubProcessingException | HubMaintenanceException e) {
- fail(e.getMessage());
- }
- }
-
- // ==== test stop command ====
- if (allowShadeMovementCommands) {
- try {
- assertNotNull(sceneId);
- webTargets.stopShade(shadeId);
- } catch (HubException e) {
- fail(e.getMessage());
- }
- }
-
- // ==== stop the client ====
- if (client.isRunning()) {
- try {
- client.stop();
- } catch (Exception e) {
- fail(e.getMessage());
- }
- }
- }
- }
-}
+++ /dev/null
-/**
- * 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.assertEquals;
-import static org.mockito.Mockito.mock;
-
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.List;
-
-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.core.thing.Channel;
-import org.openhab.core.thing.ChannelGroupUID;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.type.AutoUpdatePolicy;
-import org.osgi.framework.Bundle;
-
-/**
- * Unit tests for {@link SceneChannelBuilder}.
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class SceneChannelBuilderTest {
-
- private static final ChannelGroupUID CHANNEL_GROUP_UID = new ChannelGroupUID(
- new ThingUID(HDPowerViewBindingConstants.BINDING_ID, SceneChannelBuilderTest.class.getSimpleName()),
- HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE);
-
- private static final HDPowerViewTranslationProvider TRANSLATION_PROVIDER = new HDPowerViewTranslationProvider(
- mock(Bundle.class), new MockedTranslationProvider(), new MockedLocaleProvider());
-
- private SceneChannelBuilder builder = SceneChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
-
- @BeforeEach
- private void setUp() {
- builder = SceneChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
- }
-
- @Test
- public void labelIsCorrect() {
- List<Scene> scenes = createScenes();
- List<Channel> channels = builder.withScenes(scenes).build();
-
- assertEquals(1, channels.size());
- assertEquals("TestScene", channels.get(0).getLabel());
- }
-
- @Test
- public void descriptionIsCorrect() {
- List<Scene> scenes = createScenes();
- List<Channel> channels = builder.withScenes(scenes).build();
-
- assertEquals(1, channels.size());
- assertEquals("Activates the scene 'TestScene'", channels.get(0).getDescription());
- }
-
- @Test
- public void groupAndIdAreCorrect() {
- List<Scene> scenes = createScenes();
- List<Channel> channels = builder.withScenes(scenes).build();
-
- assertEquals(1, channels.size());
- assertEquals(CHANNEL_GROUP_UID.getId(), channels.get(0).getUID().getGroupId());
- assertEquals(Integer.toString(scenes.get(0).id), channels.get(0).getUID().getIdWithoutGroup());
- }
-
- @Test
- public void autoUpdatePolicyIsCorrect() {
- List<Scene> scenes = createScenes();
- List<Channel> channels = builder.withScenes(scenes).build();
-
- assertEquals(1, channels.size());
- assertEquals(AutoUpdatePolicy.VETO, channels.get(0).getAutoUpdatePolicy());
- }
-
- @Test
- public void suppliedListIsUsed() {
- List<Scene> scenes = createScenes();
- List<Channel> existingChannels = new ArrayList<>(0);
- List<Channel> channels = builder.withScenes(scenes).withChannels(existingChannels).build();
-
- assertEquals(existingChannels, channels);
- }
-
- @Test
- public void emptyListWhenNoScenes() {
- List<Channel> channels = builder.build();
-
- assertEquals(0, channels.size());
- }
-
- private List<Scene> createScenes() {
- Scene scene = new Scene();
- scene.id = 1;
- scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes());
- return new ArrayList<>(List.of(scene));
- }
-}
+++ /dev/null
-/**
- * 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.assertEquals;
-import static org.mockito.Mockito.mock;
-
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.List;
-
-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.core.thing.Channel;
-import org.openhab.core.thing.ChannelGroupUID;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.thing.type.AutoUpdatePolicy;
-import org.osgi.framework.Bundle;
-
-/**
- * Unit tests for {@link SceneGroupChannelBuilder}.
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class SceneGroupChannelBuilderTest {
-
- private static final ChannelGroupUID CHANNEL_GROUP_UID = new ChannelGroupUID(
- new ThingUID(HDPowerViewBindingConstants.BINDING_ID, SceneGroupChannelBuilderTest.class.getSimpleName()),
- HDPowerViewBindingConstants.CHANNELTYPE_SCENE_GROUP_ACTIVATE);
-
- private static final HDPowerViewTranslationProvider TRANSLATION_PROVIDER = new HDPowerViewTranslationProvider(
- mock(Bundle.class), new MockedTranslationProvider(), new MockedLocaleProvider());
-
- private SceneGroupChannelBuilder builder = SceneGroupChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
-
- @BeforeEach
- private void setUp() {
- builder = SceneGroupChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
- }
-
- @Test
- public void labelIsCorrect() {
- List<SceneCollection> sceneCollections = createSceneCollections();
- List<Channel> channels = builder.withSceneCollections(sceneCollections).build();
-
- assertEquals(1, channels.size());
- assertEquals("TestSceneCollection", channels.get(0).getLabel());
- }
-
- @Test
- public void descriptionIsCorrect() {
- List<SceneCollection> sceneCollections = createSceneCollections();
- List<Channel> channels = builder.withSceneCollections(sceneCollections).build();
-
- assertEquals(1, channels.size());
- assertEquals("Activates the scene group 'TestSceneCollection'", channels.get(0).getDescription());
- }
-
- @Test
- public void groupAndIdAreCorrect() {
- List<SceneCollection> sceneCollections = createSceneCollections();
- List<Channel> channels = builder.withSceneCollections(sceneCollections).build();
-
- assertEquals(1, channels.size());
- assertEquals(CHANNEL_GROUP_UID.getId(), channels.get(0).getUID().getGroupId());
- assertEquals(Integer.toString(sceneCollections.get(0).id), channels.get(0).getUID().getIdWithoutGroup());
- }
-
- @Test
- public void autoUpdatePolicyIsCorrect() {
- List<SceneCollection> sceneCollections = createSceneCollections();
- List<Channel> channels = builder.withSceneCollections(sceneCollections).build();
-
- assertEquals(1, channels.size());
- assertEquals(AutoUpdatePolicy.VETO, channels.get(0).getAutoUpdatePolicy());
- }
-
- @Test
- public void suppliedListIsUsed() {
- List<SceneCollection> sceneCollections = createSceneCollections();
- List<Channel> existingChannels = new ArrayList<>(0);
- List<Channel> channels = builder.withSceneCollections(sceneCollections).withChannels(existingChannels).build();
-
- assertEquals(existingChannels, channels);
- }
-
- @Test
- public void emptyListWhenNoSceneCollections() {
- List<Channel> channels = builder.build();
-
- assertEquals(0, channels.size());
- }
-
- private List<SceneCollection> createSceneCollections() {
- SceneCollection sceneCollection = new SceneCollection();
- sceneCollection.id = 1;
- sceneCollection.name = Base64.getEncoder().encodeToString(("TestSceneCollection").getBytes());
- return new ArrayList<>(List.of(sceneCollection));
- }
-}
+++ /dev/null
-/**
- * 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 static org.openhab.binding.hdpowerview.internal.api.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.core.library.types.PercentType;
-import org.openhab.core.types.State;
-import org.openhab.core.types.UnDefType;
-
-/**
- * Unit tests for Shade Position setting and getting.
- *
- * @author Andrew Fiddian-Green - Initial contribution
- */
-@NonNullByDefault
-public class ShadePositionTest {
-
- private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
-
- /**
- * General tests of the database of known types.
- */
- @Test
- public void testKnownTypesDatabase() {
- assertTrue(db.isTypeInDatabase(4));
- assertTrue(db.isCapabilitiesInDatabase(0));
- assertTrue(db.isCapabilitiesInDatabase(10));
-
- assertTrue(db.getCapabilities(0).supportsPrimary());
- assertTrue(db.getCapabilities(1).supportsTiltOnClosed());
- assertTrue(db.getCapabilities(2).supportsTilt180());
- assertTrue(db.getCapabilities(2).supportsTiltAnywhere());
- assertTrue(db.getCapabilities(4).supportsTilt180());
- assertTrue(db.getCapabilities(4).supportsTiltAnywhere());
- assertTrue(db.getCapabilities(5).supportsTilt180());
- assertFalse(db.getCapabilities(5).supportsPrimary());
- assertTrue(db.getCapabilities(6).isPrimaryInverted());
- assertTrue(db.getCapabilities(7).supportsSecondary());
- assertTrue(db.getCapabilities(8).supportsSecondaryOverlapped());
- assertTrue(db.getCapabilities(9).supportsSecondaryOverlapped());
- assertTrue(db.getCapabilities(9).supportsTiltOnClosed());
-
- assertEquals(db.getType(4).getCapabilities(), 0);
- assertEquals(db.getType(-1).getCapabilities(), -1);
-
- assertFalse(db.isTypeInDatabase(99));
- assertFalse(db.isCapabilitiesInDatabase(99));
-
- assertFalse(db.getCapabilities(0).isPrimaryInverted());
- assertFalse(db.getCapabilities(-1).isPrimaryInverted());
- assertFalse(db.getCapabilities(99).isPrimaryInverted());
-
- assertFalse(db.getCapabilities(0).supportsSecondary());
- assertFalse(db.getCapabilities(-1).supportsSecondary());
- assertFalse(db.getCapabilities(99).supportsSecondary());
- }
-
- /**
- * Helper method; test if shade position is a PercentType and that its value is correct.
- *
- * @param position the shade position
- * @param value the test value to compare with
- */
- private void assertShadePosition(State position, int value) {
- assertEquals(PercentType.class, position.getClass());
- assertEquals(value, ((PercentType) position).intValue());
- }
-
- /**
- * Test parsing of Capabilities 1 ShadePosition (shade fully up).
- *
- */
- @Test
- public void testCaps1ShadePositionParsingFullyUp() {
- Capabilities capabilities = db.getCapabilities(1);
- 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);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
- }
-
- /**
- * Test parsing of Capabilities 1 ShadePosition (shade fully down (method 1)).
- *
- */
- @Test
- public void testCaps1ShadePositionParsingShadeFullyDown1() {
- Capabilities capabilities = db.getCapabilities(1);
- 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);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0);
- }
-
- /**
- * Test parsing of Capabilities 1 ShadePosition (shade fully down (method 2)).
- *
- */
- @Test
- public void testCaps1ShadePositionParsingShadeFullyDown2() {
- Capabilities capabilities = db.getCapabilities(1);
- 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);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0);
- }
-
- /**
- * Test parsing of Capabilities 1 ShadePosition (shade fully down (method 2) and vane fully open).
- *
- */
- @Test
- public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() {
- Capabilities capabilities = db.getCapabilities(1);
- 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);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 88);
- }
-
- /**
- * On dual rail shades, it should not be possible to drive the upper rail below the lower rail, or vice-versa. So
- * the binding code applies constraints on setting such positions. This test checks that the constraint code is
- * working.
- */
- @Test
- public void testDualRailConstraints() {
- Capabilities capabilities = db.getCapabilities(7);
- ShadePosition test = new ShadePosition();
-
- // ==== OK !! primary at bottom, secondary at top ====
- test.setPosition(capabilities, PRIMARY_POSITION, 100).setPosition(capabilities, SECONDARY_POSITION, 0);
- assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100);
- assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
-
- // ==== OK !! primary at middle, secondary at top ====
- test.setPosition(capabilities, PRIMARY_POSITION, 50).setPosition(capabilities, SECONDARY_POSITION, 0);
- assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 50);
- assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
-
- // ==== OK !! primary at middle, secondary at middle ====
- test.setPosition(capabilities, PRIMARY_POSITION, 50).setPosition(capabilities, SECONDARY_POSITION, 50);
- assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 50);
- assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 50);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
-
- // ==== IMPOSSIBLE !! secondary at middle, primary above => test the constraining code ====
- test.setPosition(capabilities, SECONDARY_POSITION, 0).setPosition(capabilities, PRIMARY_POSITION, 100);
- test.setPosition(capabilities, SECONDARY_POSITION, 40).setPosition(capabilities, PRIMARY_POSITION, 25);
- assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 40);
- assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 40);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
-
- // ==== OK !! secondary at middle, primary below ====
- test.setPosition(capabilities, SECONDARY_POSITION, 0).setPosition(capabilities, PRIMARY_POSITION, 100);
- test.setPosition(capabilities, SECONDARY_POSITION, 50).setPosition(capabilities, PRIMARY_POSITION, 75);
- assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 75);
- assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 50);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
-
- // ==== IMPOSSIBLE !! primary at middle, secondary below => test the constraining code ====
- test.setPosition(capabilities, SECONDARY_POSITION, 0).setPosition(capabilities, PRIMARY_POSITION, 100);
- test.setPosition(capabilities, PRIMARY_POSITION, 60).setPosition(capabilities, SECONDARY_POSITION, 75);
- assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 60);
- assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 60);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
-
- // ==== OK !! primary at middle, secondary above ====
- test.setPosition(capabilities, SECONDARY_POSITION, 0).setPosition(capabilities, PRIMARY_POSITION, 100);
- test.setPosition(capabilities, PRIMARY_POSITION, 60).setPosition(capabilities, SECONDARY_POSITION, 25);
- assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 60);
- assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 25);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
- }
-
- /**
- * Test parsing of DuoLite shades having a secondary blackout shade.
- *
- */
- @Test
- public void testDuoliteShadePositionParsing() {
- // blackout shades have capabilities 8
- Capabilities capabilities = db.getCapabilities(8);
- ShadePosition test;
-
- // both shades up
- 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 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 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 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 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 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), UnDefType.UNDEF);
- }
-
- /**
- * Test parsing of DuoLite shades having both a secondary blackout shade, and tilt functionality.
- *
- */
- @Test
- public void testDuoliteTiltShadePositionParsing() {
- // blackout shades with tilt have capabilities 9
- Capabilities capabilities = db.getCapabilities(9);
- ShadePosition test;
-
- // front shade up
- 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 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 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 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 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 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 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 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 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 ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30).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);
-
- // test constraints on impossible values: primary 30% => tilt 30% => back shade 30% down
- 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);
- assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 30);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100);
- }
-
- /**
- * Test parsing of Capabilities 0 ShadePosition (shade fully up).
- *
- */
- @Test
- public void testCaps0ShadePositionParsingFullyUp() {
- Capabilities capabilities = db.getCapabilities(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);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
- }
-
- /**
- * Test parsing of Capabilities 0 ShadePosition (shade fully down).
- *
- */
- @Test
- public void testCap0ShadePositionParsingShadeFullyDown() {
- Capabilities capabilities = db.getCapabilities(0);
- 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);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
- }
-
- /**
- * Helper method; test if shade State is correct.
- *
- * @param actual the shade State
- * @param target the test value to compare with
- */
- private void assertShadePosition(State actual, State target) {
- assertTrue(target.equals(actual));
- }
-
- /**
- * Test parsing of Type 44 ShadePosition (shade fully up).
- *
- */
- @Test
- public void testType44ShadePositionParsingFullyUp() {
- Capabilities capabilities = db.getCapabilities(44, null);
- 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);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
- }
-
- /**
- * Test parsing of Type 44 ShadePosition (shade fully down (method 2) and vane fully open).
- *
- */
- @Test
- public void testType44ShadePositionParsingShadeFullyDownVaneOpen() {
- Capabilities capabilities = db.getCapabilities(44, null);
- 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);
- assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 88);
- }
-
- /**
- * Test the getCapabilities functionality.
- */
- @Test
- public void testGetCapabilities() {
- Capabilities caps;
- /*
- * - type not in database
- * - null external capabilities
- * => return default (0)
- */
- caps = db.getCapabilities(0, null);
- assertEquals(0, caps.getValue());
- /*
- * - type not in database
- * - valid external capabilities (1)
- * => return external capabilities (1)
- */
- caps = db.getCapabilities(0, 1);
- assertEquals(1, caps.getValue());
- /*
- * - type not in database
- * - external capabilities not in database (99)
- * => return default (0)
- */
- caps = db.getCapabilities(0, 99);
- assertEquals(0, caps.getValue());
- /*
- * - type 62 in database
- * - inherent capabilities (2)
- * - null external capabilities
- * => return inherent capabilities (2)
- */
- caps = db.getCapabilities(62, null);
- assertEquals(2, caps.getValue());
- /*
- * - type 62 in database
- * - inherent capabilities (2)
- * - non matching external capabilities (1)
- * => return external capabilities (1)
- */
- caps = db.getCapabilities(62, 1);
- assertEquals(1, caps.getValue());
- /*
- * - type 44 in database
- * - inherent capabilities (0)
- * - with capabilitiesOverride (1)
- * - non matching external capabilities (2)
- * => return capabilitiesOverride (1)
- */
- caps = db.getCapabilities(44, 2);
- assertEquals(1, caps.getValue());
- }
-}
--- /dev/null
+/**
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.hdpowerview.internal.builders.AutomationChannelBuilder;
+import org.openhab.binding.hdpowerview.internal.dto.Scene;
+import org.openhab.binding.hdpowerview.internal.dto.SceneCollection;
+import org.openhab.binding.hdpowerview.internal.dto.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;
+import org.osgi.framework.Bundle;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+
+/**
+ * Unit tests for {@link AutomationChannelBuilder}.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class AutomationChannelBuilderTest {
+
+ private static final ChannelGroupUID CHANNEL_GROUP_UID = new ChannelGroupUID(
+ new ThingUID(HDPowerViewBindingConstants.BINDING_ID, AutomationChannelBuilderTest.class.getSimpleName()),
+ HDPowerViewBindingConstants.CHANNELTYPE_AUTOMATION_ENABLED);
+
+ private static final HDPowerViewTranslationProvider TRANSLATION_PROVIDER = new HDPowerViewTranslationProvider(
+ mock(Bundle.class), new MockedTranslationProvider(), new MockedLocaleProvider());
+
+ private AutomationChannelBuilder builder = AutomationChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
+ private List<Scene> scenes = new ArrayList<>();
+ private List<SceneCollection> sceneCollections = new ArrayList<>();
+
+ @BeforeEach
+ private void setUp() {
+ final Logger logger = (Logger) LoggerFactory.getLogger(AutomationChannelBuilder.class);
+ logger.setLevel(Level.OFF);
+ builder = AutomationChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
+
+ Scene scene = new Scene();
+ scene.id = 1;
+ scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes());
+ scenes = new ArrayList<>(List.of(scene));
+
+ SceneCollection sceneCollection = new SceneCollection();
+ sceneCollection.id = 2;
+ sceneCollection.name = Base64.getEncoder().encodeToString(("TestSceneCollection").getBytes());
+ sceneCollections = new ArrayList<>(List.of(sceneCollection));
+ }
+
+ @Test
+ public void sceneSunriseWeekends() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE);
+ scheduledEvent.daySaturday = true;
+ scheduledEvent.daySunday = true;
+
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestScene, At sunrise, Weekends", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void sceneSunsetWeekdays() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNSET);
+ scheduledEvent.dayMonday = true;
+ scheduledEvent.dayTuesday = true;
+ scheduledEvent.dayWednesday = true;
+ scheduledEvent.dayThursday = true;
+ scheduledEvent.dayFriday = true;
+
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestScene, At sunset, Weekdays", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void sceneTimeAllDays() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_TIME);
+ scheduledEvent.dayMonday = true;
+ scheduledEvent.dayTuesday = true;
+ scheduledEvent.dayWednesday = true;
+ scheduledEvent.dayThursday = true;
+ scheduledEvent.dayFriday = true;
+ scheduledEvent.daySaturday = true;
+ scheduledEvent.daySunday = true;
+ scheduledEvent.hour = 6;
+ scheduledEvent.minute = 30;
+
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestScene, 06:30, All days", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void sceneMinutesBeforeSunriseMondayTuesday() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE);
+ scheduledEvent.dayMonday = true;
+ scheduledEvent.dayTuesday = true;
+ scheduledEvent.minute = -15;
+
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestScene, 15m before sunrise, Mon, Tue", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void sceneHoursMinutesAfterSunriseMonday() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE);
+ scheduledEvent.dayMonday = true;
+ scheduledEvent.minute = 61;
+
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestScene, 1hr 1m after sunrise, Mon", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void sceneMinutesBeforeSunsetWednesdayThursdayFriday() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNSET);
+ scheduledEvent.dayWednesday = true;
+ scheduledEvent.dayThursday = true;
+ scheduledEvent.dayFriday = true;
+ scheduledEvent.minute = -59;
+
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestScene, 59m before sunset, Wed, Thu, Fri", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void sceneHourAfterSunsetFridaySaturdaySunday() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNSET);
+ scheduledEvent.dayFriday = true;
+ scheduledEvent.daySaturday = true;
+ scheduledEvent.daySunday = true;
+ scheduledEvent.minute = 60;
+
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestScene, 1hr after sunset, Fri, Sat, Sun", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void sceneCollection() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithSceneCollection(
+ ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE);
+
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents)
+ .build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestSceneCollection, At sunrise, ", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void suppliedListIsUsed() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE);
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> existingChannels = new ArrayList<>(0);
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents)
+ .withChannels(existingChannels).build();
+
+ assertEquals(existingChannels, channels);
+ }
+
+ @Test
+ public void emptyListWhenNoScheduledEvents() {
+ List<Channel> channels = builder.build();
+
+ assertEquals(0, channels.size());
+ }
+
+ @Test
+ public void emptyListWhenNoScenesOrSceneCollections() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE);
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(0, channels.size());
+ }
+
+ @Test
+ public void emptyListWhenNoSceneForScheduledEvent() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithSceneCollection(
+ ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE);
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(0, channels.size());
+ }
+
+ @Test
+ public void emptyListWhenNoSceneCollectionForScheduledEvent() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE);
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+
+ List<Channel> channels = builder.withSceneCollections(sceneCollections).withScheduledEvents(scheduledEvents)
+ .build();
+
+ assertEquals(0, channels.size());
+ }
+
+ @Test
+ public void groupAndIdAreCorrect() {
+ ScheduledEvent scheduledEvent = createScheduledEventWithScene(ScheduledEvent.SCHEDULED_EVENT_TYPE_SUNRISE);
+ scheduledEvent.id = 42;
+ List<ScheduledEvent> scheduledEvents = new ArrayList<>(List.of(scheduledEvent));
+ List<Channel> channels = builder.withScenes(scenes).withScheduledEvents(scheduledEvents).build();
+
+ assertEquals(1, channels.size());
+ assertEquals(CHANNEL_GROUP_UID.getId(), channels.get(0).getUID().getGroupId());
+ assertEquals(Integer.toString(scheduledEvent.id), channels.get(0).getUID().getIdWithoutGroup());
+ }
+
+ private ScheduledEvent createScheduledEventWithScene(int eventType) {
+ ScheduledEvent scheduledEvent = new ScheduledEvent();
+ scheduledEvent.id = 1;
+ scheduledEvent.sceneId = scenes.get(0).id;
+ scheduledEvent.eventType = eventType;
+ return scheduledEvent;
+ }
+
+ private ScheduledEvent createScheduledEventWithSceneCollection(int eventType) {
+ ScheduledEvent scheduledEvent = new ScheduledEvent();
+ scheduledEvent.id = 1;
+ scheduledEvent.sceneCollectionId = sceneCollections.get(0).id;
+ scheduledEvent.eventType = eventType;
+ return scheduledEvent;
+ }
+}
--- /dev/null
+/**
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+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.Scene;
+import org.openhab.binding.hdpowerview.internal.dto.SceneCollection;
+import org.openhab.binding.hdpowerview.internal.dto.ShadeData;
+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.Scenes;
+import org.openhab.binding.hdpowerview.internal.dto.responses.Shades;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+
+import com.google.gson.Gson;
+
+/**
+ * Unit tests for HD PowerView binding.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ * @author Jacob Laursen - Add support for scene groups
+ */
+@NonNullByDefault
+public class HDPowerViewJUnitTests {
+
+ private Gson gson = new Gson();
+
+ private <T> T getObjectFromJson(String filename, Class<T> clazz) 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");
+ }
+ String json = new String(bytes, StandardCharsets.UTF_8);
+ return Objects.requireNonNull(gson.fromJson(json, clazz));
+ }
+ }
+
+ /**
+ * Test generic JSON shades response.
+ */
+ @Test
+ public void shadeNameIsDecoded() throws IOException {
+ Shades shades = getObjectFromJson("shades.json", Shades.class);
+ List<ShadeData> shadeData = shades.shadeData;
+ assertNotNull(shadeData);
+ assertEquals(3, shadeData.size());
+ ShadeData shade = shadeData.get(0);
+ assertEquals("Shade 2", shade.getName());
+ }
+
+ /**
+ * Test the BatteryKind decoding.
+ */
+ @Test
+ public void testBatteryKind() throws IOException {
+ Shades shades = getObjectFromJson("shades.json", Shades.class);
+ List<ShadeData> shadeData = shades.shadeData;
+ assertNotNull(shadeData);
+ ShadeData shade = shadeData.get(0);
+ assertEquals(BatteryKind.HARDWIRED_POWER_SUPPLY, shade.getBatteryKind());
+ shade = shadeData.get(1);
+ assertEquals(BatteryKind.ERROR_UNKNOWN, shade.getBatteryKind());
+ }
+
+ /**
+ * Test generic JSON scene response.
+ */
+ @Test
+ public void sceneNameIsDecoded() throws IOException {
+ Scenes scenes = getObjectFromJson("scenes.json", Scenes.class);
+ List<Scene> sceneData = scenes.sceneData;
+ assertNotNull(sceneData);
+ assertEquals(4, sceneData.size());
+ Scene scene = sceneData.get(0);
+ assertEquals("Door Open", scene.getName());
+ }
+
+ /**
+ * Test generic JSON scene collection response.
+ */
+ @Test
+ public void sceneCollectionNameIsDecoded() throws IOException {
+ SceneCollections sceneCollections = getObjectFromJson("sceneCollections.json", SceneCollections.class);
+
+ List<SceneCollection> sceneCollectionData = sceneCollections.sceneCollectionData;
+ assertNotNull(sceneCollectionData);
+ assertEquals(1, sceneCollectionData.size());
+
+ SceneCollection sceneCollection = sceneCollectionData.get(0);
+ assertEquals("Børn op", sceneCollection.getName());
+ }
+
+ /**
+ * Test the JSON parsing for a duette top down bottom up shade.
+ */
+ @Test
+ public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws IOException {
+ Shades shades = getObjectFromJson("duette.json", Shades.class);
+ List<ShadeData> shadesData = shades.shadeData;
+ assertNotNull(shadesData);
+
+ assertEquals(1, shadesData.size());
+ ShadeData shadeData = shadesData.get(0);
+ assertNotNull(shadeData);
+
+ assertEquals("Gardin 1", shadeData.getName());
+ assertEquals(63778, shadeData.id);
+
+ ShadePosition shadePos = shadeData.positions;
+ assertNotNull(shadePos);
+
+ Integer capabilitiesValue = shadeData.capabilities;
+ assertNotNull(capabilitiesValue);
+ assertEquals(7, capabilitiesValue.intValue());
+ ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
+ Capabilities capabilities = db.getCapabilities(capabilitiesValue);
+
+ State pos = shadePos.getState(capabilities, PRIMARY_POSITION);
+ assertEquals(PercentType.class, pos.getClass());
+ assertEquals(59, ((PercentType) pos).intValue());
+
+ pos = shadePos.getState(capabilities, SECONDARY_POSITION);
+ assertEquals(PercentType.class, pos.getClass());
+ assertEquals(35, ((PercentType) pos).intValue());
+
+ pos = shadePos.getState(capabilities, VANE_TILT_POSITION);
+ assertEquals(UnDefType.class, pos.getClass());
+
+ assertEquals(3, shadeData.batteryStatus);
+
+ assertEquals(4, shadeData.signalStrength);
+
+ assertEquals(8, shadeData.type);
+
+ assertTrue(db.isTypeInDatabase(shadeData.type));
+ assertTrue(db.isCapabilitiesInDatabase(capabilitiesValue.intValue()));
+
+ assertEquals(db.getType(shadeData.type).getCapabilities(), capabilitiesValue.intValue());
+
+ assertTrue(db.getCapabilities(capabilitiesValue.intValue()).supportsSecondary());
+ assertNotEquals(db.getType(shadeData.type).getCapabilities(), capabilitiesValue.intValue() + 1);
+
+ // ==== when changing position1, position2 value is not changed (vice-versa) ====
+ ShadePosition shadePosition = shadeData.positions;
+ assertNotNull(shadePosition);
+ // ==== position2 ====
+ State position2Old = shadePosition.getState(capabilities, SECONDARY_POSITION);
+ shadePosition.setPosition(capabilities, PRIMARY_POSITION, 99);
+ State position2New = shadePosition.getState(capabilities, SECONDARY_POSITION);
+ assertEquals(PercentType.class, position2Old.getClass());
+ assertEquals(PercentType.class, position2New.getClass());
+ assertEquals(((PercentType) position2Old).intValue(), ((PercentType) position2New).intValue());
+
+ // ==== position2 ====
+ State position1Old = shadePosition.getState(capabilities, PRIMARY_POSITION);
+ shadePosition.setPosition(capabilities, SECONDARY_POSITION, 99);
+ State position1New = shadePosition.getState(capabilities, PRIMARY_POSITION);
+ assertEquals(PercentType.class, position1Old.getClass());
+ assertEquals(PercentType.class, position1New.getClass());
+ assertEquals(((PercentType) position1Old).intValue(), ((PercentType) position1New).intValue());
+ }
+}
--- /dev/null
+/**
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.openhab.binding.hdpowerview.internal.dto.CoordinateSystem.*;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jetty.client.HttpClient;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase;
+import org.openhab.binding.hdpowerview.internal.database.ShadeCapabilitiesDatabase.Capabilities;
+import org.openhab.binding.hdpowerview.internal.dto.Scene;
+import org.openhab.binding.hdpowerview.internal.dto.ShadeData;
+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.Shades;
+import org.openhab.binding.hdpowerview.internal.exceptions.HubException;
+import org.openhab.binding.hdpowerview.internal.exceptions.HubMaintenanceException;
+import org.openhab.binding.hdpowerview.internal.exceptions.HubProcessingException;
+import org.openhab.core.library.types.PercentType;
+import org.openhab.core.types.State;
+
+/**
+ * Unit tests for HD PowerView binding.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+@NonNullByDefault
+public class OnlineCommunicationTest {
+
+ private 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");
+
+ /**
+ * Run a series of ONLINE tests on the communication with a hub.
+ *
+ * @param hubIPAddress must be a valid hub IP address to run the
+ * tests on; or an INVALID IP address to
+ * suppress the tests
+ * @param allowShadeMovementCommands set to true if you accept that the tests
+ * shall physically move the shades
+ */
+ @Test
+ public void testOnlineCommunication() {
+ /*
+ * NOTE: in order to actually run these tests you must have a hub physically
+ * available, and its IP address must be correctly configured in the
+ * "hubIPAddress" string constant e.g. "192.168.1.123"
+ */
+ String hubIPAddress = "192.168.1.xxx";
+
+ /*
+ * NOTE: set allowShadeMovementCommands = true if you accept physically moving
+ * the shades during these tests
+ */
+ boolean allowShadeMovementCommands = false;
+
+ if (VALID_IP_V4_ADDRESS.matcher(hubIPAddress).matches()) {
+ // ==== initialize stuff ====
+ HttpClient client = new HttpClient();
+ assertNotNull(client);
+
+ // ==== start the client ====
+ try {
+ client.start();
+ assertTrue(client.isStarted());
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+
+ HDPowerViewWebTargets webTargets = new HDPowerViewWebTargets(client, hubIPAddress);
+ assertNotNull(webTargets);
+
+ int shadeId = 0;
+ ShadePosition shadePos = null;
+ Shades shadesX = null;
+
+ // ==== get all shades ====
+ try {
+ shadesX = webTargets.getShades();
+ assertNotNull(shadesX);
+ List<ShadeData> shadesData = shadesX.shadeData;
+ assertNotNull(shadesData);
+
+ assertTrue(!shadesData.isEmpty());
+ ShadeData shadeData;
+ shadeData = shadesData.get(0);
+ assertNotNull(shadeData);
+ assertTrue(shadeData.getName().length() > 0);
+ shadePos = shadeData.positions;
+ assertNotNull(shadePos);
+ ShadeData shadeZero = shadesData.get(0);
+ assertNotNull(shadeZero);
+ shadeId = shadeZero.id;
+ assertNotEquals(0, shadeId);
+
+ for (ShadeData shadexData : shadesData) {
+ String shadeName = shadexData.getName();
+ assertNotNull(shadeName);
+ }
+ } catch (HubException e) {
+ fail(e.getMessage());
+ }
+
+ // ==== get all scenes ====
+ int sceneId = 0;
+ try {
+ Scenes scenes = webTargets.getScenes();
+ assertNotNull(scenes);
+
+ List<Scene> scenesData = scenes.sceneData;
+ assertNotNull(scenesData);
+
+ assertTrue(!scenesData.isEmpty());
+ Scene sceneZero = scenesData.get(0);
+ assertNotNull(sceneZero);
+ sceneId = sceneZero.id;
+ assertTrue(sceneId > 0);
+
+ for (Scene scene : scenesData) {
+ String sceneName = scene.getName();
+ assertNotNull(sceneName);
+ }
+ } catch (HubException e) {
+ fail(e.getMessage());
+ }
+
+ // ==== refresh a specific shade ====
+ ShadeData shadeData = null;
+ try {
+ assertNotEquals(0, shadeId);
+ shadeData = webTargets.refreshShadePosition(shadeId);
+ } catch (HubException e) {
+ fail(e.getMessage());
+ }
+
+ // ==== move a specific shade ====
+ try {
+ assertNotEquals(0, shadeId);
+
+ if (shadeData != null) {
+ ShadePosition positions = shadeData.positions;
+ assertNotNull(positions);
+ Integer capabilitiesValue = shadeData.capabilities;
+ assertNotNull(capabilitiesValue);
+
+ Capabilities capabilities = new ShadeCapabilitiesDatabase()
+ .getCapabilities(capabilitiesValue.intValue());
+
+ State pos = positions.getState(capabilities, PRIMARY_POSITION);
+ assertEquals(PercentType.class, pos.getClass());
+
+ int position = ((PercentType) pos).intValue();
+ position = position + ((position <= 10) ? 5 : -5);
+
+ ShadePosition targetPosition = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION,
+ position);
+ assertNotNull(targetPosition);
+
+ if (allowShadeMovementCommands) {
+ webTargets.moveShade(shadeId, targetPosition);
+
+ ShadeData newData = webTargets.getShade(shadeId);
+ ShadePosition actualPosition = newData.positions;
+ assertNotNull(actualPosition);
+ assertEquals(targetPosition.getState(capabilities, PRIMARY_POSITION),
+ actualPosition.getState(capabilities, PRIMARY_POSITION));
+ }
+ }
+ } catch (HubException e) {
+ fail(e.getMessage());
+ }
+
+ // ==== activate a specific scene ====
+ if (allowShadeMovementCommands) {
+ try {
+ assertNotNull(sceneId);
+ webTargets.activateScene(sceneId);
+ } catch (HubProcessingException | HubMaintenanceException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ // ==== test stop command ====
+ if (allowShadeMovementCommands) {
+ try {
+ assertNotNull(sceneId);
+ webTargets.stopShade(shadeId);
+ } catch (HubException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ // ==== stop the client ====
+ if (client.isRunning()) {
+ try {
+ client.stop();
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/**
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.hdpowerview.internal.builders.SceneChannelBuilder;
+import org.openhab.binding.hdpowerview.internal.dto.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;
+import org.openhab.core.thing.type.AutoUpdatePolicy;
+import org.osgi.framework.Bundle;
+
+/**
+ * Unit tests for {@link SceneChannelBuilder}.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class SceneChannelBuilderTest {
+
+ private static final ChannelGroupUID CHANNEL_GROUP_UID = new ChannelGroupUID(
+ new ThingUID(HDPowerViewBindingConstants.BINDING_ID, SceneChannelBuilderTest.class.getSimpleName()),
+ HDPowerViewBindingConstants.CHANNELTYPE_SCENE_ACTIVATE);
+
+ private static final HDPowerViewTranslationProvider TRANSLATION_PROVIDER = new HDPowerViewTranslationProvider(
+ mock(Bundle.class), new MockedTranslationProvider(), new MockedLocaleProvider());
+
+ private SceneChannelBuilder builder = SceneChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
+
+ @BeforeEach
+ private void setUp() {
+ builder = SceneChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
+ }
+
+ @Test
+ public void labelIsCorrect() {
+ List<Scene> scenes = createScenes();
+ List<Channel> channels = builder.withScenes(scenes).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestScene", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void descriptionIsCorrect() {
+ List<Scene> scenes = createScenes();
+ List<Channel> channels = builder.withScenes(scenes).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("Activates the scene 'TestScene'", channels.get(0).getDescription());
+ }
+
+ @Test
+ public void groupAndIdAreCorrect() {
+ List<Scene> scenes = createScenes();
+ List<Channel> channels = builder.withScenes(scenes).build();
+
+ assertEquals(1, channels.size());
+ assertEquals(CHANNEL_GROUP_UID.getId(), channels.get(0).getUID().getGroupId());
+ assertEquals(Integer.toString(scenes.get(0).id), channels.get(0).getUID().getIdWithoutGroup());
+ }
+
+ @Test
+ public void autoUpdatePolicyIsCorrect() {
+ List<Scene> scenes = createScenes();
+ List<Channel> channels = builder.withScenes(scenes).build();
+
+ assertEquals(1, channels.size());
+ assertEquals(AutoUpdatePolicy.VETO, channels.get(0).getAutoUpdatePolicy());
+ }
+
+ @Test
+ public void suppliedListIsUsed() {
+ List<Scene> scenes = createScenes();
+ List<Channel> existingChannels = new ArrayList<>(0);
+ List<Channel> channels = builder.withScenes(scenes).withChannels(existingChannels).build();
+
+ assertEquals(existingChannels, channels);
+ }
+
+ @Test
+ public void emptyListWhenNoScenes() {
+ List<Channel> channels = builder.build();
+
+ assertEquals(0, channels.size());
+ }
+
+ private List<Scene> createScenes() {
+ Scene scene = new Scene();
+ scene.id = 1;
+ scene.name = Base64.getEncoder().encodeToString(("TestScene").getBytes());
+ return new ArrayList<>(List.of(scene));
+ }
+}
--- /dev/null
+/**
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.hdpowerview.internal.builders.SceneGroupChannelBuilder;
+import org.openhab.binding.hdpowerview.internal.dto.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;
+import org.openhab.core.thing.type.AutoUpdatePolicy;
+import org.osgi.framework.Bundle;
+
+/**
+ * Unit tests for {@link SceneGroupChannelBuilder}.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class SceneGroupChannelBuilderTest {
+
+ private static final ChannelGroupUID CHANNEL_GROUP_UID = new ChannelGroupUID(
+ new ThingUID(HDPowerViewBindingConstants.BINDING_ID, SceneGroupChannelBuilderTest.class.getSimpleName()),
+ HDPowerViewBindingConstants.CHANNELTYPE_SCENE_GROUP_ACTIVATE);
+
+ private static final HDPowerViewTranslationProvider TRANSLATION_PROVIDER = new HDPowerViewTranslationProvider(
+ mock(Bundle.class), new MockedTranslationProvider(), new MockedLocaleProvider());
+
+ private SceneGroupChannelBuilder builder = SceneGroupChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
+
+ @BeforeEach
+ private void setUp() {
+ builder = SceneGroupChannelBuilder.create(TRANSLATION_PROVIDER, CHANNEL_GROUP_UID);
+ }
+
+ @Test
+ public void labelIsCorrect() {
+ List<SceneCollection> sceneCollections = createSceneCollections();
+ List<Channel> channels = builder.withSceneCollections(sceneCollections).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("TestSceneCollection", channels.get(0).getLabel());
+ }
+
+ @Test
+ public void descriptionIsCorrect() {
+ List<SceneCollection> sceneCollections = createSceneCollections();
+ List<Channel> channels = builder.withSceneCollections(sceneCollections).build();
+
+ assertEquals(1, channels.size());
+ assertEquals("Activates the scene group 'TestSceneCollection'", channels.get(0).getDescription());
+ }
+
+ @Test
+ public void groupAndIdAreCorrect() {
+ List<SceneCollection> sceneCollections = createSceneCollections();
+ List<Channel> channels = builder.withSceneCollections(sceneCollections).build();
+
+ assertEquals(1, channels.size());
+ assertEquals(CHANNEL_GROUP_UID.getId(), channels.get(0).getUID().getGroupId());
+ assertEquals(Integer.toString(sceneCollections.get(0).id), channels.get(0).getUID().getIdWithoutGroup());
+ }
+
+ @Test
+ public void autoUpdatePolicyIsCorrect() {
+ List<SceneCollection> sceneCollections = createSceneCollections();
+ List<Channel> channels = builder.withSceneCollections(sceneCollections).build();
+
+ assertEquals(1, channels.size());
+ assertEquals(AutoUpdatePolicy.VETO, channels.get(0).getAutoUpdatePolicy());
+ }
+
+ @Test
+ public void suppliedListIsUsed() {
+ List<SceneCollection> sceneCollections = createSceneCollections();
+ List<Channel> existingChannels = new ArrayList<>(0);
+ List<Channel> channels = builder.withSceneCollections(sceneCollections).withChannels(existingChannels).build();
+
+ assertEquals(existingChannels, channels);
+ }
+
+ @Test
+ public void emptyListWhenNoSceneCollections() {
+ List<Channel> channels = builder.build();
+
+ assertEquals(0, channels.size());
+ }
+
+ private List<SceneCollection> createSceneCollections() {
+ SceneCollection sceneCollection = new SceneCollection();
+ sceneCollection.id = 1;
+ sceneCollection.name = Base64.getEncoder().encodeToString(("TestSceneCollection").getBytes());
+ return new ArrayList<>(List.of(sceneCollection));
+ }
+}
--- /dev/null
+/**
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.*;
+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.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;
+
+/**
+ * Unit tests for Shade Position setting and getting.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ */
+@NonNullByDefault
+public class ShadePositionTest {
+
+ private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
+
+ /**
+ * General tests of the database of known types.
+ */
+ @Test
+ public void testKnownTypesDatabase() {
+ assertTrue(db.isTypeInDatabase(4));
+ assertTrue(db.isCapabilitiesInDatabase(0));
+ assertTrue(db.isCapabilitiesInDatabase(10));
+
+ assertTrue(db.getCapabilities(0).supportsPrimary());
+ assertTrue(db.getCapabilities(1).supportsTiltOnClosed());
+ assertTrue(db.getCapabilities(2).supportsTilt180());
+ assertTrue(db.getCapabilities(2).supportsTiltAnywhere());
+ assertTrue(db.getCapabilities(4).supportsTilt180());
+ assertTrue(db.getCapabilities(4).supportsTiltAnywhere());
+ assertTrue(db.getCapabilities(5).supportsTilt180());
+ assertFalse(db.getCapabilities(5).supportsPrimary());
+ assertTrue(db.getCapabilities(6).isPrimaryInverted());
+ assertTrue(db.getCapabilities(7).supportsSecondary());
+ assertTrue(db.getCapabilities(8).supportsSecondaryOverlapped());
+ assertTrue(db.getCapabilities(9).supportsSecondaryOverlapped());
+ assertTrue(db.getCapabilities(9).supportsTiltOnClosed());
+
+ assertEquals(db.getType(4).getCapabilities(), 0);
+ assertEquals(db.getType(-1).getCapabilities(), -1);
+
+ assertFalse(db.isTypeInDatabase(99));
+ assertFalse(db.isCapabilitiesInDatabase(99));
+
+ assertFalse(db.getCapabilities(0).isPrimaryInverted());
+ assertFalse(db.getCapabilities(-1).isPrimaryInverted());
+ assertFalse(db.getCapabilities(99).isPrimaryInverted());
+
+ assertFalse(db.getCapabilities(0).supportsSecondary());
+ assertFalse(db.getCapabilities(-1).supportsSecondary());
+ assertFalse(db.getCapabilities(99).supportsSecondary());
+ }
+
+ /**
+ * Helper method; test if shade position is a PercentType and that its value is correct.
+ *
+ * @param position the shade position
+ * @param value the test value to compare with
+ */
+ private void assertShadePosition(State position, int value) {
+ assertEquals(PercentType.class, position.getClass());
+ assertEquals(value, ((PercentType) position).intValue());
+ }
+
+ /**
+ * Test parsing of Capabilities 1 ShadePosition (shade fully up).
+ *
+ */
+ @Test
+ public void testCaps1ShadePositionParsingFullyUp() {
+ Capabilities capabilities = db.getCapabilities(1);
+ 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);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+ }
+
+ /**
+ * Test parsing of Capabilities 1 ShadePosition (shade fully down (method 1)).
+ *
+ */
+ @Test
+ public void testCaps1ShadePositionParsingShadeFullyDown1() {
+ Capabilities capabilities = db.getCapabilities(1);
+ 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);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0);
+ }
+
+ /**
+ * Test parsing of Capabilities 1 ShadePosition (shade fully down (method 2)).
+ *
+ */
+ @Test
+ public void testCaps1ShadePositionParsingShadeFullyDown2() {
+ Capabilities capabilities = db.getCapabilities(1);
+ 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);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0);
+ }
+
+ /**
+ * Test parsing of Capabilities 1 ShadePosition (shade fully down (method 2) and vane fully open).
+ *
+ */
+ @Test
+ public void testCaps1ShadePositionParsingShadeFullyDownVaneOpen() {
+ Capabilities capabilities = db.getCapabilities(1);
+ 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);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 88);
+ }
+
+ /**
+ * On dual rail shades, it should not be possible to drive the upper rail below the lower rail, or vice-versa. So
+ * the binding code applies constraints on setting such positions. This test checks that the constraint code is
+ * working.
+ */
+ @Test
+ public void testDualRailConstraints() {
+ Capabilities capabilities = db.getCapabilities(7);
+ ShadePosition test = new ShadePosition();
+
+ // ==== OK !! primary at bottom, secondary at top ====
+ test.setPosition(capabilities, PRIMARY_POSITION, 100).setPosition(capabilities, SECONDARY_POSITION, 0);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+
+ // ==== OK !! primary at middle, secondary at top ====
+ test.setPosition(capabilities, PRIMARY_POSITION, 50).setPosition(capabilities, SECONDARY_POSITION, 0);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 50);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+
+ // ==== OK !! primary at middle, secondary at middle ====
+ test.setPosition(capabilities, PRIMARY_POSITION, 50).setPosition(capabilities, SECONDARY_POSITION, 50);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 50);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 50);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+
+ // ==== IMPOSSIBLE !! secondary at middle, primary above => test the constraining code ====
+ test.setPosition(capabilities, SECONDARY_POSITION, 0).setPosition(capabilities, PRIMARY_POSITION, 100);
+ test.setPosition(capabilities, SECONDARY_POSITION, 40).setPosition(capabilities, PRIMARY_POSITION, 25);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 40);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 40);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+
+ // ==== OK !! secondary at middle, primary below ====
+ test.setPosition(capabilities, SECONDARY_POSITION, 0).setPosition(capabilities, PRIMARY_POSITION, 100);
+ test.setPosition(capabilities, SECONDARY_POSITION, 50).setPosition(capabilities, PRIMARY_POSITION, 75);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 75);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 50);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+
+ // ==== IMPOSSIBLE !! primary at middle, secondary below => test the constraining code ====
+ test.setPosition(capabilities, SECONDARY_POSITION, 0).setPosition(capabilities, PRIMARY_POSITION, 100);
+ test.setPosition(capabilities, PRIMARY_POSITION, 60).setPosition(capabilities, SECONDARY_POSITION, 75);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 60);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 60);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+
+ // ==== OK !! primary at middle, secondary above ====
+ test.setPosition(capabilities, SECONDARY_POSITION, 0).setPosition(capabilities, PRIMARY_POSITION, 100);
+ test.setPosition(capabilities, PRIMARY_POSITION, 60).setPosition(capabilities, SECONDARY_POSITION, 25);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 60);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 25);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+ }
+
+ /**
+ * Test parsing of DuoLite shades having a secondary blackout shade.
+ *
+ */
+ @Test
+ public void testDuoliteShadePositionParsing() {
+ // blackout shades have capabilities 8
+ Capabilities capabilities = db.getCapabilities(8);
+ ShadePosition test;
+
+ // both shades up
+ 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 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 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 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 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 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), UnDefType.UNDEF);
+ }
+
+ /**
+ * Test parsing of DuoLite shades having both a secondary blackout shade, and tilt functionality.
+ *
+ */
+ @Test
+ public void testDuoliteTiltShadePositionParsing() {
+ // blackout shades with tilt have capabilities 9
+ Capabilities capabilities = db.getCapabilities(9);
+ ShadePosition test;
+
+ // front shade up
+ 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 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 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 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 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 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 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 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 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 ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 30).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);
+
+ // test constraints on impossible values: primary 30% => tilt 30% => back shade 30% down
+ 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);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 30);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100);
+ }
+
+ /**
+ * Test parsing of Capabilities 0 ShadePosition (shade fully up).
+ *
+ */
+ @Test
+ public void testCaps0ShadePositionParsingFullyUp() {
+ Capabilities capabilities = db.getCapabilities(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);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+ }
+
+ /**
+ * Test parsing of Capabilities 0 ShadePosition (shade fully down).
+ *
+ */
+ @Test
+ public void testCap0ShadePositionParsingShadeFullyDown() {
+ Capabilities capabilities = db.getCapabilities(0);
+ 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);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+ }
+
+ /**
+ * Helper method; test if shade State is correct.
+ *
+ * @param actual the shade State
+ * @param target the test value to compare with
+ */
+ private void assertShadePosition(State actual, State target) {
+ assertTrue(target.equals(actual));
+ }
+
+ /**
+ * Test parsing of Type 44 ShadePosition (shade fully up).
+ *
+ */
+ @Test
+ public void testType44ShadePositionParsingFullyUp() {
+ Capabilities capabilities = db.getCapabilities(44, null);
+ 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);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), UnDefType.UNDEF);
+ }
+
+ /**
+ * Test parsing of Type 44 ShadePosition (shade fully down (method 2) and vane fully open).
+ *
+ */
+ @Test
+ public void testType44ShadePositionParsingShadeFullyDownVaneOpen() {
+ Capabilities capabilities = db.getCapabilities(44, null);
+ 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);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 88);
+ }
+
+ /**
+ * Test the getCapabilities functionality.
+ */
+ @Test
+ public void testGetCapabilities() {
+ Capabilities caps;
+ /*
+ * - type not in database
+ * - null external capabilities
+ * => return default (0)
+ */
+ caps = db.getCapabilities(0, null);
+ assertEquals(0, caps.getValue());
+ /*
+ * - type not in database
+ * - valid external capabilities (1)
+ * => return external capabilities (1)
+ */
+ caps = db.getCapabilities(0, 1);
+ assertEquals(1, caps.getValue());
+ /*
+ * - type not in database
+ * - external capabilities not in database (99)
+ * => return default (0)
+ */
+ caps = db.getCapabilities(0, 99);
+ assertEquals(0, caps.getValue());
+ /*
+ * - type 62 in database
+ * - inherent capabilities (2)
+ * - null external capabilities
+ * => return inherent capabilities (2)
+ */
+ caps = db.getCapabilities(62, null);
+ assertEquals(2, caps.getValue());
+ /*
+ * - type 62 in database
+ * - inherent capabilities (2)
+ * - non matching external capabilities (1)
+ * => return external capabilities (1)
+ */
+ caps = db.getCapabilities(62, 1);
+ assertEquals(1, caps.getValue());
+ /*
+ * - type 44 in database
+ * - inherent capabilities (0)
+ * - with capabilitiesOverride (1)
+ * - non matching external capabilities (2)
+ * => return capabilitiesOverride (1)
+ */
+ caps = db.getCapabilities(44, 2);
+ assertEquals(1, caps.getValue());
+ }
+}
--- /dev/null
+/**
+ * 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.providers;
+
+import java.util.Locale;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.i18n.LocaleProvider;
+
+/**
+ * Locale provider for unit tests.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class MockedLocaleProvider implements LocaleProvider {
+ public Locale getLocale() {
+ return Locale.ENGLISH;
+ }
+}
--- /dev/null
+/**
+ * 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.providers;
+
+import static java.util.Map.entry;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.i18n.TranslationProvider;
+import org.osgi.framework.Bundle;
+
+/**
+ * Translation provider for unit tests.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class MockedTranslationProvider implements TranslationProvider {
+
+ private static final Map<String, String> TEXTS = Map.ofEntries(
+ entry("dynamic-channel.scene-activate.description", "Activates the scene ''{0}''"),
+ entry("dynamic-channel.scene-group-activate.description", "Activates the scene group ''{0}''"),
+ entry("dynamic-channel.automation-enabled.description", "Enables/disables the automation ''{0}''"),
+ entry("dynamic-channel.automation-enabled.label", "{0}, {1}, {2}"),
+ entry("dynamic-channel.automation.hour", "{0}hr"), entry("dynamic-channel.automation.minute", "{0}m"),
+ entry("dynamic-channel.automation.hour-minute", "{0}hr {1}m"),
+ entry("dynamic-channel.automation.at-sunrise", "At sunrise"),
+ entry("dynamic-channel.automation.before-sunrise", "{0} before sunrise"),
+ entry("dynamic-channel.automation.after-sunrise", "{0} after sunrise"),
+ entry("dynamic-channel.automation.at-sunset", "At sunset"),
+ entry("dynamic-channel.automation.before-sunset", "{0} before sunset"),
+ entry("dynamic-channel.automation.after-sunset", "{0} after sunset"),
+ entry("dynamic-channel.automation.weekdays", "Weekdays"),
+ entry("dynamic-channel.automation.weekends", "Weekends"),
+ entry("dynamic-channel.automation.all-days", "All days"));
+
+ public MockedTranslationProvider() {
+ }
+
+ @Nullable
+ public String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
+ @Nullable Locale locale) {
+ return "";
+ }
+
+ @Nullable
+ public String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
+ @Nullable Locale locale, @Nullable Object @Nullable... arguments) {
+ String text = TEXTS.get(key);
+ return MessageFormat.format(text != null ? text : key, arguments);
+ }
+}
+++ /dev/null
-/**
- * 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.providers;
-
-import java.util.Locale;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.core.i18n.LocaleProvider;
-
-/**
- * Locale provider for unit tests.
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class MockedLocaleProvider implements LocaleProvider {
- public Locale getLocale() {
- return Locale.ENGLISH;
- }
-}
+++ /dev/null
-/**
- * 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.providers;
-
-import static java.util.Map.entry;
-
-import java.text.MessageFormat;
-import java.util.Locale;
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.core.i18n.TranslationProvider;
-import org.osgi.framework.Bundle;
-
-/**
- * Translation provider for unit tests.
- *
- * @author Jacob Laursen - Initial contribution
- */
-@NonNullByDefault
-public class MockedTranslationProvider implements TranslationProvider {
-
- private static final Map<String, String> TEXTS = Map.ofEntries(
- entry("dynamic-channel.scene-activate.description", "Activates the scene ''{0}''"),
- entry("dynamic-channel.scene-group-activate.description", "Activates the scene group ''{0}''"),
- entry("dynamic-channel.automation-enabled.description", "Enables/disables the automation ''{0}''"),
- entry("dynamic-channel.automation-enabled.label", "{0}, {1}, {2}"),
- entry("dynamic-channel.automation.hour", "{0}hr"), entry("dynamic-channel.automation.minute", "{0}m"),
- entry("dynamic-channel.automation.hour-minute", "{0}hr {1}m"),
- entry("dynamic-channel.automation.at-sunrise", "At sunrise"),
- entry("dynamic-channel.automation.before-sunrise", "{0} before sunrise"),
- entry("dynamic-channel.automation.after-sunrise", "{0} after sunrise"),
- entry("dynamic-channel.automation.at-sunset", "At sunset"),
- entry("dynamic-channel.automation.before-sunset", "{0} before sunset"),
- entry("dynamic-channel.automation.after-sunset", "{0} after sunset"),
- entry("dynamic-channel.automation.weekdays", "Weekdays"),
- entry("dynamic-channel.automation.weekends", "Weekends"),
- entry("dynamic-channel.automation.all-days", "All days"));
-
- public MockedTranslationProvider() {
- }
-
- @Nullable
- public String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
- @Nullable Locale locale) {
- return "";
- }
-
- @Nullable
- public String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
- @Nullable Locale locale, @Nullable Object @Nullable... arguments) {
- String text = TEXTS.get(key);
- return MessageFormat.format(text != null ? text : key, arguments);
- }
-}
+++ /dev/null
-{
- "shadeIds": [
- 63778
- ],
- "shadeData": [
- {
- "id": 63778,
- "type": 8,
- "batteryStatus": 3,
- "batteryStrength": 168,
- "roomId": 891,
- "firmware": {
- "revision": 1,
- "subRevision": 8,
- "build": 1944
- },
- "motor": {
- "revision": 0,
- "subRevision": 0,
- "build": 267
- },
- "name": "R2FyZGluIDE=",
- "groupId": 18108,
- "positions": {
- "posKind2": 2,
- "position2": 23194,
- "posKind1": 1,
- "position1": 26566
- },
- "signalStrength": 4,
- "aid": 2,
- "capabilities": 7,
- "batteryKind": "unassigned"
- }
- ]
-}
--- /dev/null
+{
+ "shadeIds": [
+ 63778
+ ],
+ "shadeData": [
+ {
+ "id": 63778,
+ "type": 8,
+ "batteryStatus": 3,
+ "batteryStrength": 168,
+ "roomId": 891,
+ "firmware": {
+ "revision": 1,
+ "subRevision": 8,
+ "build": 1944
+ },
+ "motor": {
+ "revision": 0,
+ "subRevision": 0,
+ "build": 267
+ },
+ "name": "R2FyZGluIDE=",
+ "groupId": 18108,
+ "positions": {
+ "posKind2": 2,
+ "position2": 23194,
+ "posKind1": 1,
+ "position1": 26566
+ },
+ "signalStrength": 4,
+ "aid": 2,
+ "capabilities": 7,
+ "batteryKind": "unassigned"
+ }
+ ]
+}
--- /dev/null
+{
+ "sceneCollectionIds": [
+ 27119
+ ],
+ "sceneCollectionData": [
+ {
+ "name": "QsO4cm4gb3A=",
+ "colorId": 12,
+ "iconId": 17,
+ "id": 27119,
+ "order": 0,
+ "hkAssist": false
+ }
+ ]
+}
--- /dev/null
+{
+ "sceneIds": [
+ 18097,
+ 22663,
+ 35821,
+ 6551
+ ],
+ "sceneData": [
+ {
+ "roomId": 59611,
+ "name": "RG9vciBPcGVu",
+ "colorId": 6,
+ "iconId": 160,
+ "networkNumber": 19,
+ "id": 18097,
+ "order": 3,
+ "hkAssist": false
+ },
+ {
+ "roomId": 59611,
+ "name": "SGVhcnQ=",
+ "colorId": 15,
+ "iconId": 49,
+ "networkNumber": 3,
+ "id": 22663,
+ "order": 0,
+ "hkAssist": false
+ },
+ {
+ "roomId": 59611,
+ "name": "Q2xvc2U=",
+ "colorId": 5,
+ "iconId": 31,
+ "networkNumber": 8,
+ "id": 35821,
+ "order": 2,
+ "hkAssist": false
+ },
+ {
+ "roomId": 59611,
+ "name": "T3Blbg==",
+ "colorId": 10,
+ "iconId": 95,
+ "networkNumber": 20,
+ "id": 6551,
+ "order": 1,
+ "hkAssist": false
+ }
+ ]
+}
--- /dev/null
+{
+ "shadeIds": [
+ 25206,
+ 55854,
+ 50150
+ ],
+ "shadeData": [
+ {
+ "id": 25206,
+ "type": 44,
+ "batteryStatus": 3,
+ "batteryStrength": 179,
+ "roomId": 59611,
+ "firmware": {
+ "revision": 1,
+ "subRevision": 8,
+ "build": 1944
+ },
+ "name": "U2hhZGUgMg==",
+ "motor": {
+ "revision": 51,
+ "subRevision": 51,
+ "build": 11825
+ },
+ "groupId": 64003,
+ "aid": 2,
+ "signalStrength": 4,
+ "capabilities": 0,
+ "batteryKind": 1,
+ "positions": {
+ "posKind1": 3,
+ "position1": 32579
+ }
+ },
+ {
+ "id": 55854,
+ "type": 44,
+ "batteryStatus": 3,
+ "batteryStrength": 187,
+ "roomId": 59611,
+ "firmware": {
+ "revision": 1,
+ "subRevision": 8,
+ "build": 1944
+ },
+ "motor": {
+ "revision": 51,
+ "subRevision": 51,
+ "build": 11825
+ },
+ "name": "U2hhZGUgMw==",
+ "groupId": 64003,
+ "aid": 3,
+ "signalStrength": 4,
+ "capabilities": 0,
+ "positions": {
+ "posKind1": 1,
+ "position1": 65534
+ },
+ "batteryKind": "unassigned"
+ },
+ {
+ "id": 50150,
+ "type": 44,
+ "batteryStatus": 3,
+ "batteryStrength": 181,
+ "roomId": 59611,
+ "name": "U2hhZGUgMQ==",
+ "firmware": {
+ "revision": 1,
+ "subRevision": 8,
+ "build": 1944
+ },
+ "motor": {
+ "revision": 51,
+ "subRevision": 51,
+ "build": 11825
+ },
+ "groupId": 64003,
+ "aid": 4,
+ "signalStrength": 4,
+ "capabilities": 0,
+ "batteryKind": "unassigned",
+ "positions": {
+ "posKind1": 1,
+ "position1": 1040
+ }
+ }
+ ]
+}
+++ /dev/null
-{
- "sceneCollectionIds": [
- 27119
- ],
- "sceneCollectionData": [
- {
- "name": "QsO4cm4gb3A=",
- "colorId": 12,
- "iconId": 17,
- "id": 27119,
- "order": 0,
- "hkAssist": false
- }
- ]
-}
+++ /dev/null
-{
- "sceneIds": [
- 18097,
- 22663,
- 35821,
- 6551
- ],
- "sceneData": [
- {
- "roomId": 59611,
- "name": "RG9vciBPcGVu",
- "colorId": 6,
- "iconId": 160,
- "networkNumber": 19,
- "id": 18097,
- "order": 3,
- "hkAssist": false
- },
- {
- "roomId": 59611,
- "name": "SGVhcnQ=",
- "colorId": 15,
- "iconId": 49,
- "networkNumber": 3,
- "id": 22663,
- "order": 0,
- "hkAssist": false
- },
- {
- "roomId": 59611,
- "name": "Q2xvc2U=",
- "colorId": 5,
- "iconId": 31,
- "networkNumber": 8,
- "id": 35821,
- "order": 2,
- "hkAssist": false
- },
- {
- "roomId": 59611,
- "name": "T3Blbg==",
- "colorId": 10,
- "iconId": 95,
- "networkNumber": 20,
- "id": 6551,
- "order": 1,
- "hkAssist": false
- }
- ]
-}
+++ /dev/null
-{
- "shadeIds": [
- 25206,
- 55854,
- 50150
- ],
- "shadeData": [
- {
- "id": 25206,
- "type": 44,
- "batteryStatus": 3,
- "batteryStrength": 179,
- "roomId": 59611,
- "firmware": {
- "revision": 1,
- "subRevision": 8,
- "build": 1944
- },
- "name": "U2hhZGUgMg==",
- "motor": {
- "revision": 51,
- "subRevision": 51,
- "build": 11825
- },
- "groupId": 64003,
- "aid": 2,
- "signalStrength": 4,
- "capabilities": 0,
- "batteryKind": 1,
- "positions": {
- "posKind1": 3,
- "position1": 32579
- }
- },
- {
- "id": 55854,
- "type": 44,
- "batteryStatus": 3,
- "batteryStrength": 187,
- "roomId": 59611,
- "firmware": {
- "revision": 1,
- "subRevision": 8,
- "build": 1944
- },
- "motor": {
- "revision": 51,
- "subRevision": 51,
- "build": 11825
- },
- "name": "U2hhZGUgMw==",
- "groupId": 64003,
- "aid": 3,
- "signalStrength": 4,
- "capabilities": 0,
- "positions": {
- "posKind1": 1,
- "position1": 65534
- },
- "batteryKind": "unassigned"
- },
- {
- "id": 50150,
- "type": 44,
- "batteryStatus": 3,
- "batteryStrength": 181,
- "roomId": 59611,
- "name": "U2hhZGUgMQ==",
- "firmware": {
- "revision": 1,
- "subRevision": 8,
- "build": 1944
- },
- "motor": {
- "revision": 51,
- "subRevision": 51,
- "build": 11825
- },
- "groupId": 64003,
- "aid": 4,
- "signalStrength": 4,
- "capabilities": 0,
- "batteryKind": "unassigned",
- "positions": {
- "posKind1": 1,
- "position1": 1040
- }
- }
- ]
-}