2 * Copyright (c) 2010-2023 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.robonect.internal.handler;
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.ArgumentMatchers.eq;
17 import static org.mockito.Mockito.*;
19 import java.time.Month;
20 import java.time.ZoneId;
21 import java.time.ZonedDateTime;
23 import org.eclipse.jetty.client.HttpClient;
24 import org.junit.jupiter.api.BeforeEach;
25 import org.junit.jupiter.api.Test;
26 import org.junit.jupiter.api.extension.ExtendWith;
27 import org.mockito.ArgumentCaptor;
28 import org.mockito.Mock;
29 import org.mockito.Mockito;
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.robonect.internal.RobonectBindingConstants;
34 import org.openhab.binding.robonect.internal.RobonectClient;
35 import org.openhab.binding.robonect.internal.model.ErrorEntry;
36 import org.openhab.binding.robonect.internal.model.ErrorList;
37 import org.openhab.binding.robonect.internal.model.MowerInfo;
38 import org.openhab.binding.robonect.internal.model.MowerMode;
39 import org.openhab.binding.robonect.internal.model.MowerStatus;
40 import org.openhab.binding.robonect.internal.model.NextTimer;
41 import org.openhab.binding.robonect.internal.model.Status;
42 import org.openhab.binding.robonect.internal.model.Timer;
43 import org.openhab.binding.robonect.internal.model.Wlan;
44 import org.openhab.core.i18n.TimeZoneProvider;
45 import org.openhab.core.library.types.DateTimeType;
46 import org.openhab.core.library.types.DecimalType;
47 import org.openhab.core.library.types.OnOffType;
48 import org.openhab.core.library.types.QuantityType;
49 import org.openhab.core.library.types.StringType;
50 import org.openhab.core.thing.ChannelUID;
51 import org.openhab.core.thing.Thing;
52 import org.openhab.core.thing.ThingUID;
53 import org.openhab.core.thing.binding.ThingHandlerCallback;
54 import org.openhab.core.types.RefreshType;
55 import org.openhab.core.types.State;
56 import org.openhab.core.types.UnDefType;
59 * The goal of this class is to test RobonectHandler in isolation.
61 * @author Marco Meyer - Initial contribution
63 @ExtendWith(MockitoExtension.class)
64 @MockitoSettings(strictness = Strictness.LENIENT)
65 public class RobonectHandlerTest {
67 private RobonectHandler subject;
69 private @Mock Thing robonectThingMock;
70 private @Mock RobonectClient robonectClientMock;
71 private @Mock ThingHandlerCallback callbackMock;
72 private @Mock HttpClient httpClientMock;
73 private @Mock TimeZoneProvider timezoneProvider;
77 subject = new RobonectHandler(robonectThingMock, httpClientMock, timezoneProvider);
78 subject.setCallback(callbackMock);
79 subject.setRobonectClient(robonectClientMock);
81 Mockito.when(timezoneProvider.getTimeZone()).thenReturn(ZoneId.of("Europe/Berlin"));
85 public void shouldUpdateNextTimerChannelWithDateTimeState() throws InterruptedException {
86 ArgumentCaptor<State> stateCaptor = ArgumentCaptor.forClass(State.class);
89 MowerInfo mowerInfo = createSuccessfulMowerInfoResponse();
90 Timer timer = new Timer();
91 timer.setStatus(Timer.TimerMode.ACTIVE);
92 NextTimer nextTimer = new NextTimer();
93 nextTimer.setDate("01.05.2017");
94 nextTimer.setTime("19:00:00");
95 nextTimer.setUnix("1493665200");
96 timer.setNext(nextTimer);
99 when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
100 when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3"));
102 subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_TIMER_NEXT_TIMER),
103 RefreshType.REFRESH);
106 verify(callbackMock, times(1)).stateUpdated(
107 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_TIMER_NEXT_TIMER)),
108 stateCaptor.capture());
110 State value = stateCaptor.getValue();
111 assertTrue(value instanceof DateTimeType);
113 ZonedDateTime zdt = ((DateTimeType) value).getZonedDateTime();
114 assertEquals(1, zdt.getDayOfMonth());
115 assertEquals(2017, zdt.getYear());
116 assertEquals(Month.MAY, zdt.getMonth());
117 assertEquals(19, zdt.getHour());
118 assertEquals(0, zdt.getMinute());
119 assertEquals(0, zdt.getSecond());
123 public void shouldUpdateErrorChannelsIfErrorStatusReturned() throws InterruptedException {
124 ArgumentCaptor<State> errorCodeCaptor = ArgumentCaptor.forClass(State.class);
125 ArgumentCaptor<State> errorMessageCaptor = ArgumentCaptor.forClass(State.class);
126 ArgumentCaptor<State> errorDateCaptor = ArgumentCaptor.forClass(State.class);
129 MowerInfo mowerInfo = createSuccessfulMowerInfoResponse();
130 ErrorEntry error = new ErrorEntry();
131 error.setDate("01.05.2017");
132 error.setTime("19:00:00");
133 error.setUnix("1493665200");
134 error.setErrorCode(Integer.valueOf(22));
135 error.setErrorMessage("Dummy Message");
136 mowerInfo.getStatus().setStatus(MowerStatus.ERROR_STATUS);
137 mowerInfo.setError(error);
138 ErrorList errorList = new ErrorList();
139 errorList.setSuccessful(true);
142 when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
143 when(robonectClientMock.errorList()).thenReturn(errorList);
144 when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3"));
146 subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
147 RefreshType.REFRESH);
150 verify(callbackMock, times(1)).stateUpdated(
151 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_CODE)),
152 errorCodeCaptor.capture());
153 verify(callbackMock, times(1)).stateUpdated(
154 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_MESSAGE)),
155 errorMessageCaptor.capture());
156 verify(callbackMock, times(1)).stateUpdated(
157 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_DATE)),
158 errorDateCaptor.capture());
160 State errorDate = errorDateCaptor.getValue();
161 assertTrue(errorDate instanceof DateTimeType);
163 ZonedDateTime zdt = ((DateTimeType) errorDate).getZonedDateTime();
164 assertEquals(1, zdt.getDayOfMonth());
165 assertEquals(2017, zdt.getYear());
166 assertEquals(Month.MAY, zdt.getMonth());
167 assertEquals(19, zdt.getHour());
168 assertEquals(0, zdt.getMinute());
169 assertEquals(0, zdt.getSecond());
171 State errorMessage = errorMessageCaptor.getValue();
172 assertTrue(errorMessage instanceof StringType);
173 StringType msgStringType = (StringType) errorMessage;
174 assertEquals("Dummy Message", msgStringType.toFullString());
176 State errorCode = errorCodeCaptor.getValue();
177 assertTrue(errorCode instanceof DecimalType);
178 DecimalType codeDecimaltype = (DecimalType) errorCode;
179 assertEquals(22, codeDecimaltype.intValue());
183 public void shouldResetErrorStateIfNoErrorInStatusUpdate() throws InterruptedException {
184 ArgumentCaptor<State> errorCodeCaptor = ArgumentCaptor.forClass(State.class);
185 ArgumentCaptor<State> errorMessageCaptor = ArgumentCaptor.forClass(State.class);
186 ArgumentCaptor<State> errorDateCaptor = ArgumentCaptor.forClass(State.class);
189 MowerInfo mowerInfo = createSuccessfulMowerInfoResponse();
190 mowerInfo.getStatus().setStatus(MowerStatus.MOWING);
191 mowerInfo.setError(null);
194 when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
195 when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3"));
197 subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
198 RefreshType.REFRESH);
201 verify(callbackMock, times(1)).stateUpdated(
202 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_CODE)),
203 errorCodeCaptor.capture());
204 verify(callbackMock, times(1)).stateUpdated(
205 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_MESSAGE)),
206 errorMessageCaptor.capture());
207 verify(callbackMock, times(1)).stateUpdated(
208 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_DATE)),
209 errorDateCaptor.capture());
211 assertEquals(errorCodeCaptor.getValue(), UnDefType.UNDEF);
212 assertEquals(errorMessageCaptor.getValue(), UnDefType.UNDEF);
213 assertEquals(errorDateCaptor.getValue(), UnDefType.UNDEF);
217 public void shouldUpdateNumericStateOnMowerStatusRefresh() throws InterruptedException {
218 ArgumentCaptor<State> stateCaptor = ArgumentCaptor.forClass(State.class);
221 MowerInfo mowerInfo = createSuccessfulMowerInfoResponse();
222 mowerInfo.getStatus().setStatus(MowerStatus.MOWING);
225 when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
226 when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3"));
228 subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
229 RefreshType.REFRESH);
232 verify(callbackMock, times(1)).stateUpdated(
233 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS)),
234 stateCaptor.capture());
236 State value = stateCaptor.getValue();
237 assertTrue(value instanceof DecimalType);
238 DecimalType status = (DecimalType) value;
240 assertEquals(MowerStatus.MOWING.getStatusCode(), status.intValue());
244 public void shouldUpdateAllChannels() {
245 ArgumentCaptor<State> stateCaptorName = ArgumentCaptor.forClass(State.class);
246 ArgumentCaptor<State> stateCaptorBattery = ArgumentCaptor.forClass(State.class);
247 ArgumentCaptor<State> stateCaptorStatus = ArgumentCaptor.forClass(State.class);
248 ArgumentCaptor<State> stateCaptorDuration = ArgumentCaptor.forClass(State.class);
249 ArgumentCaptor<State> stateCaptorHours = ArgumentCaptor.forClass(State.class);
250 ArgumentCaptor<State> stateCaptorMode = ArgumentCaptor.forClass(State.class);
251 ArgumentCaptor<State> stateCaptorStarted = ArgumentCaptor.forClass(State.class);
252 ArgumentCaptor<State> stateCaptorWlan = ArgumentCaptor.forClass(State.class);
255 MowerInfo mowerInfo = createSuccessfulMowerInfoResponse();
256 ErrorList errorList = new ErrorList();
257 errorList.setSuccessful(true);
260 when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
261 when(robonectClientMock.errorList()).thenReturn(errorList);
262 when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3"));
264 subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
265 RefreshType.REFRESH);
268 verify(callbackMock, times(1)).stateUpdated(
269 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_MOWER_NAME)),
270 stateCaptorName.capture());
271 verify(callbackMock, times(1)).stateUpdated(
272 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS_BATTERY)),
273 stateCaptorBattery.capture());
274 verify(callbackMock, times(1)).stateUpdated(
275 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS)),
276 stateCaptorStatus.capture());
277 verify(callbackMock, times(1)).stateUpdated(
278 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS_DURATION)),
279 stateCaptorDuration.capture());
280 verify(callbackMock, times(1)).stateUpdated(
281 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS_HOURS)),
282 stateCaptorHours.capture());
283 verify(callbackMock, times(1)).stateUpdated(
284 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS_MODE)),
285 stateCaptorMode.capture());
286 verify(callbackMock, times(1)).stateUpdated(
287 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_MOWER_START)),
288 stateCaptorStarted.capture());
289 verify(callbackMock, times(1)).stateUpdated(
290 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_WLAN_SIGNAL)),
291 stateCaptorWlan.capture());
293 assertEquals("Mowy", stateCaptorName.getValue().toFullString());
294 assertEquals(99, ((DecimalType) stateCaptorBattery.getValue()).intValue());
295 assertEquals(4, ((DecimalType) stateCaptorStatus.getValue()).intValue());
296 assertEquals(55, ((QuantityType<?>) stateCaptorDuration.getValue()).intValue());
297 assertEquals(22, ((QuantityType<?>) stateCaptorHours.getValue()).intValue());
298 assertEquals(MowerMode.AUTO.name(), stateCaptorMode.getValue().toFullString());
299 assertEquals(OnOffType.ON, stateCaptorStarted.getValue());
300 assertEquals(-88, ((DecimalType) stateCaptorWlan.getValue()).intValue());
303 private MowerInfo createSuccessfulMowerInfoResponse() {
304 MowerInfo mowerInfo = new MowerInfo();
305 Timer timer = new Timer();
306 timer.setStatus(Timer.TimerMode.ACTIVE);
307 NextTimer nextTimer = new NextTimer();
308 nextTimer.setDate("01.05.2017");
309 nextTimer.setTime("19:00:00");
310 nextTimer.setUnix("1493665200");
311 timer.setNext(nextTimer);
312 mowerInfo.setTimer(timer);
313 Status status = new Status();
314 status.setBattery(99);
315 status.setDuration(55);
317 status.setMode(MowerMode.AUTO);
318 status.setStatus(MowerStatus.CHARGING);
319 mowerInfo.setStatus(status);
320 mowerInfo.setName("Mowy");
321 Wlan wlan = new Wlan();
323 mowerInfo.setWlan(wlan);
324 mowerInfo.setSuccessful(true);
325 mowerInfo.getStatus().setStopped(false);