Skip to content

Commit

Permalink
Allow WindowCovering to be used without optional characteristics
Browse files Browse the repository at this point in the history
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 hap-java#56)

Change is made in a backwards compatible way, so that existing implementations
that depend on WindowCovering as implemented will continue to function

Addresses hap-java#56
  • Loading branch information
timcharper committed Feb 18, 2019
1 parent 939c344 commit e4068ce
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 121 deletions.
Original file line number Diff line number Diff line change
@@ -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<Integer> getCurrentPosition();

/**
* Retrieves the target position
*
* @return a future that will contain the target position as a value between 0 and 100
*/
CompletableFuture<Integer> getTargetPosition();

/**
* Retrieves the state of the position: increasing, decreasing, or stopped
*
* @return a future that will contain the current state
*/
CompletableFuture<WindowCoveringPositionState> 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<Void> 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<Service> getServices() {
return Collections.singleton(new WindowCoveringService(this));
}
}
Original file line number Diff line number Diff line change
@@ -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<Void> setHoldPosition(boolean hold) throws Exception;
}
Original file line number Diff line number Diff line change
@@ -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<Boolean> 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();
}
107 changes: 8 additions & 99 deletions src/main/java/com/beowulfe/hap/accessories/WindowCovering.java
Original file line number Diff line number Diff line change
@@ -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<Integer> getCurrentPosition();

/**
* Retrieves the target position
*
* @return a future that will contain the target position as a value between 0 and 100
*/
CompletableFuture<Integer> getTargetPosition();

/**
* Retrieves the state of the position: increasing, decreasing, or stopped
*
* @return a future that will contain the current state
*/
CompletableFuture<WindowCoveringPositionState> 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<Boolean> getObstructionDetected();

@Override
default Collection<Service> 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<Void> 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<Void> 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 */
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
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;

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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
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;

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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
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;

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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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()));
}
}
}

0 comments on commit e4068ce

Please sign in to comment.