]> git.basschouten.com Git - openhab-addons.git/commitdiff
[netatmo] Adding Webhook event support for Doorbell (#12972)
authorGaël L'hopital <gael@lhopital.org>
Fri, 24 Jun 2022 11:52:02 +0000 (13:52 +0200)
committerGitHub <noreply@github.com>
Fri, 24 Jun 2022 11:52:02 +0000 (13:52 +0200)
* Adding Webhook event support for Doorbell
* Adding doorbell rtc.
* Enhancing NAPushType deserialization
* Setting empty fields to NULL

Signed-off-by: clinique <gael@lhopital.org>
15 files changed:
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoHandlerFactory.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/EventType.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/dto/Event.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/dto/WebhookEvent.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/NAPushType.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/deserialization/NAPushTypeDeserializer.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/DoorbellCapability.java [new file with mode: 0644]
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EventCapability.java [deleted file]
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/HomeSecurityThingCapability.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/servlet/WebhookServlet.java
bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/utils/ChannelTypeUtils.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

index 1912f12f7b8c0339efa1d4164a8274929bcf7f45..ffe339dd7eed053465a93df9a06e3bbad5a176d4 100644 (file)
@@ -31,7 +31,7 @@ import org.openhab.binding.netatmo.internal.handler.capability.CameraCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.Capability;
 import org.openhab.binding.netatmo.internal.handler.capability.ChannelHelperCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.DeviceCapability;
-import org.openhab.binding.netatmo.internal.handler.capability.EventCapability;
+import org.openhab.binding.netatmo.internal.handler.capability.DoorbellCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.HomeCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.MeasureCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.PersonCapability;
@@ -122,14 +122,14 @@ public class NetatmoHandlerFactory extends BaseThingHandlerFactory {
                 newCap = new DeviceCapability(handler);
             } else if (capability == AirCareCapability.class) {
                 newCap = new AirCareCapability(handler);
-            } else if (capability == EventCapability.class) {
-                newCap = new EventCapability(handler);
             } else if (capability == HomeCapability.class) {
                 newCap = new HomeCapability(handler, stateDescriptionProvider);
             } else if (capability == WeatherCapability.class) {
                 newCap = new WeatherCapability(handler);
             } else if (capability == RoomCapability.class) {
                 newCap = new RoomCapability(handler);
+            } else if (capability == DoorbellCapability.class) {
+                newCap = new DoorbellCapability(handler, stateDescriptionProvider, helpers);
             } else if (capability == PersonCapability.class) {
                 newCap = new PersonCapability(handler, stateDescriptionProvider, helpers);
             } else if (capability == CameraCapability.class) {
index e7b8708f2049562b21ab7128e8dc20f730e73cda..19528e6e7086568a39fbc322a010d89799984d1c 100644 (file)
@@ -12,6 +12,7 @@
  */
 package org.openhab.binding.netatmo.internal.api.data;
 
+import java.util.EnumSet;
 import java.util.Set;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -97,6 +98,9 @@ public enum EventType {
     @SerializedName("incoming_call") // When a call as been answered by a user
     INCOMING_CALL(ModuleType.DOORBELL),
 
+    @SerializedName("rtc") // Button pressed
+    RTC(ModuleType.DOORBELL),
+
     @SerializedName("missed_call") // When a call has not been answered by anyone
     MISSED_CALL(ModuleType.DOORBELL),
 
@@ -124,6 +128,8 @@ public enum EventType {
     @SerializedName("new_device")
     NEW_DEVICE(ModuleType.HOME);
 
+    public static final EnumSet<EventType> AS_SET = EnumSet.allOf(EventType.class);
+
     private final Set<ModuleType> appliesTo;
 
     EventType(ModuleType... appliesTo) {
index 8eef24168693da106d414550c67e6a5f9b398b2a..f999e6a1e1684c80760f34b7147b94249efea282 100644 (file)
@@ -30,7 +30,7 @@ import org.openhab.binding.netatmo.internal.handler.capability.CameraCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.Capability;
 import org.openhab.binding.netatmo.internal.handler.capability.ChannelHelperCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.DeviceCapability;
-import org.openhab.binding.netatmo.internal.handler.capability.EventCapability;
+import org.openhab.binding.netatmo.internal.handler.capability.DoorbellCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.HomeCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.MeasureCapability;
 import org.openhab.binding.netatmo.internal.handler.capability.PersonCapability;
@@ -67,29 +67,27 @@ public enum ModuleType {
     ACCOUNT(FeatureArea.NONE, "", null, Set.of()),
 
     HOME(FeatureArea.NONE, "NAHome", ACCOUNT,
-            Set.of(DeviceCapability.class, EventCapability.class, HomeCapability.class, ChannelHelperCapability.class),
+            Set.of(DeviceCapability.class, HomeCapability.class, ChannelHelperCapability.class),
             new ChannelGroup(SecurityChannelHelper.class, GROUP_SECURITY),
             new ChannelGroup(EnergyChannelHelper.class, GROUP_ENERGY)),
 
-    PERSON(FeatureArea.SECURITY, "NAPerson", HOME,
-            Set.of(EventCapability.class, PersonCapability.class, ChannelHelperCapability.class),
+    PERSON(FeatureArea.SECURITY, "NAPerson", 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(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
-            ChannelGroup.EVENT, new ChannelGroup(CameraChannelHelper.class, GROUP_CAM_STATUS, GROUP_CAM_LIVE)),
+    WELCOME(FeatureArea.SECURITY, "NACamera", HOME, Set.of(CameraCapability.class, ChannelHelperCapability.class),
+            ChannelGroup.SIGNAL, ChannelGroup.EVENT,
+            new ChannelGroup(CameraChannelHelper.class, GROUP_CAM_STATUS, GROUP_CAM_LIVE)),
 
     SIREN(FeatureArea.SECURITY, "NIS", WELCOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
             ChannelGroup.BATTERY, ChannelGroup.TIMESTAMP, new ChannelGroup(SirenChannelHelper.class, GROUP_SIREN)),
 
-    PRESENCE(FeatureArea.SECURITY, "NOC", HOME,
-            Set.of(EventCapability.class, PresenceCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
-            ChannelGroup.EVENT,
+    PRESENCE(FeatureArea.SECURITY, "NOC", HOME, Set.of(PresenceCapability.class, ChannelHelperCapability.class),
+            ChannelGroup.SIGNAL, ChannelGroup.EVENT,
             new ChannelGroup(PresenceChannelHelper.class, GROUP_CAM_STATUS, GROUP_CAM_LIVE, GROUP_PRESENCE)),
 
-    DOORBELL(FeatureArea.SECURITY, "NDB", HOME,
-            Set.of(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
+    DOORBELL(FeatureArea.SECURITY, "NDB", HOME, Set.of(DoorbellCapability.class, ChannelHelperCapability.class),
+            ChannelGroup.SIGNAL,
             new ChannelGroup(CameraChannelHelper.class, GROUP_DOORBELL_STATUS, GROUP_DOORBELL_LIVE),
             new ChannelGroup(EventDoorbellChannelHelper.class, GROUP_DOORBELL_LAST_EVENT, GROUP_DOORBELL_SUB_EVENT)),
 
@@ -208,9 +206,4 @@ public enum ModuleType {
         return ModuleType.AS_SET.stream().filter(mt -> mt.thingTypeUID.equals(thingTypeUID)).findFirst()
                 .orElseThrow(() -> new IllegalArgumentException());
     }
-
-    public static ModuleType from(String apiName) {
-        return ModuleType.AS_SET.stream().filter(mt -> apiName.equals(mt.apiName)).findFirst()
-                .orElseThrow(() -> new IllegalArgumentException());
-    }
 }
index 305815c0c7c2146af73314212aaa75684a52f6c7..09edf976b43fa156585f6c8b719b5ec0d69745d5 100644 (file)
@@ -61,8 +61,4 @@ public abstract class Event extends NAObject {
         return Stream.of(EventSubType.values()).filter(v -> v.types.contains(getEventType()) && v.subType == subType)
                 .findFirst();
     }
-
-    public void setEventType(EventType type) {
-        this.type = type;
-    }
 }
index eefa6a53815b123e4c3e84b9667238c953ee5020..21f4a245cf1247453fcdbe6a0d3ba8b26d2b33e2 100644 (file)
@@ -13,6 +13,8 @@
 package org.openhab.binding.netatmo.internal.api.dto;
 
 import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
@@ -29,9 +31,11 @@ import org.openhab.binding.netatmo.internal.deserialization.NAPushType;
  */
 @NonNullByDefault
 public class WebhookEvent extends Event {
-    private @NonNullByDefault({}) NAPushType pushType;
+    private NAPushType pushType = NAPushType.UNKNOWN;
     private String homeId = "";
+    private String deviceId = "";
     private @Nullable String snapshotUrl;
+    private @Nullable String vignetteUrl;
     private NAObjectMap<Person> persons = new NAObjectMap<>();
     // Webhook does not provide the event generation time, so we'll use the event reception time
     private ZonedDateTime time = ZonedDateTime.now();
@@ -63,4 +67,24 @@ public class WebhookEvent extends Event {
     public @Nullable String getSnapshotUrl() {
         return snapshotUrl;
     }
+
+    public @Nullable String getVignetteUrl() {
+        return vignetteUrl;
+    }
+
+    public List<String> getNAObjectList() {
+        List<String> result = new ArrayList<>();
+        result.add(getCameraId());
+        addNotBlank(result, homeId);
+        addNotBlank(result, deviceId);
+        addNotBlank(result, getCameraId());
+        result.addAll(getPersons().keySet());
+        return result;
+    }
+
+    private void addNotBlank(List<String> list, String value) {
+        if (!value.isBlank()) {
+            list.add(value);
+        }
+    }
 }
index 096e8943b5661ab9d4d4bcb0d41311b6f9114473..cac492ec68f6aa9c0ff2541c050451aa4011c6a0 100644 (file)
@@ -23,6 +23,8 @@ import org.openhab.binding.netatmo.internal.api.data.ModuleType;
  */
 @NonNullByDefault
 public class NAPushType {
+    public final static NAPushType UNKNOWN = new NAPushType(ModuleType.UNKNOWN, EventType.UNKNOWN);
+
     private final ModuleType moduleType;
     private final EventType event;
 
index b99cb4db68c921fca1b43adbf3f67fe4ee7b30df..78f5fc45140472cf42ec18758589c35b48b27df4 100644 (file)
@@ -18,11 +18,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.netatmo.internal.api.data.EventType;
 import org.openhab.binding.netatmo.internal.api.data.ModuleType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.gson.JsonDeserializationContext;
 import com.google.gson.JsonDeserializer;
 import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
 
 /**
  * Specialized deserializer for push_type field
@@ -31,21 +32,41 @@ import com.google.gson.JsonParseException;
  */
 @NonNullByDefault
 class NAPushTypeDeserializer implements JsonDeserializer<NAPushType> {
+    private final Logger logger = LoggerFactory.getLogger(NAPushTypeDeserializer.class);
 
     @Override
-    public @Nullable NAPushType deserialize(JsonElement json, Type clazz, JsonDeserializationContext context)
-            throws JsonParseException {
-        String string = json.getAsString();
-        String[] elements = string.split("-");
-        if (elements.length > 1) {
-            try {
-                ModuleType moduleType = ModuleType.from(elements[0]);
-                EventType eventType = EventType.valueOf(elements[1].toUpperCase());
-
-                return new NAPushType(moduleType, eventType);
-            } catch (IllegalArgumentException e) {
-            }
+    public @Nullable NAPushType deserialize(JsonElement json, Type clazz, JsonDeserializationContext context) {
+        final String string = json.getAsString();
+        final String[] elements = string.split("-");
+        ModuleType moduleType = ModuleType.UNKNOWN;
+        EventType eventType = EventType.UNKNOWN;
+        if (elements.length == 2) {
+            moduleType = fromNetatmoObject(elements[0]);
+            eventType = fromEvent(elements[1]);
+        } else {
+            logger.warn("Unexpected syntax received for push_type field : {}", string);
+        }
+        if (moduleType.equals(ModuleType.UNKNOWN) || eventType.equals(EventType.UNKNOWN)) {
+            logger.warn("Unknown module or event type : {}, deserialized to '{}-{}'", string, moduleType, eventType);
         }
-        throw new JsonParseException("Error deserializing : " + string);
+        return new NAPushType(moduleType, eventType);
+    }
+
+    /**
+     * @param apiName : Netatmo Object name (NSD, NACamera...)
+     * @return moduletype value if found, or else Unknown
+     */
+    public static ModuleType fromNetatmoObject(String apiName) {
+        return ModuleType.AS_SET.stream().filter(mt -> apiName.equals(mt.apiName)).findFirst()
+                .orElse(ModuleType.UNKNOWN);
+    }
+
+    /**
+     * @param apiName : Netatmo Event name (hush, off, on ...)
+     * @return eventType value if found, or else Unknown
+     */
+    public static EventType fromEvent(String apiName) {
+        return EventType.AS_SET.stream().filter(et -> apiName.equalsIgnoreCase(et.name())).findFirst()
+                .orElse(EventType.UNKNOWN);
     }
 }
index 380c6f5f2497580c57d333e6ab6632b0b857fb4a..ac7a9f0e375eb3ce93e9b90f5e7fcc341c6ffbe3 100644 (file)
@@ -32,6 +32,7 @@ import org.openhab.binding.netatmo.internal.api.dto.NAHomeStatus.HomeStatus;
 import org.openhab.binding.netatmo.internal.api.dto.NAMain;
 import org.openhab.binding.netatmo.internal.api.dto.NAObject;
 import org.openhab.binding.netatmo.internal.api.dto.NAThing;
+import org.openhab.binding.netatmo.internal.api.dto.WebhookEvent;
 import org.openhab.binding.netatmo.internal.handler.CommonInterface;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.binding.ThingHandlerService;
@@ -74,6 +75,9 @@ public class Capability {
         if (newData instanceof Event) {
             updateEvent((Event) newData);
         }
+        if (newData instanceof WebhookEvent) {
+            updateWebhookEvent((WebhookEvent) newData);
+        }
         if (newData instanceof HomeEvent) {
             updateHomeEvent((HomeEvent) newData);
         }
@@ -137,6 +141,10 @@ public class Capability {
         // do nothing by default, can be overridden by subclasses
     }
 
+    protected void updateWebhookEvent(WebhookEvent newData) {
+        // do nothing by default, can be overridden by subclasses
+    }
+
     protected void updateNADevice(Device newData) {
         // do nothing by default, can be overridden by subclasses
     }
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/DoorbellCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/DoorbellCapability.java
new file mode 100644 (file)
index 0000000..3bbc2e9
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * 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.capability;
+
+import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
+import static org.openhab.binding.netatmo.internal.utils.ChannelTypeUtils.*;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.netatmo.internal.api.dto.WebhookEvent;
+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;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.types.UnDefType;
+
+/**
+ * {@link DoorbellCapability} give to handle Welcome Doorbell specifics
+ *
+ * @author Gaël L'hopital - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class DoorbellCapability extends CameraCapability {
+    private final ThingUID thingUid;
+
+    public DoorbellCapability(CommonInterface handler, NetatmoDescriptionProvider descriptionProvider,
+            List<ChannelHelper> channelHelpers) {
+        super(handler, descriptionProvider, channelHelpers);
+        thingUid = handler.getThing().getUID();
+    }
+
+    @Override
+    public void updateWebhookEvent(WebhookEvent event) {
+        super.updateWebhookEvent(event);
+
+        handler.updateState(new ChannelUID(thingUid, GROUP_SUB_EVENT, CHANNEL_EVENT_TYPE),
+                toStringType(event.getEventType()));
+        handler.updateState(new ChannelUID(thingUid, GROUP_SUB_EVENT, CHANNEL_EVENT_TIME),
+                toDateTimeType(event.getTime()));
+        handler.updateState(new ChannelUID(thingUid, GROUP_SUB_EVENT, CHANNEL_EVENT_SNAPSHOT),
+                toRawType(event.getSnapshotUrl()));
+        handler.updateState(new ChannelUID(thingUid, GROUP_SUB_EVENT, CHANNEL_EVENT_SNAPSHOT_URL),
+                toStringType(event.getSnapshotUrl()));
+        handler.updateState(new ChannelUID(thingUid, GROUP_SUB_EVENT, CHANNEL_EVENT_VIGNETTE),
+                toRawType(event.getVignetteUrl()));
+        handler.updateState(new ChannelUID(thingUid, GROUP_SUB_EVENT, CHANNEL_EVENT_VIGNETTE_URL),
+                toStringType(event.getVignetteUrl()));
+
+        String message = event.getName();
+        handler.updateState(new ChannelUID(thingUid, GROUP_SUB_EVENT, CHANNEL_EVENT_MESSAGE),
+                message == null || message.isBlank() ? UnDefType.NULL : toStringType(message));
+    }
+}
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EventCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/EventCapability.java
deleted file mode 100644 (file)
index aa4e294..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * 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.capability;
-
-import java.util.Optional;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.netatmo.internal.handler.ApiBridgeHandler;
-import org.openhab.binding.netatmo.internal.handler.CommonInterface;
-import org.openhab.binding.netatmo.internal.servlet.WebhookServlet;
-
-/**
- * {@link EventCapability} is the base class for handlers subject to receive event notifications.
- * This class registers to NetatmoServletService so it can be notified when an event arrives.
- *
- * @author Gaël L'hopital - Initial contribution
- *
- */
-@NonNullByDefault
-public class EventCapability extends Capability {
-    private Optional<WebhookServlet> webhook = Optional.empty();
-
-    public EventCapability(CommonInterface handler) {
-        super(handler);
-    }
-
-    @Override
-    public void initialize() {
-        ApiBridgeHandler accountHandler = handler.getAccountHandler();
-        if (accountHandler != null) {
-            webhook = accountHandler.getWebHookServlet();
-            webhook.ifPresent(servlet -> servlet.registerDataListener(handler.getId(), this));
-        }
-    }
-
-    @Override
-    public void dispose() {
-        webhook.ifPresent(servlet -> servlet.unregisterDataListener(handler.getId()));
-    }
-}
index 0b808fab93bfb4e22d5212cb272b6635b5c89746..88c1aa2c51abcd22ec246ffdbbfdd60ec8f5c2a2 100644 (file)
@@ -16,10 +16,12 @@ import java.util.List;
 import java.util.Optional;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.netatmo.internal.handler.ApiBridgeHandler;
 import org.openhab.binding.netatmo.internal.handler.CommonInterface;
 import org.openhab.binding.netatmo.internal.handler.channelhelper.ChannelHelper;
 import org.openhab.binding.netatmo.internal.handler.channelhelper.EventChannelHelper;
 import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider;
+import org.openhab.binding.netatmo.internal.servlet.WebhookServlet;
 
 /**
  * {@link HomeSecurityThingCapability} is the ancestor of capabilities hosted by a security home
@@ -33,6 +35,7 @@ public class HomeSecurityThingCapability extends Capability {
     protected final NetatmoDescriptionProvider descriptionProvider;
     protected final EventChannelHelper eventHelper;
 
+    private Optional<WebhookServlet> webhook = Optional.empty();
     protected Optional<SecurityCapability> securityCapability = Optional.empty();
     protected Optional<HomeCapability> homeCapability = Optional.empty();
 
@@ -51,5 +54,16 @@ public class HomeSecurityThingCapability extends Capability {
         super.initialize();
         securityCapability = handler.getHomeCapability(SecurityCapability.class);
         homeCapability = handler.getHomeCapability(HomeCapability.class);
+        ApiBridgeHandler accountHandler = handler.getAccountHandler();
+        if (accountHandler != null) {
+            webhook = accountHandler.getWebHookServlet();
+            webhook.ifPresent(servlet -> servlet.registerDataListener(handler.getId(), this));
+        }
+    }
+
+    @Override
+    public void dispose() {
+        webhook.ifPresent(servlet -> servlet.unregisterDataListener(handler.getId()));
+        super.dispose();
     }
 }
index 378081e296062def2e764b92842585b0f775e368..5720388d9bebf865af512be6fb8b8b518020b333 100644 (file)
 package org.openhab.binding.netatmo.internal.servlet;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
-import java.util.Scanner;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.servlet.http.HttpServletRequest;
@@ -35,7 +31,7 @@ import org.openhab.binding.netatmo.internal.api.SecurityApi;
 import org.openhab.binding.netatmo.internal.api.dto.WebhookEvent;
 import org.openhab.binding.netatmo.internal.deserialization.NADeserializer;
 import org.openhab.binding.netatmo.internal.handler.ApiBridgeHandler;
-import org.openhab.binding.netatmo.internal.handler.capability.EventCapability;
+import org.openhab.binding.netatmo.internal.handler.capability.Capability;
 import org.osgi.service.http.HttpService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,7 +45,7 @@ import org.slf4j.LoggerFactory;
 public class WebhookServlet extends NetatmoServlet {
     private static final long serialVersionUID = -354583910860541214L;
 
-    private final Map<String, EventCapability> dataListeners = new ConcurrentHashMap<>();
+    private final Map<String, Capability> dataListeners = new ConcurrentHashMap<>();
     private final Logger logger = LoggerFactory.getLogger(WebhookServlet.class);
     private final SecurityApi securityApi;
     private final NADeserializer deserializer;
@@ -96,7 +92,7 @@ public class WebhookServlet extends NetatmoServlet {
     @Override
     protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         replyQuick(resp);
-        processEvent(inputStreamToString(req.getInputStream()));
+        processEvent(new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8));
     }
 
     private void processEvent(String data) throws IOException {
@@ -104,10 +100,7 @@ public class WebhookServlet extends NetatmoServlet {
             logger.debug("Event transmitted from restService : {}", data);
             try {
                 WebhookEvent event = deserializer.deserialize(WebhookEvent.class, data);
-                List<String> toBeNotified = new ArrayList<>();
-                toBeNotified.add(event.getCameraId());
-                toBeNotified.addAll(event.getPersons().keySet());
-                notifyListeners(toBeNotified, event);
+                notifyListeners(event);
             } catch (NetatmoException e) {
                 logger.debug("Error deserializing webhook data received : {}. {}", data, e.getMessage());
             }
@@ -119,31 +112,22 @@ public class WebhookServlet extends NetatmoServlet {
         resp.setContentType(MediaType.APPLICATION_JSON);
         resp.setHeader("Access-Control-Allow-Origin", "*");
         resp.setHeader("Access-Control-Allow-Methods", HttpMethod.POST);
-        resp.setIntHeader("Access-Control-Max-Age", 3600);
         resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
+        resp.setIntHeader("Access-Control-Max-Age", 3600);
         resp.getWriter().write("");
     }
 
-    private String inputStreamToString(InputStream is) throws IOException {
-        String value = "";
-        try (Scanner scanner = new Scanner(is)) {
-            scanner.useDelimiter("\\A");
-            value = scanner.hasNext() ? scanner.next() : "";
-        }
-        return value;
-    }
-
-    private void notifyListeners(List<String> tobeNotified, WebhookEvent event) {
-        tobeNotified.forEach(id -> {
-            EventCapability module = dataListeners.get(id);
+    private void notifyListeners(WebhookEvent event) {
+        event.getNAObjectList().forEach(id -> {
+            Capability module = dataListeners.get(id);
             if (module != null) {
                 module.setNewData(event);
             }
         });
     }
 
-    public void registerDataListener(String id, EventCapability eventCapability) {
-        dataListeners.put(id, eventCapability);
+    public void registerDataListener(String id, Capability capability) {
+        dataListeners.put(id, capability);
     }
 
     public void unregisterDataListener(String id) {
index 10f66999d957a78ff6579e5347fa29dabea595a0..518b1a8b4fce434f2065d3e327a1e6dd686aff6a 100644 (file)
@@ -100,6 +100,6 @@ public class ChannelTypeUtils {
                 return picture;
             }
         }
-        return UnDefType.UNDEF;
+        return UnDefType.NULL;
     }
 }
index 38efb1219df0fe156e9b18f3457606f0c0a4035f..c0f5a687799677a84632b5847b240e6e69c0c73c 100644 (file)
@@ -206,6 +206,7 @@ channel-type.netatmo.event-type.state.option.SD = SD card status changed
 channel-type.netatmo.event-type.state.option.ALIM = Power status changed
 channel-type.netatmo.event-type.state.option.ACCEPTED_CALL = Call is incoming
 channel-type.netatmo.event-type.state.option.INCOMING_CALL = Call has been answered by a user
+channel-type.netatmo.event-type.state.option.RTC = Button pressed
 channel-type.netatmo.event-type.state.option.MISSED_CALL = Call has not been answered by anyone
 channel-type.netatmo.event-type.state.option.HUSH = Smoke detector status
 channel-type.netatmo.event-type.state.option.SMOKE = Smoke detection
index 09e33b0a3646713bb6e471406d435ede8e898f5a..5b60f29de173fedf7e647c967a48fdf8adbf5023 100644 (file)
                                <option value="ALIM">Power status changed</option>
                                <option value="ACCEPTED_CALL">Call is incoming</option>
                                <option value="INCOMING_CALL">Call has been answered by a user</option>
+                               <option value="RTC">Button pressed</option>
                                <option value="MISSED_CALL">Call has not been answered by anyone</option>
                                <option value="HUSH">Smoke detector status</option>
                                <option value="SMOKE">Smoke detection</option>
                                <option value="ALIM"/>
                                <option value="ACCEPTED_CALL"/>
                                <option value="INCOMING_CALL"/>
+                               <option value="RTC"/>
                                <option value="MISSED_CALL"/>
                        </options>
                </event>