Skip to content

Commit 3d271dc

Browse files
authored
Merge pull request #172 from ccutrer/maintain-subscriptions
2 parents de16264 + a781e85 commit 3d271dc

File tree

6 files changed

+216
-79
lines changed

6 files changed

+216
-79
lines changed

src/main/java/io/github/hapjava/characteristics/Characteristic.java

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
* @author Andy Lintner
1919
*/
2020
public interface Characteristic {
21+
/** @return The UUID type for this characteristic. */
22+
String getType();
2123

2224
/**
2325
* Adds an attribute to the passed JsonObjectBuilder named "value" with the current value of the

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

+6
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ public BaseCharacteristic(
7070
this.unsubscriber = unsubscriber;
7171
}
7272

73+
@Override
74+
/** {@inheritDoc} */
75+
public String getType() {
76+
return type;
77+
}
78+
7379
@Override
7480
/** {@inheritDoc} */
7581
public final CompletableFuture<JsonObject> toJson(int iid) {

src/main/java/io/github/hapjava/server/impl/HomekitRegistry.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.github.hapjava.accessories.HomekitAccessory;
44
import io.github.hapjava.characteristics.Characteristic;
5+
import io.github.hapjava.server.impl.connections.SubscriptionManager;
56
import io.github.hapjava.services.Service;
67
import io.github.hapjava.services.impl.AccessoryInformationService;
78
import java.util.ArrayList;
@@ -19,14 +20,16 @@ public class HomekitRegistry {
1920
private static final Logger logger = LoggerFactory.getLogger(HomekitRegistry.class);
2021

2122
private final String label;
23+
private final SubscriptionManager subscriptions;
2224
private final Map<Integer, HomekitAccessory> accessories;
2325
private final Map<HomekitAccessory, Map<Integer, Service>> services = new HashMap<>();
2426
private final Map<HomekitAccessory, Map<Integer, Characteristic>> characteristics =
2527
new HashMap<>();
2628
private boolean isAllowUnauthenticatedRequests = false;
2729

28-
public HomekitRegistry(String label) {
30+
public HomekitRegistry(String label, SubscriptionManager subscriptions) {
2931
this.label = label;
32+
this.subscriptions = subscriptions;
3033
this.accessories = new ConcurrentHashMap<>();
3134
reset();
3235
}
@@ -61,6 +64,7 @@ public synchronized void reset() {
6164
services.put(accessory, newServicesByInterfaceId);
6265
characteristics.put(accessory, newCharacteristicsByInterfaceId);
6366
}
67+
subscriptions.resync(this);
6468
}
6569

6670
public String getLabel() {
@@ -87,8 +91,8 @@ public void add(HomekitAccessory accessory) {
8791
accessories.put(accessory.getId(), accessory);
8892
}
8993

90-
public void remove(HomekitAccessory accessory) {
91-
accessories.remove(accessory.getId());
94+
public boolean remove(HomekitAccessory accessory) {
95+
return accessories.remove(accessory.getId()) != null;
9296
}
9397

9498
public boolean isAllowUnauthenticatedRequests() {

src/main/java/io/github/hapjava/server/impl/HomekitRoot.java

+37-15
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public class HomekitRoot {
3636
private final SubscriptionManager subscriptions = new SubscriptionManager();
3737
private boolean started = false;
3838
private int configurationIndex = 1;
39+
private int nestedBatches = 0;
40+
private boolean madeChanges = false;
3941

4042
HomekitRoot(
4143
String label, HomekitWebHandler webHandler, InetAddress host, HomekitAuthInfo authInfo)
@@ -65,7 +67,7 @@ public class HomekitRoot {
6567
this.authInfo = authInfo;
6668
this.label = label;
6769
this.category = category;
68-
this.registry = new HomekitRegistry(label);
70+
this.registry = new HomekitRegistry(label, subscriptions);
6971
}
7072

7173
HomekitRoot(
@@ -83,11 +85,27 @@ public class HomekitRoot {
8385
this(
8486
label, DEFAULT_ACCESSORY_CATEGORY, webHandler, authInfo, new JmdnsHomekitAdvertiser(jmdns));
8587
}
88+
8689
/**
87-
* Add an accessory to be handled and advertised by this root. Any existing HomeKit connections
88-
* will be terminated to allow the clients to reconnect and see the updated accessory list. When
89-
* using this for a bridge, the ID of the accessory must be greater than 1, as that ID is reserved
90-
* for the Bridge itself.
90+
* Begin a batch update of accessories.
91+
*
92+
* <p>After calling this, you can call addAccessory() and removeAccessory() multiple times without
93+
* causing HAP-Java to re-publishing the metadata to HomeKit. You'll need to call
94+
* completeUpdateBatch in order to publish all accumulated changes.
95+
*/
96+
public synchronized void batchUpdate() {
97+
if (this.nestedBatches == 0) madeChanges = false;
98+
++this.nestedBatches;
99+
}
100+
101+
/** Publish accumulated accessory changes since batchUpdate() was called. */
102+
public synchronized void completeUpdateBatch() {
103+
if (--this.nestedBatches == 0 && madeChanges) registry.reset();
104+
}
105+
106+
/**
107+
* Add an accessory to be handled and advertised by this root. When using this for a bridge, the
108+
* ID of the accessory must be greater than 1, as that ID is reserved for the Bridge itself.
91109
*
92110
* @param accessory to advertise and handle.
93111
*/
@@ -110,25 +128,28 @@ void addAccessorySkipRangeCheck(HomekitAccessory accessory) {
110128
if (logger.isTraceEnabled()) {
111129
accessory.getName().thenAccept(name -> logger.trace("Added accessory {}", name));
112130
}
113-
if (started) {
131+
madeChanges = true;
132+
if (started && nestedBatches == 0) {
114133
registry.reset();
115134
}
116135
}
117136

118137
/**
119-
* Removes an accessory from being handled or advertised by this root. Any existing HomeKit
120-
* connections will be terminated to allow the clients to reconnect and see the updated accessory
121-
* list.
138+
* Removes an accessory from being handled or advertised by this root.
122139
*
123140
* @param accessory accessory to cease advertising and handling
124141
*/
125142
public void removeAccessory(HomekitAccessory accessory) {
126-
this.registry.remove(accessory);
127-
if (logger.isTraceEnabled()) {
128-
accessory.getName().thenAccept(name -> logger.trace("Removed accessory {}", name));
129-
}
130-
if (started) {
131-
registry.reset();
143+
if (this.registry.remove(accessory)) {
144+
if (logger.isTraceEnabled()) {
145+
accessory.getName().thenAccept(name -> logger.trace("Removed accessory {}", name));
146+
}
147+
madeChanges = true;
148+
if (started && nestedBatches == 0) {
149+
registry.reset();
150+
}
151+
} else {
152+
accessory.getName().thenAccept(name -> logger.warn("Could not remove accessory {}", name));
132153
}
133154
}
134155

@@ -140,6 +161,7 @@ public void removeAccessory(HomekitAccessory accessory) {
140161
*/
141162
public void start() {
142163
started = true;
164+
madeChanges = false;
143165
registry.reset();
144166
webHandler
145167
.start(

0 commit comments

Comments
 (0)