Skip to content

Commit

Permalink
[netatmo] Support for Welcome camera event categories (openhab#8203)
Browse files Browse the repository at this point in the history
Signed-off-by: Sven Strohschein <[email protected]>
  • Loading branch information
Novanic authored and andrewfg committed Aug 31, 2020
1 parent 585410b commit e527c18
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 22 deletions.
6 changes: 4 additions & 2 deletions bundles/org.openhab.binding.netatmo/pom.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

Expand Down Expand Up @@ -67,7 +69,7 @@
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>https://raw.githubusercontent.com/cbornet/netatmo-swagger-decl/8ba3583f8d851e0b0c0bb4c5066338fe4898cb11/spec/swagger.yaml</inputSpec>
<inputSpec>https://raw.githubusercontent.com/cbornet/netatmo-swagger-decl/35e27745fb0d432bc6c8b5ec7a83ed2a09944cea/spec/swagger.yaml</inputSpec>
<language>java</language>
<library>retrofit</library>
<configOptions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ protected Optional<NAWelcomeHome> updateReadings() {
Optional<NAWelcomeHome> result = getBridgeHandler().flatMap(handler -> handler.getWelcomeDataBody(getId()))
.map(dataBody -> dataBody.getHomes().stream().filter(device -> device.getId().equalsIgnoreCase(getId()))
.findFirst().orElse(null));
// data time stamp is updated to now as WelcomeDataBody does not provide any information according to this
// need
// data time stamp is updated to now as WelcomeDataBody does not provide any information according to this need
dataTimeStamp = (int) (Calendar.getInstance().getTimeInMillis() / 1000);
result.ifPresent(home -> {
home.getCameras().forEach(camera -> childs.put(camera.getId(), camera));
Expand Down Expand Up @@ -181,12 +180,17 @@ private static Set<String> findDetectedObjectTypes(Optional<NAWelcomeEvent> even

NAWelcomeEvent event = eventOptional.get();

if (event.getPersonId() != null) {
detectedObjectTypes.add(NAWelcomeSubEvent.TypeEnum.HUMAN.name());
}

if (NAWebhookCameraEvent.EventTypeEnum.MOVEMENT.toString().equals(event.getType())) {
detectedObjectTypes.add(NAWebhookCameraEvent.EventTypeEnum.MOVEMENT.name());
if (event.getPersonId() != null) {
detectedObjectTypes.add(NAWelcomeSubEvent.TypeEnum.HUMAN.name());
} else {
Optional<NAWelcomeSubEvent.TypeEnum> detectedCategory = findDetectedCategory(event);
if (detectedCategory.isPresent()) {
detectedObjectTypes.add(detectedCategory.get().name());
} else {
detectedObjectTypes.add(NAWebhookCameraEvent.EventTypeEnum.MOVEMENT.name());
}
}
}

event.getEventList().forEach(subEvent -> {
Expand All @@ -196,6 +200,15 @@ private static Set<String> findDetectedObjectTypes(Optional<NAWelcomeEvent> even
return detectedObjectTypes;
}

private static Optional<NAWelcomeSubEvent.TypeEnum> findDetectedCategory(NAWelcomeEvent event) {
NAWelcomeEvent.CategoryEnum category = event.getCategory();
if (category != null) {
// It is safe to convert the enum, both enums have the same values.
return Optional.of(NAWelcomeSubEvent.TypeEnum.valueOf(category.name()));
}
return Optional.empty();
}

private Optional<String> findEventMessage() {
Optional<NAWelcomeEvent> lastEvt = getLastEvent();
if (lastEvt.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@
*/
package org.openhab.binding.netatmo.internal.welcome;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
import java.util.*;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.smarthome.core.i18n.TimeZoneProvider;
Expand Down Expand Up @@ -51,14 +50,13 @@ public class NAWelcomeHomeHandlerTest {

@Mock
private TimeZoneProvider timeZoneProviderMock;
private Thing welcomeHomeThing;
private NAWelcomeHomeHandlerAccessible handler;
@Mock
private NetatmoBridgeHandler bridgeHandlerMock;

@Before
public void before() {
welcomeHomeThing = new ThingImpl(new ThingTypeUID("netatmo", "NAWelcomeHome"), "1");
Thing welcomeHomeThing = new ThingImpl(new ThingTypeUID("netatmo", "NAWelcomeHome"), "1");
handler = new NAWelcomeHomeHandlerAccessible(welcomeHomeThing);
}

Expand Down Expand Up @@ -155,42 +153,165 @@ public void testTriggerChannelIfRequired() {

when(bridgeHandlerMock.getWelcomeDataBody(DUMMY_HOME_ID)).thenReturn(Optional.of(homeData));

handler.updateReadings();
handler.triggerChannelIfRequired(NetatmoBindingConstants.CHANNEL_CAMERA_EVENT);
triggerCameraEvents();

// No triggered event is expected, because the binding is just started (with existing events).
assertEquals(0, handler.getTriggerChannelCount());

home.setEvents(Arrays.asList(event1, event2));

handler.updateReadings();
handler.triggerChannelIfRequired(NetatmoBindingConstants.CHANNEL_CAMERA_EVENT);
triggerCameraEvents();

// 1 triggered event is expected, because there is 1 new event since binding start (outdoor / detected human).
assertEquals(1, handler.getTriggerChannelCount());
assertEquals(new StringType("outdoor"),
handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
assertEquals("HUMAN", handler.getLastDetectedObject());

home.setEvents(Arrays.asList(event1, event2));

handler.updateReadings();
handler.triggerChannelIfRequired(NetatmoBindingConstants.CHANNEL_CAMERA_EVENT);
triggerCameraEvents();

// No new triggered event is expected, because there are still the same events as before the refresh.
assertEquals(1, handler.getTriggerChannelCount());
assertEquals(new StringType("outdoor"),
handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
assertEquals("HUMAN", handler.getLastDetectedObject());

home.setEvents(Arrays.asList(event1, event2, event3));

handler.updateReadings();
handler.triggerChannelIfRequired(NetatmoBindingConstants.CHANNEL_CAMERA_EVENT);
triggerCameraEvents();

// 1 new triggered event is expected (2 in sum), because there is 1 new event since the last triggered event
// (movement after outdoor / detected human).
assertEquals(2, handler.getTriggerChannelCount());
assertEquals(new StringType("movement"),
handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
assertEquals("MOVEMENT", handler.getLastDetectedObject());
}

@Test
public void testTriggerChannelIfRequiredNoEventAvailable() {
NAWelcomeHome home = new NAWelcomeHome();
home.setId(DUMMY_HOME_ID);

NAWelcomeHomeData homeData = new NAWelcomeHomeData();
homeData.setHomes(Collections.singletonList(home));

when(bridgeHandlerMock.getWelcomeDataBody(DUMMY_HOME_ID)).thenReturn(Optional.of(homeData));

triggerCameraEvents();

// No triggered event is expected, because there aren't any events (the collection is NULL)
assertEquals(0, handler.getTriggerChannelCount());

home.setEvents(Collections.emptyList());

triggerCameraEvents();

// No triggered event is expected, because there aren't any events (the collection is empty)
assertEquals(0, handler.getTriggerChannelCount());
}

@Test
public void testTriggerChannelIfRequiredPersonMovement() {
NAWelcomeHome home = initHome();

NAWelcomeEvent event = createEvent(1592661882, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
event.setPersonId("1");

home.getEvents().add(event);

triggerCameraEvents();

assertEquals(1, handler.getTriggerChannelCount());
assertEquals(new StringType("movement"),
handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
assertEquals("HUMAN", handler.getLastDetectedObject());
}

@Test
public void testTriggerChannelIfRequiredHumanMovement() {
NAWelcomeHome home = initHome();

NAWelcomeEvent event = createEvent(1592661882, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
event.setCategory(NAWelcomeEvent.CategoryEnum.HUMAN);

home.getEvents().add(event);

triggerCameraEvents();

assertEquals(1, handler.getTriggerChannelCount());
assertEquals(new StringType("movement"),
handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
assertEquals("HUMAN", handler.getLastDetectedObject());
}

@Test
public void testTriggerChannelIfRequiredAnimalMovement() {
NAWelcomeHome home = initHome();

NAWelcomeEvent event = createEvent(1592661882, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
event.setCategory(NAWelcomeEvent.CategoryEnum.ANIMAL);

home.getEvents().add(event);

triggerCameraEvents();

assertEquals(1, handler.getTriggerChannelCount());
assertEquals(new StringType("movement"),
handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
assertEquals("ANIMAL", handler.getLastDetectedObject());
}

@Test
public void testTriggerChannelIfRequiredVehicleMovement() {
NAWelcomeHome home = initHome();

NAWelcomeEvent event = createEvent(1592661882, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
event.setCategory(NAWelcomeEvent.CategoryEnum.VEHICLE);

home.getEvents().add(event);

triggerCameraEvents();

assertEquals(1, handler.getTriggerChannelCount());
assertEquals(new StringType("movement"),
handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
assertEquals("VEHICLE", handler.getLastDetectedObject());
}

@Test
public void testMatchDetectedObjectEnums() {
assertArrayEquals(
"The detected object enums aren't equal anymore, that could lead to a bug! Please check the usages!",
Arrays.stream(NAWelcomeEvent.CategoryEnum.values()).map(Enum::name).toArray(),
Arrays.stream(NAWelcomeSubEvent.TypeEnum.values()).map(Enum::name).toArray());
}

private NAWelcomeHome initHome() {
NAWelcomeEvent initLastEvent = createEvent(1592661881, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);

NAWelcomeHome home = new NAWelcomeHome();
home.setId(DUMMY_HOME_ID);

List<NAWelcomeEvent> events = new ArrayList<>();
events.add(initLastEvent);
home.setEvents(events);

NAWelcomeHomeData homeData = new NAWelcomeHomeData();
homeData.setHomes(Collections.singletonList(home));

when(bridgeHandlerMock.getWelcomeDataBody(DUMMY_HOME_ID)).thenReturn(Optional.of(homeData));

triggerCameraEvents();

return home;
}

private void triggerCameraEvents() {
handler.updateReadings();
handler.triggerChannelIfRequired(NetatmoBindingConstants.CHANNEL_CAMERA_EVENT);
}

private static NAWelcomeEvent createPresenceEvent(int eventTime, NAWelcomeSubEvent.TypeEnum detectedObjectType) {
Expand All @@ -213,6 +334,7 @@ private static NAWelcomeEvent createEvent(int eventTime, NAWebhookCameraEvent.Ev
private class NAWelcomeHomeHandlerAccessible extends NAWelcomeHomeHandler {

private int triggerChannelCount;
private String lastDetectedObject;

private NAWelcomeHomeHandlerAccessible(Thing thing) {
super(thing, timeZoneProviderMock);
Expand All @@ -231,11 +353,16 @@ protected String getId() {
@Override
protected void triggerChannel(@NonNull String channelID, @NonNull String event) {
triggerChannelCount++;
lastDetectedObject = event;
super.triggerChannel(channelID, event);
}

private int getTriggerChannelCount() {
return triggerChannelCount;
}

public String getLastDetectedObject() {
return lastDetectedObject;
}
}
}

0 comments on commit e527c18

Please sign in to comment.