]> git.basschouten.com Git - openhab-addons.git/blob
23d7a5fa68467e379cf5f05fcbc6a7e2d9ed6f84
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.netatmo.internal.presence;
14
15 import static org.junit.Assert.*;
16 import static org.mockito.ArgumentMatchers.any;
17 import static org.mockito.Mockito.*;
18
19 import java.util.Optional;
20
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;
38
39 import io.swagger.client.model.NAWelcomeCamera;
40
41 /**
42  * @author Sven Strohschein - Initial contribution
43  */
44 @RunWith(MockitoJUnitRunner.class)
45 public class NAPresenceCameraHandlerTest {
46
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);
50
51     @Mock
52     private RequestExecutor requestExecutorMock;
53     @Mock
54     private TimeZoneProvider timeZoneProviderMock;
55
56     private Thing presenceCameraThing;
57     private NAWelcomeCamera presenceCamera;
58     private ChannelUID cameraStatusChannelUID;
59     private ChannelUID floodlightChannelUID;
60     private ChannelUID floodlightAutoModeChannelUID;
61     private NAPresenceCameraHandlerAccessible handler;
62
63     @Before
64     public void before() {
65         presenceCameraThing = new ThingImpl(new ThingTypeUID("netatmo", "NOC"), "1");
66         presenceCamera = new NAWelcomeCamera();
67
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);
74
75         handler = new NAPresenceCameraHandlerAccessible(presenceCameraThing, presenceCamera);
76     }
77
78     @Test
79     public void testHandleCommandSwitchSurveillanceOn() {
80         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
81
82         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
83         handler.handleCommand(cameraStatusChannelUID, OnOffType.ON);
84
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");
87     }
88
89     @Test
90     public void testHandleCommandSwitchSurveillanceOff() {
91         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
92
93         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
94         handler.handleCommand(cameraStatusChannelUID, OnOffType.OFF);
95
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");
98     }
99
100     @Test
101     public void testHandleCommandSwitchSurveillanceUnknownCommand() {
102         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
103         handler.handleCommand(cameraStatusChannelUID, RefreshType.REFRESH);
104
105         verify(requestExecutorMock, never()).executeGETRequest(any()); // nothing should get executed on a refresh
106                                                                        // command
107     }
108
109     @Test
110     public void testHandleCommandSwitchSurveillanceWithoutVPN() {
111         handler.handleCommand(cameraStatusChannelUID, OnOffType.ON);
112
113         verify(requestExecutorMock, never()).executeGETRequest(any()); // nothing should get executed when no VPN
114                                                                        // address is set
115     }
116
117     @Test
118     public void testHandleCommandSwitchFloodlightOn() {
119         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
120
121         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
122         handler.handleCommand(floodlightChannelUID, OnOffType.ON);
123
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");
127     }
128
129     @Test
130     public void testHandleCommandSwitchFloodlightOff() {
131         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
132
133         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
134         handler.handleCommand(floodlightChannelUID, OnOffType.OFF);
135
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");
139     }
140
141     @Test
142     public void testHandleCommandSwitchFloodlightOffWithAutoModeOn() {
143         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
144
145         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
146         presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.AUTO);
147         assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
148
149         handler.handleCommand(floodlightChannelUID, OnOffType.OFF);
150
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");
154     }
155
156     @Test
157     public void testHandleCommandSwitchFloodlightOnAddressChanged() {
158         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
159
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");
166
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");
174
175         final String newDummyVPNURL = DUMMY_VPN_URL + "2";
176         final String newDummyLocalURL = DUMMY_LOCAL_URL + "2";
177         final Optional<String> newDummyPingResponse = createPingResponseContent(newDummyLocalURL);
178
179         when(requestExecutorMock.executeGETRequest(newDummyVPNURL + "/command/ping")).thenReturn(newDummyPingResponse);
180
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");
191     }
192
193     @Test
194     public void testHandleCommandSwitchFloodlightUnknownCommand() {
195         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
196         handler.handleCommand(floodlightChannelUID, RefreshType.REFRESH);
197
198         verify(requestExecutorMock, never()).executeGETRequest(any()); // nothing should get executed on a refresh
199                                                                        // command
200     }
201
202     @Test
203     public void testHandleCommandSwitchFloodlightAutoModeOn() {
204         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
205
206         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
207
208         handler.handleCommand(floodlightAutoModeChannelUID, OnOffType.ON);
209
210         verify(requestExecutorMock, times(2)).executeGETRequest(any()); // 1.) execute ping + 2.) execute switch
211                                                                         // auto-mode on
212         verify(requestExecutorMock).executeGETRequest(
213                 DUMMY_LOCAL_URL + "/command/floodlight_set_config?config=%7B%22mode%22:%22auto%22%7D");
214     }
215
216     @Test
217     public void testHandleCommandSwitchFloodlightAutoModeOff() {
218         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(DUMMY_PING_RESPONSE);
219
220         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
221
222         handler.handleCommand(floodlightAutoModeChannelUID, OnOffType.OFF);
223
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");
227     }
228
229     @Test
230     public void testHandleCommandSwitchFloodlightAutoModeUnknownCommand() {
231         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
232         handler.handleCommand(floodlightAutoModeChannelUID, RefreshType.REFRESH);
233
234         verify(requestExecutorMock, never()).executeGETRequest(any()); // nothing should get executed on a refresh
235                                                                        // command
236     }
237
238     /**
239      * The request "fails" because there is no response content of the ping command.
240      */
241     @Test
242     public void testHandleCommandRequestFailed() {
243         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
244         handler.handleCommand(floodlightChannelUID, OnOffType.ON);
245
246         verify(requestExecutorMock, times(1)).executeGETRequest(any()); // 1.) execute ping
247     }
248
249     @Test
250     public void testHandleCommandWithoutVPN() {
251         handler.handleCommand(floodlightChannelUID, OnOffType.ON);
252
253         verify(requestExecutorMock, never()).executeGETRequest(any()); // no executions because the VPN URL is still
254                                                                        // unknown
255     }
256
257     @Test
258     public void testHandleCommandPingFailedNULLResponse() {
259         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(Optional.of(""));
260
261         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
262         handler.handleCommand(floodlightChannelUID, OnOffType.ON);
263
264         verify(requestExecutorMock, times(1)).executeGETRequest(any()); // 1.) execute ping
265     }
266
267     @Test
268     public void testHandleCommandPingFailedEmptyResponse() {
269         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping")).thenReturn(Optional.of(""));
270
271         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
272         handler.handleCommand(floodlightChannelUID, OnOffType.ON);
273
274         verify(requestExecutorMock, times(1)).executeGETRequest(any()); // 1.) execute ping
275     }
276
277     @Test
278     public void testHandleCommandPingFailedWrongResponse() {
279         when(requestExecutorMock.executeGETRequest(DUMMY_VPN_URL + "/command/ping"))
280                 .thenReturn(Optional.of("{ \"message\":  \"error\" }"));
281
282         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
283         handler.handleCommand(floodlightChannelUID, OnOffType.ON);
284
285         verify(requestExecutorMock, times(1)).executeGETRequest(any()); // 1.) execute ping
286     }
287
288     @Test
289     public void testHandleCommandModuleNULL() {
290         NAPresenceCameraHandler handlerWithoutModule = new NAPresenceCameraHandler(presenceCameraThing,
291                 timeZoneProviderMock);
292         handlerWithoutModule.handleCommand(floodlightChannelUID, OnOffType.ON);
293
294         verify(requestExecutorMock, never()).executeGETRequest(any()); // no executions because the thing isn't
295                                                                        // initialized
296     }
297
298     @Test
299     public void testGetNAThingPropertyCommonChannel() {
300         assertEquals(OnOffType.OFF, handler.getNAThingProperty(NetatmoBindingConstants.CHANNEL_CAMERA_STATUS));
301     }
302
303     @Test
304     public void testGetNAThingPropertyFloodlightOn() {
305         presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.ON);
306         assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightChannelUID.getId()));
307     }
308
309     @Test
310     public void testGetNAThingPropertyFloodlightOff() {
311         presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.OFF);
312         assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightChannelUID.getId()));
313     }
314
315     @Test
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()));
320     }
321
322     @Test
323     public void testGetNAThingPropertyFloodlightWithoutLightModeState() {
324         assertEquals(OnOffType.OFF, handler.getNAThingProperty(floodlightChannelUID.getId()));
325     }
326
327     @Test
328     public void testGetNAThingPropertyFloodlightModuleNULL() {
329         NAPresenceCameraHandler handlerWithoutModule = new NAPresenceCameraHandler(presenceCameraThing,
330                 timeZoneProviderMock);
331         assertEquals(UnDefType.UNDEF, handlerWithoutModule.getNAThingProperty(floodlightChannelUID.getId()));
332     }
333
334     @Test
335     public void testGetNAThingPropertyFloodlightAutoModeFloodlightAuto() {
336         presenceCamera.setLightModeStatus(NAWelcomeCamera.LightModeStatusEnum.AUTO);
337         assertEquals(OnOffType.ON, handler.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
338     }
339
340     @Test
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()));
346     }
347
348     @Test
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()));
353     }
354
355     @Test
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()));
360
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()));
366
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()));
372     }
373
374     @Test
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()));
379
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()));
385
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()));
391     }
392
393     @Test
394     public void testGetNAThingPropertyFloodlightAutoModeModuleNULL() {
395         NAPresenceCameraHandler handlerWithoutModule = new NAPresenceCameraHandler(presenceCameraThing,
396                 timeZoneProviderMock);
397         assertEquals(UnDefType.UNDEF, handlerWithoutModule.getNAThingProperty(floodlightAutoModeChannelUID.getId()));
398     }
399
400     @Test
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());
406     }
407
408     @Test
409     public void testGetStreamURLLocal() {
410         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
411         presenceCamera.setIsLocal(true);
412
413         Optional<String> streamURL = handler.getStreamURL("dummyVideoId");
414         assertTrue(streamURL.isPresent());
415         assertEquals(DUMMY_VPN_URL + "/vod/dummyVideoId/index_local.m3u8", streamURL.get());
416     }
417
418     @Test
419     public void testGetStreamURLNotLocal() {
420         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
421         presenceCamera.setIsLocal(false);
422
423         Optional<String> streamURL = handler.getStreamURL("dummyVideoId");
424         assertTrue(streamURL.isPresent());
425         assertEquals(DUMMY_VPN_URL + "/vod/dummyVideoId/index.m3u8", streamURL.get());
426     }
427
428     @Test
429     public void testGetStreamURLWithoutVPN() {
430         Optional<String> streamURL = handler.getStreamURL("dummyVideoId");
431         assertFalse(streamURL.isPresent());
432     }
433
434     @Test
435     public void testGetLivePictureURLState() {
436         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
437
438         State livePictureURLState = handler.getLivePictureURLState();
439         assertEquals(new StringType(DUMMY_VPN_URL + "/live/snapshot_720.jpg"), livePictureURLState);
440     }
441
442     @Test
443     public void testGetLivePictureURLStateWithoutVPN() {
444         State livePictureURLState = handler.getLivePictureURLState();
445         assertEquals(UnDefType.UNDEF, livePictureURLState);
446     }
447
448     @Test
449     public void testGetLiveStreamState() {
450         presenceCamera.setVpnUrl(DUMMY_VPN_URL);
451
452         State liveStreamState = handler.getLiveStreamState();
453         assertEquals(new StringType(DUMMY_VPN_URL + "/live/index.m3u8"), liveStreamState);
454     }
455
456     @Test
457     public void testGetLiveStreamStateWithoutVPN() {
458         State liveStreamState = handler.getLiveStreamState();
459         assertEquals(UnDefType.UNDEF, liveStreamState);
460     }
461
462     private static Optional<String> createPingResponseContent(final String localURL) {
463         return Optional.of("{\"local_url\":\"" + localURL + "\",\"product_name\":\"Welcome Netatmo\"}");
464     }
465
466     private interface RequestExecutor {
467
468         Optional<String> executeGETRequest(String url);
469     }
470
471     private class NAPresenceCameraHandlerAccessible extends NAPresenceCameraHandler {
472
473         private NAPresenceCameraHandlerAccessible(Thing thing, NAWelcomeCamera presenceCamera) {
474             super(thing, timeZoneProviderMock);
475             setModule(presenceCamera);
476         }
477
478         @Override
479         protected @NonNull Optional<@NonNull String> executeGETRequest(@NonNull String url) {
480             return requestExecutorMock.executeGETRequest(url);
481         }
482
483         @Override
484         protected @NonNull State getLivePictureURLState() {
485             return super.getLivePictureURLState();
486         }
487
488         @Override
489         protected @NonNull State getLiveStreamState() {
490             return super.getLiveStreamState();
491         }
492     }
493 }