]> git.basschouten.com Git - openhab-addons.git/blob
2323d52253513b36e4a9cb4a579786a49e27b026
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.homematic.internal.communicator.parser;
14
15 import java.io.IOException;
16 import java.util.Objects;
17
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.openhab.binding.homematic.internal.misc.HomematicConstants;
20 import org.openhab.binding.homematic.internal.misc.MiscUtils;
21 import org.openhab.binding.homematic.internal.model.HmDatapoint;
22 import org.openhab.binding.homematic.internal.model.HmParamsetType;
23 import org.openhab.binding.homematic.internal.model.HmValueType;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Abstract base class for all parsers with common methods.
29  *
30  * @author Gerhard Riegler - Initial contribution
31  */
32 public abstract class CommonRpcParser<M, R> implements RpcParser<M, R> {
33
34     private final Logger logger = LoggerFactory.getLogger(CommonRpcParser.class);
35
36     /**
37      * Converts the object to a string.
38      */
39     protected String toString(Object object) {
40         String value = Objects.toString(object, "").trim();
41         return value.isEmpty() ? null : value;
42     }
43
44     /**
45      * Converts the object to an integer.
46      */
47     protected Integer toInteger(Object object) {
48         if (object == null || object instanceof Integer) {
49             return (Integer) object;
50         }
51         try {
52             return Double.valueOf(object.toString()).intValue();
53         } catch (NumberFormatException ex) {
54             logger.debug("Failed converting {} to a Double", object, ex);
55             return null;
56         }
57     }
58
59     /**
60      * Converts the object to a double.
61      */
62     protected Double toDouble(Object object) {
63         if (object == null || object instanceof Double) {
64             return (Double) object;
65         }
66         try {
67             return Double.valueOf(object.toString());
68         } catch (NumberFormatException ex) {
69             logger.debug("Failed converting {} to a Double", object, ex);
70             return null;
71         }
72     }
73
74     /**
75      * Converts the object to a number.
76      */
77     protected Number toNumber(Object object) {
78         if (object == null || object instanceof Number) {
79             return (Number) object;
80         }
81         try {
82             String value = object.toString();
83             if (value.contains(".")) {
84                 return Float.parseFloat(value);
85             } else {
86                 return Integer.parseInt(value);
87             }
88         } catch (NumberFormatException ex) {
89             logger.debug("Failed converting {} to a Number", object, ex);
90             return null;
91         }
92     }
93
94     /**
95      * Converts the object to a boolean.
96      */
97     protected Boolean toBoolean(Object object) {
98         if (object == null || object instanceof Boolean) {
99             return (Boolean) object;
100         }
101         return "true".equals(object.toString().toLowerCase());
102     }
103
104     /**
105      * Converts the object to a string array.
106      */
107     protected String[] toOptionList(Object optionList) {
108         if (optionList != null && optionList instanceof Object[]) {
109             Object[] vl = (Object[]) optionList;
110             String[] stringArray = new String[vl.length];
111             for (int i = 0; i < vl.length; i++) {
112                 stringArray[i] = vl[i].toString();
113             }
114             return stringArray;
115         }
116         return null;
117     }
118
119     /**
120      * Returns the address of a device, replacing group address identifier and illegal characters.
121      */
122     @NonNull
123     protected String getSanitizedAddress(Object object) {
124         String address = Objects.toString(object, "").trim().replaceFirst("\\*", "T-");
125         return MiscUtils.validateCharacters(address.isEmpty() ? null : address, "Address", "_");
126     }
127
128     /**
129      * Adjust uninitialized rssi values to zero.
130      */
131     protected void adjustRssiValue(HmDatapoint dp) {
132         if (dp.getValue() != null && dp.getName().startsWith("RSSI_") && dp.isIntegerType()) {
133             int rssiValue = ((Number) dp.getValue()).intValue();
134             dp.setValue(getAdjustedRssiValue(rssiValue));
135         }
136     }
137
138     /**
139      * Adjust a rssi value if it is out of range.
140      */
141     protected Integer getAdjustedRssiValue(Integer rssiValue) {
142         if (rssiValue == null || rssiValue >= 255 || rssiValue <= -255) {
143             return 0;
144         }
145         return rssiValue;
146     }
147
148     /**
149      * Converts the value to the correct type if necessary.
150      */
151     protected Object convertToType(HmDatapoint dp, Object value) {
152         if (value == null) {
153             return null;
154         } else if (dp.isBooleanType()) {
155             return toBoolean(value);
156         } else if (dp.isIntegerType()) {
157             return toInteger(value);
158         } else if (dp.isFloatType()) {
159             return toNumber(value);
160         } else if (dp.isStringType()) {
161             return toString(value);
162         } else {
163             return value;
164         }
165     }
166
167     /**
168      * Assembles a datapoint with the given parameters.
169      */
170     protected HmDatapoint assembleDatapoint(String name, String unit, String type, String[] options, Object min,
171             Object max, Integer operations, Object defaultValue, HmParamsetType paramsetType, boolean isHmIpDevice)
172             throws IOException {
173         HmDatapoint dp = new HmDatapoint();
174         dp.setName(name);
175         dp.setDescription(name);
176         if (unit != null) {
177             unit = unit.trim().replace("\ufffd", "°");
178         }
179         dp.setUnit(unit == null || unit.isEmpty() ? null : unit);
180
181         // Bypass: For several devices the CCU does not send a unit together with the value in the data point definition
182         if (dp.getUnit() == null && dp.getName() != null) {
183             if (dp.getName().startsWith("RSSI_")) {
184                 dp.setUnit("dBm");
185             } else if (dp.getName().startsWith(HomematicConstants.DATAPOINT_NAME_OPERATING_VOLTAGE)) {
186                 dp.setUnit("V");
187             } else if (HomematicConstants.DATAPOINT_NAME_HUMIDITY.equals(dp.getName())) {
188                 dp.setUnit("%");
189             } else if (HomematicConstants.DATAPOINT_NAME_LEVEL.equals(dp.getName())) {
190                 dp.setUnit("100%");
191             }
192         }
193
194         HmValueType valueType = HmValueType.parse(type);
195         if (valueType == null || valueType == HmValueType.UNKNOWN) {
196             throw new IOException("Unknown datapoint type: " + type);
197         } else if (valueType == HmValueType.FLOAT && dp.getUnit() == null
198                 && dp.getName().matches("\\w*_TEMPERATURE(_\\w.*|$)")) {
199             logger.debug("No unit information found for temperature datapoint {}, assuming Number:Temperature",
200                     dp.getName());
201             dp.setUnit("°C"); // Bypass for a problem with HMIP devices where unit of temperature channels is sometimes
202                               // empty
203         }
204         dp.setType(valueType);
205
206         dp.setOptions(options);
207         if (dp.isNumberType() || dp.isEnumType()) {
208             if (isHmIpDevice && dp.isEnumType()) {
209                 dp.setMinValue(dp.getOptionIndex(toString(min)));
210                 dp.setMaxValue(dp.getOptionIndex(toString(max)));
211             } else {
212                 dp.setMinValue(toNumber(min));
213                 dp.setMaxValue(toNumber(max));
214             }
215         }
216         dp.setReadOnly((operations & 2) != 2);
217         dp.setReadable((operations & 1) == 1);
218         dp.setParamsetType(paramsetType);
219         if (isHmIpDevice && dp.isEnumType()) {
220             dp.setDefaultValue(dp.getOptionIndex(toString(defaultValue)));
221         } else {
222             dp.setDefaultValue(convertToType(dp, defaultValue));
223         }
224         dp.setValue(dp.getDefaultValue());
225         return dp;
226     }
227
228     /**
229      * Converts a string value to the type.
230      */
231     protected Object convertToType(String value) {
232         if (value == null || value.isBlank()) {
233             return null;
234         }
235         if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("on")) {
236             return (Boolean.TRUE);
237         } else if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("off")) {
238             return (Boolean.FALSE);
239         } else if (value.matches("(-|\\+)?[0-9]+")) {
240             return (Integer.valueOf(value));
241         } else if (value.matches("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?")) {
242             return (Double.valueOf(value));
243         } else {
244             return value;
245         }
246     }
247 }