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