Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[netatmo] Support for Welcome camera event categories #8203

Merged
merged 23 commits into from
Jul 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
78699f9
Merge pull request #1 from openhab/2.5.x
Novanic Jun 10, 2020
ae6b891
Merge pull request #2 from openhab/2.5.x
Novanic Jun 12, 2020
66abc11
Merge pull request #3 from openhab/2.5.x
Novanic Jun 13, 2020
4f2cd51
Merge pull request #4 from openhab/2.5.x
Novanic Jun 16, 2020
bd9cfc1
Merge pull request #5 from openhab/2.5.x
Novanic Jun 18, 2020
de62002
Merge pull request #6 from openhab/2.5.x
Novanic Jun 20, 2020
ad272b7
Merge pull request #7 from openhab/2.5.x
Novanic Jun 20, 2020
76c47b8
Merge pull request #8 from openhab/2.5.x
Novanic Jun 21, 2020
2ce668b
Merge pull request #9 from openhab/2.5.x
Novanic Jul 6, 2020
bcfbee2
Merge pull request #10 from openhab/2.5.x
Novanic Jul 10, 2020
2d654b9
Merge pull request #11 from openhab/2.5.x
Novanic Jul 24, 2020
d494fac
Tests added
sstrohschein Jul 26, 2020
c309e9e
Bug-fix: A detected person was reported as a human AND as a movement.…
sstrohschein Jul 26, 2020
b234651
Support for Welcome camera event categories (the Welcome indoor camer…
sstrohschein Jul 26, 2020
2484e18
Tests added
sstrohschein Jul 26, 2020
7bd1be8
Test code optimized
sstrohschein Jul 26, 2020
793d810
Code optimization
sstrohschein Jul 26, 2020
027e687
Code optimization
sstrohschein Jul 26, 2020
ff874d7
Test code optimized
sstrohschein Jul 26, 2020
4ae43e3
Spotless applied
sstrohschein Jul 26, 2020
b698a3b
Enum used to check the category
sstrohschein Jul 28, 2020
d84849d
Enum for the new element "category" used, according to https://forum.…
sstrohschein Jul 28, 2020
747fd28
Enum for the new element "category" used, according to https://forum.…
sstrohschein Jul 29, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}
}