2 * Copyright (c) 2010-2023 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.neohub.internal;
15 import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
17 import javax.measure.Unit;
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;
41 * The {@link NeoBaseHandler} is the openHAB Handler for NeoPlug devices
43 * @author Andrew Fiddian-Green - Initial contribution
47 public class NeoBaseHandler extends BaseThingHandler {
49 protected final Logger logger = LoggerFactory.getLogger(NeoBaseHandler.class);
51 protected @Nullable NeoBaseConfiguration config;
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";
63 * an object used to de-bounce state changes between openHAB and the NeoHub
65 protected NeoHubDebouncer debouncer = new NeoHubDebouncer();
67 public NeoBaseHandler(Thing thing) {
71 // ======== BaseThingHandler methods that are overridden =============
74 public void handleCommand(ChannelUID channelUID, Command command) {
78 if ((hub = getNeoHub()) != null) {
79 if (command == RefreshType.REFRESH) {
80 hub.startFastPollingBurst();
85 toNeoHubSendCommandSet(channelUID.getId(), command);
89 public void initialize() {
90 NeoBaseConfiguration config = getConfigAs(NeoBaseConfiguration.class);
92 if (config.deviceNameInHub.isEmpty()) {
93 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MSG_DEVICE_NAME_NOT_CONFIGURED);
99 NeoHubHandler hub = getNeoHub();
101 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MSG_HUB_CONFIG);
105 updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING);
108 // ======== helper methods used by this class or descendants ===========
111 * this method is called back by the NeoHub handler to inform this handler about
112 * polling results from the hub handler
114 public void toBaseSendPollResponse(NeoHubAbstractDeviceData deviceData) {
115 NeoBaseConfiguration config = this.config;
116 if (config == null) {
120 AbstractRecord deviceRecord = deviceData.getDeviceRecord(config.deviceNameInHub);
122 if (deviceRecord == null) {
123 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
124 logger.warn(MSG_FMT_DEVICE_CONFIG, thing.getLabel());
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());
135 if ((!deviceRecord.offline()) && (thingStatus != ThingStatus.ONLINE)) {
136 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
139 toOpenHabSendChannelValues(deviceRecord);
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
149 protected void toOpenHabSendValueDebounced(String channelId, State state, boolean offline) {
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
157 if (debouncer.timeExpired(channelId)) {
159 * in normal circumstances just forward the hub's reported state to OpenHAB
161 updateState(channelId, state);
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()) {
172 * the Configuration Parameter "holdOnlineState" is True so do NOT send a
173 * state update to OpenHAB
179 * the Configuration Parameter "holdOnlineState" is either not existing or
180 * it is False so send a state=undefined update to OpenHAB
182 updateState(channelUID, UnDefType.UNDEF);
187 * sends a channel command & value from openHAB => NeoHub. It delegates upwards
188 * to the NeoHub to handle the command
190 protected void toNeoHubSendCommand(String channelId, Command command) {
191 String cmdStr = toNeoHubBuildCommandString(channelId, command);
193 if (!cmdStr.isEmpty()) {
194 NeoHubHandler hub = getNeoHub();
198 * issue command, check result, and update status accordingly
200 switch (hub.toNeoHubSendChannelValue(cmdStr)) {
202 logger.debug(MSG_FMT_COMMAND_OK, getThing().getLabel());
204 if (getThing().getStatus() != ThingStatus.ONLINE) {
205 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
208 // initialize the de-bouncer for this channel
209 debouncer.initialize(channelId);
213 case ERR_COMMUNICATION:
214 logger.debug(MSG_HUB_COMM, hub.getThing().getUID());
215 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
218 case ERR_INITIALIZATION:
219 logger.warn(MSG_HUB_CONFIG, hub.getThing().getUID());
220 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
224 logger.debug(MSG_HUB_CONFIG, "unknown");
227 logger.debug(MSG_FMT_COMMAND_BAD, command.toString());
232 * internal getter returns the NeoHub handler
234 * @return the neohub handler or null
236 protected @Nullable NeoHubHandler getNeoHub() {
242 if ((b = getBridge()) != null && (h = b.getHandler()) != null && h instanceof NeoHubHandler) {
243 return (NeoHubHandler) h;
249 // ========= methods that MAY / MUST be overridden in descendants ============
252 * NOTE: descendant classes MUST override this method. It builds the command
253 * string to be sent to the NeoHub
255 protected String toNeoHubBuildCommandString(String channelId, Command command) {
260 * NOTE: descendant classes MAY override this method e.g. to send additional
261 * commands for dependent channels (if any)
263 protected void toNeoHubSendCommandSet(String channelId, Command command) {
264 toNeoHubSendCommand(channelId, command);
268 * NOTE: descendant classes MUST override this method method by which the
269 * handler informs openHAB about channel state changes
271 protected void toOpenHabSendChannelValues(AbstractRecord deviceRecord) {
274 protected OnOffType invert(OnOffType value) {
275 return OnOffType.from(value == OnOffType.OFF);
278 protected Unit<?> getTemperatureUnit() {
280 NeoHubHandler hub = getNeoHub();
282 return hub.getTemperatureUnit();
284 return SIUnits.CELSIUS;