[netatmo] Adding Webhook event support for Doorbell (#12972)

* Adding Webhook event support for Doorbell
* Adding doorbell rtc.
* Enhancing NAPushType deserialization
* Setting empty fields to NULL

Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2022-06-24 13:52:02 +02:00 committed by GitHub
parent 8ee86d786a
commit a1a02f05bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 183 additions and 116 deletions

View 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) {

View 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) {

View 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());
}
}

View 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;
}
}

View 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);
}
}
}

View 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;

View 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);
}
throw new JsonParseException("Error deserializing : " + string);
if (moduleType.equals(ModuleType.UNKNOWN) || eventType.equals(EventType.UNKNOWN)) {
logger.warn("Unknown module or event type : {}, deserialized to '{}-{}'", string, moduleType, eventType);
}
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);
}
}

View 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
}

View File

@ -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));
}
}

View File

@ -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()));
}
}

View 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();
}
}

View File

@ -13,13 +13,9 @@
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) {

View File

@ -100,6 +100,6 @@ public class ChannelTypeUtils {
return picture;
}
}
return UnDefType.UNDEF;
return UnDefType.NULL;
}
}

View 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

View File

@ -356,6 +356,7 @@
<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>
@ -433,6 +434,7 @@
<option value="ALIM"/>
<option value="ACCEPTED_CALL"/>
<option value="INCOMING_CALL"/>
<option value="RTC"/>
<option value="MISSED_CALL"/>
</options>
</event>