Skip to content

Commit a746a02

Browse files
authored
Merge pull request #110 from dfrommi/add-linked-service-support
add support for linked services
2 parents ec8e7cd + 0436d88 commit a746a02

File tree

5 files changed

+68
-18
lines changed

5 files changed

+68
-18
lines changed

Diff for: CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* `services` package consists of services, which grouping characteristics. e.g. `WindowCoveringService` defines mandatory and optional characteristics for a window covering service as it is defined in HAP spec.
88
* `server` package consists classes to run HomeKit server and handle communication
99
* the process is following: client, e.g. openHAB bindings, extends accessory classes, e.g. `WindowCoveringAccessory` and implements all required methods. WindowCoveringAccessory is linked already to WindowCoveringService, that in turn is link to single characteristics.
10+
* linked service support
1011

1112
## New and improved
1213

Diff for: src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java

+21-11
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
import io.github.hapjava.characteristics.Characteristic;
55
import io.github.hapjava.services.Service;
66
import io.github.hapjava.services.impl.AccessoryInformationService;
7-
import java.util.*;
7+
import java.util.ArrayList;
8+
import java.util.Collection;
9+
import java.util.Collections;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
813
import java.util.concurrent.ConcurrentHashMap;
914
import org.slf4j.Logger;
1015
import org.slf4j.LoggerFactory;
@@ -15,7 +20,7 @@ public class HomekitRegistry {
1520

1621
private final String label;
1722
private final Map<Integer, HomekitAccessory> accessories;
18-
private final Map<HomekitAccessory, List<Service>> services = new HashMap<>();
23+
private final Map<HomekitAccessory, Map<Integer, Service>> services = new HashMap<>();
1924
private final Map<HomekitAccessory, Map<Integer, Characteristic>> characteristics =
2025
new HashMap<>();
2126
private boolean isAllowUnauthenticatedRequests = false;
@@ -35,21 +40,26 @@ public synchronized void reset() {
3540
try {
3641
newServices = new ArrayList<>(2);
3742
newServices.add(new AccessoryInformationService(accessory));
38-
newServices.addAll(accessory.getServices());
43+
for (Service service : accessory.getServices()) {
44+
newServices.add(service);
45+
newServices.addAll(service.getLinkedServices());
46+
}
3947
} catch (Exception e) {
4048
logger.warn("Could not instantiate services for accessory " + accessory.getName(), e);
41-
services.put(accessory, Collections.emptyList());
49+
services.put(accessory, Collections.emptyMap());
4250
continue;
4351
}
44-
Map<Integer, Characteristic> newCharacteristics = new HashMap<>();
45-
services.put(accessory, newServices);
52+
53+
Map<Integer, Characteristic> newCharacteristicsByInterfaceId = new HashMap<>();
54+
Map<Integer, Service> newServicesByInterfaceId = new HashMap<>();
4655
for (Service service : newServices) {
47-
iid++;
56+
newServicesByInterfaceId.put(++iid, service);
4857
for (Characteristic characteristic : service.getCharacteristics()) {
49-
newCharacteristics.put(++iid, characteristic);
58+
newCharacteristicsByInterfaceId.put(++iid, characteristic);
5059
}
5160
}
52-
characteristics.put(accessory, newCharacteristics);
61+
services.put(accessory, newServicesByInterfaceId);
62+
characteristics.put(accessory, newCharacteristicsByInterfaceId);
5363
}
5464
}
5565

@@ -61,8 +71,8 @@ public Collection<HomekitAccessory> getAccessories() {
6171
return accessories.values();
6272
}
6373

64-
public List<Service> getServices(Integer aid) {
65-
return Collections.unmodifiableList(services.get(accessories.get(aid)));
74+
public Map<Integer, Service> getServices(Integer aid) {
75+
return Collections.unmodifiableMap(services.get(accessories.get(aid)));
6676
}
6777

6878
public Map<Integer, Characteristic> getCharacteristics(Integer aid) {

Diff for: src/main/java/io/github/hapjava/server/impl/json/AccessoryController.java

+29-7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.Map;
1414
import java.util.Map.Entry;
1515
import java.util.concurrent.CompletableFuture;
16+
import java.util.stream.Collectors;
1617
import javax.json.Json;
1718
import javax.json.JsonArrayBuilder;
1819
import javax.json.JsonObject;
@@ -31,12 +32,18 @@ public HttpResponse listing() throws Exception {
3132

3233
Map<Integer, List<CompletableFuture<JsonObject>>> accessoryServiceFutures = new HashMap<>();
3334
for (HomekitAccessory accessory : registry.getAccessories()) {
34-
int iid = 0;
3535
List<CompletableFuture<JsonObject>> serviceFutures = new ArrayList<>();
36-
for (Service service : registry.getServices(accessory.getId())) {
37-
serviceFutures.add(toJson(service, iid));
38-
iid += service.getCharacteristics().size() + 1;
36+
37+
Map<Integer, Service> servicesByInterfaceId = registry.getServices(accessory.getId());
38+
39+
Map<Object, Integer> iidLookup = new HashMap<>();
40+
iidLookup.putAll(swapKeyAndValue(servicesByInterfaceId));
41+
iidLookup.putAll(swapKeyAndValue(registry.getCharacteristics(accessory.getId())));
42+
43+
for (Service service : servicesByInterfaceId.values()) {
44+
serviceFutures.add(toJson(service, iidLookup));
3945
}
46+
4047
accessoryServiceFutures.put(accessory.getId(), serviceFutures);
4148
}
4249

@@ -64,16 +71,18 @@ public HttpResponse listing() throws Exception {
6471
}
6572
}
6673

67-
private CompletableFuture<JsonObject> toJson(Service service, int interfaceId) throws Exception {
74+
private CompletableFuture<JsonObject> toJson(Service service, Map<Object, Integer> iidLookup)
75+
throws Exception {
6876
String shortType =
6977
service.getType().replaceAll("^0*([0-9a-fA-F]+)-0000-1000-8000-0026BB765291$", "$1");
7078
JsonObjectBuilder builder =
71-
Json.createObjectBuilder().add("iid", ++interfaceId).add("type", shortType);
79+
Json.createObjectBuilder().add("iid", iidLookup.get(service)).add("type", shortType);
7280
List<Characteristic> characteristics = service.getCharacteristics();
7381
Collection<CompletableFuture<JsonObject>> characteristicFutures =
7482
new ArrayList<>(characteristics.size());
7583
for (Characteristic characteristic : characteristics) {
76-
characteristicFutures.add(characteristic.toJson(++interfaceId));
84+
Integer iid = iidLookup.get(characteristic);
85+
characteristicFutures.add(characteristic.toJson(iid));
7786
}
7887

7988
return CompletableFuture.allOf(
@@ -85,7 +94,20 @@ private CompletableFuture<JsonObject> toJson(Service service, int interfaceId) t
8594
.map(future -> future.join())
8695
.forEach(c -> jsonCharacteristics.add(c));
8796
builder.add("characteristics", jsonCharacteristics);
97+
98+
if (!service.getLinkedServices().isEmpty()) {
99+
JsonArrayBuilder jsonLinkedServices = Json.createArrayBuilder();
100+
service.getLinkedServices().stream()
101+
.map(iidLookup::get)
102+
.forEach(jsonLinkedServices::add);
103+
builder.add("linked", jsonLinkedServices);
104+
}
105+
88106
return builder.build();
89107
});
90108
}
109+
110+
private <K, V> Map<V, K> swapKeyAndValue(Map<K, V> map) {
111+
return map.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey));
112+
}
91113
}

Diff for: src/main/java/io/github/hapjava/services/Service.java

+7
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,11 @@ public interface Service {
2929
* ########-####-####-####-############.
3030
*/
3131
String getType();
32+
33+
/**
34+
* List of all the services to which the service links
35+
*
36+
* @return the list of linked services.
37+
*/
38+
List<Service> getLinkedServices();
3239
}

Diff for: src/main/java/io/github/hapjava/services/impl/AbstractServiceImpl.java

+10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ abstract class AbstractServiceImpl implements Service {
1212
private final Logger logger = LoggerFactory.getLogger(this.getClass());
1313
private final String type;
1414
private final List<Characteristic> characteristics = new LinkedList<>();
15+
private final List<Service> linkedServices = new LinkedList<>();
1516

1617
/** @param type unique UUID of the service according to HAP specification. */
1718
public AbstractServiceImpl(String type) {
@@ -28,7 +29,16 @@ public String getType() {
2829
return type;
2930
}
3031

32+
@Override
33+
public List<Service> getLinkedServices() {
34+
return Collections.unmodifiableList(linkedServices);
35+
}
36+
3137
public void addCharacteristic(Characteristic characteristic) {
3238
this.characteristics.add(characteristic);
3339
}
40+
41+
public void addLinkedService(Service service) {
42+
this.linkedServices.add(service);
43+
}
3444
}

0 commit comments

Comments
 (0)