]> git.basschouten.com Git - openhab-addons.git/blob
ef5178b285fad97854653803281e4e51ec5c0a3b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.type;
14
15 import static org.openhab.binding.homematic.internal.HomematicBindingConstants.*;
16 import static org.openhab.binding.homematic.internal.misc.HomematicConstants.*;
17
18 import java.io.BufferedReader;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.InputStreamReader;
22 import java.math.BigDecimal;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.ResourceBundle;
30 import java.util.Set;
31
32 import org.apache.commons.lang.StringUtils;
33 import org.apache.commons.lang.WordUtils;
34 import org.openhab.binding.homematic.internal.model.HmDatapoint;
35 import org.openhab.binding.homematic.internal.model.HmDevice;
36 import org.openhab.core.config.core.ConfigDescriptionParameter.Type;
37 import org.osgi.framework.Bundle;
38 import org.osgi.framework.FrameworkUtil;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * Helper methods for generating the openHAB metadata.
44  *
45  * @author Gerhard Riegler - Initial contribution
46  * @author Michael Reitler - QuantityType support
47  */
48
49 public class MetadataUtils {
50     private static final Logger logger = LoggerFactory.getLogger(MetadataUtils.class);
51     private static ResourceBundle descriptionsBundle;
52     private static Map<String, String> descriptions = new HashMap<>();
53     private static Map<String, Set<String>> standardDatapoints = new HashMap<>();
54
55     protected static void initialize() {
56         // loads all Homematic device names
57         loadBundle("homematic/generated-descriptions");
58         loadBundle("homematic/extra-descriptions");
59         loadStandardDatapoints();
60     }
61
62     private static void loadBundle(String filename) {
63         descriptionsBundle = ResourceBundle.getBundle(filename, Locale.getDefault());
64         for (String key : descriptionsBundle.keySet()) {
65             descriptions.put(key.toUpperCase(), descriptionsBundle.getString(key));
66         }
67         ResourceBundle.clearCache();
68         descriptionsBundle = null;
69     }
70
71     /**
72      * Loads the standard datapoints for channel metadata generation.
73      */
74     private static void loadStandardDatapoints() {
75         Bundle bundle = FrameworkUtil.getBundle(MetadataUtils.class);
76         try (InputStream stream = bundle.getResource("homematic/standard-datapoints.properties").openStream();
77                 BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
78             String line;
79             while ((line = reader.readLine()) != null) {
80                 if (StringUtils.trimToNull(line) != null && !StringUtils.startsWith(line, "#")) {
81                     String channelType = StringUtils.trimToNull(StringUtils.substringBefore(line, "|"));
82                     String datapointName = StringUtils.trimToNull(StringUtils.substringAfter(line, "|"));
83
84                     Set<String> channelDatapoints = standardDatapoints.get(channelType);
85                     if (channelDatapoints == null) {
86                         channelDatapoints = new HashSet<>();
87                         standardDatapoints.put(channelType, channelDatapoints);
88                     }
89
90                     channelDatapoints.add(datapointName);
91                 }
92             }
93         } catch (IllegalStateException | IOException e) {
94             logger.warn("Can't load standard-datapoints.properties file!", e);
95         }
96     }
97
98     public interface OptionsBuilder<T> {
99         public T createOption(String value, String description);
100     }
101
102     /**
103      * Creates channel and config description metadata options for the given Datapoint.
104      */
105     public static <T> List<T> generateOptions(HmDatapoint dp, OptionsBuilder<T> optionsBuilder) {
106         List<T> options = null;
107         if (dp.getOptions() == null) {
108             logger.warn("No options for ENUM datapoint {}", dp);
109         } else {
110             options = new ArrayList<>();
111             for (int i = 0; i < dp.getOptions().length; i++) {
112                 String description = null;
113                 if (!dp.isVariable() && !dp.isScript()) {
114                     description = getDescription(dp.getChannel().getType(), dp.getName(), dp.getOptions()[i]);
115                 }
116                 if (description == null) {
117                     description = dp.getOptions()[i];
118                 }
119                 options.add(optionsBuilder.createOption(dp.getOptions()[i], description));
120             }
121         }
122         return options;
123     }
124
125     /**
126      * Returns the ConfigDescriptionParameter type for the given Datapoint.
127      */
128     public static Type getConfigDescriptionParameterType(HmDatapoint dp) {
129         if (dp.isBooleanType()) {
130             return Type.BOOLEAN;
131         } else if (dp.isIntegerType()) {
132             return Type.INTEGER;
133         } else if (dp.isFloatType()) {
134             return Type.DECIMAL;
135         } else {
136             return Type.TEXT;
137         }
138     }
139
140     /**
141      * Returns the unit metadata string for the given Datapoint.
142      */
143     public static String getUnit(HmDatapoint dp) {
144         if (dp.getUnit() != null) {
145             String unit = StringUtils.replace(dp.getUnit(), "100%", "%");
146             return StringUtils.replace(unit, "%", "%%");
147         }
148         return null;
149     }
150
151     /**
152      * Returns the pattern metadata string for the given Datapoint.
153      */
154     public static String getPattern(HmDatapoint dp) {
155         if (dp.isFloatType()) {
156             return "%.2f";
157         } else if (dp.isNumberType()) {
158             return "%d";
159         } else {
160             return null;
161         }
162     }
163
164     /**
165      * Returns the state pattern metadata string with unit for the given Datapoint.
166      */
167     public static String getStatePattern(HmDatapoint dp) {
168         String unit = getUnit(dp);
169         if ("%%".equals(unit)) {
170             return "%d %%";
171         }
172         if (unit != null && unit != "") {
173             String pattern = getPattern(dp);
174             if (pattern != null) {
175                 return String.format("%s %s", pattern, "%unit%");
176             }
177         }
178         return null;
179     }
180
181     /**
182      * Returns the label string for the given Datapoint.
183      */
184     public static String getLabel(HmDatapoint dp) {
185         return WordUtils.capitalizeFully(StringUtils.replace(dp.getName(), "_", " "));
186     }
187
188     /**
189      * Returns the parameter name for the specified Datapoint.
190      */
191     public static String getParameterName(HmDatapoint dp) {
192         return String.format("HMP_%d_%s", dp.getChannel().getNumber(), dp.getName());
193     }
194
195     /**
196      * Returns the description for the given keys.
197      */
198     public static String getDescription(String... keys) {
199         StringBuilder sb = new StringBuilder();
200         for (int startIdx = 0; startIdx < keys.length; startIdx++) {
201             String key = StringUtils.join(keys, "|", startIdx, keys.length);
202             if (key.endsWith("|")) {
203                 key = key.substring(0, key.length() - 1);
204             }
205             String description = descriptions.get(key.toUpperCase());
206             if (description != null) {
207                 return description;
208             }
209             sb.append(key).append(", ");
210         }
211         if (logger.isTraceEnabled()) {
212             logger.trace("Description not found for: {}", StringUtils.substring(sb.toString(), 0, -2));
213         }
214         return null;
215     }
216
217     /**
218      * Returns the device name for the given device type.
219      */
220     public static String getDeviceName(HmDevice device) {
221         if (device.isGatewayExtras()) {
222             return getDescription(HmDevice.TYPE_GATEWAY_EXTRAS);
223         }
224
225         String deviceDescription = null;
226         boolean isTeam = device.getType().endsWith("-Team");
227         String type = isTeam ? StringUtils.remove(device.getType(), "-Team") : device.getType();
228         deviceDescription = getDescription(type);
229
230         if (deviceDescription != null && isTeam) {
231             deviceDescription += " Team";
232         }
233
234         return deviceDescription == null ? "No Description" : deviceDescription;
235     }
236
237     /**
238      * Returns the description for the given datapoint.
239      */
240     public static String getDatapointDescription(HmDatapoint dp) {
241         if (dp.isVariable() || dp.isScript()) {
242             return null;
243         }
244         return getDescription(dp.getChannel().getType(), dp.getName());
245     }
246
247     /**
248      * Returns true, if the given datapoint is a standard datapoint.
249      */
250     public static boolean isStandard(HmDatapoint dp) {
251         Set<String> channelDatapoints = standardDatapoints.get(dp.getChannel().getType());
252         if (channelDatapoints == null) {
253             return true;
254         }
255
256         return channelDatapoints.contains(dp.getName());
257     }
258
259     /**
260      * Helper method for creating a BigDecimal.
261      */
262     public static BigDecimal createBigDecimal(Number number) {
263         if (number == null) {
264             return null;
265         }
266         try {
267             return new BigDecimal(number.toString());
268         } catch (Exception ex) {
269             logger.warn("Can't create BigDecimal for number: {}", number.toString());
270             return null;
271         }
272     }
273
274     /**
275      * Determines the itemType for the given Datapoint.
276      */
277     public static String getItemType(HmDatapoint dp) {
278         String dpName = dp.getName();
279         String channelType = StringUtils.defaultString(dp.getChannel().getType());
280
281         if (dp.isBooleanType()) {
282             if (((dpName.equals(DATAPOINT_NAME_STATE) || dpName.equals(VIRTUAL_DATAPOINT_NAME_STATE_CONTACT))
283                     && (channelType.equals(CHANNEL_TYPE_SHUTTER_CONTACT)
284                             || channelType.contentEquals(CHANNEL_TYPE_TILT_SENSOR)))
285                     || (dpName.equals(DATAPOINT_NAME_SENSOR) && channelType.equals(CHANNEL_TYPE_SENSOR))) {
286                 return ITEM_TYPE_CONTACT;
287             } else {
288                 return ITEM_TYPE_SWITCH;
289             }
290         } else if (dp.isNumberType()) {
291             if (dpName.startsWith(DATAPOINT_NAME_LEVEL) && isRollerShutter(dp)) {
292                 return ITEM_TYPE_ROLLERSHUTTER;
293             } else if (dpName.startsWith(DATAPOINT_NAME_LEVEL) && !channelType.equals(CHANNEL_TYPE_WINMATIC)
294                     && !channelType.equals(CHANNEL_TYPE_AKKU)) {
295                 return ITEM_TYPE_DIMMER;
296             } else {
297                 // determine QuantityType
298                 String unit = dp.getUnit() != null ? dp.getUnit() : "";
299                 switch (unit) {
300                     case "°C":
301                     case "°C":
302                         return ITEM_TYPE_NUMBER + ":Temperature";
303                     case "V":
304                         return ITEM_TYPE_NUMBER + ":ElectricPotential";
305                     case "%":
306                         return ITEM_TYPE_NUMBER + ":Dimensionless";
307                     case "mHz":
308                     case "Hz":
309                         return ITEM_TYPE_NUMBER + ":Frequency";
310                     case "hPa":
311                         return ITEM_TYPE_NUMBER + ":Pressure";
312                     case "Lux":
313                         return ITEM_TYPE_NUMBER + ":Illuminance";
314                     case "degree":
315                         return ITEM_TYPE_NUMBER + ":Angle";
316                     case "km/h":
317                         return ITEM_TYPE_NUMBER + ":Speed";
318                     case "mm":
319                         return ITEM_TYPE_NUMBER + ":Length";
320                     case "W":
321                         return ITEM_TYPE_NUMBER + ":Power";
322                     case "Wh":
323                         return ITEM_TYPE_NUMBER + ":Energy";
324                     case "m3":
325                         return ITEM_TYPE_NUMBER + ":Volume";
326                     case "s":
327                     case "min":
328                     case "minutes":
329                     case "day":
330                     case "month":
331                     case "year":
332                     case "100%":
333                     case "":
334                     default:
335                         return ITEM_TYPE_NUMBER;
336                 }
337             }
338         } else if (dp.isDateTimeType()) {
339             return ITEM_TYPE_DATETIME;
340         } else {
341             return ITEM_TYPE_STRING;
342         }
343     }
344
345     /**
346      * Returns true, if the device of the datapoint is a rollershutter.
347      */
348     public static boolean isRollerShutter(HmDatapoint dp) {
349         String channelType = dp.getChannel().getType();
350         return channelType.equals(CHANNEL_TYPE_BLIND) || channelType.equals(CHANNEL_TYPE_JALOUSIE)
351                 || channelType.equals(CHANNEL_TYPE_BLIND_TRANSMITTER)
352                 || channelType.equals(CHANNEL_TYPE_SHUTTER_TRANSMITTER)
353                 || channelType.equals(CHANNEL_TYPE_SHUTTER_VIRTUAL_RECEIVER)
354                 || channelType.contentEquals(CHANNEL_TYPE_BLIND_VIRTUAL_RECEIVER);
355     }
356
357     /**
358      * Determines the category for the given Datapoint.
359      */
360     public static String getCategory(HmDatapoint dp, String itemType) {
361         String dpName = dp.getName();
362         String channelType = StringUtils.defaultString(dp.getChannel().getType());
363
364         if (dpName.equals(DATAPOINT_NAME_BATTERY_TYPE) || dpName.equals(DATAPOINT_NAME_LOWBAT)
365                 || dpName.equals(DATAPOINT_NAME_LOWBAT_IP)) {
366             return CATEGORY_BATTERY;
367         } else if (dpName.equals(DATAPOINT_NAME_STATE) && channelType.equals(CHANNEL_TYPE_ALARMACTUATOR)) {
368             return CATEGORY_ALARM;
369         } else if (dpName.equals(DATAPOINT_NAME_HUMIDITY)) {
370             return CATEGORY_HUMIDITY;
371         } else if (dpName.contains(DATAPOINT_NAME_TEMPERATURE)) {
372             return CATEGORY_TEMPERATURE;
373         } else if (dpName.equals(DATAPOINT_NAME_MOTION)) {
374             return CATEGORY_MOTION;
375         } else if (dpName.equals(DATAPOINT_NAME_AIR_PRESSURE)) {
376             return CATEGORY_PRESSURE;
377         } else if (dpName.equals(DATAPOINT_NAME_STATE) && channelType.equals(CHANNEL_TYPE_SMOKE_DETECTOR)) {
378             return CATEGORY_SMOKE;
379         } else if (dpName.equals(DATAPOINT_NAME_STATE) && channelType.equals(CHANNEL_TYPE_WATERDETECTIONSENSOR)) {
380             return CATEGORY_WATER;
381         } else if (dpName.equals(DATAPOINT_NAME_WIND_SPEED)) {
382             return CATEGORY_WIND;
383         } else if (dpName.startsWith(DATAPOINT_NAME_RAIN)
384                 || dpName.equals(DATAPOINT_NAME_STATE) && channelType.equals(CHANNEL_TYPE_RAINDETECTOR)) {
385             return CATEGORY_RAIN;
386         } else if (channelType.equals(CHANNEL_TYPE_POWERMETER) && !dpName.equals(DATAPOINT_NAME_BOOT)
387                 && !dpName.equals(DATAPOINT_NAME_FREQUENCY)) {
388             return CATEGORY_ENERGY;
389         } else if (itemType.equals(ITEM_TYPE_ROLLERSHUTTER)) {
390             return CATEGORY_BLINDS;
391         } else if (itemType.equals(ITEM_TYPE_CONTACT)) {
392             return CATEGORY_CONTACT;
393         } else if (itemType.equals(ITEM_TYPE_DIMMER)) {
394             return "";
395         } else if (itemType.equals(ITEM_TYPE_SWITCH)) {
396             return CATEGORY_SWITCH;
397         } else {
398             return null;
399         }
400     }
401 }