Skip to content

Commit ee9cf3e

Browse files
authored
Merge pull request #124 from gjvanderheiden/FilterMaintenanceAccessory
Add support for FilterMaintenanceAccessory
2 parents 75b2349 + 4db045b commit ee9cf3e

12 files changed

+267
-14
lines changed

CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
* Valid values are supported for enum characteristics instead of min and max values
1515
* Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#120](https://github.com/hap-java/HAP-Java/pull/120)
16+
* Support for FilterMaintenance. Can be used as a linked service for an Air Purifier [#124](https://github.com/hap-java/HAP-Java/pull/124)
1617

1718
# HAP-Java 1.1.5
1819

@@ -27,6 +28,7 @@
2728
* Fix various spec violations and optimize communications to improve performance [#65](https://github.com/hap-java/HAP-Java/pull/65)
2829
* Fix a pairing issue in which HAP-Java could listen on a different interface than that which it advertises [#67](https://github.com/hap-java/HAP-Java/pull/67)
2930
* Allow window covering to be used without optional characteristics. The inclusion of `HoldPositionCharacteristic` did terrible things, and we're still not sure why. Addressed [#56](https://github.com/hap-java/HAP-Java/pull/56)
31+
* Air Purifier didn't support rotation speed characteristics. [#124](https://github.com/hap-java/HAP-Java/pull/124)
3032

3133
## New and improved
3234

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ After that, check out the [Sample](https://github.com/hap-java/HAP-Java/tree/sam
2323
Supported HomeKit Accessories
2424
=========
2525

26-
Current implementation fully supports 37 HomeKit accessory/services.
26+
Current implementation fully supports 38 HomeKit accessory/services.
2727

2828
| HomeKit Accessory & Service type | Supported by Java-HAP |
2929
|--------------------|--------------------|
@@ -41,7 +41,7 @@ Current implementation fully supports 37 HomeKit accessory/services.
4141
| Doorbell | :white_check_mark: |
4242
| Fan | :white_check_mark: |
4343
| Faucet | :white_check_mark: |
44-
| Filter Maintenance | :x: |
44+
| Filter Maintenance | :white_check_mark: |
4545
| Garage Door Opener | :white_check_mark: |
4646
| HAP Protocol Information | :white_check_mark: |
4747
| Heater Cooler | :white_check_mark: |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.github.hapjava.accessories;
2+
3+
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
4+
import io.github.hapjava.characteristics.impl.filtermaintenance.FilterChangeIndicationEnum;
5+
import io.github.hapjava.services.Service;
6+
import io.github.hapjava.services.impl.FilterMaintenanceService;
7+
import java.util.Collection;
8+
import java.util.Collections;
9+
import java.util.concurrent.CompletableFuture;
10+
11+
/**
12+
* A Filter maintenance with mandatory characteristics.
13+
*
14+
* <p>The HomeKit app doesn't support a separate FilterMaintenance, but as a linked service to
15+
* AirPurifier.
16+
*/
17+
public interface FilterMaintenanceAccessory extends HomekitAccessory {
18+
19+
/**
20+
* The filter change indictaion. It's either yes or no.
21+
*
22+
* @return FilterChangeIndicationEnum
23+
*/
24+
CompletableFuture<FilterChangeIndicationEnum> getFilterChangeIndication();
25+
26+
/**
27+
* Subscribes to changes in the filter change indication.
28+
*
29+
* @param callback the function to call when the state changes.
30+
*/
31+
void subscribeFilterChangeIndication(HomekitCharacteristicChangeCallback callback);
32+
33+
/** Unsubscribes from changes in the filter change indication. */
34+
void unsubscribeFilterChangeIndication();
35+
36+
@Override
37+
default Collection<Service> getServices() {
38+
return Collections.singleton(new FilterMaintenanceService(this));
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.github.hapjava.accessories.optionalcharacteristic;
2+
3+
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
4+
import java.util.concurrent.CompletableFuture;
5+
6+
/** Accessory with filter level characteristics */
7+
public interface AccessoryWithFilterLifeLevel {
8+
9+
/**
10+
* what's the filter life level, percentage wise
11+
*
12+
* @return filter life level
13+
*/
14+
CompletableFuture<Double> getFilterLifeLevel();
15+
16+
/**
17+
* Subscribes to changes in the filter life level.
18+
*
19+
* @param callback the function to call when the level changes.
20+
*/
21+
void subscribeFilterLifeLevel(HomekitCharacteristicChangeCallback callback);
22+
23+
/** Unsubscribes from changes in the current filter life level. */
24+
void unsubscribeFilterLifeLevel();
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.github.hapjava.accessories.optionalcharacteristic;
2+
3+
import java.util.concurrent.CompletableFuture;
4+
5+
/** Accessory with filter reset characteristics */
6+
public interface AccessoryWithResetFilterIndication {
7+
8+
/**
9+
* Request to reset the filter level
10+
*
11+
* @param indication always 1, by HomeKit protocol. (to be ignored)
12+
* @return a future that completes when the change is made
13+
* @throws Exception when the change cannot be made
14+
*/
15+
CompletableFuture<Void> resetFilterIndication(Integer indication) throws Exception;
16+
}

src/main/java/io/github/hapjava/characteristics/impl/base/IntegerCharacteristic.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,17 @@ protected CompletableFuture<JsonObjectBuilder> makeBuilder(int iid) {
6262
return super.makeBuilder(iid)
6363
.thenApply(
6464
builder -> {
65-
return builder
66-
.add("minValue", minValue)
67-
.add("maxValue", maxValue)
68-
.add("minStep", 1)
69-
.add("unit", unit);
65+
builder.add("minValue", minValue).add("maxValue", maxValue).add("minStep", 1);
66+
if (this.unit != null) {
67+
builder.add("unit", unit);
68+
}
69+
return builder;
7070
});
7171
}
7272

7373
@Override
7474
protected CompletableFuture<Integer> getValue() {
75-
return getter.map(integerGetter -> integerGetter.get()).get();
75+
return getter.map(integerGetter -> integerGetter.get()).orElse(null);
7676
}
7777

7878
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.github.hapjava.characteristics.impl.filtermaintenance;
2+
3+
import io.github.hapjava.characteristics.EventableCharacteristic;
4+
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
5+
import io.github.hapjava.characteristics.impl.base.EnumCharacteristic;
6+
import java.util.Optional;
7+
import java.util.concurrent.CompletableFuture;
8+
import java.util.function.Consumer;
9+
import java.util.function.Supplier;
10+
11+
public class FilterChangeIndicationCharacteristic
12+
extends EnumCharacteristic<FilterChangeIndicationEnum> implements EventableCharacteristic {
13+
14+
public FilterChangeIndicationCharacteristic(
15+
Supplier<CompletableFuture<FilterChangeIndicationEnum>> getter,
16+
Consumer<HomekitCharacteristicChangeCallback> subscriber,
17+
Runnable unsubscriber) {
18+
super(
19+
"000000AC-0000-1000-8000-0026BB765291",
20+
"filter change indication",
21+
FilterChangeIndicationEnum.values(),
22+
Optional.of(getter),
23+
Optional.empty(),
24+
Optional.of(subscriber),
25+
Optional.of(unsubscriber));
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.github.hapjava.characteristics.impl.filtermaintenance;
2+
3+
import io.github.hapjava.characteristics.CharacteristicEnum;
4+
import java.util.Arrays;
5+
import java.util.Map;
6+
import java.util.stream.Collectors;
7+
8+
public enum FilterChangeIndicationEnum implements CharacteristicEnum {
9+
NO_CHANGE_NEEDED(0),
10+
CHANGE_NEEDED(1);
11+
12+
private static final Map<Integer, FilterChangeIndicationEnum> reverse;
13+
14+
static {
15+
reverse =
16+
Arrays.stream(FilterChangeIndicationEnum.values())
17+
.collect(Collectors.toMap(FilterChangeIndicationEnum::getCode, t -> t));
18+
}
19+
20+
public static FilterChangeIndicationEnum fromCode(Integer code) {
21+
return reverse.get(code);
22+
}
23+
24+
private final int code;
25+
26+
FilterChangeIndicationEnum(int code) {
27+
this.code = code;
28+
}
29+
30+
@Override
31+
public int getCode() {
32+
return code;
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package io.github.hapjava.characteristics.impl.filtermaintenance;
2+
3+
import io.github.hapjava.characteristics.EventableCharacteristic;
4+
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
5+
import io.github.hapjava.characteristics.impl.base.FloatCharacteristic;
6+
import java.util.Optional;
7+
import java.util.concurrent.CompletableFuture;
8+
import java.util.function.Consumer;
9+
import java.util.function.Supplier;
10+
11+
public class FilterLifeLevelCharacteristic extends FloatCharacteristic
12+
implements EventableCharacteristic {
13+
14+
public FilterLifeLevelCharacteristic(
15+
Supplier<CompletableFuture<Double>> getter,
16+
Consumer<HomekitCharacteristicChangeCallback> subscriber,
17+
Runnable unsubscriber) {
18+
super(
19+
"000000AB-0000-1000-8000-0026BB765291",
20+
"Filter Life Level",
21+
0,
22+
100,
23+
1,
24+
"%",
25+
Optional.of(getter),
26+
Optional.empty(),
27+
Optional.of(subscriber),
28+
Optional.of(unsubscriber));
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.github.hapjava.characteristics.impl.filtermaintenance;
2+
3+
import io.github.hapjava.characteristics.ExceptionalConsumer;
4+
import io.github.hapjava.characteristics.impl.base.IntegerCharacteristic;
5+
import java.util.Optional;
6+
7+
public class ResetFilterIndicationCharacteristic extends IntegerCharacteristic {
8+
9+
public ResetFilterIndicationCharacteristic(ExceptionalConsumer<Integer> setter) {
10+
super(
11+
"000000AD-0000-1000-8000-0026BB765291",
12+
"Reset Filter Indication",
13+
1,
14+
1,
15+
null,
16+
Optional.empty(),
17+
Optional.of(setter),
18+
Optional.empty(),
19+
Optional.empty());
20+
}
21+
}

src/main/java/io/github/hapjava/services/impl/AirPurifierService.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.github.hapjava.accessories.AirPurifierAccessory;
44
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName;
55
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithPhysicalControlsLock;
6+
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithRotationSpeed;
67
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithSwingMode;
78
import io.github.hapjava.characteristics.impl.airpurifier.CurrentAirPurifierCharacteristic;
89
import io.github.hapjava.characteristics.impl.airpurifier.TargetAirPurifierStateCharacteristic;
@@ -53,13 +54,13 @@ public AirPurifierService(AirPurifierAccessory accessory) {
5354
((AccessoryWithSwingMode) accessory)::subscribeSwingMode,
5455
((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode));
5556
}
56-
if (accessory instanceof AccessoryWithSwingMode) {
57+
if (accessory instanceof AccessoryWithRotationSpeed) {
5758
addOptionalCharacteristic(
58-
new SwingModeCharacteristic(
59-
((AccessoryWithSwingMode) accessory)::getSwingMode,
60-
((AccessoryWithSwingMode) accessory)::setSwingMode,
61-
((AccessoryWithSwingMode) accessory)::subscribeSwingMode,
62-
((AccessoryWithSwingMode) accessory)::unsubscribeSwingMode));
59+
new RotationSpeedCharacteristic(
60+
((AccessoryWithRotationSpeed) accessory)::getRotationSpeed,
61+
((AccessoryWithRotationSpeed) accessory)::setRotationSpeed,
62+
((AccessoryWithRotationSpeed) accessory)::subscribeRotationSpeed,
63+
((AccessoryWithRotationSpeed) accessory)::unsubscribeRotationSpeed));
6364
}
6465
if (accessory instanceof AccessoryWithPhysicalControlsLock) {
6566
addOptionalCharacteristic(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.github.hapjava.services.impl;
2+
3+
import io.github.hapjava.accessories.FilterMaintenanceAccessory;
4+
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithFilterLifeLevel;
5+
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithName;
6+
import io.github.hapjava.accessories.optionalcharacteristic.AccessoryWithResetFilterIndication;
7+
import io.github.hapjava.characteristics.impl.common.NameCharacteristic;
8+
import io.github.hapjava.characteristics.impl.filtermaintenance.FilterChangeIndicationCharacteristic;
9+
import io.github.hapjava.characteristics.impl.filtermaintenance.FilterLifeLevelCharacteristic;
10+
import io.github.hapjava.characteristics.impl.filtermaintenance.ResetFilterIndicationCharacteristic;
11+
12+
/** This service describes a filter maintenance. */
13+
public class FilterMaintenanceService extends AbstractServiceImpl {
14+
15+
public FilterMaintenanceService(FilterChangeIndicationCharacteristic filerChange) {
16+
super("000000BA-0000-1000-8000-0026BB765291");
17+
addCharacteristic(filerChange);
18+
}
19+
20+
public FilterMaintenanceService(FilterMaintenanceAccessory accessory) {
21+
this(
22+
new FilterChangeIndicationCharacteristic(
23+
accessory::getFilterChangeIndication,
24+
accessory::subscribeFilterChangeIndication,
25+
accessory::unsubscribeFilterChangeIndication));
26+
27+
if (accessory instanceof AccessoryWithName) {
28+
addOptionalCharacteristic(new NameCharacteristic(((AccessoryWithName) accessory)::getName));
29+
}
30+
31+
if (accessory instanceof AccessoryWithFilterLifeLevel) {
32+
addOptionalCharacteristic(
33+
new FilterLifeLevelCharacteristic(
34+
((AccessoryWithFilterLifeLevel) accessory)::getFilterLifeLevel,
35+
((AccessoryWithFilterLifeLevel) accessory)::subscribeFilterLifeLevel,
36+
((AccessoryWithFilterLifeLevel) accessory)::unsubscribeFilterLifeLevel));
37+
}
38+
39+
if (accessory instanceof AccessoryWithResetFilterIndication) {
40+
addOptionalCharacteristic(
41+
new ResetFilterIndicationCharacteristic(
42+
((AccessoryWithResetFilterIndication) accessory)::resetFilterIndication));
43+
}
44+
}
45+
46+
public void addOptionalCharacteristic(NameCharacteristic name) {
47+
addCharacteristic(name);
48+
}
49+
50+
public void addOptionalCharacteristic(FilterLifeLevelCharacteristic filterLifeLevel) {
51+
addCharacteristic(filterLifeLevel);
52+
}
53+
54+
public void addOptionalCharacteristic(ResetFilterIndicationCharacteristic resetFilterIndication) {
55+
addCharacteristic(resetFilterIndication);
56+
}
57+
}

0 commit comments

Comments
 (0)