/** * 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.gpio.internal; import org.openhab.binding.gpio.GPIOBindingProvider; import org.openhab.core.binding.BindingConfig; import org.openhab.core.items.Item; import org.openhab.core.library.items.ContactItem; import org.openhab.core.library.items.SwitchItem; import org.openhab.io.gpio.GPIOPin; import org.openhab.model.item.binding.AbstractGenericBindingProvider; import org.openhab.model.item.binding.BindingConfigParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <p> * Manage GPIO binding configuration. * </p> * * <p> * Allowed item types in the configuration are "Switch" and "Contact". * "Switch" is used for output pins, "Contact" - input pins. * * </p> * * <p> * Allowed item configuration string is following: * </p> * <p> * <code> * gpio="pin:PIN_NUMBER [debounce:DEBOUNCE_INTERVAL] [activelow:yes|no] [force:yes|no] [initialValue:high|low]" * </code> * </p> * <p> * where: * </p> * <p> * key-value pairs are separated by space * <br> * order of pairs isn't important, the same is valid for character's case * <br> * key "pin" is mandatory, "debounce", "activelow", "force" and "initialValue" are optional. If omitted * "activelow" is set to "no", "debounce" - to global option in openHAB, * configuration file (gpio:debounce) or 0 (zero) if neither is specified * "force" - to global option in openHAB, * configuration file (gpio:force) or "no" if neither is specified * "initialValue" - to LOW level * <br> * PIN_NUMBER is the number of the pin as seen by the kernel * <br> * DEBOUNCE_INTERVAL is the time interval in milliseconds when pin interrupts * are ignored to prevent bounce effect mainly on buttons. Note that underlying * OS isn't realtime nor the application is, so debounce implementation isn't * something on which you can rely on 100%. You need to experiment with the * value here. * </p> * * <p> * Examples: * </p> * <p> * <code> * gpio="pin:49"<br> * gpio="pin:49 debounce:10"<br> * gpio="pin:49 activelow:yes"<br> * gpio="pin:49 force:yes initialValue:high"<br> * gpio="pin:49 debounce:10 activelow:yes"</code> * </p> * * @author Dancho Penev * @since 1.5.0 */ public class GPIOGenericBindingProvider extends AbstractGenericBindingProvider implements GPIOBindingProvider { private static final Logger logger = LoggerFactory.getLogger(GPIOGenericBindingProvider.class); public String getBindingType() { return "gpio"; } public void validateItemType(Item item, String bindingConfig) throws BindingConfigParseException { /* Only 'Switch' and 'Contact' types are allowed */ if (!((item instanceof SwitchItem) || (item instanceof ContactItem))) { logger.error("Item '" + item.getName() + "' is of type '" + item.getClass().getSimpleName() + "' while only 'Switch' or 'Contact' types are allowed"); throw new BindingConfigParseException("Item '" + item.getName() + "' is of type '" + item.getClass().getSimpleName() + "' while only 'Switch' or 'Contact' types are allowed"); } } @Override public void processBindingConfiguration(String context, Item item, String bindingConfig) throws BindingConfigParseException { /* Not sure when must be called, other bindings seems to call it at the beginning */ super.processBindingConfiguration(context, item, bindingConfig); GPIOPinBindingConfig config = new GPIOPinBindingConfig(); /* Configuration string should be in the form "pin:NUMBER [debounse:NUMBER] [activelow:yes|no]" */ String[] properties = bindingConfig.split(" "); if (properties.length > 3) { logger.error("Wrong number of arguments (" + properties.length + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException("Wrong number of agruments (" + properties.length + ") in configuration string '" + bindingConfig + "'"); } for (String property : properties) { String[] keyValueStructure = property.split(":"); if (keyValueStructure.length != 2) { logger.error("Incorrect key:value structure (" + property + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException("Incorrect key:value structure (" + property + ") in configuration string '" + bindingConfig + "'"); } String key = keyValueStructure[0]; String value = keyValueStructure[1]; if (key.compareToIgnoreCase("pin") == 0) { try { config.pinNumber = Integer.parseInt(value); if (config.pinNumber < 0) { logger.error("Unsupported, negative value for pin number (" + value + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException("Unsupported, negative value for pin number (" + value + ") in configuration string '" + bindingConfig + "'"); } } catch (NumberFormatException e) { logger.error("Unsupported, not numeric value for pin number (" + value + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException("Unsupported, not numeric value for pin number (" + value + ") in configuration string '" + bindingConfig + "'"); } } else if (key.compareToIgnoreCase("force") == 0) { config.pinForce = false; if (value.compareToIgnoreCase("yes") == 0 || value.compareToIgnoreCase("true") == 0) { config.pinForce = true; } else if (value.compareToIgnoreCase("no") != 0 && value.compareToIgnoreCase("false") != 0) { logger.error("Unsupported value for force (" + value + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException("Unsupported value for force (" + value + ") in configuration string '" + bindingConfig + "'"); } } else if (key.compareToIgnoreCase("debounce") == 0) { try { config.debounceInterval = Long.parseLong(value); if (config.debounceInterval < 0) { logger.error("Unsupported, negative value for debounce (" + value + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException("Unsupported, negative value for debounce (" + value + ") in configuration string '" + bindingConfig + "'"); } } catch (NumberFormatException e) { logger.error("Unsupported, not numeric value for debounce (" + value + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException("Unsupported, not numeric value for debounce (" + value + ") in configuration string '" + bindingConfig + "'"); } } else if (key.compareToIgnoreCase("activelow") == 0) { if (value.compareToIgnoreCase("yes") == 0) { config.activeLow = GPIOPin.ACTIVELOW_ENABLED; } else if (value.compareToIgnoreCase("no") != 0) { logger.error("Unsupported value for activelow (" + value + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException("Unsupported value for activelow (" + value + ") in configuration string '" + bindingConfig + "'"); } } else if (key.compareToIgnoreCase("initialValue") == 0) { if (value.compareToIgnoreCase("high") == 0) { config.direction = GPIOPin.DIRECTION_OUT_HIGH; } else if (value.compareToIgnoreCase("low") == 0) { config.direction = GPIOPin.DIRECTION_OUT_LOW; } else { logger.error("Unsupported value for initialValue (" + value + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException("Unsupported value for initialValue (" + value + ") in configuration string '" + bindingConfig + "'"); } } else { logger.error("Unsupported key (" + key + ") in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException( "Unsupported key (" + key + ") in configuration string '" + bindingConfig + "'"); } } /* Pin number wasn't configured */ if (config.pinNumber == GPIOBindingProvider.PINNUMBER_UNDEFINED) { logger.error("Mandatory parameter (pin) is missing in configuration string '" + bindingConfig + "'"); throw new BindingConfigParseException( "Mandatory parameter (pin) is missing in configuration string '" + bindingConfig + "'"); } if (item instanceof ContactItem) { config.direction = GPIOPin.DIRECTION_IN; } addBindingConfig(item, config); } public int getPinNumber(String itemName) { GPIOPinBindingConfig config = (GPIOPinBindingConfig) bindingConfigs.get(itemName); if (config == null) { throw new IllegalArgumentException( "The item name '" + itemName + "'is invalid or the item isn't configured"); } return config.pinNumber; } public boolean getPinForce(String itemName) { GPIOPinBindingConfig config = (GPIOPinBindingConfig) bindingConfigs.get(itemName); if (config == null) { throw new IllegalArgumentException( "The item name '" + itemName + "'is invalid or the item isn't configured"); } return config.pinForce; } public long getDebounceInterval(String itemName) { GPIOPinBindingConfig config = (GPIOPinBindingConfig) bindingConfigs.get(itemName); if (config == null) { throw new IllegalArgumentException( "The item name '" + itemName + "'is invalid or the item isn't configured"); } return config.debounceInterval; } public int getActiveLow(String itemName) { GPIOPinBindingConfig config = (GPIOPinBindingConfig) bindingConfigs.get(itemName); if (config == null) { throw new IllegalArgumentException( "The item name '" + itemName + "'is invalid or the item isn't configured"); } return config.activeLow; } public int getDirection(String itemName) { GPIOPinBindingConfig config = (GPIOPinBindingConfig) bindingConfigs.get(itemName); if (config == null) { throw new IllegalArgumentException( "The item name '" + itemName + "'is invalid or the item isn't configured"); } return config.direction; } public boolean isItemConfigured(String itemName) { if (bindingConfigs.containsKey(itemName)) { return true; } return false; } /** * GPIO binding configuration data structure. * * @author Dancho Penev * @since 1.3.1 */ public class GPIOPinBindingConfig implements BindingConfig { /** Configured pin number */ public int pinNumber = GPIOBindingProvider.PINNUMBER_UNDEFINED; /** Configured pin force */ public boolean pinForce = false; /** Configured pin debounce interval in milliseconds */ public long debounceInterval = GPIOBindingProvider.DEBOUNCEINTERVAL_UNDEFINED; /** Configured activelow state */ public int activeLow = GPIOPin.ACTIVELOW_DISABLED; /** * Pin direction. If item type is <code>Switch</code> the pin * direction is out, if <code>Contact</code> - in */ public int direction = GPIOPin.DIRECTION_OUT_LOW; } }