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.junit.jupiter.api.BeforeEach;
24 import org.junit.jupiter.api.Test;
25 import org.junit.jupiter.api.extension.ExtendWith;
26 import org.mockito.ArgumentCaptor;
27 import org.mockito.Mock;
28 import org.mockito.Mockito;
29 import org.mockito.junit.jupiter.MockitoExtension;
30 import org.mockito.junit.jupiter.MockitoSettings;
31 import org.mockito.quality.Strictness;
32 import org.openhab.binding.robonect.internal.RobonectBindingConstants;
33 import org.openhab.binding.robonect.internal.RobonectClient;
34 import org.openhab.binding.robonect.internal.model.ErrorEntry;
35 import org.openhab.binding.robonect.internal.model.ErrorList;
36 import org.openhab.binding.robonect.internal.model.MowerInfo;
37 import org.openhab.binding.robonect.internal.model.MowerMode;
38 import org.openhab.binding.robonect.internal.model.MowerStatus;
39 import org.openhab.binding.robonect.internal.model.NextTimer;
40 import org.openhab.binding.robonect.internal.model.Status;
41 import org.openhab.binding.robonect.internal.model.Timer;
42 import org.openhab.binding.robonect.internal.model.Wlan;
43 import org.openhab.core.i18n.TimeZoneProvider;
44 import org.openhab.core.io.net.http.HttpClientFactory;
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 HttpClientFactory httpClientFactoryMock;
73 private @Mock TimeZoneProvider timezoneProvider;
77 Mockito.when(robonectThingMock.getUID()).thenReturn(new ThingUID("1:2:3"));
78 Mockito.when(timezoneProvider.getTimeZone()).thenReturn(ZoneId.of("Europe/Berlin"));
80 subject = new RobonectHandler(robonectThingMock, httpClientFactoryMock, timezoneProvider);
81 subject.setCallback(callbackMock);
82 subject.setRobonectClient(robonectClientMock);
86 public void shouldUpdateNextTimerChannelWithDateTimeState() throws InterruptedException {
87 ArgumentCaptor<State> stateCaptor = ArgumentCaptor.forClass(State.class);
90 MowerInfo mowerInfo = createSuccessfulMowerInfoResponse();
91 Timer timer = new Timer();
92 timer.setStatus(Timer.TimerMode.ACTIVE);
93 NextTimer nextTimer = new NextTimer();
94 nextTimer.setDate("01.05.2017");
95 nextTimer.setTime("19:00:00");
96 nextTimer.setUnix("1493665200");
97 timer.setNext(nextTimer);
100 when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
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);
145 subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
146 RefreshType.REFRESH);
149 verify(callbackMock, times(1)).stateUpdated(
150 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_CODE)),
151 errorCodeCaptor.capture());
152 verify(callbackMock, times(1)).stateUpdated(
153 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_MESSAGE)),
154 errorMessageCaptor.capture());
155 verify(callbackMock, times(1)).stateUpdated(
156 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_DATE)),
157 errorDateCaptor.capture());
159 State errorDate = errorDateCaptor.getValue();
160 assertTrue(errorDate instanceof DateTimeType);
162 ZonedDateTime zdt = ((DateTimeType) errorDate).getZonedDateTime();
163 assertEquals(1, zdt.getDayOfMonth());
164 assertEquals(2017, zdt.getYear());
165 assertEquals(Month.MAY, zdt.getMonth());
166 assertEquals(19, zdt.getHour());
167 assertEquals(0, zdt.getMinute());
168 assertEquals(0, zdt.getSecond());
170 State errorMessage = errorMessageCaptor.getValue();
171 assertTrue(errorMessage instanceof StringType);
172 StringType msgStringType = (StringType) errorMessage;
173 assertEquals("Dummy Message", msgStringType.toFullString());
175 State errorCode = errorCodeCaptor.getValue();
176 assertTrue(errorCode instanceof DecimalType);
177 DecimalType codeDecimaltype = (DecimalType) errorCode;
178 assertEquals(22, codeDecimaltype.intValue());
182 public void shouldResetErrorStateIfNoErrorInStatusUpdate() throws InterruptedException {
183 ArgumentCaptor<State> errorCodeCaptor = ArgumentCaptor.forClass(State.class);
184 ArgumentCaptor<State> errorMessageCaptor = ArgumentCaptor.forClass(State.class);
185 ArgumentCaptor<State> errorDateCaptor = ArgumentCaptor.forClass(State.class);
188 MowerInfo mowerInfo = createSuccessfulMowerInfoResponse();
189 mowerInfo.getStatus().setStatus(MowerStatus.MOWING);
190 mowerInfo.setError(null);
193 when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
195 subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
196 RefreshType.REFRESH);
199 verify(callbackMock, times(1)).stateUpdated(
200 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_CODE)),
201 errorCodeCaptor.capture());
202 verify(callbackMock, times(1)).stateUpdated(
203 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_MESSAGE)),
204 errorMessageCaptor.capture());
205 verify(callbackMock, times(1)).stateUpdated(
206 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_ERROR_DATE)),
207 errorDateCaptor.capture());
209 assertEquals(errorCodeCaptor.getValue(), UnDefType.UNDEF);
210 assertEquals(errorMessageCaptor.getValue(), UnDefType.UNDEF);
211 assertEquals(errorDateCaptor.getValue(), UnDefType.UNDEF);
215 public void shouldUpdateNumericStateOnMowerStatusRefresh() throws InterruptedException {
216 ArgumentCaptor<State> stateCaptor = ArgumentCaptor.forClass(State.class);
219 MowerInfo mowerInfo = createSuccessfulMowerInfoResponse();
220 mowerInfo.getStatus().setStatus(MowerStatus.MOWING);
223 when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
225 subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
226 RefreshType.REFRESH);
229 verify(callbackMock, times(1)).stateUpdated(
230 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS)),
231 stateCaptor.capture());
233 State value = stateCaptor.getValue();
234 assertTrue(value instanceof DecimalType);
235 DecimalType status = (DecimalType) value;
237 assertEquals(MowerStatus.MOWING.getStatusCode(), status.intValue());
241 public void shouldUpdateAllChannels() {
242 ArgumentCaptor<State> stateCaptorName = ArgumentCaptor.forClass(State.class);
243 ArgumentCaptor<State> stateCaptorBattery = ArgumentCaptor.forClass(State.class);
244 ArgumentCaptor<State> stateCaptorStatus = ArgumentCaptor.forClass(State.class);
245 ArgumentCaptor<State> stateCaptorDuration = ArgumentCaptor.forClass(State.class);
246 ArgumentCaptor<State> stateCaptorHours = ArgumentCaptor.forClass(State.class);
247 ArgumentCaptor<State> stateCaptorMode = ArgumentCaptor.forClass(State.class);
248 ArgumentCaptor<State> stateCaptorStarted = ArgumentCaptor.forClass(State.class);
249 ArgumentCaptor<State> stateCaptorWlan = ArgumentCaptor.forClass(State.class);
252 MowerInfo mowerInfo = createSuccessfulMowerInfoResponse();
253 ErrorList errorList = new ErrorList();
254 errorList.setSuccessful(true);
257 when(robonectClientMock.getMowerInfo()).thenReturn(mowerInfo);
258 when(robonectClientMock.errorList()).thenReturn(errorList);
260 subject.handleCommand(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS),
261 RefreshType.REFRESH);
264 verify(callbackMock, times(1)).stateUpdated(
265 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_MOWER_NAME)),
266 stateCaptorName.capture());
267 verify(callbackMock, times(1)).stateUpdated(
268 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS_BATTERY)),
269 stateCaptorBattery.capture());
270 verify(callbackMock, times(1)).stateUpdated(
271 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS)),
272 stateCaptorStatus.capture());
273 verify(callbackMock, times(1)).stateUpdated(
274 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS_DURATION)),
275 stateCaptorDuration.capture());
276 verify(callbackMock, times(1)).stateUpdated(
277 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS_HOURS)),
278 stateCaptorHours.capture());
279 verify(callbackMock, times(1)).stateUpdated(
280 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_STATUS_MODE)),
281 stateCaptorMode.capture());
282 verify(callbackMock, times(1)).stateUpdated(
283 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_MOWER_START)),
284 stateCaptorStarted.capture());
285 verify(callbackMock, times(1)).stateUpdated(
286 eq(new ChannelUID(new ThingUID("1:2:3"), RobonectBindingConstants.CHANNEL_WLAN_SIGNAL)),
287 stateCaptorWlan.capture());
289 assertEquals("Mowy", stateCaptorName.getValue().toFullString());
290 assertEquals(99, ((DecimalType) stateCaptorBattery.getValue()).intValue());
291 assertEquals(4, ((DecimalType) stateCaptorStatus.getValue()).intValue());
292 assertEquals(55, ((QuantityType<?>) stateCaptorDuration.getValue()).intValue());
293 assertEquals(22, ((QuantityType<?>) stateCaptorHours.getValue()).intValue());
294 assertEquals(MowerMode.AUTO.name(), stateCaptorMode.getValue().toFullString());
295 assertEquals(OnOffType.ON, stateCaptorStarted.getValue());
296 assertEquals(-88, ((DecimalType) stateCaptorWlan.getValue()).intValue());
299 private MowerInfo createSuccessfulMowerInfoResponse() {
300 MowerInfo mowerInfo = new MowerInfo();
301 Timer timer = new Timer();
302 timer.setStatus(Timer.TimerMode.ACTIVE);
303 NextTimer nextTimer = new NextTimer();
304 nextTimer.setDate("01.05.2017");
305 nextTimer.setTime("19:00:00");
306 nextTimer.setUnix("1493665200");
307 timer.setNext(nextTimer);
308 mowerInfo.setTimer(timer);
309 Status status = new Status();
310 status.setBattery(99);
311 status.setDuration(55);
313 status.setMode(MowerMode.AUTO);
314 status.setStatus(MowerStatus.CHARGING);
315 mowerInfo.setStatus(status);
316 mowerInfo.setName("Mowy");
317 Wlan wlan = new Wlan();
319 mowerInfo.setWlan(wlan);
320 mowerInfo.setSuccessful(true);
321 mowerInfo.getStatus().setStopped(false);