]> git.basschouten.com Git - openhab-addons.git/blob
c2e325361dcb2ebc6085d42d20244034e5c173da
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.netatmo.internal.api.data;
14
15 import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
16 import static org.openhab.core.library.CoreItemFactory.*;
17 import static org.openhab.core.library.unit.MetricPrefix.*;
18
19 import java.net.URI;
20 import java.util.Arrays;
21 import java.util.EnumSet;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.stream.Collectors;
26 import java.util.stream.Stream;
27
28 import javax.measure.Unit;
29
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.openhab.core.library.unit.SIUnits;
32 import org.openhab.core.library.unit.Units;
33 import org.openhab.core.types.StateDescriptionFragment;
34 import org.openhab.core.types.StateDescriptionFragmentBuilder;
35 import org.openhab.core.types.util.UnitUtils;
36
37 import com.google.gson.annotations.SerializedName;
38
39 /**
40  * This class holds various definitions and settings provided by the Netatmo
41  * API documentation
42  *
43  * @author GaĆ«l L'hopital - Initial contribution
44  */
45 @NonNullByDefault
46 public class NetatmoConstants {
47     public static class Measure {
48         public final double minValue;
49         public final double maxValue;
50         public final int scale;
51         public final Unit<?> unit;
52
53         private Measure(double minValue, double maxValue, double precision, Unit<?> unit) {
54             this.minValue = minValue;
55             this.maxValue = maxValue;
56             this.unit = unit;
57             String[] splitter = Double.toString(precision).split("\\.");
58             if (splitter.length > 1) {
59                 int dec = Integer.parseInt(splitter[1]);
60                 this.scale = dec > 0 ? Integer.toString(dec).length() : 0;
61             } else {
62                 this.scale = 0;
63             }
64         }
65     }
66
67     public static class MeasureChannelDetails {
68         private static final StateDescriptionFragmentBuilder BUILDER = StateDescriptionFragmentBuilder.create();
69         public final URI configURI;
70         public final String itemType;
71         public final StateDescriptionFragment stateDescriptionFragment;
72
73         private MeasureChannelDetails(String measureType, String itemType, String pattern) {
74             this.configURI = URI.create(String.join(":", BINDING_ID, measureType, "config"));
75             this.itemType = itemType;
76             this.stateDescriptionFragment = BUILDER.withReadOnly(true).withPattern(pattern).build();
77         }
78     }
79
80     public enum MeasureClass {
81         INSIDE_TEMPERATURE(0, 50, 0.3, SIUnits.CELSIUS, "temp", "measure", true),
82         OUTSIDE_TEMPERATURE(-40, 65, 0.3, SIUnits.CELSIUS, "temp", "measure", true),
83         HEAT_INDEX(-40, 65, 1, SIUnits.CELSIUS, "", "", false),
84         PRESSURE(260, 1260, 0.1, HECTO(SIUnits.PASCAL), "pressure", "measure", true),
85         CO2(0, 5000, 50, Units.PARTS_PER_MILLION, "co2", "measure", true),
86         NOISE(35, 120, 1, Units.DECIBEL, "noise", "measure", true),
87         RAIN_QUANTITY(0, Double.MAX_VALUE, 0.1, MILLI(SIUnits.METRE), "sum_rain", "sum_rain", false),
88         RAIN_INTENSITY(0, 150, 0.1, Units.MILLIMETRE_PER_HOUR, "", "", false),
89         WIND_SPEED(0, 160, 1.8, SIUnits.KILOMETRE_PER_HOUR, "", "", false),
90         WIND_ANGLE(0, 360, 5, Units.DEGREE_ANGLE, "", "", false),
91         HUMIDITY(0, 100, 3, Units.PERCENT, "hum", "measure", true);
92
93         public static final EnumSet<MeasureClass> AS_SET = EnumSet.allOf(MeasureClass.class);
94
95         public final Measure measureDefinition;
96         public final String apiDescriptor;
97         public final Map<String, MeasureChannelDetails> channels = new HashMap<>(2);
98
99         MeasureClass(double min, double max, double precision, Unit<?> unit, String apiDescriptor, String confFragment,
100                 boolean canScale) {
101             this.measureDefinition = new Measure(min, max, precision, unit);
102             this.apiDescriptor = apiDescriptor;
103             if (!apiDescriptor.isBlank()) {
104                 String dimension = UnitUtils.getDimensionName(unit);
105
106                 channels.put(String.join("-", apiDescriptor, "measurement"),
107                         new MeasureChannelDetails(confFragment, String.join(":", NUMBER, dimension),
108                                 "%%.%df %s".formatted(measureDefinition.scale, UnitUtils.UNIT_PLACEHOLDER)));
109                 if (canScale) {
110                     channels.put(String.join("-", apiDescriptor, GROUP_TIMESTAMP), new MeasureChannelDetails(
111                             GROUP_TIMESTAMP, DATETIME, "@text/extensible-channel-type.timestamp.pattern"));
112                 }
113             }
114         }
115     }
116
117     // Content types
118     public static final String CONTENT_APP_JSON = "application/json;charset=utf-8";
119     public static final String CONTENT_APP_FORM = "application/x-www-form-urlencoded;charset=UTF-8";
120
121     // Netatmo API urls
122     public static final String URL_API = "https://api.netatmo.com/";
123     public static final String PATH_OAUTH = "oauth2";
124     public static final String SUB_PATH_TOKEN = "token";
125     public static final String SUB_PATH_AUTHORIZE = "authorize";
126     public static final String PATH_API = "api";
127     public static final String PATH_COMMAND = "command";
128     public static final String PATH_STATE = "setstate";
129     public static final String SUB_PATH_PERSON_AWAY = "setpersonsaway";
130     public static final String SUB_PATH_PERSON_HOME = "setpersonshome";
131     public static final String SUB_PATH_HOMES_DATA = "homesdata";
132     public static final String SUB_PATH_ADD_WEBHOOK = "addwebhook";
133     public static final String SUB_PATH_DROP_WEBHOOK = "dropwebhook";
134     public static final String SUB_PATH_SET_ROOM_THERMPOINT = "setroomthermpoint";
135     public static final String SUB_PATH_SET_THERM_MODE = "setthermmode";
136     public static final String SUB_PATH_SWITCH_SCHEDULE = "switchschedule";
137     public static final String SUB_PATH_GET_STATION = "getstationsdata";
138     public static final String SUB_PATH_GET_MEASURE = "getmeasure";
139     public static final String SUB_PATH_HOMESTATUS = "homestatus";
140     public static final String SUB_PATH_HOMECOACH = "gethomecoachsdata";
141     public static final String SUB_PATH_GET_EVENTS = "getevents";
142     public static final String SUB_PATH_PING = "ping";
143     public static final String SUB_PATH_CHANGESTATUS = "changestatus";
144     public static final String PARAM_DEVICE_ID = "device_id";
145     public static final String PARAM_MODULE_ID = "module_id";
146     public static final String PARAM_HOME_ID = "home_id";
147     public static final String PARAM_ROOM_ID = "room_id";
148     public static final String PARAM_PERSON_ID = "person_id";
149     public static final String PARAM_EVENT_ID = "event_id";
150     public static final String PARAM_SCHEDULE_ID = "schedule_id";
151     public static final String PARAM_OFFSET = "offset";
152     public static final String PARAM_GATEWAY_TYPE = "gateway_types";
153     public static final String PARAM_MODE = "mode";
154     public static final String PARAM_URL = "url";
155     public static final String PARAM_FAVORITES = "get_favorites";
156     public static final String PARAM_STATUS = "status";
157     public static final String PARAM_DEVICES_TYPE = "device_types";
158
159     // Payloads
160     public static final String PAYLOAD_FLOODLIGHT = "{\"home\": {\"id\":\"%s\",\"modules\": [ {\"id\":\"%s\",\"floodlight\":\"%s\"} ]}}";
161     public static final String PAYLOAD_SIREN_PRESENCE = "{\"home\": {\"id\":\"%s\",\"modules\": [ {\"id\":\"%s\",\"siren_status\":\"%s\"} ]}}";
162     public static final String PAYLOAD_PERSON_AWAY = "{\"home_id\":\"%s\",\"person_id\":\"%s\"}";
163     public static final String PAYLOAD_PERSON_HOME = "{\"home_id\":\"%s\",\"person_ids\":[\"%s\"]}";
164
165     // Autentication process params
166     public static final String PARAM_ERROR = "error";
167
168     // Global variables
169     public static final int THERM_MAX_SETPOINT = 30;
170
171     // Token scopes
172     public enum Scope {
173         @SerializedName("read_station")
174         READ_STATION,
175         @SerializedName("read_thermostat")
176         READ_THERMOSTAT,
177         @SerializedName("write_thermostat")
178         WRITE_THERMOSTAT,
179         @SerializedName("read_camera")
180         READ_CAMERA,
181         @SerializedName("write_camera")
182         WRITE_CAMERA,
183         @SerializedName("access_camera")
184         ACCESS_CAMERA,
185         @SerializedName("read_presence")
186         READ_PRESENCE,
187         @SerializedName("write_presence")
188         WRITE_PRESENCE,
189         @SerializedName("access_presence")
190         ACCESS_PRESENCE,
191         @SerializedName("read_smokedetector")
192         READ_SMOKEDETECTOR,
193         @SerializedName("read_homecoach")
194         READ_HOMECOACH,
195         @SerializedName("read_doorbell")
196         READ_DOORBELL,
197         @SerializedName("write_doorbell")
198         WRITE_DOORBELL,
199         @SerializedName("access_doorbell")
200         ACCESS_DOORBELL,
201         @SerializedName("read_carbonmonoxidedetector")
202         READ_CARBONMONOXIDEDETECTOR,
203         UNKNOWN
204     }
205
206     // Topology Changes
207     public enum TopologyChange {
208         @SerializedName("home_owner_added")
209         HOME_OWNER_ADDED,
210         @SerializedName("device_associated_to_user")
211         DEVICE_ASSOCIATED_TO_USER,
212         @SerializedName("device_associated_to_home")
213         DEVICE_ASSOCIATED_TO_HOME,
214         @SerializedName("device_updated")
215         DEVICE_UPDATED,
216         @SerializedName("device_associated_to_room")
217         DEVICE_ASSOCIATED_TO_ROOM,
218         @SerializedName("room_created")
219         ROOM_CREATED,
220         UNKNOWN
221     }
222
223     private static final Scope[] SMOKE_SCOPES = { Scope.READ_SMOKEDETECTOR };
224     private static final Scope[] CARBON_MONOXIDE_SCOPES = { Scope.READ_CARBONMONOXIDEDETECTOR };
225     private static final Scope[] AIR_CARE_SCOPES = { Scope.READ_HOMECOACH };
226     private static final Scope[] WEATHER_SCOPES = { Scope.READ_STATION };
227     private static final Scope[] THERMOSTAT_SCOPES = { Scope.READ_THERMOSTAT, Scope.WRITE_THERMOSTAT };
228     private static final Scope[] WELCOME_SCOPES = { Scope.READ_CAMERA, Scope.WRITE_CAMERA, Scope.ACCESS_CAMERA };
229     private static final Scope[] DOORBELL_SCOPES = { Scope.READ_DOORBELL, Scope.WRITE_DOORBELL, Scope.ACCESS_DOORBELL };
230     private static final Scope[] PRESENCE_SCOPES = { Scope.READ_PRESENCE, Scope.WRITE_PRESENCE, Scope.ACCESS_PRESENCE };
231
232     public enum FeatureArea {
233         AIR_CARE(AIR_CARE_SCOPES),
234         WEATHER(WEATHER_SCOPES),
235         ENERGY(THERMOSTAT_SCOPES),
236         SECURITY(WELCOME_SCOPES, PRESENCE_SCOPES, SMOKE_SCOPES, DOORBELL_SCOPES, CARBON_MONOXIDE_SCOPES),
237         NONE();
238
239         public static String ALL_SCOPES = EnumSet.allOf(FeatureArea.class).stream().map(fa -> fa.scopes)
240                 .flatMap(Set::stream).map(s -> s.name().toLowerCase()).collect(Collectors.joining(" "));
241
242         public final Set<Scope> scopes;
243
244         FeatureArea(Scope[]... scopeArrays) {
245             this.scopes = Stream.of(scopeArrays).flatMap(Arrays::stream).collect(Collectors.toSet());
246         }
247     }
248
249     // Radio signal quality thresholds
250     static final int[] WIFI_SIGNAL_LEVELS = new int[] { 99, 84, 69, 54 }; // Resp : bad, average, good, full
251     static final int[] RADIO_SIGNAL_LEVELS = new int[] { 90, 80, 70, 60 }; // Resp : low, medium, high, full
252
253     // Thermostat definitions
254     public enum SetpointMode {
255         @SerializedName("program")
256         PROGRAM("program"),
257         @SerializedName("away")
258         AWAY("away"),
259         @SerializedName("hg")
260         FROST_GUARD("hg"),
261         @SerializedName("manual")
262         MANUAL("manual"),
263         @SerializedName("off")
264         OFF("off"),
265         @SerializedName("max")
266         MAX("max"),
267         @SerializedName("schedule")
268         SCHEDULE("schedule"),
269         HOME("home"),
270         UNKNOWN("");
271
272         public final String apiDescriptor;
273
274         SetpointMode(String descriptor) {
275             this.apiDescriptor = descriptor;
276         }
277     }
278
279     public enum ThermostatZoneType {
280         @SerializedName("0")
281         DAY("0"),
282         @SerializedName("1")
283         NIGHT("1"),
284         @SerializedName("2")
285         AWAY("2"),
286         @SerializedName("3")
287         FROST_GUARD("3"),
288         @SerializedName("4")
289         CUSTOM("4"),
290         @SerializedName("5")
291         ECO("5"),
292         @SerializedName("8")
293         COMFORT("8"),
294         UNKNOWN("");
295
296         public final String zoneId;
297
298         private ThermostatZoneType(String id) {
299             zoneId = id;
300         }
301     }
302
303     public enum FloodLightMode {
304         @SerializedName("on")
305         ON,
306         @SerializedName("off")
307         OFF,
308         @SerializedName("auto")
309         AUTO,
310         UNKNOWN
311     }
312
313     public enum EventCategory {
314         @SerializedName("human")
315         HUMAN,
316         @SerializedName("animal")
317         ANIMAL,
318         @SerializedName("vehicle")
319         VEHICLE,
320         UNKNOWN
321     }
322
323     public enum TrendDescription {
324         @SerializedName("up")
325         UP,
326         @SerializedName("stable")
327         STABLE,
328         @SerializedName("down")
329         DOWN,
330         UNKNOWN
331     }
332
333     public enum VideoStatus {
334         @SerializedName("recording")
335         RECORDING,
336         @SerializedName("available")
337         AVAILABLE,
338         @SerializedName("deleted")
339         DELETED,
340         UNKNOWN
341     }
342
343     public enum SdCardStatus {
344         @SerializedName("1")
345         SD_CARD_MISSING,
346         @SerializedName("2")
347         SD_CARD_INSERTED,
348         @SerializedName("3")
349         SD_CARD_FORMATTED,
350         @SerializedName("4")
351         SD_CARD_WORKING,
352         @SerializedName("5")
353         SD_CARD_DEFECTIVE,
354         @SerializedName("6")
355         SD_CARD_INCOMPATIBLE_SPEED,
356         @SerializedName("7")
357         SD_CARD_INSUFFICIENT_SPACE,
358         UNKNOWN
359     }
360
361     public enum AlimentationStatus {
362         @SerializedName("1")
363         ALIM_INCORRECT_POWER,
364         @SerializedName("2")
365         ALIM_CORRECT_POWER,
366         UNKNOWN
367     }
368
369     public enum SirenStatus {
370         SOUND,
371         NO_SOUND,
372         UNKNOWN;
373
374         public static SirenStatus get(String value) {
375             try {
376                 return valueOf(value.toUpperCase());
377             } catch (IllegalArgumentException e) {
378                 return UNKNOWN;
379             }
380         }
381     }
382
383     public enum BatteryState {
384         @SerializedName("full")
385         FULL(100),
386         @SerializedName("high")
387         HIGH(80),
388         @SerializedName("medium")
389         MEDIUM(50),
390         @SerializedName("low")
391         LOW(15),
392         UNKNOWN(-1);
393
394         public final int level;
395
396         BatteryState(int i) {
397             this.level = i;
398         }
399     }
400
401     public enum ServiceError {
402         @SerializedName("99")
403         UNKNOWN,
404         @SerializedName("-2")
405         UNKNOWN_ERROR_IN_OAUTH,
406         @SerializedName("-1")
407         GRANT_IS_INVALID,
408         @SerializedName("1")
409         ACCESS_TOKEN_MISSING,
410         @SerializedName("2")
411         INVALID_TOKEN_MISSING,
412         @SerializedName("3")
413         ACCESS_TOKEN_EXPIRED,
414         @SerializedName("5")
415         APPLICATION_DEACTIVATED,
416         @SerializedName("7")
417         NOTHING_TO_MODIFY,
418         @SerializedName("9")
419         DEVICE_NOT_FOUND,
420         @SerializedName("10")
421         MISSING_ARGUMENTS,
422         @SerializedName("13")
423         OPERATION_FORBIDDEN,
424         @SerializedName("19")
425         IP_NOT_FOUND,
426         @SerializedName("21")
427         INVALID_ARGUMENT,
428         @SerializedName("22")
429         APPLICATION_NOT_FOUND,
430         @SerializedName("23")
431         USER_NOT_FOUND,
432         @SerializedName("25")
433         INVALID_DATE,
434         @SerializedName("26")
435         MAXIMUM_USAGE_REACHED,
436         @SerializedName("30")
437         INVALID_REFRESH_TOKEN,
438         @SerializedName("31")
439         METHOD_NOT_FOUND,
440         @SerializedName("35")
441         UNABLE_TO_EXECUTE,
442         @SerializedName("36")
443         PROHIBITED_STRING,
444         @SerializedName("37")
445         NO_MORE_SPACE_AVAILABLE_ON_THE_CAMERA,
446         @SerializedName("40")
447         JSON_GIVEN_HAS_AN_INVALID_ENCODING,
448         @SerializedName("41")
449         DEVICE_IS_UNREACHABLE
450     }
451
452     public enum HomeStatusError {
453         @SerializedName("1")
454         UNKNOWN_ERROR("homestatus-unknown-error"),
455         @SerializedName("2")
456         INTERNAL_ERROR("homestatus-internal-error"),
457         @SerializedName("3")
458         PARSER_ERROR("homestatus-parser-error"),
459         @SerializedName("4")
460         COMMAND_UNKNOWN_NODE_MODULE_ERROR("homestatus-command-unknown"),
461         @SerializedName("5")
462         COMMAND_INVALID_PARAMS("homestatus-invalid-params"),
463         @SerializedName("6")
464         UNREACHABLE("device-not-connected"),
465         UNKNOWN("deserialization-unknown");
466
467         // Associated error message that can be found in properties files
468         public final String message;
469
470         HomeStatusError(String message) {
471             this.message = message;
472         }
473     }
474 }