Skip to content

Commit

Permalink
[netatmo] Adding Webhook event support for Doorbell (openhab#12972)
Browse files Browse the repository at this point in the history
* Adding Webhook event support for Doorbell
* Adding doorbell rtc.
* Enhancing NAPushType deserialization
* Setting empty fields to NULL

Signed-off-by: clinique <gael@lhopital.org>
  • Loading branch information
clinique authored and andan67 committed Nov 5, 2022
1 parent ab78a25 commit 454cbdf
Show file tree
Hide file tree
Showing 15 changed files with 183 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
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;
Expand Down Expand Up @@ -122,14 +122,14 @@ private BaseThingHandler buildHandler(Thing thing, ModuleType moduleType) {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),

Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
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;
Expand Down Expand Up @@ -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)),

Expand Down Expand Up @@ -208,9 +206,4 @@ public static ModuleType from(ThingTypeUID thingTypeUID) {
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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,4 @@ public Optional<EventSubType> getSubTypeDescription() {
return Stream.of(EventSubType.values()).filter(v -> v.types.contains(getEventType()) && v.subType == subType)
.findFirst();
}

public void setEventType(EventType type) {
this.type = type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,9 +31,11 @@
*/
@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();
Expand Down Expand Up @@ -63,4 +67,24 @@ public ZonedDateTime getTime() {
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*/
@NonNullByDefault
public class NAPushType {
public final static NAPushType UNKNOWN = new NAPushType(ModuleType.UNKNOWN, EventType.UNKNOWN);

private final ModuleType moduleType;
private final EventType event;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
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
Expand All @@ -31,21 +32,41 @@
*/
@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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -137,6 +141,10 @@ protected void updateEvent(Event newData) {
// 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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}
}

This file was deleted.

Loading

0 comments on commit 454cbdf

Please sign in to comment.