2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.netatmo.internal.api.data;
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.*;
20 import java.util.EnumSet;
21 import java.util.HashMap;
24 import java.util.stream.Collectors;
25 import java.util.stream.Stream;
27 import javax.measure.Unit;
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.openhab.core.library.unit.SIUnits;
31 import org.openhab.core.library.unit.Units;
32 import org.openhab.core.types.StateDescriptionFragment;
33 import org.openhab.core.types.StateDescriptionFragmentBuilder;
34 import org.openhab.core.types.util.UnitUtils;
36 import com.google.gson.annotations.SerializedName;
39 * This class holds various definitions and settings provided by the Netatmo
42 * @author Gaƫl L'hopital - Initial contribution
45 public class NetatmoConstants {
46 public static class Measure {
47 public final double minValue;
48 public final double maxValue;
49 public final int scale;
50 public final Unit<?> unit;
52 private Measure(double minValue, double maxValue, double precision, Unit<?> unit) {
53 this.minValue = minValue;
54 this.maxValue = maxValue;
56 String[] splitter = Double.valueOf(precision).toString().split("\\.");
57 if (splitter.length > 1) {
58 int dec = Integer.parseInt(splitter[1]);
59 this.scale = dec > 0 ? Integer.toString(dec).length() : 0;
66 public static class MeasureChannelDetails {
67 private static final StateDescriptionFragmentBuilder BUILDER = StateDescriptionFragmentBuilder.create();
68 public final URI configURI;
69 public final String itemType;
70 public final StateDescriptionFragment stateDescriptionFragment;
72 private MeasureChannelDetails(String measureType, String itemType, String pattern) {
73 this.configURI = URI.create(String.join(":", BINDING_ID, measureType, "config"));
74 this.itemType = itemType;
75 this.stateDescriptionFragment = BUILDER.withReadOnly(true).withPattern(pattern).build();
79 public enum MeasureClass {
80 INSIDE_TEMPERATURE(0, 50, 0.3, SIUnits.CELSIUS, "temp", "measure", true),
81 OUTSIDE_TEMPERATURE(-40, 65, 0.3, SIUnits.CELSIUS, "temp", "measure", true),
82 HEAT_INDEX(-40, 65, 1, SIUnits.CELSIUS, "", "", false),
83 PRESSURE(260, 1260, 1, HECTO(SIUnits.PASCAL), "pressure", "measure", true),
84 CO2(0, 5000, 50, Units.PARTS_PER_MILLION, "co2", "measure", true),
85 NOISE(35, 120, 1, Units.DECIBEL, "noise", "measure", true),
86 RAIN_QUANTITY(0, 150, 0.1, MILLI(SIUnits.METRE), "sum_rain", "sum_rain", false),
87 RAIN_INTENSITY(0, 150, 0.1, Units.MILLIMETRE_PER_HOUR, "", "", false),
88 WIND_SPEED(0, 160, 1.8, SIUnits.KILOMETRE_PER_HOUR, "", "", false),
89 WIND_ANGLE(0, 360, 5, Units.DEGREE_ANGLE, "", "", false),
90 HUMIDITY(0, 100, 3, Units.PERCENT, "hum", "measure", true);
92 public static final EnumSet<MeasureClass> AS_SET = EnumSet.allOf(MeasureClass.class);
94 public final Measure measureDefinition;
95 public final String apiDescriptor;
96 public final Map<String, MeasureChannelDetails> channels = new HashMap<>(2);
98 MeasureClass(double min, double max, double precision, Unit<?> unit, String apiDescriptor, String confFragment,
100 this.measureDefinition = new Measure(min, max, precision, unit);
101 this.apiDescriptor = apiDescriptor;
102 if (!apiDescriptor.isBlank()) {
103 String dimension = UnitUtils.getDimensionName(unit);
105 channels.put(String.join("-", apiDescriptor, "measurement"),
106 new MeasureChannelDetails(confFragment, String.join(":", NUMBER, dimension),
107 String.format("%%.%df %s", measureDefinition.scale, UnitUtils.UNIT_PLACEHOLDER)));
109 channels.put(String.join("-", apiDescriptor, GROUP_TIMESTAMP),
110 new MeasureChannelDetails(GROUP_TIMESTAMP, DATETIME, "%1$tA, %1$td.%1$tm. %1$tH:%1$tM"));
117 public static final String URL_API = "https://api.netatmo.com/";
118 public static final String URL_APP = "https://app.netatmo.net/";
119 public static final String PATH_OAUTH = "oauth2/token";
120 public static final String PATH_API = "api";
121 public static final String PATH_COMMAND = "command";
122 public static final String SUB_PATH_PERSON_AWAY = "setpersonsaway";
123 public static final String SUB_PATH_PERSON_HOME = "setpersonshome";
124 public static final String SUB_PATH_HOMES_DATA = "homesdata";
125 public static final String SUB_PATH_ADDWEBHOOK = "addwebhook";
126 public static final String SUB_PATH_DROPWEBHOOK = "dropwebhook";
127 public static final String SUB_PATH_SETROOMTHERMPOINT = "setroomthermpoint";
128 public static final String SUB_PATH_SETTHERMMODE = "setthermmode";
129 public static final String SUB_PATH_SWITCHSCHEDULE = "switchschedule";
130 public static final String SUB_PATH_GETSTATION = "getstationsdata";
131 public static final String SUB_PATH_GETMEASURE = "getmeasure";
132 public static final String SUB_PATH_HOMESTATUS = "homestatus";
133 public static final String SUB_PATH_HOMECOACH = "gethomecoachsdata";
134 public static final String SUB_PATH_GETEVENTS = "getevents";
135 public static final String SUB_PATH_PING = "ping";
136 public static final String SUB_PATH_CHANGESTATUS = "changestatus";
137 public static final String SUB_PATH_FLOODLIGHTSET = "floodlight_set_config";
138 public static final String PARAM_DEVICEID = "device_id";
139 public static final String PARAM_MODULEID = "module_id";
140 public static final String PARAM_HOMEID = "home_id";
141 public static final String PARAM_ROOMID = "room_id";
142 public static final String PARAM_PERSONID = "person_id";
143 public static final String PARAM_SCHEDULEID = "schedule_id";
144 public static final String PARAM_OFFSET = "offset";
145 public static final String PARAM_GATEWAYTYPE = "gateway_types";
146 public static final String PARAM_MODE = "mode";
147 public static final String PARAM_URL = "url";
148 public static final String PARAM_FAVORITES = "get_favorites";
149 public static final String PARAM_STATUS = "status";
152 public static final int THERM_MAX_SETPOINT = 30;
155 public static enum Scope {
156 @SerializedName("read_station")
158 @SerializedName("read_thermostat")
160 @SerializedName("write_thermostat")
162 @SerializedName("read_camera")
164 @SerializedName("write_camera")
166 @SerializedName("access_camera")
168 @SerializedName("read_presence")
170 @SerializedName("write_presence")
172 @SerializedName("access_presence")
174 @SerializedName("read_smokedetector")
176 @SerializedName("read_homecoach")
178 @SerializedName("read_doorbell")
180 @SerializedName("write_doorbell")
182 @SerializedName("access_doorbell")
187 private static final Set<Scope> SMOKE = Set.of(Scope.READ_SMOKEDETECTOR);
188 private static final Set<Scope> WELCOME = Set.of(Scope.READ_CAMERA, Scope.WRITE_CAMERA, Scope.ACCESS_CAMERA);
189 private static final Set<Scope> DOORBELL = Set.of(Scope.READ_DOORBELL, Scope.WRITE_DOORBELL, Scope.ACCESS_DOORBELL);
190 private static final Set<Scope> PRESENCE = Set.of(Scope.READ_PRESENCE, Scope.WRITE_PRESENCE, Scope.ACCESS_PRESENCE);
192 // Radio signal quality thresholds
193 static final int[] WIFI_SIGNAL_LEVELS = new int[] { 99, 84, 69, 54 }; // Resp : bad, average, good, full
194 static final int[] RADIO_SIGNAL_LEVELS = new int[] { 90, 80, 70, 60 }; // Resp : low, medium, high, full
196 public static enum FeatureArea {
197 AIR_CARE(Scope.READ_HOMECOACH),
198 WEATHER(Scope.READ_STATION),
199 ENERGY(Scope.READ_THERMOSTAT, Scope.WRITE_THERMOSTAT),
200 SECURITY(Stream.of(WELCOME, PRESENCE, SMOKE, DOORBELL).flatMap(Set::stream).toArray(Scope[]::new)),
203 public static final Set<FeatureArea> AS_SET = EnumSet.allOf(FeatureArea.class);
205 public static String toScopeString(Set<FeatureArea> featureSet) {
206 return featureSet.stream().map(fa -> fa.scopes).flatMap(Set::stream).map(s -> s.name().toLowerCase())
207 .collect(Collectors.joining(" "));
210 public final Set<Scope> scopes;
212 FeatureArea(Scope... scopes) {
213 this.scopes = Set.of(scopes);
217 // Thermostat definitions
218 public static enum SetpointMode {
219 @SerializedName("program")
221 @SerializedName("away")
223 @SerializedName("hg")
225 @SerializedName("manual")
227 @SerializedName("off")
229 @SerializedName("max")
231 @SerializedName("schedule")
232 SCHEDULE("schedule"),
236 public final String apiDescriptor;
238 SetpointMode(String descriptor) {
239 this.apiDescriptor = descriptor;
243 public static enum ThermostatZoneType {
260 public final String zoneId;
262 private ThermostatZoneType(String id) {
267 public enum FloodLightMode {
268 @SerializedName("on")
270 @SerializedName("off")
272 @SerializedName("auto")
277 public enum EventCategory {
278 @SerializedName("human")
280 @SerializedName("animal")
282 @SerializedName("vehicle")
287 public enum TrendDescription {
288 @SerializedName("up")
290 @SerializedName("stable")
292 @SerializedName("down")
297 public enum VideoStatus {
298 @SerializedName("recording")
300 @SerializedName("available")
302 @SerializedName("deleted")
307 public enum SdCardStatus {
319 SD_CARD_INCOMPATIBLE_SPEED,
321 SD_CARD_INSUFFICIENT_SPACE,
325 public enum AlimentationStatus {
327 ALIM_INCORRECT_POWER,
333 public enum BatteryState {
334 @SerializedName("full")
336 @SerializedName("high")
338 @SerializedName("medium")
340 @SerializedName("low")
344 public final int level;
346 BatteryState(int i) {
351 public enum ServiceError {
352 @SerializedName("99")
354 @SerializedName("-2")
355 UNKNOWN_ERROR_IN_OAUTH,
356 @SerializedName("-1")
359 ACCESS_TOKEN_MISSING,
361 INVALID_TOKEN_MISSING,
363 ACCESS_TOKEN_EXPIRED,
365 APPLICATION_DEACTIVATED,
370 @SerializedName("10")
372 @SerializedName("13")
374 @SerializedName("19")
376 @SerializedName("21")
378 @SerializedName("22")
379 APPLICATION_NOT_FOUND,
380 @SerializedName("23")
382 @SerializedName("25")
384 @SerializedName("26")
385 MAXIMUM_USAGE_REACHED,
386 @SerializedName("30")
387 INVALID_REFRESH_TOKEN,
388 @SerializedName("31")
390 @SerializedName("35")
392 @SerializedName("36")
394 @SerializedName("37")
395 NO_MORE_SPACE_AVAILABLE_ON_THE_CAMERA,
396 @SerializedName("40")
397 JSON_GIVEN_HAS_AN_INVALID_ENCODING,
398 @SerializedName("41")
399 DEVICE_IS_UNREACHABLE;