gt:                 deviceConfigCache = new HashMap<String, DeviceConfig>();
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.primare.internal;
  
  import java.util.Dictionary;
  import java.util.Enumeration;
  import java.util.EventObject;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import org.openhab.binding.primare.PrimareBindingProvider;
  import org.openhab.binding.primare.internal.protocol.PrimareConnector;
  import org.openhab.binding.primare.internal.protocol.PrimareResponse;
  import org.openhab.binding.primare.internal.protocol.PrimareSerialConnector;
  import org.openhab.binding.primare.internal.protocol.PrimareTCPConnector;
  import org.openhab.binding.primare.internal.protocol.PrimareUtils;
  import org.openhab.core.binding.AbstractBinding;
  import org.openhab.core.binding.BindingChangeListener;
  import org.openhab.core.binding.BindingProvider;
  import org.openhab.core.items.Item;
  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;
  
  /**
   * Binding listening to openHAB event bus and send commands to Primare devices when certain
   * commands are received.
   *
   * @author Pauli Anttila, Veli-Pekka Juslin
   * @since 1.7.0
   */
  public class PrimareBinding extends AbstractBinding<PrimareBindingProvider>
          implements ManagedService, BindingChangeListener, PrimareEventListener {
  
      private static final Logger logger = LoggerFactory.getLogger(PrimareBinding.class);
  
      protected static final String WILDCARD_COMMAND_KEY = "*";
  
      /** RegEx to validate a config <code>'^(.*?)\\.(host|port|serial|model)$'</code> */
      private static final Pattern EXTRACT_CONFIG_PATTERN = Pattern.compile("^(.*?)\\.(host|port|serial|model)$");
  
      /** Map table to store all available receivers configured by the user */
      protected Map<String, DeviceConfig> deviceConfigCache = null;
  
      public PrimareBinding() {
      }
  
      @Override
      public void activate() {
      }
  
      @Override
      public void deactivate() {
          closeAllConnections();
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void bindingChanged(BindingProvider provider, String itemName) {
          logger.debug("bindingChanged for {}", itemName);
          initializeItem(itemName);
      }
  
      /**
       * @{inheritDoc}
       */
      @Override
      protected void internalReceiveCommand(String itemName, Command command) {
  
          if (itemName == null) {
              logger.warn("ignore command {} - no item name", command);
              return;
          }
  
          logger.debug("Received command [item:{} command:{}, class:{}]", itemName, command.toString(),
                  command.getClass().toString());
  
          PrimareBindingProvider provider = findFirstMatchingBindingProvider(itemName, command.toString());
  
          if (provider == null) {
              logger.warn("Ignore item:{} command:{} - no matching binding provider", itemName, command);
              return;
          }
  
          String tmp = provider.getDeviceCommand(itemName, command.toString());
  
          if (tmp == null) {
              tmp = provider.getDeviceCommand(itemName, WILDCARD_COMMAND_KEY);
          }
  
          if (tmp == null) {
              logger.warn("Ignore item:{} command {} - no matching command", itemName, command);
              return;
          }
  
          String[] commandParts = tmp.split(":");
          String deviceId = commandParts[0];
          String deviceCmd = commandParts[1];
  
          if (deviceId == null || deviceCmd == null) {
              logger.warn("Ignore item:{} command:{} - failed to find both device id and command in {}", itemName,
                      command, tmp);
              return;
          }
  
          DeviceConfig deviceConfig = deviceConfigCache.get(deviceId);
          if (deviceConfig == null) {
              logger.warn("Ignore item:{} command:{} - no configuration found for device {}", itemName, command,
                      deviceId);
              return;
          }
  
          PrimareConnector deviceConnector = deviceConfig.getInitializedConnector();
  
          if (deviceConnector == null) {
              logger.warn("Ignore {} item:{} command:{} - no connector found", deviceConfig.toString(), itemName,
                      command);
              return;
          }
  
          if (deviceConnector.isConnected()) {
              try {
                  deviceConnector.sendCommand(command, deviceCmd);
              } catch (Exception e) {
                  logger.warn("Ignore {} item:{} command:{} - message send error {}", deviceConfig.toString(), itemName,
                          command, e.getMessage());
              }
          } else {
              logger.warn("Ignore {} item:{} command:{} - device disconnected", deviceConfig.toString(), itemName,
                      command);
          }
      }
  
      /**
       * Find the first matching {@link PrimareBindingProvider} according to
       * <code>itemName</code> and <code>command</code>.
       *
       * @param itemName
       *            the item for which to find a provider
       * @param command
       *            the openHAB command for which to find a provider
       *
       * @return the matching binding provider or <code>null</code> if no binding
       *         provider could be found
       */
      private PrimareBindingProvider findFirstMatchingBindingProvider(String itemName, String command) {
  
          PrimareBindingProvider firstMatchingProvider = null;
  
          for (PrimareBindingProvider provider : this.providers) {
              String tmp = provider.getDeviceCommand(itemName, command.toString());
              if (tmp != null) {
                  firstMatchingProvider = provider;
                  break;
              }
          }
  
          if (firstMatchingProvider == null) {
              for (PrimareBindingProvider provider : this.providers) {
                  String tmp = provider.getDeviceCommand(itemName, WILDCARD_COMMAND_KEY);
                  if (tmp != null) {
                      firstMatchingProvider = provider;
                      break;
                  }
              }
  
          }
  
          return firstMatchingProvider;
      }
  
      protected void addBindingProvider(PrimareBindingProvider bindingProvider) {
          super.addBindingProvider(bindingProvider);
      }
  
      protected void removeBindingProvider(PrimareBindingProvider bindingProvider) {
          super.removeBindingProvider(bindingProvider);
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void updated(Dictionary<String, ?> config) throws ConfigurationException {
  
          logger.debug("Configuration updated, valid configuration {}", (config != null ? "exists" : "does not exist"));
  
          if (config != null) {
              Enumeration<String> keys = config.keys();
  
              if (deviceConfigCache == null) {
  