]> git.basschouten.com Git - openhab-addons.git/blob
dd7b1ea9296686e1b64aa6550a4bbbf50c8fbb7e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.shelly.internal.util;
14
15 import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
16
17 import java.io.UnsupportedEncodingException;
18 import java.math.BigDecimal;
19 import java.math.RoundingMode;
20 import java.net.URLEncoder;
21 import java.nio.charset.StandardCharsets;
22 import java.time.DateTimeException;
23 import java.time.Instant;
24 import java.time.LocalDateTime;
25 import java.time.ZoneId;
26 import java.time.ZonedDateTime;
27 import java.time.format.DateTimeFormatter;
28
29 import javax.measure.Unit;
30
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.openhab.binding.shelly.internal.api.ShellyApiException;
34 import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
35 import org.openhab.core.library.types.DateTimeType;
36 import org.openhab.core.library.types.DecimalType;
37 import org.openhab.core.library.types.OnOffType;
38 import org.openhab.core.library.types.PercentType;
39 import org.openhab.core.library.types.QuantityType;
40 import org.openhab.core.library.types.StringType;
41 import org.openhab.core.types.Command;
42 import org.openhab.core.types.State;
43 import org.openhab.core.types.UnDefType;
44
45 import com.google.gson.Gson;
46 import com.google.gson.JsonSyntaxException;
47
48 /**
49  * {@link ShellyUtils} provides general utility functions
50  *
51  * @author Markus Michels - Initial contribution
52  */
53 @NonNullByDefault
54 public class ShellyUtils {
55     private static final String PRE = "Unable to create object of type ";
56     public static final DateTimeFormatter DATE_TIME = DateTimeFormatter.ofPattern(DateTimeType.DATE_PATTERN);
57
58     public static <T> T fromJson(Gson gson, @Nullable String json, Class<T> classOfT) throws ShellyApiException {
59         @Nullable
60         T o = fromJson(gson, json, classOfT, true);
61         if (o == null) {
62             throw new ShellyApiException("Unable to create JSON object");
63         }
64         return o;
65     }
66
67     public static @Nullable <T> T fromJson(Gson gson, @Nullable String json, Class<T> classOfT, boolean exceptionOnNull)
68             throws ShellyApiException {
69         String className = substringAfter(classOfT.getName(), "$");
70
71         if (json == null) {
72             if (exceptionOnNull) {
73                 throw new IllegalArgumentException(PRE + className + ": json is null!");
74             } else {
75                 return null;
76             }
77         }
78
79         if (classOfT.isInstance(json)) {
80             return wrap(classOfT).cast(json);
81         } else if (json.isEmpty()) { // update GSON might return null
82             throw new ShellyApiException(PRE + className + "from empty JSON");
83         } else {
84             try {
85                 @Nullable
86                 T obj = gson.fromJson(json, classOfT);
87                 if ((obj == null) && exceptionOnNull) { // new in OH3: fromJson may return null
88                     throw new ShellyApiException(PRE + className + "from JSON: " + json);
89                 }
90                 return obj;
91             } catch (JsonSyntaxException e) {
92                 throw new ShellyApiException(
93                         PRE + className + "from JSON (syntax/format error: " + e.getMessage() + "): " + json, e);
94             } catch (RuntimeException e) {
95                 throw new ShellyApiException(PRE + className + "from JSON: " + json, e);
96             }
97         }
98     }
99
100     @SuppressWarnings("unchecked")
101     private static <T> Class<T> wrap(Class<T> type) {
102         if (type == int.class) {
103             return (Class<T>) Integer.class;
104         }
105         if (type == float.class) {
106             return (Class<T>) Float.class;
107         }
108         if (type == byte.class) {
109             return (Class<T>) Byte.class;
110         }
111         if (type == double.class) {
112             return (Class<T>) Double.class;
113         }
114         if (type == long.class) {
115             return (Class<T>) Long.class;
116         }
117         if (type == char.class) {
118             return (Class<T>) Character.class;
119         }
120         if (type == boolean.class) {
121             return (Class<T>) Boolean.class;
122         }
123         if (type == short.class) {
124             return (Class<T>) Short.class;
125         }
126         if (type == void.class) {
127             return (Class<T>) Void.class;
128         }
129         return type;
130     }
131
132     public static String mkChannelId(String group, String channel) {
133         return group + "#" + channel;
134     }
135
136     public static String getString(@Nullable String value) {
137         return value != null ? value : "";
138     }
139
140     public static String substringBefore(@Nullable String string, String pattern) {
141         if (string != null) {
142             int pos = string.indexOf(pattern);
143             if (pos > 0) {
144                 return string.substring(0, pos);
145             }
146         }
147         return "";
148     }
149
150     public static String substringBeforeLast(@Nullable String string, String pattern) {
151         if (string != null) {
152             int pos = string.lastIndexOf(pattern);
153             if (pos > 0) {
154                 return string.substring(0, pos);
155             }
156         }
157         return "";
158     }
159
160     public static String substringAfter(@Nullable String string, String pattern) {
161         if (string != null) {
162             int pos = string.indexOf(pattern);
163             if (pos != -1) {
164                 return string.substring(pos + pattern.length());
165             }
166         }
167         return "";
168     }
169
170     public static String substringAfterLast(@Nullable String string, String pattern) {
171         if (string == null) {
172             return "";
173         }
174         int pos = string.lastIndexOf(pattern);
175         if (pos != -1) {
176             return string.substring(pos + pattern.length());
177         }
178         return string;
179     }
180
181     public static String substringBetween(@Nullable String string, String begin, String end) {
182         if (string != null) {
183             int s = string.indexOf(begin);
184             if (s != -1) {
185                 // The end tag might be included before the start tag, e.g.
186                 // when using "http://" and ":" to get the IP from http://192.168.1.1:8081/xxx
187                 // therefore make it 2 steps
188                 String result = string.substring(s + begin.length());
189                 return substringBefore(result, end);
190             }
191         }
192         return "";
193     }
194
195     public static String getMessage(Exception e) {
196         String message = e.getMessage();
197         return message != null ? message : "";
198     }
199
200     public static Integer getInteger(@Nullable Integer value) {
201         return (value != null ? (Integer) value : 0);
202     }
203
204     public static Long getLong(@Nullable Long value) {
205         return (value != null ? (Long) value : 0);
206     }
207
208     public static Double getDouble(@Nullable Double value) {
209         return (value != null ? (Double) value : 0);
210     }
211
212     public static Boolean getBool(@Nullable Boolean value) {
213         return (value != null ? (Boolean) value : false);
214     }
215
216     // as State
217
218     public static StringType getStringType(@Nullable String value) {
219         return new StringType(value != null ? value : "");
220     }
221
222     public static DecimalType getDecimal(@Nullable Double value) {
223         return new DecimalType((value != null ? value : 0));
224     }
225
226     public static DecimalType getDecimal(@Nullable Integer value) {
227         return new DecimalType((value != null ? value : 0));
228     }
229
230     public static DecimalType getDecimal(@Nullable Long value) {
231         return new DecimalType((value != null ? value : 0));
232     }
233
234     public static Double getNumber(Command command) throws IllegalArgumentException {
235         if (command instanceof DecimalType) {
236             return ((DecimalType) command).doubleValue();
237         }
238         if (command instanceof QuantityType) {
239             return ((QuantityType<?>) command).doubleValue();
240         }
241         throw new IllegalArgumentException("Unable to convert number");
242     }
243
244     public static OnOffType getOnOff(@Nullable Boolean value) {
245         return (value != null ? value ? OnOffType.ON : OnOffType.OFF : OnOffType.OFF);
246     }
247
248     public static OnOffType getOnOff(int value) {
249         return value == 0 ? OnOffType.OFF : OnOffType.ON;
250     }
251
252     public static State toQuantityType(@Nullable Double value, int digits, Unit<?> unit) {
253         if (value == null) {
254             return UnDefType.NULL;
255         }
256         BigDecimal bd = new BigDecimal(value.doubleValue());
257         return toQuantityType(bd.setScale(digits, RoundingMode.HALF_UP), unit);
258     }
259
260     public static State toQuantityType(@Nullable Number value, Unit<?> unit) {
261         return value == null ? UnDefType.NULL : new QuantityType<>(value, unit);
262     }
263
264     public static State toQuantityType(@Nullable PercentType value, Unit<?> unit) {
265         return value == null ? UnDefType.NULL : toQuantityType(value.toBigDecimal(), unit);
266     }
267
268     public static void validateRange(String name, Integer value, int min, int max) {
269         if ((value < min) || (value > max)) {
270             throw new IllegalArgumentException("Value " + name + " is out of range (" + min + "-" + max + ")");
271         }
272     }
273
274     public static String urlEncode(String input) {
275         try {
276             return URLEncoder.encode(input, StandardCharsets.UTF_8.toString());
277         } catch (UnsupportedEncodingException e) {
278             return input;
279         }
280     }
281
282     public static Long now() {
283         return System.currentTimeMillis() / 1000L;
284     }
285
286     public static DateTimeType getTimestamp() {
287         return new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochSecond(now()), ZoneId.systemDefault()));
288     }
289
290     public static DateTimeType getTimestamp(String zone, long timestamp) {
291         try {
292             ZoneId zoneId = !zone.isEmpty() ? ZoneId.of(zone) : ZoneId.systemDefault();
293             ZonedDateTime zdt = LocalDateTime.now().atZone(zoneId);
294             int delta = zdt.getOffset().getTotalSeconds();
295             return new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochSecond(timestamp - delta), zoneId));
296         } catch (DateTimeException e) {
297             // Unable to convert device's timezone, use system one
298             return getTimestamp();
299         }
300     }
301
302     public static String getTimestamp(DateTimeType dt) {
303         return dt.getZonedDateTime().toString().replace('T', ' ').replace('-', '/');
304     }
305
306     public static String convertTimestamp(long ts) {
307         if (ts == 0) {
308             return "";
309         }
310         String time = DATE_TIME.format(ZonedDateTime.ofInstant(Instant.ofEpochSecond(ts), ZoneId.systemDefault()));
311         return time.replace('T', ' ').replace('-', '/');
312     }
313
314     public static Integer getLightIdFromGroup(String groupName) {
315         if (groupName.startsWith(CHANNEL_GROUP_LIGHT_CHANNEL)) {
316             return Integer.parseInt(substringAfter(groupName, CHANNEL_GROUP_LIGHT_CHANNEL)) - 1;
317         }
318         return 0; // only 1 light, e.g. bulb or rgbw2 in color mode
319     }
320
321     public static String buildControlGroupName(ShellyDeviceProfile profile, Integer channelId) {
322         return profile.isBulb || profile.isDuo || profile.inColor ? CHANNEL_GROUP_LIGHT_CONTROL
323                 : CHANNEL_GROUP_LIGHT_CHANNEL + channelId.toString();
324     }
325
326     public static String buildWhiteGroupName(ShellyDeviceProfile profile, Integer channelId) {
327         return profile.isBulb || profile.isDuo ? CHANNEL_GROUP_WHITE_CONTROL
328                 : CHANNEL_GROUP_LIGHT_CHANNEL + channelId.toString();
329     }
330
331     public static DecimalType mapSignalStrength(int dbm) {
332         int strength = -1;
333         if (dbm > -60) {
334             strength = 4;
335         } else if (dbm > -70) {
336             strength = 3;
337         } else if (dbm > -80) {
338             strength = 2;
339         } else if (dbm > -90) {
340             strength = 1;
341         } else {
342             strength = 0;
343         }
344         return new DecimalType(strength);
345     }
346
347     public static boolean isDigit(char c) {
348         return c >= '0' && c <= '9';
349     }
350
351     public static char lastChar(String s) {
352         return s.length() > 1 ? s.charAt(s.length() - 1) : '*';
353     }
354 }