/** * 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.serial.internal; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.openhab.core.events.AbstractEventSubscriber; import org.openhab.core.events.EventPublisher; import org.openhab.core.items.Item; 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.OnOffType; import org.openhab.core.library.types.StopMoveType; import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.UpDownType; import org.openhab.core.transform.TransformationService; import org.openhab.core.types.Command; import org.openhab.core.types.State; import org.openhab.model.item.binding.BindingConfigParseException; import org.openhab.model.item.binding.BindingConfigReader; /** * <p> * This class implements a binding of serial devices to openHAB. * The binding configurations are provided by the {@link GenericItemProvider}. * </p> * * <p> * The format of the binding configuration is simple and looks like this: * </p> * serial="<port>" where <port> is the identification of the serial port on the host system, e.g. * "COM1" on Windows, "/dev/ttyS0" on Linux or "/dev/tty.PL2303-0000103D" on Mac * <p> * Switch items with this binding will receive an ON-OFF update on the bus, whenever data becomes available on the * serial interface<br/> * String items will receive the submitted data in form of a string value as a status update, while openHAB commands to * a Switch item is * sent out as data through the serial interface. * </p> * * @author Kai Kreuzer * */ public class SerialBinding extends AbstractEventSubscriber implements BindingConfigReader { private Map<String, SerialDevice> serialDevices = new HashMap<String, SerialDevice>(); /** * stores information about the which items are associated to which port. The map has this content structure: * itemname -> port */ private Map<String, String> itemMap = new HashMap<String, String>(); /** * stores information about the context of items. The map has this content structure: context -> Set of itemNames */ private Map<String, Set<String>> contextMap = new HashMap<String, Set<String>>(); private EventPublisher eventPublisher = null; private TransformationService transformationService; public void setEventPublisher(EventPublisher eventPublisher) { this.eventPublisher = eventPublisher; for (SerialDevice serialDevice : serialDevices.values()) { serialDevice.setEventPublisher(eventPublisher); } } public void unsetEventPublisher(EventPublisher eventPublisher) { this.eventPublisher = null; for (SerialDevice serialDevice : serialDevices.values()) { serialDevice.setEventPublisher(null); } } public void setTransformationService(TransformationService transformationService) { this.transformationService = transformationService; for (SerialDevice serialDevice : serialDevices.values()) { serialDevice.setTransformationService(transformationService); } } public void unsetTransformationService(TransformationService transformationService) { this.transformationService = null; for (SerialDevice serialDevice : serialDevices.values()) { serialDevice.setTransformationService(null); } } /** * {@inheritDoc} */ @Override public void receiveCommand(String itemName, Command command) { if (itemMap.keySet().contains(itemName)) { SerialDevice serialDevice = serialDevices.get(itemMap.get(itemName)); if (command instanceof StringType) { serialDevice.writeString(command.toString()); } else if (command instanceof OnOffType) { if (command == OnOffType.ON) { serialDevice.writeString(serialDevice.getOnCommand(itemName)); } else { serialDevice.writeString(serialDevice.getOffCommand(itemName)); } } else if (command instanceof UpDownType) { if (command == UpDownType.UP) { serialDevice.writeString(serialDevice.getUpCommand(itemName)); } else { serialDevice.writeString(serialDevice.getDownCommand(itemName)); } } else if (command instanceof StopMoveType) { if (command == StopMoveType.STOP) { serialDevice.writeString(serialDevice.getStopCommand(itemName)); } } } } /** * {@inheritDoc} */ @Override public void receiveUpdate(String itemName, State newStatus) { // ignore any updates } /** * {@inheritDoc} */ @Override public String getBindingType() { return "serial"; } /** * {@inheritDoc} */ @Override public void validateItemType(Item item, String bindingConfig) throws BindingConfigParseException { if (!(item instanceof SwitchItem || item instanceof StringItem || item instanceof NumberItem || item instanceof RollershutterItem)) { throw new BindingConfigParseException("item '" + item.getName() + "' is of type '" + item.getClass().getSimpleName() + "', only Switch-, Number- and StringItems are allowed - please check your *.items configuration"); } } /** * {@inheritDoc} */ @Override public void processBindingConfiguration(String context, Item item, String bindingConfig) throws BindingConfigParseException { String pattern = null; boolean base64 = false; String onCommand = null; String offCommand = null; String upCommand = null; String downCommand = null; String stopCommand = null; String[] split = bindingConfig.split(","); if (split.length > 0) { for (int i = 1; i < split.length; i++) { String part = split[i]; String substring = part.substring(0, part.length()); if (substring.startsWith("REGEX(")) { pattern = substring.substring(6, substring.length() - 1); } else if (substring.equals("BASE64")) { base64 = true; } else if (substring.startsWith("ON(")) { onCommand = substring.substring(3, substring.length() - 1); } else if (substring.startsWith("OFF(")) { offCommand = substring.substring(4, substring.length() - 1); } else if (substring.startsWith("UP(")) { upCommand = substring.substring(3, substring.length() - 1); } else if (substring.startsWith("DOWN(")) { downCommand = substring.substring(5, substring.length() - 1); } else if (substring.startsWith("STOP(")) { stopCommand = substring.substring(5, substring.length() - 1); } } } String portConfig[] = split[0].split("@"); String port = portConfig[0]; int baudRate = 0; if (portConfig.length > 1) { baudRate = Integer.parseInt(portConfig[1]); } SerialDevice serialDevice = serialDevices.get(port); if (serialDevice == null) { if (baudRate > 0) { serialDevice = new SerialDevice(port, baudRate); } else { serialDevice = new SerialDevice(port); } serialDevice.setTransformationService(transformationService); serialDevice.setEventPublisher(eventPublisher); try { serialDevice.initialize(); } catch (InitializationException e) { throw new BindingConfigParseException("Could not open serial port " + port + ": " + e.getMessage()); } catch (Throwable e) { throw new BindingConfigParseException("Could not open serial port " + port + ": " + e.getMessage()); } serialDevices.put(port, serialDevice); } itemMap.put(item.getName(), port); serialDevice.addConfig(item.getName(), item.getClass(), pattern, base64, onCommand, offCommand, upCommand, downCommand, stopCommand); Set<String> itemNames = contextMap.get(context); if (itemNames == null) { itemNames = new HashSet<String>(); contextMap.put(context, itemNames); } itemNames.add(item.getName()); } /** * {@inheritDoc} */ @Override public void removeConfigurations(String context) { Set<String> itemNames = contextMap.get(context); if (itemNames != null) { for (String itemName : itemNames) { // we remove all information in the serial devices SerialDevice serialDevice = serialDevices.get(itemMap.get(itemName)); itemMap.remove(itemName); if (serialDevice == null) { continue; } serialDevice.removeConfig(itemName); // if there is no binding left, dispose this device if (serialDevice.isEmpty()) { serialDevice.close(); serialDevices.remove(serialDevice.getPort()); } } contextMap.remove(context); } } }