/**
* Copyright (c) 2010-2016 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.irtrans.internal;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.irtrans.IRtransBindingProvider;
import org.openhab.binding.tcp.Direction;
import org.openhab.core.binding.BindingConfig;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.TypeParser;
import org.openhab.model.item.binding.AbstractGenericBindingProvider;
import org.openhab.model.item.binding.BindingConfigParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Class IRtransGenericBindingProvider.
*
* @author Karel Goderis
* @since 1.4.0
*
*
* irtrans="[ON:192.168.0.1:3000:1:Samsung:Play], [OFF:192.168.0.1:3000:2:Samsung:Pause]"
*
* [ON:192.168.0.1:3000:1:Samsung:Play] - send the Play command for the remote Samsung type
* [ON:192.168.0.1:3000:1:Samsung:*] - accept any command from remote Samsung type
* [ON:192.168.0.1:3000:*:Samsung:Play] - send command to all leds
* [ON:192.168.0.1:3000:1:*:*] - accept all commands from any type of remote
* [192.168.0.1:3000:1:*:*] - for String Items, take or update 'remote,command' strings that match the pattern
*/
public class IRtransGenericBindingProvider extends AbstractGenericBindingProvider implements IRtransBindingProvider {
/** The Constant logger. */
static final Logger logger = LoggerFactory.getLogger(IRtransGenericBindingProvider.class);
/** The patterns {@link Pattern} which matches the binding configuration parts */
private static final Pattern ACTION_CONFIG_PATTERN = Pattern.compile("\\[(.*):(.*):(.*):(.*):(.*):(.*)\\]");
private static final Pattern STATUS_CONFIG_PATTERN = Pattern.compile("\\[(.*):(.*):(.*):(.*):(.*)\\]");
static int counter = 0;
/**
* {@inheritDoc}
*/
@Override
public String getBindingType() {
return "irtrans";
}
/**
* {@inheritDoc}
*/
@Override
public void validateItemType(Item item, String bindingConfig) throws BindingConfigParseException {
}
/**
* {@inheritDoc}
*/
@Override
public void processBindingConfiguration(String context, Item item, String bindingConfig)
throws BindingConfigParseException {
super.processBindingConfiguration(context, item, bindingConfig);
if (bindingConfig != null) {
parseAndAddBindingConfig(item, bindingConfig);
} else {
logger.warn("bindingConfig is NULL (item=" + item + ") -> processing bindingConfig aborted!");
}
}
/**
* Parses the and add binding config.
*
* @param item the item
* @param bindingConfigs the binding configs
* @throws BindingConfigParseException the binding config parse exception
*/
private void parseAndAddBindingConfig(Item item, String bindingConfigs) throws BindingConfigParseException {
String bindingConfig = StringUtils.substringBefore(bindingConfigs, ",");
String bindingConfigTail = StringUtils.substringAfter(bindingConfigs, ",");
IRtransBindingConfig newConfig = new IRtransBindingConfig();
parseBindingConfig(newConfig, item, bindingConfig);
addBindingConfig(item, newConfig);
while (StringUtils.isNotBlank(bindingConfigTail)) {
bindingConfig = StringUtils.substringBefore(bindingConfigTail, ",");
bindingConfig = StringUtils.strip(bindingConfig);
bindingConfigTail = StringUtils.substringAfter(bindingConfig, ",");
parseBindingConfig(newConfig, item, bindingConfig);
addBindingConfig(item, newConfig);
}
}
/**
* Parses the binding config.
*
* @param config the config
* @param item the item
* @param bindingConfig the binding config
* @throws BindingConfigParseException the binding config parse exception
*/
private void parseBindingConfig(IRtransBindingConfig config, Item item, String bindingConfig)
throws BindingConfigParseException {
String commandAsString = null;
String host = null;
String port = null;
String led = null;
String remote = null;
String irCommand = null;
Leds ledType = Leds.DEFAULT;
if (bindingConfig != null) {
Matcher actionMatcher = ACTION_CONFIG_PATTERN.matcher(bindingConfig);
Matcher statusMatcher = STATUS_CONFIG_PATTERN.matcher(bindingConfig);
if ((!actionMatcher.matches() && !statusMatcher.matches())) {
throw new BindingConfigParseException(
getBindingType() + " binding configuration must consist of five [config="
+ statusMatcher.pattern() + "] or six parts [config=" + actionMatcher.pattern() + "]");
} else {
if (actionMatcher.matches()) {
commandAsString = actionMatcher.group(1);
host = actionMatcher.group(2);
port = actionMatcher.group(3);
led = actionMatcher.group(4);
remote = actionMatcher.group(5);
irCommand = actionMatcher.group(6);
} else if (statusMatcher.matches()) {
host = statusMatcher.group(1);
port = statusMatcher.group(2);
led = statusMatcher.group(3);
remote = statusMatcher.group(4);
irCommand = statusMatcher.group(5);
}
if (led.equals("*")) {
ledType = Leds.ALL;
} else {
ledType = Leds.valueOf(led);
}
IRtransBindingConfigElement newElement = new IRtransBindingConfigElement(host, port, ledType, remote,
irCommand, item.getAcceptedDataTypes());
Command command = null;
if (commandAsString == null) {
// for those configuration strings that are not really linked to a openHAB command we
// create a dummy Command to be able to store the configuration information
// I have choosen to do that with NumberItems
NumberItem dummy = new NumberItem(Integer.toString(counter));
command = createCommandFromString(dummy, Integer.toString(counter));
counter++;
config.put(command, newElement);
} else {
command = createCommandFromString(item, commandAsString);
config.put(command, newElement);
}
config.put(command, newElement);
}
} else {
return;
}
}
/**
* Creates a {@link Command} out of the given <code>commandAsString</code>
* incorporating the {@link TypeParser}.
*
* @param item the item
* @param commandAsString the command as string
* @return an appropriate Command (see {@link TypeParser} for more
* information
* @throws BindingConfigParseException if the {@link TypeParser} couldn't
* create a command appropriately
* @see {@link TypeParser}
*/
private Command createCommandFromString(Item item, String commandAsString) throws BindingConfigParseException {
Command command = TypeParser.parseCommand(item.getAcceptedCommandTypes(), commandAsString);
if (command == null) {
throw new BindingConfigParseException("couldn't create Command from '" + commandAsString + "' ");
}
return command;
}
/**
* This is an internal data structure to map commands to.
*
* {@link IRtransBindingConfigElement }. There will be map like
* <code>ON->IRtransBindingConfigElement</code>
*/
static class IRtransBindingConfig extends HashMap<Command, IRtransBindingConfigElement>implements BindingConfig {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 7225634469734836948L;
}
/**
* The Class IRtransBindingConfigElement.
*/
static class IRtransBindingConfigElement implements BindingConfig {
/** The host. */
final private String host;
/** The port. */
final private String port;
/** The led. */
final private Leds led;
/** The remote. */
final private String remote;
/** The ir command. */
final private String irCommand;
final private List<Class<? extends State>> acceptedTypes;
/**
* Instantiates a new ir trans binding config element.
*
* @param host the host
* @param port the port
* @param led the led
* @param direction the direction
* @param remote the remote
* @param irCommand the ir command
*/
public IRtransBindingConfigElement(String host, String port, Leds led, String remote, String irCommand,
List<Class<? extends State>> acceptedTypes) {
this.host = host;
this.port = port;
this.led = led;
this.remote = remote;
this.irCommand = irCommand;
this.acceptedTypes = acceptedTypes;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "IRtransBindingConfigElement [" + ", host=" + host + ", port=" + port + ", led=" + led + ", remote="
+ remote + ", ir=" + irCommand + "]";
}
/**
* Gets the remote.
*
* @return the remote
*/
public String getRemote() {
return remote;
}
/**
* Gets the ir command.
*
* @return the irCommand
*/
public String getIrCommand() {
return irCommand;
}
/**
* Gets the host.
*
* @return the host
*/
public String getHost() {
return host;
}
/**
* Gets the port.
*
* @return the port
*/
public String getPort() {
return port;
}
/**
* Gets the led.
*
* @return the led
*/
public Leds getLed() {
return led;
}
/**
* @return the list of accepted DataTypes for the Item linked to this Binding Config Element
*/
public List<Class<? extends State>> getAcceptedTypes() {
return acceptedTypes;
}
}
/**
* {@inheritDoc}
*/
@Override
public String getHost(String itemName, Command command) {
IRtransBindingConfig config = (IRtransBindingConfig) bindingConfigs.get(itemName);
return config != null && config.get(command) != null ? config.get(command).getHost() : null;
}
/**
* {@inheritDoc}
*/
@Override
public int getPort(String itemName, Command command) {
IRtransBindingConfig config = (IRtransBindingConfig) bindingConfigs.get(itemName);
return config != null && config.get(command) != null ? Integer.parseInt(config.get(command).getPort()) : null;
}
/**
* {@inheritDoc}
*/
@Override
public Leds getLed(String itemName, Command command) {
IRtransBindingConfig config = (IRtransBindingConfig) bindingConfigs.get(itemName);
return config != null && config.get(command) != null ? config.get(command).getLed() : null;
}
/**
* {@inheritDoc}
*/
@Override
public String getRemote(String itemName, Command command) {
IRtransBindingConfig config = (IRtransBindingConfig) bindingConfigs.get(itemName);
return config != null && config.get(command) != null ? config.get(command).getRemote() : null;
}
/**
* {@inheritDoc}
*/
@Override
public String getIrCommand(String itemName, Command command) {
IRtransBindingConfig config = (IRtransBindingConfig) bindingConfigs.get(itemName);
return config != null && config.get(command) != null ? config.get(command).getIrCommand() : null;
}
/**
* {@inheritDoc}
*/
@Override
public InetSocketAddress getInetSocketAddress(String itemName, Command command) {
InetSocketAddress socketAddress = null;
try {
socketAddress = new InetSocketAddress(InetAddress.getByName(getHost(itemName, command)),
getPort(itemName, command));
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return socketAddress;
}
/**
* {@inheritDoc}
*/
@Override
public List<InetSocketAddress> getInetSocketAddresses(String itemName) {
List<InetSocketAddress> theList = new ArrayList<InetSocketAddress>();
for (Command command : getAllCommands(itemName)) {
InetSocketAddress anAddress = null;
try {
anAddress = new InetSocketAddress(InetAddress.getByName(getHost(itemName, command)),
getPort(itemName, command));
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
theList.add(anAddress);
}
return theList;
}
/**
* {@inheritDoc}
*/
@Override
public List<String> getItemNames(String remote, String irCommand) {
List<String> itemNames = new ArrayList<String>();
for (String itemName : bindingConfigs.keySet()) {
IRtransBindingConfig aConfig = (IRtransBindingConfig) bindingConfigs.get(itemName);
for (Command aCommand : aConfig.keySet()) {
IRtransBindingConfigElement anElement = aConfig.get(aCommand);
if (anElement.remote.equals(remote) && anElement.irCommand.equals(irCommand)) {
itemNames.add(itemName);
}
}
}
return itemNames;
}
/**
* {@inheritDoc}
*/
@Override
public Collection<String> getItemNames(String host, int port) {
List<String> items = new ArrayList<String>();
for (String itemName : bindingConfigs.keySet()) {
IRtransBindingConfig aConfig = (IRtransBindingConfig) bindingConfigs.get(itemName);
for (Command aCommand : aConfig.keySet()) {
IRtransBindingConfigElement anElement = aConfig.get(aCommand);
if (anElement.getHost().equals(host) && anElement.getPort() == Integer.toString(port)) {
if (!items.contains(itemName)) {
items.add(itemName);
}
}
}
}
return items;
}
/**
* {@inheritDoc}
*/
@Override
public List<Command> getAllCommands(String itemName) {
List<Command> commands = new ArrayList<Command>();
IRtransBindingConfig aConfig = (IRtransBindingConfig) bindingConfigs.get(itemName);
for (Command aCommand : aConfig.keySet()) {
commands.add(aCommand);
}
return commands;
}
/**
* {@inheritDoc}
*/
@Override
public List<Command> getAllCommands(String itemName, String remote, String irCommand) {
List<Command> commands = new ArrayList<Command>();
IRtransBindingConfig aConfig = (IRtransBindingConfig) bindingConfigs.get(itemName);
for (Command aCommand : aConfig.keySet()) {
IRtransBindingConfigElement anElement = aConfig.get(aCommand);
if (anElement.remote.equals(remote) && anElement.irCommand.equals(irCommand)) {
commands.add(aCommand);
}
}
return commands;
}
/**
* {@inheritDoc}
*/
@Override
public List<Command> getQualifiedCommands(String itemName, Command command) {
List<Command> commands = new ArrayList<Command>();
IRtransBindingConfig aConfig = (IRtransBindingConfig) bindingConfigs.get(itemName);
for (Command aCommand : aConfig.keySet()) {
if (aCommand == command) {
commands.add(aCommand);
} else {
if (aCommand instanceof DecimalType) {
commands.add(aCommand);
}
}
}
return commands;
}
/**
* {@inheritDoc}
*/
@Override
public List<Class<? extends State>> getAcceptedDataTypes(String itemName, Command command) {
if (itemName != null) {
IRtransBindingConfig config = (IRtransBindingConfig) bindingConfigs.get(itemName);
if (config != null) {
IRtransBindingConfigElement element = config.get(command);
if (element != null) {
return element.getAcceptedTypes();
}
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public String getPortAsString(String itemName, Command command) {
IRtransBindingConfig config = (IRtransBindingConfig) bindingConfigs.get(itemName);
return config != null && config.get(command) != null ? config.get(command).getPort() : null;
}
/**
* {@inheritDoc}
*/
@Override
public Direction getDirection(String itemName, Command command) {
// the IRtrans binding will only establish outbound connections to the remote IRtrans device
return Direction.OUT;
}
}