/**
* 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.wr3223.internal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.wr3223.WR3223BindingProvider;
import org.openhab.binding.wr3223.WR3223CommandType;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.library.items.ContactItem;
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.library.types.OpenClosedType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class is an active binding to control the WR3223. To control the WR3223 over RS232/USB it must receive
* at least every 20 second a message.
*
* @author Michael Fraefel
* @since 1.10.0
*/
public class WR3223Binding extends AbstractActiveBinding<WR3223BindingProvider> {
private static final WR3223CommandType[] READ_COMMANDS = { WR3223CommandType.TEMPERATURE_EVAPORATOR,
WR3223CommandType.TEMPERATURE_CONDENSER, WR3223CommandType.TEMPERATURE_OUTSIDE,
WR3223CommandType.TEMPERATURE_OUTGOING_AIR, WR3223CommandType.TEMPERATURE_AFTER_HEAT_EXCHANGER,
WR3223CommandType.TEMPERATURE_SUPPLY_AIR, WR3223CommandType.TEMPERATURE_AFTER_BRINE_PREHEATING,
WR3223CommandType.TEMPERATURE_AFTER_PREHEATING, WR3223CommandType.VENTILATION_LEVEL,
WR3223CommandType.ROTATION_SPEED_SUPPLY_AIR_MOTOR, WR3223CommandType.ROTATION_SPEED_EXHAUST_AIR_MOTOR,
WR3223CommandType.OPERATION_MODE, WR3223CommandType.TEMPERATURE_SUPPLY_AIR_TARGET,
WR3223CommandType.HEAT_FEEDBACK_RATE, WR3223CommandType.SPEED_DEVIATION_MAX_LEVEL_1,
WR3223CommandType.SPEED_DEVIATION_MAX_LEVEL_2, WR3223CommandType.SPEED_DEVIATION_MAX_LEVEL_3,
WR3223CommandType.SPEED_INCREASE_EARTH_HEAT_EXCHANGER_LEVEL_1,
WR3223CommandType.SPEED_INCREASE_EARTH_HEAT_EXCHANGER_LEVEL_2,
WR3223CommandType.SPEED_INCREASE_EARTH_HEAT_EXCHANGER_LEVEL_3,
WR3223CommandType.AIR_EXCHANGE_DECREASE_OUTSIDE_TEMPERATURE, WR3223CommandType.VENTILATION_SPEED_LEVEL_1,
WR3223CommandType.VENTILATION_SPEED_LEVEL_2, WR3223CommandType.VENTILATION_SPEED_LEVEL_3,
WR3223CommandType.SUMMER_EARTH_HEAT_EXCHANGER_ACTIVATION_TEMPERATURE,
WR3223CommandType.WINTER_EARTH_HEAT_EXCHANGER_ACTIVATION_TEMPERATURE,
WR3223CommandType.DEFROSTING_START_TEMPERATURE, WR3223CommandType.DEFROSTING_END_TEMPERATURE,
WR3223CommandType.DEFROSTING_VENTILATION_LEVEL, WR3223CommandType.DEFROSTING_HOLD_OFF_TIME,
WR3223CommandType.DEFROSTING_OVERTRAVEL_TIME, WR3223CommandType.DEFROSTING_HEAT_FEEDBACK_RATE,
WR3223CommandType.SOLAR_MAX, WR3223CommandType.SOLAR_USAGE, WR3223CommandType.DELTA_T_OFF,
WR3223CommandType.DELTA_T_ON, WR3223CommandType.TEMPERATURE_CONDENSER_MAX,
WR3223CommandType.IDLE_TIME_PRESSURE_REDUCTION, WR3223CommandType.SUPPORT_FAN_LEVEL_1_EARTH_HEAT_EXCHANGER,
WR3223CommandType.SUPPORT_FAN_LEVEL_2_EARTH_HEAT_EXCHANGER,
WR3223CommandType.SUPPORT_FAN_LEVEL_3_EARTH_HEAT_EXCHANGER, WR3223CommandType.CONTROL_VOLTAGE_OUTGOING_AIR,
WR3223CommandType.CONTROL_VOLTAGE_SUPPLY_AIR, WR3223CommandType.WARM_WATER_TARGET_TEMPERATURE,
WR3223CommandType.HEAT_PUMP_OPEN, WR3223CommandType.ADDITIONAL_HEATER_OPEN };
private static final WR3223CommandType[] WRITE_COMMANDS = { WR3223CommandType.OPERATION_MODE,
WR3223CommandType.TEMPERATURE_SUPPLY_AIR_TARGET, WR3223CommandType.SPEED_DEVIATION_MAX_LEVEL_1,
WR3223CommandType.SPEED_DEVIATION_MAX_LEVEL_2, WR3223CommandType.SPEED_DEVIATION_MAX_LEVEL_3,
WR3223CommandType.SPEED_INCREASE_EARTH_HEAT_EXCHANGER_LEVEL_1,
WR3223CommandType.SPEED_INCREASE_EARTH_HEAT_EXCHANGER_LEVEL_2,
WR3223CommandType.SPEED_INCREASE_EARTH_HEAT_EXCHANGER_LEVEL_3,
WR3223CommandType.AIR_EXCHANGE_DECREASE_OUTSIDE_TEMPERATURE, WR3223CommandType.VENTILATION_SPEED_LEVEL_1,
WR3223CommandType.VENTILATION_SPEED_LEVEL_2, WR3223CommandType.VENTILATION_SPEED_LEVEL_3,
WR3223CommandType.SUMMER_EARTH_HEAT_EXCHANGER_ACTIVATION_TEMPERATURE,
WR3223CommandType.WINTER_EARTH_HEAT_EXCHANGER_ACTIVATION_TEMPERATURE,
WR3223CommandType.DEFROSTING_START_TEMPERATURE, WR3223CommandType.DEFROSTING_END_TEMPERATURE,
WR3223CommandType.DEFROSTING_VENTILATION_LEVEL, WR3223CommandType.DEFROSTING_HOLD_OFF_TIME,
WR3223CommandType.DEFROSTING_OVERTRAVEL_TIME, WR3223CommandType.DEFROSTING_HEAT_FEEDBACK_RATE,
WR3223CommandType.SOLAR_MAX, WR3223CommandType.DELTA_T_OFF, WR3223CommandType.DELTA_T_ON,
WR3223CommandType.TEMPERATURE_CONDENSER_MAX, WR3223CommandType.IDLE_TIME_PRESSURE_REDUCTION,
WR3223CommandType.SUPPORT_FAN_LEVEL_1_EARTH_HEAT_EXCHANGER,
WR3223CommandType.SUPPORT_FAN_LEVEL_2_EARTH_HEAT_EXCHANGER,
WR3223CommandType.SUPPORT_FAN_LEVEL_3_EARTH_HEAT_EXCHANGER, WR3223CommandType.WARM_WATER_TARGET_TEMPERATURE,
WR3223CommandType.HEAT_PUMP_OPEN, WR3223CommandType.ADDITIONAL_HEATER_OPEN };
private static final Logger logger = LoggerFactory.getLogger(WR3223Binding.class);
/**
* The BundleContext. This is only valid when the bundle is ACTIVE. It is set in the activate()
* method and must not be accessed anymore once the deactivate() method was called or before activate()
* was called.
*/
private BundleContext bundleContext;
/**
* the refresh interval which is used to poll values from the WR3223
* server (optional, defaults to 15000ms)
*/
private long refreshInterval = 15000;
/**
* Host if connection over IP is used.
*/
private String host;
/**
* Port if connection over IP is used.
*/
private int port;
/**
* Serial port if connection over serial interface is used.
*/
private String serialPort;
/**
* Controller address.
*/
private byte controllerAddr = 1;
/**
* WR3223 Connector
*/
private AbstractWR3223Connector connector;
/**
* Status of the WR3223
*/
private StatusValueHolder statusHolder = new StatusValueHolder();
/**
* Store the value to update
*/
private Map<WR3223CommandType, Integer> updateMap = new HashMap<WR3223CommandType, Integer>();
/**
* Not all controller supports all commands. The binding automatically disable not supported commands.
*/
private Set<WR3223Commands> disabledCommands = new HashSet<>();
/**
* Called by the SCR to activate the component with its configuration read from CAS
*
* @param bundleContext BundleContext of the Bundle that defines this component
* @param configuration Configuration properties for this component obtained from the ConfigAdmin service
*/
public void activate(final BundleContext bundleContext, final Map<String, Object> configuration) {
this.bundleContext = bundleContext;
configure(configuration);
// Default start values
updateMap.put(WR3223CommandType.OPERATION_MODE, 3);
}
/**
* Called by the SCR when the configuration of a binding has been changed through the ConfigAdmin service.
*
* @param configuration Updated configuration properties
*/
public void modified(final Map<String, Object> configuration) {
configure(configuration);
}
/**
* Called by the SCR to deactivate the component when either the configuration is removed or
* mandatory references are no longer satisfied or the component has simply been stopped.
*
* @param reason Reason code for the deactivation:<br>
* <ul>
* <li>0 – Unspecified
* <li>1 – The component was disabled
* <li>2 – A reference became unsatisfied
* <li>3 – A configuration was changed
* <li>4 – A configuration was deleted
* <li>5 – The component was disposed
* <li>6 – The bundle was stopped
* </ul>
*/
public void deactivate(final int reason) {
this.bundleContext = null;
if (connector != null) {
try {
connector.close();
} catch (IOException ex) {
logger.error("Error by closing connector.", ex);
}
}
}
/**
* Configure binding.
*
* @param configuration
*/
private void configure(final Map<String, Object> configuration) {
// Configure refresh
String refreshIntervalString = (String) configuration.get("refresh");
if (StringUtils.isNotBlank(refreshIntervalString)) {
refreshInterval = Long.parseLong(refreshIntervalString);
if (refreshInterval > 20000) {
logger.warn("Refresh interval should not be over 20 seconds.");
}
}
// Controller
String controllerAddrString = (String) configuration.get("controllerAddr");
if (StringUtils.isNotBlank(controllerAddrString)) {
controllerAddr = Byte.parseByte(controllerAddrString);
}
// Configure connection
String hostString = (String) configuration.get("host");
String portString = (String) configuration.get("port");
String serialPortString = (String) configuration.get("serialPort");
if (StringUtils.isNotBlank(hostString) && StringUtils.isNotBlank(portString)) {
host = hostString;
port = Integer.parseInt(portString);
serialPort = null;
logger.info("Connect over IP to host {}:{}", host, port);
setProperlyConfigured(true);
} else if (StringUtils.isNotBlank(serialPortString)) {
serialPort = serialPortString;
host = null;
logger.info("Connect over serial port {}", serialPort);
setProperlyConfigured(true);
} else {
setProperlyConfigured(false);
logger.error("No connection configured");
}
// Close the connector if already one is open.
if (connector != null) {
try {
connector.close();
} catch (IOException ex) {
logger.error("Error by closing connector.", ex);
}
}
}
/**
* @{inheritDoc}
*/
@Override
protected long getRefreshInterval() {
return refreshInterval;
}
/**
* @{inheritDoc}
*/
@Override
protected String getName() {
return "WR3223 Refresh Service";
}
/**
* @{inheritDoc}
*/
@Override
protected void execute() {
// Connector if not already connected.
try {
if (connector == null) {
if (host != null) {
TcpWR3223Connector tcpConnector = new TcpWR3223Connector();
tcpConnector.connect(host, port);
connector = tcpConnector;
logger.info("Connected to WR3223 over tcp to host {}:{}.", host, port);
} else if (serialPort != null) {
SerialWR3223Connector serialConnector = new SerialWR3223Connector();
serialConnector.connect(serialPort, 9600);
connector = serialConnector;
logger.info("Connected to WR3223 over serial port {}.", serialPort);
}
}
} catch (IOException ex) {
logger.error("Couldn't establish connection to WR3223.", ex);
connector = null;
return;
}
try {
// Read status values (Ta)
if (checkIfCommandIsAvailable(WR3223Commands.Ta)) {
statusHolder.valueOf(connector.read(controllerAddr, WR3223Commands.Ta));
publishValueToBoundItems(WR3223CommandType.MALFUNCTION, statusHolder.isMalfunction());
publishValueToBoundItems(WR3223CommandType.HEAT_PUMP_STATUS, statusHolder.getHeatPumpOnStatus());
publishValueToBoundItems(WR3223CommandType.ADDITIONAL_HEATER_STATUS,
statusHolder.getAdditionalHeatingOnStatus());
}
// EVU Blockade handling (Tf)
if (checkIfCommandIsAvailable(WR3223Commands.Tf)) {
EvuBlockadeHandler handler = EvuBlockadeHandler
.valueOf(connector.read(controllerAddr, WR3223Commands.Tf));
publishValueToBoundItems(WR3223CommandType.EVU_BLOCKADE, handler.isBlockade());
}
// Read relais
RelaisValueDecoder relais = readAndPublishRelaisValues();
// Read errors
readAndPublishErrorValues();
// Write values if no control device connected
if (!relais.isControlDeviceActive()) {
if (connector.write(controllerAddr, WR3223Commands.SW, statusHolder.getStatusValue())) {
// Commit value updates to WR3223
Iterator<Entry<WR3223CommandType, Integer>> it = updateMap.entrySet().iterator();
while (it.hasNext()) {
Entry<WR3223CommandType, Integer> update = it.next();
if (connector.write(controllerAddr, update.getKey().getWr3223Command(),
String.valueOf(update.getValue()))) {
it.remove();
}
}
} else {
logger.error("Couldn't send keep alive message to WR3223.");
}
} else {
logger.warn(
"The control device is active! Openhab can only control the WR3223, when the control device is removed. (Bedienteil)");
}
// Read and publish other values from WR3223
for (WR3223CommandType readCommand : READ_COMMANDS) {
if (checkIfCommandIsAvailable(readCommand.getWr3223Command())) {
if (!updateMap.containsKey(readCommand)) {
readAndPublishValue(readCommand);
} else {
logger.info(
"Skip reading values for command {} from WR3223, because an updated value must first be sent to WR3223.",
readCommand.getCommand());
}
}
}
} catch (IOException e) {
logger.error("Communication error to WR3223.", e);
if (connector != null) {
try {
connector.close();
} catch (IOException e1) {
logger.error("Couldn't close communication to WR3223.", e1);
}
}
connector = null;
}
}
/**
* Check if a command is available.
*
* @param command
* @return
* @throws IOException
*/
private boolean checkIfCommandIsAvailable(WR3223Commands command) throws IOException {
if (disabledCommands.contains(command)) {
return false;
}
String value = connector.read(controllerAddr, command);
if (value == null || value.contains("???")) {
disabledCommands.add(command);
logger.warn("Command {} is not supported by the controller.", command);
return false;
}
return true;
}
/**
* @return
* @throws IOException
*/
private RelaisValueDecoder readAndPublishRelaisValues() throws IOException {
RelaisValueDecoder relais = RelaisValueDecoder.valueOf(connector.read(controllerAddr, WR3223Commands.RL));
publishValueToBoundItems(WR3223CommandType.COMPRESSOR, relais.isCompressor());
publishValueToBoundItems(WR3223CommandType.ADDITIONAL_HEATER_RELAIS, relais.isAdditionalHeater());
publishValueToBoundItems(WR3223CommandType.PREHEATING_RADIATOR_ACTIVE, relais.isPreHeaterRadiatorActive());
publishValueToBoundItems(WR3223CommandType.BYPASS, !relais.isBypass());
publishValueToBoundItems(WR3223CommandType.BYPASS_RELAY, relais.isBypassRelay());
publishValueToBoundItems(WR3223CommandType.CONTROL_DEVICE_ACTIVE, relais.isControlDeviceActive());
publishValueToBoundItems(WR3223CommandType.EARTH_HEAT_EXCHANGER, relais.isEarthHeatExchanger());
publishValueToBoundItems(WR3223CommandType.MAGNET_VALVE, relais.isMagnetValve());
publishValueToBoundItems(WR3223CommandType.OPENHAB_INTERFACE_ACTIVE, relais.isOpenhabInterfaceActive());
publishValueToBoundItems(WR3223CommandType.PREHEATING_RADIATOR, relais.isPreheatingRadiator());
publishValueToBoundItems(WR3223CommandType.VENTILATION_LEVEL_AVAILABLE, relais.isVentilationLevelAvailable());
publishValueToBoundItems(WR3223CommandType.WARM_WATER_POST_HEATER, relais.isWarmWaterPostHeater());
return relais;
}
/**
* @throws IOException
*/
private void readAndPublishErrorValues() throws IOException {
if (checkIfCommandIsAvailable(WR3223Commands.ER)) {
ErrorValueDecoder errors = ErrorValueDecoder.valueOf(connector.read(controllerAddr, WR3223Commands.ER));
publishValueToBoundItems(WR3223CommandType.ERROR_TEMP_SENSOR_SHORT, errors.isError_temp_sensor_short());
publishValueToBoundItems(WR3223CommandType.ERROR_OFFSET, errors.isError_offset());
publishValueToBoundItems(WR3223CommandType.ERROR_TEMP_SENSOR_INTERUPT,
errors.isError_temp_sensor_interupt());
publishValueToBoundItems(WR3223CommandType.ERROR_HIGH_PRESSURE, errors.isError_high_pressure());
publishValueToBoundItems(WR3223CommandType.ERROR_SYS_RAM, errors.isError_sys_ram());
publishValueToBoundItems(WR3223CommandType.ERROR_SYS_ROM, errors.isError_sys_rom());
publishValueToBoundItems(WR3223CommandType.ERROR_SYS_EE, errors.isError_sys_ee());
publishValueToBoundItems(WR3223CommandType.ERROR_SYS_IO, errors.isError_sys_io());
publishValueToBoundItems(WR3223CommandType.ERROR_SYS_67_AD, errors.isError_sys_67_ad());
publishValueToBoundItems(WR3223CommandType.ERROR_SUPPLY_AIR, errors.isError_supply_air());
publishValueToBoundItems(WR3223CommandType.ERROR_OUTGOING_AIR, errors.isError_outgoing_air());
publishValueToBoundItems(WR3223CommandType.ERROR_CONDENSER, errors.isError_condenser());
publishValueToBoundItems(WR3223CommandType.ERROR_PREHEATING, errors.isError_preheating());
}
}
/**
* Read value of given command and publish it to the bus.
*
* @param wr3223CommandType
* @throws IOException
*/
private void readAndPublishValue(WR3223CommandType wr3223CommandType) throws IOException {
List<String> itemNames = getBoundItemsForType(wr3223CommandType);
if (itemNames.size() > 0) {
String value = connector.read(controllerAddr, wr3223CommandType.getWr3223Command());
publishValueToItems(itemNames, wr3223CommandType, value);
}
}
/**
* Publish the value to all bound items.
*
* @param wr3223CommandType
* @param value
*/
private void publishValueToBoundItems(WR3223CommandType wr3223CommandType, Object value) {
List<String> itemNames = getBoundItemsForType(wr3223CommandType);
if (itemNames.size() > 0) {
publishValueToItems(itemNames, wr3223CommandType, value);
}
}
/**
* Publish the value to the given items.
*
* @param itemNames
* @param wr3223CommandType
* @param value
*/
private void publishValueToItems(List<String> itemNames, WR3223CommandType wr3223CommandType, Object value) {
if (value == null) {
logger.error("Can't set NULL value to item type {}.", wr3223CommandType.getCommand());
return;
}
State state = null;
if (wr3223CommandType.getItemClass() == NumberItem.class) {
try {
if (logger.isDebugEnabled()) {
logger.debug("WR3223Binding.publishValueToItems: publish command {} with number {} ",
wr3223CommandType.getCommand(), value.toString().trim());
}
state = DecimalType.valueOf(value.toString().trim());
} catch (NumberFormatException nfe) {
logger.error("Can't set value {} to item type {} because it's not a decimal number.", value,
wr3223CommandType.getCommand());
}
} else if (wr3223CommandType.getItemClass() == SwitchItem.class) {
state = parseBooleanValue(value);
if (logger.isDebugEnabled()) {
logger.debug("WR3223 publish switch item {} to {}.", wr3223CommandType.getCommand(), state);
}
} else if (wr3223CommandType.getItemClass() == ContactItem.class) {
state = parseBooleanValue(value) == OnOffType.ON ? OpenClosedType.CLOSED : OpenClosedType.OPEN;
if (logger.isDebugEnabled()) {
logger.debug("WR3223 publish contact item {} to {}.", wr3223CommandType.getCommand(), state);
}
} else {
logger.error("Can't set value {} to item type {}.", value, wr3223CommandType.getCommand());
}
if (state != null) {
for (String itemName : itemNames) {
eventPublisher.postUpdate(itemName, state);
}
}
}
/**
* Try to read the On/Off state.
*
* @param value
* @return state of a boolean value
*/
private State parseBooleanValue(Object value) {
State state;
String valStr = value.toString().trim();
state = (valStr.equalsIgnoreCase("true") || valStr.equals("1") || valStr.equals("1.")) ? OnOffType.ON
: OnOffType.OFF;
if (logger.isDebugEnabled()) {
logger.debug("W3223Binding.parseBooleanValue: parsed value {} to state {}", valStr, state);
}
return state;
}
private List<String> getBoundItemsForType(WR3223CommandType wr3223CommandType) {
List<String> itemNames = new ArrayList<String>();
for (WR3223BindingProvider provider : providers) {
for (String itemName : provider.getItemNamesForType(wr3223CommandType)) {
itemNames.add(itemName);
}
}
return itemNames;
}
private WR3223CommandType getWR3223BindingConfig(String itemName) {
WR3223CommandType type = null;
Iterator<WR3223BindingProvider> providerIt = providers.iterator();
while (providerIt.hasNext() && type == null) {
type = providerIt.next().getWR3223CommandTypeForItemName(itemName);
}
return type;
}
/**
* @{inheritDoc}
*/
@Override
protected void internalReceiveCommand(String itemName, Command command) {
if (logger.isDebugEnabled()) {
logger.debug("internalReceiveCommand({},{}) is called!", itemName, command);
}
WR3223CommandType type = getWR3223BindingConfig(itemName);
if (type == null) {
logger.error("Item {} is not bound to WR3223 binding.", itemName);
} else {
if (type == WR3223CommandType.VENTILATION_LEVEL) {
if (command instanceof DecimalType) {
int value = ((DecimalType) command).intValue();
if (value >= 0 && value <= 3) {
statusHolder.setVentilationLevel(value);
} else {
logger.warn("WR3223 value {} of item {} out of range.", value, itemName);
}
} else {
logger.warn("WR3223 item {} must be from type:{}.", itemName, DecimalType.class.getSimpleName());
}
} else if (type == WR3223CommandType.ADDITIONAL_HEATER_ACTIVATE) {
if (command instanceof OnOffType) {
if (command == OnOffType.ON) {
statusHolder.setAdditionalHeatingOn(true);
} else {
statusHolder.setAdditionalHeatingOn(false);
}
} else {
logger.warn("WR3223 item {} must be from type:{}.", itemName, OnOffType.class.getSimpleName());
}
} else if (type == WR3223CommandType.HEAT_PUMP_ACTIVATE) {
if (command instanceof OnOffType) {
if (command == OnOffType.ON) {
statusHolder.setHeatPumpOn(true);
} else {
statusHolder.setHeatPumpOn(false);
}
} else {
logger.warn("WR3223 item {} must be from type:{}.", itemName, OnOffType.class.getSimpleName());
}
} else if (type == WR3223CommandType.COOLING_MODE_ACTIVATE) {
if (command instanceof OnOffType) {
if (command == OnOffType.ON) {
statusHolder.setCoolingOn(true);
} else {
statusHolder.setCoolingOn(false);
}
} else {
logger.warn("WR3223 item {} must be from type:{}.", itemName, OnOffType.class.getSimpleName());
}
} else {
for (WR3223CommandType t : WRITE_COMMANDS) {
if (type == t) {
if (type.getItemClass() == NumberItem.class && command instanceof DecimalType) {
int value = ((DecimalType) command).intValue();
if (type.getMinValue() != null && type.getMinValue() > value) {
logger.warn("Value of WR3223 command {} must be bigger or equals {}.",
type.getCommand(), type.getMinValue());
} else if (type.getMaxValue() != null && type.getMaxValue() < value) {
logger.warn("Value of WR3223 command {} must be lower or equals {}.", type.getCommand(),
type.getMaxValue());
} else {
updateMap.put(type, value);
}
} else if (type.getItemClass() == SwitchItem.class && command instanceof OnOffType) {
logger.debug("SwitchCommand({},{}) is called!", itemName, command);
if (command == OnOffType.ON) {
updateMap.put(type, 1);
} else if (command == OnOffType.OFF) {
updateMap.put(type, 0);
} else {
logger.warn("WR3223 command {} should be of type {}.", type.getCommand(),
type.getItemClass());
}
} else {
logger.warn("WR3223 command {} should be of type {}.", type.getCommand(),
type.getItemClass());
}
break;
}
}
}
}
}
/**
* Hold the values which must be send every 20 seconds to the WR3223. The values received from the bus are stored in
* this class.
*
* @author Michael Fraefel
*
*/
private static final class StatusValueHolder {
private boolean heatPumpOn = false;
private int ventilationLevel = 2;
private boolean additionalHeatingOn = false;
private boolean coolingOn = false;
private boolean malfunction = false;
private final int FLAG_MALFUNCTION = 16;
private boolean heatPumpOnStatus = false;
private final int FLAG_HEAT_PUMP_STATUS = 32;
private boolean additionalHeatingOnStatus = false;
private final int FLAG_ADDITIONAL_HEATER_STATUS = 64;
private final int STATUS_MASK = 112;
/**
* @param "Wärmepumpe ein (bei Anlagen mit Wärmepumpe)"
*/
public void setHeatPumpOn(boolean heatPumpOn) {
this.heatPumpOn = heatPumpOn;
}
/**
* @param "Lüftungsstufe 0-3, 0=Aus"
*/
public void setVentilationLevel(int ventilationLevel) {
this.ventilationLevel = ventilationLevel;
}
/**
* @param "Zusatzheizung ein"
*/
public void setAdditionalHeatingOn(boolean additionalHeatingOn) {
this.additionalHeatingOn = additionalHeatingOn;
}
/**
* @param "kühlen (bei Anlagen mit Wärmepumpe)"
*/
public void setCoolingOn(boolean coolingOn) {
this.coolingOn = coolingOn;
}
/**
* @return Störung
*/
public boolean isMalfunction() {
return malfunction;
}
/**
* @return Zusatzheizung An/Aus
*/
public boolean getAdditionalHeatingOnStatus() {
return additionalHeatingOnStatus;
}
/**
* @return Wärmepumpe An/Aus
*/
public boolean getHeatPumpOnStatus() {
return heatPumpOnStatus;
}
public String getStatusValue() {
int data = 0;
if (!heatPumpOn) {
data += 1;
}
if (ventilationLevel == 2 || ventilationLevel == 1) {
data += 2;
}
if (ventilationLevel == 3 || ventilationLevel == 1) {
data += 4;
}
if (!additionalHeatingOn) {
data += 8;
}
if (ventilationLevel == 0) {
data += 16;
}
if (!coolingOn) {
data += 32;
}
return String.valueOf(data);
}
public void valueOf(String read) {
read = read.trim();
int decPoint = read.indexOf(".");
if (decPoint > 0) {
read = read.substring(0, decPoint);
}
int value = Integer.valueOf(read.trim()) * -1;
// Mask value as only bits 4, 5 and 6 are necessary
value &= STATUS_MASK;
malfunction = (value & FLAG_MALFUNCTION) == FLAG_MALFUNCTION ? true : false;
heatPumpOnStatus = (value & FLAG_HEAT_PUMP_STATUS) == FLAG_HEAT_PUMP_STATUS ? true : false;
additionalHeatingOnStatus = (value & FLAG_ADDITIONAL_HEATER_STATUS) == FLAG_ADDITIONAL_HEATER_STATUS ? true
: false;
}
}
/**
* Coding of the RL command.
*
* @author Michael Fraefel
*
*/
private static final class RelaisValueDecoder {
private static final int FLAG_COMPRESSOR = 1;
private static final int FLAG_ADDITIONAL_HEATER = 2;
private static final int FLAG_EARTH_HEAT_EXCHANGER = 4;
private static final int FLAG_BYPASS = 8;
private static final int FLAG_PREHEATING_RADIATOR = 16;
private static final int FLAG_BYPASS_RELAY = 32;
private static final int FLAG_CONTROL_DEVICE_ACTIVE = 64;
private static final int FLAG_OPENHAB_INTERFACE_ACTIVE = 128;
private static final int FLAG_VENTILATION_LEVEL_AVAILABLE = 256;
private static final int FLAG_WARM_WATER_POST_HEATER = 512;
private static final int FLAG_MAGNET_VALVE = 2048;
private static final int FLAG_PRE_HEATER_RADIATOR_ACTIVE = 4096;
private boolean compressor;
private boolean additionalHeater;
private boolean earthHeatExchanger;
private boolean bypass;
private boolean bypassRelay;
private boolean preheatingRadiator;
private boolean controlDeviceActive;
private boolean openhabInterfaceActive;
private boolean ventilationLevelAvailable;
private boolean warmWaterPostHeater;
private boolean magnetValve;
private boolean preHeaterRadiatorActive;
public static RelaisValueDecoder valueOf(String read) {
RelaisValueDecoder relaisValue = new RelaisValueDecoder();
read = read.trim();
int decPoint = read.indexOf(".");
if (decPoint > 0) {
read = read.substring(0, decPoint);
}
logger.debug("WR3223.RelaisValueDecoder read string={}.", read);
int value = Integer.valueOf(read.trim());
if ((value & FLAG_COMPRESSOR) == FLAG_COMPRESSOR) {
relaisValue.compressor = true;
}
if ((value & FLAG_ADDITIONAL_HEATER) == FLAG_ADDITIONAL_HEATER) {
relaisValue.additionalHeater = true;
}
if ((value & FLAG_EARTH_HEAT_EXCHANGER) == FLAG_EARTH_HEAT_EXCHANGER) {
relaisValue.earthHeatExchanger = true;
}
if ((value & FLAG_BYPASS) == FLAG_BYPASS) {
relaisValue.bypass = true;
}
if ((value & FLAG_PREHEATING_RADIATOR) == FLAG_PREHEATING_RADIATOR) {
relaisValue.preheatingRadiator = true;
}
if ((value & FLAG_BYPASS_RELAY) == FLAG_BYPASS_RELAY) {
relaisValue.bypassRelay = true;
}
if ((value & FLAG_CONTROL_DEVICE_ACTIVE) == FLAG_CONTROL_DEVICE_ACTIVE) {
relaisValue.controlDeviceActive = true;
}
if ((value & FLAG_OPENHAB_INTERFACE_ACTIVE) == FLAG_OPENHAB_INTERFACE_ACTIVE) {
relaisValue.openhabInterfaceActive = true;
}
if ((value & FLAG_VENTILATION_LEVEL_AVAILABLE) == FLAG_VENTILATION_LEVEL_AVAILABLE) {
relaisValue.ventilationLevelAvailable = true;
}
if ((value & FLAG_WARM_WATER_POST_HEATER) == FLAG_WARM_WATER_POST_HEATER) {
relaisValue.warmWaterPostHeater = true;
}
if ((value & FLAG_MAGNET_VALVE) == FLAG_MAGNET_VALVE) {
relaisValue.magnetValve = true;
}
if ((value & FLAG_PRE_HEATER_RADIATOR_ACTIVE) == FLAG_PRE_HEATER_RADIATOR_ACTIVE) {
relaisValue.preHeaterRadiatorActive = true;
}
return relaisValue;
}
/**
* @return "Kompressor Relais"
*/
public boolean isCompressor() {
return compressor;
}
/**
* @return "Zusatzheizung Relais"
*/
public boolean isAdditionalHeater() {
return additionalHeater;
}
/**
* @return "Erdwärmetauscher"
*/
public boolean isEarthHeatExchanger() {
return earthHeatExchanger;
}
/**
* @return "Bypass"
*/
public boolean isBypass() {
return bypass;
}
/**
* "Netzrelais Bypass"
*
* @return
*/
public boolean isBypassRelay() {
return bypassRelay;
}
/**
* @return "Vorheizregister"
*/
public boolean isPreheatingRadiator() {
return preheatingRadiator;
}
/**
* @return "Bedienteil aktiv"
*/
public boolean isControlDeviceActive() {
return controlDeviceActive;
}
/**
* @return "Bedienung über RS Schnittstelle"
*/
public boolean isOpenhabInterfaceActive() {
return openhabInterfaceActive;
}
/**
* @return "Luftstufe vorhanden"
*/
public boolean isVentilationLevelAvailable() {
return ventilationLevelAvailable;
}
/**
* @return "WW_Nachheizrgister"
*/
public boolean isWarmWaterPostHeater() {
return warmWaterPostHeater;
}
/**
* @return "Magnetventil"
*/
public boolean isMagnetValve() {
return magnetValve;
}
/**
* @return "Vorheizen aktiv"
*/
public boolean isPreHeaterRadiatorActive() {
return preHeaterRadiatorActive;
}
}
/**
* Check the different possible errors codes
*
* @author Christian Spiegel
*
*/
private static final class ErrorValueDecoder {
private static final int ERROR_TEMP_SENSOR_SHORT = 1;
private static final int ERROR_OFFSET = 2;
private static final int ERROR_TEMP_SENSOR_INTERUPT = 3;
private static final int ERROR_HIGH_PRESSURE = 4;
private static final int ERROR_SYS_RAM = 61;
private static final int ERROR_SYS_ROM = 62;
private static final int ERROR_SYS_EE = 65;
private static final int ERROR_SYS_IO = 66;
private static final int ERROR_SYS_67_AD = 67;
private static final int ERROR_SUPPLY_AIR = 128;
private static final int ERROR_OUTGOING_AIR = 132;
private static final int ERROR_CONDENSER = 130;
private static final int ERROR_PREHEATING = 133;
private boolean error_temp_sensor_short;
private boolean error_offset;
private boolean error_temp_sensor_interupt;
private boolean error_high_pressure;
private boolean error_sys_ram;
private boolean error_sys_rom;
private boolean error_sys_ee;
private boolean error_sys_io;
private boolean error_sys_67_ad;
private boolean error_supply_air;
private boolean error_outgoing_air;
private boolean error_condenser;
private boolean error_preheating;
public static ErrorValueDecoder valueOf(String read) {
ErrorValueDecoder errorValue = new ErrorValueDecoder();
read = read.trim();
int decPoint = read.indexOf(".");
if (decPoint > 0) {
read = read.substring(0, decPoint);
}
if (logger.isDebugEnabled()) {
logger.debug("WR3223.ErrorValueDecoder read string={}.", read);
}
int value = Integer.valueOf(read.trim());
if ((value & ERROR_TEMP_SENSOR_SHORT) == ERROR_TEMP_SENSOR_SHORT) {
errorValue.error_temp_sensor_short = true;
}
if ((value & ERROR_OFFSET) == ERROR_OFFSET) {
errorValue.error_offset = true;
}
if ((value & ERROR_TEMP_SENSOR_INTERUPT) == ERROR_TEMP_SENSOR_INTERUPT) {
errorValue.error_temp_sensor_interupt = true;
}
if ((value & ERROR_HIGH_PRESSURE) == ERROR_HIGH_PRESSURE) {
errorValue.error_high_pressure = true;
}
if ((value & ERROR_SYS_RAM) == ERROR_SYS_RAM) {
errorValue.error_sys_ram = true;
}
if ((value & ERROR_SYS_ROM) == ERROR_SYS_ROM) {
errorValue.error_sys_rom = true;
}
if ((value & ERROR_SYS_EE) == ERROR_SYS_EE) {
errorValue.error_sys_ee = true;
}
if ((value & ERROR_SYS_IO) == ERROR_SYS_IO) {
errorValue.error_sys_io = true;
}
if ((value & ERROR_SYS_67_AD) == ERROR_SYS_67_AD) {
errorValue.error_sys_67_ad = true;
}
if ((value & ERROR_SUPPLY_AIR) == ERROR_SUPPLY_AIR) {
errorValue.error_supply_air = true;
}
if ((value & ERROR_OUTGOING_AIR) == ERROR_OUTGOING_AIR) {
errorValue.error_outgoing_air = true;
}
if ((value & ERROR_CONDENSER) == ERROR_CONDENSER) {
errorValue.error_condenser = true;
}
if ((value & ERROR_PREHEATING) == ERROR_PREHEATING) {
errorValue.error_preheating = true;
}
return errorValue;
}
/**
* @return the error_temp_sensor_short
*/
public boolean isError_temp_sensor_short() {
return error_temp_sensor_short;
}
/**
* @return the error_offset
*/
public boolean isError_offset() {
return error_offset;
}
/**
* @return the error_temp_sensor_interupt
*/
public boolean isError_temp_sensor_interupt() {
return error_temp_sensor_interupt;
}
/**
* @return the error_high_pressure
*/
public boolean isError_high_pressure() {
return error_high_pressure;
}
/**
* @return the error_sys_ram
*/
public boolean isError_sys_ram() {
return error_sys_ram;
}
/**
* @return the error_sys_rom
*/
public boolean isError_sys_rom() {
return error_sys_rom;
}
/**
* @return the error_sys_ee
*/
public boolean isError_sys_ee() {
return error_sys_ee;
}
/**
* @return the error_sys_io
*/
public boolean isError_sys_io() {
return error_sys_io;
}
/**
* @return the error_sys_67_ad
*/
public boolean isError_sys_67_ad() {
return error_sys_67_ad;
}
/**
* @return the error_supply_air
*/
public boolean isError_supply_air() {
return error_supply_air;
}
/**
* @return the error_outgoing_air
*/
public boolean isError_outgoing_air() {
return error_outgoing_air;
}
/**
* @return the error_condenser
*/
public boolean isError_condenser() {
return error_condenser;
}
/**
* @return the error_preheating
*/
public boolean isError_preheating() {
return error_preheating;
}
}
/**
* Check if EVU blockade is on/off.
*
* @author Christian Spiegel
*
*/
private static final class EvuBlockadeHandler {
private static final int FLAG_BLOCKADE = 1;
private boolean blockade = false;
public static EvuBlockadeHandler valueOf(String read) {
EvuBlockadeHandler handler = new EvuBlockadeHandler();
read = read.trim();
int decPoint = read.indexOf(".");
if (decPoint > 0) {
read = read.substring(0, decPoint);
}
int value = Integer.valueOf(read.trim()) * -1;
if (logger.isDebugEnabled()) {
logger.debug("WR3223.EvuBlockadeHandler value={}.", value);
}
if ((value & FLAG_BLOCKADE) == FLAG_BLOCKADE) {
handler.blockade = true;
// TODO Anlage ausschalten bei EVU Sperre?
}
return handler;
}
/**
* @return the blockade
*/
public boolean isBlockade() {
return blockade;
}
}
}