gt:                         LgTvCommand cmd = LgTvCommand.valueOf(deviceCmd);
context: /**
   * Copyright (c) 2010-2019 by the respective copyright holders.
   *
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   */
  package org.openhab.binding.lgtv.internal;
  
  import java.util.Dictionary;
  import java.util.Enumeration;
  import java.util.EventObject;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.concurrent.Executors;
  import java.util.concurrent.ScheduledExecutorService;
  import java.util.concurrent.TimeUnit;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import org.openhab.binding.lgtv.LgtvBindingProvider;
  import org.openhab.binding.lgtv.lginteraction.LgTvCommand;
  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.library.items.DimmerItem;
  import org.openhab.core.library.items.NumberItem;
  import org.openhab.core.library.items.RollershutterItem;
  import org.openhab.core.library.items.StringItem;
  import org.openhab.core.library.items.SwitchItem;
  import org.openhab.core.library.types.DecimalType;
  import org.openhab.core.library.types.OnOffType;
  import org.openhab.core.library.types.PercentType;
  import org.openhab.core.library.types.StringType;
  import org.openhab.core.types.Command;
  import org.openhab.core.types.State;
  import org.openhab.core.types.UnDefType;
  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 LGTV devices when
   * certain commands are received.
   *
   * @author Martin Fluch
   * @since 1.6.0
   */
  public class LgtvBinding extends AbstractBinding<LgtvBindingProvider>
          implements ManagedService, BindingChangeListener, LgtvEventListener {
  
      private static final Logger logger = LoggerFactory.getLogger(LgtvBinding.class);
  
      protected static final String ADVANCED_COMMAND_KEY = "#";
      protected static final String WILDCARD_COMMAND_KEY = "*";
  
      /** RegEx to validate a config <code>'^(.*?)\\.(host|port)$'</code> */
      private static final Pattern EXTRACT_CONFIG_PATTERN = Pattern
              .compile("^(.*?)\\.(host|port|serverport|pairkey|xmldatafiles|checkalive)$");
  
      /** LGTV default tcp port */
      private final static int DEFAULT_PORT = 8080;
  
      /** Map table to store all available receivers configured by the user */
      protected Map<String, DeviceConfig> deviceConfigCache = null;
  
      private ScheduledExecutorService executor = null;
  
      public LgtvBinding() {
      }
  
      @Override
      public void activate() {
          logger.debug("Activate");
  
          Runnable checkalivetask = new Runnable() {
              public void run() {
                  // System.out.println("Hello world");
                  checkalive();
              }
          };
  
          executor = Executors.newScheduledThreadPool(1);
          executor.scheduleAtFixedRate(checkalivetask, 0, 10, TimeUnit.SECONDS);
  
      }
  
      @Override
      public void deactivate() {
          logger.debug("Deactivate");
          closeAllConnections();
          executor.shutdown();
          executor = null;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void bindingChanged(BindingProvider provider, String itemName) {
          logger.debug("bindingChanged {}", itemName);
          initializeItem(itemName);
      }
  
      /**
       * @{inheritDoc
       */
      @Override
      protected void internalReceiveCommand(String itemName, Command command) {
  
          if (itemName != null) {
              LgtvBindingProvider provider = findFirstMatchingBindingProvider(itemName, command.toString());
  
              if (provider == null) {
                  logger.warn("Doesn't find matching binding provider [itemName={}, command={}]", itemName, command);
                  return;
              }
  
              logger.debug("Received command (item='{}', state='{}', class='{}')",
                      new Object[] { itemName, command.toString(), command.getClass().toString() });
  
              String tmp = provider.getDeviceCommand(itemName, command.toString());
  
              if (tmp == null) {
                  tmp = provider.getDeviceCommand(itemName, WILDCARD_COMMAND_KEY);
              }
  
              String[] commandParts = tmp.split(":");
              String deviceId = commandParts[0];
              String deviceCmd = commandParts[1];
  
              LgtvConnection remoteController = null;
              DeviceConfig device = deviceConfigCache.get(deviceId);
              if (device == null) {
                  logger.error("cannot find device " + deviceId);
              } else {
                  remoteController = device.getConnection();
              }
  
              if (device != null && remoteController != null) {
  
                  if (deviceCmd.startsWith(ADVANCED_COMMAND_KEY)) {
  
                      // advanced command
  
                      deviceCmd = deviceCmd.replace(ADVANCED_COMMAND_KEY, "");
  
                      if (deviceCmd.contains("%")) {
  
                          // lgtv command is a template where value should be
                          // updated
                          deviceCmd = convertOpenHabCommandToDeviceCommand(command, deviceCmd);
                      }
  
                  } else {
  
                      // normal command
  
                      LgTvCommand cmd = LgTvCommand.valueOf(deviceCmd);
                      deviceCmd = cmd.getCommand();
  
                      if (deviceCmd.contains("%")) {
  
                          // eISCP command is a template where value should be
                          // updated
                          deviceCmd = convertOpenHabCommandToDeviceCommand(command, deviceCmd);
                      }
                  }
  
                  if (deviceCmd != null) {
  
                      remoteController.send(deviceCmd, command.toString());
  
                  } else {
                      logger.warn("Cannot convert value '{}' to LgTV format", command);
                  }
  
              } else {
                  logger.warn("Cannot find connection details for device id '{}'", deviceId);
              }
          }
      }
  
      /**
       * Convert OpenHAB commmand to LGTV command.
       *
       * @param command
       * @param cmdTemplate
       *
       * @return
       */
      private String convertOpenHabCommandToDeviceCommand(Command command, String cmdTemplate) {
          String deviceCmd = null;
  
          if (command instanceof OnOffType) {
              deviceCmd = String.format(cmdTemplate, command == OnOffType.ON ? 1 : 0);
  
          } else if (command instanceof StringType) {
              deviceCmd = String.format(cmdTemplate, command);
  
          } else if (command instanceof DecimalType) {
              deviceCmd = String.format(cmdTemplate, ((DecimalType) command).intValue());
  
          }
  
          return deviceCmd;
      }
  
      /**
       * Find the first matching {@link LgtvBindingProvider} according to
       * <code>itemName</code>.
       *
       * @param itemName
       *
       * @return the matching binding provider or <code>null</code> if no binding
       *         provider could be found
       */
      private LgtvBindingProvider findFirstMatchingBindingProvider(String itemName, String command) {
          LgtvBindingProvider firstMatchingProvider = null;
  
          for (LgtvBindingProvider provider : this.providers) {
              String tmp = provider.getDeviceCommand(itemName, command.toString());
              if (tmp != null) {
                  firstMatchingProvider = provider;
                  break;
              }
          }
  
          if (firstMatchingProvider == null) {
              for (LgtvBindingProvider provider : this.providers) {
                  String tmp = provider.getDeviceCommand(itemName, WILDCARD_COMMAND_KEY);
                  if (tmp != null) {
                      firstMatchingProvider = provider;
                      break;
                  }
              }
  
          }
  
          return firstMatchingProvider;
      }
  
      protected void addBindingProvider(LgtvBindingProvider bindingProvider) {
          super.addBindingProvider(bindingProvider);
      }
  
      protected void removeBindingProvider(LgtvBindingProvider bindingProvider) {
          super.removeBindingProvider(bindingProvider);
      }
  
      /**
       * {@inheritDoc}
       */
      public void updated(Dictionary<String, ?> config) throws ConfigurationException {
  
          logger.debug("Configuration updated, config {}", config != null ? true : false);
  
          if (config != null) {
              Enumeration<String> keys = config.keys();
  
              if (deviceConfigCache == null) {
                  deviceConfigCache = new HashMap<String, DeviceConfig>();
              }
  
              while (keys.hasMoreElements()) {
                  String key = keys.nextElement();
  
                  // the config-key enumeration contains additional keys that we
                  // don't want to process here ...
                  if ("service.pid".equals(key)) {
                      continue;
                  }
  
                  Matcher matcher = EXTRACT_CONFIG_PATTERN.matcher(key);
  
                  if (!matcher.matches()) {
                      logger.debug(
                              "given config key '" + key + "' does not follow the expected pattern '<id>.<host|port>'");
                      continue;
                  }
  
                  matcher.reset();
                  matcher.find();
  
                  String deviceId = matcher.group(1);
  
                  DeviceConfig deviceConfig = deviceConfigCache.get(deviceId);
  
                  if (deviceConfig == null) {
                      deviceConfig = new DeviceConfig(deviceId);
                      deviceConfigCache.put(deviceId, deviceConfig);
                  }
  
                  String configKey = matcher.group(2);
                  String value = (String) config.get(key);
  
                  if ("host".equals(configKey)) {
                      deviceConfig.host = value;
                  } else if ("port".equals(configKey)) {
                      deviceConfig.port = Integer.valueOf(value);
                  } else if ("serverport".equals(configKey)) {
                      deviceConfig.localport = Integer.valueOf(value);
                  } else if ("pairkey".equals(configKey)) {
                      deviceConfig.pairkey = value;
                  } else if ("xmldatafiles".equals(configKey)) {
                      deviceConfig.xmldatafiles = value;
                  } else if ("checkalive".equals(configKey)) {
                      deviceConfig.checkalive = Integer.valueOf(value);
                  } else {
                      throw new ConfigurationException(configKey, "the given configKey '" + configKey + "' is unknown");
                  }
  
              }
          }
  
          // open connection to all receivers
          for (String device : deviceConfigCache.keySet()) {
              LgtvConnection connection = deviceConfigCache.get(device).getConnection();
              if (connection != null) {
                  connection.addEventListener(this); // MF25012014
                  connection.openConnection();
  
              }
          }
  
          // initializeallitems();
  
      }
  
      private void initializeallitems(String currentdeviceid) {
          for (LgtvBindingProvider provider : this.providers) {
  
              for (String itemName : provider.getItemNames()) {
  
                  String initCmd = provider.getItemInitCommand(itemName);
  
                  if (initCmd != null) {
  
                      String[] commandParts = initCmd.split(":");
                      String deviceId = commandParts[0];
  
                      if (currentdeviceid == "" || deviceId.equals(currentdeviceid)) {
                          initializeItem(itemName);
                      }
  
                  }
  
              }
  
          }
      }
  
      private void checkalive() {
  
          if (deviceConfigCache != null && deviceConfigCache.size() > 0) {
              for (String device : deviceConfigCache.keySet()) {
                  // logger.debug("checkalive: device="+device);
                  LgtvConnection connection = deviceConfigCache.get(device).getConnection();
                  if (connection != null) {
                      connection.checkalive();
                  }
              }
  
          }
  
      }
  
      private void closeAllConnections() {
          if (deviceConfigCache != null) {
              for (String device : deviceConfigCache.keySet()) {
                  LgtvConnection connection = deviceConfigCache.get(device).getConnection();
                  if (connection != null) {
                      connection.closeConnection();
                      connection.removeEventListener(this);
                  }
              }
              deviceConfigCache = null;
          }
      }
  
      /**
       * Find receiver from device caache by ip address.
       *
       * @param ip
       * @return
       */
      private DeviceConfig findDevice(String ip) {
          for (String device : deviceConfigCache.keySet()) {
              DeviceConfig deviceConfig = deviceConfigCache.get(device);
              if (deviceConfig != null) {
                  if (deviceConfig.getHost().equals(ip)) {
                      return deviceConfig;
                  }
              }
          }
  
          return null;
      }
  
      // mfcheck @Override
      public void statusUpdateReceived(EventObject event, String ip, String data) {
          // find correct device from device cache
          DeviceConfig deviceConfig = findDevice(ip);
  
          if (deviceConfig != null) {
              logger.debug("Received status update '{}' from ip {} for deviceId {}", data, ip, deviceConfig.deviceId);
  
              if (data.startsWith("CONNECTION_STATUS=1")) {
                  initializeallitems(deviceConfig.deviceId);
              }
  
              String[] dataparts = data.split("=");
              String cmdname = dataparts[0];
              String cmdval = dataparts[1];
  
              for (LgtvBindingProvider provider : providers) {
                  for (String itemName : provider.getItemNames()) {
                      // Update all items which refer to command
                      HashMap<String, String> values = provider.getDeviceCommands(itemName);
  
                      for (String cmd : values.keySet()) {
                          String[] commandParts = values.get(cmd).split(":");
                          String deviceCmd = commandParts[1];
                          String deviceId = commandParts[0];
                          // logger.debug("check: "+values.get(cmd));
                          boolean match = false;
  
                          if (deviceCmd.equals(cmdname) && deviceId.equals(deviceConfig.deviceId)) {
                              match = true;
                          }
  
                          if (match) {
                              logger.debug("statusupdaterec match found itemname=" + itemName + " cmd=" + cmdname
                                      + " deviceId=" + deviceConfig.deviceId + " value=" + cmdval);
                              Class<? extends Item> itemType = provider.getItemType(itemName);
                              State v = convertDeviceValueToOpenHabState(itemType, cmdval);
                              eventPublisher.postUpdate(itemName, v);
                              // break;
                              match = false;
                          } // match
                      } // for (String cmd : values.keySet()) {
  
                  } // itemnames
              } // bindingproviders
          } // ip of device found
      } // function
  
      /**
       * Convert receiver value to OpenHAB state.
       *
       * @param itemType
       * @param data
       *
       * @return
       */
      private State convertDeviceValueToOpenHabState(Class<? extends Item> itemType, String data) {
          State state = UnDefType.UNDEF;
  
          try {
              int index;
  
              if (itemType == SwitchItem.class) {
                  index = Integer.parseInt(data);
                  state = index == 0 ? OnOffType.OFF : OnOffType.ON;
  
              } else if (itemType == NumberItem.class) {
                  index = Integer.parseInt(data);
                  state = new DecimalType(index);
  
              } else if (itemType == DimmerItem.class) {
                  index = Integer.parseInt(data);
                  state = new PercentType(index);
  
              } else if (itemType == RollershutterItem.class) {
                  index = Integer.parseInt(data);
                  state = new PercentType(index);
  
              } else if (itemType == StringItem.class) {
                  // s = data.substring(3, data.length());
                  state = new StringType(data);
              }
          } catch (Exception e) {
              logger.debug("Cannot convert value '{}' to data type {}", data, itemType);
          }
          // logger.debug("Convert value=" + data + " to openhab type="
          // + itemType.toString() + " val=" + state.toString());
          return state;
      }
  
      /**
       * Initialize item value. Method send query to receiver if init query is
       * configured to binding item configuration
       *
       * @param itemType
       *
       */
      private void initializeItem(String itemName) {
          for (LgtvBindingProvider provider : providers) {
              String initCmd = provider.getItemInitCommand(itemName);
  
              if (initCmd != null) {
  
                  String[] commandParts = initCmd.split(":");
                  String deviceId = commandParts[0];
                  String deviceCmd = commandParts[1];
  
                  LgtvConnection remoteController = null;
                  DeviceConfig device = deviceConfigCache.get(deviceId);
                  if (device == null) {
                      logger.error("cannot find device " + deviceId);
                  } else {
                      remoteController = device.getConnection();
                  }
  
                  if (remoteController == null) {
                      logger.debug("Initialize item {} canceled - remotecontroller not initialized", itemName);
                      return;
                  }
  
                  if (remoteController.ispaired() == false) {
                      logger.debug("Initialize item {} canceled - not paired", itemName);
                      return;
                  }
  
                  logger.debug("Initialize item {}", itemName);
  
                  if (device != null && remoteController != null) {
                      if (deviceCmd.startsWith(ADVANCED_COMMAND_KEY)) {
                          deviceCmd = deviceCmd.replace(ADVANCED_COMMAND_KEY, "");
                      } else {
  