Package org.openhab.binding.pioneeravr.internal

Source Code of org.openhab.binding.pioneeravr.internal.PioneerAvrBinding$DeviceConfig

/**
* Copyright (c) 2010-2014, openHAB.org and others.
*
* 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.pioneeravr.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.pioneeravr.PioneerAvrBindingProvider;
import org.openhab.binding.pioneeravr.internal.ipcontrolprotocol.IpControl;
import org.openhab.binding.pioneeravr.internal.ipcontrolprotocol.IpControlCommand;
import org.openhab.binding.pioneeravr.internal.ipcontrolprotocol.IpControlCommandRef;
import org.openhab.binding.pioneeravr.internal.ipcontrolprotocol.IpControlDisplayInformation;
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 pioneerav devices when certain
* commands are received.
*
* @author Rainer Ostendorf
* @author based on the Onkyo binding by Pauli Anttila and others
* @since 1.4.0
*/
public class PioneerAvrBinding extends AbstractBinding<PioneerAvrBindingProvider> implements ManagedService, BindingChangeListener, PioneerAvrEventListener {

  private static final Logger logger = LoggerFactory.getLogger(PioneerAvrBinding.class);

  protected static final String ADVANCED_COMMAND_KEY = "#";
  protected static final String WILDCARD_COMMAND_KEY = "*";

  /** RegEx to validate a config <code>'^(.*?)\\.(host|port|checkconn)$'</code> */
  private static final Pattern EXTRACT_CONFIG_PATTERN = Pattern.compile("^(.*?)\\.(host|port|checkconn)$");
 
  /** pioneerav receiver default tcp port */
  private final static int DEFAULT_PORT = IpControl.DEFAULT_IPCONTROL_PORT;

  /** Map table to store all available receivers configured by the user */
  protected Map<String, DeviceConfig> deviceConfigCache = null;
 
  public PioneerAvrBinding() {
  }

  public void activate() {
    logger.debug("Activate");
  }

  public void deactivate() {
    logger.debug("Deactivate");
    closeAllConnections();
  }

  /**
   * {@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) {
      PioneerAvrBindingProvider 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];
     
      DeviceConfig device = deviceConfigCache.get(deviceId);
      PioneerAvrConnection 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("%")) {

            deviceCmd = convertOpenHabCommandToDeviceCommand( command, deviceCmd);
          }
         
        } else {
          // normal command
          IpControlCommand cmd = IpControlCommand.valueOf(deviceCmd);
          deviceCmd = cmd.getCommand();
         
          if (deviceCmd.contains("%")) {
            deviceCmd = convertOpenHabCommandToDeviceCommand( command, deviceCmd);
          }
        }
       
        if (deviceCmd != null) {
          remoteController.send(deviceCmd);
        } else {
          logger.warn("Cannot convert value '{}' to IpControl format", command);
        }

      } else {
        logger.warn("Cannot find connection details for device id '{}'", deviceId);
      }
    }
  }
 
  /**
   * Convert OpenHAB commmand to pioneer avr receiver command.
   *
   * @param command the openhab command to send
   * @param cmdTemplate the format string to used for converting the command
   *
   * @return the receiver command string
   */
  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 PercentType) {
     
      // when settings the volume as percent, we need to map it
      if( cmdTemplate.equals( IpControlCommand.VOLUME_SET.getCommand() ) ) {
        Integer percentValue = convertPercentToVolume(((DecimalType) command).intValue());
        deviceCmd = String.format(cmdTemplate, percentValue );
      }
      else {
        deviceCmd = String.format(cmdTemplate, ((DecimalType) command).intValue());
      }
     
    } else if (command instanceof DecimalType) {
      deviceCmd = String.format(cmdTemplate, ((DecimalType) command).intValue());  
    }   
   

    return deviceCmd;
  }

  /**
   * Find the first matching {@link PioneerAvrBindingProvider} according to
   * <code>itemName</code>.
   *
   * @param itemName
   *
   * @return the matching binding provider or <code>null</code> if no binding
   *         provider could be found
   */
  private PioneerAvrBindingProvider findFirstMatchingBindingProvider(String itemName, String command) {
    PioneerAvrBindingProvider firstMatchingProvider = null;

    for (PioneerAvrBindingProvider provider : this.providers) {
      String tmp = provider.getDeviceCommand(itemName, command.toString());
      if (tmp != null) {
        firstMatchingProvider = provider;
        break;
      }
    }

    if (firstMatchingProvider == null) {
      for (PioneerAvrBindingProvider provider : this.providers) {
        String tmp = provider.getDeviceCommand(itemName, WILDCARD_COMMAND_KEY);
        if (tmp != null) {
          firstMatchingProvider = provider;
          break;
        }
      }

    }
   
    return firstMatchingProvider;
  }

  /**
   * @{inheritDoc
   */
  @Override
  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 = (String) 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|checkconn>'");
          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 ("checkconn".equals(configKey)) {
          if( value.equals("0") )
            deviceConfig.connectionCheckActive = false;
          else
            deviceConfig.connectionCheckActive = true
        } else {
          throw new ConfigurationException(configKey, "the given configKey '" + configKey + "' is unknown");
        }
      }
     
      // open connection to all receivers
      for (String device : deviceConfigCache.keySet()) {
        PioneerAvrConnection connection = deviceConfigCache.get(device).getConnection();
        if (connection != null) {
          connection.openConnection();
          connection.addEventListener(this);
        }
      }
     
      for (PioneerAvrBindingProvider provider : this.providers) {
        for (String itemName : provider.getItemNames()) {
          initializeItem(itemName);
        }
      }

    }
  }

  private void closeAllConnections() {
    if (deviceConfigCache != null) {
      for (String device : deviceConfigCache.keySet()) {
        PioneerAvrConnection 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;
  }
 
  @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 device {}", data, deviceConfig.host);

      for (PioneerAvrBindingProvider 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];
            boolean match = false;
           
            if( deviceCmd.startsWith(ADVANCED_COMMAND_KEY) )
            {
              // we currently have no info about the expected response string
              // of a user configured "advanced" command, so skip this item
              continue;
            }
           
            try {
              String commandResponse = IpControlCommand.valueOf(deviceCmd).getResponse();
             
              // when no respone is expected, the response string is empty
              if( !commandResponse.isEmpty() ) {
                // compare response from network with response in enum               
                if (data.startsWith(commandResponse)) {
                  match = true;
               
              }
             
            } catch (Exception e) {
              logger.error("Unregonized command '" + deviceCmd + "'", e);
            }
           

            if (match) {
              Class<? extends Item> itemType = provider.getItemType(itemName);
              State v = convertDeviceValueToOpenHabState(itemType, data, IpControlCommand.valueOf(deviceCmd) );
              eventPublisher.postUpdate(itemName, v);
              break;
            }
          }
        }
      }
    }
  }

  /**
   * Convert receiver value to OpenHAB state.
   *
   * @param itemType
   * @param data
   *
   * @return
   */
  private State convertDeviceValueToOpenHabState(Class<? extends Item> itemType, String data, IpControlCommand cmdType) {
    State state = UnDefType.UNDEF;

    try {
      int index;
     
      // cut off the leading response identifier to get the payload string      
      String payloadSubstring = data.substring( cmdType.getResponse().length() );
     
      // if the response consisted just of the response-identifier and had
      // no further value attached handle it as boolean of value 1
      // this is used e.g. when switch items are used for selecting the source.
      // the selected source will then be set to "ON"
      if( payloadSubstring.length() == 0)
        payloadSubstring = "1";
     
      // special case for display info query: convert to human readable string
      if( cmdType.getCommandRef() == IpControlCommandRef.DISPLAY_INFO_QUERY )
      {
        IpControlDisplayInformation displayInfo = new IpControlDisplayInformation( payloadSubstring );
        payloadSubstring = displayInfo.getInfoText();       
        logger.debug("DisplayInfo: converted value '{}' to string '{}'", data, payloadSubstring );
      }
         
      if (itemType == SwitchItem.class) {
        index = Integer.parseInt(payloadSubstring);
        state = (index == 0) ? OnOffType.ON : OnOffType.OFF; // according to Spec: 0=ON, 1=OFF!
     
      
      else if (itemType == DimmerItem.class) {
       
        if( cmdType.getCommandRef().getCommand() == IpControlCommandRef.VOLUME_QUERY.getCommand() ||
          cmdType.getCommandRef().getCommand() == IpControlCommandRef.VOLUME_SET.getCommand())  {
          index = convertVolumeToPercent( Integer.parseInt(payloadSubstring) );
        }
        else {
          index = Integer.parseInt(payloadSubstring);
        }
        state = new PercentType(index);
       
      } else if (itemType == NumberItem.class) {
          index = Integer.parseInt(payloadSubstring);
          state = new DecimalType(index);
       
      } else if (itemType == RollershutterItem.class) {
        index = Integer.parseInt(payloadSubstring);
        state = new PercentType(index);
       
      } else if (itemType == StringItem.class) {
        state = new StringType(payloadSubstring);
      }
    } catch (Exception e) {
      logger.debug("Cannot convert value '{}' to data type {}", data, itemType);
    }
   
    return state;
  }
 
  /**
   * map the receiver volume values to percent values
   *
   * receiver volume 0 is -80db and 0%
   * receiver volume 185 is +12dB and 100% (at least for zone 1)
   *
   * @param volume the receiver volume value
   *
   */
  private Integer convertVolumeToPercent( Integer volume )  {
    Integer percent = Math.round( (volume*100)/185 );
    logger.debug("converted volume '" +  volume.toString() + "' to '" + percent.toString() + "%'" );
    return percent;
  }
 
  /**
   * map percent values to receiver volumes
   *
   * receiver volume 0 is -80db and 0%
   * receiver volume 185 is +12dB and 100% (at least for zone 1)
   *
   * @param volume the receiver volume value
   *
   */
  private Integer convertPercentToVolume( Integer percent )  {
    Integer volume = Math.round( (percent*185)/100 );
    logger.debug("converted " +  percent.toString() + "% to volume " + volume.toString() );
    return volume;
  }
 
 
  /**
   * 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 (PioneerAvrBindingProvider provider : providers) {
      String initCmd = provider.getItemInitCommand(itemName);
      if (initCmd != null) {
        logger.debug("Initialize item {}", itemName);

        String[] commandParts = initCmd.split(":");
        String deviceId = commandParts[0];
        String deviceCmd = commandParts[1];

        DeviceConfig device = deviceConfigCache.get(deviceId);
        PioneerAvrConnection remoteController = device.getConnection();

        if (device != null && remoteController != null) {
          if (deviceCmd.startsWith(ADVANCED_COMMAND_KEY)) {
            deviceCmd = deviceCmd.replace(ADVANCED_COMMAND_KEY, "");
          } else {
            IpControlCommand cmd = IpControlCommand.valueOf(deviceCmd);
            deviceCmd = cmd.getCommand();
          }

          remoteController.send(deviceCmd);
        } else {
          logger.warn(
              "Cannot find connection details for device id '{}'",
              deviceId);
        }
      }
    }
  }
 

  /**
   * Internal data structure which carries the connection details of one
   * device (there could be several)
   */
  static class DeviceConfig {

    String host;
    int port = DEFAULT_PORT;

    PioneerAvrConnection connection = null;
    String deviceId;
    Boolean connectionCheckActive;

    public DeviceConfig(String deviceId) {
      this.deviceId = deviceId;
      connectionCheckActive = true; // by default, the conn check is active
    }

    public String getHost(){
      return host;
    }
   
    public int getPort(){
      return port;
    }
   
   
   
    @Override
    public String toString() {
      return "Device [id=" + deviceId + ", host=" + host + ", port=" + port + "]";
    }

    PioneerAvrConnection getConnection() {
      if (connection == null) {
        connection = new PioneerAvrConnection(host, port, connectionCheckActive);
      }
      return connection;
    }

  }

}
TOP

Related Classes of org.openhab.binding.pioneeravr.internal.PioneerAvrBinding$DeviceConfig

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.