/** * 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.hdanywhere.internal; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.openhab.binding.hdanywhere.HDanywhereBindingProvider; import org.openhab.core.binding.BindingConfig; import org.openhab.core.items.Item; import org.openhab.core.library.items.NumberItem; import org.openhab.model.item.binding.AbstractGenericBindingProvider; import org.openhab.model.item.binding.BindingConfigParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation class of the HDanywhere Binding Provider * * hdanywhere="[192.168.0.88:1:15]" - Number item where input/output value corresponds to a * source port number that is/to be attached to given destination port. The format is * "hdanywhere ip address":"destination port":"refresh interval". The refresh interval is used * to poll the hdanywhere device for the current source/destination mapping * * hdanywhere="[192.168.0.88:1:15], [192.168.0.88:2:15]" - when the Number item is updated, both output * ports 1 and 2 will be switched to the given source port number. If the bindings polls for * the current source/destination map, then the value of the Number item can oscillate if the * the destination ports are connected to the same source port * * * @author Karel Goderis * @since 1.4.0 */ public class HDanywhereGenericBindingProvider extends AbstractGenericBindingProvider implements HDanywhereBindingProvider { static final Logger logger = LoggerFactory.getLogger(HDanywhereGenericBindingProvider.class); static int counter = 0; /** {@link Pattern} which matches a binding configuration part */ private static final Pattern STATUS_CONFIG_PATTERN = Pattern.compile("\\[(.*):(.*):(.*)\\]"); /** * {@inheritDoc} */ @Override public String getBindingType() { return "hdanywhere"; } /** * {@inheritDoc} */ @Override public void validateItemType(Item item, String bindingConfig) throws BindingConfigParseException { if (!(item instanceof NumberItem)) { throw new BindingConfigParseException( "item '" + item.getName() + "' is of type '" + item.getClass().getSimpleName() + "', only NumberItems are allowed - please check your *.items configuration"); } } /** * {@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(getBindingType() + " bindingConfig is NULL (item=" + item + ") -> processing bindingConfig aborted!"); } } private void parseAndAddBindingConfig(Item item, String bindingConfigs) throws BindingConfigParseException { String bindingConfig = StringUtils.substringBefore(bindingConfigs, ","); String bindingConfigTail = StringUtils.substringAfter(bindingConfigs, ","); HDanywhereBindingConfig newConfig = new HDanywhereBindingConfig(); 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 configuration string and update the provided config * * @param config * @param item * @param bindingConfig * @throws BindingConfigParseException */ private void parseBindingConfig(HDanywhereBindingConfig config, Item item, String bindingConfig) throws BindingConfigParseException { String host = null; int port = 0; int interval = 60; if (bindingConfig != null) { Matcher statusMatcher = STATUS_CONFIG_PATTERN.matcher(bindingConfig); if (!statusMatcher.matches()) { throw new BindingConfigParseException( "HDanywhere binding configuration must consist of either three [config=" + statusMatcher + "] parts"); } else { host = statusMatcher.group(1); port = Integer.valueOf(statusMatcher.group(2)); interval = Integer.valueOf(statusMatcher.group(3)); HDanywhereBindingConfigElement newElement = new HDanywhereBindingConfigElement(host, port, interval); config.add(newElement); } } } /** * This is an internal data structure to track * {@link ProtocolBindingConfigElement }. */ static class HDanywhereBindingConfig extends ArrayList<HDanywhereBindingConfigElement>implements BindingConfig { private static final long serialVersionUID = -7252828812548386063L; } public static class HDanywhereBindingConfigElement implements BindingConfig { final private String host; final private int port; final private int interval; /** * @return the hostname of the HDanwyhere matrix */ public String getHost() { return host; } /** * @return the matrix output port number */ public int getPort() { return port; } /** * @return the interval to poll the HDanywhere matrix for status updates */ public int getInterval() { return interval; } public HDanywhereBindingConfigElement(String host, int port, int interval) { this.host = host; this.port = port; this.interval = interval; } @Override public String toString() { return "HDanywhereBindingConfigElement [host=" + host + ", port=" + port + ", interval=" + interval + "]"; } } /** * {@inheritDoc} */ @Override public Boolean autoUpdate(String itemName) { return false; } @Override public List<String> getHosts(String itemName) { List<String> hosts = new ArrayList<String>(); for (String anItem : bindingConfigs.keySet()) { HDanywhereBindingConfig aConfig = (HDanywhereBindingConfig) bindingConfigs.get(anItem); Iterator<HDanywhereBindingConfigElement> iterator = aConfig.iterator(); while (iterator.hasNext()) { HDanywhereBindingConfigElement anElement = iterator.next(); if (!hosts.contains(anElement.getHost())) { hosts.add(anElement.getHost()); } } } return hosts; } @Override public List<Integer> getPorts(String host, String itemName) { List<Integer> ports = new ArrayList<Integer>(); HDanywhereBindingConfig aConfig = (HDanywhereBindingConfig) bindingConfigs.get(itemName); Iterator<HDanywhereBindingConfigElement> iterator = aConfig.iterator(); while (iterator.hasNext()) { HDanywhereBindingConfigElement anElement = iterator.next(); if (anElement.getHost().equals(host)) { if (!ports.contains(anElement.getPort())) { ports.add(anElement.getPort()); } } } return ports; } @Override public HashMap<String, Integer> getIntervalList() { HashMap<String, Integer> result = new HashMap<String, Integer>(); Collection<String> items = getItemNames(); Iterator<String> itemIterator = items.iterator(); while (itemIterator.hasNext()) { String anItem = itemIterator.next(); HDanywhereBindingConfig aConfig = (HDanywhereBindingConfig) bindingConfigs.get(anItem); Iterator<HDanywhereBindingConfigElement> iterator = aConfig.iterator(); while (iterator.hasNext()) { HDanywhereBindingConfigElement anElement = iterator.next(); boolean found = false; if (result.containsKey(anElement.getHost())) { found = true; if (result.get(anElement.getHost()) > anElement.getInterval()) { result.put(anElement.getHost(), anElement.getInterval()); break; } } if (!found) { result.put(anElement.getHost(), anElement.getInterval()); } } } return result; } }