/** * 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.openenergymonitor.internal; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.Map.Entry; import javax.xml.bind.DatatypeConverter; import org.apache.commons.lang.StringUtils; import org.openhab.binding.openenergymonitor.OpenEnergyMonitorBindingProvider; import org.openhab.binding.openenergymonitor.protocol.OpenEnergyMonitorConnector; import org.openhab.binding.openenergymonitor.protocol.OpenEnergyMonitorDataParser; import org.openhab.binding.openenergymonitor.protocol.OpenEnergyMonitorParserRule; import org.openhab.binding.openenergymonitor.protocol.OpenEnergyMonitorSerialConnector; import org.openhab.binding.openenergymonitor.protocol.OpenEnergyMonitorSimulator; import org.openhab.binding.openenergymonitor.protocol.OpenEnergyMonitorUDPConnector; import org.openhab.core.binding.AbstractBinding; import org.openhab.core.library.types.DecimalType; import org.openhab.core.transform.TransformationException; import org.openhab.core.transform.TransformationHelper; import org.openhab.core.transform.TransformationService; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.astesana.javaluator.DoubleEvaluator; /** * * Binding to receive data from Open Energy Monitor devices. * * @author Pauli Anttila * @since 1.4.0 */ public class OpenEnergyMonitorBinding extends AbstractBinding<OpenEnergyMonitorBindingProvider> implements ManagedService { private static final Logger logger = LoggerFactory.getLogger(OpenEnergyMonitorBinding.class); /* configuration variables for communication */ private int udpPort = 9997; private String serialPort = null; private boolean simulate = false; private OpenEnergyMonitorDataParser dataParser = null; /** Thread to handle messages from Open Energy Monitor devices */ private MessageListener messageListener = null; public OpenEnergyMonitorBinding() { } @Override public void activate() { logger.debug("Activate"); } @Override public void deactivate() { logger.debug("Deactivate"); if (messageListener != null) { messageListener.setInterrupted(true); } } protected void addBindingProvider(OpenEnergyMonitorBindingProvider bindingProvider) { super.addBindingProvider(bindingProvider); } protected void removeBindingProvider(OpenEnergyMonitorBindingProvider bindingProvider) { super.removeBindingProvider(bindingProvider); } /** * {@inheritDoc} */ @Override public void updated(Dictionary<String, ?> config) throws ConfigurationException { if (config != null) { HashMap<String, OpenEnergyMonitorParserRule> parsingRules = new HashMap<String, OpenEnergyMonitorParserRule>(); Enumeration<String> keys = config.keys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); // the config-key enumeration contains additional keys that we // don't want to process here ... if ("service.pid".equals(key)) { continue; } String value = (String) config.get(key); if ("udpPort".equals(key)) { if (StringUtils.isNotBlank(value)) { udpPort = Integer.parseInt(value); } } else if ("serialPort".equals(key)) { serialPort = value; } else if ("simulate".equals(key)) { if (StringUtils.isNotBlank(value)) { simulate = Boolean.parseBoolean(value); } } else { // process all data parsing rules try { OpenEnergyMonitorParserRule rule = new OpenEnergyMonitorParserRule(value); parsingRules.put(key, rule); } catch (OpenEnergyMonitorException e) { throw new ConfigurationException(key, "invalid parser rule", e); } } } if (parsingRules != null) { dataParser = new OpenEnergyMonitorDataParser(parsingRules); } 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(); } } /** * The MessageListener runs as a separate thread. * * Thread listening message from Open Energy Monitoring devices and send * updates to openHAB bus. * */ private class MessageListener extends Thread { private boolean interrupted = false; MessageListener() { } public void setInterrupted(boolean interrupted) { this.interrupted = interrupted; messageListener.interrupt(); } @Override public void run() { logger.debug("Open Energy Monitor message listener started"); OpenEnergyMonitorConnector connector; if (simulate == true) { connector = new OpenEnergyMonitorSimulator(); } else if (serialPort != null) { connector = new OpenEnergyMonitorSerialConnector(serialPort); } else { connector = new OpenEnergyMonitorUDPConnector(udpPort); } try { connector.connect(); } catch (OpenEnergyMonitorException e) { logger.error("Error occured when connecting to Open Energy Monitor device", e); logger.warn("Closing Open Energy Monitor 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<String, Number> vals = dataParser.parseData(data); for (OpenEnergyMonitorBindingProvider provider : providers) { for (String itemName : provider.getItemNames()) { for (Entry<String, Number> entry : vals.entrySet()) { String key = entry.getKey(); Number value = entry.getValue(); if (key != null && value != null) { boolean found = false; org.openhab.core.types.State state = null; String variable = provider.getVariable(itemName); if (variable.equals(key)) { state = new DecimalType(value.doubleValue()); found = true; } else if (variable.contains(key) && variable.matches(".*[+-/*^%].*")) { logger.debug("Eval key={}, variable={}", key, variable); String tmp = replaceVariables(vals, variable); try { double result = new DoubleEvaluator().evaluate(tmp); logger.debug("Eval '{}={}={}'", variable, tmp, result); state = new DecimalType(result); found = true; } catch (Exception e) { logger.error("Error occured during data evaluation", e); } } if (found) { state = transformData(provider.getTransformationType(itemName), provider.getTransformationFunction(itemName), state); if (state != null) { eventPublisher.postUpdate(itemName, state); break; } } } } } } } catch (OpenEnergyMonitorException e) { logger.error("Error occured when received data from Open Energy Monitor device", e); } } try { connector.disconnect(); } catch (OpenEnergyMonitorException e) { logger.error("Error occured when disconnecting form Open Energy Monitor device", e); } } } private String replaceVariables(HashMap<String, Number> vals, String variable) { for (Entry<String, Number> entry : vals.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); variable = variable.replace(key, String.valueOf(value)); } return variable; } /** * Transform received data by Transformation service. * */ protected org.openhab.core.types.State transformData(String transformationType, String transformationFunction, org.openhab.core.types.State data) { if (transformationType != null && transformationFunction != null) { String transformedResponse = null; try { TransformationService transformationService = TransformationHelper .getTransformationService(OpenEnergyMonitorActivator.getContext(), transformationType); if (transformationService != null) { transformedResponse = transformationService.transform(transformationFunction, String.valueOf(data)); } else { logger.warn("couldn't transform response because transformationService of type '{}' is unavailable", transformationType); } } catch (TransformationException te) { logger.error( "transformation throws exception [transformation type=" + transformationType + ", transformation function=" + transformationFunction + ", response=" + data + "]", te); } logger.debug("transformed response is '{}'", transformedResponse); if (transformedResponse != null) { return new DecimalType(transformedResponse); } } return data; } }