2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.netatmo.internal.welcome;
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.ArgumentMatchers.any;
17 import static org.mockito.Mockito.when;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Optional;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.junit.jupiter.api.BeforeEach;
27 import org.junit.jupiter.api.Test;
28 import org.junit.jupiter.api.extension.ExtendWith;
29 import org.mockito.Mock;
30 import org.mockito.junit.jupiter.MockitoExtension;
31 import org.mockito.junit.jupiter.MockitoSettings;
32 import org.mockito.quality.Strictness;
33 import org.openhab.binding.netatmo.internal.NetatmoBindingConstants;
34 import org.openhab.binding.netatmo.internal.handler.NetatmoBridgeHandler;
35 import org.openhab.binding.netatmo.internal.webhook.NAWebhookCameraEvent;
36 import org.openhab.core.i18n.TimeZoneProvider;
37 import org.openhab.core.library.types.StringType;
38 import org.openhab.core.thing.Thing;
39 import org.openhab.core.thing.ThingTypeUID;
40 import org.openhab.core.thing.internal.ThingImpl;
41 import org.openhab.core.types.UnDefType;
43 import io.swagger.client.model.NAWelcomeEvent;
44 import io.swagger.client.model.NAWelcomeHome;
45 import io.swagger.client.model.NAWelcomeHomeData;
46 import io.swagger.client.model.NAWelcomeSubEvent;
49 * @author Sven Strohschein - Initial contribution
51 @ExtendWith(MockitoExtension.class)
52 @MockitoSettings(strictness = Strictness.WARN)
53 public class NAWelcomeHomeHandlerTest {
55 private static final String DUMMY_HOME_ID = "1";
57 private NAWelcomeHomeHandlerAccessible handler;
59 private @Mock NetatmoBridgeHandler bridgeHandlerMock;
60 private @Mock TimeZoneProvider timeZoneProviderMock;
63 public void before() {
64 Thing welcomeHomeThing = new ThingImpl(new ThingTypeUID("netatmo", "NAWelcomeHome"), "1");
65 handler = new NAWelcomeHomeHandlerAccessible(welcomeHomeThing);
69 public void testUpdateReadingsWithEvents() {
70 NAWelcomeEvent event1 = createEvent(1592661881, NAWebhookCameraEvent.EventTypeEnum.PERSON);
71 NAWelcomeEvent event2 = createEvent(1592661882, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
73 NAWelcomeHome home = new NAWelcomeHome();
74 home.setId(DUMMY_HOME_ID);
75 home.setEvents(Arrays.asList(event1, event2));
77 NAWelcomeHomeData homeData = new NAWelcomeHomeData();
78 homeData.setHomes(Collections.singletonList(home));
80 when(bridgeHandlerMock.getWelcomeDataBody(DUMMY_HOME_ID)).thenReturn(Optional.of(homeData));
82 handler.updateReadings();
84 // the second (last) event is expected
85 assertEquals(new StringType("movement"),
86 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
88 home.setEvents(Arrays.asList(event2, event1));
89 // the second (last) event is still expected (independent from the order of these are added)
90 assertEquals(new StringType("movement"),
91 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
95 public void testUpdateReadingsWith1Event() {
96 NAWelcomeEvent event = createEvent(1592661881, NAWebhookCameraEvent.EventTypeEnum.PERSON);
98 NAWelcomeHome home = new NAWelcomeHome();
99 home.setId(DUMMY_HOME_ID);
100 home.setEvents(Collections.singletonList(event));
102 NAWelcomeHomeData homeData = new NAWelcomeHomeData();
103 homeData.setHomes(Collections.singletonList(home));
105 when(bridgeHandlerMock.getWelcomeDataBody(DUMMY_HOME_ID)).thenReturn(Optional.of(homeData));
107 handler.updateReadings();
109 assertEquals(new StringType("person"),
110 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
114 public void testUpdateReadingsNoEvents() {
115 NAWelcomeHome home = new NAWelcomeHome();
116 home.setId(DUMMY_HOME_ID);
118 NAWelcomeHomeData homeData = new NAWelcomeHomeData();
119 homeData.setHomes(Collections.singletonList(home));
121 when(bridgeHandlerMock.getWelcomeDataBody(DUMMY_HOME_ID)).thenReturn(Optional.of(homeData));
123 handler.updateReadings();
125 assertEquals(UnDefType.UNDEF, handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
129 public void testUpdateReadingsEmptyHomeData() {
130 NAWelcomeHomeData homeData = new NAWelcomeHomeData();
132 when(bridgeHandlerMock.getWelcomeDataBody(any())).thenReturn(Optional.of(homeData));
134 handler.updateReadings();
136 assertEquals(UnDefType.UNDEF, handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
140 public void testUpdateReadingsNoHomeData() {
141 handler.updateReadings();
143 assertEquals(UnDefType.UNDEF, handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
147 public void testTriggerChannelIfRequired() {
148 NAWelcomeEvent event1 = createPresenceEvent(1592661881, NAWelcomeSubEvent.TypeEnum.ANIMAL);
149 NAWelcomeEvent event2 = createPresenceEvent(1592661882, NAWelcomeSubEvent.TypeEnum.HUMAN);
150 NAWelcomeEvent event3 = createEvent(1592661883, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
152 NAWelcomeHome home = new NAWelcomeHome();
153 home.setId(DUMMY_HOME_ID);
154 home.setEvents(Collections.singletonList(event1));
156 NAWelcomeHomeData homeData = new NAWelcomeHomeData();
157 homeData.setHomes(Collections.singletonList(home));
159 when(bridgeHandlerMock.getWelcomeDataBody(DUMMY_HOME_ID)).thenReturn(Optional.of(homeData));
161 triggerCameraEvents();
163 // No triggered event is expected, because the binding is just started (with existing events).
164 assertEquals(0, handler.getTriggerChannelCount());
166 home.setEvents(Arrays.asList(event1, event2));
168 triggerCameraEvents();
170 // 1 triggered event is expected, because there is 1 new event since binding start (outdoor / detected human).
171 assertEquals(1, handler.getTriggerChannelCount());
172 assertEquals(new StringType("outdoor"),
173 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
174 assertEquals("HUMAN", handler.getLastDetectedObject());
176 home.setEvents(Arrays.asList(event1, event2));
178 triggerCameraEvents();
180 // No new triggered event is expected, because there are still the same events as before the refresh.
181 assertEquals(1, handler.getTriggerChannelCount());
182 assertEquals(new StringType("outdoor"),
183 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
184 assertEquals("HUMAN", handler.getLastDetectedObject());
186 home.setEvents(Arrays.asList(event1, event2, event3));
188 triggerCameraEvents();
190 // 1 new triggered event is expected (2 in sum), because there is 1 new event since the last triggered event
191 // (movement after outdoor / detected human).
192 assertEquals(2, handler.getTriggerChannelCount());
193 assertEquals(new StringType("movement"),
194 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
195 assertEquals("MOVEMENT", handler.getLastDetectedObject());
199 public void testTriggerChannelIfRequiredNoEventAvailable() {
200 NAWelcomeHome home = new NAWelcomeHome();
201 home.setId(DUMMY_HOME_ID);
203 NAWelcomeHomeData homeData = new NAWelcomeHomeData();
204 homeData.setHomes(Collections.singletonList(home));
206 when(bridgeHandlerMock.getWelcomeDataBody(DUMMY_HOME_ID)).thenReturn(Optional.of(homeData));
208 triggerCameraEvents();
210 // No triggered event is expected, because there aren't any events (the collection is NULL)
211 assertEquals(0, handler.getTriggerChannelCount());
213 home.setEvents(Collections.emptyList());
215 triggerCameraEvents();
217 // No triggered event is expected, because there aren't any events (the collection is empty)
218 assertEquals(0, handler.getTriggerChannelCount());
222 public void testTriggerChannelIfRequiredPersonMovement() {
223 NAWelcomeHome home = initHome();
225 NAWelcomeEvent event = createEvent(1592661882, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
226 event.setPersonId("1");
228 home.getEvents().add(event);
230 triggerCameraEvents();
232 assertEquals(1, handler.getTriggerChannelCount());
233 assertEquals(new StringType("movement"),
234 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
235 assertEquals("HUMAN", handler.getLastDetectedObject());
239 public void testTriggerChannelIfRequiredHumanMovement() {
240 NAWelcomeHome home = initHome();
242 NAWelcomeEvent event = createEvent(1592661882, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
243 event.setCategory(NAWelcomeEvent.CategoryEnum.HUMAN);
245 home.getEvents().add(event);
247 triggerCameraEvents();
249 assertEquals(1, handler.getTriggerChannelCount());
250 assertEquals(new StringType("movement"),
251 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
252 assertEquals("HUMAN", handler.getLastDetectedObject());
256 public void testTriggerChannelIfRequiredAnimalMovement() {
257 NAWelcomeHome home = initHome();
259 NAWelcomeEvent event = createEvent(1592661882, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
260 event.setCategory(NAWelcomeEvent.CategoryEnum.ANIMAL);
262 home.getEvents().add(event);
264 triggerCameraEvents();
266 assertEquals(1, handler.getTriggerChannelCount());
267 assertEquals(new StringType("movement"),
268 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
269 assertEquals("ANIMAL", handler.getLastDetectedObject());
273 public void testTriggerChannelIfRequiredVehicleMovement() {
274 NAWelcomeHome home = initHome();
276 NAWelcomeEvent event = createEvent(1592661882, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
277 event.setCategory(NAWelcomeEvent.CategoryEnum.VEHICLE);
279 home.getEvents().add(event);
281 triggerCameraEvents();
283 assertEquals(1, handler.getTriggerChannelCount());
284 assertEquals(new StringType("movement"),
285 handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_WELCOME_EVENT_TYPE));
286 assertEquals("VEHICLE", handler.getLastDetectedObject());
290 public void testMatchDetectedObjectEnums() {
291 assertArrayEquals(Arrays.stream(NAWelcomeEvent.CategoryEnum.values()).map(Enum::name).toArray(),
292 Arrays.stream(NAWelcomeSubEvent.TypeEnum.values()).map(Enum::name).toArray(),
293 "The detected object enums aren't equal anymore, that could lead to a bug! Please check the usages!");
296 private NAWelcomeHome initHome() {
297 NAWelcomeEvent initLastEvent = createEvent(1592661881, NAWebhookCameraEvent.EventTypeEnum.MOVEMENT);
299 NAWelcomeHome home = new NAWelcomeHome();
300 home.setId(DUMMY_HOME_ID);
302 List<NAWelcomeEvent> events = new ArrayList<>();
303 events.add(initLastEvent);
304 home.setEvents(events);
306 NAWelcomeHomeData homeData = new NAWelcomeHomeData();
307 homeData.setHomes(Collections.singletonList(home));
309 when(bridgeHandlerMock.getWelcomeDataBody(DUMMY_HOME_ID)).thenReturn(Optional.of(homeData));
311 triggerCameraEvents();
316 private void triggerCameraEvents() {
317 handler.updateReadings();
318 handler.triggerChannelIfRequired(NetatmoBindingConstants.CHANNEL_CAMERA_EVENT);
321 private static NAWelcomeEvent createPresenceEvent(int eventTime, NAWelcomeSubEvent.TypeEnum detectedObjectType) {
322 NAWelcomeSubEvent subEvent = new NAWelcomeSubEvent();
323 subEvent.setTime(eventTime);
324 subEvent.setType(detectedObjectType);
326 NAWelcomeEvent event = createEvent(eventTime, NAWebhookCameraEvent.EventTypeEnum.OUTDOOR);
327 event.setEventList(Collections.singletonList(subEvent));
331 private static NAWelcomeEvent createEvent(int eventTime, NAWebhookCameraEvent.EventTypeEnum eventType) {
332 NAWelcomeEvent event = new NAWelcomeEvent();
333 event.setType(eventType.toString());
334 event.setTime(eventTime);
338 private class NAWelcomeHomeHandlerAccessible extends NAWelcomeHomeHandler {
340 private int triggerChannelCount;
341 private String lastDetectedObject;
343 private NAWelcomeHomeHandlerAccessible(Thing thing) {
344 super(thing, timeZoneProviderMock);
348 protected Optional<NetatmoBridgeHandler> getBridgeHandler() {
349 return Optional.of(bridgeHandlerMock);
353 protected String getId() {
354 return DUMMY_HOME_ID;
358 protected void triggerChannel(@NonNull String channelID, @NonNull String event) {
359 triggerChannelCount++;
360 lastDetectedObject = event;
361 super.triggerChannel(channelID, event);
364 private int getTriggerChannelCount() {
365 return triggerChannelCount;
368 public String getLastDetectedObject() {
369 return lastDetectedObject;