2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.homematic.internal.communicator.parser;
15 import java.io.IOException;
17 import org.apache.commons.lang.BooleanUtils;
18 import org.apache.commons.lang.ObjectUtils;
19 import org.apache.commons.lang.StringUtils;
20 import org.apache.commons.lang.math.NumberUtils;
21 import org.openhab.binding.homematic.internal.misc.MiscUtils;
22 import org.openhab.binding.homematic.internal.model.HmDatapoint;
23 import org.openhab.binding.homematic.internal.model.HmParamsetType;
24 import org.openhab.binding.homematic.internal.model.HmValueType;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
29 * Abstract base class for all parsers with common methods.
31 * @author Gerhard Riegler - Initial contribution
33 public abstract class CommonRpcParser<M, R> implements RpcParser<M, R> {
35 private final Logger logger = LoggerFactory.getLogger(CommonRpcParser.class);
38 * Converts the object to a string.
40 protected String toString(Object object) {
41 return StringUtils.trimToNull(ObjectUtils.toString(object));
45 * Converts the object to a integer.
47 protected Integer toInteger(Object object) {
48 if (object == null || object instanceof Integer) {
49 return (Integer) object;
52 return Double.valueOf(ObjectUtils.toString(object)).intValue();
53 } catch (NumberFormatException ex) {
54 logger.debug("Failed converting {} to a Double", object, ex);
60 * Converts the object to a double.
62 protected Double toDouble(Object object) {
63 if (object == null || object instanceof Double) {
64 return (Double) object;
67 return Double.valueOf(ObjectUtils.toString(object));
68 } catch (NumberFormatException ex) {
69 logger.debug("Failed converting {} to a Double", object, ex);
75 * Converts the object to a number.
77 protected Number toNumber(Object object) {
78 if (object == null || object instanceof Number) {
79 return (Number) object;
82 return NumberUtils.createNumber(ObjectUtils.toString(object));
83 } catch (NumberFormatException ex) {
84 logger.debug("Failed converting {} to a Number", object, ex);
90 * Converts the object to a boolean.
92 protected Boolean toBoolean(Object object) {
93 if (object == null || object instanceof Boolean) {
94 return (Boolean) object;
96 return BooleanUtils.toBoolean(ObjectUtils.toString(object));
100 * Converts the object to a string array.
102 protected String[] toOptionList(Object optionList) {
103 if (optionList != null && optionList instanceof Object[]) {
104 Object[] vl = (Object[]) optionList;
105 String[] stringArray = new String[vl.length];
106 for (int i = 0; i < vl.length; i++) {
107 stringArray[i] = vl[i].toString();
115 * Returns the address of a device, replacing group address identifier and illegal characters.
117 protected String getSanitizedAddress(Object object) {
118 String address = StringUtils.trimToNull(StringUtils.replaceOnce(toString(object), "*", "T-"));
119 return MiscUtils.validateCharacters(address, "Address", "_");
123 * Adjust uninitialized rssi values to zero.
125 protected void adjustRssiValue(HmDatapoint dp) {
126 if (dp.getValue() != null && dp.getName().startsWith("RSSI_") && dp.isIntegerType()) {
127 int rssiValue = ((Number) dp.getValue()).intValue();
128 dp.setValue(getAdjustedRssiValue(rssiValue));
133 * Adjust a rssi value if it is out of range.
135 protected Integer getAdjustedRssiValue(Integer rssiValue) {
136 if (rssiValue == null || rssiValue >= 255 || rssiValue <= -255) {
143 * Converts the value to the correct type if necessary.
145 protected Object convertToType(HmDatapoint dp, Object value) {
148 } else if (dp.isBooleanType()) {
149 return toBoolean(value);
150 } else if (dp.isIntegerType()) {
151 return toInteger(value);
152 } else if (dp.isFloatType()) {
153 return toNumber(value);
154 } else if (dp.isStringType()) {
155 return toString(value);
162 * Assembles a datapoint with the given parameters.
164 protected HmDatapoint assembleDatapoint(String name, String unit, String type, String[] options, Object min,
165 Object max, Integer operations, Object defaultValue, HmParamsetType paramsetType, boolean isHmIpDevice)
167 HmDatapoint dp = new HmDatapoint();
169 dp.setDescription(name);
170 dp.setUnit(StringUtils.replace(StringUtils.trimToNull(unit), "\ufffd", "°"));
171 if (dp.getUnit() == null && StringUtils.startsWith(dp.getName(), "RSSI_")) {
175 HmValueType valueType = HmValueType.parse(type);
176 if (valueType == null || valueType == HmValueType.UNKNOWN) {
177 throw new IOException("Unknown datapoint type: " + type);
178 } else if (valueType == HmValueType.FLOAT && dp.getUnit() == null
179 && dp.getName().matches("\\w*_TEMPERATURE(_\\w.*|$)")) {
180 logger.debug("No unit information found for temperature datapoint {}, assuming Number:Temperature",
182 dp.setUnit("°C"); // Bypass for a problem with HMIP devices where unit of temperature channels is sometimes
185 dp.setType(valueType);
187 dp.setOptions(options);
188 if (dp.isNumberType() || dp.isEnumType()) {
189 if (isHmIpDevice && dp.isEnumType()) {
190 dp.setMinValue(dp.getOptionIndex(toString(min)));
191 dp.setMaxValue(dp.getOptionIndex(toString(max)));
193 dp.setMinValue(toNumber(min));
194 dp.setMaxValue(toNumber(max));
197 dp.setReadOnly((operations & 2) != 2);
198 dp.setReadable((operations & 1) == 1);
199 dp.setParamsetType(paramsetType);
200 if (isHmIpDevice && dp.isEnumType()) {
201 dp.setDefaultValue(dp.getOptionIndex(toString(defaultValue)));
203 dp.setDefaultValue(convertToType(dp, defaultValue));
205 dp.setValue(dp.getDefaultValue());
210 * Converts a string value to the type.
212 protected Object convertToType(String value) {
213 if (StringUtils.isBlank(value)) {
216 if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("on")) {
217 return (Boolean.TRUE);
218 } else if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("off")) {
219 return (Boolean.FALSE);
220 } else if (value.matches("(-|\\+)?[0-9]+")) {
221 return (Integer.valueOf(value));
222 } else if (value.matches("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?")) {
223 return (Double.valueOf(value));