2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.atlona.internal;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.locks.Lock;
18 import java.util.concurrent.locks.ReentrantLock;
20 import org.openhab.core.thing.ThingStatus;
21 import org.openhab.core.thing.ThingStatusDetail;
22 import org.openhab.core.types.State;
25 * Defines an implementation of {@link AtlonaHandlerCallback} that will remember the last state
26 * for an channelId and suppress the callback if the state hasn't changed
28 * @author Tim Roberts - Initial contribution
30 public class StatefulHandlerCallback implements AtlonaHandlerCallback {
32 /** The wrapped callback */
33 private final AtlonaHandlerCallback wrappedCallback;
35 /** The state by channel id */
36 private final Map<String, State> state = new ConcurrentHashMap<>();
38 private final Lock statusLock = new ReentrantLock();
39 private ThingStatus lastThingStatus;
40 private ThingStatusDetail lastThingStatusDetail;
43 * Create the callback from the other {@link AtlonaHandlerCallback}
45 * @param wrappedCallback a non-null {@link AtlonaHandlerCallback}
46 * @throws IllegalArgumentException if wrappedCallback is null
48 public StatefulHandlerCallback(AtlonaHandlerCallback wrappedCallback) {
49 if (wrappedCallback == null) {
50 throw new IllegalArgumentException("wrappedCallback cannot be null");
53 this.wrappedCallback = wrappedCallback;
57 * Overrides the status changed to simply call the {@link #wrappedCallback}
59 * @param status the new status
60 * @param detail the new detail
61 * @param msg the new message
64 public void statusChanged(ThingStatus status, ThingStatusDetail detail, String msg) {
67 // Simply return we match the last status change (prevents loops if changing to the same status)
68 if (status == lastThingStatus && detail == lastThingStatusDetail) {
72 lastThingStatus = status;
73 lastThingStatusDetail = detail;
77 // If we got this far - call the underlying one
78 wrappedCallback.statusChanged(status, detail, msg);
82 * Overrides the state changed to determine if the state is new or changed and then
83 * to call the {@link #wrappedCallback} if it has
85 * @param channelId the channel id that changed
86 * @param state the new state
89 public void stateChanged(String channelId, State state) {
90 if (channelId == null || "".equals(channelId)) {
94 final State oldState = this.state.get(channelId);
96 // If both null OR the same value (enums), nothing changed
97 if (oldState == state) {
101 // If they are equal - nothing changed
102 if (oldState != null && oldState.equals(state)) {
106 // Something changed - save the new state and call the underlying wrapped
107 this.state.put(channelId, state);
108 wrappedCallback.stateChanged(channelId, state);
112 * Removes the state associated with the channel id. If the channelid
113 * doesn't exist (or is null or is empty), this method will do nothing.
115 * @param channelId the channel id to remove state
117 public void removeState(String channelId) {
118 if (channelId == null || "".equals(channelId)) {
121 state.remove(channelId);
125 * Overrides the set property to simply call the {@link #wrappedCallback}
127 * @param propertyName a non-null, non-empty property name
128 * @param propertyValue a non-null, possibly empty property value
131 public void setProperty(String propertyName, String propertyValue) {
132 wrappedCallback.setProperty(propertyName, propertyValue);
136 * Callback to get the {@link State} for a given property name
138 * @param propertyName a possibly null, possibly empty property name
139 * @return the {@link State} for the propertyName or null if not found
141 public State getState(String propertyName) {
142 return state.get(propertyName);