gt:         if (hub != null) {
context: /**
   * Copyright (c) 2010-2021 Contributors to the openHAB project
   *
   * See the NOTICE file(s) distributed with this work for additional
   * information.
   *
   * This program and the accompanying materials are made available under the
   * terms of the Eclipse Public License 2.0 which is available at
   * http://www.eclipse.org/legal/epl-2.0
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  package org.openhab.binding.neohub.internal;
  
  import static org.openhab.binding.neohub.internal.NeoHubBindingConstants.*;
  
  import javax.measure.Unit;
  
  import org.eclipse.jdt.annotation.NonNullByDefault;
  import org.eclipse.jdt.annotation.Nullable;
  import org.openhab.binding.neohub.internal.NeoHubAbstractDeviceData.AbstractRecord;
  import org.openhab.core.config.core.Configuration;
  import org.openhab.core.library.types.OnOffType;
  import org.openhab.core.library.unit.SIUnits;
  import org.openhab.core.thing.Bridge;
  import org.openhab.core.thing.Channel;
  import org.openhab.core.thing.ChannelUID;
  import org.openhab.core.thing.Thing;
  import org.openhab.core.thing.ThingStatus;
  import org.openhab.core.thing.ThingStatusDetail;
  import org.openhab.core.thing.binding.BaseThingHandler;
  import org.openhab.core.thing.binding.BridgeHandler;
  import org.openhab.core.types.Command;
  import org.openhab.core.types.RefreshType;
  import org.openhab.core.types.State;
  import org.openhab.core.types.UnDefType;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * The {@link NeoBaseHandler} is the openHAB Handler for NeoPlug devices
   * 
   * @author Andrew Fiddian-Green - Initial contribution
   * 
   */
  @NonNullByDefault
  public class NeoBaseHandler extends BaseThingHandler {
  
      protected final Logger logger = LoggerFactory.getLogger(NeoBaseHandler.class);
  
      protected @Nullable NeoBaseConfiguration config;
  
      /*
       * error messages
       */
      private static final String MSG_FMT_DEVICE_CONFIG = "device \"{}\" needs to configured in hub!";
      private static final String MSG_FMT_DEVICE_COMM = "device \"{}\" not communicating with hub!";
      private static final String MSG_FMT_COMMAND_OK = "command for \"{}\" succeeded.";
      private static final String MSG_FMT_COMMAND_BAD = "\"{}\" is an invalid or empty command!";
      private static final String MSG_DEVICE_NAME_NOT_CONFIGURED = "the parameter \"deviceNameInHub\" is not configured";
  
      /*
       * an object used to de-bounce state changes between openHAB and the NeoHub
       */
      protected NeoHubDebouncer debouncer = new NeoHubDebouncer();
  
      public NeoBaseHandler(Thing thing) {
          super(thing);
      }
  
      // ======== BaseThingHandler methods that are overridden =============
  
      @Override
      public void handleCommand(ChannelUID channelUID, Command command) {
          @Nullable
          NeoHubHandler hub;
  
          if ((hub = getNeoHub()) != null) {
              if (command == RefreshType.REFRESH) {
                  hub.startFastPollingBurst();
                  return;
              }
          }
  
          toNeoHubSendCommandSet(channelUID.getId(), command);
      }
  
      @Override
      public void initialize() {
          NeoBaseConfiguration config = getConfigAs(NeoBaseConfiguration.class);
  
          if (config.deviceNameInHub.isEmpty()) {
              updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MSG_DEVICE_NAME_NOT_CONFIGURED);
              return;
          }
  
          this.config = config;
  
          NeoHubHandler hub = getNeoHub();
          if (hub == null) {
              updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MSG_HUB_CONFIG);
              return;
          }
  
          updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING);
      }
  
      // ======== helper methods used by this class or descendants ===========
  
      /*
       * this method is called back by the NeoHub handler to inform this handler about
       * polling results from the hub handler
       */
      public void toBaseSendPollResponse(NeoHubAbstractDeviceData deviceData) {
          NeoBaseConfiguration config = this.config;
          if (config == null) {
              return;
          }
  
          AbstractRecord deviceRecord = deviceData.getDeviceRecord(config.deviceNameInHub);
  
          if (deviceRecord == null) {
              updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
              logger.warn(MSG_FMT_DEVICE_CONFIG, thing.getLabel());
              return;
          }
  
          ThingStatus thingStatus = getThing().getStatus();
          if (deviceRecord.offline() && (thingStatus == ThingStatus.ONLINE)) {
              updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
              logger.debug(MSG_FMT_DEVICE_COMM, thing.getLabel());
              return;
          }
  
          if ((!deviceRecord.offline()) && (thingStatus != ThingStatus.ONLINE)) {
              updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
          }
  
          toOpenHabSendChannelValues(deviceRecord);
      }
  
      /*
       * internal method used by by sendChannelValuesToOpenHab(); it checks the
       * de-bouncer before actually sending the channel value to openHAB; or if the
       * device has lost its connection to the RF mesh, either a) send no updates to
       * OpenHAB or b) send state = undefined, depending on the value of a
       * Configuration Parameter
       */
      protected void toOpenHabSendValueDebounced(String channelId, State state, boolean offline) {
          /*
           * if the device has been lost from the RF mesh network there are two possible
           * behaviors: either a) do not report a state value, or b) show an undefined
           * state; the choice of a) or b) depends on whether the channel has the
           * Configuration Parameter holdOnlineState=true
           */
          if (!offline) {
              if (debouncer.timeExpired(channelId)) {
                  /*
                   * in normal circumstances just forward the hub's reported state to OpenHAB
                   */
                  updateState(channelId, state);
              }
          } else {
              ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId);
              Channel channel = thing.getChannel(channelUID);
              if (channel != null) {
                  Configuration config = channel.getConfiguration();
                  Object holdOnlineState = config.get(PARAM_HOLD_ONLINE_STATE);
                  if (holdOnlineState != null && (holdOnlineState instanceof Boolean)
                          && ((Boolean) holdOnlineState).booleanValue()) {
                      /*
                       * the Configuration Parameter "holdOnlineState" is True so do NOT send a
                       * state update to OpenHAB
                       */
                      return;
                  }
              }
              /*
               * the Configuration Parameter "holdOnlineState" is either not existing or
               * it is False so send a state=undefined update to OpenHAB
               */
              updateState(channelUID, UnDefType.UNDEF);
          }
      }
  
      /*
       * sends a channel command & value from openHAB => NeoHub. It delegates upwards
       * to the NeoHub to handle the command
       */
      protected void toNeoHubSendCommand(String channelId, Command command) {
          String cmdStr = toNeoHubBuildCommandString(channelId, command);
  
          if (!cmdStr.isEmpty()) {
              NeoHubHandler hub = getNeoHub();
  
              if (hub != null) {
                  /*
                   * issue command, check result, and update status accordingly
                   */
                  switch (hub.toNeoHubSendChannelValue(cmdStr)) {
                      case SUCCEEDED:
                          logger.debug(MSG_FMT_COMMAND_OK, getThing().getLabel());
  
                          if (getThing().getStatus() != ThingStatus.ONLINE) {
                              updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
                          }
  
                          // initialize the de-bouncer for this channel
                          debouncer.initialize(channelId);
  
                          break;
  
                      case ERR_COMMUNICATION:
                          logger.debug(MSG_HUB_COMM);
                          updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
                          break;
  
                      case ERR_INITIALIZATION:
                          logger.warn(MSG_HUB_CONFIG);
                          updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
                          break;
                  }
              } else {
                  logger.debug(MSG_HUB_CONFIG);
              }
          } else {
              logger.debug(MSG_FMT_COMMAND_BAD, command.toString());
          }
      }
  
      /**
       * internal getter returns the NeoHub handler
       * 
       * @return the neohub handler or null
       */
      protected @Nullable NeoHubHandler getNeoHub() {
          @Nullable
          Bridge b;
          @Nullable
          BridgeHandler h;
  
          if ((b = getBridge()) != null && (h = b.getHandler()) != null && h instanceof NeoHubHandler) {
              return (NeoHubHandler) h;
          }
  
          return null;
      }
  
      // ========= methods that MAY / MUST be overridden in descendants ============
  
      /*
       * NOTE: descendant classes MUST override this method. It builds the command
       * string to be sent to the NeoHub
       */
      protected String toNeoHubBuildCommandString(String channelId, Command command) {
          return "";
      }
  
      /*
       * NOTE: descendant classes MAY override this method e.g. to send additional
       * commands for dependent channels (if any)
       */
      protected void toNeoHubSendCommandSet(String channelId, Command command) {
          toNeoHubSendCommand(channelId, command);
      }
  
      /*
       * NOTE: descendant classes MUST override this method method by which the
       * handler informs openHAB about channel state changes
       */
      protected void toOpenHabSendChannelValues(AbstractRecord deviceRecord) {
      }
  
      protected OnOffType invert(OnOffType value) {
          return OnOffType.from(value == OnOffType.OFF);
      }
  
      protected Unit<?> getTemperatureUnit() {
          @Nullable
          NeoHubHandler hub = getNeoHub();
  