2 * Copyright (c) 2010-2020 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.presence;
15 import static org.junit.Assert.*;
16 import static org.mockito.ArgumentMatchers.any;
17 import static org.mockito.Mockito.*;
19 import java.util.Optional;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.openhab.core.i18n.TimeZoneProvider;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.library.types.StringType;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.core.thing.Thing;
27 import org.openhab.core.thing.ThingTypeUID;
28 import org.openhab.core.thing.internal.ThingImpl;
29 import org.openhab.core.types.RefreshType;
30 import org.openhab.core.types.State;
31 import org.openhab.core.types.UnDefType;
32 import org.junit.Before;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.mockito.Mock;
36 import org.mockito.junit.MockitoJUnitRunner;
37 import org.openhab.binding.netatmo.internal.NetatmoBindingConstants;
39 import io.swagger.client.model.NAWelcomeCamera;
42 * @author Sven Strohschein - Initial contribution
44 @RunWith(MockitoJUnitRunner.class)
45 public class NAPresenceCameraHandlerTest {
47 private static final String DUMMY_VPN_URL = "https://dummytestvpnaddress.net/restricted/10.255.89.96/9826069dc689e8327ac3ed2ced4ff089/MTU5MTgzMzYwMDrQ7eHHhG0_OJ4TgmPhGlnK7QQ5pZ,,";
48 private static final String DUMMY_LOCAL_URL = "http://192.168.178.76/9826069dc689e8327ac3ed2ced4ff089";
49 private static final Optional<String> DUMMY_PING_RESPONSE = createPingResponseContent(DUMMY_LOCAL_URL);
52 private RequestExecutor requestExecutorMock;
54 private TimeZoneProvider timeZoneProviderMock;
56 private Thing presenceCameraThing;
57 private NAWelcomeCamera presenceCamera;
58 private ChannelUID cameraStatusChannelUID;
59 private ChannelUID floodlightChannelUID;
60 private ChannelUID floodlightAutoModeChannelUID;
61 private NAPresenceCameraHandlerAccessible handler;
64 public void before() {
65 presenceCameraThing = new ThingImpl(new ThingTypeUID("netatmo", "NOC"), "1");
66 presenceCamera = new NAWelcomeCamera();
68 cameraStatusChannelUID = new ChannelUID(presenceCameraThing.getUID(),
69 NetatmoBindingConstants.CHANNEL_CAMERA_STATUS);
70 floodlightChannelUID = new ChannelUID(presenceCameraThing.getUID(),
71 NetatmoBindingConstants.CHANNEL_CAMERA_FLOODLIGHT);
72 floodlightAutoModeChannelUID = new ChannelUID(presenceCameraThing.getUID(),
73 NetatmoBindingConstants.CHANNEL_CAMERA_FLOODLIGHT_AUTO_MODE);
75 handler = new NAPresenceCameraHandlerAccessible(presenceCameraThing, presenceCamera);
79 public void testHandleCommandSwitchSurveillanceOn() {
80 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
82 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
83 handler.handleCommand(cameraStatusChannelUID, OnOffType.ON);
85 verify(requestExecutorMock, times(2)).executeGETRequest(any()); // 1.) execute ping + 2.) execute switch on
86 verify(requestExecutorMock).executeGETRequest(DUMMY_LOCAL_URL + "/command/changestatus?status=on");
90 public void testHandleCommandSwitchSurveillanceOff() {
91 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
93 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
94 handler.handleCommand(cameraStatusChannelUID, OnOffType.OFF);
96 verify(requestExecutorMock, times(2)).executeGETRequest(any()); // 1.) execute ping + 2.) execute switch off
97 verify(requestExecutorMock).executeGETRequest(DUMMY_LOCAL_URL + "/command/changestatus?status=off");
101 public void testHandleCommandSwitchSurveillanceUnknownCommand() {
102 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
103 handler.handleCommand(cameraStatusChannelUID, RefreshType.REFRESH);
105 verify(requestExecutorMock, never()).executeGETRequest(any()); // nothing should get executed on a refresh
110 public void testHandleCommandSwitchSurveillanceWithoutVPN() {
111 handler.handleCommand(cameraStatusChannelUID, OnOffType.ON);
113 verify(requestExecutorMock, never()).executeGETRequest(any()); // nothing should get executed when no VPN
118 public void testHandleCommandSwitchFloodlightOn() {
119 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
121 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
122 handler.handleCommand(floodlightChannelUID, OnOffType.ON);
124 verify(requestExecutorMock, times(2)).executeGETRequest(any()); // 1.) execute ping + 2.) execute switch on
125 verify(requestExecutorMock)
126 .executeGETRequest(DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22on%22%7D");
130 public void testHandleCommandSwitchFloodlightOff() {
131 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
133 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
134 handler.handleCommand(floodlightChannelUID, OnOffType.OFF);
136 verify(requestExecutorMock, times(2)).executeGETRequest(any()); // 1.) execute ping + 2.) execute switch off
137 verify(requestExecutorMock).executeGETRequest(
138 DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22off%22%7D");
142 public void testHandleCommandSwitchFloodlightOffWithAutoModeOn() {
143 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
145 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
146 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.AUTO);
147 assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
149 handler.handleCommand(floodlightChannelUID, OnOffType.OFF);
151 verify(requestExecutorMock, times(2)).executeGETRequest(any()); // 1.) execute ping + 2.) execute switch off
152 verify(requestExecutorMock).executeGETRequest(
153 DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22auto%22%7D");
157 public void testHandleCommandSwitchFloodlightOnAddressChanged() {
158 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
160 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
161 handler.handleCommand(floodlightChannelUID, OnOffType.ON);
162 // 1.) execute ping + 2.) execute switch on
163 verify(requestExecutorMock, times(2)).executeGETRequest(any());
164 verify(requestExecutorMock)
165 .executeGETRequest(DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22on%22%7D");
167 handler.handleCommand(floodlightChannelUID, OnOffType.OFF);
168 // 1.) execute ping + 2.) execute switch on + 3.) execute switch off
169 verify(requestExecutorMock, times(3)).executeGETRequest(any());
170 verify(requestExecutorMock)
171 .executeGETRequest(DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22on%22%7D");
172 verify(requestExecutorMock).executeGETRequest(
173 DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22off%22%7D");
175 final String newDummyVPNURL = DUMMY_VPN_URL + "2";
176 final String newDummyLocalURL = DUMMY_LOCAL_URL + "2";
177 final Optional<String> newDummyPingResponse = createPingResponseContent(newDummyLocalURL);
179 when(requestExecutorMock.executeGETRequest(newDummyVPNURL + "/command/ping")).thenReturn(newDummyPingResponse);
181 presenceCamera.setVpnUrl(newDummyVPNURL);
182 handler.handleCommand(floodlightChannelUID, OnOffType.ON);
183 // 1.) execute ping + 2.) execute switch on + 3.) execute switch off + 4.) execute ping + 5.) execute switch on
184 verify(requestExecutorMock, times(5)).executeGETRequest(any());
185 verify(requestExecutorMock)
186 .executeGETRequest(DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22on%22%7D");
187 verify(requestExecutorMock).executeGETRequest(
188 DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22off%22%7D");
189 verify(requestExecutorMock).executeGETRequest(
190 newDummyLocalURL + "/command/floodlight_set_config?config=%7B%22mode%22:%22on%22%7D");
194 public void testHandleCommandSwitchFloodlightUnknownCommand() {
195 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
196 handler.handleCommand(floodlightChannelUID, RefreshType.REFRESH);
198 verify(requestExecutorMock, never()).executeGETRequest(any()); // nothing should get executed on a refresh
203 public void testHandleCommandSwitchFloodlightAutoModeOn() {
204 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
206 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
208 handler.handleCommand(floodlightAutoModeChannelUID, OnOffType.ON);
210 verify(requestExecutorMock, times(2)).executeGETRequest(any()); // 1.) execute ping + 2.) execute switch
212 verify(requestExecutorMock).executeGETRequest(
213 DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22auto%22%7D");
217 public void testHandleCommandSwitchFloodlightAutoModeOff() {
218 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
220 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
222 handler.handleCommand(floodlightAutoModeChannelUID, OnOffType.OFF);
224 verify(requestExecutorMock, times(2)).executeGETRequest(any()); // 1.) execute ping + 2.) execute switch off
225 verify(requestExecutorMock).executeGETRequest(
226 DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22off%22%7D");
230 public void testHandleCommandSwitchFloodlightAutoModeUnknownCommand() {
231 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
232 handler.handleCommand(floodlightAutoModeChannelUID, RefreshType.REFRESH);
234 verify(requestExecutorMock, never()).executeGETRequest(any()); // nothing should get executed on a refresh
239 * The request "fails" because there is no response content of the ping command.
242 public void testHandleCommandRequestFailed() {
243 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
244 handler.handleCommand(floodlightChannelUID, OnOffType.ON);
246 verify(requestExecutorMock, times(1)).executeGETRequest(any()); // 1.) execute ping
250 public void testHandleCommandWithoutVPN() {
251 handler.handleCommand(floodlightChannelUID, OnOffType.ON);
253 verify(requestExecutorMock, never()).executeGETRequest(any()); // no executions because the VPN URL is still
258 public void testHandleCommandPingFailedNULLResponse() {
259 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(Optional.of(""));
261 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
262 handler.handleCommand(floodlightChannelUID, OnOffType.ON);
264 verify(requestExecutorMock, times(1)).executeGETRequest(any()); // 1.) execute ping
268 public void testHandleCommandPingFailedEmptyResponse() {
269 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(Optional.of(""));
271 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
272 handler.handleCommand(floodlightChannelUID, OnOffType.ON);
274 verify(requestExecutorMock, times(1)).executeGETRequest(any()); // 1.) execute ping
278 public void testHandleCommandPingFailedWrongResponse() {
279 when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping"))
280 .thenReturn(Optional.of("{ \"message\": \"error\" }"));
282 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
283 handler.handleCommand(floodlightChannelUID, OnOffType.ON);
285 verify(requestExecutorMock, times(1)).executeGETRequest(any()); // 1.) execute ping
289 public void testHandleCommandModuleNULL() {
290 NAPresenceCameraHandler handlerWithoutModule = new NAPresenceCameraHandler(presenceCameraThing,
291 timeZoneProviderMock);
292 handlerWithoutModule.handleCommand(floodlightChannelUID, OnOffType.ON);
294 verify(requestExecutorMock, never()).executeGETRequest(any()); // no executions because the thing isn't
299 public void testGetNAThingPropertyCommonChannel() {
300 assertEquals(OnOffType.OFF, handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_CAMERA_STATUS));
304 public void testGetNAThingPropertyFloodlightOn() {
305 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.ON);
306 assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightChannelUID.getId()));
310 public void testGetNAThingPropertyFloodlightOff() {
311 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.OFF);
312 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightChannelUID.getId()));
316 public void testGetNAThingPropertyFloodlightAuto() {
317 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.AUTO);
318 // When the floodlight is set to auto-mode it is currently off.
319 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightChannelUID.getId()));
323 public void testGetNAThingPropertyFloodlightWithoutLightModeState() {
324 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightChannelUID.getId()));
328 public void testGetNAThingPropertyFloodlightModuleNULL() {
329 NAPresenceCameraHandler handlerWithoutModule = new NAPresenceCameraHandler(presenceCameraThing,
330 timeZoneProviderMock);
331 assertEquals(UnDefType.UNDEF, handlerWithoutModule.getNAThingProperty(floodlightChannelUID.getId()));
335 public void testGetNAThingPropertyFloodlightAutoModeFloodlightAuto() {
336 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.AUTO);
337 assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
341 public void testGetNAThingPropertyFloodlightAutoModeFloodlightOn() {
342 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.ON);
343 // When the floodlight is initially on (on starting the binding), there is no information about if the auto-mode
344 // was set before. Therefore the auto-mode is detected as deactivated / off.
345 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
349 public void testGetNAThingPropertyFloodlightAutoModeFloodlightOff() {
350 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.ON);
351 // When the floodlight is initially off (on starting the binding), the auto-mode isn't set.
352 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
356 public void testGetNAThingPropertyFloodlightScenarioWithAutoMode() {
357 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.AUTO);
358 assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
359 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightChannelUID.getId()));
361 // The auto-mode was initially set, after that the floodlight was switched on by the user.
362 // In this case the binding should still know that the auto-mode is/was set.
363 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.ON);
364 assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
365 assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightChannelUID.getId()));
367 // After that the user switched off the floodlight.
368 // In this case the binding should still know that the auto-mode is/was set.
369 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.OFF);
370 assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
371 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightChannelUID.getId()));
375 public void testGetNAThingPropertyFloodlightScenarioWithoutAutoMode() {
376 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.OFF);
377 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
378 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightChannelUID.getId()));
380 // The auto-mode wasn't set, after that the floodlight was switched on by the user.
381 // In this case the binding should still know that the auto-mode isn't/wasn't set.
382 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.ON);
383 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
384 assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightChannelUID.getId()));
386 // After that the user switched off the floodlight.
387 // In this case the binding should still know that the auto-mode isn't/wasn't set.
388 presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.OFF);
389 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
390 assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightChannelUID.getId()));
394 public void testGetNAThingPropertyFloodlightAutoModeModuleNULL() {
395 NAPresenceCameraHandler handlerWithoutModule = new NAPresenceCameraHandler(presenceCameraThing,
396 timeZoneProviderMock);
397 assertEquals(UnDefType.UNDEF, handlerWithoutModule.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
401 public void testGetStreamURL() {
402 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
403 Optional<String> streamURL = handler.getStreamURL("dummyVideoId");
404 assertTrue(streamURL.isPresent());
405 assertEquals(DUMMY_VPN_URL + "/vod/dummyVideoId/index.m3u8", streamURL.get());
409 public void testGetStreamURLLocal() {
410 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
411 presenceCamera.setIsLocal(true);
413 Optional<String> streamURL = handler.getStreamURL("dummyVideoId");
414 assertTrue(streamURL.isPresent());
415 assertEquals(DUMMY_VPN_URL + "/vod/dummyVideoId/index_local.m3u8", streamURL.get());
419 public void testGetStreamURLNotLocal() {
420 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
421 presenceCamera.setIsLocal(false);
423 Optional<String> streamURL = handler.getStreamURL("dummyVideoId");
424 assertTrue(streamURL.isPresent());
425 assertEquals(DUMMY_VPN_URL + "/vod/dummyVideoId/index.m3u8", streamURL.get());
429 public void testGetStreamURLWithoutVPN() {
430 Optional<String> streamURL = handler.getStreamURL("dummyVideoId");
431 assertFalse(streamURL.isPresent());
435 public void testGetLivePictureURLState() {
436 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
438 State livePictureURLState = handler.getLivePictureURLState();
439 assertEquals(new StringType(DUMMY_VPN_URL + "/live/snapshot_720.jpg"), livePictureURLState);
443 public void testGetLivePictureURLStateWithoutVPN() {
444 State livePictureURLState = handler.getLivePictureURLState();
445 assertEquals(UnDefType.UNDEF, livePictureURLState);
449 public void testGetLiveStreamState() {
450 presenceCamera.setVpnUrl(DUMMY_VPN_URL);
452 State liveStreamState = handler.getLiveStreamState();
453 assertEquals(new StringType(DUMMY_VPN_URL + "/live/index.m3u8"), liveStreamState);
457 public void testGetLiveStreamStateWithoutVPN() {
458 State liveStreamState = handler.getLiveStreamState();
459 assertEquals(UnDefType.UNDEF, liveStreamState);
462 private static Optional<String> createPingResponseContent(final String localURL) {
463 return Optional.of("{\"local_url\":\"" + localURL + "\",\"product_name\":\"Welcome Netatmo\"}");
466 private interface RequestExecutor {
468 Optional<String> executeGETRequest(String url);
471 private class NAPresenceCameraHandlerAccessible extends NAPresenceCameraHandler {
473 private NAPresenceCameraHandlerAccessible(Thing thing, NAWelcomeCamera presenceCamera) {
474 super(thing, timeZoneProviderMock);
475 setModule(presenceCamera);
479 protected @NonNull Optional<@NonNull String> executeGETRequest(@NonNull String url) {
480 return requestExecutorMock.executeGETRequest(url);
484 protected @NonNull State getLivePictureURLState() {
485 return super.getLivePictureURLState();
489 protected @NonNull State getLiveStreamState() {
490 return super.getLiveStreamState();