/** * 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.nibeheatpump.internal; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import javax.xml.bind.DatatypeConverter; import org.apache.commons.lang.StringUtils; import org.openhab.binding.nibeheatpump.NibeHeatPumpBindingProvider; import org.openhab.binding.nibeheatpump.protocol.NibeHeatPumpConnector; import org.openhab.binding.nibeheatpump.protocol.NibeHeatPumpDataParser; import org.openhab.binding.nibeheatpump.protocol.NibeHeatPumpDataParser.VariableInformation; import org.openhab.binding.nibeheatpump.protocol.NibeHeatPumpSerialConnector; import org.openhab.binding.nibeheatpump.protocol.NibeHeatPumpSimulator; import org.openhab.binding.nibeheatpump.protocol.NibeHeatPumpUDPConnector; import org.openhab.core.binding.AbstractBinding; import org.openhab.core.library.types.DecimalType; 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 Nibe heat pumps. * * @author Pauli Anttila * @author John Cocula -- work around bad data from F750 * @since 1.3.0 */ public class NibeHeatPumpBinding extends AbstractBinding<NibeHeatPumpBindingProvider>implements ManagedService { private static final Logger logger = LoggerFactory.getLogger(NibeHeatPumpBinding.class); /* configuration variables for communication */ private int udpPort = 9999; private String serialPort = null; private boolean simulateHeatPump = false; /* configuration variables for parsing */ private int modelNo = 1145; /** Thread to handle messages from heat pump */ private NibeHeatPumpMessageListener messageListener = null; @Override public void activate() { logger.debug("Activate"); } @Override public void deactivate() { logger.debug("Deactivate"); messageListener.setInterrupted(true); messageListener.interrupt(); } protected void addBindingProvider(NibeHeatPumpBindingProvider bindingProvider) { super.addBindingProvider(bindingProvider); } protected void removeBindingProvider(NibeHeatPumpBindingProvider 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); } String modelNoString = (String) config.get("modelNo"); if (StringUtils.isNotBlank(modelNoString)) { modelNo = Integer.parseInt(modelNoString); } serialPort = (String) config.get("serialPort"); String testPortString = (String) config.get("simulate"); if (StringUtils.isNotBlank(testPortString)) { simulateHeatPump = Boolean.parseBoolean(testPortString); } 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 NibeHeatPumpMessageListener(); messageListener.start(); } } /** * The NibeHeatPumpMessageListener runs as a separate thread. The Thread is listening * message from heat pump and send updates to openHAB bus. */ private class NibeHeatPumpMessageListener extends Thread { private boolean interrupted = false; public void setInterrupted(boolean interrupted) { this.interrupted = interrupted; } @Override public void run() { logger.debug("Nibe heatpump message listener started"); NibeHeatPumpConnector connector; if (simulateHeatPump == true) { connector = new NibeHeatPumpSimulator(); } else if (serialPort != null) { connector = new NibeHeatPumpSerialConnector(serialPort); } else { connector = new NibeHeatPumpUDPConnector(udpPort); } try { connector.connect(); } catch (NibeHeatPumpException e) { logger.error("Error occured when connecting to heat pump", e); logger.warn("Closing Nibe heatpump 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(); if (logger.isDebugEnabled()) { logger.debug("Received data (len={}): {}", data.length, DatatypeConverter.printHexBinary(data)); } Hashtable<Integer, Short> regValues = NibeHeatPumpDataParser.ParseData(data); if (regValues != null) { Enumeration<Integer> keys = regValues.keys(); while (keys.hasMoreElements()) { int key = keys.nextElement(); double value = regValues.get(key); VariableInformation variableInfo; if (modelNo == 750) { variableInfo = NibeHeatPumpDataParser.VARIABLE_INFO_F750.get(key); } else { variableInfo = NibeHeatPumpDataParser.VARIABLE_INFO_F1145_F1245.get(key); } if (variableInfo == null) { logger.debug("Unknown variable {}", key); } else { // 32bit handling: if (variableInfo.dataType == NibeHeatPumpDataParser.NibeDataType.U32 || variableInfo.dataType == NibeHeatPumpDataParser.NibeDataType.S32) { logger.debug("{}:32bit dataType", key); int keyValue, keyPlusOneValue; try { keyValue = (int) regValues.get(key) & 0xffff; // Handling the short-value as // unsigned when casting to int keyPlusOneValue = (int) regValues.get(key + 1) & 0xffff; } catch (Exception ex) { logger.error("Received bad data key={}; skipping.", key); continue; } if (logger.isDebugEnabled()) { logger.debug("{}: {} {}", key, Integer.toHexString(keyValue), Integer.toHexString(keyPlusOneValue)); } value = keyPlusOneValue << 16 | keyValue; } value = value / variableInfo.factor; BigDecimal bd = new BigDecimal(value).setScale((int) Math.log10(variableInfo.factor), RoundingMode.HALF_EVEN); org.openhab.core.types.State state = new DecimalType(bd); // Updates the item with // correct resolution based on // 'variableInfo.factor' logger.debug("{}:{}={}", key, variableInfo.variable, value); for (NibeHeatPumpBindingProvider provider : providers) { for (String itemName : provider.getItemNames()) { int itemId = provider.getItemId(itemName); if (key == itemId) { eventPublisher.postUpdate(itemName, state); } } } } } } } catch (NibeHeatPumpException e) { logger.error("Error occured when received data from heat pump", e); } } try { connector.disconnect(); } catch (NibeHeatPumpException e) { logger.error("Error occured when disconnecting form heat pump", e); } } } }