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