]> git.basschouten.com Git - openhab-addons.git/blob
42d075b5482c71ea17e9055fa20fca40e1dd0b73
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.io.homekit.internal;
14
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.ConcurrentMap;
17
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.openhab.core.items.GenericItem;
20 import org.openhab.core.items.Item;
21 import org.openhab.core.items.StateChangeListener;
22 import org.openhab.core.types.State;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
27
28 /**
29  * Subscribes and unsubscribes from Item changes to enable notification to HomeKit
30  * clients. Each item/key pair (key is optional) should be unique, as the underlying
31  * HomeKit library takes care of insuring only a single subscription exists for
32  * each accessory.
33  *
34  * @author Andy Lintner - Initial contribution
35  */
36 public class HomekitAccessoryUpdater {
37     private final Logger logger = LoggerFactory.getLogger(HomekitAccessoryUpdater.class);
38     private final ConcurrentMap<ItemKey, Subscription> subscriptionsByName = new ConcurrentHashMap<>();
39
40     public void subscribe(GenericItem item, HomekitCharacteristicChangeCallback callback) {
41         subscribe(item, null, callback);
42     }
43
44     public void subscribe(GenericItem item, String key, HomekitCharacteristicChangeCallback callback) {
45         logger.trace("Received subscription request for {} / {}", item, key);
46         if (item == null) {
47             return;
48         }
49         if (callback == null) {
50             logger.trace("The received subscription contains a null callback, skipping");
51             return;
52         }
53         ItemKey itemKey = new ItemKey(item, key);
54         subscriptionsByName.compute(itemKey, (k, v) -> {
55             if (v != null) {
56                 logger.debug("Received duplicate subscription for {} / {}", item, key);
57                 unsubscribe(item, key);
58             }
59             logger.trace("Adding subscription for {} / {}", item, key);
60             Subscription subscription = (changedItem, oldState, newState) -> callback.changed();
61             item.addStateChangeListener(subscription);
62             return subscription;
63         });
64     }
65
66     public void unsubscribe(GenericItem item) {
67         unsubscribe(item, null);
68     }
69
70     public void unsubscribe(GenericItem item, String key) {
71         if (item == null) {
72             return;
73         }
74         subscriptionsByName.computeIfPresent(new ItemKey(item, key), (k, v) -> {
75             logger.trace("Removing existing subscription for {} / {}", item, key);
76             item.removeStateChangeListener(v);
77             return null;
78         });
79     }
80
81     @FunctionalInterface
82     @NonNullByDefault
83     private interface Subscription extends StateChangeListener {
84
85         @Override
86         void stateChanged(Item item, State oldState, State newState);
87
88         @Override
89         default void stateUpdated(Item item, State state) {
90             // Do nothing on non-change update
91         }
92     }
93
94     private static class ItemKey {
95         public final GenericItem item;
96         public final String key;
97
98         public ItemKey(GenericItem item, String key) {
99             this.item = item;
100             this.key = key;
101         }
102
103         @Override
104         public int hashCode() {
105             final int prime = 31;
106             int result = 1;
107             result = prime * result + ((item == null) ? 0 : item.hashCode());
108             result = prime * result + ((key == null) ? 0 : key.hashCode());
109             return result;
110         }
111
112         @Override
113         public boolean equals(Object obj) {
114             if (this == obj) {
115                 return true;
116             }
117             if (obj == null) {
118                 return false;
119             }
120             if (getClass() != obj.getClass()) {
121                 return false;
122             }
123             ItemKey other = (ItemKey) obj;
124             if (item == null) {
125                 if (other.item != null) {
126                     return false;
127                 }
128             } else if (!item.equals(other.item)) {
129                 return false;
130             }
131             if (key == null) {
132                 if (other.key != null) {
133                     return false;
134                 }
135             } else if (!key.equals(other.key)) {
136                 return false;
137             }
138             return true;
139         }
140     }
141 }