diff --git a/bundles/org.openhab.binding.hdpowerview/README.md b/bundles/org.openhab.binding.hdpowerview/README.md
index c777915c4681e..694e812983dab 100644
--- a/bundles/org.openhab.binding.hdpowerview/README.md
+++ b/bundles/org.openhab.binding.hdpowerview/README.md
@@ -111,7 +111,7 @@ The `position` and `secondary` channels are Rollershutter types.
For vertical shades, the binding maps the vertical position of the "rail" to the Rollershutter ▲ / ▼ commands, and its respective percent value.
And for horizontal shades, it maps the horizontal position of the "truck" to the Rollershutter ▲ / ▼ commands, and its respective percent value.
-Depending on whether the shade is a top-down, bottom-up, left-right, right-left, or dual action shade, the `OPEN` and `CLOSED` position of the shades may differ from the ▲ / ▼ commands follows..
+Depending on whether the shade is a top-down, bottom-up, left-right, right-left, dual action shade, or, a shade with a secondary blackout panel, the `OPEN` and `CLOSED` position of the shades may differ from the ▲ / ▼ commands follows..
| Type of Shade | Channel | Rollershutter Command | Motion direction | Shade State | Percent | Pebble Remote Button |
|-----------------------------|-------------------|-----------------------|------------------|----------------|-------------------|----------------------|
@@ -127,6 +127,8 @@ Depending on whether the shade is a top-down, bottom-up, left-right, right-left,
| | | ▼ | Down | `CLOSED` | 100% | ▼ |
| Dual action
(upper rail) | ***`secondary`*** | ▲ | Up | ***`CLOSED`*** | 0%1) | ![](doc/right.png) |
| | | ▼ | Down | ***`OPEN`*** | 100%1) | ![](doc/left.png) |
+| Blackout panel ('DuoLite') | ***`secondary`*** | ▲ | Up | `OPEN` | 0% | ▲ |
+| | | ▼ | Down | `CLOSED` | 100% | ▼ |
***1) BUG NOTE***: In openHAB versions v3.1.x and earlier, there was a bug in the handling of the position percent value of the `secondary` shade.
Although the RollerShutter Up/Down commands functioned properly as described in the table above, the percent state values (e.g. displayed on a slider control), did not.
@@ -159,6 +161,9 @@ On dual action shades, the top rail cannot move below the bottom rail, nor can t
So the value of `secondary` is constrained by the prior value of `position`.
And the value of `position` is constrained by the prior value of `secondary`.
+On shades with a secondary blackout panel 'DuoLite', the secondary blackout panel cannot be moved unless the main shade panel is already down.
+In this case, the position of the secondary blackout panel is reported as 0%.
+
## Refreshing the PowerView Hub Cache
The hub maintains a cache of the last known state of its shades, and this binding delivers those values.
diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java
index 46db0371a9998..236e468947820 100644
--- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java
+++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/CoordinateSystem.java
@@ -19,9 +19,9 @@
* 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_ZERO_IS_CLOSED primary rail, whose coordinate value 0 means shade is closed.
- * @param SECONDARY_ZERO_IS_OPEN secondary rail, whose coordinate value 0 means shade is open.
- * @param VANE_TILT_COORDS vane/tilt operator, whose coordinate system is for vanes.
+ * @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
@@ -77,9 +77,9 @@ public enum CoordinateSystem {
*
*/
NONE,
- PRIMARY_ZERO_IS_CLOSED,
- SECONDARY_ZERO_IS_OPEN,
- VANE_TILT_COORDS,
+ PRIMARY_POSITION,
+ SECONDARY_POSITION,
+ VANE_TILT_POSITION,
ERROR_UNKNOWN;
public static final int MAX_SHADE = 65535;
diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java
index 0c32427e4a61e..1c397d60bbac4 100644
--- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java
+++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/api/ShadePosition.java
@@ -77,13 +77,13 @@ public State getState(Capabilities shadeCapabilities, CoordinateSystem posKindCo
*/
private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) {
switch (posKindCoords) {
- case PRIMARY_ZERO_IS_CLOSED:
+ 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_ZERO_IS_OPEN);
+ State secondary = getState(shadeCapabilities, SECONDARY_POSITION);
if (secondary instanceof PercentType) {
int secPercent = ((PercentType) secondary).intValue();
if (percent < secPercent) {
@@ -95,15 +95,20 @@ private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKi
position1 = MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE);
break;
- case SECONDARY_ZERO_IS_OPEN:
+ case SECONDARY_POSITION:
/*
+ * Secondary, blackout shade a 'Duolite' shade: => INVERTED
* Secondary, upper rail of a dual action shade: => NOT INVERTED
*/
posKind1 = posKindCoords.ordinal();
- position1 = (int) Math.round((double) percent / 100 * MAX_SHADE);
+ if (shadeCapabilities.supportsBlackoutShade()) {
+ 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_COORDS:
+ case VANE_TILT_POSITION:
/*
* Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED
*/
@@ -127,28 +132,38 @@ private void setPosition1(Capabilities shadeCapabilities, CoordinateSystem posKi
*/
private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posKindCoords) {
switch (posKindCoords) {
- case PRIMARY_ZERO_IS_CLOSED:
+ 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_COORDS.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) {
+ if (VANE_TILT_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) {
+ return PercentType.HUNDRED;
+ }
+ if (SECONDARY_POSITION.equals(posKind1) && shadeCapabilities.supportsBlackoutShade()) {
return PercentType.HUNDRED;
}
break;
- case SECONDARY_ZERO_IS_OPEN:
+ 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.supportsBlackoutShade()) {
+ return new PercentType(100 - (int) Math.round((double) position1 / MAX_SHADE * 100));
+ }
return new PercentType((int) Math.round((double) position1 / MAX_SHADE * 100));
}
+ if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsBlackoutShade()) {
+ return PercentType.ZERO;
+ }
break;
- case VANE_TILT_COORDS:
+ case VANE_TILT_POSITION:
/*
* Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED
*
@@ -164,7 +179,7 @@ private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posK
int max = shadeCapabilities.supportsTilt180() ? MAX_SHADE : MAX_VANE;
return new PercentType((int) Math.round((double) Math.min(position1, max) / max * 100));
}
- if (PRIMARY_ZERO_IS_CLOSED.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) {
+ if (PRIMARY_POSITION.equals(posKind1) && shadeCapabilities.supportsTiltOnClosed()) {
return position1 != 0 ? UnDefType.UNDEF : PercentType.ZERO;
}
break;
@@ -185,7 +200,7 @@ private State getPosition1(Capabilities shadeCapabilities, CoordinateSystem posK
*/
private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKindCoords, int percent) {
switch (posKindCoords) {
- case PRIMARY_ZERO_IS_CLOSED:
+ case PRIMARY_POSITION:
/*
* Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED
*/
@@ -193,13 +208,13 @@ private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKi
position2 = Integer.valueOf(MAX_SHADE - (int) Math.round((double) percent / 100 * MAX_SHADE));
break;
- case SECONDARY_ZERO_IS_OPEN:
+ 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_ZERO_IS_CLOSED);
+ State primary = getState(shadeCapabilities, PRIMARY_POSITION);
if (primary instanceof PercentType) {
int primaryPercent = ((PercentType) primary).intValue();
if (percent > primaryPercent) {
@@ -211,7 +226,7 @@ private void setPosition2(Capabilities shadeCapabilities, CoordinateSystem posKi
position2 = Integer.valueOf((int) Math.round((double) percent / 100 * MAX_SHADE));
break;
- case VANE_TILT_COORDS:
+ 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));
@@ -239,7 +254,7 @@ private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posK
}
switch (posKindCoords) {
- case PRIMARY_ZERO_IS_CLOSED:
+ case PRIMARY_POSITION:
/*
* Primary rail of a bottom-up shade, or lower rail of a dual action shade: => INVERTED
*/
@@ -248,7 +263,7 @@ private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posK
}
break;
- case SECONDARY_ZERO_IS_OPEN:
+ case SECONDARY_POSITION:
/*
* Secondary, upper rail of a dual action shade: => NOT INVERTED
*/
@@ -259,12 +274,8 @@ private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posK
/*
* Vane angle of the primary rail of a bottom-up single action shade: => NOT INVERTED
- *
- * 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..
*/
- case VANE_TILT_COORDS:
+ 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));
@@ -284,7 +295,7 @@ private State getPosition2(Capabilities shadeCapabilities, CoordinateSystem posK
* @return true if the ShadePosition supports a secondary rail.
*/
public boolean secondaryRailDetected() {
- return SECONDARY_ZERO_IS_OPEN.equals(posKind1) || SECONDARY_ZERO_IS_OPEN.equals(posKind2);
+ return SECONDARY_POSITION.equals(posKind1) || SECONDARY_POSITION.equals(posKind2);
}
/**
@@ -294,8 +305,8 @@ public boolean secondaryRailDetected() {
* @return true if potential support for tilt anywhere functionality was detected.
*/
public boolean tiltAnywhereDetected() {
- return ((PRIMARY_ZERO_IS_CLOSED.equals(posKind1)) && (VANE_TILT_COORDS.equals(posKind2))
- || ((PRIMARY_ZERO_IS_CLOSED.equals(posKind2) && (VANE_TILT_COORDS.equals(posKind1)))));
+ return ((PRIMARY_POSITION.equals(posKind1)) && (VANE_TILT_POSITION.equals(posKind2))
+ || ((PRIMARY_POSITION.equals(posKind2) && (VANE_TILT_POSITION.equals(posKind1)))));
}
/**
@@ -310,7 +321,7 @@ public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSyste
logger.trace("setPosition(): capabilities={}, coords={}, percent={}", shadeCapabilities, posKindCoords,
percent);
// if necessary swap the order of position1 and position2
- if (PRIMARY_ZERO_IS_CLOSED.equals(posKind2) && !PRIMARY_ZERO_IS_CLOSED.equals(posKind1)) {
+ if (PRIMARY_POSITION.equals(posKind2) && !PRIMARY_POSITION.equals(posKind1)) {
final Integer posKind2Temp = posKind2;
final Integer position2Temp = position2;
posKind2 = Integer.valueOf(posKind1);
@@ -327,23 +338,25 @@ public ShadePosition setPosition(Capabilities shadeCapabilities, CoordinateSyste
// logic to set either position1 or position2
switch (posKindCoords) {
- case PRIMARY_ZERO_IS_CLOSED:
+ case PRIMARY_POSITION:
if (shadeCapabilities.supportsPrimary()) {
setPosition1(shadeCapabilities, posKindCoords, percent);
}
break;
- case SECONDARY_ZERO_IS_OPEN:
+ case SECONDARY_POSITION:
if (shadeCapabilities.supportsSecondary()) {
if (shadeCapabilities.supportsPrimary()) {
setPosition2(shadeCapabilities, posKindCoords, percent);
} else {
setPosition1(shadeCapabilities, posKindCoords, percent);
}
+ } else if (shadeCapabilities.supportsBlackoutShade()) {
+ setPosition1(shadeCapabilities, posKindCoords, percent);
}
break;
- case VANE_TILT_COORDS:
+ case VANE_TILT_POSITION:
if (shadeCapabilities.supportsPrimary()) {
if (shadeCapabilities.supportsTiltOnClosed()) {
setPosition1(shadeCapabilities, posKindCoords, percent);
diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/database/ShadeCapabilitiesDatabase.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/database/ShadeCapabilitiesDatabase.java
index 1fcb7a529b87e..a1f665c1aee5c 100644
--- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/database/ShadeCapabilitiesDatabase.java
+++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/database/ShadeCapabilitiesDatabase.java
@@ -48,8 +48,8 @@ public class ShadeCapabilitiesDatabase {
new Capabilities(5) .tiltAnywhere().tilt180() .text("Tilt Only 180°"),
new Capabilities(6).primary() .text("Top Down") .primaryStateInverted(),
new Capabilities(7).primary() .secondary().text("Top Down Bottom Up"),
- new Capabilities(8).primary() .text("Duolite Lift"),
- new Capabilities(9).primary().tiltAnywhere() .text("Duolite Lift and Tilt 90°"),
+ new Capabilities(8).primary() .text("Duolite Lift") .withBlackoutShade(),
+ new Capabilities(9).primary().tiltAnywhere() .text("Duolite Lift and Tilt 90°").withBlackoutShade(),
// @formatter:on
new Capabilities()).stream().collect(Collectors.toMap(Capabilities::getValue, Function.identity()));
@@ -148,12 +148,18 @@ public static class Capabilities extends Base {
private boolean supportsSecondary;
private boolean supportsTiltOnClosed;
private boolean supportsTiltAnywhere;
+ private boolean supportsBlackoutShade;
private boolean primaryStateInverted;
private boolean tilt180Degrees;
public Capabilities() {
}
+ protected Capabilities withBlackoutShade() {
+ supportsBlackoutShade = true;
+ return this;
+ }
+
protected Capabilities(int capabilities) {
intValue = capabilities;
}
@@ -249,6 +255,15 @@ public boolean supportsTiltOnClosed() {
public boolean supportsTilt180() {
return tilt180Degrees;
}
+
+ /**
+ * Check if the Capabilities class instance supports a secondary 'DuoLite' blackout shade.
+ *
+ * @return true if the primary shade supports a secondary blackout shade.
+ */
+ public boolean supportsBlackoutShade() {
+ return supportsBlackoutShade;
+ }
}
/**
diff --git a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java
index 30ab73e700856..428cb5f895079 100644
--- a/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java
+++ b/bundles/org.openhab.binding.hdpowerview/src/main/java/org/openhab/binding/hdpowerview/internal/handler/HDPowerViewShadeHandler.java
@@ -192,9 +192,9 @@ private void handleShadeCommand(String channelId, Command command, HDPowerViewWe
switch (channelId) {
case CHANNEL_SHADE_POSITION:
if (command instanceof PercentType) {
- moveShade(PRIMARY_ZERO_IS_CLOSED, ((PercentType) command).intValue(), webTargets, shadeId);
+ moveShade(PRIMARY_POSITION, ((PercentType) command).intValue(), webTargets, shadeId);
} else if (command instanceof UpDownType) {
- moveShade(PRIMARY_ZERO_IS_CLOSED, UpDownType.UP == command ? 0 : 100, webTargets, shadeId);
+ moveShade(PRIMARY_POSITION, UpDownType.UP == command ? 0 : 100, webTargets, shadeId);
} else if (command instanceof StopMoveType) {
if (StopMoveType.STOP == command) {
stopShade(webTargets, shadeId);
@@ -206,17 +206,17 @@ private void handleShadeCommand(String channelId, Command command, HDPowerViewWe
case CHANNEL_SHADE_VANE:
if (command instanceof PercentType) {
- moveShade(VANE_TILT_COORDS, ((PercentType) command).intValue(), webTargets, shadeId);
+ moveShade(VANE_TILT_POSITION, ((PercentType) command).intValue(), webTargets, shadeId);
} else if (command instanceof OnOffType) {
- moveShade(VANE_TILT_COORDS, OnOffType.ON == command ? 100 : 0, webTargets, shadeId);
+ moveShade(VANE_TILT_POSITION, OnOffType.ON == command ? 100 : 0, webTargets, shadeId);
}
break;
case CHANNEL_SHADE_SECONDARY_POSITION:
if (command instanceof PercentType) {
- moveShade(SECONDARY_ZERO_IS_OPEN, ((PercentType) command).intValue(), webTargets, shadeId);
+ moveShade(SECONDARY_POSITION, ((PercentType) command).intValue(), webTargets, shadeId);
} else if (command instanceof UpDownType) {
- moveShade(SECONDARY_ZERO_IS_OPEN, UpDownType.UP == command ? 0 : 100, webTargets, shadeId);
+ moveShade(SECONDARY_POSITION, UpDownType.UP == command ? 0 : 100, webTargets, shadeId);
} else if (command instanceof StopMoveType) {
if (StopMoveType.STOP == command) {
stopShade(webTargets, shadeId);
@@ -392,9 +392,9 @@ private void updatePositionStates(ShadePosition shadePos) {
updateState(CHANNEL_SHADE_SECONDARY_POSITION, UnDefType.UNDEF);
return;
}
- updateState(CHANNEL_SHADE_POSITION, shadePos.getState(capabilities, PRIMARY_ZERO_IS_CLOSED));
- updateState(CHANNEL_SHADE_VANE, shadePos.getState(capabilities, VANE_TILT_COORDS));
- updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePos.getState(capabilities, SECONDARY_ZERO_IS_OPEN));
+ updateState(CHANNEL_SHADE_POSITION, shadePos.getState(capabilities, PRIMARY_POSITION));
+ updateState(CHANNEL_SHADE_VANE, shadePos.getState(capabilities, VANE_TILT_POSITION));
+ updateState(CHANNEL_SHADE_SECONDARY_POSITION, shadePos.getState(capabilities, SECONDARY_POSITION));
}
private void updateBatteryLevelStates(int batteryStatus) {
diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java
index 229468ecb53d8..883124450d0cb 100644
--- a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java
+++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/HDPowerViewJUnitTests.java
@@ -57,8 +57,6 @@ public class HDPowerViewJUnitTests {
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");
- private final ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
-
/*
* load a test JSON string from a file.
*/
@@ -190,16 +188,17 @@ public void testOnlineCommunication() {
assertNotNull(capabilitiesValue);
if (positions != null && capabilitiesValue != null) {
- Capabilities capabilities = db.getCapabilities(capabilitiesValue.intValue());
+ Capabilities capabilities = new ShadeCapabilitiesDatabase()
+ .getCapabilities(capabilitiesValue.intValue());
- State pos = positions.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
+ 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_ZERO_IS_CLOSED, position);
+ ShadePosition targetPosition = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION,
+ position);
assertNotNull(targetPosition);
if (allowShadeMovementCommands) {
@@ -209,8 +208,8 @@ public void testOnlineCommunication() {
ShadePosition actualPosition = newData.positions;
assertNotNull(actualPosition);
if (actualPosition != null) {
- assertEquals(targetPosition.getState(capabilities, PRIMARY_ZERO_IS_CLOSED),
- actualPosition.getState(capabilities, PRIMARY_ZERO_IS_CLOSED));
+ assertEquals(targetPosition.getState(capabilities, PRIMARY_POSITION),
+ actualPosition.getState(capabilities, PRIMARY_POSITION));
}
}
}
@@ -250,73 +249,6 @@ public void testOnlineCommunication() {
}
}
- /**
- * Test parsing of ShadePosition (shade fully up).
- *
- */
- @Test
- public void testShadePositionParsingFullyUp() {
- Capabilities capabilities = db.getCapabilities(0);
- ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_ZERO_IS_CLOSED, 0);
- assertNotNull(test);
- State pos = test.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
- assertEquals(PercentType.class, pos.getClass());
- assertEquals(0, ((PercentType) pos).intValue());
- pos = test.getState(capabilities, VANE_TILT_COORDS);
- assertTrue(UnDefType.UNDEF.equals(pos));
- }
-
- /**
- * Test parsing of ShadePosition (shade fully down (method 1)).
- *
- */
- @Test
- public void testShadePositionParsingShadeFullyDown1() {
- Capabilities capabilities = db.getCapabilities(0);
- ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_ZERO_IS_CLOSED, 100);
- assertNotNull(test);
- State pos = test.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
- assertEquals(PercentType.class, pos.getClass());
- assertEquals(100, ((PercentType) pos).intValue());
- pos = test.getState(capabilities, VANE_TILT_COORDS);
- assertEquals(PercentType.class, pos.getClass());
- assertEquals(0, ((PercentType) pos).intValue());
- }
-
- /**
- * Test parsing of ShadePosition (shade fully down (method 2)).
- *
- */
- @Test
- public void testShadePositionParsingShadeFullyDown2() {
- Capabilities capabilities = db.getCapabilities(0);
- ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_COORDS, 0);
- assertNotNull(test);
- State pos = test.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
- assertEquals(PercentType.class, pos.getClass());
- assertEquals(100, ((PercentType) pos).intValue());
- pos = test.getState(capabilities, VANE_TILT_COORDS);
- assertEquals(PercentType.class, pos.getClass());
- assertEquals(0, ((PercentType) pos).intValue());
- }
-
- /**
- * Test parsing of ShadePosition (shade fully down (method 2) and vane fully open).
- *
- */
- @Test
- public void testShadePositionParsingShadeFullyDownVaneOpen() {
- Capabilities capabilities = db.getCapabilities(0);
- ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_COORDS, 100);
- assertNotNull(test);
- State pos = test.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
- assertEquals(PercentType.class, pos.getClass());
- assertEquals(100, ((PercentType) pos).intValue());
- pos = test.getState(capabilities, VANE_TILT_COORDS);
- assertEquals(PercentType.class, pos.getClass());
- assertEquals(100, ((PercentType) pos).intValue());
- }
-
/**
* Test generic JSON shades response.
*/
@@ -409,18 +341,18 @@ public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws JsonParseExcept
assertNotNull(capabilitiesValue);
if (capabilitiesValue != null) {
assertEquals(7, capabilitiesValue.intValue());
-
+ ShadeCapabilitiesDatabase db = new ShadeCapabilitiesDatabase();
Capabilities capabilities = db.getCapabilities(capabilitiesValue);
- State pos = shadePos.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
+ State pos = shadePos.getState(capabilities, PRIMARY_POSITION);
assertEquals(PercentType.class, pos.getClass());
assertEquals(59, ((PercentType) pos).intValue());
- pos = shadePos.getState(capabilities, SECONDARY_ZERO_IS_OPEN);
+ pos = shadePos.getState(capabilities, SECONDARY_POSITION);
assertEquals(PercentType.class, pos.getClass());
assertEquals(35, ((PercentType) pos).intValue());
- pos = shadePos.getState(capabilities, VANE_TILT_COORDS);
+ pos = shadePos.getState(capabilities, VANE_TILT_POSITION);
assertEquals(UnDefType.class, pos.getClass());
assertEquals(3, shadeData.batteryStatus);
@@ -442,18 +374,18 @@ public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws JsonParseExcept
assertNotNull(shadePosition);
if (shadePosition != null) {
// ==== position2 ====
- State position2Old = shadePosition.getState(capabilities, SECONDARY_ZERO_IS_OPEN);
- shadePosition.setPosition(capabilities, PRIMARY_ZERO_IS_CLOSED, 99);
- State position2New = shadePosition.getState(capabilities, SECONDARY_ZERO_IS_OPEN);
+ 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_ZERO_IS_CLOSED);
- shadePosition.setPosition(capabilities, SECONDARY_ZERO_IS_OPEN, 99);
- State position1New = shadePosition.getState(capabilities, PRIMARY_ZERO_IS_CLOSED);
+ 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(),
@@ -464,80 +396,4 @@ public void duetteTopDownBottomUpShadeIsParsedCorrectly() throws JsonParseExcept
}
}
}
-
- /**
- * General tests of the database of known types.
- */
- @Test
- public void testKnownTypesDatabase() {
- assertTrue(db.isTypeInDatabase(4));
- assertTrue(db.isCapabilitiesInDatabase(0));
-
- assertTrue(db.getCapabilities(6).isPrimaryStateInverted());
- assertTrue(db.getCapabilities(7).supportsSecondary());
-
- assertEquals(db.getType(4).getCapabilities(), 0);
- assertEquals(db.getType(-1).getCapabilities(), -1);
-
- assertFalse(db.isTypeInDatabase(99));
- assertFalse(db.isCapabilitiesInDatabase(99));
-
- assertFalse(db.getCapabilities(0).isPrimaryStateInverted());
- assertFalse(db.getCapabilities(-1).isPrimaryStateInverted());
- assertFalse(db.getCapabilities(99).isPrimaryStateInverted());
-
- assertFalse(db.getCapabilities(0).supportsSecondary());
- assertFalse(db.getCapabilities(-1).supportsSecondary());
- assertFalse(db.getCapabilities(99).supportsSecondary());
- }
-
- /**
- * 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() {
- ShadePosition shade = new ShadePosition();
- Capabilities caps = db.getCapabilities(7);
-
- // ==== OK !! primary at bottom, secondary at top ====
- shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0);
- assertEquals(PercentType.HUNDRED, shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
- assertEquals(PercentType.ZERO, shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
-
- // ==== OK !! primary at middle, secondary at top ====
- shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 50).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0);
- assertEquals(new PercentType(50), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
- assertEquals(PercentType.ZERO, shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
-
- // ==== OK !! primary at middle, secondary at middle ====
- shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 50).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 50);
- assertEquals(new PercentType(50), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
- assertEquals(new PercentType(50), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
-
- // ==== IMPOSSIBLE !! secondary at middle, primary above => test the constraining code ====
- shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100);
- shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 40).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 25);
- assertEquals(new PercentType(40), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
- assertEquals(new PercentType(40), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
-
- // ==== OK !! secondary at middle, primary below ====
- shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100);
- shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 50).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 75);
- assertEquals(new PercentType(50), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
- assertEquals(new PercentType(75), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
-
- // ==== IMPOSSIBLE !! primary at middle, secondary below => test the constraining code ====
- shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100);
- shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 60).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 75);
- assertEquals(new PercentType(60), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
- assertEquals(new PercentType(60), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
-
- // ==== OK !! primary at middle, secondary above ====
- shade.setPosition(caps, SECONDARY_ZERO_IS_OPEN, 0).setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 100);
- shade.setPosition(caps, PRIMARY_ZERO_IS_CLOSED, 60).setPosition(caps, SECONDARY_ZERO_IS_OPEN, 25);
- assertEquals(new PercentType(60), shade.getState(caps, PRIMARY_ZERO_IS_CLOSED));
- assertEquals(new PercentType(25), shade.getState(caps, SECONDARY_ZERO_IS_OPEN));
- }
}
diff --git a/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java
new file mode 100644
index 0000000000000..818dc4a153f83
--- /dev/null
+++ b/bundles/org.openhab.binding.hdpowerview/src/test/java/org/openhab/binding/hdpowerview/ShadePositionTest.java
@@ -0,0 +1,292 @@
+/**
+ * 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.getCapabilities(0).supportsPrimary());
+ assertTrue(db.getCapabilities(0).supportsTiltOnClosed());
+ assertTrue(db.getCapabilities(1).supportsTiltAnywhere());
+ assertTrue(db.getCapabilities(2).supportsTilt180());
+ assertTrue(db.getCapabilities(3).supportsTiltOnClosed());
+ assertTrue(db.getCapabilities(4).supportsTilt180());
+ assertTrue(db.getCapabilities(5).supportsTilt180());
+ assertFalse(db.getCapabilities(5).supportsPrimary());
+ assertTrue(db.getCapabilities(6).isPrimaryStateInverted());
+ assertTrue(db.getCapabilities(7).supportsSecondary());
+ assertTrue(db.getCapabilities(8).supportsBlackoutShade());
+ assertTrue(db.getCapabilities(9).supportsBlackoutShade());
+
+ assertEquals(db.getType(4).getCapabilities(), 0);
+ assertEquals(db.getType(-1).getCapabilities(), -1);
+
+ assertFalse(db.isTypeInDatabase(99));
+ assertFalse(db.isCapabilitiesInDatabase(99));
+
+ assertFalse(db.getCapabilities(0).isPrimaryStateInverted());
+ assertFalse(db.getCapabilities(-1).isPrimaryStateInverted());
+ assertFalse(db.getCapabilities(99).isPrimaryStateInverted());
+
+ 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 ShadePosition (shade fully up).
+ *
+ */
+ @Test
+ public void testShadePositionParsingFullyUp() {
+ Capabilities capabilities = db.getCapabilities(0);
+ ShadePosition test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0);
+ assertNotNull(test);
+ State pos = test.getState(capabilities, PRIMARY_POSITION);
+ assertShadePosition(pos, 0);
+ pos = test.getState(capabilities, VANE_TILT_POSITION);
+ assertTrue(UnDefType.UNDEF.equals(pos));
+ }
+
+ /**
+ * Test parsing of ShadePosition (shade fully down (method 1)).
+ *
+ */
+ @Test
+ public void testShadePositionParsingShadeFullyDown1() {
+ 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, VANE_TILT_POSITION), 0);
+ }
+
+ /**
+ * Test parsing of ShadePosition (shade fully down (method 2)).
+ *
+ */
+ @Test
+ public void testShadePositionParsingShadeFullyDown2() {
+ Capabilities capabilities = db.getCapabilities(0);
+ ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 0);
+ assertNotNull(test);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 0);
+ }
+
+ /**
+ * Test parsing of ShadePosition (shade fully down (method 2) and vane fully open).
+ *
+ */
+ @Test
+ public void testShadePositionParsingShadeFullyDownVaneOpen() {
+ Capabilities capabilities = db.getCapabilities(0);
+ ShadePosition test = new ShadePosition().setPosition(capabilities, VANE_TILT_POSITION, 100);
+ assertNotNull(test);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 100);
+ }
+
+ /**
+ * 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);
+
+ // ==== 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);
+
+ // ==== 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);
+
+ // ==== 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);
+
+ // ==== 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);
+
+ // ==== 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);
+
+ // ==== 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);
+ }
+
+ /**
+ * 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);
+
+ // 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);
+
+ // 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);
+
+ // 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);
+
+ // 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);
+
+ // 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);
+ }
+
+ /**
+ * Test parsing of DuoLite shades having both a secondary blackout shade, and tilt anywhere functionality.
+ *
+ */
+ @Test
+ public void testDuoliteTiltShadePositionParsing() {
+ // blackout shades with tilt have capabilities 9
+ Capabilities capabilities = db.getCapabilities(9);
+ ShadePosition test;
+
+ // both shades up, tilt 0%
+ test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 0).setPosition(capabilities,
+ VANE_TILT_POSITION, 0);
+ assertNotNull(test);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 0);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0);
+
+ // front shade 50% down, tilt 30%
+ test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 50).setPosition(capabilities,
+ VANE_TILT_POSITION, 30);
+ assertNotNull(test);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 50);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 0);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30);
+
+ // front shade 100% down, back shade 0% down, tilt 30%
+ test = new ShadePosition().setPosition(capabilities, PRIMARY_POSITION, 100).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);
+
+ // front shade 100% down, back shade 0% down, tilt 30% (ALTERNATE)
+ test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 0).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);
+
+ // front shade 100% down, back shade 50% down, tilt 30%
+ test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 50).setPosition(capabilities,
+ VANE_TILT_POSITION, 30);
+ assertNotNull(test);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 50);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 30);
+
+ // front shade 100% down, back shade 100% down, tilt 70%
+ test = new ShadePosition().setPosition(capabilities, SECONDARY_POSITION, 100).setPosition(capabilities,
+ VANE_TILT_POSITION, 70);
+ assertNotNull(test);
+ assertShadePosition(test.getState(capabilities, PRIMARY_POSITION), 100);
+ assertShadePosition(test.getState(capabilities, SECONDARY_POSITION), 100);
+ assertShadePosition(test.getState(capabilities, VANE_TILT_POSITION), 70);
+ }
+}