]> git.basschouten.com Git - openhab-addons.git/blob
9665e0d3b0c0e26e25d9b2a14662067522965f11
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.ArrayList;
16 import java.util.List;
17
18 import org.openhab.binding.russound.internal.net.SocketSession;
19 import org.openhab.binding.russound.internal.net.SocketSessionListener;
20 import org.openhab.core.library.types.StringType;
21 import org.openhab.core.thing.Bridge;
22 import org.openhab.core.thing.ChannelUID;
23 import org.openhab.core.thing.Thing;
24 import org.openhab.core.thing.ThingStatus;
25 import org.openhab.core.thing.ThingStatusInfo;
26 import org.openhab.core.thing.binding.BaseBridgeHandler;
27 import org.openhab.core.thing.binding.ThingHandler;
28
29 import com.google.gson.Gson;
30
31 /**
32  * Represents the abstract base to a {@link BaseBridgeHandler} for common functionality to all Bridges. This abstract
33  * base provides management of the {@link AbstractRioProtocol}, parent {@link #bridgeStatusChanged(ThingStatusInfo)}
34  * event processing and the ability to get the current {@link SocketSession}.
35  * {@link #sendCommand(String)} and responses will be received on any {@link SocketSessionListener}
36  *
37  * @author Tim Roberts - Initial contribution
38  */
39 public abstract class AbstractBridgeHandler<E extends AbstractRioProtocol> extends BaseBridgeHandler
40         implements RioCallbackHandler {
41     /**
42      * The protocol handler for this base
43      */
44     private E protocolHandler;
45
46     /**
47      * Creates the handler from the given {@link Bridge}
48      *
49      * @param bridge a non-null {@link Bridge}
50      */
51     protected AbstractBridgeHandler(Bridge bridge) {
52         super(bridge);
53     }
54
55     /**
56      * Sets a new {@link AbstractRioProtocol} as the current protocol handler. If one already exists, it will be
57      * disposed of first.
58      *
59      * @param newProtocolHandler a, possibly null, {@link AbstractRioProtocol}
60      */
61     protected void setProtocolHandler(E newProtocolHandler) {
62         if (protocolHandler != null) {
63             protocolHandler.dispose();
64         }
65         protocolHandler = newProtocolHandler;
66     }
67
68     /**
69      * Get's the {@link AbstractRioProtocol} handler. May be null if none currently exists
70      *
71      * @return an {@link AbstractRioProtocol} handler or null if none exists
72      */
73     protected E getProtocolHandler() {
74         return protocolHandler;
75     }
76
77     /**
78      * Overridden to simply get the protocol handler's {@link RioHandlerCallback}
79      *
80      * @return the {@link RioHandlerCallback} or null if not found
81      */
82     @Override
83     public RioHandlerCallback getRioHandlerCallback() {
84         final E protocolHandler = getProtocolHandler();
85         return protocolHandler == null ? null : protocolHandler.getCallback();
86     }
87
88     /**
89      * Returns the {@link SocketSession} for this {@link Bridge}. The default implementation is to look in the parent
90      * {@link #getBridge()} for the {@link SocketSession}
91      *
92      * @return a {@link SocketSession} or null if none exists
93      */
94     @SuppressWarnings("rawtypes")
95     public SocketSession getSocketSession() {
96         final Bridge bridge = getBridge();
97         if (bridge.getHandler() instanceof AbstractBridgeHandler) {
98             return ((AbstractBridgeHandler) bridge.getHandler()).getSocketSession();
99         }
100         return null;
101     }
102
103     /**
104      * Returns the {@link RioPresetsProtocol} for this {@link Bridge}. The default implementation is to look in the
105      * parent
106      * {@link #getPresetsProtocol()} for the {@link RioPresetsProtocol}
107      *
108      * @return a {@link RioPresetsProtocol} or null if none exists
109      */
110     @SuppressWarnings("rawtypes")
111     public RioPresetsProtocol getPresetsProtocol() {
112         final Bridge bridge = getBridge();
113         if (bridge != null && bridge.getHandler() instanceof AbstractBridgeHandler) {
114             return ((AbstractBridgeHandler) bridge.getHandler()).getPresetsProtocol();
115         }
116         return null;
117     }
118
119     /**
120      * Returns the {@link RioSystemFavoritesProtocol} for this {@link Bridge}. The default implementation is to look in
121      * the
122      * parent
123      * {@link #getPresetsProtocol()} for the {@link RioSystemFavoritesProtocol}
124      *
125      * @return a {@link RioSystemFavoritesProtocol} or null if none exists
126      */
127     @SuppressWarnings("rawtypes")
128     public RioSystemFavoritesProtocol getSystemFavoritesHandler() {
129         final Bridge bridge = getBridge();
130         if (bridge != null && bridge.getHandler() instanceof AbstractBridgeHandler) {
131             return ((AbstractBridgeHandler) bridge.getHandler()).getSystemFavoritesHandler();
132         }
133         return null;
134     }
135
136     /**
137      * Overrides the base to initialize or dispose the handler based on the parent bridge status changing. If offline,
138      * {@link #dispose()} will be called instead. We then try to reinitialize ourselves when the bridge goes back online
139      * via the {@link #retryBridge()} method.
140      */
141     @Override
142     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
143         if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
144             reconnect();
145         } else {
146             disconnect();
147         }
148         super.bridgeStatusChanged(bridgeStatusInfo);
149     }
150
151     /**
152      * Creates an "{id:x, name: 'xx'}" json string from all {@link RioNamedHandler} for a specific class and sends that
153      * result to a channel id.
154      *
155      * @param gson a non-null {@link Gson} to use
156      * @param clazz a non-null class that the results will be for
157      * @param channelId a non-null, non-empty channel identifier to send the results to
158      * @throws IllegalArgumentException if any argument is null or empty
159      */
160     protected <H extends RioNamedHandler> void refreshNamedHandler(Gson gson, Class<H> clazz, String channelId) {
161         if (gson == null) {
162             throw new IllegalArgumentException("gson cannot be null");
163         }
164         if (clazz == null) {
165             throw new IllegalArgumentException("clazz cannot be null");
166         }
167         if (channelId == null || channelId.isEmpty()) {
168             throw new IllegalArgumentException("channelId cannot be null or empty");
169         }
170
171         final List<IdName> ids = new ArrayList<>();
172         for (Thing thn : getThing().getThings()) {
173             if (thn.getStatus() == ThingStatus.ONLINE) {
174                 final ThingHandler handler = thn.getHandler();
175                 if (handler != null && handler.getClass().isAssignableFrom(clazz)) {
176                     final RioNamedHandler namedHandler = (RioNamedHandler) handler;
177                     if (namedHandler.getId() > 0) { // 0 returned when handler is initializing
178                         ids.add(new IdName(namedHandler.getId(), namedHandler.getName()));
179                     }
180                 }
181             }
182         }
183
184         final String json = gson.toJson(ids);
185         updateState(channelId, new StringType(json));
186     }
187
188     /**
189      * Overrides the base method to remove any state linked to the {@lin ChannelUID} from the
190      * {@link StatefulHandlerCallback}
191      */
192     @Override
193     public void channelUnlinked(ChannelUID channelUID) {
194         // Remove any state when unlinking (that way if it is relinked - we get it)
195         final RioHandlerCallback callback = getProtocolHandler().getCallback();
196         if (callback instanceof StatefulHandlerCallback) {
197             ((StatefulHandlerCallback) callback).removeState(channelUID.getId());
198         }
199         super.channelUnlinked(channelUID);
200     }
201
202     /**
203      * Base method to reconnect the handler. The base implementation will simply {@link #disconnect()} then
204      * {@link #initialize()} the handler.
205      */
206     protected void reconnect() {
207         disconnect();
208         initialize();
209     }
210
211     /**
212      * Base method to disconnect the handler. This implementation will simply call
213      * {@link #setProtocolHandler(AbstractRioProtocol)} to null.
214      */
215     protected void disconnect() {
216         setProtocolHandler(null);
217     }
218
219     /**
220      * Overrides the dispose to call the {@link #disconnect()} method to disconnect the handler
221      */
222     @Override
223     public void dispose() {
224         disconnect();
225         super.dispose();
226     }
227
228     /**
229      * Private class that simply stores an ID & Name. This class is solely used to create a json result like "{id:1,
230      * name:'stuff'}"
231      *
232      * @author Tim Roberts
233      */
234     private class IdName {
235         @SuppressWarnings("unused")
236         private final int id;
237         @SuppressWarnings("unused")
238         private final String name;
239
240         public IdName(int id, String name) {
241             this.id = id;
242             this.name = name;
243         }
244     }
245 }