From 1aec6c9b307ae941fdb839f3d6ad924e36e0b170 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ga=C3=ABl=20L=27hopital?= Date: Sat, 28 May 2022 11:26:30 +0200 Subject: [PATCH] [netatmo] Add siren device (#12805) * Starting Siren addition Signed-off-by: clinique --- bundles/org.openhab.binding.netatmo/README.md | 16 +++++- .../internal/NetatmoBindingConstants.java | 2 + .../internal/api/AuthenticationApi.java | 7 +-- .../netatmo/internal/api/data/ModuleType.java | 48 ++++++++-------- .../internal/api/data/NetatmoConstants.java | 40 ++++++------- .../internal/api/dto/HomeDataModule.java | 5 ++ .../ModuleTypeDeserializer.java | 39 +++++++++++++ .../deserialization/NADeserializer.java | 2 + .../discovery/NetatmoDiscoveryService.java | 56 +++++++++---------- .../internal/handler/CommonInterface.java | 9 +++ .../handler/capability/Capability.java | 3 +- .../handler/capability/EnergyCapability.java | 50 ++++++++++------- .../capability/SecurityCapability.java | 27 ++++++--- .../channelhelper/SirenChannelHelper.java | 52 +++++++++++++++++ .../resources/OH-INF/i18n/netatmo.properties | 8 ++- .../OH-INF/i18n/netatmo_it.properties | 1 - .../main/resources/OH-INF/thing/channels.xml | 19 +++++++ .../main/resources/OH-INF/thing/energy.xml | 7 --- .../main/resources/OH-INF/thing/security.xml | 8 +++ 19 files changed, 284 insertions(+), 115 deletions(-) create mode 100644 bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/ModuleTypeDeserializer.java create mode 100644 bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/SirenChannelHelper.java diff --git a/bundles/org.openhab.binding.netatmo/README.md b/bundles/org.openhab.binding.netatmo/README.md index 3292554065..58d5a6f132 100644 --- a/bundles/org.openhab.binding.netatmo/README.md +++ b/bundles/org.openhab.binding.netatmo/README.md @@ -5,7 +5,9 @@ The Netatmo binding integrates the following Netatmo products: - *Personal Weather Station*. Reports temperature, humidity, air pressure, carbon dioxide concentration in the air, as well as the ambient noise level. - *Thermostat*. Reports ambient temperature, allow to check target temperature, consult and change furnace heating status. - *Indoor Camera / Welcome*. Reports last event and persons at home, consult picture and video from event/camera. +- *Siren* - *Outdoor Camera / Presence*. Reports last event, consult picture and video from event/camera. +- *Doorbell* See https://www.netatmo.com/ for details on their product. @@ -505,7 +507,7 @@ Warnings: (*) This channel is configurable : low, poor, high. -**Supported channels for the Welcome Doorbell thing:** +**Supported channels for the Doorbell thing:** | Channel Group | Channel ID | Item Type | Read/Write | Description | |---------------|-------------------|--------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------| @@ -532,6 +534,18 @@ Warnings: Note: live feeds either locally or via VPN are not available in Netatmo API. +**Supported channels for the Siren thing:** + +| Channel Group | Channel ID | Item Type | Read/Write | Description | +|---------------|-------------------|--------------|------------|------------------------------------------------------| +| siren | status | String | Read-only | Status of the siren, if silent or emitting an alarm | +| siren | monitoring | Switch | Read-only | State of the siren device | +| signal | strength | Number | Read-only | Signal strength (0 for no signal, 1 for weak...) | +| signal | value | Number:Power | Read-only | Signal strength in dBm | +| timestamp | last-seen | DateTime | Read-only | Last time the module reported its presence | +| battery | value | Number | Read-only | Battery level | +| battery | low-battery | Switch | Read-only | Low battery | + ### Welcome Person diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoBindingConstants.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoBindingConstants.java index c4a3d585fc..9878a32292 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoBindingConstants.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoBindingConstants.java @@ -53,6 +53,7 @@ public class NetatmoBindingConstants { public static final String GROUP_CAM_STATUS = "status"; public static final String GROUP_CAM_LIVE = "live"; public static final String GROUP_PRESENCE = "presence"; + public static final String GROUP_SIREN = "siren"; public static final String GROUP_PERSON = "person"; public static final String GROUP_PROPERTIES = "properties"; public static final String GROUP_SETPOINT = "setpoint"; @@ -105,6 +106,7 @@ public class NetatmoBindingConstants { public static final String CHANNEL_SUM_RAIN1 = "sum-1"; public static final String CHANNEL_SUM_RAIN24 = "sum-24"; public static final String CHANNEL_WIND_ANGLE = "angle"; + public static final String CHANNEL_STATUS = GROUP_CAM_STATUS; public static final String CHANNEL_WIND_STRENGTH = "strength"; public static final String CHANNEL_MAX_WIND_STRENGTH = "max-strength"; public static final String CHANNEL_DATE_MAX_WIND_STRENGTH = "max-strength-date"; diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/AuthenticationApi.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/AuthenticationApi.java index 926ce41685..a95b335cc3 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/AuthenticationApi.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/AuthenticationApi.java @@ -43,7 +43,6 @@ import org.slf4j.LoggerFactory; */ @NonNullByDefault public class AuthenticationApi extends RestManager { - private static final String ALL_SCOPES = FeatureArea.toScopeString(FeatureArea.AS_SET); private static final UriBuilder OAUTH_BUILDER = getApiBaseBuilder().path(PATH_OAUTH); private static final UriBuilder AUTH_BUILDER = OAUTH_BUILDER.clone().path(SUB_PATH_AUTHORIZE); private static final URI TOKEN_URI = OAUTH_BUILDER.clone().path(SUB_PATH_TOKEN).build(); @@ -62,7 +61,7 @@ public class AuthenticationApi extends RestManager { public String authorize(ApiHandlerConfiguration credentials, @Nullable String code, @Nullable String redirectUri) throws NetatmoException { if (!(credentials.clientId.isBlank() || credentials.clientSecret.isBlank())) { - Map params = new HashMap<>(Map.of(SCOPE, ALL_SCOPES)); + Map params = new HashMap<>(Map.of(SCOPE, FeatureArea.ALL_SCOPES)); String refreshToken = credentials.refreshToken; if (!refreshToken.isBlank()) { params.put(REFRESH_TOKEN, refreshToken); @@ -118,7 +117,7 @@ public class AuthenticationApi extends RestManager { } public static UriBuilder getAuthorizationBuilder(String clientId) { - return AUTH_BUILDER.clone().queryParam(CLIENT_ID, clientId).queryParam(SCOPE, ALL_SCOPES).queryParam(STATE, - clientId); + return AUTH_BUILDER.clone().queryParam(CLIENT_ID, clientId).queryParam(SCOPE, FeatureArea.ALL_SCOPES) + .queryParam(STATE, clientId); } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java index 5a0f9acb8e..34adc622f6 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java @@ -59,6 +59,7 @@ import org.openhab.binding.netatmo.internal.handler.channelhelper.RainChannelHel import org.openhab.binding.netatmo.internal.handler.channelhelper.RoomChannelHelper; import org.openhab.binding.netatmo.internal.handler.channelhelper.SetpointChannelHelper; import org.openhab.binding.netatmo.internal.handler.channelhelper.SignalChannelHelper; +import org.openhab.binding.netatmo.internal.handler.channelhelper.SirenChannelHelper; import org.openhab.binding.netatmo.internal.handler.channelhelper.TemperatureChannelHelper; import org.openhab.binding.netatmo.internal.handler.channelhelper.TemperatureExtChannelHelper; import org.openhab.binding.netatmo.internal.handler.channelhelper.TemperatureOutChannelHelper; @@ -68,8 +69,6 @@ import org.openhab.binding.netatmo.internal.handler.channelhelper.TimestampExtCh import org.openhab.binding.netatmo.internal.handler.channelhelper.WindChannelHelper; import org.openhab.core.thing.ThingTypeUID; -import com.google.gson.annotations.SerializedName; - /** * This enum all handled Netatmo modules and devices along with their capabilities * @@ -79,75 +78,76 @@ import com.google.gson.annotations.SerializedName; public enum ModuleType { UNKNOWN(FeatureArea.NONE, "", null, List.of(), List.of()), ACCOUNT(FeatureArea.NONE, "", null, List.of(), List.of()), - @SerializedName("NAHome") + HOME(FeatureArea.NONE, "NAHome", ACCOUNT, List.of(DeviceCapability.class, EventCapability.class, HomeCapability.class, ChannelHelperCapability.class), List.of(HomeSecurityChannelHelper.class, HomeEnergyChannelHelper.class)), - @SerializedName("NAPerson") + PERSON(FeatureArea.SECURITY, "NAPerson", HOME, List.of(EventCapability.class, PersonCapability.class, ChannelHelperCapability.class), List.of(PersonChannelHelper.class, EventPersonChannelHelper.class)), - @SerializedName("NACamera") + WELCOME(FeatureArea.SECURITY, "NACamera", HOME, List.of(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class), List.of(CameraChannelHelper.class, SignalChannelHelper.class, EventChannelHelper.class)), - @SerializedName("NOC") + + SIREN(FeatureArea.SECURITY, "NIS", WELCOME, List.of(ChannelHelperCapability.class), + List.of(SirenChannelHelper.class, BatteryChannelHelper.class, TimestampChannelHelper.class, + SignalChannelHelper.class)), + PRESENCE(FeatureArea.SECURITY, "NOC", HOME, List.of(EventCapability.class, PresenceCapability.class, ChannelHelperCapability.class), List.of(PresenceChannelHelper.class, SignalChannelHelper.class, EventChannelHelper.class)), - @SerializedName("NIS") - SIREN(FeatureArea.SECURITY, "NIS", HOME, List.of(ChannelHelperCapability.class), - List.of(BatteryChannelHelper.class, TimestampChannelHelper.class, SignalChannelHelper.class)), - @SerializedName("NDB") + DOORBELL(FeatureArea.SECURITY, "NDB", HOME, List.of(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class), List.of(DoorbellChannelHelper.class, SignalChannelHelper.class, EventDoorbellChannelHelper.class)), - @SerializedName("NAMain") + WEATHER_STATION(FeatureArea.WEATHER, "NAMain", ACCOUNT, List.of(DeviceCapability.class, WeatherCapability.class, MeasureCapability.class, ChannelHelperCapability.class), List.of(PressureExtChannelHelper.class, NoiseChannelHelper.class, HumidityChannelHelper.class, TemperatureExtChannelHelper.class, AirQualityChannelHelper.class, LocationChannelHelper.class, TimestampExtChannelHelper.class, MeasuresChannelHelper.class, SignalChannelHelper.class)), - @SerializedName("NAModule1") + OUTDOOR(FeatureArea.WEATHER, "NAModule1", WEATHER_STATION, List.of(MeasureCapability.class, ChannelHelperCapability.class), List.of(HumidityChannelHelper.class, TemperatureOutChannelHelper.class, BatteryChannelHelper.class, MeasuresChannelHelper.class, TimestampExtChannelHelper.class, SignalChannelHelper.class)), - @SerializedName("NAModule2") + WIND(FeatureArea.WEATHER, "NAModule2", WEATHER_STATION, List.of(ChannelHelperCapability.class), List.of(WindChannelHelper.class, BatteryChannelHelper.class, TimestampExtChannelHelper.class, SignalChannelHelper.class)), - @SerializedName("NAModule3") + RAIN(FeatureArea.WEATHER, "NAModule3", WEATHER_STATION, List.of(MeasureCapability.class, ChannelHelperCapability.class), List.of(RainChannelHelper.class, BatteryChannelHelper.class, MeasuresChannelHelper.class, TimestampExtChannelHelper.class, SignalChannelHelper.class)), - @SerializedName("NAModule4") + INDOOR(FeatureArea.WEATHER, "NAModule4", WEATHER_STATION, List.of(MeasureCapability.class, ChannelHelperCapability.class), List.of(HumidityChannelHelper.class, TemperatureExtChannelHelper.class, AirQualityChannelHelper.class, BatteryChannelHelper.class, MeasuresChannelHelper.class, TimestampExtChannelHelper.class, SignalChannelHelper.class)), - @SerializedName("NHC") + HOME_COACH(FeatureArea.AIR_CARE, "NHC", ACCOUNT, List.of(DeviceCapability.class, AirCareCapability.class, MeasureCapability.class, ChannelHelperCapability.class), List.of(NoiseChannelHelper.class, HumidityChannelHelper.class, AirQualityExtChannelHelper.class, TemperatureChannelHelper.class, PressureChannelHelper.class, TimestampExtChannelHelper.class, SignalChannelHelper.class, MeasuresChannelHelper.class, LocationChannelHelper.class)), - @SerializedName("NAPlug") + PLUG(FeatureArea.ENERGY, "NAPlug", HOME, List.of(ChannelHelperCapability.class), List.of(SignalChannelHelper.class)), - @SerializedName("NATherm1") - THERMOSTAT(FeatureArea.ENERGY, "NATherm1", HOME, List.of(ChannelHelperCapability.class), + + VALVE(FeatureArea.ENERGY, "NRV", PLUG, List.of(ChannelHelperCapability.class), + List.of(BatteryExtChannelHelper.class, SignalChannelHelper.class)), + + THERMOSTAT(FeatureArea.ENERGY, "NATherm1", PLUG, List.of(ChannelHelperCapability.class), List.of(Therm1ChannelHelper.class, BatteryExtChannelHelper.class, SignalChannelHelper.class)), - @SerializedName("NARoom") + ROOM(FeatureArea.ENERGY, "NARoom", HOME, List.of(RoomCapability.class, ChannelHelperCapability.class), - List.of(RoomChannelHelper.class, SetpointChannelHelper.class)), - @SerializedName("NRV") - VALVE(FeatureArea.ENERGY, "NRV", HOME, List.of(ChannelHelperCapability.class), - List.of(BatteryExtChannelHelper.class, SignalChannelHelper.class)); + List.of(RoomChannelHelper.class, SetpointChannelHelper.class)); public static final EnumSet AS_SET = EnumSet.allOf(ModuleType.class); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java index 80e8ef6cc8..fdd7d2340b 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java @@ -17,6 +17,7 @@ import static org.openhab.core.library.CoreItemFactory.*; import static org.openhab.core.library.unit.MetricPrefix.*; import java.net.URI; +import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; @@ -190,36 +191,35 @@ public class NetatmoConstants { UNKNOWN; } - private static final Set SMOKE = Set.of(Scope.READ_SMOKEDETECTOR); - private static final Set WELCOME = Set.of(Scope.READ_CAMERA, Scope.WRITE_CAMERA, Scope.ACCESS_CAMERA); - private static final Set DOORBELL = Set.of(Scope.READ_DOORBELL, Scope.WRITE_DOORBELL, Scope.ACCESS_DOORBELL); - private static final Set PRESENCE = Set.of(Scope.READ_PRESENCE, Scope.WRITE_PRESENCE, Scope.ACCESS_PRESENCE); - - // Radio signal quality thresholds - static final int[] WIFI_SIGNAL_LEVELS = new int[] { 99, 84, 69, 54 }; // Resp : bad, average, good, full - static final int[] RADIO_SIGNAL_LEVELS = new int[] { 90, 80, 70, 60 }; // Resp : low, medium, high, full + private static final Scope[] SMOKE_SCOPES = { Scope.READ_SMOKEDETECTOR }; + private static final Scope[] AIR_CARE_SCOPES = { Scope.READ_HOMECOACH }; + private static final Scope[] WEATHER_SCOPES = { Scope.READ_STATION }; + private static final Scope[] THERMOSTAT_SCOPES = { Scope.READ_THERMOSTAT, Scope.WRITE_THERMOSTAT }; + private static final Scope[] WELCOME_SCOPES = { Scope.READ_CAMERA, Scope.WRITE_CAMERA, Scope.ACCESS_CAMERA }; + private static final Scope[] DOORBELL_SCOPES = { Scope.READ_DOORBELL, Scope.WRITE_DOORBELL, Scope.ACCESS_DOORBELL }; + private static final Scope[] PRESENCE_SCOPES = { Scope.READ_PRESENCE, Scope.WRITE_PRESENCE, Scope.ACCESS_PRESENCE }; public static enum FeatureArea { - AIR_CARE(Scope.READ_HOMECOACH), - WEATHER(Scope.READ_STATION), - ENERGY(Scope.READ_THERMOSTAT, Scope.WRITE_THERMOSTAT), - SECURITY(Stream.of(WELCOME, PRESENCE, SMOKE, DOORBELL).flatMap(Set::stream).toArray(Scope[]::new)), + AIR_CARE(AIR_CARE_SCOPES), + WEATHER(WEATHER_SCOPES), + ENERGY(THERMOSTAT_SCOPES), + SECURITY(WELCOME_SCOPES, PRESENCE_SCOPES, SMOKE_SCOPES, DOORBELL_SCOPES), NONE(); - public static final Set AS_SET = EnumSet.allOf(FeatureArea.class); - - public static String toScopeString(Set featureSet) { - return featureSet.stream().map(fa -> fa.scopes).flatMap(Set::stream).map(s -> s.name().toLowerCase()) - .collect(Collectors.joining(" ")); - } + public static String ALL_SCOPES = EnumSet.allOf(FeatureArea.class).stream().map(fa -> fa.scopes) + .flatMap(Set::stream).map(s -> s.name().toLowerCase()).collect(Collectors.joining(" ")); public final Set scopes; - FeatureArea(Scope... scopes) { - this.scopes = Set.of(scopes); + FeatureArea(Scope[]... scopeArrays) { + this.scopes = Stream.of(scopeArrays).flatMap(Arrays::stream).collect(Collectors.toSet()); } } + // Radio signal quality thresholds + static final int[] WIFI_SIGNAL_LEVELS = new int[] { 99, 84, 69, 54 }; // Resp : bad, average, good, full + static final int[] RADIO_SIGNAL_LEVELS = new int[] { 90, 80, 70, 60 }; // Resp : low, medium, high, full + // Thermostat definitions public static enum SetpointMode { @SerializedName("program") diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeDataModule.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeDataModule.java index da2c91b630..5f0ac3b871 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeDataModule.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeDataModule.java @@ -43,4 +43,9 @@ public class HomeDataModule extends NAThing implements NAModule { public List getModuleBridged() { return moduleBridged; } + + @Override + public boolean isIgnoredForThingUpdate() { + return true; + } } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/ModuleTypeDeserializer.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/ModuleTypeDeserializer.java new file mode 100644 index 0000000000..954688962c --- /dev/null +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/ModuleTypeDeserializer.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.netatmo.internal.deserialization; + +import java.lang.reflect.Type; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.netatmo.internal.api.data.ModuleType; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; + +/** + * Specialized deserializer for ModuleType class + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public class ModuleTypeDeserializer implements JsonDeserializer { + + @Override + public @Nullable ModuleType deserialize(JsonElement json, Type clazz, JsonDeserializationContext context) { + String string = json.getAsString(); + return ModuleType.AS_SET.stream().filter(mt -> mt.apiName.equalsIgnoreCase(string)).findFirst() + .orElse(ModuleType.UNKNOWN); + } +} diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/NADeserializer.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/NADeserializer.java index 1976cf7f88..f3c45a2ab0 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/NADeserializer.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/NADeserializer.java @@ -18,6 +18,7 @@ import java.time.ZonedDateTime; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.netatmo.internal.api.NetatmoException; +import org.openhab.binding.netatmo.internal.api.data.ModuleType; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OpenClosedType; @@ -47,6 +48,7 @@ public class NADeserializer { .registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory()) .registerTypeAdapter(NAObjectMap.class, new NAObjectMapDeserializer()) .registerTypeAdapter(NAPushType.class, new NAPushTypeDeserializer()) + .registerTypeAdapter(ModuleType.class, new ModuleTypeDeserializer()) .registerTypeAdapter(ZonedDateTime.class, (JsonDeserializer) (json, type, jsonDeserializationContext) -> { long netatmoTS = json.getAsJsonPrimitive().getAsLong(); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/discovery/NetatmoDiscoveryService.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/discovery/NetatmoDiscoveryService.java index 23bcfdeacd..a518971a41 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/discovery/NetatmoDiscoveryService.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/discovery/NetatmoDiscoveryService.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.netatmo.internal.discovery; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -47,7 +49,7 @@ import org.slf4j.LoggerFactory; @NonNullByDefault public class NetatmoDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService, DiscoveryService { private static final Set SKIPPED_TYPES = Set.of(ModuleType.UNKNOWN, ModuleType.ACCOUNT); - private static final int DISCOVER_TIMEOUT_SECONDS = 5; + private static final int DISCOVER_TIMEOUT_SECONDS = 3; private final Logger logger = LoggerFactory.getLogger(NetatmoDiscoveryService.class); private @Nullable ApiBridgeHandler handler; @@ -60,13 +62,13 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements public void startScan() { ApiBridgeHandler localHandler = handler; if (localHandler != null) { - ThingUID apiBridgeUID = localHandler.getThing().getUID(); + ThingUID accountUID = localHandler.getThing().getUID(); try { AircareApi airCareApi = localHandler.getRestManager(AircareApi.class); if (airCareApi != null) { // Search Healthy Home Coaches ListBodyResponse body = airCareApi.getHomeCoachData(null).getBody(); if (body != null) { - body.getElements().stream().forEach(homeCoach -> createThing(homeCoach, apiBridgeUID)); + body.getElements().stream().forEach(homeCoach -> createThing(homeCoach, accountUID)); } } if (localHandler.getReadFriends()) { @@ -74,31 +76,34 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements if (weatherApi != null) { // Search favorite stations weatherApi.getFavoriteAndGuestStationsData().stream().filter(NAMain::isReadOnly) .forEach(station -> { - ThingUID bridgeUID = createThing(station, apiBridgeUID); + ThingUID bridgeUID = createThing(station, accountUID); station.getModules().values().stream() .forEach(module -> createThing(module, bridgeUID)); }); } } HomeApi homeApi = localHandler.getRestManager(HomeApi.class); - if (homeApi != null) { // Search all the rest + if (homeApi != null) { // Search those who depend from a home homeApi.getHomesData(null, null).stream().filter(h -> !h.getFeatures().isEmpty()).forEach(home -> { - ThingUID homeUID = createThing(home, apiBridgeUID); + ThingUID homeUID = createThing(home, accountUID); + home.getKnownPersons().forEach(person -> createThing(person, homeUID)); - home.getModules().values().stream().forEach(device -> { - ModuleType deviceType = device.getType(); - String deviceBridge = device.getBridge(); - ThingUID bridgeUID = deviceBridge != null && deviceType.getBridge() != ModuleType.HOME - ? findThingUID(deviceType.getBridge(), deviceBridge, apiBridgeUID) - : deviceType.getBridge() == ModuleType.HOME ? homeUID : apiBridgeUID; - createThing(device, bridgeUID); - }); + + Map bridgesUids = new HashMap<>(); + home.getRooms().values().stream().forEach(room -> { room.getModuleIds().stream().map(id -> home.getModules().get(id)) .map(m -> m != null ? m.getType().feature : FeatureArea.NONE) .filter(f -> FeatureArea.ENERGY.equals(f)).findAny() - .ifPresent(f -> createThing(room, homeUID)); + .ifPresent(f -> bridgesUids.put(room.getId(), createThing(room, homeUID))); }); + + // Creating modules that have no bridge first + home.getModules().values().stream().filter(module -> module.getBridge() == null) + .forEach(device -> bridgesUids.put(device.getId(), createThing(device, homeUID))); + // Then the others + home.getModules().values().stream().filter(module -> module.getBridge() != null).forEach( + device -> createThing(device, bridgesUids.getOrDefault(device.getBridge(), homeUID))); }); } } catch (NetatmoException e) { @@ -107,26 +112,19 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements } } - private ThingUID findThingUID(ModuleType thingType, String thingId, @Nullable ThingUID brigdeUID) { - for (ThingTypeUID supported : getSupportedThingTypes()) { - ThingTypeUID thingTypeUID = thingType.thingTypeUID; - if (supported.equals(thingTypeUID)) { - String id = thingId.replaceAll("[^a-zA-Z0-9_]", ""); - return brigdeUID == null ? new ThingUID(supported, id) : new ThingUID(supported, brigdeUID, id); - } - } - throw new IllegalArgumentException("Unsupported device type discovered : " + thingType); + private ThingUID findThingUID(ModuleType thingType, String thingId, ThingUID bridgeUID) { + ThingTypeUID thingTypeUID = thingType.thingTypeUID; + return getSupportedThingTypes().stream().filter(supported -> supported.equals(thingTypeUID)).findFirst() + .map(supported -> new ThingUID(supported, bridgeUID, thingId.replaceAll("[^a-zA-Z0-9_]", ""))) + .orElseThrow(() -> new IllegalArgumentException("Unsupported device type discovered : " + thingType)); } - private ThingUID createThing(NAModule module, @Nullable ThingUID bridgeUID) { + private ThingUID createThing(NAModule module, ThingUID bridgeUID) { ThingUID moduleUID = findThingUID(module.getType(), module.getId(), bridgeUID); DiscoveryResultBuilder resultBuilder = DiscoveryResultBuilder.create(moduleUID) .withProperty(NAThingConfiguration.ID, module.getId()) .withRepresentationProperty(NAThingConfiguration.ID) - .withLabel(module.getName() != null ? module.getName() : module.getId()); - if (bridgeUID != null) { - resultBuilder.withBridge(bridgeUID); - } + .withLabel(module.getName() != null ? module.getName() : module.getId()).withBridge(bridgeUID); thingDiscovered(resultBuilder.build()); return moduleUID; } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java index d620431126..18eb8b3ffe 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java @@ -25,6 +25,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.netatmo.internal.api.data.ModuleType; import org.openhab.binding.netatmo.internal.api.dto.NAObject; +import org.openhab.binding.netatmo.internal.api.dto.NAThing; import org.openhab.binding.netatmo.internal.config.NAThingConfiguration; import org.openhab.binding.netatmo.internal.handler.capability.Capability; import org.openhab.binding.netatmo.internal.handler.capability.CapabilityMap; @@ -139,6 +140,14 @@ public interface CommonInterface { } default void setNewData(NAObject newData) { + if (newData instanceof NAThing) { + NAThing thingData = (NAThing) newData; + if (getId().equals(thingData.getBridge())) { + getActiveChildren().stream().filter(child -> child.getId().equals(thingData.getId())).findFirst() + .ifPresent(child -> child.setNewData(thingData)); + return; + } + } String finalReason = null; for (Capability cap : getCapabilities().values()) { String thingStatusReason = cap.setNewData(newData); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/Capability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/Capability.java index a2ef48a640..380c6f5f24 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/Capability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/Capability.java @@ -94,8 +94,9 @@ public class Capability { properties = new HashMap<>(thing.getProperties()); firstLaunch = properties.isEmpty(); if (firstLaunch && !moduleType.isLogical()) { + String name = moduleType.apiName.isBlank() ? moduleType.name() : moduleType.apiName; + properties.put(PROPERTY_MODEL_ID, name); properties.put(PROPERTY_VENDOR, VENDOR); - properties.put(PROPERTY_MODEL_ID, moduleType.name()); } statusReason = null; } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java index 328eb26227..3589f9d379 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EnergyCapability.java @@ -58,17 +58,21 @@ public class EnergyCapability extends RestCapability { protected void updateHomeData(HomeData homeData) { NAObjectMap rooms = homeData.getRooms(); NAObjectMap modules = homeData.getModules(); - handler.getActiveChildren().forEach(handler -> { - HomeDataRoom roomData = rooms.get(handler.getId()); - if (roomData != null) { + handler.getActiveChildren().forEach(childHandler -> { + String childId = childHandler.getId(); + rooms.getOpt(childId).ifPresentOrElse(roomData -> { roomData.setIgnoredForThingUpdate(true); - handler.setNewData(roomData); - } - HomeDataModule moduleData = modules.get(handler.getId()); - if (moduleData != null) { - moduleData.setIgnoredForThingUpdate(true); - handler.setNewData(moduleData); - } + childHandler.setNewData(roomData); + }, () -> { + modules.getOpt(childId).ifPresent(childData -> { + childData.setIgnoredForThingUpdate(true); + childHandler.setNewData(childData); + }); + modules.values().stream().filter(module -> childId.equals(module.getBridge())) + .forEach(bridgedModule -> { + childHandler.setNewData(bridgedModule); + }); + }); }); descriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), GROUP_ENERGY, CHANNEL_PLANNING), homeData.getThermSchedules().stream().map(p -> new StateOption(p.getId(), p.getName())) @@ -80,15 +84,23 @@ public class EnergyCapability extends RestCapability { protected void updateHomeStatus(HomeStatus homeStatus) { NAObjectMap rooms = homeStatus.getRooms(); NAObjectMap modules = homeStatus.getModules(); - handler.getActiveChildren().forEach(handler -> { - Room roomData = rooms.get(handler.getId()); - if (roomData != null) { - handler.setNewData(roomData); - } - HomeStatusModule data = modules.get(handler.getId()); - if (data != null) { - handler.setNewData(data); - } + handler.getActiveChildren().forEach(childHandler -> { + String childId = childHandler.getId(); + rooms.getOpt(childId).ifPresentOrElse(roomData -> childHandler.setNewData(roomData), () -> { + modules.getOpt(childId).ifPresentOrElse(childData -> { + childHandler.setNewData(childData); + modules.values().stream().filter(module -> childId.equals(module.getBridge())) + .forEach(bridgedModule -> { + childHandler.setNewData(bridgedModule); + }); + + }, () -> { + // This module is not present in the homestatus data, so it is considered as unreachable + HomeStatusModule module = new HomeStatusModule(); + module.setReachable(false); + childHandler.setNewData(module); + }); + }); }); } diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java index c19757d4ae..d6d7848a35 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java @@ -52,14 +52,18 @@ class SecurityCapability extends RestCapability { NAObjectMap modules = homeData.getModules(); handler.getActiveChildren().forEach(childHandler -> { String childId = childHandler.getId(); - persons.getOpt(childId).ifPresentOrElse(person -> { - person.setIgnoredForThingUpdate(true); - childHandler.setNewData(person); + persons.getOpt(childId).ifPresentOrElse(personData -> { + personData.setIgnoredForThingUpdate(true); + childHandler.setNewData(personData); }, () -> { - modules.getOpt(childId).ifPresent(module -> { - module.setIgnoredForThingUpdate(true); - childHandler.setNewData(module); + modules.getOpt(childId).ifPresent(childData -> { + childData.setIgnoredForThingUpdate(true); + childHandler.setNewData(childData); }); + modules.values().stream().filter(module -> childId.equals(module.getBridge())) + .forEach(bridgedModule -> { + childHandler.setNewData(bridgedModule); + }); }); }); } @@ -70,8 +74,15 @@ class SecurityCapability extends RestCapability { NAObjectMap modules = homeStatus.getModules(); handler.getActiveChildren().forEach(childHandler -> { String childId = childHandler.getId(); - persons.getOpt(childId).ifPresentOrElse(person -> childHandler.setNewData(person), () -> { - modules.getOpt(childId).ifPresentOrElse(module -> childHandler.setNewData(module), () -> { + persons.getOpt(childId).ifPresentOrElse(personData -> childHandler.setNewData(personData), () -> { + modules.getOpt(childId).ifPresentOrElse(childData -> { + childHandler.setNewData(childData); + modules.values().stream().filter(module -> childId.equals(module.getBridge())) + .forEach(bridgedModule -> { + childHandler.setNewData(bridgedModule); + }); + + }, () -> { // This module is not present in the homestatus data, so it is considered as unreachable HomeStatusModule module = new HomeStatusModule(); module.setReachable(false); diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/SirenChannelHelper.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/SirenChannelHelper.java new file mode 100644 index 0000000000..96ccdc7ca9 --- /dev/null +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/SirenChannelHelper.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2010-2022 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.netatmo.internal.handler.channelhelper; + +import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*; +import static org.openhab.binding.netatmo.internal.utils.ChannelTypeUtils.toStringType; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.netatmo.internal.api.dto.HomeStatusModule; +import org.openhab.binding.netatmo.internal.api.dto.NAThing; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * The {@link SirenChannelHelper} handles specific behavior of the siren module + * + * @author Gaël L'hopital - Initial contribution + * + */ +@NonNullByDefault +public class SirenChannelHelper extends ChannelHelper { + + public SirenChannelHelper() { + super(GROUP_SIREN); + } + + @Override + protected @Nullable State internalGetProperty(String channelId, NAThing naThing, Configuration config) { + if (naThing instanceof HomeStatusModule) { + HomeStatusModule homeStatus = (HomeStatusModule) naThing; + switch (channelId) { + case CHANNEL_MONITORING: + return homeStatus.getMonitoring(); + case CHANNEL_STATUS: + return homeStatus.getStatus().map(status -> toStringType(status)).orElse(UnDefType.UNDEF); + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties index 6f323041dd..042d1972e9 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties @@ -55,7 +55,6 @@ channel-group-type.netatmo.noise.label = Noise channel-group-type.netatmo.person.label = Person channel-group-type.netatmo.person.channel.last-seen.label = Last Seen channel-group-type.netatmo.person.channel.last-seen.description = Moment when this person was last seen. -channel-group-type.netatmo.plug.label = Thermostat Plug channel-group-type.netatmo.presence.label = Presence Camera channel-group-type.netatmo.pressure-extended.label = Pressure channel-group-type.netatmo.pressure-extended.channel.trend.label = Pressure Trend @@ -74,6 +73,7 @@ channel-group-type.netatmo.setpoint.channel.end.description = End time of the cu channel-group-type.netatmo.setpoint.channel.start.label = Setpoint Start channel-group-type.netatmo.setpoint.channel.start.description = Start time of the currently applied setpoint. channel-group-type.netatmo.signal.label = Signal +channel-group-type.netatmo.siren.label = Siren Status channel-group-type.netatmo.status-doorbell.label = Camera Status channel-group-type.netatmo.status.label = Camera Status channel-group-type.netatmo.sub-event-doorbell.label = Sub Event @@ -268,6 +268,12 @@ channel-type.netatmo.setpoint-duration.label = Setpoint Duration channel-type.netatmo.setpoint-duration.description = Default duration of manual setpoint changes. channel-type.netatmo.setpoint.label = Setpoint channel-type.netatmo.setpoint.description = Thermostat temperature setpoint. +channel-type.netatmo.siren-monitoring.label = Monitoring +channel-type.netatmo.siren-monitoring.description = Monitoring state of the equipment +channel-type.netatmo.siren-status.label = Status +channel-type.netatmo.siren-status.description = Status of the siren +channel-type.netatmo.siren-status.state.option.no_sound = Silent +channel-type.netatmo.siren-status.state.option.sound = Alarm channel-type.netatmo.th-mode.label = Thermostat Mode channel-type.netatmo.th-mode.description = Chosen thermostat mode (home, frost guard, manual, max). channel-type.netatmo.th-mode.state.option.HOME = Home diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo_it.properties b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo_it.properties index 2abba902ff..4b491de833 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo_it.properties +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo_it.properties @@ -55,7 +55,6 @@ channel-group-type.netatmo.noise.label = Rumore channel-group-type.netatmo.person.label = Persona channel-group-type.netatmo.person.channel.last-seen.label = Visto l'ultima volta channel-group-type.netatmo.person.channel.last-seen.description = Momento quando questa persona è stata vista per l'ultima volta. -channel-group-type.netatmo.plug.label = Spina Termostato channel-group-type.netatmo.presence.label = Fotocamera Presenza channel-group-type.netatmo.pressure-extended.label = Pressione channel-group-type.netatmo.pressure-extended.channel.trend.label = Grafico Pressione diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml index 9c174af72f..2507101011 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml @@ -10,6 +10,25 @@ Monitoring state of the camera + + Switch + + Monitoring state of the equipment + + + + + String + + Status of the siren + + + + + + + + Contact diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/energy.xml b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/energy.xml index 29a76de128..98597d4e5c 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/energy.xml +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/energy.xml @@ -4,13 +4,6 @@ xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0" xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - - - - - - - diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/security.xml b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/security.xml index 82e2483ee4..37214aaba7 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/security.xml +++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/security.xml @@ -23,6 +23,14 @@ + + + + + + + + -- 2.47.3