From e4068cedb791daa330d7fb6206d4d831902ddb55 Mon Sep 17 00:00:00 2001 From: Tim Harper Date: Mon, 18 Feb 2019 00:31:49 -0700 Subject: [PATCH] Allow WindowCovering to be used without optional characteristics The pattern for most accessories is to require only the required characteristics, and then allow the specification of optional characteristics via the inclusion of interfaces. This pattern was not followed with WindowCovering. Worse, for reasons currently not understood, the inclusion of the HoldPositionCharacteristic causes the homekit pairing with the bridge to be completely unresponsive. (See issue #56) Change is made in a backwards compatible way, so that existing implementations that depend on WindowCovering as implemented will continue to function Addresses #56 --- .../hap/accessories/BasicWindowCovering.java | 78 +++++++++++++ .../HoldPositionWindowCovering.java | 15 +++ .../ObstructionDetectedWindowCovering.java | 24 ++++ .../hap/accessories/WindowCovering.java | 107 ++---------------- .../CurrentPositionCharacteristic.java | 6 +- .../HoldPositionCharacteristic.java | 6 +- .../PositionStateCharacteristic.java | 6 +- .../TargetPositionCharacteristic.java | 6 +- .../impl/services/WindowCoveringService.java | 35 ++++-- 9 files changed, 162 insertions(+), 121 deletions(-) create mode 100644 src/main/java/com/beowulfe/hap/accessories/BasicWindowCovering.java create mode 100644 src/main/java/com/beowulfe/hap/accessories/HoldPositionWindowCovering.java create mode 100644 src/main/java/com/beowulfe/hap/accessories/ObstructionDetectedWindowCovering.java diff --git a/src/main/java/com/beowulfe/hap/accessories/BasicWindowCovering.java b/src/main/java/com/beowulfe/hap/accessories/BasicWindowCovering.java new file mode 100644 index 000000000..827291310 --- /dev/null +++ b/src/main/java/com/beowulfe/hap/accessories/BasicWindowCovering.java @@ -0,0 +1,78 @@ +package com.beowulfe.hap.accessories; + +import com.beowulfe.hap.HomekitAccessory; +import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import com.beowulfe.hap.Service; +import com.beowulfe.hap.accessories.properties.WindowCoveringPositionState; +import com.beowulfe.hap.impl.services.WindowCoveringService; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +public interface BasicWindowCovering extends HomekitAccessory { + + /** + * Retrieves the current position + * + * @return a future that will contain the position as a value between 0 and 100 + */ + CompletableFuture getCurrentPosition(); + + /** + * Retrieves the target position + * + * @return a future that will contain the target position as a value between 0 and 100 + */ + CompletableFuture getTargetPosition(); + + /** + * Retrieves the state of the position: increasing, decreasing, or stopped + * + * @return a future that will contain the current state + */ + CompletableFuture getPositionState(); + + /** + * Sets the target position + * + * @param position the target position to set, as a value between 1 and 100 + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setTargetPosition(int position) throws Exception; + + /** + * Subscribes to changes in the current position. + * + * @param callback the function to call when the state changes. + */ + void subscribeCurrentPosition(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the target position. + * + * @param callback the function to call when the state changes. + */ + void subscribeTargetPosition(HomekitCharacteristicChangeCallback callback); + + /** + * Subscribes to changes in the position state: increasing, decreasing, or stopped + * + * @param callback the function to call when the state changes. + */ + void subscribePositionState(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the current position. */ + void unsubscribeCurrentPosition(); + + /** Unsubscribes from changes in the target position. */ + void unsubscribeTargetPosition(); + + /** Unsubscribes from changes in the position state */ + void unsubscribePositionState(); + + @Override + default Collection getServices() { + return Collections.singleton(new WindowCoveringService(this)); + } +} diff --git a/src/main/java/com/beowulfe/hap/accessories/HoldPositionWindowCovering.java b/src/main/java/com/beowulfe/hap/accessories/HoldPositionWindowCovering.java new file mode 100644 index 000000000..7437bfec9 --- /dev/null +++ b/src/main/java/com/beowulfe/hap/accessories/HoldPositionWindowCovering.java @@ -0,0 +1,15 @@ +package com.beowulfe.hap.accessories; + +import java.util.concurrent.CompletableFuture; + +public interface HoldPositionWindowCovering { + + /** + * Sets the hold position state + * + * @param hold whether or not to hold the current position state + * @return a future that completes when the change is made + * @throws Exception when the change cannot be made + */ + CompletableFuture setHoldPosition(boolean hold) throws Exception; +} diff --git a/src/main/java/com/beowulfe/hap/accessories/ObstructionDetectedWindowCovering.java b/src/main/java/com/beowulfe/hap/accessories/ObstructionDetectedWindowCovering.java new file mode 100644 index 000000000..c83f61621 --- /dev/null +++ b/src/main/java/com/beowulfe/hap/accessories/ObstructionDetectedWindowCovering.java @@ -0,0 +1,24 @@ +package com.beowulfe.hap.accessories; + +import com.beowulfe.hap.HomekitCharacteristicChangeCallback; +import java.util.concurrent.CompletableFuture; + +public interface ObstructionDetectedWindowCovering { + + /** + * Retrieves an indication that the window covering is obstructed from moving + * + * @return a future that will contain a boolean indicating whether an obstruction is present + */ + CompletableFuture getObstructionDetected(); + + /** + * Subscribes to changes in the obstruction detected state + * + * @param callback the function to call when the state changes. + */ + void subscribeObstructionDetected(HomekitCharacteristicChangeCallback callback); + + /** Unsubscribes from changes in the obstruction detected state */ + void unsubscribeObstructionDetected(); +} diff --git a/src/main/java/com/beowulfe/hap/accessories/WindowCovering.java b/src/main/java/com/beowulfe/hap/accessories/WindowCovering.java index 340013ca7..58fb0dfe9 100644 --- a/src/main/java/com/beowulfe/hap/accessories/WindowCovering.java +++ b/src/main/java/com/beowulfe/hap/accessories/WindowCovering.java @@ -1,107 +1,16 @@ package com.beowulfe.hap.accessories; -import com.beowulfe.hap.*; -import com.beowulfe.hap.accessories.properties.WindowCoveringPositionState; -import com.beowulfe.hap.impl.services.WindowCoveringService; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - /** * A window covering, like blinds, which can be remotely controlled. * * @author Andy Lintner + * @deprecated In 1.2.x, this interface will become replaced with BasicWindowCovering. Update your + * code to use that interface for now, and include the HoldPositionWindowCovering and + * ObstructionDetectedWindowCovering interfaces respectively */ -public interface WindowCovering extends HomekitAccessory { - - /** - * Retrieves the current position - * - * @return a future that will contain the position as a value between 0 and 100 - */ - CompletableFuture getCurrentPosition(); - - /** - * Retrieves the target position - * - * @return a future that will contain the target position as a value between 0 and 100 - */ - CompletableFuture getTargetPosition(); - - /** - * Retrieves the state of the position: increasing, decreasing, or stopped - * - * @return a future that will contain the current state - */ - CompletableFuture getPositionState(); - - /** - * Retrieves an indication that the window covering is obstructed from moving - * - * @return a future that will contain a boolean indicating whether an obstruction is present - */ - CompletableFuture getObstructionDetected(); - - @Override - default Collection getServices() { - return Collections.singleton(new WindowCoveringService(this)); - } - - /** - * Sets the target position - * - * @param position the target position to set, as a value between 1 and 100 - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setTargetPosition(int position) throws Exception; - - /** - * Sets the hold position state - * - * @param hold whether or not to hold the current position state - * @return a future that completes when the change is made - * @throws Exception when the change cannot be made - */ - CompletableFuture setHoldPosition(boolean hold) throws Exception; - - /** - * Subscribes to changes in the current position. - * - * @param callback the function to call when the state changes. - */ - void subscribeCurrentPosition(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the target position. - * - * @param callback the function to call when the state changes. - */ - void subscribeTargetPosition(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the position state: increasing, decreasing, or stopped - * - * @param callback the function to call when the state changes. - */ - void subscribePositionState(HomekitCharacteristicChangeCallback callback); - - /** - * Subscribes to changes in the obstruction detected state - * - * @param callback the function to call when the state changes. - */ - void subscribeObstructionDetected(HomekitCharacteristicChangeCallback callback); - - /** Unsubscribes from changes in the current position. */ - void unsubscribeCurrentPosition(); - - /** Unsubscribes from changes in the target position. */ - void unsubscribeTargetPosition(); - - /** Unsubscribes from changes in the position state */ - void unsubscribePositionState(); - - /** Unsubscribes from changes in the obstruction detected state */ - void unsubscribeObstructionDetected(); +@Deprecated +public interface WindowCovering + extends BasicWindowCovering, HoldPositionWindowCovering, ObstructionDetectedWindowCovering { + /* + * TODO - 1.2.x: Replace this interface with BasicWindowCovering */ } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java index 0ce752232..ab3490bc8 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/CurrentPositionCharacteristic.java @@ -1,7 +1,7 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; import com.beowulfe.hap.HomekitCharacteristicChangeCallback; -import com.beowulfe.hap.accessories.WindowCovering; +import com.beowulfe.hap.accessories.BasicWindowCovering; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; import java.util.concurrent.CompletableFuture; @@ -9,9 +9,9 @@ public class CurrentPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - private final WindowCovering windowCovering; + private final BasicWindowCovering windowCovering; - public CurrentPositionCharacteristic(WindowCovering windowCovering) { + public CurrentPositionCharacteristic(BasicWindowCovering windowCovering) { super("0000006D-0000-1000-8000-0026BB765291", false, true, "The current position", 0, 100, "%"); this.windowCovering = windowCovering; } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/HoldPositionCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/HoldPositionCharacteristic.java index 3dccf5252..b3b2434f5 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/HoldPositionCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/HoldPositionCharacteristic.java @@ -1,14 +1,14 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; -import com.beowulfe.hap.accessories.WindowCovering; +import com.beowulfe.hap.accessories.HoldPositionWindowCovering; import com.beowulfe.hap.characteristics.BooleanCharacteristic; import java.util.concurrent.CompletableFuture; public class HoldPositionCharacteristic extends BooleanCharacteristic { - private final WindowCovering windowCovering; + private final HoldPositionWindowCovering windowCovering; - public HoldPositionCharacteristic(WindowCovering windowCovering) { + public HoldPositionCharacteristic(HoldPositionWindowCovering windowCovering) { super("0000006F-0000-1000-8000-0026BB765291", true, false, "Whether or not to hold position"); this.windowCovering = windowCovering; } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/PositionStateCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/PositionStateCharacteristic.java index ccf159a1e..a880192c3 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/PositionStateCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/PositionStateCharacteristic.java @@ -1,7 +1,7 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; import com.beowulfe.hap.HomekitCharacteristicChangeCallback; -import com.beowulfe.hap.accessories.WindowCovering; +import com.beowulfe.hap.accessories.BasicWindowCovering; import com.beowulfe.hap.characteristics.EnumCharacteristic; import com.beowulfe.hap.characteristics.EventableCharacteristic; import java.util.concurrent.CompletableFuture; @@ -9,9 +9,9 @@ public class PositionStateCharacteristic extends EnumCharacteristic implements EventableCharacteristic { - private final WindowCovering windowCovering; + private final BasicWindowCovering windowCovering; - public PositionStateCharacteristic(WindowCovering windowCovering) { + public PositionStateCharacteristic(BasicWindowCovering windowCovering) { super("00000072-0000-1000-8000-0026BB765291", false, true, "The position state", 2); this.windowCovering = windowCovering; } diff --git a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetPositionCharacteristic.java b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetPositionCharacteristic.java index ceaf76e29..d46bd9770 100644 --- a/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetPositionCharacteristic.java +++ b/src/main/java/com/beowulfe/hap/impl/characteristics/windowcovering/TargetPositionCharacteristic.java @@ -1,7 +1,7 @@ package com.beowulfe.hap.impl.characteristics.windowcovering; import com.beowulfe.hap.HomekitCharacteristicChangeCallback; -import com.beowulfe.hap.accessories.WindowCovering; +import com.beowulfe.hap.accessories.BasicWindowCovering; import com.beowulfe.hap.characteristics.EventableCharacteristic; import com.beowulfe.hap.characteristics.IntegerCharacteristic; import java.util.concurrent.CompletableFuture; @@ -9,9 +9,9 @@ public class TargetPositionCharacteristic extends IntegerCharacteristic implements EventableCharacteristic { - private final WindowCovering windowCovering; + private final BasicWindowCovering windowCovering; - public TargetPositionCharacteristic(WindowCovering windowCovering) { + public TargetPositionCharacteristic(BasicWindowCovering windowCovering) { super("0000007C-0000-1000-8000-0026BB765291", true, true, "The target position", 0, 100, "%"); this.windowCovering = windowCovering; } diff --git a/src/main/java/com/beowulfe/hap/impl/services/WindowCoveringService.java b/src/main/java/com/beowulfe/hap/impl/services/WindowCoveringService.java index f26cce03d..3e5c797db 100644 --- a/src/main/java/com/beowulfe/hap/impl/services/WindowCoveringService.java +++ b/src/main/java/com/beowulfe/hap/impl/services/WindowCoveringService.java @@ -1,28 +1,31 @@ package com.beowulfe.hap.impl.services; +import com.beowulfe.hap.accessories.BasicWindowCovering; +import com.beowulfe.hap.accessories.HoldPositionWindowCovering; import com.beowulfe.hap.accessories.HorizontalTiltingWindowCovering; +import com.beowulfe.hap.accessories.ObstructionDetectedWindowCovering; import com.beowulfe.hap.accessories.VerticalTiltingWindowCovering; -import com.beowulfe.hap.accessories.WindowCovering; import com.beowulfe.hap.impl.characteristics.common.ObstructionDetectedCharacteristic; -import com.beowulfe.hap.impl.characteristics.windowcovering.*; +import com.beowulfe.hap.impl.characteristics.windowcovering.CurrentHorizontalTiltAngleCharacteristic; +import com.beowulfe.hap.impl.characteristics.windowcovering.CurrentPositionCharacteristic; +import com.beowulfe.hap.impl.characteristics.windowcovering.CurrentVerticalTiltAngleCharacteristic; +import com.beowulfe.hap.impl.characteristics.windowcovering.HoldPositionCharacteristic; +import com.beowulfe.hap.impl.characteristics.windowcovering.PositionStateCharacteristic; +import com.beowulfe.hap.impl.characteristics.windowcovering.TargetHorizontalTiltAngleCharacteristic; +import com.beowulfe.hap.impl.characteristics.windowcovering.TargetPositionCharacteristic; +import com.beowulfe.hap.impl.characteristics.windowcovering.TargetVerticalTiltAngleCharacteristic; public class WindowCoveringService extends AbstractServiceImpl { - public WindowCoveringService(WindowCovering windowCovering) { + public WindowCoveringService(BasicWindowCovering windowCovering) { this(windowCovering, windowCovering.getLabel()); } - public WindowCoveringService(WindowCovering windowCovering, String serviceName) { + public WindowCoveringService(BasicWindowCovering windowCovering, String serviceName) { super("0000008C-0000-1000-8000-0026BB765291", windowCovering, serviceName); addCharacteristic(new CurrentPositionCharacteristic(windowCovering)); - addCharacteristic(new HoldPositionCharacteristic(windowCovering)); addCharacteristic(new PositionStateCharacteristic(windowCovering)); addCharacteristic(new TargetPositionCharacteristic(windowCovering)); - addCharacteristic( - new ObstructionDetectedCharacteristic( - () -> windowCovering.getObstructionDetected(), - c -> windowCovering.subscribeObstructionDetected(c), - () -> windowCovering.unsubscribeObstructionDetected())); if (windowCovering instanceof HorizontalTiltingWindowCovering) { addCharacteristic( @@ -40,5 +43,17 @@ public WindowCoveringService(WindowCovering windowCovering, String serviceName) new TargetVerticalTiltAngleCharacteristic( (VerticalTiltingWindowCovering) windowCovering)); } + if (windowCovering instanceof HoldPositionWindowCovering) { + HoldPositionWindowCovering hpwc = (HoldPositionWindowCovering) windowCovering; + addCharacteristic(new HoldPositionCharacteristic(hpwc)); + } + if (windowCovering instanceof ObstructionDetectedWindowCovering) { + ObstructionDetectedWindowCovering wc = (ObstructionDetectedWindowCovering) windowCovering; + addCharacteristic( + new ObstructionDetectedCharacteristic( + () -> wc.getObstructionDetected(), + c -> wc.subscribeObstructionDetected(c), + () -> wc.unsubscribeObstructionDetected())); + } } }