]> git.basschouten.com Git - openhab-addons.git/blob
e68159458b0dbb07175ef15eb02d5ce620826aad
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.binding.russound.internal.rio;
14
15 import java.util.Map;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.locks.Lock;
18 import java.util.concurrent.locks.ReentrantLock;
19
20 import org.openhab.core.thing.ThingStatus;
21 import org.openhab.core.thing.ThingStatusDetail;
22 import org.openhab.core.types.State;
23
24 /**
25  * Defines an implementation of {@link RioHandlerCallback} that will remember the last state
26  * for an channelId and suppress the callback if the state hasn't changed
27  *
28  * @author Tim Roberts - Initial contribution
29  */
30 public class StatefulHandlerCallback implements RioHandlerCallback {
31
32     /** The wrapped callback */
33     private final RioHandlerCallback wrappedCallback;
34
35     /** The state by channel id */
36     private final Map<String, State> state = new ConcurrentHashMap<>();
37
38     private final Lock statusLock = new ReentrantLock();
39     private ThingStatus lastThingStatus = null;
40     private ThingStatusDetail lastThingStatusDetail = null;
41
42     /**
43      * Create the callback from the other {@link RioHandlerCallback}
44      *
45      * @param wrappedCallback a non-null {@link RioHandlerCallback}
46      * @throws IllegalArgumentException if wrappedCallback is null
47      */
48     public StatefulHandlerCallback(RioHandlerCallback wrappedCallback) {
49         if (wrappedCallback == null) {
50             throw new IllegalArgumentException("wrappedCallback cannot be null");
51         }
52
53         this.wrappedCallback = wrappedCallback;
54     }
55
56     /**
57      * Overrides the status changed to simply call the {@link #wrappedCallback}
58      *
59      * @param status the new status
60      * @param detail the new detail
61      * @param msg the new message
62      */
63     @Override
64     public void statusChanged(ThingStatus status, ThingStatusDetail detail, String msg) {
65         statusLock.lock();
66         try {
67             // Simply return we match the last status change (prevents loops if changing to the same status)
68             if (status == lastThingStatus && detail == lastThingStatusDetail) {
69                 return;
70             }
71
72             lastThingStatus = status;
73             lastThingStatusDetail = detail;
74         } finally {
75             statusLock.unlock();
76         }
77
78         // If we got this far - call the underlying one
79         wrappedCallback.statusChanged(status, detail, msg);
80     }
81
82     /**
83      * Overrides the state changed to determine if the state is new or changed and then
84      * to call the {@link #wrappedCallback} if it has
85      *
86      * @param channelId the channel id that changed
87      * @param newState the new state
88      */
89     @Override
90     public void stateChanged(String channelId, State newState) {
91         if (channelId == null || channelId.isEmpty()) {
92             return;
93         }
94
95         final State oldState = state.get(channelId);
96
97         // If both null OR the same value (enums), nothing changed
98         if (oldState == newState) {
99             return;
100         }
101
102         // If they are equal - nothing changed
103         if (oldState != null && oldState.equals(newState)) {
104             return;
105         }
106
107         // Something changed - save the new state and call the underlying wrapped
108         state.put(channelId, newState);
109         wrappedCallback.stateChanged(channelId, newState);
110     }
111
112     /**
113      * Removes the state associated with the channel id. If the channelid
114      * doesn't exist (or is null or is empty), this method will do nothing.
115      *
116      * @param channelId the channel id to remove state
117      */
118     public void removeState(String channelId) {
119         if (channelId == null || channelId.isEmpty()) {
120             return;
121         }
122         state.remove(channelId);
123     }
124
125     /**
126      * Overrides the set property to simply call the {@link #wrappedCallback}
127      *
128      * @param propertyName a non-null, non-empty property name
129      * @param propertyValue a non-null, possibly empty property value
130      */
131     @Override
132     public void setProperty(String propertyName, String propertyValue) {
133         wrappedCallback.setProperty(propertyName, propertyValue);
134     }
135
136     /**
137      * Returns teh current state for the property
138      *
139      * @param propertyName a possibly null, possibly empty property name
140      * @return the {@link State} for the property or null if not found (or property name is null/empty)
141      */
142     public State getProperty(String propertyName) {
143         return state.get(propertyName);
144     }
145
146     @Override
147     public void addListener(String channelId, RioHandlerCallbackListener listener) {
148         wrappedCallback.addListener(channelId, listener);
149     }
150
151     @Override
152     public void removeListener(String channelId, RioHandlerCallbackListener listener) {
153         wrappedCallback.removeListener(channelId, listener);
154     }
155 }