Skip to content

Commit 344b653

Browse files
authored
[homekit] implement all AccessoryInformationService characteristics (openhab#17031)
mostly as metadata static characteristics fixes openhab#9595 Signed-off-by: Cody Cutrer <[email protected]>
1 parent 026c1e2 commit 344b653

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+416
-75
lines changed

bundles/org.openhab.io.homekit/README.md

+13-32
Large diffs are not rendered by default.

bundles/org.openhab.io.homekit/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<dependency>
2323
<groupId>io.github.hap-java</groupId>
2424
<artifactId>hap</artifactId>
25-
<version>2.0.5</version>
25+
<version>2.0.6</version>
2626
<scope>compile</scope>
2727
</dependency>
2828
<dependency>

bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitChangeListener.java

+1-16
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
*/
1313
package org.openhab.io.homekit.internal;
1414

15-
import java.lang.reflect.InvocationTargetException;
1615
import java.time.Clock;
1716
import java.time.Duration;
1817
import java.util.ArrayList;
@@ -48,7 +47,6 @@
4847
import org.slf4j.LoggerFactory;
4948

5049
import io.github.hapjava.accessories.HomekitAccessory;
51-
import io.github.hapjava.characteristics.impl.common.NameCharacteristic;
5250
import io.github.hapjava.server.impl.HomekitRoot;
5351

5452
/**
@@ -476,20 +474,7 @@ private void createRootAccessories(Item item) {
476474
try {
477475
final AbstractHomekitAccessoryImpl additionalAccessory = HomekitAccessoryFactory
478476
.create(additionalTaggedItem, metadataRegistry, updater, settings);
479-
// Secondary accessories that don't explicitly specify a name will implicitly
480-
// get a name characteristic based on the item's name
481-
if (!additionalAccessory.getCharacteristic(HomekitCharacteristicType.NAME).isPresent()) {
482-
try {
483-
additionalAccessory.addCharacteristic(
484-
new NameCharacteristic(() -> additionalAccessory.getName()));
485-
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
486-
// This should never happen; all services should support NameCharacteristic as an
487-
// optional Characteristic.
488-
// If HAP-Java defined a service that doesn't support
489-
// addOptionalCharacteristic(NameCharacteristic), then it's a bug there, and we're
490-
// just going to ignore the exception here.
491-
}
492-
}
477+
additionalAccessory.promoteNameCharacteristic();
493478
accessory.getServices().add(additionalAccessory.getPrimaryService());
494479
} catch (HomekitException e) {
495480
logger.warn("Cannot create additional accessory {}", additionalTaggedItem);

bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCharacteristicType.java

+8
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@ public enum HomekitCharacteristicType {
2727
* It is illegal to have a characteristic type also be a device type
2828
*/
2929
EMPTY("Empty"), // used in case only accessory type but no characteristic provided
30+
3031
NAME("Name"),
32+
MANUFACTURER("Manufacturer"),
33+
MODEL("Model"),
34+
SERIAL_NUMBER("SerialNumber"),
35+
FIRMWARE_REVISION("FirmwareRevision"),
36+
HARDWARE_REVISION("HardwareRevision"),
37+
IDENTIFY("Identify"),
38+
3139
BATTERY_LOW_STATUS("BatteryLowStatus"),
3240
ACTIVE_STATUS("ActiveStatus"),
3341
ISCONFIGURED("IsConfigured"),

bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitAccessoryImpl.java

+104-6
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,16 @@
4545
import io.github.hapjava.characteristics.Characteristic;
4646
import io.github.hapjava.characteristics.CharacteristicEnum;
4747
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
48+
import io.github.hapjava.characteristics.impl.accessoryinformation.FirmwareRevisionCharacteristic;
49+
import io.github.hapjava.characteristics.impl.accessoryinformation.HardwareRevisionCharacteristic;
50+
import io.github.hapjava.characteristics.impl.accessoryinformation.IdentifyCharacteristic;
51+
import io.github.hapjava.characteristics.impl.accessoryinformation.ManufacturerCharacteristic;
52+
import io.github.hapjava.characteristics.impl.accessoryinformation.ModelCharacteristic;
53+
import io.github.hapjava.characteristics.impl.accessoryinformation.SerialNumberCharacteristic;
4854
import io.github.hapjava.characteristics.impl.base.BaseCharacteristic;
55+
import io.github.hapjava.characteristics.impl.common.NameCharacteristic;
4956
import io.github.hapjava.services.Service;
57+
import io.github.hapjava.services.impl.AccessoryInformationService;
5058

5159
/**
5260
* Abstract class for Homekit Accessory implementations, this provides the
@@ -62,6 +70,7 @@ public abstract class AbstractHomekitAccessoryImpl implements HomekitAccessory {
6270
private final HomekitSettings settings;
6371
private final List<Service> services;
6472
private final Map<Class<? extends Characteristic>, Characteristic> rawCharacteristics;
73+
private boolean isLinkedService = false;
6574

6675
public AbstractHomekitAccessoryImpl(HomekitTaggedItem accessory, List<HomekitTaggedItem> characteristics,
6776
HomekitAccessoryUpdater updater, HomekitSettings settings) {
@@ -88,6 +97,52 @@ public AbstractHomekitAccessoryImpl(HomekitTaggedItem accessory, List<HomekitTag
8897
* @throws HomekitException
8998
*/
9099
public void init() throws HomekitException {
100+
// initialize the AccessoryInformation Service with defaults if not specified
101+
if (!rawCharacteristics.containsKey(NameCharacteristic.class)) {
102+
rawCharacteristics.put(NameCharacteristic.class, new NameCharacteristic(() -> {
103+
return CompletableFuture.completedFuture(accessory.getItem().getLabel());
104+
}));
105+
}
106+
107+
if (!isLinkedService()) {
108+
if (!rawCharacteristics.containsKey(IdentifyCharacteristic.class)) {
109+
rawCharacteristics.put(IdentifyCharacteristic.class, new IdentifyCharacteristic(v -> {
110+
}));
111+
}
112+
if (!rawCharacteristics.containsKey(ManufacturerCharacteristic.class)) {
113+
rawCharacteristics.put(ManufacturerCharacteristic.class, new ManufacturerCharacteristic(() -> {
114+
return CompletableFuture.completedFuture("none");
115+
}));
116+
}
117+
if (!rawCharacteristics.containsKey(ModelCharacteristic.class)) {
118+
rawCharacteristics.put(ModelCharacteristic.class, new ModelCharacteristic(() -> {
119+
return CompletableFuture.completedFuture("none");
120+
}));
121+
}
122+
if (!rawCharacteristics.containsKey(SerialNumberCharacteristic.class)) {
123+
rawCharacteristics.put(SerialNumberCharacteristic.class, new SerialNumberCharacteristic(() -> {
124+
return CompletableFuture.completedFuture(accessory.getItem().getName());
125+
}));
126+
}
127+
if (!rawCharacteristics.containsKey(FirmwareRevisionCharacteristic.class)) {
128+
rawCharacteristics.put(FirmwareRevisionCharacteristic.class, new FirmwareRevisionCharacteristic(() -> {
129+
return CompletableFuture.completedFuture("none");
130+
}));
131+
}
132+
133+
var service = new AccessoryInformationService(getCharacteristic(IdentifyCharacteristic.class).get(),
134+
getCharacteristic(ManufacturerCharacteristic.class).get(),
135+
getCharacteristic(ModelCharacteristic.class).get(),
136+
getCharacteristic(NameCharacteristic.class).get(),
137+
getCharacteristic(SerialNumberCharacteristic.class).get(),
138+
getCharacteristic(FirmwareRevisionCharacteristic.class).get());
139+
140+
getCharacteristic(HardwareRevisionCharacteristic.class)
141+
.ifPresent(c -> service.addOptionalCharacteristic(c));
142+
143+
// make sure this is the first service
144+
services.add(0, service);
145+
}
91146
}
92147

93148
/**
@@ -99,6 +154,20 @@ public boolean isLinkable(HomekitAccessory parentAccessory) {
99154
return false;
100155
}
101156

157+
/**
158+
* Sets if this accessory is being used as a linked service.
159+
*/
160+
public void setIsLinkedService(boolean value) {
161+
isLinkedService = value;
162+
}
163+
164+
/**
165+
* @return If this accessory is being used as a linked service.
166+
*/
167+
public boolean isLinkedService() {
168+
return isLinkedService;
169+
}
170+
102171
/**
103172
* @return If this accessory is only valid as a linked service, not as a standalone accessory.
104173
*/
@@ -118,32 +187,36 @@ public int getId() {
118187

119188
@Override
120189
public CompletableFuture<String> getName() {
121-
return CompletableFuture.completedFuture(accessory.getItem().getLabel());
190+
return getCharacteristic(NameCharacteristic.class).get().getValue();
122191
}
123192

124193
@Override
125194
public CompletableFuture<String> getManufacturer() {
126-
return CompletableFuture.completedFuture("none");
195+
return getCharacteristic(ManufacturerCharacteristic.class).get().getValue();
127196
}
128197

129198
@Override
130199
public CompletableFuture<String> getModel() {
131-
return CompletableFuture.completedFuture("none");
200+
return getCharacteristic(ModelCharacteristic.class).get().getValue();
132201
}
133202

134203
@Override
135204
public CompletableFuture<String> getSerialNumber() {
136-
return CompletableFuture.completedFuture(accessory.getItem().getName());
205+
return getCharacteristic(SerialNumberCharacteristic.class).get().getValue();
137206
}
138207

139208
@Override
140209
public CompletableFuture<String> getFirmwareRevision() {
141-
return CompletableFuture.completedFuture("none");
210+
return getCharacteristic(FirmwareRevisionCharacteristic.class).get().getValue();
142211
}
143212

144213
@Override
145214
public void identify() {
146-
// We're not going to support this for now
215+
try {
216+
getCharacteristic(IdentifyCharacteristic.class).get().setValue(true);
217+
} catch (Exception e) {
218+
// ignore
219+
}
147220
}
148221

149222
public HomekitTaggedItem getRootAccessory() {
@@ -356,6 +429,31 @@ public void addCharacteristic(Characteristic characteristic)
356429
}
357430
}
358431

432+
/**
433+
* Takes the NameCharacteristic that normally exists on the AccessoryInformationService,
434+
* and puts it on the primary service.
435+
*/
436+
public void promoteNameCharacteristic() {
437+
var characteristic = getCharacteristic(NameCharacteristic.class);
438+
if (!characteristic.isPresent()) {
439+
return;
440+
}
441+
442+
var service = getPrimaryService();
443+
if (service != null) {
444+
try {
445+
// find the corresponding add method at service and call it.
446+
service.getClass().getMethod("addOptionalCharacteristic", NameCharacteristic.class).invoke(service,
447+
characteristic.get());
448+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
449+
// This should never happen; all services should support NameCharacteristic as an optional
450+
// Characteristic.
451+
// If HAP-Java defined a service that doesn't support addOptionalCharacteristic(NameCharacteristic),
452+
// Then it's a bug there, and we're just going to ignore the exception here.
453+
}
454+
}
455+
}
456+
359457
@NonNullByDefault
360458
public <T> Optional<T> getCharacteristic(Class<? extends T> klazz) {
361459
return Optional.ofNullable((T) rawCharacteristics.get(klazz));

bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java

+3-11
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
import org.slf4j.LoggerFactory;
5353

5454
import io.github.hapjava.characteristics.Characteristic;
55-
import io.github.hapjava.characteristics.impl.common.NameCharacteristic;
5655

5756
/**
5857
* Creates a HomekitAccessory for a given HomekitTaggedItem.
@@ -211,13 +210,14 @@ private static AbstractHomekitAccessoryImpl create(HomekitTaggedItem taggedItem,
211210
taggedItem.getName());
212211
throw new HomekitException("Circular accessory references");
213212
}
214-
ancestorServices.add(taggedItem);
215213
accessoryImpl = accessoryImplClass.getConstructor(HomekitTaggedItem.class, List.class,
216214
HomekitAccessoryUpdater.class, HomekitSettings.class)
217215
.newInstance(taggedItem, foundCharacteristics, updater, settings);
218216
addOptionalCharacteristics(taggedItem, accessoryImpl, metadataRegistry);
219217
addOptionalMetadataCharacteristics(taggedItem, accessoryImpl);
218+
accessoryImpl.setIsLinkedService(!ancestorServices.isEmpty());
220219
accessoryImpl.init();
220+
ancestorServices.add(taggedItem);
221221
addLinkedServices(taggedItem, accessoryImpl, metadataRegistry, updater, settings, ancestorServices);
222222
return accessoryImpl;
223223
} else {
@@ -467,15 +467,7 @@ private static void addLinkedServices(HomekitTaggedItem taggedItem, AbstractHome
467467
final var itemProxy = new HomekitOHItemProxy(groupMember);
468468
final var subTaggedItem = new HomekitTaggedItem(itemProxy, accessoryType, itemConfiguration);
469469
final var subAccessory = create(subTaggedItem, metadataRegistry, updater, settings, ancestorServices);
470-
471-
try {
472-
subAccessory.addCharacteristic(new NameCharacteristic(() -> subAccessory.getName()));
473-
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
474-
// This should never happen; all services should support NameCharacteristic as an optional
475-
// Characteristic.
476-
// If HAP-Java defined a service that doesn't support addOptionalCharacteristic(NameCharacteristic),
477-
// Then it's a bug there, and we're just going to ignore the exception here.
478-
}
470+
subAccessory.promoteNameCharacteristic();
479471

480472
if (subAccessory.isLinkable(accessory)) {
481473
accessory.getPrimaryService().addLinkedService(subAccessory.getPrimaryService());

bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAirQualitySensorImpl.java

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.concurrent.CompletableFuture;
2020

2121
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
22+
import org.openhab.io.homekit.internal.HomekitException;
2223
import org.openhab.io.homekit.internal.HomekitSettings;
2324
import org.openhab.io.homekit.internal.HomekitTaggedItem;
2425

@@ -39,6 +40,11 @@ public HomekitAirQualitySensorImpl(HomekitTaggedItem taggedItem, List<HomekitTag
3940
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
4041
super(taggedItem, mandatoryCharacteristics, updater, settings);
4142
qualityStateMapping = createMapping(AIR_QUALITY, AirQualityEnum.class);
43+
}
44+
45+
@Override
46+
public void init() throws HomekitException {
47+
super.init();
4248
getServices().add(new AirQualityService(this));
4349
}
4450

bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitBasicFanImpl.java

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.eclipse.jdt.annotation.NonNullByDefault;
2121
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
22+
import org.openhab.io.homekit.internal.HomekitException;
2223
import org.openhab.io.homekit.internal.HomekitSettings;
2324
import org.openhab.io.homekit.internal.HomekitTaggedItem;
2425

@@ -39,6 +40,11 @@ public HomekitBasicFanImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem>
3940
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
4041
super(taggedItem, mandatoryCharacteristics, updater, settings);
4142
onReader = createBooleanReader(ON_STATE);
43+
}
44+
45+
@Override
46+
public void init() throws HomekitException {
47+
super.init();
4248
this.getServices().add(new BasicFanService(this));
4349
}
4450

bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitBatteryImpl.java

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.openhab.core.library.types.DecimalType;
2525
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
2626
import org.openhab.io.homekit.internal.HomekitCharacteristicType;
27+
import org.openhab.io.homekit.internal.HomekitException;
2728
import org.openhab.io.homekit.internal.HomekitSettings;
2829
import org.openhab.io.homekit.internal.HomekitTaggedItem;
2930

@@ -56,6 +57,11 @@ public HomekitBatteryImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem>
5657
if (isChargeable) {
5758
chargingBatteryReader = createBooleanReader(BATTERY_CHARGING_STATE);
5859
}
60+
}
61+
62+
@Override
63+
public void init() throws HomekitException {
64+
super.init();
5965
getServices().add(new BatteryService(this));
6066
}
6167

bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCarbonDioxideSensorImpl.java

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.concurrent.CompletableFuture;
2020

2121
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
22+
import org.openhab.io.homekit.internal.HomekitException;
2223
import org.openhab.io.homekit.internal.HomekitSettings;
2324
import org.openhab.io.homekit.internal.HomekitTaggedItem;
2425

@@ -40,6 +41,11 @@ public HomekitCarbonDioxideSensorImpl(HomekitTaggedItem taggedItem,
4041
throws IncompleteAccessoryException {
4142
super(taggedItem, mandatoryCharacteristics, updater, settings);
4243
mapping = createMapping(CARBON_DIOXIDE_DETECTED_STATE, CarbonDioxideDetectedEnum.class);
44+
}
45+
46+
@Override
47+
public void init() throws HomekitException {
48+
super.init();
4349
getServices().add(new CarbonDioxideSensorService(this));
4450
}
4551

bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCarbonMonoxideSensorImpl.java

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.concurrent.CompletableFuture;
2020

2121
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
22+
import org.openhab.io.homekit.internal.HomekitException;
2223
import org.openhab.io.homekit.internal.HomekitSettings;
2324
import org.openhab.io.homekit.internal.HomekitTaggedItem;
2425

@@ -40,6 +41,11 @@ public HomekitCarbonMonoxideSensorImpl(HomekitTaggedItem taggedItem,
4041
throws IncompleteAccessoryException {
4142
super(taggedItem, mandatoryCharacteristics, updater, settings);
4243
mapping = createMapping(CARBON_MONOXIDE_DETECTED_STATE, CarbonMonoxideDetectedEnum.class);
44+
}
45+
46+
@Override
47+
public void init() throws HomekitException {
48+
super.init();
4349
getServices().add(new CarbonMonoxideSensorService(this));
4450
}
4551

0 commit comments

Comments
 (0)