mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[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:
parent
81bada78a6
commit
c8b3834461
@ -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");
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
return current;
|
||||
if (current != null) {
|
||||
return current;
|
||||
} else {
|
||||
return data[0];
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrent(Current current) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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,13 +95,10 @@ 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);
|
||||
}
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
]
|
||||
} ]
|
||||
}
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user