2 * Copyright (c) 2010-2024 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.Arrays;
21 import java.util.EnumSet;
22 import java.util.HashMap;
25 import java.util.stream.Collectors;
26 import java.util.stream.Stream;
28 import javax.measure.Unit;
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;
37 import com.google.gson.annotations.SerializedName;
40 * This class holds various definitions and settings provided by the Netatmo
43 * @author Gaƫl L'hopital - Initial contribution
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;
53 private Measure(double minValue, double maxValue, double precision, Unit<?> unit) {
54 this.minValue = minValue;
55 this.maxValue = maxValue;
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;
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;
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();
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);
93 public static final EnumSet<MeasureClass> AS_SET = EnumSet.allOf(MeasureClass.class);
95 public final Measure measureDefinition;
96 public final String apiDescriptor;
97 public final Map<String, MeasureChannelDetails> channels = new HashMap<>(2);
99 MeasureClass(double min, double max, double precision, Unit<?> unit, String apiDescriptor, String confFragment,
101 this.measureDefinition = new Measure(min, max, precision, unit);
102 this.apiDescriptor = apiDescriptor;
103 if (!apiDescriptor.isBlank()) {
104 String dimension = UnitUtils.getDimensionName(unit);
106 channels.put(String.join("-", apiDescriptor, "measurement"),
107 new MeasureChannelDetails(confFragment, String.join(":", NUMBER, dimension),
108 "%%.%df %s".formatted(measureDefinition.scale, UnitUtils.UNIT_PLACEHOLDER)));
110 channels.put(String.join("-", apiDescriptor, GROUP_TIMESTAMP), new MeasureChannelDetails(
111 GROUP_TIMESTAMP, DATETIME, "@text/extensible-channel-type.timestamp.pattern"));
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";
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";
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\"]}";
165 // Autentication process params
166 public static final String PARAM_ERROR = "error";
169 public static final int THERM_MAX_SETPOINT = 30;
173 @SerializedName("read_station")
175 @SerializedName("read_thermostat")
177 @SerializedName("write_thermostat")
179 @SerializedName("read_camera")
181 @SerializedName("write_camera")
183 @SerializedName("access_camera")
185 @SerializedName("read_presence")
187 @SerializedName("write_presence")
189 @SerializedName("access_presence")
191 @SerializedName("read_smokedetector")
193 @SerializedName("read_homecoach")
195 @SerializedName("read_doorbell")
197 @SerializedName("write_doorbell")
199 @SerializedName("access_doorbell")
201 @SerializedName("read_carbonmonoxidedetector")
202 READ_CARBONMONOXIDEDETECTOR,
207 public enum TopologyChange {
208 @SerializedName("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")
216 @SerializedName("device_associated_to_room")
217 DEVICE_ASSOCIATED_TO_ROOM,
218 @SerializedName("room_created")
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 };
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),
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(" "));
242 public final Set<Scope> scopes;
244 FeatureArea(Scope[]... scopeArrays) {
245 this.scopes = Stream.of(scopeArrays).flatMap(Arrays::stream).collect(Collectors.toSet());
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
253 // Thermostat definitions
254 public enum SetpointMode {
255 @SerializedName("program")
257 @SerializedName("away")
259 @SerializedName("hg")
261 @SerializedName("manual")
263 @SerializedName("off")
265 @SerializedName("max")
267 @SerializedName("schedule")
268 SCHEDULE("schedule"),
272 public final String apiDescriptor;
274 SetpointMode(String descriptor) {
275 this.apiDescriptor = descriptor;
279 public enum ThermostatZoneType {
296 public final String zoneId;
298 private ThermostatZoneType(String id) {
303 public enum FloodLightMode {
304 @SerializedName("on")
306 @SerializedName("off")
308 @SerializedName("auto")
313 public enum EventCategory {
314 @SerializedName("human")
316 @SerializedName("animal")
318 @SerializedName("vehicle")
323 public enum TrendDescription {
324 @SerializedName("up")
326 @SerializedName("stable")
328 @SerializedName("down")
333 public enum VideoStatus {
334 @SerializedName("recording")
336 @SerializedName("available")
338 @SerializedName("deleted")
343 public enum SdCardStatus {
355 SD_CARD_INCOMPATIBLE_SPEED,
357 SD_CARD_INSUFFICIENT_SPACE,
361 public enum AlimentationStatus {
363 ALIM_INCORRECT_POWER,
369 public enum SirenStatus {
374 public static SirenStatus get(String value) {
376 return valueOf(value.toUpperCase());
377 } catch (IllegalArgumentException e) {
383 public enum BatteryState {
384 @SerializedName("full")
386 @SerializedName("high")
388 @SerializedName("medium")
390 @SerializedName("low")
394 public final int level;
396 BatteryState(int i) {
401 public enum ServiceError {
402 @SerializedName("99")
404 @SerializedName("-2")
405 UNKNOWN_ERROR_IN_OAUTH,
406 @SerializedName("-1")
409 ACCESS_TOKEN_MISSING,
411 INVALID_TOKEN_MISSING,
413 ACCESS_TOKEN_EXPIRED,
415 APPLICATION_DEACTIVATED,
420 @SerializedName("10")
422 @SerializedName("13")
424 @SerializedName("19")
426 @SerializedName("21")
428 @SerializedName("22")
429 APPLICATION_NOT_FOUND,
430 @SerializedName("23")
432 @SerializedName("25")
434 @SerializedName("26")
435 MAXIMUM_USAGE_REACHED,
436 @SerializedName("30")
437 INVALID_REFRESH_TOKEN,
438 @SerializedName("31")
440 @SerializedName("35")
442 @SerializedName("36")
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
452 public enum HomeStatusError {
454 UNKNOWN_ERROR("homestatus-unknown-error"),
456 INTERNAL_ERROR("homestatus-internal-error"),
458 PARSER_ERROR("homestatus-parser-error"),
460 COMMAND_UNKNOWN_NODE_MODULE_ERROR("homestatus-command-unknown"),
462 COMMAND_INVALID_PARAMS("homestatus-invalid-params"),
464 UNREACHABLE("device-not-connected"),
465 UNKNOWN("deserialization-unknown");
467 // Associated error message that can be found in properties files
468 public final String message;
470 HomeStatusError(String message) {
471 this.message = message;