@@ -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