]> git.basschouten.com Git - openhab-addons.git/blob
d37b4a4ee5bd6f731abf19eebd9feefcb57be0b0
[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.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(PRE + className + "from JSON (syntax/format error): " + json, e);
93             } catch (RuntimeException e) {
94                 throw new ShellyApiException(PRE + className + "from JSON: " + json, e);
95             }
96         }
97     }
98
99     @SuppressWarnings("unchecked")
100     private static <T> Class<T> wrap(Class<T> type) {
101         if (type == int.class) {
102             return (Class<T>) Integer.class;
103         }
104         if (type == float.class) {
105             return (Class<T>) Float.class;
106         }
107         if (type == byte.class) {
108             return (Class<T>) Byte.class;
109         }
110         if (type == double.class) {
111             return (Class<T>) Double.class;
112         }
113         if (type == long.class) {
114             return (Class<T>) Long.class;
115         }
116         if (type == char.class) {
117             return (Class<T>) Character.class;
118         }
119         if (type == boolean.class) {
120             return (Class<T>) Boolean.class;
121         }
122         if (type == short.class) {
123             return (Class<T>) Short.class;
124         }
125         if (type == void.class) {
126             return (Class<T>) Void.class;
127         }
128         return type;
129     }
130
131     public static String mkChannelId(String group, String channel) {
132         return group + "#" + channel;
133     }
134
135     public static String getString(@Nullable String value) {
136         return value != null ? value : "";
137     }
138
139     public static String substringBefore(@Nullable String string, String pattern) {
140         if (string != null) {
141             int pos = string.indexOf(pattern);
142             if (pos > 0) {
143                 return string.substring(0, pos);
144             }
145         }
146         return "";
147     }
148
149     public static String substringBeforeLast(@Nullable String string, String pattern) {
150         if (string != null) {
151             int pos = string.lastIndexOf(pattern);
152             if (pos > 0) {
153                 return string.substring(0, pos);
154             }
155         }
156         return "";
157     }
158
159     public static String substringAfter(@Nullable String string, String pattern) {
160         if (string != null) {
161             int pos = string.indexOf(pattern);
162             if (pos != -1) {
163                 return string.substring(pos + pattern.length());
164             }
165         }
166         return "";
167     }
168
169     public static String substringAfterLast(@Nullable String string, String pattern) {
170         if (string == null) {
171             return "";
172         }
173         int pos = string.lastIndexOf(pattern);
174         if (pos != -1) {
175             return string.substring(pos + pattern.length());
176         }
177         return string;
178     }
179
180     public static String substringBetween(@Nullable String string, String begin, String end) {
181         if (string != null) {
182             int s = string.indexOf(begin);
183             if (s != -1) {
184                 // The end tag might be included before the start tag, e.g.
185                 // when using "http://" and ":" to get the IP from http://192.168.1.1:8081/xxx
186                 // therefore make it 2 steps
187                 String result = string.substring(s + begin.length());
188                 return substringBefore(result, end);
189             }
190         }
191         return "";
192     }
193
194     public static String getMessage(Exception e) {
195         String message = e.getMessage();
196         return message != null ? message : "";
197     }
198
199     public static Integer getInteger(@Nullable Integer value) {
200         return (value != null ? (Integer) value : 0);
201     }
202
203     public static Long getLong(@Nullable Long value) {
204         return (value != null ? (Long) value : 0);
205     }
206
207     public static Double getDouble(@Nullable Double value) {
208         return (value != null ? (Double) value : 0);
209     }
210
211     public static Boolean getBool(@Nullable Boolean value) {
212         return (value != null ? (Boolean) value : false);
213     }
214
215     // as State
216
217     public static StringType getStringType(@Nullable String value) {
218         return new StringType(value != null ? value : "");
219     }
220
221     public static DecimalType getDecimal(@Nullable Double value) {
222         return new DecimalType((value != null ? value : 0));
223     }
224
225     public static DecimalType getDecimal(@Nullable Integer value) {
226         return new DecimalType((value != null ? value : 0));
227     }
228
229     public static DecimalType getDecimal(@Nullable Long value) {
230         return new DecimalType((value != null ? value : 0));
231     }
232
233     public static Double getNumber(Command command) throws IllegalArgumentException {
234         if (command instanceof DecimalType) {
235             return ((DecimalType) command).doubleValue();
236         }
237         if (command instanceof QuantityType) {
238             return ((QuantityType<?>) command).doubleValue();
239         }
240         throw new IllegalArgumentException("Unable to convert number");
241     }
242
243     public static OnOffType getOnOff(@Nullable Boolean value) {
244         return (value != null ? value ? OnOffType.ON : OnOffType.OFF : OnOffType.OFF);
245     }
246
247     public static OnOffType getOnOff(int value) {
248         return value == 0 ? OnOffType.OFF : OnOffType.ON;
249     }
250
251     public static State toQuantityType(@Nullable Double value, int digits, Unit<?> unit) {
252         if (value == null) {
253             return UnDefType.NULL;
254         }
255         BigDecimal bd = new BigDecimal(value.doubleValue());
256         return toQuantityType(bd.setScale(digits, RoundingMode.HALF_UP), unit);
257     }
258
259     public static State toQuantityType(@Nullable Number value, Unit<?> unit) {
260         return value == null ? UnDefType.NULL : new QuantityType<>(value, unit);
261     }
262
263     public static State toQuantityType(@Nullable PercentType value, Unit<?> unit) {
264         return value == null ? UnDefType.NULL : toQuantityType(value.toBigDecimal(), unit);
265     }
266
267     public static void validateRange(String name, Integer value, int min, int max) {
268         if ((value < min) || (value > max)) {
269             throw new IllegalArgumentException("Value " + name + " is out of range (" + min + "-" + max + ")");
270         }
271     }
272
273     public static String urlEncode(String input) {
274         try {
275             return URLEncoder.encode(input, StandardCharsets.UTF_8.toString());
276         } catch (UnsupportedEncodingException e) {
277             return input;
278         }
279     }
280
281     public static Long now() {
282         return System.currentTimeMillis() / 1000L;
283     }
284
285     public static DateTimeType getTimestamp() {
286         return new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochSecond(now()), ZoneId.systemDefault()));
287     }
288
289     public static DateTimeType getTimestamp(String zone, long timestamp) {
290         try {
291             if (timestamp == 0) {
292                 throw new IllegalArgumentException("Timestamp value 0 is invalid");
293             }
294             ZoneId zoneId = !zone.isEmpty() ? ZoneId.of(zone) : ZoneId.systemDefault();
295             ZonedDateTime zdt = LocalDateTime.now().atZone(zoneId);
296             int delta = zdt.getOffset().getTotalSeconds();
297             return new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochSecond(timestamp - delta), zoneId));
298         } catch (DateTimeException e) {
299             // Unable to convert device's timezone, use system one
300             return getTimestamp();
301         }
302     }
303
304     public static String getTimestamp(DateTimeType dt) {
305         return dt.getZonedDateTime().toString().replace('T', ' ').replace('-', '/');
306     }
307
308     public static String convertTimestamp(long ts) {
309         if (ts == 0) {
310             return "";
311         }
312         String time = DATE_TIME.format(ZonedDateTime.ofInstant(Instant.ofEpochSecond(ts), ZoneId.systemDefault()));
313         return time.replace('T', ' ').replace('-', '/');
314     }
315
316     public static Integer getLightIdFromGroup(String groupName) {
317         if (groupName.startsWith(CHANNEL_GROUP_LIGHT_CHANNEL)) {
318             return Integer.parseInt(substringAfter(groupName, CHANNEL_GROUP_LIGHT_CHANNEL)) - 1;
319         }
320         return 0; // only 1 light, e.g. bulb or rgbw2 in color mode
321     }
322
323     public static String buildControlGroupName(ShellyDeviceProfile profile, Integer channelId) {
324         return profile.isBulb || profile.isDuo || profile.inColor ? CHANNEL_GROUP_LIGHT_CONTROL
325                 : CHANNEL_GROUP_LIGHT_CHANNEL + channelId.toString();
326     }
327
328     public static String buildWhiteGroupName(ShellyDeviceProfile profile, Integer channelId) {
329         return profile.isBulb || profile.isDuo ? CHANNEL_GROUP_WHITE_CONTROL
330                 : CHANNEL_GROUP_LIGHT_CHANNEL + channelId.toString();
331     }
332
333     public static DecimalType mapSignalStrength(int dbm) {
334         int strength = -1;
335         if (dbm > -60) {
336             strength = 4;
337         } else if (dbm > -70) {
338             strength = 3;
339         } else if (dbm > -80) {
340             strength = 2;
341         } else if (dbm > -90) {
342             strength = 1;
343         } else {
344             strength = 0;
345         }
346         return new DecimalType(strength);
347     }
348 }