diff --git a/bundles/org.openhab.binding.netatmo/pom.xml b/bundles/org.openhab.binding.netatmo/pom.xml index 4fc4bc8631a21..8ddf1091ecd88 100644 --- a/bundles/org.openhab.binding.netatmo/pom.xml +++ b/bundles/org.openhab.binding.netatmo/pom.xml @@ -1,4 +1,6 @@ - + + 4.0.0 @@ -67,7 +69,7 @@ generate - https://raw.githubusercontent.com/cbornet/netatmo-swagger-decl/8ba3583f8d851e0b0c0bb4c5066338fe4898cb11/spec/swagger.yaml + https://raw.githubusercontent.com/cbornet/netatmo-swagger-decl/35e27745fb0d432bc6c8b5ec7a83ed2a09944cea/spec/swagger.yaml java retrofit diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/welcome/NAWelcomeHomeHandler.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/welcome/NAWelcomeHomeHandler.java index 0986213b88eb8..d25e3f41b350f 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/welcome/NAWelcomeHomeHandler.java +++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/welcome/NAWelcomeHomeHandler.java @@ -71,8 +71,7 @@ protected Optional updateReadings() { Optional 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)); @@ -181,12 +180,17 @@ private static Set findDetectedObjectTypes(Optional 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 detectedCategory = findDetectedCategory(event); + if (detectedCategory.isPresent()) { + detectedObjectTypes.add(detectedCategory.get().name()); + } else { + detectedObjectTypes.add(NAWebhookCameraEvent.EventTypeEnum.MOVEMENT.name()); + } + } } event.getEventList().forEach(subEvent -> { @@ -196,6 +200,15 @@ private static Set findDetectedObjectTypes(Optional even return detectedObjectTypes; } + private static Optional 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 findEventMessage() { Optional lastEvt = getLastEvent(); if (lastEvt.isPresent()) { diff --git a/bundles/org.openhab.binding.netatmo/src/test/java/org/openhab/binding/netatmo/internal/welcome/NAWelcomeHomeHandlerTest.java b/bundles/org.openhab.binding.netatmo/src/test/java/org/openhab/binding/netatmo/internal/welcome/NAWelcomeHomeHandlerTest.java index f6bc93e963857..8f0a6c80491c0 100644 --- a/bundles/org.openhab.binding.netatmo/src/test/java/org/openhab/binding/netatmo/internal/welcome/NAWelcomeHomeHandlerTest.java +++ b/bundles/org.openhab.binding.netatmo/src/test/java/org/openhab/binding/netatmo/internal/welcome/NAWelcomeHomeHandlerTest.java @@ -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; @@ -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); } @@ -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 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) { @@ -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); @@ -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; + } } }