/** * 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.swegonventilation.internal; import java.util.Dictionary; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.xml.bind.DatatypeConverter; import org.apache.commons.lang.StringUtils; import org.openhab.binding.swegonventilation.SwegonVentilationBindingProvider; import org.openhab.binding.swegonventilation.protocol.SwegonVentilationConnector; import org.openhab.binding.swegonventilation.protocol.SwegonVentilationDataParser; import org.openhab.binding.swegonventilation.protocol.SwegonVentilationSerialConnector; import org.openhab.binding.swegonventilation.protocol.SwegonVentilationSimulator; import org.openhab.binding.swegonventilation.protocol.SwegonVentilationUDPConnector; import org.openhab.core.binding.AbstractBinding; 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.SwitchItem; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; 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 to receive data from Swegon ventilation system. * * @author Pauli Anttila * @since 1.4.0 */ public class SwegonVentilationBinding extends AbstractBinding<SwegonVentilationBindingProvider> implements ManagedService { private static final Logger logger = LoggerFactory.getLogger(SwegonVentilationBinding.class); /* configuration variables for communication */ private int udpPort = 9998; private String serialPort = null; private boolean simulator = false; /** Thread to handle messages from heat pump */ private MessageListener messageListener = null; public SwegonVentilationBinding() { } @Override public void activate() { logger.debug("Activate"); } @Override public void deactivate() { logger.debug("Deactivate"); if (messageListener != null) { messageListener.setInterrupted(true); } } @Override public void setEventPublisher(EventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } @Override public void unsetEventPublisher(EventPublisher eventPublisher) { this.eventPublisher = null; } protected void addBindingProvider(SwegonVentilationBindingProvider bindingProvider) { super.addBindingProvider(bindingProvider); } protected void removeBindingProvider(SwegonVentilationBindingProvider bindingProvider) { super.removeBindingProvider(bindingProvider); } /** * {@inheritDoc} */ @Override public void updated(Dictionary<String, ?> config) throws ConfigurationException { logger.debug("Configuration updated, config {}", config != null ? true : false); if (config != null) { String portString = (String) config.get("udpPort"); if (StringUtils.isNotBlank(portString)) { udpPort = Integer.parseInt(portString); } serialPort = (String) config.get("serialPort"); String simulateString = (String) config.get("simulate"); if (StringUtils.isNotBlank(simulateString)) { simulator = Boolean.parseBoolean(simulateString); } } if (messageListener != null) { logger.debug("Close previous message listener"); messageListener.setInterrupted(true); try { messageListener.join(); } catch (InterruptedException e) { logger.info("Previous message listener closing interrupted", e); } } messageListener = new MessageListener(); messageListener.start(); } /** * Convert device value to OpenHAB state. * * @param itemType * @param value * * @return a {@link State} */ private State convertDeviceValueToOpenHabState(Class<? extends Item> itemType, Integer value) { State state = UnDefType.UNDEF; try { if (itemType == SwitchItem.class) { state = value == 0 ? OnOffType.OFF : OnOffType.ON; } else if (itemType == NumberItem.class) { state = new DecimalType(value); } } catch (Exception e) { logger.debug("Cannot convert value '{}' to data type {}", value, itemType); } return state; } /** * The MessageListener runs as a separate thread. * * Thread listening message from Swegon ventilation system and send updates * to openHAB bus. * */ private class MessageListener extends Thread { private boolean interrupted = false; MessageListener() { } public void setInterrupted(boolean interrupted) { this.interrupted = interrupted; this.interrupt(); } @Override public void run() { logger.debug("Swegon ventilation system message listener started"); SwegonVentilationConnector connector; if (simulator == true) { connector = new SwegonVentilationSimulator(); } else if (serialPort != null) { connector = new SwegonVentilationSerialConnector(serialPort); } else { connector = new SwegonVentilationUDPConnector(udpPort); } try { connector.connect(); } catch (SwegonVentilationException e) { logger.error("Error occured when connecting to Swegon ventilation system", e); logger.warn("Closing Swegon ventilation system message listener"); // exit interrupted = true; } // as long as no interrupt is requested, continue running while (!interrupted) { try { // Wait a packet (blocking) byte[] data = connector.receiveDatagram(); logger.trace("Received data (len={}): {}", data.length, DatatypeConverter.printHexBinary(data)); HashMap<SwegonVentilationCommandType, Integer> regValues = SwegonVentilationDataParser .parseData(data); if (regValues != null) { logger.debug("regValues (len={}): {}", regValues.size(), regValues); Set<Map.Entry<SwegonVentilationCommandType, Integer>> set = regValues.entrySet(); for (Entry<SwegonVentilationCommandType, Integer> val : set) { SwegonVentilationCommandType cmdType = val.getKey(); Integer value = val.getValue(); for (SwegonVentilationBindingProvider provider : providers) { for (String itemName : provider.getItemNames()) { SwegonVentilationCommandType commandType = provider.getCommandType(itemName); if (commandType.equals(cmdType)) { Class<? extends Item> itemType = provider.getItemType(itemName); org.openhab.core.types.State state = convertDeviceValueToOpenHabState(itemType, value); eventPublisher.postUpdate(itemName, state); } } } } } } catch (SwegonVentilationException e) { logger.error("Error occured when received data from Swegon ventilation system", e); } } try { connector.disconnect(); } catch (SwegonVentilationException e) { logger.error("Error occured when disconnecting from Swegon ventilation system", e); } } } }