[openweathermap] Fix NullPointerException (#17189)

* Fix compilation warnings

Signed-off-by: Leo Siepel <leosiepel@gmail.com>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
lsiepel 2024-09-08 21:26:59 +02:00 committed by Ciprian Pascu
parent 81bada78a6
commit c8b3834461
19 changed files with 503 additions and 30 deletions

View File

@ -45,6 +45,8 @@ public class OpenWeatherMapBindingConstants {
public static final String CONFIG_API_KEY = "apikey";
public static final String CONFIG_LANGUAGE = "language";
public static final String CONFIG_LOCATION = "location";
public static final String CONFIG_HISTORY_DAYS = "historyDay";
public static final String CONFIG_API_VERSION = "apiVersion";
// Channel group types
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_STATION = new ChannelGroupTypeUID(BINDING_ID, "station");

View File

@ -16,10 +16,10 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
/**
* The {@link OpenWeatherMapAPIConfiguration} is the class used to match the {@link OpenWeatherMapAPIHandler}s
* The {@link OpenWeatherMapAPIConfiguration} is the class used to match the
* {@link org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler}s
* configuration.
*
* @author Christoph Weitkamp - Initial contribution
@ -32,7 +32,7 @@ public class OpenWeatherMapAPIConfiguration {
"lt", "mk", "nl", "no", "pl", "pt", "pt_br", "ro", "ru", "se", "sv", "sk", "sl", "sr", "th", "tr", "uk",
"ua", "vi", "zh_cn", "zh_tw", "zu");
public @Nullable String apikey;
public String apikey = "";
public int refreshInterval;
public @Nullable String language;
public String apiVersion = "2.5";

View File

@ -13,11 +13,10 @@
package org.openhab.binding.openweathermap.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAirPollutionHandler;
/**
* The {@link OpenWeatherMapAirPollutionConfiguration} is the class used to match the
* {@link OpenWeatherMapAirPollutionHandler}s configuration.
* {@link org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAirPollutionHandler}s configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/

View File

@ -13,10 +13,10 @@
package org.openhab.binding.openweathermap.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler;
/**
* The {@link OpenWeatherMapLocationConfiguration} is the class used to match the {@link AbstractOpenWeatherMapHandler}s
* The {@link OpenWeatherMapLocationConfiguration} is the class used to match the
* {@link org.openhab.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler}s
* configuration.
*
* @author Christoph Weitkamp - Initial contribution

View File

@ -13,10 +13,10 @@
package org.openhab.binding.openweathermap.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapOneCallHandler;
/**
* The {@link OpenWeatherMapOneCallConfiguration} is the class used to match the {@link OpenWeatherMapOneCallHandler}s
* The {@link OpenWeatherMapOneCallConfiguration} is the class used to match the
* {@link org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapOneCallHandler}s
* configuration.
*
* @author Wolfgang Klimt - Initial contribution

View File

@ -13,11 +13,10 @@
package org.openhab.binding.openweathermap.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapWeatherAndForecastHandler;
/**
* The {@link OpenWeatherMapWeatherAndForecastConfiguration} is the class used to match the
* {@link OpenWeatherMapWeatherAndForecastHandler}s configuration.
* {@link org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapWeatherAndForecastHandler}s configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/

View File

@ -314,11 +314,10 @@ public class OpenWeatherMapConnection {
Map<String, String> params = new HashMap<>();
// API key (see https://openweathermap.org/appid)
String apikey = config.apikey;
if (apikey == null || (apikey = apikey.trim()).isEmpty()) {
if (config.apikey.isBlank()) {
throw new ConfigurationException("@text/offline.conf-error-missing-apikey");
}
params.put(PARAM_APPID, apikey);
params.put(PARAM_APPID, config.apikey);
// Units format (see https://openweathermap.org/current#data)
params.put(PARAM_UNITS, "metric");
@ -380,6 +379,9 @@ public class OpenWeatherMapConnection {
throw new ConfigurationException(errorMessage);
case TOO_MANY_REQUESTS_429:
// TODO disable refresh job temporarily (see https://openweathermap.org/appid#Accesslimitation)
errorMessage = getErrorMessage(content);
logger.debug("OpenWeatherMap server responded with status code {}: {}", httpStatus, errorMessage);
throw new CommunicationException(errorMessage);
default:
errorMessage = getErrorMessage(content);
logger.debug("OpenWeatherMap server responded with status code {}: {}", httpStatus, errorMessage);

View File

@ -32,6 +32,7 @@ public class OpenWeatherMapOneCallHistAPIData {
private String timezone;
@SerializedName("timezone_offset")
private int timezoneOffset;
private Current[] data;
private Current current;
private List<Hourly> hourly = null;
@ -68,7 +69,11 @@ public class OpenWeatherMapOneCallHistAPIData {
}
public Current getCurrent() {
if (current != null) {
return current;
} else {
return data[0];
}
}
public void setCurrent(Current current) {

View File

@ -36,6 +36,8 @@ public class Precipitation {
}
public Double getVolume() {
Double oneHour = this.oneHour;
Double threeHours = this.threeHours;
return oneHour != null ? oneHour : threeHours != null ? threeHours / 3 : 0;
}
}

View File

@ -83,11 +83,10 @@ public abstract class AbstractOpenWeatherMapHandler extends BaseThingHandler {
public void initialize() {
OpenWeatherMapLocationConfiguration config = getConfigAs(OpenWeatherMapLocationConfiguration.class);
boolean configValid = true;
if (config.location == null || config.location.trim().isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-missing-location");
configValid = false;
return;
}
try {
@ -96,14 +95,11 @@ public abstract class AbstractOpenWeatherMapHandler extends BaseThingHandler {
logger.warn("Error parsing 'location' parameter: {}", e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-parsing-location");
location = null;
configValid = false;
return;
}
if (configValid) {
updateStatus(ThingStatus.UNKNOWN);
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {

View File

@ -127,7 +127,12 @@ public class OpenWeatherMapAirPollutionHandler extends AbstractOpenWeatherMapHan
@Override
protected void updateChannel(ChannelUID channelUID) {
switch (channelUID.getGroupId()) {
String channelGroupId = channelUID.getGroupId();
if (channelGroupId == null) {
logger.debug("Cannot update {} as it has no GroupId", channelUID);
return;
}
switch (channelGroupId) {
case CHANNEL_GROUP_CURRENT_AIR_POLLUTION:
updateCurrentAirPollutionChannel(channelUID);
break;

View File

@ -239,6 +239,10 @@ public class OpenWeatherMapOneCallHandler extends AbstractOpenWeatherMapHandler
@Override
protected void updateChannel(ChannelUID channelUID) {
String channelGroupId = channelUID.getGroupId();
if (channelGroupId == null) {
logger.debug("Cannot update {} as it has no GroupId", channelUID);
return;
}
logger.debug("OneCallHandler: updateChannel {}, groupID {}", channelUID, channelGroupId);
switch (channelGroupId) {
case CHANNEL_GROUP_ONECALL_CURRENT:

View File

@ -17,6 +17,7 @@ import static org.openhab.core.library.unit.MetricPrefix.*;
import static org.openhab.core.library.unit.SIUnits.*;
import static org.openhab.core.library.unit.Units.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -103,6 +104,10 @@ public class OpenWeatherMapOneCallHistoryHandler extends AbstractOpenWeatherMapH
@Override
protected void updateChannel(ChannelUID channelUID) {
String channelGroupId = channelUID.getGroupId();
if (channelGroupId == null) {
logger.debug("Cannot update {} as it has no GroupId", channelUID);
return;
}
switch (channelGroupId) {
case CHANNEL_GROUP_ONECALL_HISTORY:
updateHistoryCurrentChannel(channelUID);
@ -233,9 +238,17 @@ public class OpenWeatherMapOneCallHistoryHandler extends AbstractOpenWeatherMapH
String channelGroupId = channelUID.getGroupId();
logger.debug("Updating hourly history data for channel {}, group {}, count {}", channelId, channelGroupId,
count);
OpenWeatherMapOneCallHistAPIData localWeatherData = weatherData;
if (localWeatherData != null && localWeatherData.getHourly().size() > count) {
Hourly historyData = localWeatherData.getHourly().get(count);
List<Hourly> hourly;
if (localWeatherData == null || (hourly = localWeatherData.getHourly()) == null) {
logger.warn("No weather data available for channel {}, possible cause: api v3.0 does not support it",
channelUID);
return;
}
if (hourly.size() > count) {
Hourly historyData = hourly.get(count);
State state = UnDefType.UNDEF;
switch (channelId) {
case CHANNEL_TIME_STAMP:

View File

@ -179,11 +179,12 @@ public class OpenWeatherMapWeatherAndForecastHandler extends AbstractOpenWeather
editConfig.put(CONFIG_FORECAST_DAYS, 0);
updateConfiguration(editConfig);
logger.debug("Removing daily forecast channel groups.");
List<Channel> channels = getThing().getChannels().stream()
.filter(c -> CHANNEL_GROUP_FORECAST_TODAY.equals(c.getUID().getGroupId())
|| CHANNEL_GROUP_FORECAST_TOMORROW.equals(c.getUID().getGroupId())
|| c.getUID().getGroupId().startsWith(CHANNEL_GROUP_DAILY_FORECAST_PREFIX))
.collect(Collectors.toList());
List<Channel> channels = getThing().getChannels().stream().filter(c -> {
String groupId = c.getUID().getGroupId();
return CHANNEL_GROUP_FORECAST_TODAY.equals(groupId)
|| CHANNEL_GROUP_FORECAST_TOMORROW.equals(groupId)
|| (groupId != null && groupId.startsWith(CHANNEL_GROUP_DAILY_FORECAST_PREFIX));
}).collect(Collectors.toList());
updateThing(editThing().withoutChannels(channels).build());
} else {
throw e;
@ -200,6 +201,10 @@ public class OpenWeatherMapWeatherAndForecastHandler extends AbstractOpenWeather
@Override
protected void updateChannel(ChannelUID channelUID) {
String channelGroupId = channelUID.getGroupId();
if (channelGroupId == null) {
logger.debug("Cannot update {} as it has no GroupId", channelUID);
return;
}
switch (channelGroupId) {
case CHANNEL_GROUP_STATION:
case CHANNEL_GROUP_CURRENT_WEATHER:

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.Gson;
/**
* Utility class for working with test data in unit tests
*
* @author Leo Siepel - Initial contribution
*/
@NonNullByDefault
public class DataUtil {
@SuppressWarnings("null")
public static Reader openDataReader(String fileName) throws FileNotFoundException {
String filePath = "src/test/resources/" + fileName;
InputStream inputStream = new FileInputStream(filePath);
return new InputStreamReader(inputStream, StandardCharsets.UTF_8);
}
public static <T> T fromJson(String fileName, Type typeOfT) throws IOException {
try (Reader reader = openDataReader(fileName)) {
return new Gson().fromJson(reader, typeOfT);
}
}
@SuppressWarnings("null")
public static String fromFile(String fileName) throws IOException {
try (Reader reader = openDataReader(fileName)) {
return new BufferedReader(reader).lines().parallel().collect(Collectors.joining("\n"));
}
}
}

View File

@ -0,0 +1,74 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.mockito.Mockito;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.types.State;
/**
* Utility class for working with test objects in unit tests
*
* @author Leo Siepel - Initial contribution
*/
@NonNullByDefault
public class TestObjectsUtil {
public static Configuration createConfig(boolean returnValid, @Nullable String apiVersion) {
final Configuration config = new Configuration();
if (returnValid) {
config.put(OpenWeatherMapBindingConstants.CONFIG_LOCATION, "51.0435,7.2865");
config.put(OpenWeatherMapBindingConstants.CONFIG_HISTORY_DAYS, 1);
if (apiVersion != null) {
config.put(OpenWeatherMapBindingConstants.CONFIG_API_VERSION, apiVersion);
}
}
return config;
}
public static Thing mockThing(Configuration configuration) {
final Thing thing = mock(Thing.class);
when(thing.getUID()).thenReturn(new ThingUID(OpenWeatherMapBindingConstants.BINDING_ID, "owm-test-thing"));
when(thing.getConfiguration()).thenReturn(configuration);
return thing;
}
public static Channel mockChannel(final ThingUID thingId, final String channelId) {
final Channel channel = Mockito.mock(Channel.class);
when(channel.getUID()).thenReturn(new ChannelUID(thingId, channelId));
when(channel.getKind()).thenReturn(ChannelKind.STATE);
return channel;
}
public static State getState(final double input, Unit<?> unit) {
return new QuantityType<>(input, unit);
}
public static State getState(final int input, Unit<?> unit) {
return new QuantityType<>(input, unit);
}
}

View File

@ -0,0 +1,230 @@
/**
* Copyright (c) 2010-2024 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.handler;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
import static org.openhab.binding.openweathermap.internal.TestObjectsUtil.*;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.binding.openweathermap.internal.DataUtil;
import org.openhab.binding.openweathermap.internal.TestObjectsUtil;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapOneCallHistAPIData;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.types.State;
/**
* @author Leo Siepel - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapOneCallHistoryHandlerTest {
private ThingHandlerCallback callback = mock(ThingHandlerCallback.class);
private static Thing mockThing(Configuration configuration) {
final Thing thing = TestObjectsUtil.mockThing(configuration);
final List<Channel> channelList = Arrays.asList(
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_STATION_LOCATION), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_TIME_STAMP), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_SUNRISE), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_SUNSET), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_CONDITION), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_CONDITION_ID), //
// CHANNEL_CONDITION_ICON was left out of this test
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_CONDITION_ICON_ID), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_TEMPERATURE), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_APPARENT_TEMPERATURE), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_PRESSURE), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_HUMIDITY), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_DEW_POINT), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_WIND_SPEED), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_WIND_DIRECTION), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_GUST_SPEED), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_CLOUDINESS), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_UVINDEX), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_RAIN), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_SNOW), //
mockChannel(thing.getUID(), CHANNEL_GROUP_ONECALL_HISTORY + "#" + CHANNEL_VISIBILITY) //
);
when(thing.getChannels()).thenReturn(channelList);
return thing;
}
private static OpenWeatherMapOneCallHistoryHandler createAndInitHandler(final ThingHandlerCallback callback,
final Thing thing) {
TimeZoneProvider timeZoneProvider = mock(TimeZoneProvider.class);
when(timeZoneProvider.getTimeZone()).thenReturn(ZoneId.of("UTC"));
final OpenWeatherMapOneCallHistoryHandler handler = spy(
new OpenWeatherMapOneCallHistoryHandler(thing, timeZoneProvider));
when(callback.isChannelLinked(any())).thenReturn(true);
handler.setCallback(callback);
handler.initialize();
return handler;
}
private static void assertGroupChannelStateSet(ThingHandlerCallback callback, ThingUID uid, String channel,
State state) {
verify(callback).stateUpdated(new ChannelUID(uid, CHANNEL_GROUP_ONECALL_HISTORY + "#" + channel), state);
}
@Test
public void testInvalidConfiguration() {
// Arrange
final Configuration configuration = createConfig(false, null);
final Thing thing = mockThing(configuration);
final OpenWeatherMapOneCallHistoryHandler handler = createAndInitHandler(callback, thing);
try {
verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.OFFLINE)
&& arg.getStatusDetail().equals(ThingStatusDetail.CONFIGURATION_ERROR)));
} finally {
handler.dispose();
}
}
@Test
public void testCurrentWithResponseMessageV30() throws IOException {
// Arrange
final Configuration configuration = createConfig(true, "3.0");
final Thing thing = mockThing(configuration);
final OpenWeatherMapOneCallHistoryHandler handler = createAndInitHandler(callback, thing);
OpenWeatherMapOneCallHistAPIData data = DataUtil.fromJson("history_v3_0.json",
OpenWeatherMapOneCallHistAPIData.class);
OpenWeatherMapConnection connectionMock = mock(OpenWeatherMapConnection.class);
when(connectionMock.getOneCallHistAPIData(handler.location,
((BigDecimal) configuration.get(CONFIG_HISTORY_DAYS)).intValue())).thenReturn(data);
// Act
handler.updateData(connectionMock);
// Assert
ThingUID uid = thing.getUID();
try {
verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
verify(callback, atLeast(2)).statusUpdated(eq(thing),
argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
assertGroupChannelStateSet(callback, uid, CHANNEL_STATION_LOCATION, new PointType("52.2297,21.0122"));
assertGroupChannelStateSet(callback, uid, CHANNEL_TIME_STAMP,
new DateTimeType("2022-02-26T15:22:56.000+0000"));
assertGroupChannelStateSet(callback, uid, CHANNEL_SUNRISE,
new DateTimeType("2022-02-26T05:29:21.000+0000"));
assertGroupChannelStateSet(callback, uid, CHANNEL_SUNSET, new DateTimeType("2022-02-26T16:08:47.000+0000"));
assertGroupChannelStateSet(callback, uid, CHANNEL_CONDITION, new StringType("clear sky"));
assertGroupChannelStateSet(callback, uid, CHANNEL_CONDITION_ID, new StringType("800"));
// CHANNEL_CONDITION_ICON was left out of this test
assertGroupChannelStateSet(callback, uid, CHANNEL_CONDITION_ICON_ID, new StringType("01d"));
assertGroupChannelStateSet(callback, uid, CHANNEL_TEMPERATURE, getState(279.13, SIUnits.CELSIUS));
assertGroupChannelStateSet(callback, uid, CHANNEL_APPARENT_TEMPERATURE, getState(276.44, SIUnits.CELSIUS));
assertGroupChannelStateSet(callback, uid, CHANNEL_PRESSURE, getState(102900, SIUnits.PASCAL));
assertGroupChannelStateSet(callback, uid, CHANNEL_HUMIDITY, getState(64, Units.PERCENT));
assertGroupChannelStateSet(callback, uid, CHANNEL_DEW_POINT, getState(272.88, SIUnits.CELSIUS));
assertGroupChannelStateSet(callback, uid, CHANNEL_WIND_SPEED, getState(3.6, Units.METRE_PER_SECOND));
assertGroupChannelStateSet(callback, uid, CHANNEL_WIND_DIRECTION, getState(340, Units.DEGREE_ANGLE));
assertGroupChannelStateSet(callback, uid, CHANNEL_GUST_SPEED, getState(0, Units.METRE_PER_SECOND));
assertGroupChannelStateSet(callback, uid, CHANNEL_CLOUDINESS, getState(0, Units.PERCENT));
assertGroupChannelStateSet(callback, uid, CHANNEL_UVINDEX, new DecimalType(0.06));
assertGroupChannelStateSet(callback, uid, CHANNEL_RAIN, getState(0.0, SIUnits.METRE));
assertGroupChannelStateSet(callback, uid, CHANNEL_SNOW, getState(0.0, SIUnits.METRE));
assertGroupChannelStateSet(callback, uid, CHANNEL_VISIBILITY, getState(10000, SIUnits.METRE));
} finally {
handler.dispose();
}
}
@Test
public void testCurrentWithResponseMessageV25() throws IOException {
// Arrange
final Configuration configuration = createConfig(true, "3.0");
final Thing thing = mockThing(configuration);
final OpenWeatherMapOneCallHistoryHandler handler = createAndInitHandler(callback, thing);
OpenWeatherMapOneCallHistAPIData data = DataUtil.fromJson("history_v2_5.json",
OpenWeatherMapOneCallHistAPIData.class);
OpenWeatherMapConnection connectionMock = mock(OpenWeatherMapConnection.class);
when(connectionMock.getOneCallHistAPIData(handler.location,
((BigDecimal) configuration.get(CONFIG_HISTORY_DAYS)).intValue())).thenReturn(data);
// Act
handler.updateData(connectionMock);
// Assert
ThingUID uid = thing.getUID();
try {
verify(callback).statusUpdated(eq(thing), argThat(arg -> arg.getStatus().equals(ThingStatus.UNKNOWN)));
verify(callback, atLeast(2)).statusUpdated(eq(thing),
argThat(arg -> arg.getStatus().equals(ThingStatus.ONLINE)));
assertGroupChannelStateSet(callback, uid, CHANNEL_STATION_LOCATION, new PointType("60.99,30.9"));
assertGroupChannelStateSet(callback, uid, CHANNEL_TIME_STAMP,
new DateTimeType("2020-04-09T21:33:47.000+0000"));
assertGroupChannelStateSet(callback, uid, CHANNEL_SUNRISE,
new DateTimeType("2020-04-10T02:57:04.000+0000"));
assertGroupChannelStateSet(callback, uid, CHANNEL_SUNSET, new DateTimeType("2020-04-10T17:04:57.000+0000"));
assertGroupChannelStateSet(callback, uid, CHANNEL_CONDITION, new StringType("clear sky"));
assertGroupChannelStateSet(callback, uid, CHANNEL_CONDITION_ID, new StringType("800"));
// CHANNEL_CONDITION_ICON was left out of this test
assertGroupChannelStateSet(callback, uid, CHANNEL_CONDITION_ICON_ID, new StringType("01n"));
assertGroupChannelStateSet(callback, uid, CHANNEL_TEMPERATURE, getState(274.31, SIUnits.CELSIUS));
assertGroupChannelStateSet(callback, uid, CHANNEL_APPARENT_TEMPERATURE, getState(269.79, SIUnits.CELSIUS));
assertGroupChannelStateSet(callback, uid, CHANNEL_PRESSURE, getState(100600, SIUnits.PASCAL));
assertGroupChannelStateSet(callback, uid, CHANNEL_HUMIDITY, getState(72, Units.PERCENT));
assertGroupChannelStateSet(callback, uid, CHANNEL_DEW_POINT, getState(270.21, SIUnits.CELSIUS));
assertGroupChannelStateSet(callback, uid, CHANNEL_WIND_SPEED, getState(3, Units.METRE_PER_SECOND));
assertGroupChannelStateSet(callback, uid, CHANNEL_WIND_DIRECTION, getState(260, Units.DEGREE_ANGLE));
assertGroupChannelStateSet(callback, uid, CHANNEL_GUST_SPEED, getState(0, Units.METRE_PER_SECOND));
assertGroupChannelStateSet(callback, uid, CHANNEL_CLOUDINESS, getState(0, Units.PERCENT));
assertGroupChannelStateSet(callback, uid, CHANNEL_UVINDEX, new DecimalType(0.0));
assertGroupChannelStateSet(callback, uid, CHANNEL_RAIN, getState(0.0, SIUnits.METRE));
assertGroupChannelStateSet(callback, uid, CHANNEL_SNOW, getState(0.0, SIUnits.METRE));
assertGroupChannelStateSet(callback, uid, CHANNEL_VISIBILITY, getState(10000, SIUnits.METRE));
} finally {
handler.dispose();
}
}
}

View File

@ -0,0 +1,49 @@
{
"lat": 60.99,
"lon": 30.9,
"timezone": "Europe/Moscow",
"timezone_offset": 10800,
"current": {
"dt": 1586468027,
"sunrise": 1586487424,
"sunset": 1586538297,
"temp": 274.31,
"feels_like": 269.79,
"pressure": 1006,
"humidity": 72,
"dew_point": 270.21,
"clouds": 0,
"visibility": 10000,
"wind_speed": 3,
"wind_deg": 260,
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01n"
}
]
},
"hourly": [
{
"dt": 1586390400,
"temp": 278.41,
"feels_like": 269.43,
"pressure": 1006,
"humidity": 65,
"dew_point": 272.46,
"clouds": 0,
"wind_speed": 9.83,
"wind_deg": 60,
"wind_gust": 15.65,
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01n"
}
]
} ]
}

View File

@ -0,0 +1,31 @@
{
"lat": 52.2297,
"lon": 21.0122,
"timezone": "Europe/Warsaw",
"timezone_offset": 3600,
"data": [
{
"dt": 1645888976,
"sunrise": 1645853361,
"sunset": 1645891727,
"temp": 279.13,
"feels_like": 276.44,
"pressure": 1029,
"humidity": 64,
"dew_point": 272.88,
"uvi": 0.06,
"clouds": 0,
"visibility": 10000,
"wind_speed": 3.6,
"wind_deg": 340,
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}
]
}
]
}