]> git.basschouten.com Git - openhab-addons.git/blob
c774e27da9a80857f501f7d21446380c2cfb19c0
[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.neohub.internal;
14
15 import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
16
17 import javax.measure.Unit;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord;
22 import org.openhab.core.config.core.Configuration;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.library.unit.SIUnits;
25 import org.openhab.core.thing.Bridge;
26 import org.openhab.core.thing.Channel;
27 import org.openhab.core.thing.ChannelUID;
28 import org.openhab.core.thing.Thing;
29 import org.openhab.core.thing.ThingStatus;
30 import org.openhab.core.thing.ThingStatusDetail;
31 import org.openhab.core.thing.binding.BaseThingHandler;
32 import org.openhab.core.thing.binding.BridgeHandler;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.RefreshType;
35 import org.openhab.core.types.State;
36 import org.openhab.core.types.UnDefType;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The {@link NeoBaseHandler} is the openHAB Handler for NeoPlug devices
42  *
43  * @author Andrew Fiddian-Green - Initial contribution
44  *
45  */
46 @NonNullByDefault
47 public class NeoBaseHandler extends BaseThingHandler {
48
49     protected final Logger logger = LoggerFactory.getLogger(NeoBaseHandler.class);
50
51     protected @Nullable NeoBaseConfiguration config;
52
53     /*
54      * error messages
55      */
56     private static final String MSG_FMT_DEVICE_CONFIG = "device \"{}\" needs to configured in hub!";
57     private static final String MSG_FMT_DEVICE_COMM = "device \"{}\" not communicating with hub!";
58     private static final String MSG_FMT_COMMAND_OK = "command for \"{}\" succeeded.";
59     private static final String MSG_FMT_COMMAND_BAD = "\"{}\" is an invalid or empty command!";
60     private static final String MSG_DEVICE_NAME_NOT_CONFIGURED = "the parameter \"deviceNameInHub\" is not configured";
61
62     /*
63      * an object used to de-bounce state changes between openHAB and the NeoHub
64      */
65     protected NeoHubDebouncer debouncer = new NeoHubDebouncer();
66
67     public NeoBaseHandler(Thing thing) {
68         super(thing);
69     }
70
71     // ======== BaseThingHandler methods that are overridden =============
72
73     @Override
74     public void handleCommand(ChannelUID channelUID, Command command) {
75         @Nullable
76         NeoHubHandler hub;
77
78         if ((hub = getNeoHub()) != null) {
79             if (command == RefreshType.REFRESH) {
80                 hub.startFastPollingBurst();
81                 return;
82             }
83         }
84
85         toNeoHubSendCommandSet(channelUID.getId(), command);
86     }
87
88     @Override
89     public void initialize() {
90         NeoBaseConfiguration config = getConfigAs(NeoBaseConfiguration.class);
91
92         if (config.deviceNameInHub.isEmpty()) {
93             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MSG_DEVICE_NAME_NOT_CONFIGURED);
94             return;
95         }
96
97         this.config = config;
98
99         NeoHubHandler hub = getNeoHub();
100         if (hub == null) {
101             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MSG_HUB_CONFIG);
102             return;
103         }
104
105         updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING);
106     }
107
108     // ======== helper methods used by this class or descendants ===========
109
110     /*
111      * this method is called back by the NeoHub handler to inform this handler about
112      * polling results from the hub handler
113      */
114     public void toBaseSendPollResponse(NeoHubAbstractDeviceData deviceData) {
115         NeoBaseConfiguration config = this.config;
116         if (config == null) {
117             return;
118         }
119
120         AbstractRecord deviceRecord = deviceData.getDeviceRecord(config.deviceNameInHub);
121
122         if (deviceRecord == null) {
123             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
124             logger.warn(MSG_FMT_DEVICE_CONFIG, thing.getLabel());
125             return;
126         }
127
128         ThingStatus thingStatus = getThing().getStatus();
129         if (deviceRecord.offline() && (thingStatus == ThingStatus.ONLINE)) {
130             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
131             logger.debug(MSG_FMT_DEVICE_COMM, thing.getLabel());
132             return;
133         }
134
135         if ((!deviceRecord.offline()) && (thingStatus != ThingStatus.ONLINE)) {
136             updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
137         }
138
139         toOpenHabSendChannelValues(deviceRecord);
140     }
141
142     /*
143      * internal method used by by sendChannelValuesToOpenHab(); it checks the
144      * de-bouncer before actually sending the channel value to openHAB; or if the
145      * device has lost its connection to the RF mesh, either a) send no updates to
146      * OpenHAB or b) send state = undefined, depending on the value of a
147      * Configuration Parameter
148      */
149     protected void toOpenHabSendValueDebounced(String channelId, State state, boolean offline) {
150         /*
151          * if the device has been lost from the RF mesh network there are two possible
152          * behaviors: either a) do not report a state value, or b) show an undefined
153          * state; the choice of a) or b) depends on whether the channel has the
154          * Configuration Parameter holdOnlineState=true
155          */
156         if (!offline) {
157             if (debouncer.timeExpired(channelId)) {
158                 /*
159                  * in normal circumstances just forward the hub's reported state to OpenHAB
160                  */
161                 updateState(channelId, state);
162             }
163         } else {
164             ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId);
165             Channel channel = thing.getChannel(channelUID);
166             if (channel != null) {
167                 Configuration config = channel.getConfiguration();
168                 Object holdOnlineState = config.get(PARAM_HOLD_ONLINE_STATE);
169                 if (holdOnlineState != null && (holdOnlineState instanceof Boolean)
170                         && ((Boolean) holdOnlineState).booleanValue()) {
171                     /*
172                      * the Configuration Parameter "holdOnlineState" is True so do NOT send a
173                      * state update to OpenHAB
174                      */
175                     return;
176                 }
177             }
178             /*
179              * the Configuration Parameter "holdOnlineState" is either not existing or
180              * it is False so send a state=undefined update to OpenHAB
181              */
182             updateState(channelUID, UnDefType.UNDEF);
183         }
184     }
185
186     /*
187      * sends a channel command & value from openHAB => NeoHub. It delegates upwards
188      * to the NeoHub to handle the command
189      */
190     protected void toNeoHubSendCommand(String channelId, Command command) {
191         String cmdStr = toNeoHubBuildCommandString(channelId, command);
192
193         if (!cmdStr.isEmpty()) {
194             NeoHubHandler hub = getNeoHub();
195
196             if (hub != null) {
197                 /*
198                  * issue command, check result, and update status accordingly
199                  */
200                 switch (hub.toNeoHubSendChannelValue(cmdStr)) {
201                     case SUCCEEDED:
202                         logger.debug(MSG_FMT_COMMAND_OK, getThing().getLabel());
203
204                         if (getThing().getStatus() != ThingStatus.ONLINE) {
205                             updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
206                         }
207
208                         // initialize the de-bouncer for this channel
209                         debouncer.initialize(channelId);
210
211                         break;
212
213                     case ERR_COMMUNICATION:
214                         logger.debug(MSG_HUB_COMM, hub.getThing().getUID());
215                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
216                         break;
217
218                     case ERR_INITIALIZATION:
219                         logger.warn(MSG_HUB_CONFIG, hub.getThing().getUID());
220                         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
221                         break;
222                 }
223             } else {
224                 logger.debug(MSG_HUB_CONFIG, "unknown");
225             }
226         } else {
227             logger.debug(MSG_FMT_COMMAND_BAD, command.toString());
228         }
229     }
230
231     /**
232      * internal getter returns the NeoHub handler
233      *
234      * @return the neohub handler or null
235      */
236     protected @Nullable NeoHubHandler getNeoHub() {
237         @Nullable
238         Bridge b;
239         @Nullable
240         BridgeHandler h;
241
242         if ((b = getBridge()) != null && (h = b.getHandler()) != null && h instanceof NeoHubHandler) {
243             return (NeoHubHandler) h;
244         }
245
246         return null;
247     }
248
249     // ========= methods that MAY / MUST be overridden in descendants ============
250
251     /*
252      * NOTE: descendant classes MUST override this method. It builds the command
253      * string to be sent to the NeoHub
254      */
255     protected String toNeoHubBuildCommandString(String channelId, Command command) {
256         return "";
257     }
258
259     /*
260      * NOTE: descendant classes MAY override this method e.g. to send additional
261      * commands for dependent channels (if any)
262      */
263     protected void toNeoHubSendCommandSet(String channelId, Command command) {
264         toNeoHubSendCommand(channelId, command);
265     }
266
267     /*
268      * NOTE: descendant classes MUST override this method method by which the
269      * handler informs openHAB about channel state changes
270      */
271     protected void toOpenHabSendChannelValues(AbstractRecord deviceRecord) {
272     }
273
274     protected OnOffType invert(OnOffType value) {
275         return OnOffType.from(value == OnOffType.OFF);
276     }
277
278     protected Unit<?> getTemperatureUnit() {
279         @Nullable
280         NeoHubHandler hub = getNeoHub();
281         if (hub != null) {
282             return hub.getTemperatureUnit();
283         }
284         return SIUnits.CELSIUS;
285     }
286 }