gt:             Runnable refreshCommand = new Runnable() {
context: /**
   * Copyright (c) 2010-2019 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.nikobus.internal;
  
  import java.util.Dictionary;
  import java.util.List;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
  import java.util.concurrent.ScheduledExecutorService;
  import java.util.concurrent.TimeUnit;
  
  import org.apache.commons.lang.StringUtils;
  import org.openhab.binding.nikobus.NikobusBindingProvider;
  import org.openhab.binding.nikobus.internal.config.AbstractNikobusItemConfig;
  import org.openhab.binding.nikobus.internal.core.NikobusAckMonitor;
  import org.openhab.binding.nikobus.internal.core.NikobusCommand;
  import org.openhab.binding.nikobus.internal.core.NikobusCommandListener;
  import org.openhab.binding.nikobus.internal.core.NikobusCommandReceiver;
  import org.openhab.binding.nikobus.internal.core.NikobusCommandSender;
  import org.openhab.binding.nikobus.internal.core.NikobusInterface;
  import org.openhab.binding.nikobus.internal.core.NikobusModule;
  import org.openhab.core.binding.AbstractBinding;
  import org.openhab.core.binding.BindingProvider;
  import org.openhab.core.types.Command;
  import org.openhab.core.types.State;
  import org.osgi.service.cm.ConfigurationException;
  import org.osgi.service.cm.ManagedService;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * Nikobus event binding class. Receives all command events and forwards them to
   * the appropriate configured item.
   *
   * @author Davy Vanherbergen
   * @since 1.3.0
   */
  public class NikobusBinding extends AbstractBinding<NikobusBindingProvider>implements ManagedService {
  
      private static final Logger log = LoggerFactory.getLogger(NikobusBinding.class);
  
      private NikobusInterface serialInterface = new NikobusInterface();
  
      private NikobusCommandReceiver commandReceiver;
  
      private NikobusCommandSender commandSender;
  
      private long refreshInterval = 600;
  
      private int nextModuleToRefresh = 0;
  
      private ScheduledExecutorService refreshService;
  
      private ExecutorService statusRequestService = Executors.newSingleThreadExecutor();
  
      public NikobusBinding() {
          // setup a command receiver in its own thread
          commandReceiver = new NikobusCommandReceiver(this);
          commandReceiver.setBufferQueue(serialInterface.getBufferQueue());
          Thread receiverThread = new Thread(commandReceiver);
          receiverThread.setName("Nikobus Receiver");
          receiverThread.setDaemon(true);
          receiverThread.start();
          log.debug("Started Command Receiver Thread.");
  
          commandSender = new NikobusCommandSender(serialInterface);
          Thread senderThread = new Thread(commandSender);
          senderThread.setName("Nikobus Sender");
          senderThread.setDaemon(true);
          senderThread.start();
          log.debug("Started Command Sender Thread.");
      }
  
      /**
       * {@inheritDoc}
       *
       * Redistribute an openHAB command to the nikobus binding for that item.
       */
      @Override
      public void internalReceiveCommand(String itemName, Command command) {
  
          log.trace("Received command {} for item {}", command.toString(), itemName);
  
          // get the item's corresponding Nikobus binding configuration
          AbstractNikobusItemConfig itemBinding = null;
          for (NikobusBindingProvider provider : providers) {
              if (provider.providesBindingFor(itemName)) {
                  itemBinding = provider.getItemConfig(itemName);
                  break;
              }
          }
  
          if (providers.size() == 0) {
              log.error("No providers");
          }
          if (itemBinding == null) {
              log.trace("No item found");
              return;
          }
  
          try {
              // process the command
              itemBinding.processCommand(command, this);
          } catch (Exception e) {
              log.error("Error processing commmand {} for item {} : {}",
                      new Object[] { command.toString(), itemBinding.getName(), e.getMessage() });
          }
  
      }
  
      /**
       * Send a status update to the openHab bus.
       *
       * @param itemName
       *            item for which to send update
       * @param state
       *            status
       */
      public void postUpdate(String itemName, State state) {
          if (eventPublisher == null) {
              log.error("Could not send status update.  Event Publisher is missing");
              return;
          }
  
          log.trace("Sending status update to {} : {}", itemName, state);
          eventPublisher.postUpdate(itemName, state);
          return;
  
      }
  
      /**
       * Send a command via the serial port to the Nikobus.
       *
       * @param nikobusCommand
       *            command to send
       * @throws Exception
       *             TimeoutException if waitForAck is true and no ack was
       *             received within the timeout limit.
       */
      public void sendCommand(NikobusCommand cmd) throws Exception {
  
          if (cmd.getAck() != null) {
              // send & wait for ACK
              log.trace("Sending command with ack {}", cmd.getCommand());
  
              NikobusAckMonitor monitor = new NikobusAckMonitor(cmd);
              commandReceiver.register(monitor);
              try {
                  monitor.waitForAck(commandSender);
              } finally {
                  commandReceiver.unregister(monitor);
              }
  
          } else {
              // send only
              log.trace("Sending command without waiting for ack {}", cmd.getCommand());
              commandSender.sendCommand(cmd);
          }
      }
  
      /**
       * Start a connection to nikobus.
       */
      public void connect() {
  
          try {
              log.trace("Starting connection");
              serialInterface.connect();
          } catch (Exception e) {
              log.error(e.getMessage());
          }
      }
  
      /**
       * @return nikobus connection status.
       */
      public String getConnectionStatus() {
  
          if (serialInterface != null && serialInterface.isConnected()) {
              return "Connected to " + serialInterface.getPort();
          }
          return "Not Connected.";
      }
  
      /**
       * @return NikobusBindingProvider
       */
      private NikobusBindingProvider getBindingProvider() {
          for (NikobusBindingProvider p : providers) {
              return p;
          }
          return null;
      }
  
      /**
       * Request a status update for the module with the given address. This will
       * trigger a read command being sent to the nikobus if none is already
       * pending. The read command will be send using a separate thread.
       *
       * @param moduleAddress
       *            address of the module for which to request the status update.
       * @param delayedSend
       *            when true, sending will wait for empty bus
       */
      public void scheduleStatusUpdateRequest(String moduleAddress, boolean delayedSend) {
  
          NikobusModule module = getBindingProvider().getModule(moduleAddress);
          final NikobusCommand cmd = module.getStatusRequestCommand();
          cmd.setWaitForSilence(delayedSend);
          cmd.setMaxRetryCount(10);
  
          if (commandSender.isCommandRedundant(cmd)) {
              // no need to send, there is a similar command already pending
              return;
          }
  
          Runnable updater = new Runnable() {
              @Override
              public void run() {
                  try {
                      sendCommand(cmd);
                  } catch (Exception e) {
                      log.error("Error occurred during status request.", e);
                  }
              }
          };
  
          statusRequestService.submit(updater);
  
      }
  
      /**
       * Activate the binding provider. After initial connection;
       */
      @Override
      public void activate() {
  
          log.trace("Activating binding");
          startRefreshScheduler();
  
          log.trace("Registering modules");
          for (NikobusModule module : getBindingProvider().getAllModules()) {
              register(module);
          }
      }
  
      /**
       * Start an automatic refresh scheduler.
       */
      private void startRefreshScheduler() {
  
          // stop any running instance..
          if (refreshService != null) {
              refreshService.shutdownNow();
          }
  
          if (refreshInterval > 0) {
              refreshService = Executors.newScheduledThreadPool(1);
  
  