]> git.basschouten.com Git - openhab-addons.git/commitdiff
[netatmo] Add Siren capability to Presence Outdoor Camera (#14485)
authorGaël L'hopital <gael@lhopital.org>
Thu, 13 Jul 2023 16:32:46 +0000 (18:32 +0200)
committerGitHub <noreply@github.com>
Thu, 13 Jul 2023 16:32:46 +0000 (18:32 +0200)
* Adding Siren capability to Presence Outdoor Camera
Resolves #14466

---------

Signed-off-by: clinique <gael@lhopital.org>
19 files changed:
bundles/org.openhab.binding.netatmo/README.md
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoBindingConstants.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/ApiError.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/NetatmoException.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/SecurityApi.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/HomeStatusModule.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/CommonInterface.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/Capability.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/PresenceCapability.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/PresenceChannelHelper.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/channelhelper/SirenChannelHelper.java
bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties
bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml
bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/security.xml
bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/update/instructions.xml [new file with mode: 0644]

index 6cd14ea6e8e1ba3c06a037b792a9fc0583c176af..83b19cb45ae16f5756d22f9463c2200bc406aec9 100644 (file)
@@ -563,6 +563,7 @@ Warnings:
 | signal         | strength             | Number       | Read-only  | Signal strength (0 for no signal, 1 for weak...)                                                                                            |
 | signal         | value                | Number:Power | Read-only  | Signal strength in dBm                                                                                                                      |
 | presence       | floodlight           | String       | Read-write | Sets the floodlight to ON/OFF/AUTO                                                                                                          |
+| presence       | siren                | Switch       | Read-write | Status of the siren, if silent or emitting an alarm                                                                                         |
 | last-event     | type                 | String       | Read-only  | Type of event                                                                                                                               |
 | last-event     | subtype              | String       | Read-only  | Sub-type of event                                                                                                                           |
 | last-event     | time                 | DateTime     | Read-only  | Time of occurrence of event                                                                                                                 |
@@ -614,7 +615,7 @@ Note: live feeds either locally or via VPN are not available in Netatmo API.
 
 | 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         | status      | Switch       | 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                              |
index 586bd5bee0c7df9de3793f80119628ab7532c56c..afe94f7fa1fd45a1e65f59171443c286571523e7 100644 (file)
@@ -34,6 +34,7 @@ public class NetatmoBindingConstants {
     public static final String PROPERTY_COUNTRY = "country";
     public static final String PROPERTY_TIMEZONE = "timezone";
     public static final String PROPERTY_FEATURE = "feature";
+    public static final String PROPERTY_THING_TYPE_VERSION = "thingTypeVersion";
 
     // Channel group ids
     public static final String GROUP_LAST_EVENT = "last-event";
@@ -113,6 +114,7 @@ public class NetatmoBindingConstants {
     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_SIREN = GROUP_SIREN;
     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";
index 85f5ebf3ad425922a41cc6bb78a3a5e414056777..19d83ff945cda2a959b2a8a28b072b5fc4a2546d 100644 (file)
@@ -16,7 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.ServiceError;
 
 /**
- * The {@link ApiError} models an errored response from API
+ * The {@link ApiError} models an error response from API
  *
  * @author Gaël L'hopital - Initial contribution
  */
index d89570bd2f36183449f52caf5ab537463bf0db3e..aa88a28e394e6e49532a32455c6afa13f81891c6 100644 (file)
@@ -29,11 +29,11 @@ public class NetatmoException extends IOException {
     private ServiceError statusCode = ServiceError.UNKNOWN;
 
     public NetatmoException(String format, Object... args) {
-        super(String.format(format, args));
+        super(format.formatted(args));
     }
 
     public NetatmoException(Exception e, String format, Object... args) {
-        super(String.format(format, args), e);
+        super(format.formatted(args), e);
     }
 
     public NetatmoException(String message) {
@@ -54,6 +54,6 @@ public class NetatmoException extends IOException {
         String message = super.getMessage();
         return message == null ? null
                 : ServiceError.UNKNOWN.equals(statusCode) ? message
-                        : String.format("Rest call failed: statusCode=%s, message=%s", statusCode, message);
+                        : "Rest call failed: statusCode=%s, message=%s".formatted(statusCode, message);
     }
 }
index 1b82c00f983aad0221e331ad339efd2a76c76388..c56a8cf93928b089594d87f06f89fc7d1d4144ce 100644 (file)
@@ -25,6 +25,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FloodLightMode;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.SirenStatus;
 import org.openhab.binding.netatmo.internal.api.dto.Home;
 import org.openhab.binding.netatmo.internal.api.dto.HomeEvent;
 import org.openhab.binding.netatmo.internal.api.dto.HomeEvent.NAEventsDataResponse;
@@ -126,6 +127,12 @@ public class SecurityApi extends RestManager {
         post(uriBuilder, ApiResponse.Ok.class, payload);
     }
 
+    public void changeSirenStatus(String homeId, String moduleId, SirenStatus status) throws NetatmoException {
+        UriBuilder uriBuilder = getApiUriBuilder(PATH_STATE);
+        String payload = PAYLOAD_SIREN_PRESENCE.formatted(homeId, moduleId, status.name().toLowerCase());
+        post(uriBuilder, ApiResponse.Ok.class, payload);
+    }
+
     public void setPersonAwayStatus(String homeId, String personId, boolean away) throws NetatmoException {
         UriBuilder uriBuilder = getApiUriBuilder(away ? SUB_PATH_PERSON_AWAY : SUB_PATH_PERSON_HOME);
         String payload = String.format(away ? PAYLOAD_PERSON_AWAY : PAYLOAD_PERSON_HOME, homeId, personId);
index d4181ca4da47391d240de7cc0ffb1f7909cc260b..da58f434894e978b25d34dd5152d08c88f0a184a 100644 (file)
@@ -64,66 +64,67 @@ import org.openhab.core.thing.ThingTypeUID;
  */
 @NonNullByDefault
 public enum ModuleType {
-    UNKNOWN(FeatureArea.NONE, "", null, Set.of()),
+    UNKNOWN(FeatureArea.NONE, "", 1, null, Set.of()),
 
-    ACCOUNT(FeatureArea.NONE, "", null, Set.of(), new ChannelGroup(ApiBridgeChannelHelper.class, GROUP_MONITORING)),
+    ACCOUNT(FeatureArea.NONE, "", 1, null, Set.of(), new ChannelGroup(ApiBridgeChannelHelper.class, GROUP_MONITORING)),
 
-    HOME(FeatureArea.NONE, "NAHome", ACCOUNT,
+    HOME(FeatureArea.NONE, "NAHome", 1, ACCOUNT,
             Set.of(DeviceCapability.class, HomeCapability.class, ChannelHelperCapability.class),
             new ChannelGroup(SecurityChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_SECURITY),
             new ChannelGroup(EnergyChannelHelper.class, GROUP_ENERGY)),
 
-    PERSON(FeatureArea.SECURITY, "NAPerson", HOME, Set.of(PersonCapability.class, ChannelHelperCapability.class),
+    PERSON(FeatureArea.SECURITY, "NAPerson", 1, HOME, Set.of(PersonCapability.class, ChannelHelperCapability.class),
             new ChannelGroup(PersonChannelHelper.class, GROUP_PERSON),
             new ChannelGroup(EventPersonChannelHelper.class, GROUP_PERSON_LAST_EVENT)),
 
-    WELCOME(FeatureArea.SECURITY, "NACamera", HOME, Set.of(CameraCapability.class, ChannelHelperCapability.class),
+    WELCOME(FeatureArea.SECURITY, "NACamera", 1, HOME, Set.of(CameraCapability.class, ChannelHelperCapability.class),
             ChannelGroup.SIGNAL, ChannelGroup.EVENT,
             new ChannelGroup(CameraChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_CAM_STATUS, GROUP_CAM_LIVE)),
 
-    TAG(FeatureArea.SECURITY, "NACamDoorTag", WELCOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
+    TAG(FeatureArea.SECURITY, "NACamDoorTag", 1, WELCOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
             ChannelGroup.BATTERY, ChannelGroup.TIMESTAMP, new ChannelGroup(DoorTagChannelHelper.class, GROUP_TAG)),
 
-    SIREN(FeatureArea.SECURITY, "NIS", WELCOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
+    SIREN(FeatureArea.SECURITY, "NIS", 1, WELCOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
             ChannelGroup.BATTERY, ChannelGroup.TIMESTAMP, new ChannelGroup(SirenChannelHelper.class, GROUP_SIREN)),
 
-    PRESENCE(FeatureArea.SECURITY, "NOC", HOME, Set.of(PresenceCapability.class, ChannelHelperCapability.class),
+    PRESENCE(FeatureArea.SECURITY, "NOC", 1, HOME, Set.of(PresenceCapability.class, ChannelHelperCapability.class),
             ChannelGroup.SIGNAL, ChannelGroup.EVENT,
             new ChannelGroup(PresenceChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_CAM_STATUS, GROUP_CAM_LIVE,
                     GROUP_PRESENCE),
             new ChannelGroup(EventCameraChannelHelper.class, GROUP_SUB_EVENT)),
 
-    DOORBELL(FeatureArea.SECURITY, "NDB", HOME, Set.of(DoorbellCapability.class, ChannelHelperCapability.class),
+    DOORBELL(FeatureArea.SECURITY, "NDB", 1, HOME, Set.of(DoorbellCapability.class, ChannelHelperCapability.class),
             ChannelGroup.SIGNAL,
             new ChannelGroup(CameraChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_DOORBELL_STATUS,
                     GROUP_DOORBELL_LIVE),
             new ChannelGroup(EventCameraChannelHelper.class, GROUP_DOORBELL_LAST_EVENT, GROUP_DOORBELL_SUB_EVENT)),
 
-    WEATHER_STATION(FeatureArea.WEATHER, "NAMain", ACCOUNT,
+    WEATHER_STATION(FeatureArea.WEATHER, "NAMain", 1, ACCOUNT,
             Set.of(DeviceCapability.class, WeatherCapability.class, MeasureCapability.class,
                     ChannelHelperCapability.class),
             ChannelGroup.SIGNAL, ChannelGroup.HUMIDITY, ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE,
             ChannelGroup.AIR_QUALITY, ChannelGroup.LOCATION, ChannelGroup.NOISE, ChannelGroup.TEMP_INSIDE_EXT,
             new ChannelGroup(PressureChannelHelper.class, MeasureClass.PRESSURE, GROUP_TYPE_PRESSURE_EXTENDED)),
 
-    OUTDOOR(FeatureArea.WEATHER, "NAModule1", WEATHER_STATION,
+    OUTDOOR(FeatureArea.WEATHER, "NAModule1", 1, WEATHER_STATION,
             Set.of(MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.HUMIDITY,
             ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY, ChannelGroup.TEMP_OUTSIDE_EXT),
 
-    WIND(FeatureArea.WEATHER, "NAModule2", WEATHER_STATION, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
-            ChannelGroup.TSTAMP_EXT, ChannelGroup.BATTERY, new ChannelGroup(WindChannelHelper.class, GROUP_WIND)),
+    WIND(FeatureArea.WEATHER, "NAModule2", 1, WEATHER_STATION, Set.of(ChannelHelperCapability.class),
+            ChannelGroup.SIGNAL, ChannelGroup.TSTAMP_EXT, ChannelGroup.BATTERY,
+            new ChannelGroup(WindChannelHelper.class, GROUP_WIND)),
 
-    RAIN(FeatureArea.WEATHER, "NAModule3", WEATHER_STATION,
+    RAIN(FeatureArea.WEATHER, "NAModule3", 1, WEATHER_STATION,
             Set.of(MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
             ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY,
             new ChannelGroup(RainChannelHelper.class, MeasureClass.RAIN_QUANTITY, GROUP_RAIN)),
 
-    INDOOR(FeatureArea.WEATHER, "NAModule4", WEATHER_STATION,
+    INDOOR(FeatureArea.WEATHER, "NAModule4", 1, WEATHER_STATION,
             Set.of(MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
             ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY, ChannelGroup.HUMIDITY,
             ChannelGroup.TEMP_INSIDE_EXT, ChannelGroup.AIR_QUALITY),
 
-    HOME_COACH(FeatureArea.AIR_CARE, "NHC", ACCOUNT,
+    HOME_COACH(FeatureArea.AIR_CARE, "NHC", 1, ACCOUNT,
             Set.of(DeviceCapability.class, AirCareCapability.class, MeasureCapability.class,
                     ChannelHelperCapability.class),
             ChannelGroup.LOCATION, ChannelGroup.SIGNAL, ChannelGroup.NOISE, ChannelGroup.HUMIDITY,
@@ -131,22 +132,23 @@ public enum ModuleType {
             new ChannelGroup(AirQualityChannelHelper.class, GROUP_TYPE_AIR_QUALITY_EXTENDED),
             new ChannelGroup(PressureChannelHelper.class, MeasureClass.PRESSURE, GROUP_PRESSURE)),
 
-    PLUG(FeatureArea.ENERGY, "NAPlug", HOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL),
+    PLUG(FeatureArea.ENERGY, "NAPlug", 1, HOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL),
 
-    VALVE(FeatureArea.ENERGY, "NRV", PLUG, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
+    VALVE(FeatureArea.ENERGY, "NRV", 1, PLUG, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
             ChannelGroup.BATTERY_EXT),
 
-    THERMOSTAT(FeatureArea.ENERGY, "NATherm1", PLUG, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
+    THERMOSTAT(FeatureArea.ENERGY, "NATherm1", 1, PLUG, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
             ChannelGroup.BATTERY_EXT, new ChannelGroup(Therm1ChannelHelper.class, GROUP_TYPE_TH_PROPERTIES)),
 
-    ROOM(FeatureArea.ENERGY, "NARoom", HOME, Set.of(RoomCapability.class, ChannelHelperCapability.class),
+    ROOM(FeatureArea.ENERGY, "NARoom", 1, HOME, Set.of(RoomCapability.class, ChannelHelperCapability.class),
             new ChannelGroup(RoomChannelHelper.class, GROUP_TYPE_ROOM_PROPERTIES, GROUP_TYPE_ROOM_TEMPERATURE),
             new ChannelGroup(SetpointChannelHelper.class, GROUP_SETPOINT)),
 
-    SMOKE_DETECTOR(FeatureArea.SECURITY, "NSD", HOME, Set.of(AlarmEventCapability.class, ChannelHelperCapability.class),
-            ChannelGroup.SIGNAL, ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT),
+    SMOKE_DETECTOR(FeatureArea.SECURITY, "NSD", 1, HOME,
+            Set.of(AlarmEventCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
+            ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT),
 
-    CO_DETECTOR(FeatureArea.SECURITY, "NCO", HOME, Set.of(AlarmEventCapability.class, ChannelHelperCapability.class),
+    CO_DETECTOR(FeatureArea.SECURITY, "NCO", 1, HOME, Set.of(AlarmEventCapability.class, ChannelHelperCapability.class),
             ChannelGroup.SIGNAL, ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT);
 
     public static final EnumSet<ModuleType> AS_SET = EnumSet.allOf(ModuleType.class);
@@ -157,8 +159,9 @@ public enum ModuleType {
     public final ThingTypeUID thingTypeUID;
     public final FeatureArea feature;
     public final String apiName;
+    public final int thingTypeVersion;
 
-    ModuleType(FeatureArea feature, String apiName, @Nullable ModuleType bridge,
+    ModuleType(FeatureArea feature, String apiName, int thingTypeVersion, @Nullable ModuleType bridge,
             Set<Class<? extends Capability>> capabilities, ChannelGroup... channelGroups) {
         this.bridgeType = Optional.ofNullable(bridge);
         this.feature = feature;
@@ -166,6 +169,7 @@ public enum ModuleType {
         this.apiName = apiName;
         this.channelGroups = Set.of(channelGroups);
         this.thingTypeUID = new ThingTypeUID(BINDING_ID, name().toLowerCase().replace("_", "-"));
+        this.thingTypeVersion = thingTypeVersion;
     }
 
     public boolean isLogical() {
index e2d692a3fea29d1b88a34094a2da8167db6ab51b..485af4f6705162ab3de8de48ffcbea3c1efd6a8d 100644 (file)
@@ -158,6 +158,7 @@ public class NetatmoConstants {
 
     // Payloads
     public static final String PAYLOAD_FLOODLIGHT = "{\"home\": {\"id\":\"%s\",\"modules\": [ {\"id\":\"%s\",\"floodlight\":\"%s\"} ]}}";
+    public static final String PAYLOAD_SIREN_PRESENCE = "{\"home\": {\"id\":\"%s\",\"modules\": [ {\"id\":\"%s\",\"siren_status\":\"%s\"} ]}}";
     public static final String PAYLOAD_PERSON_AWAY = "{\"home_id\":\"%s\",\"person_id\":\"%s\"}";
     public static final String PAYLOAD_PERSON_HOME = "{\"home_id\":\"%s\",\"person_ids\":[\"%s\"]}";
 
@@ -348,6 +349,20 @@ public class NetatmoConstants {
         UNKNOWN;
     }
 
+    public enum SirenStatus {
+        SOUND,
+        NO_SOUND,
+        UNKNOWN;
+
+        public static SirenStatus get(String value) {
+            try {
+                return valueOf(value.toUpperCase());
+            } catch (IllegalArgumentException e) {
+                return UNKNOWN;
+            }
+        }
+    }
+
     public enum BatteryState {
         @SerializedName("full")
         FULL(100),
index d45369b6bbc3b8a0f3817b69550a87ae3588824e..478c0966d13a4ae763a33fcc0702ae379bab2fc5 100644 (file)
@@ -21,6 +21,7 @@ import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.Alimentati
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.BatteryState;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FloodLightMode;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.SdCardStatus;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.SirenStatus;
 import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.OpenClosedType;
 import org.openhab.core.types.State;
@@ -42,7 +43,7 @@ public class HomeStatusModule extends NAThing {
     private FloodLightMode floodlight = FloodLightMode.UNKNOWN;
     private SdCardStatus sdStatus = SdCardStatus.UNKNOWN;
     private AlimentationStatus alimStatus = AlimentationStatus.UNKNOWN;
-    private @Nullable String sirenStatus;
+    private SirenStatus sirenStatus = SirenStatus.UNKNOWN;
     private @Nullable String vpnUrl;
     private boolean isLocal;
     private BatteryState batteryState = BatteryState.UNKNOWN;
@@ -90,8 +91,8 @@ public class HomeStatusModule extends NAThing {
         return alimStatus;
     }
 
-    public Optional<String> getSirenStatus() {
-        return Optional.ofNullable(sirenStatus);
+    public SirenStatus getSirenStatus() {
+        return sirenStatus;
     }
 
     public @Nullable String getVpnUrl() {
index c3b8209694609816a0843833023060b5143476cb..10580177587fe5b63110c58c05a43c1a4aa0a4bd 100644 (file)
@@ -317,6 +317,7 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
                 InputStream stream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
                 try (InputStreamContentProvider inputStreamContentProvider = new InputStreamContentProvider(stream)) {
                     request.content(inputStreamContentProvider, contentType);
+                    request.header(HttpHeader.ACCEPT, "application/json");
                 }
                 logger.trace(" -with payload : {} ", payload);
             }
@@ -404,10 +405,9 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
                                 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 -> {
-                                                action.apply(room, homeUID)
-                                                        .ifPresent(roomUID -> bridgesUids.put(room.getId(), roomUID));
-                                            });
+                                            .filter(f -> FeatureArea.ENERGY.equals(f)).findAny()
+                                            .ifPresent(f -> action.apply(room, homeUID)
+                                                    .ifPresent(roomUID -> bridgesUids.put(room.getId(), roomUID)));
                                 });
 
                                 // Creating modules that have no bridge first, avoiding weather station itself
index 91637754459dc5b1e84e4ed1a21f040d1c0ceba8..d552d77c5518c56523f09410294d32ce76069a36 100644 (file)
@@ -180,7 +180,7 @@ public interface CommonInterface {
             String channelName = channelUID.getIdWithoutGroup();
             getCapabilities().values().forEach(cap -> cap.handleCommand(channelName, command));
         } else {
-            getLogger().debug("Command {}, on channel {} dropped - thing is not ONLINE", command, channelUID);
+            getLogger().debug("Command {} on channel {} dropped - thing is not ONLINE", command, channelUID);
         }
     }
 
index f7c75e0038f98f7f65240791ff40cec5162fa839..692be0465a7fd8cd9923b1779e1b400c173d073f 100644 (file)
@@ -12,7 +12,7 @@
  */
 package org.openhab.binding.netatmo.internal.handler.capability;
 
-import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.VENDOR;
+import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
 import static org.openhab.core.thing.Thing.*;
 
 import java.util.Collection;
@@ -97,10 +97,13 @@ public class Capability {
     protected void beforeNewData() {
         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);
+        if (firstLaunch) {
+            properties.put(PROPERTY_THING_TYPE_VERSION, Integer.toString(moduleType.thingTypeVersion));
+            if (!moduleType.isLogical()) {
+                String name = moduleType.apiName.isBlank() ? moduleType.name() : moduleType.apiName;
+                properties.put(PROPERTY_MODEL_ID, name);
+                properties.put(PROPERTY_VENDOR, VENDOR);
+            }
         }
         statusReason = null;
     }
index d615d7dc9f589080754951f0ce13995e91e85e20..9d3526f956376d30377eb904803d17dd58752143 100644 (file)
  */
 package org.openhab.binding.netatmo.internal.handler.capability;
 
-import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.CHANNEL_FLOODLIGHT;
+import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
 
 import java.util.List;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FloodLightMode;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.SirenStatus;
+import org.openhab.binding.netatmo.internal.api.dto.HomeStatusModule;
 import org.openhab.binding.netatmo.internal.handler.CommonInterface;
 import org.openhab.binding.netatmo.internal.handler.channelhelper.ChannelHelper;
 import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider;
@@ -42,6 +44,12 @@ public class PresenceCapability extends CameraCapability {
         super(handler, descriptionProvider, channelHelpers);
     }
 
+    @Override
+    public void updateHomeStatusModule(HomeStatusModule newData) {
+        super.updateHomeStatusModule(newData);
+        getSecurityCapability().ifPresent(cap -> cap.changeSirenStatus(handler.getId(), SirenStatus.SOUND));
+    }
+
     @Override
     public void handleCommand(String channelName, Command command) {
         if (CHANNEL_FLOODLIGHT.equals(channelName)) {
@@ -50,13 +58,16 @@ public class PresenceCapability extends CameraCapability {
                 return;
             } else if (command instanceof StringType) {
                 try {
-                    FloodLightMode mode = FloodLightMode.valueOf(command.toString());
-                    changeFloodlightMode(mode);
+                    changeFloodlightMode(FloodLightMode.valueOf(command.toString()));
                 } catch (IllegalArgumentException e) {
                     logger.info("Incorrect command '{}' received for channel '{}'", command, channelName);
                 }
                 return;
             }
+        } else if (CHANNEL_SIREN.equals(channelName) && command instanceof OnOffType) {
+            getSecurityCapability().ifPresent(cap -> cap.changeSirenStatus(handler.getId(),
+                    command == OnOffType.ON ? SirenStatus.SOUND : SirenStatus.NO_SOUND));
+            return;
         }
         super.handleCommand(channelName, command);
     }
index 84e422ae208927b80dc2157c95b8dea928f812ef..3cecf7f63e7da9b61cbba9ea5b9a927611e0d7c3 100644 (file)
@@ -25,6 +25,7 @@ import org.openhab.binding.netatmo.internal.api.NetatmoException;
 import org.openhab.binding.netatmo.internal.api.SecurityApi;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
 import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FloodLightMode;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.SirenStatus;
 import org.openhab.binding.netatmo.internal.api.dto.HomeData;
 import org.openhab.binding.netatmo.internal.api.dto.HomeDataModule;
 import org.openhab.binding.netatmo.internal.api.dto.HomeDataPerson;
@@ -238,4 +239,15 @@ class SecurityCapability extends RestCapability<SecurityApi> {
             }
         });
     }
+
+    public void changeSirenStatus(String moduleId, SirenStatus status) {
+        getApi().ifPresent(api -> {
+            try {
+                api.changeSirenStatus(handler.getId(), moduleId, status);
+                handler.expireData();
+            } catch (NetatmoException e) {
+                logger.warn("Error changing siren status '{}' : {}", status, e.getMessage());
+            }
+        });
+    }
 }
index d8882afacd1ac47adc8e371fc46c38e9a300a731..b80011004221f84941731439ccc542c6922380f0 100644 (file)
@@ -12,7 +12,7 @@
  */
 package org.openhab.binding.netatmo.internal.handler.channelhelper;
 
-import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.CHANNEL_FLOODLIGHT;
+import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
 import static org.openhab.binding.netatmo.internal.utils.ChannelTypeUtils.toStringType;
 
 import java.util.Set;
@@ -22,7 +22,9 @@ 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.library.types.OnOffType;
 import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
 
 /**
  * The {@link PresenceChannelHelper} handles specific channels of Presence external cameras
@@ -39,11 +41,19 @@ public class PresenceChannelHelper extends CameraChannelHelper {
 
     @Override
     protected @Nullable State internalGetProperty(String channelId, NAThing naThing, Configuration config) {
-        if (naThing instanceof HomeStatusModule) {
-            HomeStatusModule camera = (HomeStatusModule) naThing;
+        if (naThing instanceof HomeStatusModule presence) {
             switch (channelId) {
                 case CHANNEL_FLOODLIGHT:
-                    return toStringType(camera.getFloodlight());
+                    return toStringType(presence.getFloodlight());
+                case CHANNEL_SIREN:
+                    switch (presence.getSirenStatus()) {
+                        case NO_SOUND:
+                            return OnOffType.OFF;
+                        case SOUND:
+                            return OnOffType.ON;
+                        case UNKNOWN:
+                            return UnDefType.NULL;
+                    }
             }
         }
         return super.internalGetProperty(channelId, naThing, config);
index 896f4f806b4eea0c63c406eec0b1ea47a031629f..3a17c2f668655ddf4921de6aec864df3720282de 100644 (file)
 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 java.util.Set;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.SirenStatus;
 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.library.types.OnOffType;
 import org.openhab.core.types.State;
 import org.openhab.core.types.UnDefType;
 
@@ -32,7 +33,7 @@ import org.openhab.core.types.UnDefType;
  *
  */
 @NonNullByDefault
-public class SirenChannelHelper extends ChannelHelper {
+public class SirenChannelHelper extends EventChannelHelper {
 
     public SirenChannelHelper(Set<String> providedGroups) {
         super(providedGroups);
@@ -40,14 +41,14 @@ public class SirenChannelHelper extends ChannelHelper {
 
     @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);
-            }
+        if (naThing instanceof HomeStatusModule homeStatus) {
+            return switch (channelId) {
+                case CHANNEL_MONITORING -> homeStatus.getMonitoring();
+                case CHANNEL_STATUS -> homeStatus.getStatus().map(SirenStatus::get)
+                        .map(status -> SirenStatus.SOUND == status ? OnOffType.ON : OnOffType.OFF)
+                        .map(State.class::cast).orElse(UnDefType.UNDEF);
+                default -> null;
+            };
         }
         return null;
     }
index d86de3707ebd6a6455e68deadc7fceaf839ef225..c67f080b420cfec370c5619d4be200719f0ac6e4 100644 (file)
@@ -311,10 +311,10 @@ 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-rw.label = Siren
+channel-type.netatmo.siren-status-rw.description = Status of the siren
+channel-type.netatmo.siren-status.label = Siren
 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.tag-status.label = Door Status
 channel-type.netatmo.th-mode.label = Thermostat Mode
 channel-type.netatmo.th-mode.description = Chosen thermostat mode (home, frost guard, manual, max).
index ae4b96ff0ea205b846e5038bba5ba8a3cfe7c7ea..c57680b2b5499d33851a99117556bfb9305e325b 100644 (file)
        </channel-type>
 
        <channel-type id="siren-status">
-               <item-type>String</item-type>
-               <label>Status</label>
+               <item-type>Switch</item-type>
+               <label>Siren</label>
                <description>Status of the siren</description>
-               <state pattern="%s" readOnly="true">
-                       <options>
-                               <option value="no_sound">Silent</option>
-                               <option value="sound">Alarm</option>
-                       </options>
-               </state>
+               <category>Soundvolume</category>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="siren-status-rw">
+               <item-type>Switch</item-type>
+               <label>Siren</label>
+               <description>Status of the siren</description>
+               <category>Soundvolume</category>
        </channel-type>
 
        <channel-type id="window-open">
index 1a8bd9afb24746261b8ec19ee6ce1fc5715ff7b1..29a601f53419533124f91a826498a46f34b24d39 100644 (file)
        <channel-group-type id="presence">
                <label>Presence Camera</label>
                <channels>
+                       <channel id="siren" typeId="siren-status-rw"/>
                        <channel id="floodlight" typeId="floodlight-mode"/>
                </channels>
        </channel-group-type>
diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/update/instructions.xml
new file mode 100644 (file)
index 0000000..10d3dff
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
+
+       <thing-type uid="netatmo:presence">
+
+               <instruction-set targetVersion="1">
+                       <add-channel id="siren" groupIds="presence">
+                               <type>netatmo:siren-status-rw</type>
+                       </add-channel>
+                       <update-channel id="status" groupIds="siren">
+                               <type>netatmo:siren-status</type>
+                       </update-channel>
+               </instruction-set>
+
+       </thing-type>
+
+</update:update-descriptions>