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