2 * Copyright (c) 2010-2022 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.wundergroundupdatereceiver.internal;
15 import static org.hamcrest.MatcherAssert.assertThat;
16 import static org.hamcrest.core.Is.is;
17 import static org.hamcrest.core.IsIterableContaining.hasItems;
18 import static org.mockito.Mockito.any;
19 import static org.mockito.Mockito.eq;
20 import static org.mockito.Mockito.mock;
21 import static org.mockito.Mockito.never;
22 import static org.mockito.Mockito.times;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.when;
25 import static org.mockito.MockitoAnnotations.openMocks;
26 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.DATEUTC_DATETIME_CHANNELTYPEUID;
27 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.HUMIDITY_GROUP;
28 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.LAST_QUERY_STATE_CHANNELTYPEUID;
29 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER_CHANNELTYPEUID;
30 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.LAST_RECEIVED_DATETIME_CHANNELTYPEUID;
31 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.METADATA_GROUP;
32 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.POLLUTION_GROUP;
33 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.PRESSURE_GROUP;
34 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.RAIN_GROUP;
35 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.SUNLIGHT_GROUP;
36 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.TEMPERATURE_GROUP;
37 import static org.openhab.binding.wundergroundupdatereceiver.internal.WundergroundUpdateReceiverBindingConstants.WIND_GROUP;
39 import java.io.IOException;
40 import java.util.List;
43 import javax.servlet.ServletException;
44 import javax.servlet.http.HttpServletResponse;
46 import org.eclipse.jdt.annotation.NonNullByDefault;
47 import org.eclipse.jetty.http.HttpFields;
48 import org.eclipse.jetty.http.HttpURI;
49 import org.eclipse.jetty.http.HttpVersion;
50 import org.eclipse.jetty.http.MetaData;
51 import org.eclipse.jetty.server.HttpChannel;
52 import org.eclipse.jetty.server.Request;
53 import org.junit.jupiter.api.BeforeEach;
54 import org.junit.jupiter.api.Test;
55 import org.mockito.Answers;
56 import org.mockito.ArgumentCaptor;
57 import org.mockito.Mock;
58 import org.openhab.core.config.core.Configuration;
59 import org.openhab.core.library.types.DateTimeType;
60 import org.openhab.core.library.types.DecimalType;
61 import org.openhab.core.library.types.OnOffType;
62 import org.openhab.core.library.types.QuantityType;
63 import org.openhab.core.library.types.StringType;
64 import org.openhab.core.library.unit.ImperialUnits;
65 import org.openhab.core.library.unit.Units;
66 import org.openhab.core.thing.Channel;
67 import org.openhab.core.thing.ChannelUID;
68 import org.openhab.core.thing.Thing;
69 import org.openhab.core.thing.ThingStatus;
70 import org.openhab.core.thing.ThingUID;
71 import org.openhab.core.thing.binding.ThingHandlerCallback;
72 import org.openhab.core.thing.binding.builder.ChannelBuilder;
73 import org.openhab.core.thing.binding.builder.ThingBuilder;
74 import org.openhab.core.thing.type.ChannelKind;
75 import org.openhab.core.thing.type.ChannelTypeBuilder;
76 import org.openhab.core.thing.type.ChannelTypeRegistry;
77 import org.osgi.service.http.HttpService;
78 import org.osgi.service.http.NamespaceException;
81 * @author Daniel Demus - Initial contribution
84 class WundergroundUpdateReceiverServletTest {
86 private static final String STATION_ID_1 = "abcd1234";
87 private static final String STATION_ID_2 = "1234abcd";
88 private static final String REQ_STATION_ID = "dfggger";
89 private static final ThingUID TEST_THING_UID = new ThingUID(
90 WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, "test-receiver");
92 private @Mock HttpService httpService;
93 private @Mock ChannelTypeRegistry channelTypeRegistry;
94 private @Mock WundergroundUpdateReceiverDiscoveryService discoveryService;
102 void theServletIsActiveAfterTheFirstHandlerIsAdded() throws ServletException, NamespaceException {
104 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
105 WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class);
106 when(handler.getStationId()).thenReturn(STATION_ID_1);
109 sut.addHandler(handler);
112 verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
113 assertThat(sut.isActive(), is(true));
117 void theServletIsInactiveAfterTheLastHandlerIsRemovedAndBackgroundDiscoveryIsDisabled()
118 throws ServletException, NamespaceException {
120 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
121 WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class);
122 when(handler.getStationId()).thenReturn(STATION_ID_1);
123 when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(false);
126 sut.addHandler(handler);
129 verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
130 assertThat(sut.isActive(), is(true));
133 sut.removeHandler(handler.getStationId());
136 verify(httpService).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL);
137 assertThat(sut.isActive(), is(false));
141 void theServletIsActiveAfterTheLastHandlerIsRemovedButBackgroundDiscoveryIsEnabled()
142 throws ServletException, NamespaceException {
144 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
145 WundergroundUpdateReceiverHandler handler = mock(WundergroundUpdateReceiverHandler.class);
146 when(handler.getStationId()).thenReturn(STATION_ID_1);
147 when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(true);
150 sut.addHandler(handler);
153 verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
154 assertThat(sut.isActive(), is(true));
157 sut.removeHandler(handler.getStationId());
160 verify(httpService, never()).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL);
161 assertThat(sut.isActive(), is(true));
165 void onDisposeAllHandlersAreRemovedAndServletIsInactive() throws ServletException, NamespaceException {
167 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
168 WundergroundUpdateReceiverHandler handler1 = mock(WundergroundUpdateReceiverHandler.class);
169 when(handler1.getStationId()).thenReturn(STATION_ID_1);
170 WundergroundUpdateReceiverHandler handler2 = mock(WundergroundUpdateReceiverHandler.class);
171 when(handler2.getStationId()).thenReturn(STATION_ID_2);
174 sut.addHandler(handler1);
175 sut.addHandler(handler2);
178 verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
179 assertThat(sut.isActive(), is(true));
185 verify(httpService, times(2)).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL);
186 assertThat(sut.isActive(), is(false));
190 void OnDisposeAllHandlersAreRemovedAndServletIsInactiveEvenThoughBackgroundDiscoveryIsEnabled()
191 throws ServletException, NamespaceException {
193 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
194 WundergroundUpdateReceiverHandler handler1 = mock(WundergroundUpdateReceiverHandler.class);
195 when(handler1.getStationId()).thenReturn(STATION_ID_1);
196 WundergroundUpdateReceiverHandler handler2 = mock(WundergroundUpdateReceiverHandler.class);
197 when(handler2.getStationId()).thenReturn(STATION_ID_2);
198 when(discoveryService.isBackgroundDiscoveryEnabled()).thenReturn(true);
201 sut.addHandler(handler1);
202 sut.addHandler(handler2);
205 verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
206 assertThat(sut.isActive(), is(true));
212 verify(httpService).unregister(WundergroundUpdateReceiverServlet.SERVLET_URL);
213 assertThat(sut.isActive(), is(false));
217 void changedStationIdPropagatesToHandlerKey() throws ServletException, NamespaceException {
219 Thing thing = mock(Thing.class);
220 when(thing.getUID()).thenReturn(TEST_THING_UID);
221 when(thing.getConfiguration()).thenReturn(new Configuration(
222 Map.of(WundergroundUpdateReceiverBindingConstants.REPRESENTATION_PROPERTY, STATION_ID_1)));
223 when(thing.getStatus()).thenReturn(ThingStatus.ONLINE);
224 when(this.channelTypeRegistry.getChannelType(LAST_RECEIVED_DATETIME_CHANNELTYPEUID))
225 .thenReturn(ChannelTypeBuilder.state(LAST_RECEIVED_DATETIME_CHANNELTYPEUID, "Label", "String").build());
226 when(this.channelTypeRegistry.getChannelType(DATEUTC_DATETIME_CHANNELTYPEUID))
227 .thenReturn(ChannelTypeBuilder.state(DATEUTC_DATETIME_CHANNELTYPEUID, "Label", "DateTime").build());
228 when(this.channelTypeRegistry.getChannelType(LAST_QUERY_STATE_CHANNELTYPEUID))
229 .thenReturn(ChannelTypeBuilder.state(LAST_QUERY_STATE_CHANNELTYPEUID, "Label", "String").build());
230 when(this.channelTypeRegistry.getChannelType(LAST_QUERY_TRIGGER_CHANNELTYPEUID))
231 .thenReturn(ChannelTypeBuilder.trigger(LAST_QUERY_TRIGGER_CHANNELTYPEUID, "Label").build());
232 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
233 WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(thing, sut, discoveryService,
234 new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry);
235 ThingHandlerCallback mockCallback = mock(ThingHandlerCallback.class);
236 handler.setCallback(mockCallback);
239 handler.initialize();
242 verify(httpService).registerServlet(eq(WundergroundUpdateReceiverServlet.SERVLET_URL), eq(sut), any(), any());
243 assertThat(sut.isActive(), is(true));
244 assertThat(sut.getStationIds(), hasItems(STATION_ID_1));
247 handler.handleConfigurationUpdate(
248 Map.of(WundergroundUpdateReceiverBindingConstants.REPRESENTATION_PROPERTY, STATION_ID_2));
251 assertThat(sut.isActive(), is(true));
252 ArgumentCaptor<Thing> thingArg = ArgumentCaptor.forClass(Thing.class);
253 verify(mockCallback).configurationUpdated(thingArg.capture());
254 assertThat(thingArg.getValue().getConfiguration().getProperties()
255 .get(WundergroundUpdateReceiverBindingConstants.REPRESENTATION_PROPERTY), is(STATION_ID_2));
259 void aGetRequestIsCorrectlyParsed() throws IOException {
261 ThingUID testThingUID = new ThingUID(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER,
263 final String queryString = "ID=dfggger&PASSWORD=XXXXXX&tempf=26.1&humidity=74&dewptf=18.9&windchillf=26.1&winddir=14&windspeedmph=1.34&windgustmph=2.46&rainin=0.00&dailyrainin=0.00&weeklyrainin=0.00&monthlyrainin=0.08&yearlyrainin=3.06&solarradiation=42.24&UV=1&indoortempf=69.3&indoorhumidity=32&baromin=30.39&AqNOX=21&lowbatt=1&dateutc=2021-02-07%2014:04:03&softwaretype=WH2600%20V2.2.8&action=updateraw&realtime=1&rtfreq=5";
264 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
265 List<Channel> channels = List.of(
267 .create(new ChannelUID(testThingUID, METADATA_GROUP,
268 WundergroundUpdateReceiverBindingConstants.DATEUTC), "String")
269 .withKind(ChannelKind.STATE).build(),
271 .create(new ChannelUID(testThingUID, METADATA_GROUP,
272 WundergroundUpdateReceiverBindingConstants.REALTIME_FREQUENCY), "Number")
273 .withKind(ChannelKind.STATE).build(),
275 .create(new ChannelUID(testThingUID, METADATA_GROUP,
276 WundergroundUpdateReceiverBindingConstants.LOW_BATTERY), "Switch")
277 .withKind(ChannelKind.STATE).build(),
279 .create(new ChannelUID(testThingUID, WIND_GROUP,
280 WundergroundUpdateReceiverBindingConstants.WIND_DIRECTION), "Number:Angle")
281 .withKind(ChannelKind.STATE).build(),
283 .create(new ChannelUID(testThingUID, WIND_GROUP,
284 WundergroundUpdateReceiverBindingConstants.WIND_SPEED), "Number:Speed")
285 .withKind(ChannelKind.STATE).build(),
287 .create(new ChannelUID(testThingUID, WIND_GROUP,
288 WundergroundUpdateReceiverBindingConstants.GUST_SPEED), "Number:Speed")
289 .withKind(ChannelKind.STATE).build(),
291 .create(new ChannelUID(testThingUID, TEMPERATURE_GROUP,
292 WundergroundUpdateReceiverBindingConstants.TEMPERATURE), "Number:Temperature")
293 .withKind(ChannelKind.STATE).build(),
295 .create(new ChannelUID(testThingUID, RAIN_GROUP,
296 WundergroundUpdateReceiverBindingConstants.RAIN_IN), "Number:Length")
297 .withKind(ChannelKind.STATE).build(),
299 .create(new ChannelUID(testThingUID, SUNLIGHT_GROUP,
300 WundergroundUpdateReceiverBindingConstants.SOLAR_RADIATION), "Number:Intensity")
301 .withKind(ChannelKind.STATE).build(),
303 .create(new ChannelUID(testThingUID, SUNLIGHT_GROUP,
304 WundergroundUpdateReceiverBindingConstants.UV), "Number")
305 .withKind(ChannelKind.STATE).build(),
307 .create(new ChannelUID(testThingUID, PRESSURE_GROUP,
308 WundergroundUpdateReceiverBindingConstants.BAROM_IN), "Number:Pressure")
309 .withKind(ChannelKind.STATE).build(),
311 .create(new ChannelUID(testThingUID, HUMIDITY_GROUP,
312 WundergroundUpdateReceiverBindingConstants.DEWPOINT), "Number:Temperature")
313 .withKind(ChannelKind.STATE).build(),
315 .create(new ChannelUID(testThingUID, HUMIDITY_GROUP,
316 WundergroundUpdateReceiverBindingConstants.HUMIDITY), "Number:Dimensionless")
317 .withKind(ChannelKind.STATE).build(),
319 .create(new ChannelUID(testThingUID, POLLUTION_GROUP,
320 WundergroundUpdateReceiverBindingConstants.AQ_NOX), "Number:Dimensionless")
321 .withKind(ChannelKind.STATE).build(),
323 .create(new ChannelUID(testThingUID, METADATA_GROUP,
324 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER), "StringType")
325 .withKind(ChannelKind.TRIGGER).build());
327 Configuration config = new Configuration(Map.of("stationId", REQ_STATION_ID));
328 Thing testThing = ThingBuilder
329 .create(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, testThingUID)
330 .withChannels(channels).withConfiguration(config).build();
331 ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
332 WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(testThing, sut,
333 discoveryService, new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry);
334 handler.setCallback(callback);
335 handler.initialize();
337 HttpChannel httpChannel = mock(HttpChannel.class);
338 MetaData.Request request = new MetaData.Request("GET",
339 new HttpURI("http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + queryString),
340 HttpVersion.HTTP_1_1, new HttpFields());
341 Request req = new Request(httpChannel, null);
342 req.setMetaData(request);
345 sut.doGet(req, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
348 verify(callback).stateUpdated(
349 new ChannelUID(TEST_THING_UID, METADATA_GROUP, WundergroundUpdateReceiverBindingConstants.DATEUTC),
350 StringType.valueOf("2021-02-07 14:04:03"));
351 verify(callback).stateUpdated(
352 new ChannelUID(TEST_THING_UID, METADATA_GROUP, WundergroundUpdateReceiverBindingConstants.LOW_BATTERY),
354 verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
355 WundergroundUpdateReceiverBindingConstants.REALTIME_FREQUENCY), new DecimalType(5));
356 verify(callback).stateUpdated(
357 new ChannelUID(TEST_THING_UID, WIND_GROUP, WundergroundUpdateReceiverBindingConstants.WIND_DIRECTION),
358 new QuantityType<>(14, Units.DEGREE_ANGLE));
359 verify(callback).stateUpdated(
360 new ChannelUID(TEST_THING_UID, WIND_GROUP, WundergroundUpdateReceiverBindingConstants.WIND_SPEED),
361 new QuantityType<>(1.34, ImperialUnits.MILES_PER_HOUR));
362 verify(callback).stateUpdated(
363 new ChannelUID(TEST_THING_UID, WIND_GROUP, WundergroundUpdateReceiverBindingConstants.GUST_SPEED),
364 new QuantityType<>(2.46, ImperialUnits.MILES_PER_HOUR));
365 verify(callback).stateUpdated(
366 new ChannelUID(TEST_THING_UID, TEMPERATURE_GROUP,
367 WundergroundUpdateReceiverBindingConstants.TEMPERATURE),
368 new QuantityType<>(26.1, ImperialUnits.FAHRENHEIT));
369 verify(callback).stateUpdated(
370 new ChannelUID(TEST_THING_UID, RAIN_GROUP, WundergroundUpdateReceiverBindingConstants.RAIN_IN),
371 new QuantityType<>(0, ImperialUnits.INCH));
372 verify(callback).stateUpdated(
373 new ChannelUID(TEST_THING_UID, SUNLIGHT_GROUP,
374 WundergroundUpdateReceiverBindingConstants.SOLAR_RADIATION),
375 new QuantityType<>(42.24, Units.IRRADIANCE));
376 verify(callback).stateUpdated(
377 new ChannelUID(TEST_THING_UID, SUNLIGHT_GROUP, WundergroundUpdateReceiverBindingConstants.UV),
379 verify(callback).stateUpdated(
380 new ChannelUID(TEST_THING_UID, PRESSURE_GROUP, WundergroundUpdateReceiverBindingConstants.BAROM_IN),
381 new QuantityType<>(30.39, ImperialUnits.INCH_OF_MERCURY));
382 verify(callback).stateUpdated(
383 new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, WundergroundUpdateReceiverBindingConstants.DEWPOINT),
384 new QuantityType<>(18.9, ImperialUnits.FAHRENHEIT));
385 verify(callback).stateUpdated(
386 new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, WundergroundUpdateReceiverBindingConstants.HUMIDITY),
387 new QuantityType<>(74, Units.PERCENT));
388 verify(callback).stateUpdated(
389 new ChannelUID(TEST_THING_UID, POLLUTION_GROUP, WundergroundUpdateReceiverBindingConstants.AQ_NOX),
390 new QuantityType<>(21, Units.PARTS_PER_BILLION));
391 verify(callback, never()).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
392 WundergroundUpdateReceiverBindingConstants.SOFTWARE_TYPE), StringType.valueOf("WH2600 V2.2.8"));
393 verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
394 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_STATE), StringType.valueOf(queryString));
395 verify(callback).stateUpdated(eq(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
396 WundergroundUpdateReceiverBindingConstants.LAST_RECEIVED)), any(DateTimeType.class));
397 verify(callback).channelTriggered(testThing, new ChannelUID(TEST_THING_UID, METADATA_GROUP,
398 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER), queryString);
402 void aGetRequestWithIndexedParametresAreCorrectlyParsed() throws IOException {
404 ThingUID testThingUID = new ThingUID(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER,
406 final String queryString = "ID=dfggger&PASSWORD=XXXXXX&temp1f=26.1&humidity=74&temp2f=25.1&lowbatt=1&soilmoisture1=78&soilmoisture2=73&dateutc=2021-02-07%2014:04:03&softwaretype=WH2600%20V2.2.8&action=updateraw&realtime=1&rtfreq=5";
407 WundergroundUpdateReceiverServlet sut = new WundergroundUpdateReceiverServlet(httpService, discoveryService);
408 List<Channel> channels = List.of(
410 .create(new ChannelUID(testThingUID, METADATA_GROUP,
411 WundergroundUpdateReceiverBindingConstants.DATEUTC), "String")
412 .withKind(ChannelKind.STATE).build(),
414 .create(new ChannelUID(testThingUID, METADATA_GROUP,
415 WundergroundUpdateReceiverBindingConstants.REALTIME_FREQUENCY), "Number")
416 .withKind(ChannelKind.STATE).build(),
418 .create(new ChannelUID(testThingUID, METADATA_GROUP,
419 WundergroundUpdateReceiverBindingConstants.LOW_BATTERY), "Switch")
420 .withKind(ChannelKind.STATE).build(),
421 ChannelBuilder.create(new ChannelUID(testThingUID, TEMPERATURE_GROUP, "temp1f"), "Number:Temperature")
422 .withKind(ChannelKind.STATE).build(),
423 ChannelBuilder.create(new ChannelUID(testThingUID, TEMPERATURE_GROUP, "temp2f"), "Number:Temperature")
424 .withKind(ChannelKind.STATE).build(),
426 .create(new ChannelUID(testThingUID, HUMIDITY_GROUP, "soilmoisture1"), "Number:Dimensionless")
427 .withKind(ChannelKind.STATE).build(),
429 .create(new ChannelUID(testThingUID, HUMIDITY_GROUP, "soilmoisture2"), "Number:Dimensionless")
430 .withKind(ChannelKind.STATE).build(),
432 .create(new ChannelUID(testThingUID, HUMIDITY_GROUP,
433 WundergroundUpdateReceiverBindingConstants.HUMIDITY), "Number:Dimensionless")
434 .withKind(ChannelKind.STATE).build(),
436 .create(new ChannelUID(testThingUID, METADATA_GROUP,
437 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER), "StringType")
438 .withKind(ChannelKind.TRIGGER).build());
440 Configuration config = new Configuration(Map.of("stationId", REQ_STATION_ID));
441 Thing testThing = ThingBuilder
442 .create(WundergroundUpdateReceiverBindingConstants.THING_TYPE_UPDATE_RECEIVER, testThingUID)
443 .withChannels(channels).withConfiguration(config).build();
444 ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
445 WundergroundUpdateReceiverHandler handler = new WundergroundUpdateReceiverHandler(testThing, sut,
446 discoveryService, new WundergroundUpdateReceiverUnknownChannelTypeProvider(), channelTypeRegistry);
447 handler.setCallback(callback);
448 handler.initialize();
450 HttpChannel httpChannel = mock(HttpChannel.class);
451 MetaData.Request request = new MetaData.Request("GET",
452 new HttpURI("http://localhost" + WundergroundUpdateReceiverServlet.SERVLET_URL + "?" + queryString),
453 HttpVersion.HTTP_1_1, new HttpFields());
454 Request req = new Request(httpChannel, null);
455 req.setMetaData(request);
458 sut.doGet(req, mock(HttpServletResponse.class, Answers.RETURNS_MOCKS));
461 verify(callback).stateUpdated(
462 new ChannelUID(TEST_THING_UID, METADATA_GROUP, WundergroundUpdateReceiverBindingConstants.DATEUTC),
463 StringType.valueOf("2021-02-07 14:04:03"));
464 verify(callback).stateUpdated(
465 new ChannelUID(TEST_THING_UID, METADATA_GROUP, WundergroundUpdateReceiverBindingConstants.LOW_BATTERY),
467 verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
468 WundergroundUpdateReceiverBindingConstants.REALTIME_FREQUENCY), new DecimalType(5));
469 verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, TEMPERATURE_GROUP, "temp1f"),
470 new QuantityType<>(26.1, ImperialUnits.FAHRENHEIT));
471 verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, TEMPERATURE_GROUP, "temp2f"),
472 new QuantityType<>(25.1, ImperialUnits.FAHRENHEIT));
473 verify(callback).stateUpdated(
474 new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, WundergroundUpdateReceiverBindingConstants.HUMIDITY),
475 new QuantityType<>(74, Units.PERCENT));
476 verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, "soilmoisture1"),
477 new QuantityType<>(78, Units.PERCENT));
478 verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, HUMIDITY_GROUP, "soilmoisture2"),
479 new QuantityType<>(73, Units.PERCENT));
480 verify(callback, never()).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
481 WundergroundUpdateReceiverBindingConstants.SOFTWARE_TYPE), StringType.valueOf("WH2600 V2.2.8"));
482 verify(callback).stateUpdated(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
483 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_STATE), StringType.valueOf(queryString));
484 verify(callback).stateUpdated(eq(new ChannelUID(TEST_THING_UID, METADATA_GROUP,
485 WundergroundUpdateReceiverBindingConstants.LAST_RECEIVED)), any(DateTimeType.class));
486 verify(callback).channelTriggered(testThing, new ChannelUID(TEST_THING_UID, METADATA_GROUP,
487 WundergroundUpdateReceiverBindingConstants.LAST_QUERY_TRIGGER), queryString);