added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.openweathermap-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-openweathermap" description="OpenWeatherMap Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.openweathermap/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2010-2020 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 org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelGroupTypeUID;
/**
* The {@link OpenWeatherMapBindingConstants} class defines common constants, which are used across the whole binding.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapBindingConstants {
public static final String BINDING_ID = "openweathermap";
public static final String API = "api";
public static final String LOCAL = "local";
// Bridge
public static final ThingTypeUID THING_TYPE_WEATHER_API = new ThingTypeUID(BINDING_ID, "weather-api");
// Thing
public static final ThingTypeUID THING_TYPE_WEATHER_AND_FORECAST = new ThingTypeUID(BINDING_ID,
"weather-and-forecast");
public static final ThingTypeUID THING_TYPE_UVINDEX = new ThingTypeUID(BINDING_ID, "uvindex");
// List of all properties
public static final String CONFIG_API_KEY = "apikey";
public static final String CONFIG_LANGUAGE = "language";
public static final String CONFIG_LOCATION = "location";
// Channel group types
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_STATION = new ChannelGroupTypeUID(BINDING_ID, "station");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_HOURLY_FORECAST = new ChannelGroupTypeUID(BINDING_ID,
"hourlyForecast");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_DAILY_FORECAST = new ChannelGroupTypeUID(BINDING_ID,
"dailyForecast");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_UVINDEX = new ChannelGroupTypeUID(BINDING_ID, "uvindex");
// List of all channel groups
public static final String CHANNEL_GROUP_STATION = "station";
public static final String CHANNEL_GROUP_CURRENT_WEATHER = "current";
public static final String CHANNEL_GROUP_FORECAST_TODAY = "forecastToday";
public static final String CHANNEL_GROUP_FORECAST_TOMORROW = "forecastTomorrow";
public static final String CHANNEL_GROUP_CURRENT_UVINDEX = "current";
// List of all channels
public static final String CHANNEL_STATION_ID = "id";
public static final String CHANNEL_STATION_NAME = "name";
public static final String CHANNEL_STATION_LOCATION = "location";
public static final String CHANNEL_TIME_STAMP = "time-stamp";
public static final String CHANNEL_CONDITION = "condition";
public static final String CHANNEL_CONDITION_ID = "condition-id";
public static final String CHANNEL_CONDITION_ICON = "icon";
public static final String CHANNEL_CONDITION_ICON_ID = "icon-id";
public static final String CHANNEL_TEMPERATURE = "temperature";
public static final String CHANNEL_APPARENT_TEMPERATURE = "apparent-temperature";
public static final String CHANNEL_MIN_TEMPERATURE = "min-temperature";
public static final String CHANNEL_MAX_TEMPERATURE = "max-temperature";
public static final String CHANNEL_PRESSURE = "pressure";
public static final String CHANNEL_HUMIDITY = "humidity";
public static final String CHANNEL_WIND_SPEED = "wind-speed";
public static final String CHANNEL_WIND_DIRECTION = "wind-direction";
public static final String CHANNEL_GUST_SPEED = "gust-speed";
public static final String CHANNEL_CLOUDINESS = "cloudiness";
public static final String CHANNEL_RAIN = "rain";
public static final String CHANNEL_SNOW = "snow";
public static final String CHANNEL_VISIBILITY = "visibility";
public static final String CHANNEL_UVINDEX = "uvindex";
// List of all configuration
public static final String CONFIG_FORECAST_DAYS = "forecastDays";
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2020 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.config;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
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
* configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapAPIConfiguration {
// supported languages (see https://openweathermap.org/current#multi)
public static final Set<String> SUPPORTED_LANGUAGES = Collections.unmodifiableSet(Stream.of("ar", "bg", "ca", "cz",
"de", "el", "en", "es", "fa", "fi", "fr", "gl", "hr", "hu", "it", "ja", "kr", "la", "lt", "mk", "nl", "pl",
"pt", "ro", "ru", "se", "sk", "sl", "tr", "ua", "vi", "zh_cn", "zh_tw").collect(Collectors.toSet()));
public @Nullable String apikey;
public int refreshInterval;
public @Nullable String language;
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 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.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
* configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapLocationConfiguration {
public @NonNullByDefault({}) String location;
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 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.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapUVIndexHandler;
/**
* The {@link OpenWeatherMapUVIndexConfiguration} is the class used to match the
* {@link OpenWeatherMapUVIndexHandler}s configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapUVIndexConfiguration extends OpenWeatherMapLocationConfiguration {
public int forecastDays;
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 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.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.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapWeatherAndForecastConfiguration extends OpenWeatherMapLocationConfiguration {
public int forecastHours;
public int forecastDays;
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2020 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.connection;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link OpenWeatherMapCommunicationException} is a communication exception for the connections to OpenWeatherMap
* API.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapCommunicationException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* Constructs a new exception with null as its detail message.
*/
public OpenWeatherMapCommunicationException() {
super();
}
/**
* Constructs a new exception with the specified detail message.
*
* @param message Detail message
*/
public OpenWeatherMapCommunicationException(String message) {
super(message);
}
/**
* Constructs a new exception with the specified cause.
*
* @param cause The cause
*/
public OpenWeatherMapCommunicationException(Throwable cause) {
super(cause);
}
/**
* Constructs a new exception with the specified detail message and cause.
*
* @param message Detail message
* @param cause The cause
*/
public OpenWeatherMapCommunicationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2020 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.connection;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link OpenWeatherMapConfigurationException} is a configuration exception for the connections to OpenWeatherMap
* API.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapConfigurationException extends IllegalArgumentException {
private static final long serialVersionUID = 1L;
/**
* Constructs a new exception with null as its detail message.
*/
public OpenWeatherMapConfigurationException() {
super();
}
/**
* Constructs a new exception with the specified detail message.
*
* @param message Detail message
*/
public OpenWeatherMapConfigurationException(String message) {
super(message);
}
/**
* Constructs a new exception with the specified cause.
*
* @param cause The cause
*/
public OpenWeatherMapConfigurationException(Throwable cause) {
super(cause);
}
/**
* Constructs a new exception with the specified detail message and cause.
*
* @param message Detail message
* @param cause The cause
*/
public OpenWeatherMapConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,344 @@
/**
* Copyright (c) 2010-2020 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.connection;
import static java.util.stream.Collectors.joining;
import static org.eclipse.jetty.http.HttpMethod.GET;
import static org.eclipse.jetty.http.HttpStatus.*;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.api.ContentResponse;
import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapAPIConfiguration;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonDailyForecastData;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonHourlyForecastData;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonUVIndexData;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonWeatherData;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
import org.openhab.binding.openweathermap.internal.utils.ByteArrayFileCache;
import org.openhab.core.cache.ExpiringCacheMap;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.RawType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
/**
* The {@link OpenWeatherMapConnection} is responsible for handling the connections to OpenWeatherMap API.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapConnection {
private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapConnection.class);
private static final String PROPERTY_MESSAGE = "message";
private static final String PNG_CONTENT_TYPE = "image/png";
private static final String PARAM_APPID = "appid";
private static final String PARAM_UNITS = "units";
private static final String PARAM_LAT = "lat";
private static final String PARAM_LON = "lon";
private static final String PARAM_LANG = "lang";
private static final String PARAM_FORECAST_CNT = "cnt";
// Current weather data (see https://openweathermap.org/current)
private static final String WEATHER_URL = "https://api.openweathermap.org/data/2.5/weather";
// 5 day / 3 hour forecast (see https://openweathermap.org/forecast5)
private static final String THREE_HOUR_FORECAST_URL = "https://api.openweathermap.org/data/2.5/forecast";
// 16 day / daily forecast (see https://openweathermap.org/forecast16)
private static final String DAILY_FORECAST_URL = "https://api.openweathermap.org/data/2.5/forecast/daily";
// UV Index (see https://openweathermap.org/api/uvi)
private static final String UVINDEX_URL = "https://api.openweathermap.org/data/2.5/uvi";
private static final String UVINDEX_FORECAST_URL = "https://api.openweathermap.org/data/2.5/uvi/forecast";
// Weather icons (see https://openweathermap.org/weather-conditions)
private static final String ICON_URL = "https://openweathermap.org/img/w/%s.png";
private final OpenWeatherMapAPIHandler handler;
private final HttpClient httpClient;
private static final ByteArrayFileCache IMAGE_CACHE = new ByteArrayFileCache("org.openhab.binding.openweathermap");
private final ExpiringCacheMap<String, String> cache;
private final JsonParser parser = new JsonParser();
private final Gson gson = new Gson();
public OpenWeatherMapConnection(OpenWeatherMapAPIHandler handler, HttpClient httpClient) {
this.handler = handler;
this.httpClient = httpClient;
OpenWeatherMapAPIConfiguration config = handler.getOpenWeatherMapAPIConfig();
cache = new ExpiringCacheMap<>(TimeUnit.MINUTES.toMillis(config.refreshInterval));
}
/**
* Requests the current weather data for the given location (see https://openweathermap.org/current).
*
* @param location location represented as {@link PointType}
* @return the current weather data
* @throws JsonSyntaxException
* @throws OpenWeatherMapCommunicationException
* @throws OpenWeatherMapConfigurationException
*/
public synchronized @Nullable OpenWeatherMapJsonWeatherData getWeatherData(@Nullable PointType location)
throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
return gson.fromJson(
getResponseFromCache(
buildURL(WEATHER_URL, getRequestParams(handler.getOpenWeatherMapAPIConfig(), location))),
OpenWeatherMapJsonWeatherData.class);
}
/**
* Requests the hourly forecast data for the given location (see https://openweathermap.org/forecast5).
*
* @param location location represented as {@link PointType}
* @param count number of hours
* @return the hourly forecast data
* @throws JsonSyntaxException
* @throws OpenWeatherMapCommunicationException
* @throws OpenWeatherMapConfigurationException
*/
public synchronized @Nullable OpenWeatherMapJsonHourlyForecastData getHourlyForecastData(
@Nullable PointType location, int count)
throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
if (count <= 0) {
throw new OpenWeatherMapConfigurationException("@text/offline.conf-error-not-supported-number-of-hours");
}
Map<String, String> params = getRequestParams(handler.getOpenWeatherMapAPIConfig(), location);
params.put(PARAM_FORECAST_CNT, Integer.toString(count));
return gson.fromJson(getResponseFromCache(buildURL(THREE_HOUR_FORECAST_URL, params)),
OpenWeatherMapJsonHourlyForecastData.class);
}
/**
* Requests the daily forecast data for the given location (see https://openweathermap.org/forecast16).
*
* @param location location represented as {@link PointType}
* @param count number of days
* @return the daily forecast data
* @throws JsonSyntaxException
* @throws OpenWeatherMapCommunicationException
* @throws OpenWeatherMapConfigurationException
*/
public synchronized @Nullable OpenWeatherMapJsonDailyForecastData getDailyForecastData(@Nullable PointType location,
int count)
throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
if (count <= 0) {
throw new OpenWeatherMapConfigurationException("@text/offline.conf-error-not-supported-number-of-days");
}
Map<String, String> params = getRequestParams(handler.getOpenWeatherMapAPIConfig(), location);
params.put(PARAM_FORECAST_CNT, Integer.toString(count));
return gson.fromJson(getResponseFromCache(buildURL(DAILY_FORECAST_URL, params)),
OpenWeatherMapJsonDailyForecastData.class);
}
/**
* Requests the UV Index data for the given location (see https://api.openweathermap.org/data/2.5/uvi).
*
* @param location location represented as {@link PointType}
* @return the UV Index data
* @throws JsonSyntaxException
* @throws OpenWeatherMapCommunicationException
* @throws OpenWeatherMapConfigurationException
*/
public synchronized @Nullable OpenWeatherMapJsonUVIndexData getUVIndexData(@Nullable PointType location)
throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
return gson.fromJson(
getResponseFromCache(
buildURL(UVINDEX_URL, getRequestParams(handler.getOpenWeatherMapAPIConfig(), location))),
OpenWeatherMapJsonUVIndexData.class);
}
/**
* Requests the UV Index forecast data for the given location (see https://api.openweathermap.org/data/2.5/uvi).
*
* @param location location represented as {@link PointType}
* @return the UV Index forecast data
* @throws JsonSyntaxException
* @throws OpenWeatherMapCommunicationException
* @throws OpenWeatherMapConfigurationException
*/
public synchronized @Nullable List<OpenWeatherMapJsonUVIndexData> getUVIndexForecastData(
@Nullable PointType location, int count)
throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
if (count <= 0) {
throw new OpenWeatherMapConfigurationException(
"@text/offline.conf-error-not-supported-uvindex-number-of-days");
}
Map<String, String> params = getRequestParams(handler.getOpenWeatherMapAPIConfig(), location);
params.put(PARAM_FORECAST_CNT, Integer.toString(count));
return Arrays.asList(gson.fromJson(getResponseFromCache(buildURL(UVINDEX_FORECAST_URL, params)),
OpenWeatherMapJsonUVIndexData[].class));
}
/**
* Downloads the icon for the given icon id (see https://openweathermap.org/weather-conditions).
*
* @param iconId the id of the icon
* @return the weather icon as {@link RawType}
*/
public static @Nullable RawType getWeatherIcon(String iconId) {
if (iconId.isEmpty()) {
throw new IllegalArgumentException("Cannot download weather icon as icon id is null.");
}
return downloadWeatherIconFromCache(String.format(ICON_URL, iconId));
}
private static @Nullable RawType downloadWeatherIconFromCache(String url) {
if (IMAGE_CACHE.containsKey(url)) {
return new RawType(IMAGE_CACHE.get(url), PNG_CONTENT_TYPE);
} else {
RawType image = downloadWeatherIcon(url);
if (image != null) {
IMAGE_CACHE.put(url, image.getBytes());
return image;
}
}
return null;
}
private static @Nullable RawType downloadWeatherIcon(String url) {
return HttpUtil.downloadImage(url);
}
private Map<String, String> getRequestParams(OpenWeatherMapAPIConfiguration config, @Nullable PointType location) {
if (location == null) {
throw new OpenWeatherMapConfigurationException("@text/offline.conf-error-missing-location");
}
Map<String, String> params = new HashMap<>();
// API key (see http://openweathermap.org/appid)
String apikey = config.apikey;
if (apikey == null || (apikey = apikey.trim()).isEmpty()) {
throw new OpenWeatherMapConfigurationException("@text/offline.conf-error-missing-apikey");
}
params.put(PARAM_APPID, apikey);
// Units format (see https://openweathermap.org/current#data)
params.put(PARAM_UNITS, "metric");
// By geographic coordinates (see https://openweathermap.org/current#geo)
params.put(PARAM_LAT, location.getLatitude().toString());
params.put(PARAM_LON, location.getLongitude().toString());
// Multilingual support (see https://openweathermap.org/current#multi)
String language = config.language;
if (language != null && !(language = language.trim()).isEmpty()) {
params.put(PARAM_LANG, language.toLowerCase());
}
return params;
}
private String buildURL(String url, Map<String, String> requestParams) {
return requestParams.keySet().stream().map(key -> key + "=" + encodeParam(requestParams.get(key)))
.collect(joining("&", url + "?", ""));
}
private String encodeParam(String value) {
try {
return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
logger.debug("UnsupportedEncodingException occurred during execution: {}", e.getLocalizedMessage(), e);
return "";
}
}
private @Nullable String getResponseFromCache(String url) {
return cache.putIfAbsentAndGet(url, () -> getResponse(url));
}
private String getResponse(String url) {
try {
if (logger.isTraceEnabled()) {
logger.trace("OpenWeatherMap request: URL = '{}'", uglifyApikey(url));
}
ContentResponse contentResponse = httpClient.newRequest(url).method(GET).timeout(10, TimeUnit.SECONDS)
.send();
int httpStatus = contentResponse.getStatus();
String content = contentResponse.getContentAsString();
String errorMessage = "";
logger.trace("OpenWeatherMap response: status = {}, content = '{}'", httpStatus, content);
switch (httpStatus) {
case OK_200:
return content;
case BAD_REQUEST_400:
case UNAUTHORIZED_401:
case NOT_FOUND_404:
errorMessage = getErrorMessage(content);
logger.debug("OpenWeatherMap server responded with status code {}: {}", httpStatus, errorMessage);
throw new OpenWeatherMapConfigurationException(errorMessage);
case TOO_MANY_REQUESTS_429:
// TODO disable refresh job temporarily (see https://openweathermap.org/appid#Accesslimitation)
default:
errorMessage = getErrorMessage(content);
logger.debug("OpenWeatherMap server responded with status code {}: {}", httpStatus, errorMessage);
throw new OpenWeatherMapCommunicationException(errorMessage);
}
} catch (ExecutionException e) {
String errorMessage = e.getLocalizedMessage();
logger.trace("Exception occurred during execution: {}", errorMessage, e);
if (e.getCause() instanceof HttpResponseException) {
logger.debug("OpenWeatherMap server responded with status code {}: Invalid API key.", UNAUTHORIZED_401);
throw new OpenWeatherMapConfigurationException("@text/offline.conf-error-invalid-apikey", e.getCause());
} else {
throw new OpenWeatherMapCommunicationException(errorMessage, e.getCause());
}
} catch (InterruptedException | TimeoutException e) {
logger.debug("Exception occurred during execution: {}", e.getLocalizedMessage(), e);
throw new OpenWeatherMapCommunicationException(e.getLocalizedMessage(), e.getCause());
}
}
private String uglifyApikey(String url) {
return url.replaceAll("(appid=)+\\w+", "appid=*****");
}
private String getErrorMessage(String response) {
JsonElement jsonResponse = parser.parse(response);
if (jsonResponse.isJsonObject()) {
JsonObject json = jsonResponse.getAsJsonObject();
if (json.has(PROPERTY_MESSAGE)) {
return json.get(PROPERTY_MESSAGE).getAsString();
}
}
return response;
}
}

View File

@@ -0,0 +1,144 @@
/**
* Copyright (c) 2010-2020 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.discovery;
import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.PointType;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OpenWeatherMapDiscoveryService} creates things based on the configured location.
*
* @author Christoph Weitkamp - Initial Contribution
*/
@NonNullByDefault
public class OpenWeatherMapDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapDiscoveryService.class);
private static final int DISCOVERY_TIMEOUT_SECONDS = 2;
private static final int DISCOVERY_INTERVAL_SECONDS = 60;
private @Nullable ScheduledFuture<?> discoveryJob;
private final LocationProvider locationProvider;
private @Nullable PointType previousLocation;
private final OpenWeatherMapAPIHandler bridgeHandler;
/**
* Creates an OpenWeatherMapLocationDiscoveryService.
*/
public OpenWeatherMapDiscoveryService(OpenWeatherMapAPIHandler bridgeHandler, LocationProvider locationProvider,
LocaleProvider localeProvider, TranslationProvider i18nProvider) {
super(AbstractOpenWeatherMapHandler.SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT_SECONDS);
this.bridgeHandler = bridgeHandler;
this.locationProvider = locationProvider;
this.localeProvider = localeProvider;
this.i18nProvider = i18nProvider;
activate(null);
}
@Override
protected void activate(@Nullable Map<String, @Nullable Object> configProperties) {
super.activate(configProperties);
}
@Override
public void deactivate() {
removeOlderResults(new Date().getTime(), bridgeHandler.getThing().getUID());
super.deactivate();
}
@Override
protected void startScan() {
logger.debug("Start manual OpenWeatherMap Location discovery scan.");
scanForNewLocation(false);
}
@Override
protected synchronized void stopScan() {
logger.debug("Stop manual OpenWeatherMap Location discovery scan.");
super.stopScan();
}
@Override
protected void startBackgroundDiscovery() {
if (discoveryJob == null || discoveryJob.isCancelled()) {
logger.debug("Start OpenWeatherMap Location background discovery job at interval {} s.",
DISCOVERY_INTERVAL_SECONDS);
discoveryJob = scheduler.scheduleWithFixedDelay(() -> {
scanForNewLocation(true);
}, 0, DISCOVERY_INTERVAL_SECONDS, TimeUnit.SECONDS);
}
}
@Override
protected void stopBackgroundDiscovery() {
if (discoveryJob != null && !discoveryJob.isCancelled()) {
logger.debug("Stop OpenWeatherMap Location background discovery job.");
if (discoveryJob.cancel(true)) {
discoveryJob = null;
}
}
}
private void scanForNewLocation(boolean updateOnlyIfNewLocation) {
PointType currentLocation = locationProvider.getLocation();
if (currentLocation == null) {
logger.debug("Location is not set -> Will not provide any discovery results.");
} else if (!Objects.equals(currentLocation, previousLocation)) {
logger.debug("Location has been changed from {} to {} -> Creating new discovery results.", previousLocation,
currentLocation);
createResults(currentLocation);
previousLocation = currentLocation;
} else if (!updateOnlyIfNewLocation) {
createResults(currentLocation);
}
}
private void createResults(PointType location) {
String locationString = location.toFullString();
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
createWeatherAndForecastResult(locationString, bridgeUID);
createUVIndexResult(locationString, bridgeUID);
}
private void createWeatherAndForecastResult(String location, ThingUID bridgeUID) {
thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_WEATHER_AND_FORECAST, bridgeUID, LOCAL))
.withLabel("Local weather and forecast").withProperty(CONFIG_LOCATION, location)
.withRepresentationProperty(CONFIG_LOCATION).withBridge(bridgeUID).build());
}
private void createUVIndexResult(String location, ThingUID bridgeUID) {
thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_UVINDEX, bridgeUID, LOCAL))
.withLabel("Local UV Index").withProperty(CONFIG_LOCATION, location)
.withRepresentationProperty(CONFIG_LOCATION).withBridge(bridgeUID).build());
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 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.dto;
import org.openhab.binding.openweathermap.internal.dto.base.City;
import org.openhab.binding.openweathermap.internal.dto.forecast.daily.List;
/**
* The {@link OpenWeatherMapJsonDailyForecastData} is the Java class used to map the JSON response to an OpenWeatherMap
* request.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class OpenWeatherMapJsonDailyForecastData {
private City city;
private String cod;
private Double message;
private Integer cnt;
private java.util.List<List> list;
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public String getCod() {
return cod;
}
public void setCod(String cod) {
this.cod = cod;
}
public Double getMessage() {
return message;
}
public void setMessage(Double message) {
this.message = message;
}
public Integer getCnt() {
return cnt;
}
public void setCnt(Integer cnt) {
this.cnt = cnt;
}
public java.util.List<List> getList() {
return list;
}
public void setList(java.util.List<List> list) {
this.list = list;
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 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.dto;
import org.openhab.binding.openweathermap.internal.dto.base.City;
import org.openhab.binding.openweathermap.internal.dto.forecast.hourly.List;
/**
* The {@link OpenWeatherMapJsonHourlyForecastData} is the Java class used to map the JSON response to an OpenWeatherMap
* request.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class OpenWeatherMapJsonHourlyForecastData {
private String cod;
private Double message;
private Integer cnt;
private java.util.List<List> list;
private City city;
public String getCod() {
return cod;
}
public void setCod(String cod) {
this.cod = cod;
}
public Double getMessage() {
return message;
}
public void setMessage(Double message) {
this.message = message;
}
public Integer getCnt() {
return cnt;
}
public void setCnt(Integer cnt) {
this.cnt = cnt;
}
public java.util.List<List> getList() {
return list;
}
public void setList(java.util.List<List> list) {
this.list = list;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 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.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link OpenWeatherMapJsonUVIndexData} is the Java class used to map the JSON response to an OpenWeatherMap
* request.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class OpenWeatherMapJsonUVIndexData {
private Double lat;
private Double lon;
@SerializedName("date_iso")
private String dateIso;
private Integer date;
private Double value;
public Double getLat() {
return lat;
}
public void setLat(Double lat) {
this.lat = lat;
}
public Double getLon() {
return lon;
}
public void setLon(Double lon) {
this.lon = lon;
}
public String getDateIso() {
return dateIso;
}
public void setDateIso(String dateIso) {
this.dateIso = dateIso;
}
public Integer getDate() {
return date;
}
public void setDate(Integer date) {
this.date = date;
}
public Double getValue() {
return value;
}
public void setValue(Double value) {
this.value = value;
}
}

View File

@@ -0,0 +1,160 @@
/**
* Copyright (c) 2010-2020 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.dto;
import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.dto.base.Clouds;
import org.openhab.binding.openweathermap.internal.dto.base.Coord;
import org.openhab.binding.openweathermap.internal.dto.base.Rain;
import org.openhab.binding.openweathermap.internal.dto.base.Snow;
import org.openhab.binding.openweathermap.internal.dto.base.Weather;
import org.openhab.binding.openweathermap.internal.dto.base.Wind;
import org.openhab.binding.openweathermap.internal.dto.weather.Main;
import org.openhab.binding.openweathermap.internal.dto.weather.Sys;
/**
* The {@link OpenWeatherMapJsonWeatherData} is the Java class used to map the JSON response to an OpenWeatherMap
* request.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class OpenWeatherMapJsonWeatherData {
private Coord coord;
private List<Weather> weather;
private String base;
private Main main;
private @Nullable Integer visibility;
private Wind wind;
private Clouds clouds;
private @Nullable Rain rain;
private @Nullable Snow snow;
private Integer dt;
private Sys sys;
private Integer id;
private String name;
private Integer cod;
public Coord getCoord() {
return coord;
}
public void setCoord(Coord coord) {
this.coord = coord;
}
public List<Weather> getWeather() {
return weather;
}
public void setWeather(List<Weather> weather) {
this.weather = weather;
}
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
public Main getMain() {
return main;
}
public void setMain(Main main) {
this.main = main;
}
public @Nullable Integer getVisibility() {
return visibility;
}
public void setVisibility(Integer visibility) {
this.visibility = visibility;
}
public Wind getWind() {
return wind;
}
public void setWind(Wind wind) {
this.wind = wind;
}
public Clouds getClouds() {
return clouds;
}
public void setClouds(Clouds clouds) {
this.clouds = clouds;
}
public @Nullable Rain getRain() {
return rain;
}
public void setRain(Rain rain) {
this.rain = rain;
}
public @Nullable Snow getSnow() {
return snow;
}
public void setSnow(Snow snow) {
this.snow = snow;
}
public Integer getDt() {
return dt;
}
public void setDt(Integer dt) {
this.dt = dt;
}
public Sys getSys() {
return sys;
}
public void setSys(Sys sys) {
this.sys = sys;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getCod() {
return cod;
}
public void setCod(Integer cod) {
this.cod = cod;
}
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2020 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.dto.base;
/**
* Generated Plain Old Java Objects class for {@link City} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class City {
private Integer id;
private Coord coord;
private String country;
private Integer population;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Coord getCoord() {
return coord;
}
public void setCoord(Coord coord) {
this.coord = coord;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Integer getPopulation() {
return population;
}
public void setPopulation(Integer population) {
this.population = population;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.dto.base;
/**
* Generated Plain Old Java Objects class for {@link Clouds} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Clouds {
private Integer all;
public Integer getAll() {
return all;
}
public void setAll(Integer all) {
this.all = all;
}
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2020 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.dto.base;
/**
* Generated Plain Old Java Objects class for {@link Coord} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Coord {
private Double lon;
private Double lat;
public Double getLon() {
return lon;
}
public void setLon(Double lon) {
this.lon = lon;
}
public Double getLat() {
return lat;
}
public void setLat(Double lat) {
this.lat = lat;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2020 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.dto.base;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link Rain} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Rain {
@SerializedName("1h")
private @Nullable Double oneHour;
@SerializedName("3h")
private @Nullable Double threeHours;
public @Nullable Double get1h() {
return oneHour;
}
public void set1h(Double oneHour) {
this.oneHour = oneHour;
}
public @Nullable Double get3h() {
return threeHours;
}
public void set3h(Double threeHours) {
this.threeHours = threeHours;
}
public Double getVolume() {
return oneHour != null ? oneHour : threeHours != null ? threeHours / 3 : 0;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2020 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.dto.base;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link Snow} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Snow {
@SerializedName("1h")
private @Nullable Double oneHour;
@SerializedName("3h")
private @Nullable Double threeHours;
public @Nullable Double get1h() {
return oneHour;
}
public void set1h(Double oneHour) {
this.oneHour = oneHour;
}
public @Nullable Double get3h() {
return threeHours;
}
public void set3h(Double threeHours) {
this.threeHours = threeHours;
}
public Double getVolume() {
return oneHour != null ? oneHour : threeHours != null ? threeHours / 3 : 0;
}
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2020 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.dto.base;
/**
* Generates Plain Old Java Objects class for {@link Weather} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Weather {
private Integer id;
private String main;
private String description;
private String icon;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getMain() {
return main;
}
public void setMain(String main) {
this.main = main;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2020 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.dto.base;
import org.eclipse.jdt.annotation.Nullable;
/**
* Generated Plain Old Java Objects class for {@link Wind} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Wind {
private Double speed;
private Double deg;
private @Nullable Double gust;
public Double getSpeed() {
return speed;
}
public void setSpeed(Double speed) {
this.speed = speed;
}
public Double getDeg() {
return deg;
}
public void setDeg(Double deg) {
this.deg = deg;
}
public @Nullable Double getGust() {
return gust;
}
public void setGust(Double gust) {
this.gust = speed;
}
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2020 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.dto.forecast.daily;
/**
* Generated Plain Old Java Objects class for {@link FeelsLikeTemp} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class FeelsLikeTemp {
private Double day;
private Double night;
private Double eve;
private Double morn;
public Double getDay() {
return day;
}
public void setDay(Double day) {
this.day = day;
}
public Double getNight() {
return night;
}
public void setNight(Double night) {
this.night = night;
}
public Double getEve() {
return eve;
}
public void setEve(Double eve) {
this.eve = eve;
}
public Double getMorn() {
return morn;
}
public void setMorn(Double morn) {
this.morn = morn;
}
}

View File

@@ -0,0 +1,135 @@
/**
* Copyright (c) 2010-2020 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.dto.forecast.daily;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.dto.base.Weather;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link List} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class List {
private Integer dt;
private Temp temp;
@SerializedName("feels_like")
private @Nullable FeelsLikeTemp feelsLikeTemp;
private Double pressure;
private Integer humidity;
private java.util.List<Weather> weather;
private Double speed;
private Double deg;
private @Nullable Double gust;
private Integer clouds;
private @Nullable Double rain;
private @Nullable Double snow;
public Integer getDt() {
return dt;
}
public void setDt(Integer dt) {
this.dt = dt;
}
public Temp getTemp() {
return temp;
}
public void setTemp(Temp temp) {
this.temp = temp;
}
public @Nullable FeelsLikeTemp getFeelsLikeTemp() {
return feelsLikeTemp;
}
public void setFeelsLikeTemp(FeelsLikeTemp feelsLikeTemp) {
this.feelsLikeTemp = feelsLikeTemp;
}
public Double getPressure() {
return pressure;
}
public void setPressure(Double pressure) {
this.pressure = pressure;
}
public Integer getHumidity() {
return humidity;
}
public void setHumidity(Integer humidity) {
this.humidity = humidity;
}
public java.util.List<Weather> getWeather() {
return weather;
}
public void setWeather(java.util.List<Weather> weather) {
this.weather = weather;
}
public Double getSpeed() {
return speed;
}
public void setSpeed(Double speed) {
this.speed = speed;
}
public Double getDeg() {
return deg;
}
public void setDeg(Double deg) {
this.deg = deg;
}
public @Nullable Double getGust() {
return gust;
}
public void setGust(Double gust) {
this.gust = speed;
}
public Integer getClouds() {
return clouds;
}
public void setClouds(Integer clouds) {
this.clouds = clouds;
}
public @Nullable Double getRain() {
return rain;
}
public void setRain(Double rain) {
this.rain = rain;
}
public @Nullable Double getSnow() {
return snow;
}
public void setSnow(Double snow) {
this.snow = snow;
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2010-2020 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.dto.forecast.daily;
/**
* Generated Plain Old Java Objects class for {@link Temp} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Temp {
private Double day;
private Double min;
private Double max;
private Double night;
private Double eve;
private Double morn;
public Double getDay() {
return day;
}
public void setDay(Double day) {
this.day = day;
}
public Double getMin() {
return min;
}
public void setMin(Double min) {
this.min = min;
}
public Double getMax() {
return max;
}
public void setMax(Double max) {
this.max = max;
}
public Double getNight() {
return night;
}
public void setNight(Double night) {
this.night = night;
}
public Double getEve() {
return eve;
}
public void setEve(Double eve) {
this.eve = eve;
}
public Double getMorn() {
return morn;
}
public void setMorn(Double morn) {
this.morn = morn;
}
}

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2010-2020 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.dto.forecast.hourly;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.dto.base.Clouds;
import org.openhab.binding.openweathermap.internal.dto.base.Rain;
import org.openhab.binding.openweathermap.internal.dto.base.Snow;
import org.openhab.binding.openweathermap.internal.dto.base.Weather;
import org.openhab.binding.openweathermap.internal.dto.base.Wind;
import org.openhab.binding.openweathermap.internal.dto.weather.Main;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link List} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class List {
private Integer dt;
private Main main;
private java.util.List<Weather> weather;
private Clouds clouds;
private Wind wind;
private @Nullable Rain rain;
private @Nullable Snow snow;
private Sys sys;
@SerializedName("dt_txt")
private String dtTxt;
public Integer getDt() {
return dt;
}
public void setDt(Integer dt) {
this.dt = dt;
}
public Main getMain() {
return main;
}
public void setMain(Main main) {
this.main = main;
}
public java.util.List<Weather> getWeather() {
return weather;
}
public void setWeather(java.util.List<Weather> weather) {
this.weather = weather;
}
public Clouds getClouds() {
return clouds;
}
public void setClouds(Clouds clouds) {
this.clouds = clouds;
}
public Wind getWind() {
return wind;
}
public void setWind(Wind wind) {
this.wind = wind;
}
public @Nullable Rain getRain() {
return rain;
}
public void setRain(Rain rain) {
this.rain = rain;
}
public @Nullable Snow getSnow() {
return snow;
}
public void setSnow(Snow snow) {
this.snow = snow;
}
public Sys getSys() {
return sys;
}
public void setSys(Sys sys) {
this.sys = sys;
}
public String getDtTxt() {
return dtTxt;
}
public void setDtTxt(String dtTxt) {
this.dtTxt = dtTxt;
}
}

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2010-2020 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.dto.forecast.hourly;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link Main} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Main {
private Double temp;
@SerializedName("feels_like")
private @Nullable Double feelsLikeTemp;
@SerializedName("temp_min")
private Double tempMin;
@SerializedName("temp_max")
private Double tempMax;
private Double pressure;
@SerializedName("sea_level")
private Double seaLevel;
@SerializedName("grnd_level")
private Double grndLevel;
private Integer humidity;
@SerializedName("temp_kf")
private Integer tempKf;
public Double getTemp() {
return temp;
}
public void setTemp(Double temp) {
this.temp = temp;
}
public @Nullable Double getFeelsLikeTemp() {
return feelsLikeTemp;
}
public void setFeelsLikeTemp(Double feelsLikeTemp) {
this.feelsLikeTemp = feelsLikeTemp;
}
public Double getTempMin() {
return tempMin;
}
public void setTempMin(Double tempMin) {
this.tempMin = tempMin;
}
public Double getTempMax() {
return tempMax;
}
public void setTempMax(Double tempMax) {
this.tempMax = tempMax;
}
public Double getPressure() {
return pressure;
}
public void setPressure(Double pressure) {
this.pressure = pressure;
}
public Double getSeaLevel() {
return seaLevel;
}
public void setSeaLevel(Double seaLevel) {
this.seaLevel = seaLevel;
}
public Double getGrndLevel() {
return grndLevel;
}
public void setGrndLevel(Double grndLevel) {
this.grndLevel = grndLevel;
}
public Integer getHumidity() {
return humidity;
}
public void setHumidity(Integer humidity) {
this.humidity = humidity;
}
public Integer getTempKf() {
return tempKf;
}
public void setTempKf(Integer tempKf) {
this.tempKf = tempKf;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.dto.forecast.hourly;
/**
* Generated Plain Old Java Objects class for {@link Sys} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Sys {
private String pod;
public String getPod() {
return pod;
}
public void setPod(String pod) {
this.pod = pod;
}
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2010-2020 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.dto.weather;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link Main} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Main {
private Double temp;
@SerializedName("feels_like")
private @Nullable Double feelsLikeTemp;
private Double pressure;
private Integer humidity;
@SerializedName("temp_min")
private Double tempMin;
@SerializedName("temp_max")
private Double tempMax;
public Double getTemp() {
return temp;
}
public void setTemp(Double temp) {
this.temp = temp;
}
public @Nullable Double getFeelsLikeTemp() {
return feelsLikeTemp;
}
public void setFeelsLikeTemp(Double feelsLikeTemp) {
this.feelsLikeTemp = feelsLikeTemp;
}
public Double getPressure() {
return pressure;
}
public void setPressure(Double pressure) {
this.pressure = pressure;
}
public Integer getHumidity() {
return humidity;
}
public void setHumidity(Integer humidity) {
this.humidity = humidity;
}
public Double getTempMin() {
return tempMin;
}
public void setTempMin(Double tempMin) {
this.tempMin = tempMin;
}
public Double getTempMax() {
return tempMax;
}
public void setTempMax(Double tempMax) {
this.tempMax = tempMax;
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2010-2020 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.dto.weather;
/**
* Generated Plain Old Java Objects class for {@link Sys} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Sys {
private Integer type;
private Integer id;
private Double message;
private String country;
private Integer sunrise;
private Integer sunset;
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getMessage() {
return message;
}
public void setMessage(Double message) {
this.message = message;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Integer getSunrise() {
return sunrise;
}
public void setSunrise(Integer sunrise) {
this.sunrise = sunrise;
}
public Integer getSunset() {
return sunset;
}
public void setSunset(Integer sunset) {
this.sunset = sunset;
}
}

View File

@@ -0,0 +1,123 @@
/**
* Copyright (c) 2010-2020 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.factory;
import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.openweathermap.internal.discovery.OpenWeatherMapDiscoveryService;
import org.openhab.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapUVIndexHandler;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapWeatherAndForecastHandler;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link OpenWeatherMapHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.openweathermap")
public class OpenWeatherMapHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.concat(OpenWeatherMapAPIHandler.SUPPORTED_THING_TYPES.stream(),
AbstractOpenWeatherMapHandler.SUPPORTED_THING_TYPES.stream()).collect(Collectors.toSet()));
private final Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
private final HttpClient httpClient;
private final LocaleProvider localeProvider;
private final LocationProvider locationProvider;
private final TranslationProvider i18nProvider;
private final TimeZoneProvider timeZoneProvider;
@Activate
public OpenWeatherMapHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
final @Reference LocaleProvider localeProvider, final @Reference LocationProvider locationProvider,
final @Reference TranslationProvider i18nProvider, final @Reference TimeZoneProvider timeZoneProvider) {
this.httpClient = httpClientFactory.getCommonHttpClient();
this.localeProvider = localeProvider;
this.locationProvider = locationProvider;
this.i18nProvider = i18nProvider;
this.timeZoneProvider = timeZoneProvider;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_WEATHER_API.equals(thingTypeUID)) {
OpenWeatherMapAPIHandler handler = new OpenWeatherMapAPIHandler((Bridge) thing, httpClient, localeProvider);
// register discovery service
OpenWeatherMapDiscoveryService discoveryService = new OpenWeatherMapDiscoveryService(handler,
locationProvider, localeProvider, i18nProvider);
discoveryServiceRegs.put(handler.getThing().getUID(), bundleContext
.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
return handler;
} else if (THING_TYPE_WEATHER_AND_FORECAST.equals(thingTypeUID)) {
return new OpenWeatherMapWeatherAndForecastHandler(thing, timeZoneProvider);
} else if (THING_TYPE_UVINDEX.equals(thingTypeUID)) {
return new OpenWeatherMapUVIndexHandler(thing, timeZoneProvider);
}
return null;
}
@Override
protected synchronized void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof OpenWeatherMapAPIHandler) {
ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(thingHandler.getThing().getUID());
if (serviceReg != null) {
// remove discovery service, if bridge handler is removed
OpenWeatherMapDiscoveryService discoveryService = (OpenWeatherMapDiscoveryService) bundleContext
.getService(serviceReg.getReference());
serviceReg.unregister();
if (discoveryService != null) {
discoveryService.deactivate();
}
}
}
}
}

View File

@@ -0,0 +1,231 @@
/**
* Copyright (c) 2010-2020 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.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapLocationConfiguration;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapCommunicationException;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConfigurationException;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
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.QuantityType;
import org.openhab.core.library.types.RawType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelGroupUID;
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.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.type.ChannelGroupTypeUID;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link AbstractOpenWeatherMapHandler} is responsible for handling commands, which are sent to one of the
* channels.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractOpenWeatherMapHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(AbstractOpenWeatherMapHandler.class);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.unmodifiableSet(
Stream.of(THING_TYPE_WEATHER_AND_FORECAST, THING_TYPE_UVINDEX).collect(Collectors.toSet()));
private final TimeZoneProvider timeZoneProvider;
// keeps track of the parsed location
protected @Nullable PointType location;
public AbstractOpenWeatherMapHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
super(thing);
this.timeZoneProvider = timeZoneProvider;
}
@Override
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;
}
try {
location = new PointType(config.location);
} catch (IllegalArgumentException e) {
logger.warn("Error parsing 'location' parameter: {}", e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-parsing-location");
location = null;
configValid = false;
}
if (configValid) {
updateStatus(ThingStatus.UNKNOWN);
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
updateChannel(channelUID);
} else {
logger.debug("The OpenWeatherMap binding is a read-only binding and cannot handle command '{}'.", command);
}
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (ThingStatus.ONLINE.equals(bridgeStatusInfo.getStatus())
&& ThingStatusDetail.BRIDGE_OFFLINE.equals(getThing().getStatusInfo().getStatusDetail())) {
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
} else if (ThingStatus.OFFLINE.equals(bridgeStatusInfo.getStatus())
&& !ThingStatus.OFFLINE.equals(getThing().getStatus())) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
}
/**
* Updates OpenWeatherMap data for this location.
*
* @param connection {@link OpenWeatherMapConnection} instance
*/
public void updateData(OpenWeatherMapConnection connection) {
try {
if (requestData(connection)) {
updateChannels();
updateStatus(ThingStatus.ONLINE);
}
} catch (OpenWeatherMapCommunicationException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getLocalizedMessage());
} catch (OpenWeatherMapConfigurationException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getLocalizedMessage());
}
}
/**
* Requests the data from OpenWeatherMap API.
*
* @param connection {@link OpenWeatherMapConnection} instance
* @return true, if the request for the OpenWeatherMap data was successful
* @throws OpenWeatherMapCommunicationException
* @throws OpenWeatherMapConfigurationException
*/
protected abstract boolean requestData(OpenWeatherMapConnection connection)
throws OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException;
/**
* Updates all channels of this handler from the latest OpenWeatherMap data retrieved.
*/
private void updateChannels() {
for (Channel channel : getThing().getChannels()) {
ChannelUID channelUID = channel.getUID();
if (ChannelKind.STATE.equals(channel.getKind()) && channelUID.isInGroup() && channelUID.getGroupId() != null
&& isLinked(channelUID)) {
updateChannel(channelUID);
}
}
}
/**
* Updates the channel with the given UID from the latest OpenWeatherMap data retrieved.
*
* @param channelUID UID of the channel
*/
protected abstract void updateChannel(ChannelUID channelUID);
protected State getDateTimeTypeState(@Nullable Integer value) {
return (value == null) ? UnDefType.UNDEF
: new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochSecond(value.longValue()),
timeZoneProvider.getTimeZone()));
}
protected State getDecimalTypeState(@Nullable Double value) {
return (value == null) ? UnDefType.UNDEF : new DecimalType(value);
}
protected State getPointTypeState(@Nullable Double latitude, @Nullable Double longitude) {
return ((latitude == null) || (longitude == null)) ? UnDefType.UNDEF
: new PointType(new DecimalType(latitude), new DecimalType(longitude));
}
protected State getRawTypeState(@Nullable RawType image) {
return (image == null) ? UnDefType.UNDEF : image;
}
protected State getStringTypeState(@Nullable String value) {
return (value == null) ? UnDefType.UNDEF : new StringType(value);
}
protected State getQuantityTypeState(@Nullable Number value, Unit<?> unit) {
return (value == null) ? UnDefType.UNDEF : new QuantityType<>(value, unit);
}
protected List<Channel> createChannelsForGroup(String channelGroupId, ChannelGroupTypeUID channelGroupTypeUID) {
logger.debug("Building channel group '{}' for thing '{}'.", channelGroupId, getThing().getUID());
List<Channel> channels = new ArrayList<>();
ThingHandlerCallback callback = getCallback();
if (callback != null) {
for (ChannelBuilder channelBuilder : callback.createChannelBuilders(
new ChannelGroupUID(getThing().getUID(), channelGroupId), channelGroupTypeUID)) {
Channel newChannel = channelBuilder.build(),
existingChannel = getThing().getChannel(newChannel.getUID().getId());
if (existingChannel != null) {
logger.trace("Thing '{}' already has an existing channel '{}'. Omit adding new channel '{}'.",
getThing().getUID(), existingChannel.getUID(), newChannel.getUID());
continue;
}
channels.add(newChannel);
}
}
return channels;
}
protected List<Channel> removeChannelsOfGroup(String channelGroupId) {
logger.debug("Removing channel group '{}' from thing '{}'.", channelGroupId, getThing().getUID());
return getThing().getChannelsOfGroup(channelGroupId);
}
}

View File

@@ -0,0 +1,187 @@
/**
* Copyright (c) 2010-2020 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.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapAPIConfiguration;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.thing.Bridge;
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.ThingTypeUID;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OpenWeatherMapAPIHandler} is responsible for accessing the OpenWeatherMap API.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapAPIHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapAPIHandler.class);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_WEATHER_API);
private static final long INITIAL_DELAY_IN_SECONDS = 15;
private @Nullable ScheduledFuture<?> refreshJob;
private final HttpClient httpClient;
private final LocaleProvider localeProvider;
private @NonNullByDefault({}) OpenWeatherMapConnection connection;
// keeps track of the parsed config
private @NonNullByDefault({}) OpenWeatherMapAPIConfiguration config;
public OpenWeatherMapAPIHandler(Bridge bridge, HttpClient httpClient, LocaleProvider localeProvider) {
super(bridge);
this.httpClient = httpClient;
this.localeProvider = localeProvider;
}
@Override
public void initialize() {
logger.debug("Initialize OpenWeatherMap API handler '{}'.", getThing().getUID());
config = getConfigAs(OpenWeatherMapAPIConfiguration.class);
boolean configValid = true;
if (config.apikey == null || config.apikey.trim().isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-missing-apikey");
configValid = false;
}
int refreshInterval = config.refreshInterval;
if (refreshInterval < 10) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-not-supported-refreshInterval");
configValid = false;
}
String language = config.language;
if (language != null && !(language = language.trim()).isEmpty()) {
if (!OpenWeatherMapAPIConfiguration.SUPPORTED_LANGUAGES.contains(language.toLowerCase())) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-not-supported-language");
configValid = false;
}
} else {
language = localeProvider.getLocale().getLanguage();
if (OpenWeatherMapAPIConfiguration.SUPPORTED_LANGUAGES.contains(language)) {
logger.debug("Language set to '{}'.", language);
Configuration editConfig = editConfiguration();
editConfig.put(CONFIG_LANGUAGE, language);
updateConfiguration(editConfig);
}
}
if (configValid) {
connection = new OpenWeatherMapConnection(this, httpClient);
updateStatus(ThingStatus.UNKNOWN);
ScheduledFuture<?> localRefreshJob = refreshJob;
if (localRefreshJob == null || localRefreshJob.isCancelled()) {
logger.debug("Start refresh job at interval {} min.", refreshInterval);
refreshJob = scheduler.scheduleWithFixedDelay(this::updateThings, INITIAL_DELAY_IN_SECONDS,
TimeUnit.MINUTES.toSeconds(refreshInterval), TimeUnit.SECONDS);
}
}
}
@Override
public void dispose() {
logger.debug("Dispose OpenWeatherMap API handler '{}'.", getThing().getUID());
ScheduledFuture<?> localRefreshJob = refreshJob;
if (localRefreshJob != null && !localRefreshJob.isCancelled()) {
logger.debug("Stop refresh job.");
if (localRefreshJob.cancel(true)) {
refreshJob = null;
}
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
scheduler.schedule(this::updateThings, INITIAL_DELAY_IN_SECONDS, TimeUnit.SECONDS);
} else {
logger.debug("The OpenWeatherMap binding is a read-only binding and cannot handle command '{}'.", command);
}
}
@Override
public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) {
scheduler.schedule(() -> {
updateThing((AbstractOpenWeatherMapHandler) childHandler, childThing);
determineBridgeStatus();
}, INITIAL_DELAY_IN_SECONDS, TimeUnit.SECONDS);
}
@Override
public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
determineBridgeStatus();
}
private void determineBridgeStatus() {
ThingStatus status = ThingStatus.OFFLINE;
for (Thing thing : getThing().getThings()) {
if (ThingStatus.ONLINE.equals(thing.getStatus())) {
status = ThingStatus.ONLINE;
break;
}
}
updateStatus(status);
}
private void updateThings() {
ThingStatus status = ThingStatus.OFFLINE;
for (Thing thing : getThing().getThings()) {
if (ThingStatus.ONLINE.equals(updateThing((AbstractOpenWeatherMapHandler) thing.getHandler(), thing))) {
status = ThingStatus.ONLINE;
}
}
updateStatus(status);
}
private ThingStatus updateThing(@Nullable AbstractOpenWeatherMapHandler handler, Thing thing) {
if (handler != null && connection != null) {
handler.updateData(connection);
return thing.getStatus();
} else {
logger.debug("Cannot update weather data of thing '{}' as location handler is null.", thing.getUID());
return ThingStatus.OFFLINE;
}
}
public OpenWeatherMapAPIConfiguration getOpenWeatherMapAPIConfig() {
return config;
}
}

View File

@@ -0,0 +1,204 @@
/**
* Copyright (c) 2010-2020 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.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapUVIndexConfiguration;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapCommunicationException;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConfigurationException;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonUVIndexData;
import org.openhab.core.i18n.TimeZoneProvider;
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.binding.builder.ThingBuilder;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonSyntaxException;
/**
* The {@link OpenWeatherMapUVIndexHandler} is responsible for handling commands, which are sent to one of the
* channels.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapUVIndexHandler extends AbstractOpenWeatherMapHandler {
private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapUVIndexHandler.class);
private static final String CHANNEL_GROUP_FORECAST_PREFIX = "forecastDay";
private static final Pattern CHANNEL_GROUP_FORECAST_PREFIX_PATTERN = Pattern
.compile(CHANNEL_GROUP_FORECAST_PREFIX + "([0-9]*)");
// keeps track of the parsed count
private int forecastDays = 6;
private @Nullable OpenWeatherMapJsonUVIndexData uvindexData;
private @Nullable List<OpenWeatherMapJsonUVIndexData> uvindexForecastData;
public OpenWeatherMapUVIndexHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
super(thing, timeZoneProvider);
}
@Override
public void initialize() {
super.initialize();
logger.debug("Initialize OpenWeatherMapUVIndexHandler handler '{}'.", getThing().getUID());
OpenWeatherMapUVIndexConfiguration config = getConfigAs(OpenWeatherMapUVIndexConfiguration.class);
boolean configValid = true;
int newForecastDays = config.forecastDays;
if (newForecastDays < 1 || newForecastDays > 8) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-not-supported-uvindex-number-of-days");
configValid = false;
}
if (configValid) {
logger.debug("Rebuilding thing '{}'.", getThing().getUID());
List<Channel> toBeAddedChannels = new ArrayList<>();
List<Channel> toBeRemovedChannels = new ArrayList<>();
if (forecastDays != newForecastDays) {
logger.debug("Rebuilding UV index channel groups.");
if (forecastDays > newForecastDays) {
if (newForecastDays < 2) {
toBeRemovedChannels.addAll(removeChannelsOfGroup(CHANNEL_GROUP_FORECAST_TOMORROW));
}
for (int i = newForecastDays; i < forecastDays; ++i) {
toBeRemovedChannels
.addAll(removeChannelsOfGroup(CHANNEL_GROUP_FORECAST_PREFIX + Integer.toString(i)));
}
} else {
if (forecastDays <= 1 && newForecastDays > 1) {
toBeAddedChannels.addAll(
createChannelsForGroup(CHANNEL_GROUP_FORECAST_TOMORROW, CHANNEL_GROUP_TYPE_UVINDEX));
}
for (int i = (forecastDays < 2) ? 2 : forecastDays; i < newForecastDays; ++i) {
toBeAddedChannels.addAll(createChannelsForGroup(
CHANNEL_GROUP_FORECAST_PREFIX + Integer.toString(i), CHANNEL_GROUP_TYPE_UVINDEX));
}
}
forecastDays = newForecastDays;
}
ThingBuilder builder = editThing().withoutChannels(toBeRemovedChannels);
for (Channel channel : toBeAddedChannels) {
builder.withChannel(channel);
}
updateThing(builder.build());
}
}
@Override
protected boolean requestData(OpenWeatherMapConnection connection)
throws OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
logger.debug("Update UV Index data of thing '{}'.", getThing().getUID());
try {
uvindexData = connection.getUVIndexData(location);
if (forecastDays > 0) {
uvindexForecastData = connection.getUVIndexForecastData(location, forecastDays);
}
return true;
} catch (JsonSyntaxException e) {
logger.debug("JsonSyntaxException occurred during execution: {}", e.getLocalizedMessage(), e);
return false;
}
}
@Override
protected void updateChannel(ChannelUID channelUID) {
switch (channelUID.getGroupId()) {
case CHANNEL_GROUP_CURRENT_UVINDEX:
updateUVIndexChannel(channelUID);
break;
case CHANNEL_GROUP_FORECAST_TOMORROW:
updateUVIndexForecastChannel(channelUID, 1);
break;
default:
Matcher m = CHANNEL_GROUP_FORECAST_PREFIX_PATTERN.matcher(channelUID.getGroupId());
int i;
if (m.find() && (i = Integer.parseInt(m.group(1))) > 1 && i <= 8) {
updateUVIndexForecastChannel(channelUID, i);
}
break;
}
}
/**
* Update the channel from the last OpenWeatherMap data retrieved.
*
* @param channelUID the id identifying the channel to be updated
*/
private void updateUVIndexChannel(ChannelUID channelUID) {
String channelId = channelUID.getIdWithoutGroup();
String channelGroupId = channelUID.getGroupId();
OpenWeatherMapJsonUVIndexData localUVIndexData = uvindexData;
if (localUVIndexData != null) {
State state = UnDefType.UNDEF;
switch (channelId) {
case CHANNEL_TIME_STAMP:
state = getDateTimeTypeState(localUVIndexData.getDate());
break;
case CHANNEL_UVINDEX:
state = getDecimalTypeState(localUVIndexData.getValue());
break;
}
logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
updateState(channelUID, state);
} else {
logger.debug("No UV Index data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
}
}
/**
* Update the channel from the last OpenWeatherMap data retrieved.
*
* @param channelUID the id identifying the channel to be updated
* @param count
*/
private void updateUVIndexForecastChannel(ChannelUID channelUID, int count) {
String channelId = channelUID.getIdWithoutGroup();
String channelGroupId = channelUID.getGroupId();
List<OpenWeatherMapJsonUVIndexData> localUVIndexForecastData = uvindexForecastData;
if (localUVIndexForecastData != null && localUVIndexForecastData.size() >= count) {
OpenWeatherMapJsonUVIndexData forecastData = localUVIndexForecastData.get(count - 1);
State state = UnDefType.UNDEF;
switch (channelId) {
case CHANNEL_TIME_STAMP:
state = getDateTimeTypeState(forecastData.getDate());
break;
case CHANNEL_UVINDEX:
state = getDecimalTypeState(forecastData.getValue());
break;
}
logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
updateState(channelUID, state);
} else {
logger.debug("No UV Index data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
}
}
}

View File

@@ -0,0 +1,497 @@
/**
* Copyright (c) 2010-2020 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.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
import static org.openhab.core.library.unit.MetricPrefix.*;
import static org.openhab.core.library.unit.SIUnits.*;
import static org.openhab.core.library.unit.SmartHomeUnits.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpResponseException;
import org.openhab.binding.openweathermap.internal.config.OpenWeatherMapWeatherAndForecastConfiguration;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapCommunicationException;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConfigurationException;
import org.openhab.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonDailyForecastData;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonHourlyForecastData;
import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonWeatherData;
import org.openhab.binding.openweathermap.internal.dto.base.Rain;
import org.openhab.binding.openweathermap.internal.dto.base.Snow;
import org.openhab.binding.openweathermap.internal.dto.forecast.daily.FeelsLikeTemp;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.TimeZoneProvider;
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.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonSyntaxException;
/**
* The {@link OpenWeatherMapWeatherAndForecastHandler} is responsible for handling commands, which are sent to one of
* the channels.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapWeatherAndForecastHandler extends AbstractOpenWeatherMapHandler {
private final Logger logger = LoggerFactory.getLogger(OpenWeatherMapWeatherAndForecastHandler.class);
private static final String CHANNEL_GROUP_HOURLY_FORECAST_PREFIX = "forecastHours";
private static final String CHANNEL_GROUP_DAILY_FORECAST_PREFIX = "forecastDay";
private static final Pattern CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN = Pattern
.compile(CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + "([0-9]*)");
private static final Pattern CHANNEL_GROUP_DAILY_FORECAST_PREFIX_PATTERN = Pattern
.compile(CHANNEL_GROUP_DAILY_FORECAST_PREFIX + "([0-9]*)");
// keeps track of the parsed counts
private int forecastHours = 24;
private int forecastDays = 6;
private @Nullable OpenWeatherMapJsonWeatherData weatherData;
private @Nullable OpenWeatherMapJsonHourlyForecastData hourlyForecastData;
private @Nullable OpenWeatherMapJsonDailyForecastData dailyForecastData;
public OpenWeatherMapWeatherAndForecastHandler(Thing thing, final TimeZoneProvider timeZoneProvider) {
super(thing, timeZoneProvider);
}
@Override
public void initialize() {
super.initialize();
logger.debug("Initialize OpenWeatherMapWeatherAndForecastHandler handler '{}'.", getThing().getUID());
OpenWeatherMapWeatherAndForecastConfiguration config = getConfigAs(
OpenWeatherMapWeatherAndForecastConfiguration.class);
boolean configValid = true;
int newForecastHours = config.forecastHours;
if (newForecastHours < 0 || newForecastHours > 120 || newForecastHours % 3 != 0) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-not-supported-number-of-hours");
configValid = false;
}
int newForecastDays = config.forecastDays;
if (newForecastDays < 0 || newForecastDays > 16) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-not-supported-number-of-days");
configValid = false;
}
if (configValid) {
logger.debug("Rebuilding thing '{}'.", getThing().getUID());
List<Channel> toBeAddedChannels = new ArrayList<>();
List<Channel> toBeRemovedChannels = new ArrayList<>();
if (forecastHours != newForecastHours) {
logger.debug("Rebuilding hourly forecast channel groups.");
if (forecastHours > newForecastHours) {
for (int i = newForecastHours + 3; i <= forecastHours; i += 3) {
toBeRemovedChannels.addAll(removeChannelsOfGroup(
CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i)));
}
} else {
for (int i = forecastHours + 3; i <= newForecastHours; i += 3) {
toBeAddedChannels.addAll(createChannelsForGroup(
CHANNEL_GROUP_HOURLY_FORECAST_PREFIX + ((i < 10) ? "0" : "") + Integer.toString(i),
CHANNEL_GROUP_TYPE_HOURLY_FORECAST));
}
}
forecastHours = newForecastHours;
}
if (forecastDays != newForecastDays) {
logger.debug("Rebuilding daily forecast channel groups.");
if (forecastDays > newForecastDays) {
if (newForecastDays < 1) {
toBeRemovedChannels.addAll(removeChannelsOfGroup(CHANNEL_GROUP_FORECAST_TODAY));
}
if (newForecastDays < 2) {
toBeRemovedChannels.addAll(removeChannelsOfGroup(CHANNEL_GROUP_FORECAST_TOMORROW));
}
for (int i = newForecastDays; i < forecastDays; ++i) {
toBeRemovedChannels.addAll(
removeChannelsOfGroup(CHANNEL_GROUP_DAILY_FORECAST_PREFIX + Integer.toString(i)));
}
} else {
if (forecastDays == 0 && newForecastDays > 0) {
toBeAddedChannels.addAll(createChannelsForGroup(CHANNEL_GROUP_FORECAST_TODAY,
CHANNEL_GROUP_TYPE_DAILY_FORECAST));
}
if (forecastDays <= 1 && newForecastDays > 1) {
toBeAddedChannels.addAll(createChannelsForGroup(CHANNEL_GROUP_FORECAST_TOMORROW,
CHANNEL_GROUP_TYPE_DAILY_FORECAST));
}
for (int i = (forecastDays < 2) ? 2 : forecastDays; i < newForecastDays; ++i) {
toBeAddedChannels.addAll(
createChannelsForGroup(CHANNEL_GROUP_DAILY_FORECAST_PREFIX + Integer.toString(i),
CHANNEL_GROUP_TYPE_DAILY_FORECAST));
}
}
forecastDays = newForecastDays;
}
ThingBuilder builder = editThing().withoutChannels(toBeRemovedChannels);
for (Channel channel : toBeAddedChannels) {
builder.withChannel(channel);
}
updateThing(builder.build());
}
}
@Override
protected boolean requestData(OpenWeatherMapConnection connection)
throws OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
logger.debug("Update weather and forecast data of thing '{}'.", getThing().getUID());
try {
weatherData = connection.getWeatherData(location);
if (forecastHours > 0) {
hourlyForecastData = connection.getHourlyForecastData(location, forecastHours / 3);
}
if (forecastDays > 0) {
try {
dailyForecastData = connection.getDailyForecastData(location, forecastDays);
} catch (OpenWeatherMapConfigurationException e) {
if (e.getCause() instanceof HttpResponseException) {
forecastDays = 0;
Configuration editConfig = editConfiguration();
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());
updateThing(editThing().withoutChannels(channels).build());
} else {
throw e;
}
}
}
return true;
} catch (JsonSyntaxException e) {
logger.debug("JsonSyntaxException occurred during execution: {}", e.getLocalizedMessage(), e);
return false;
}
}
@Override
protected void updateChannel(ChannelUID channelUID) {
String channelGroupId = channelUID.getGroupId();
switch (channelGroupId) {
case CHANNEL_GROUP_STATION:
case CHANNEL_GROUP_CURRENT_WEATHER:
updateCurrentChannel(channelUID);
break;
case CHANNEL_GROUP_FORECAST_TODAY:
updateDailyForecastChannel(channelUID, 0);
break;
case CHANNEL_GROUP_FORECAST_TOMORROW:
updateDailyForecastChannel(channelUID, 1);
break;
default:
int i;
Matcher hourlyForecastMatcher = CHANNEL_GROUP_HOURLY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
if (hourlyForecastMatcher.find() && (i = Integer.parseInt(hourlyForecastMatcher.group(1))) >= 3
&& i <= 120) {
updateHourlyForecastChannel(channelUID, (i / 3) - 1);
break;
}
Matcher dailyForecastMatcher = CHANNEL_GROUP_DAILY_FORECAST_PREFIX_PATTERN.matcher(channelGroupId);
if (dailyForecastMatcher.find() && (i = Integer.parseInt(dailyForecastMatcher.group(1))) > 1
&& i <= 16) {
updateDailyForecastChannel(channelUID, i);
break;
}
break;
}
}
/**
* Update the channel from the last OpenWeatherMap data retrieved.
*
* @param channelUID the id identifying the channel to be updated
*/
private void updateCurrentChannel(ChannelUID channelUID) {
String channelId = channelUID.getIdWithoutGroup();
String channelGroupId = channelUID.getGroupId();
OpenWeatherMapJsonWeatherData localWeatherData = weatherData;
if (localWeatherData != null) {
State state = UnDefType.UNDEF;
switch (channelId) {
case CHANNEL_STATION_ID:
state = getStringTypeState(localWeatherData.getId().toString());
break;
case CHANNEL_STATION_NAME:
state = getStringTypeState(localWeatherData.getName());
break;
case CHANNEL_STATION_LOCATION:
state = getPointTypeState(localWeatherData.getCoord().getLat(),
localWeatherData.getCoord().getLon());
break;
case CHANNEL_TIME_STAMP:
state = getDateTimeTypeState(localWeatherData.getDt());
break;
case CHANNEL_CONDITION:
if (!localWeatherData.getWeather().isEmpty()) {
state = getStringTypeState(localWeatherData.getWeather().get(0).getDescription());
}
break;
case CHANNEL_CONDITION_ID:
if (!localWeatherData.getWeather().isEmpty()) {
state = getStringTypeState(localWeatherData.getWeather().get(0).getId().toString());
}
break;
case CHANNEL_CONDITION_ICON:
if (!localWeatherData.getWeather().isEmpty()) {
state = getRawTypeState(OpenWeatherMapConnection
.getWeatherIcon(localWeatherData.getWeather().get(0).getIcon()));
}
break;
case CHANNEL_CONDITION_ICON_ID:
if (!localWeatherData.getWeather().isEmpty()) {
state = getStringTypeState(localWeatherData.getWeather().get(0).getIcon());
}
break;
case CHANNEL_TEMPERATURE:
state = getQuantityTypeState(localWeatherData.getMain().getTemp(), CELSIUS);
break;
case CHANNEL_APPARENT_TEMPERATURE:
state = getQuantityTypeState(localWeatherData.getMain().getFeelsLikeTemp(), CELSIUS);
break;
case CHANNEL_PRESSURE:
state = getQuantityTypeState(localWeatherData.getMain().getPressure(), HECTO(PASCAL));
break;
case CHANNEL_HUMIDITY:
state = getQuantityTypeState(localWeatherData.getMain().getHumidity(), PERCENT);
break;
case CHANNEL_WIND_SPEED:
state = getQuantityTypeState(localWeatherData.getWind().getSpeed(), METRE_PER_SECOND);
break;
case CHANNEL_WIND_DIRECTION:
state = getQuantityTypeState(localWeatherData.getWind().getDeg(), DEGREE_ANGLE);
break;
case CHANNEL_GUST_SPEED:
state = getQuantityTypeState(localWeatherData.getWind().getGust(), METRE_PER_SECOND);
break;
case CHANNEL_CLOUDINESS:
state = getQuantityTypeState(localWeatherData.getClouds().getAll(), PERCENT);
break;
case CHANNEL_RAIN:
Rain rain = localWeatherData.getRain();
state = getQuantityTypeState(rain == null ? 0 : rain.getVolume(), MILLI(METRE));
break;
case CHANNEL_SNOW:
Snow snow = localWeatherData.getSnow();
state = getQuantityTypeState(snow == null ? 0 : snow.getVolume(), MILLI(METRE));
break;
case CHANNEL_VISIBILITY:
Integer localVisibility = localWeatherData.getVisibility();
state = localVisibility == null ? UnDefType.UNDEF
: new QuantityType<>(localVisibility, METRE).toUnit(KILO(METRE));
if (state == null) {
logger.debug("State conversion failed, cannot update state.");
return;
}
break;
}
logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
updateState(channelUID, state);
} else {
logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
}
}
/**
* Update the channel from the last OpenWeatherMap data retrieved.
*
* @param channelUID the id identifying the channel to be updated
* @param count
*/
private void updateHourlyForecastChannel(ChannelUID channelUID, int count) {
String channelId = channelUID.getIdWithoutGroup();
String channelGroupId = channelUID.getGroupId();
OpenWeatherMapJsonHourlyForecastData localHourlyForecastData = hourlyForecastData;
if (localHourlyForecastData != null && localHourlyForecastData.getList().size() > count) {
org.openhab.binding.openweathermap.internal.dto.forecast.hourly.List forecastData = localHourlyForecastData
.getList().get(count);
State state = UnDefType.UNDEF;
switch (channelId) {
case CHANNEL_TIME_STAMP:
state = getDateTimeTypeState(forecastData.getDt());
break;
case CHANNEL_CONDITION:
if (!forecastData.getWeather().isEmpty()) {
state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
}
break;
case CHANNEL_CONDITION_ID:
if (!forecastData.getWeather().isEmpty()) {
state = getStringTypeState(forecastData.getWeather().get(0).getId().toString());
}
break;
case CHANNEL_CONDITION_ICON:
if (!forecastData.getWeather().isEmpty()) {
state = getRawTypeState(
OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
}
break;
case CHANNEL_CONDITION_ICON_ID:
if (!forecastData.getWeather().isEmpty()) {
state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
}
break;
case CHANNEL_TEMPERATURE:
state = getQuantityTypeState(forecastData.getMain().getTemp(), CELSIUS);
break;
case CHANNEL_APPARENT_TEMPERATURE:
state = getQuantityTypeState(forecastData.getMain().getFeelsLikeTemp(), CELSIUS);
break;
case CHANNEL_MIN_TEMPERATURE:
state = getQuantityTypeState(forecastData.getMain().getTempMin(), CELSIUS);
break;
case CHANNEL_MAX_TEMPERATURE:
state = getQuantityTypeState(forecastData.getMain().getTempMax(), CELSIUS);
break;
case CHANNEL_PRESSURE:
state = getQuantityTypeState(forecastData.getMain().getPressure(), HECTO(PASCAL));
break;
case CHANNEL_HUMIDITY:
state = getQuantityTypeState(forecastData.getMain().getHumidity(), PERCENT);
break;
case CHANNEL_WIND_SPEED:
state = getQuantityTypeState(forecastData.getWind().getSpeed(), METRE_PER_SECOND);
break;
case CHANNEL_WIND_DIRECTION:
state = getQuantityTypeState(forecastData.getWind().getDeg(), DEGREE_ANGLE);
break;
case CHANNEL_GUST_SPEED:
state = getQuantityTypeState(forecastData.getWind().getGust(), METRE_PER_SECOND);
break;
case CHANNEL_CLOUDINESS:
state = getQuantityTypeState(forecastData.getClouds().getAll(), PERCENT);
break;
case CHANNEL_RAIN:
Rain rain = forecastData.getRain();
state = getQuantityTypeState(rain == null ? 0 : rain.getVolume(), MILLI(METRE));
break;
case CHANNEL_SNOW:
Snow snow = forecastData.getSnow();
state = getQuantityTypeState(snow == null ? 0 : snow.getVolume(), MILLI(METRE));
break;
}
logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
updateState(channelUID, state);
} else {
logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
}
}
/**
* Update the channel from the last OpenWeatherMap data retrieved.
*
* @param channelUID the id identifying the channel to be updated
* @param count
*/
private void updateDailyForecastChannel(ChannelUID channelUID, int count) {
String channelId = channelUID.getIdWithoutGroup();
String channelGroupId = channelUID.getGroupId();
OpenWeatherMapJsonDailyForecastData localDailyForecastData = dailyForecastData;
if (localDailyForecastData != null && localDailyForecastData.getList().size() > count) {
org.openhab.binding.openweathermap.internal.dto.forecast.daily.List forecastData = localDailyForecastData
.getList().get(count);
State state = UnDefType.UNDEF;
switch (channelId) {
case CHANNEL_TIME_STAMP:
state = getDateTimeTypeState(forecastData.getDt());
break;
case CHANNEL_CONDITION:
if (!forecastData.getWeather().isEmpty()) {
state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
}
break;
case CHANNEL_CONDITION_ID:
if (!forecastData.getWeather().isEmpty()) {
state = getStringTypeState(forecastData.getWeather().get(0).getId().toString());
}
break;
case CHANNEL_CONDITION_ICON:
if (!forecastData.getWeather().isEmpty()) {
state = getRawTypeState(
OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
}
break;
case CHANNEL_CONDITION_ICON_ID:
if (!forecastData.getWeather().isEmpty()) {
state = getStringTypeState(forecastData.getWeather().get(0).getIcon());
}
break;
case CHANNEL_MIN_TEMPERATURE:
state = getQuantityTypeState(forecastData.getTemp().getMin(), CELSIUS);
break;
case CHANNEL_MAX_TEMPERATURE:
state = getQuantityTypeState(forecastData.getTemp().getMax(), CELSIUS);
break;
case CHANNEL_APPARENT_TEMPERATURE:
FeelsLikeTemp feelsLikeTemp = forecastData.getFeelsLikeTemp();
if (feelsLikeTemp != null) {
state = getQuantityTypeState(feelsLikeTemp.getDay(), CELSIUS);
}
break;
case CHANNEL_PRESSURE:
state = getQuantityTypeState(forecastData.getPressure(), HECTO(PASCAL));
break;
case CHANNEL_HUMIDITY:
state = getQuantityTypeState(forecastData.getHumidity(), PERCENT);
break;
case CHANNEL_WIND_SPEED:
state = getQuantityTypeState(forecastData.getSpeed(), METRE_PER_SECOND);
break;
case CHANNEL_WIND_DIRECTION:
state = getQuantityTypeState(forecastData.getDeg(), DEGREE_ANGLE);
break;
case CHANNEL_GUST_SPEED:
state = getQuantityTypeState(forecastData.getGust(), METRE_PER_SECOND);
break;
case CHANNEL_CLOUDINESS:
state = getQuantityTypeState(forecastData.getClouds(), PERCENT);
break;
case CHANNEL_RAIN:
Double rain = forecastData.getRain();
state = getQuantityTypeState(rain == null ? 0 : rain, MILLI(METRE));
break;
case CHANNEL_SNOW:
Double snow = forecastData.getSnow();
state = getQuantityTypeState(snow == null ? 0 : snow, MILLI(METRE));
break;
}
logger.debug("Update channel '{}' of group '{}' with new state '{}'.", channelId, channelGroupId, state);
updateState(channelUID, state);
} else {
logger.debug("No weather data available to update channel '{}' of group '{}'.", channelId, channelGroupId);
}
}
}

View File

@@ -0,0 +1,238 @@
/**
* Copyright (c) 2010-2020 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.utils;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.core.ConfigConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a simple file based cache implementation.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class ByteArrayFileCache {
private final Logger logger = LoggerFactory.getLogger(ByteArrayFileCache.class);
private static final String CACHE_FOLDER_NAME = "cache";
public static final char EXTENSION_SEPARATOR = '.';
protected final File cacheFolder;
public ByteArrayFileCache(String servicePID) {
// TODO track and limit folder size
// TODO support user specific folder
cacheFolder = new File(new File(new File(ConfigConstants.getUserDataFolder()), CACHE_FOLDER_NAME), servicePID);
if (!cacheFolder.exists()) {
logger.debug("Creating cache folder '{}'", cacheFolder.getAbsolutePath());
cacheFolder.mkdirs();
}
logger.debug("Using cache folder '{}'", cacheFolder.getAbsolutePath());
}
/**
* Adds a file to the cache. If the cache previously contained a file for the key, the old file is replaced by the
* new content.
*
* @param key the key with which the file is to be associated
* @param content the content for the file to be associated with the specified key
*/
public void put(String key, byte[] content) {
writeFile(getUniqueFile(key), content);
}
/**
* Adds a file to the cache.
*
* @param key the key with which the file is to be associated
* @param content the content for the file to be associated with the specified key
*/
public void putIfAbsent(String key, byte[] content) {
File fileInCache = getUniqueFile(key);
if (fileInCache.exists()) {
logger.debug("File '{}' present in cache", fileInCache.getName());
} else {
writeFile(fileInCache, content);
}
}
/**
* Adds a file to the cache and returns the content of the file.
*
* @param key the key with which the file is to be associated
* @param content the content for the file to be associated with the specified key
* @return the content of the file associated with the given key
*/
public byte[] putIfAbsentAndGet(String key, byte[] content) {
putIfAbsent(key, content);
// return get(key);
return content;
}
/**
* Writes the given content to the given {@link File}.
*
* @param fileInCache the {@link File}
* @param content the content to be written
*/
private void writeFile(File fileInCache, byte[] content) {
logger.debug("Caching file '{}'", fileInCache.getName());
try {
Files.write(fileInCache.toPath(), content);
} catch (IOException e) {
logger.warn("Could not write file '{}' to cache", fileInCache.getName(), e);
}
}
/**
* Checks if the key is present in the cache.
*
* @param key the key whose presence in the cache is to be tested
* @return true if the cache contains a file for the specified key
*/
public boolean containsKey(String key) {
return getUniqueFile(key).exists();
}
/**
* Removes the file associated with the given key from the cache.
*
* @param key the key whose associated file is to be removed
*/
public void remove(String key) {
deleteFile(getUniqueFile(key));
}
/**
* Deletes the given {@link File}.
*
* @param fileInCache the {@link File}
*/
private void deleteFile(File fileInCache) {
if (fileInCache.exists()) {
logger.debug("Deleting file '{}' from cache", fileInCache.getName());
fileInCache.delete();
} else {
logger.debug("File '{}' not found in cache", fileInCache.getName());
}
}
/**
* Removes all files from the cache.
*/
public void clear() {
File[] filesInCache = cacheFolder.listFiles();
if (filesInCache != null && filesInCache.length > 0) {
logger.debug("Deleting all files from cache");
Arrays.stream(filesInCache).forEach(File::delete);
}
}
/**
* Returns the content of the file associated with the given key, if it is present.
*
* @param key the key whose associated file is to be returned
* @return the content of the file associated with the given key
*/
public byte[] get(String key) {
return readFile(getUniqueFile(key));
}
/**
* Reads the content from the given {@link File}, if it is present.
*
* @param fileInCache the {@link File}
* @return the content of the file
*/
private byte[] readFile(File fileInCache) {
if (fileInCache.exists()) {
logger.debug("Reading file '{}' from cache", fileInCache.getName());
try {
return Files.readAllBytes(fileInCache.toPath());
} catch (IOException e) {
logger.warn("Could not read file '{}' from cache", fileInCache.getName(), e);
}
} else {
logger.debug("File '{}' not found in cache", fileInCache.getName());
}
return new byte[0];
}
/**
* Creates a unique {@link File} from the key with which the file is to be associated.
*
* @param key the key with which the file is to be associated
* @return unique file for the file associated with the given key
*/
private File getUniqueFile(String key) {
// TODO: store / cache file internally for faster operations
String fileExtension = getFileExtension(key);
return new File(cacheFolder,
getUniqueFileName(key) + (fileExtension == null ? "" : EXTENSION_SEPARATOR + fileExtension));
}
/**
* Gets the extension of a file name.
*
* @param fileName the file name to retrieve the extension of
* @return the extension of the file or null if none exists
*/
private @Nullable String getFileExtension(String fileName) {
int index = fileName.lastIndexOf(EXTENSION_SEPARATOR);
// exclude file names starting with a dot
if (index > 0) {
return fileName.substring(index + 1);
} else {
return null;
}
}
/**
* Creates a unique file name from the key with which the file is to be associated.
*
* @param key the key with which the file is to be associated
* @return unique file name for the file associated with the given key
*/
private String getUniqueFileName(String key) {
try {
byte[] bytesOfFileName = key.getBytes(StandardCharsets.UTF_8);
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] md5Hash = md.digest(bytesOfFileName);
BigInteger bigInt = new BigInteger(1, md5Hash);
StringBuilder fileNameHash = new StringBuilder(bigInt.toString(16));
// Now we need to zero pad it if you actually want the full 32 chars
while (fileNameHash.length() < 32) {
fileNameHash.insert(0, "0");
}
return fileNameHash.toString();
} catch (NoSuchAlgorithmException ex) {
// should not happen
logger.error("Could not create MD5 hash for key '{}'", key, ex);
return key.toString();
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="openweathermap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>OpenWeatherMap Binding</name>
<description>OpenWeatherMap - Current weather and forecasts in your city.</description>
<author>Christoph Weitkamp</author>
</binding:binding>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
<!-- Config for OpenWeatherMap Binding -->
<config-description uri="bridge-type:openweathermap:weather-api">
<parameter name="apikey" type="text" required="true">
<context>password</context>
<label>API Key</label>
<description>API key to access the OpenWeatherMap API.</description>
</parameter>
<parameter name="refreshInterval" type="integer" min="10" unit="min">
<label>Refresh Interval</label>
<description>Specifies the refresh interval (in minutes).</description>
<default>60</default>
</parameter>
<parameter name="language" type="text">
<label>Language</label>
<description>Language to be used by the OpenWeatherMap API.</description>
<options>
<option value="ar">Arabic</option>
<option value="bg">Bulgarian</option>
<option value="ca">Catalan</option>
<option value="zh_cn">Chinese - Simplified</option>
<option value="zh_tw">Chinese - Traditional</option>
<option value="hr">Croatian</option>
<option value="cz">Czech</option>
<option value="nl">Dutch</option>
<option value="en">English</option>
<option value="fi">Finnish</option>
<option value="fr">French</option>
<option value="gl">Galician</option>
<option value="de">German</option>
<option value="el">Greek</option>
<option value="hu">Hungarian</option>
<option value="it">Italian</option>
<option value="ja">Japanese</option>
<option value="kr">Korean</option>
<option value="la">Latvian</option>
<option value="lt">Lithuanian</option>
<option value="mk">Macedonian</option>
<option value="fa">Persian (Farsi)</option>
<option value="pl">Polish</option>
<option value="pt">Portuguese</option>
<option value="ro">Romanian</option>
<option value="ru">Russian</option>
<option value="sk">Slovak</option>
<option value="se">Swedish</option>
<option value="sl">Slovenian</option>
<option value="es">Spanish</option>
<option value="tr">Turkish</option>
<option value="ua">Ukrainian</option>
<option value="vi">Vietnamese</option>
</options>
</parameter>
</config-description>
<config-description uri="thing-type:openweathermap:weather-and-forecast">
<parameter name="location" type="text" required="true">
<context>location</context>
<label>Location of Weather</label>
<description>Location of weather in geographical coordinates (latitude/longitude/altitude).</description>
</parameter>
<parameter name="forecastHours" type="integer" min="0" max="120" step="3">
<label>Number of Hours</label>
<description>Number of hours for hourly forecast.</description>
<default>24</default>
</parameter>
<parameter name="forecastDays" type="integer" min="0" max="16" step="1">
<label>Number of Days</label>
<description>Number of days for daily forecast.</description>
<default>6</default>
</parameter>
</config-description>
<config-description uri="thing-type:openweathermap:uvindex">
<parameter name="location" type="text" required="true">
<context>location</context>
<label>Location of Weather</label>
<description>Location of weather in geographical coordinates (latitude/longitude/altitude).</description>
</parameter>
<parameter name="forecastDays" type="integer" min="1" max="8" step="1">
<label>Number of Days</label>
<description>Number of days for UV Index forecast.</description>
<default>6</default>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,15 @@
# thing status
offline.conf-error-missing-apikey = The 'apikey' parameter must be configured.
offline.conf-error-invalid-apikey = Invalid API key. Please see https://openweathermap.org/faq#error401 for more info.
offline.conf-error-not-supported-refreshInterval = The 'refreshInterval' parameter must be at least 10 minutes.
offline.conf-error-not-supported-language = The given 'language' parameter is not supported.
offline.conf-error-missing-location = The 'location' parameter must be configured.
offline.conf-error-parsing-location = The 'location' parameter could not be split into latitude and longitude.
offline.conf-error-not-supported-number-of-hours = The 'forecastHours' parameter must be between 0 and 120 - increment 3.
offline.conf-error-not-supported-number-of-days = The 'forecastDays' parameter must be between 0 and 16.
offline.conf-error-not-supported-uvindex-number-of-days = The 'forecastDays' parameter must be between 1 and 8.
# discovery result
discovery.openweathermap.weather-and-forecast.api.local.label = Local Weather And Forecast
discovery.openweathermap.uvindex.api.local.label = Local UV Index

View File

@@ -0,0 +1,263 @@
# binding
binding.openweathermap.name = OpenWeatherMap Binding
binding.openweathermap.description = OpenWeatherMap - Aktuelles Wetter und Prognosen in Ihrer Stadt.
# bridge types
thing-type.openweathermap.weather-api.label = OpenWeatherMap Konto
thing-type.openweathermap.weather-api.description = Ermöglicht den Zugriff auf die OpenWeatherMap API.
# bridge types config
bridge-type.config.openweathermap.weather-api.apikey.label = API Schlüssel
bridge-type.config.openweathermap.weather-api.apikey.description = API Schlüssel für den Zugriff auf die OpenWeatherMap API.
bridge-type.config.openweathermap.weather-api.refreshInterval.label = Abfrageintervall
bridge-type.config.openweathermap.weather-api.refreshInterval.description = Intervall zur Abfrage der OpenWeatherMap API (in min).
bridge-type.config.openweathermap.weather-api.language.label = Sprache
bridge-type.config.openweathermap.weather-api.language.description = Sprache zur Anzeige der Daten.
bridge-type.config.openweathermap.weather-api.language.option.ar = Arabisch
bridge-type.config.openweathermap.weather-api.language.option.bg = Bulgarisch
bridge-type.config.openweathermap.weather-api.language.option.ca = Katalanisch
bridge-type.config.openweathermap.weather-api.language.option.cz = Tschechisch
bridge-type.config.openweathermap.weather-api.language.option.de = Deutsch
bridge-type.config.openweathermap.weather-api.language.option.el = Griechisch
bridge-type.config.openweathermap.weather-api.language.option.en = Englisch
bridge-type.config.openweathermap.weather-api.language.option.es = Spanisch
bridge-type.config.openweathermap.weather-api.language.option.fa = Persisch (Farsi)
bridge-type.config.openweathermap.weather-api.language.option.fi = Finnisch
bridge-type.config.openweathermap.weather-api.language.option.fr = Französisch
bridge-type.config.openweathermap.weather-api.language.option.gl = Galizisch
bridge-type.config.openweathermap.weather-api.language.option.hr = Kroatisch
bridge-type.config.openweathermap.weather-api.language.option.hu = Ungarisch
bridge-type.config.openweathermap.weather-api.language.option.it = Italienisch
bridge-type.config.openweathermap.weather-api.language.option.ja = Japanisch
bridge-type.config.openweathermap.weather-api.language.option.kr = Koreanisch
bridge-type.config.openweathermap.weather-api.language.option.la = Lettisch
bridge-type.config.openweathermap.weather-api.language.option.lt = Litauisch
bridge-type.config.openweathermap.weather-api.language.option.mk = Mazedonisch
bridge-type.config.openweathermap.weather-api.language.option.nl = Holländisch
bridge-type.config.openweathermap.weather-api.language.option.pl = Polnisch
bridge-type.config.openweathermap.weather-api.language.option.pt = Portugiesisch
bridge-type.config.openweathermap.weather-api.language.option.ro = Rumänisch
bridge-type.config.openweathermap.weather-api.language.option.ru = Russisch
bridge-type.config.openweathermap.weather-api.language.option.se = Schwedisch
bridge-type.config.openweathermap.weather-api.language.option.sk = Slowakisch
bridge-type.config.openweathermap.weather-api.language.option.sl = Slowenisch
bridge-type.config.openweathermap.weather-api.language.option.tr = Türkisch
bridge-type.config.openweathermap.weather-api.language.option.ua = Ukrainisch
bridge-type.config.openweathermap.weather-api.language.option.vi = Vietnamesisch
bridge-type.config.openweathermap.weather-api.language.option.zh_cn = Chinesisch - Simplified
bridge-type.config.openweathermap.weather-api.language.option.zh_tw = Chinesisch - Traditional
# thing types
thing-type.openweathermap.weather-and-forecast.label = Wetterinformationen
thing-type.openweathermap.weather-and-forecast.description = Ermöglicht die Anzeige der aktuellen Wetterinformationen und der Wettervorhersage.
thing-type.openweathermap.uvindex.label = UV-Index
thing-type.openweathermap.uvindex.description = Ermöglicht die Anzeige des aktuellen UV-Index.
# thing types config
thing-type.config.openweathermap.weather-and-forecast.location.label = Ort der Wetterdaten
thing-type.config.openweathermap.weather-and-forecast.location.description = Ort der Wetterdaten in geographischen Koordinaten (Breitengrad/Längengrad/Höhe).
thing-type.config.openweathermap.weather-and-forecast.forecastHours.label = Stunden
thing-type.config.openweathermap.weather-and-forecast.forecastHours.description = Anzahl der Stunden für die Wettervorhersage.
thing-type.config.openweathermap.weather-and-forecast.forecastDays.label = Tage
thing-type.config.openweathermap.weather-and-forecast.forecastDays.description = Anzahl der Tage für die Wettervorhersage.
thing-type.config.openweathermap.uvindex.location.label = Ort der Wetterdaten
thing-type.config.openweathermap.uvindex.location.description = Ort der Wetterdaten in geographischen Koordinaten (Breitengrad/Längengrad/Höhe).
thing-type.config.openweathermap.uvindex.forecastDays.label = Tage
thing-type.config.openweathermap.uvindex.forecastDays.description = Anzahl der Tage für die UV-Index Vorhersage.
# channel group types
channel-group-type.openweathermap.station.label = Wetterstation
channel-group-type.openweathermap.station.description = Fasst Daten über die Wetterstation oder den Ort zusammen.
channel-group-type.openweathermap.weather.label = Aktuelles Wetter
channel-group-type.openweathermap.weather.description = Fasst aktuelle Wetterdaten zusammen.
channel-group-type.openweathermap.hourlyForecast.label = 3 Stunden Wettervorhersage
channel-group-type.openweathermap.hourlyForecast.description = Fasst Daten der 5 Tage / 3 Stunden Wettervorhersage zusammen.
channel-group-type.openweathermap.dailyForecast.label = Tägliche Wettervorhersage
channel-group-type.openweathermap.dailyForecast.description = Fasst Daten der 16 Tage / täglichen Wettervorhersage zusammen.
channel-group-type.openweathermap.uvindex.label = Aktueller UV-Index
channel-group-type.openweathermap.uvindex.description = Fasst aktuelle UV-Index Daten zusammen.
channel-group-type.openweathermap.uvindexForecast.label = UV-Index Vorhersage
channel-group-type.openweathermap.uvindexForecast.description = Fasst Daten der UV-Index Vorhersage zusammen.
# channel groups
thing-type.openweathermap.weather-and-forecast.group.forecastHours03.label = Wettervorhersage für 3 Stunden
thing-type.openweathermap.weather-and-forecast.group.forecastHours03.description = Fasst Daten der Wettervorhersage in den nächsten drei Stunden zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastHours06.label = Wettervorhersage für 6 Stunden
thing-type.openweathermap.weather-and-forecast.group.forecastHours06.description = Fasst Daten der Wettervorhersage in sechs Stunden zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastHours09.label = Wettervorhersage für 9 Stunden
thing-type.openweathermap.weather-and-forecast.group.forecastHours09.description = Fasst Daten der Wettervorhersage in neun Stunden zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastHours12.label = Wettervorhersage für 12 Stunden
thing-type.openweathermap.weather-and-forecast.group.forecastHours12.description = Fasst Daten der Wettervorhersage in zwölf Stunden zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastHours15.label = Wettervorhersage für 15 Stunden
thing-type.openweathermap.weather-and-forecast.group.forecastHours15.description = Fasst Daten der Wettervorhersage in 15 Stunden zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastHours18.label = Wettervorhersage für 18 Stunden
thing-type.openweathermap.weather-and-forecast.group.forecastHours18.description = Fasst Daten der Wettervorhersage in 18 Stunden zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastHours21.label = Wettervorhersage für 21 Stunden
thing-type.openweathermap.weather-and-forecast.group.forecastHours21.description = Fasst Daten der Wettervorhersage in 21 Stunden zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastHours24.label = Wettervorhersage für 24 Stunden
thing-type.openweathermap.weather-and-forecast.group.forecastHours24.description = Fasst Daten der Wettervorhersage in 24 Stunden zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastToday.label = Wettervorhersage für heute
thing-type.openweathermap.weather-and-forecast.group.forecastToday.description = Fasst Daten der heutigen Wettervorhersage zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastTomorrow.label = Wettervorhersage für morgen
thing-type.openweathermap.weather-and-forecast.group.forecastTomorrow.description = Fasst Daten der morgigen Wettervorhersage zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastDay2.label = Wettervorhersage für übermorgen
thing-type.openweathermap.weather-and-forecast.group.forecastDay2.description = Fasst Daten der übermorgigen Wettervorhersage zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastDay3.label = Wettervorhersage für 3 Tage
thing-type.openweathermap.weather-and-forecast.group.forecastDay3.description = Fasst Daten der Wettervorhersage in drei Tagen zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastDay4.label = Wettervorhersage für 4 Tage
thing-type.openweathermap.weather-and-forecast.group.forecastDay4.description = Fasst Daten der Wettervorhersage in vier Tagen zusammen.
thing-type.openweathermap.weather-and-forecast.group.forecastDay5.label = Wettervorhersage für 5 Tage
thing-type.openweathermap.weather-and-forecast.group.forecastDay5.description = Fasst Daten der Wettervorhersage in fünf Tagen zusammen.
thing-type.openweathermap.uvindex.group.forecastTomorrow.label = UV-Index für morgen
thing-type.openweathermap.uvindex.group.forecastTomorrow.description = Fasst Daten der morgigen UV-Index Vorhersage zusammen.
thing-type.openweathermap.uvindex.group.forecastDay2.label = UV-Index für übermorgen
thing-type.openweathermap.uvindex.group.forecastDay2.description = Fasst Daten der übermorgigen UV-Index Vorhersage zusammen.
thing-type.openweathermap.uvindex.group.forecastDay3.label = UV-Index für 3 Tage
thing-type.openweathermap.uvindex.group.forecastDay3.description = Fasst Daten der UV-Index Vorhersage in drei Tagen zusammen.
thing-type.openweathermap.uvindex.group.forecastDay4.label = UV-Index für 4 Tage
thing-type.openweathermap.uvindex.group.forecastDay4.description = Fasst Daten der UV-Index Vorhersage in vier Tagen zusammen.
thing-type.openweathermap.uvindex.group.forecastDay5.label = UV-Index für 5 Tage
thing-type.openweathermap.uvindex.group.forecastDay5.description = Fasst Daten der UV-Index Vorhersage in fünf Tagen zusammen.
# channel types
channel-type.openweathermap.station-id.label = Station-ID
channel-type.openweathermap.station-id.description = Zeigt die ID der Wetterstation oder des Ortes an.
channel-type.openweathermap.station-name.label = Name
channel-type.openweathermap.station-name.description = Zeigt den Namen der Wetterstation oder des Ortes an.
channel-group-type.openweathermap.station.channel.location.label = Ort
channel-group-type.openweathermap.station.channel.location.description = Zeigt den Ort der Wetterstation in geographischen Koordinaten (Breitengrad/Längengrad/Höhe) an.
channel-type.openweathermap.time-stamp.label = Letzte Messung
channel-type.openweathermap.time-stamp.description = Zeigt den Zeitpunkt der letzten Messung an.
channel-type.openweathermap.time-stamp.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
channel-type.openweathermap.hourly-forecast-time-stamp.label = Vorhersage Zeit
channel-type.openweathermap.hourly-forecast-time-stamp.description = Zeigt den Zeitpunkt der Vorhersage an.
channel-type.openweathermap.hourly-forecast-time-stamp.state.pattern = %1$td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS
channel-type.openweathermap.daily-forecast-time-stamp.label = Vorhersage Datum
channel-type.openweathermap.daily-forecast-time-stamp.description = Zeigt das Datum der Vorhersage an.
channel-type.openweathermap.daily-forecast-time-stamp.state.pattern = %1$td.%1$tm.%1$tY
channel-type.openweathermap.condition.label = Wetterlage
channel-type.openweathermap.condition.description = Zeigt die aktuelle Wetterlage an.
channel-type.openweathermap.forecasted-condition.label = Vorhergesagte Wetterlage
channel-type.openweathermap.forecasted-condition.description = Zeigt die vorhergesagte Wetterlage an.
channel-type.openweathermap.condition-id.label = Wetterlage-ID
channel-type.openweathermap.condition-id.description = Zeigt die ID der Wetterlage an.
channel-type.openweathermap.condition-icon.label = Icon
channel-type.openweathermap.condition-icon.description = Zeigt das Icon der Wetterlage an.
channel-type.openweathermap.condition-icon-id.label = Icon-ID
channel-type.openweathermap.condition-icon-id.description = Zeigt die ID für das Icon der Wetterlage an.
channel-type.openweathermap.forecasted-outdoor-temperature.label = Vorhergesagte Temperatur
channel-type.openweathermap.forecasted-outdoor-temperature.description = Zeigt die vorhergesagte Außentemperatur an.
channel-type.openweathermap.forecasted-min-outdoor-temperature.label = Minimale Temperatur
channel-type.openweathermap.forecasted-min-outdoor-temperature.description = Zeigt die vorhergesagte minimale Außentemperatur an.
channel-type.openweathermap.forecasted-max-outdoor-temperature.label = Maximale Temperatur
channel-type.openweathermap.forecasted-max-outdoor-temperature.description = Zeigt die vorhergesagte maximale Außentemperatur an.
channel-type.openweathermap.apparent-temperature.label = Gefühlte Temperatur
channel-type.openweathermap.apparent-temperature.description = Zeigt die gefühlte Außentemperatur an.
channel-type.openweathermap.forecasted-apparent-temperature.label = Vorhergesagte Gefühlte Temperatur
channel-type.openweathermap.forecasted-apparent-temperature.description = Zeigt die vorhergesagte gefühlte Außentemperatur an.
channel-type.openweathermap.forecasted-barometric-pressure.label = Vorhergesagter Luftdruck
channel-type.openweathermap.forecasted-barometric-pressure.description = Zeigt den vorhergesagten Luftdruck an.
channel-type.openweathermap.forecasted-atmospheric-humidity.label = Vorhergesagte Luftfeuchtigkeit
channel-type.openweathermap.forecasted-atmospheric-humidity.description = Zeigt die vorhergesagte Luftfeuchtigkeit an.
channel-type.openweathermap.forecasted-wind-speed.label = Vorhergesagte Windgeschwindigkeit
channel-type.openweathermap.forecasted-wind-speed.description = Zeigt die vorhergesagte Windgeschwindigkeit an.
channel-type.openweathermap.forecasted-wind-direction.label = Vorhergesagte Windrichtung
channel-type.openweathermap.forecasted-wind-direction.description = Zeigt die vorhergesagte Windrichtung an.
channel-type.openweathermap.gust-speed.label = Windböengeschwindigkeit
channel-type.openweathermap.gust-speed.description = Zeigt die aktuelle Windböengeschwindigkeit an.
channel-type.openweathermap.forecasted-gust-speed.label = Vorhergesagte Windböengeschwindigkeit
channel-type.openweathermap.forecasted-gust-speed.description = Zeigt die vorhergesagte Windböengeschwindigkeit an.
channel-type.openweathermap.cloudiness.label = Bewölkung
channel-type.openweathermap.cloudiness.description = Zeigt die aktuelle Bewölkung an.
channel-type.openweathermap.forecasted-cloudiness.label = Vorhergesagte Bewölkung
channel-type.openweathermap.forecasted-cloudiness.description = Zeigt die vorhergesagte Bewölkung an.
channel-type.openweathermap.visibility.label = Sichtweite
channel-type.openweathermap.visibility.description = Zeigt die aktuelle Sichtweite an.
channel-type.openweathermap.rain.label = Regen
channel-type.openweathermap.rain.description = Zeigt den kumulierten Regen der letzten Stunde an.
channel-type.openweathermap.forecasted-rain.label = Vorhergesagter Regen
channel-type.openweathermap.forecasted-rain.description = Zeigt die vorhergesagte Regenmenge an.
channel-type.openweathermap.snow.label = Schnee
channel-type.openweathermap.snow.description = Zeigt den kumulierten Schnee der letzten Stunde an.
channel-type.openweathermap.forecasted-snow.label = Vorhergesagter Schnee
channel-type.openweathermap.forecasted-snow.description = Zeigt die vorhergesagte Schneemenge an.
channel-type.openweathermap.uvindex.label = UV-Index
channel-type.openweathermap.uvindex.description = Zeigt den aktuellen UV-Index an.
channel-type.openweathermap.forecasted-uvindex.label = Vorhergesagter UV-Index
channel-type.openweathermap.forecasted-uvindex.description = Zeigt den vorhergesagten UV-Index an.
# thing status
offline.conf-error-missing-apikey = Der Parameter 'API Schlüssel' muss konfiguriert werden.
offline.conf-error-invalid-apikey = Ungültiger 'API Schlüssel'. Mehr Infos unter https://openweathermap.org/faq#error401.
offline.conf-error-not-supported-refreshInterval = Der Parameter 'Abfrageintervall' muss mindestens 10 min betragen.
offline.conf-error-not-supported-language = Der angegebene Parameter 'Sprache' wird nicht unterstützt.
offline.conf-error-missing-location = Der Parameter 'Ort' muss konfiguriert werden.
offline.conf-error-parsing-location = Der Parameter 'Ort' kann nicht in Latitude und Longitude getrennt werden.
offline.conf-error-not-supported-number-of-hours = Der Parameter 'forecastHours' muss zwischen 0 und 120 liegen - Schrittweite: 3.
offline.conf-error-not-supported-number-of-days = Der Parameter 'forecastDays' muss zwischen 0 und 16 liegen.
offline.conf-error-not-supported-uvindex-number-of-days = Der Parameter 'forecastDays' muss zwischen 1 und 8 liegen.
# discovery result
discovery.openweathermap.weather-and-forecast.api.local.label = Lokales Wetter und Wettervorhersage
discovery.openweathermap.uvindex.api.local.label = Lokaler UV-Index

View File

@@ -0,0 +1,257 @@
# binding
binding.openweathermap.name = Extension OpenWeatherMap
binding.openweathermap.description = L'extension OpenWeatherMap fournit la météo actuelle et les prévisions dans votre ville.
# bridge types
thing-type.openweathermap.weather-api.label = Compte OpenWeatherMap
thing-type.openweathermap.weather-api.description = Fournit un accès à l'API d'OpenWeatherMap.
# bridge types config
bridge-type.config.openweathermap.weather-api.apikey.label = Clé API
bridge-type.config.openweathermap.weather-api.apikey.description = Clé pour accéder à l'API d'OpenWeatherMap.
bridge-type.config.openweathermap.weather-api.refreshInterval.label = Intervalle d'actualisation
bridge-type.config.openweathermap.weather-api.refreshInterval.description = Spécifie l'intervalle d'actualisation (en minutes).
bridge-type.config.openweathermap.weather-api.language.label = Langue
bridge-type.config.openweathermap.weather-api.language.description = Langue à utiliser par l'API d'OpenWeatherMap.
bridge-type.config.openweathermap.weather-api.language.option.ar = Arabe
bridge-type.config.openweathermap.weather-api.language.option.bg = Bulgare
bridge-type.config.openweathermap.weather-api.language.option.ca = Catalan
bridge-type.config.openweathermap.weather-api.language.option.cz = Tchèque
bridge-type.config.openweathermap.weather-api.language.option.de = Allemand
bridge-type.config.openweathermap.weather-api.language.option.el = Grec
bridge-type.config.openweathermap.weather-api.language.option.en = Anglais
bridge-type.config.openweathermap.weather-api.language.option.es = Espagnol
bridge-type.config.openweathermap.weather-api.language.option.fa = Persan (Farsi)
bridge-type.config.openweathermap.weather-api.language.option.fi = Finlandais
bridge-type.config.openweathermap.weather-api.language.option.fr = Français
bridge-type.config.openweathermap.weather-api.language.option.gl = Galicien
bridge-type.config.openweathermap.weather-api.language.option.hr = Croate
bridge-type.config.openweathermap.weather-api.language.option.hu = Hongrois
bridge-type.config.openweathermap.weather-api.language.option.it = Italien
bridge-type.config.openweathermap.weather-api.language.option.ja = Japonais
bridge-type.config.openweathermap.weather-api.language.option.kr = Coréen
bridge-type.config.openweathermap.weather-api.language.option.la = Letton
bridge-type.config.openweathermap.weather-api.language.option.lt = Lituanien
bridge-type.config.openweathermap.weather-api.language.option.mk = Macédonien
bridge-type.config.openweathermap.weather-api.language.option.nl = Néerlandais
bridge-type.config.openweathermap.weather-api.language.option.pl = Polonais
bridge-type.config.openweathermap.weather-api.language.option.pt = Portugais
bridge-type.config.openweathermap.weather-api.language.option.ro = Roumain
bridge-type.config.openweathermap.weather-api.language.option.ru = Russe
bridge-type.config.openweathermap.weather-api.language.option.se = Suédois
bridge-type.config.openweathermap.weather-api.language.option.sk = Slovaque
bridge-type.config.openweathermap.weather-api.language.option.sl = Slovène
bridge-type.config.openweathermap.weather-api.language.option.tr = Turc
bridge-type.config.openweathermap.weather-api.language.option.ua = Ukrainien
bridge-type.config.openweathermap.weather-api.language.option.vi = Vietnamien
bridge-type.config.openweathermap.weather-api.language.option.zh_cn = Chinois - Simplifié
bridge-type.config.openweathermap.weather-api.language.option.zh_tw = Chinois - Traditionnel
# thing types
thing-type.openweathermap.weather-and-forecast.label = Météo actuelle et prévisions
thing-type.openweathermap.weather-and-forecast.description = Fournit les données météorologiques actuelles et les prévisions de l'API d'OpenWeatherMap.
thing-type.openweathermap.uvindex.label = Indice UV
thing-type.openweathermap.uvindex.description = Fournit les données d'indice UV de l'API d'OpenWeatherMap.
# thing types config
thing-type.config.openweathermap.weather-and-forecast.location.label = Localisation
thing-type.config.openweathermap.weather-and-forecast.location.description = Localisation en coordonnées géographiques (latitude, longitude, altitude).
thing-type.config.openweathermap.weather-and-forecast.forecastHours.label = Nombre d'heures
thing-type.config.openweathermap.weather-and-forecast.forecastHours.description = Nombre d'heures pour les prévisions horaires.
thing-type.config.openweathermap.weather-and-forecast.forecastDays.label = Nombre de jours
thing-type.config.openweathermap.weather-and-forecast.forecastDays.description = Nombre de jours pour les prévisions quotidiennes.
thing-type.config.openweathermap.uvindex.location.label = Localisation
thing-type.config.openweathermap.uvindex.location.description = Localisation en coordonnées géographiques (latitude, longitude, altitude).
thing-type.config.openweathermap.uvindex.forecastDays.label = Nombre de jours
thing-type.config.openweathermap.uvindex.forecastDays.description = Nombre de jours pour les prévisions d'indice UV.
# channel group types
channel-group-type.openweathermap.station.label = Station météo
channel-group-type.openweathermap.station.description = Représente une station météo.
channel-group-type.openweathermap.weather.label = Météo actuelle
channel-group-type.openweathermap.weather.description = Représente la météo actuelle.
channel-group-type.openweathermap.hourlyForecast.label = Prévisions à 3 heures
channel-group-type.openweathermap.hourlyForecast.description = Prévisions météorologiques à 5 jours par tranches de 3 heures.
channel-group-type.openweathermap.dailyForecast.label = Prévisions quotidiennes
channel-group-type.openweathermap.dailyForecast.description = Prévisions météorologiques quotidiennes à 16 jours.
channel-group-type.openweathermap.uvindex.label = Indice UV actuel
channel-group-type.openweathermap.uvindex.description = Représente l'indice UV actuel.
channel-group-type.openweathermap.uvindexForecast.label = Indice UV pour aujourd'hui
channel-group-type.openweathermap.uvindexForecast.description = Représente les prévisions l'indice UV pour aujourd'hui.
# channel groups
thing-type.openweathermap.weather-and-forecast.group.forecastHours03.label = Prévisions dans 3 heures
thing-type.openweathermap.weather-and-forecast.group.forecastHours03.description = Représente les prévisions météorologiques pour les 3 prochaines heures.
thing-type.openweathermap.weather-and-forecast.group.forecastHours06.label = Prévisions dans 6 heures
thing-type.openweathermap.weather-and-forecast.group.forecastHours06.description = Représente les prévisions météorologiques dans 6 heures.
thing-type.openweathermap.weather-and-forecast.group.forecastHours09.label = Prévisions dans 9 heures
thing-type.openweathermap.weather-and-forecast.group.forecastHours09.description = Représente les prévisions météorologiques dans 9 heures.
thing-type.openweathermap.weather-and-forecast.group.forecastHours12.label = Prévisions dans 12 heures
thing-type.openweathermap.weather-and-forecast.group.forecastHours12.description = Représente les prévisions météorologiques dans 12 heures.
thing-type.openweathermap.weather-and-forecast.group.forecastHours15.label = Prévisions dans 15 heures
thing-type.openweathermap.weather-and-forecast.group.forecastHours15.description = Représente les prévisions météorologiques dans 15 heures.
thing-type.openweathermap.weather-and-forecast.group.forecastHours18.label = Prévisions dans 18 heures
thing-type.openweathermap.weather-and-forecast.group.forecastHours18.description = Représente les prévisions météorologiques dans 18 heures.
thing-type.openweathermap.weather-and-forecast.group.forecastHours21.label = Prévisions dans 21 heures
thing-type.openweathermap.weather-and-forecast.group.forecastHours21.description = Représente les prévisions météorologiques dans 21 heures.
thing-type.openweathermap.weather-and-forecast.group.forecastHours24.label = Prévisions dans 24 heures
thing-type.openweathermap.weather-and-forecast.group.forecastHours24.description = Représente les prévisions météorologiques dans 24 heures.
thing-type.openweathermap.weather-and-forecast.group.forecastToday.label = Prévisions pour aujourd'hui
thing-type.openweathermap.weather-and-forecast.group.forecastToday.description = Représente les prévisions météorologiques pour aujourd'hui.
thing-type.openweathermap.weather-and-forecast.group.forecastTomorrow.label = Prévisions pour demain
thing-type.openweathermap.weather-and-forecast.group.forecastTomorrow.description = Représente les prévisions météorologiques pour demain.
thing-type.openweathermap.weather-and-forecast.group.forecastDay2.label = Prévisions dans 2 jours
thing-type.openweathermap.weather-and-forecast.group.forecastDay2.description = Représente les prévisions météorologiques dans deux jours.
thing-type.openweathermap.weather-and-forecast.group.forecastDay3.label = Prévisions dans 3 jours
thing-type.openweathermap.weather-and-forecast.group.forecastDay3.description = Représente les prévisions météorologiques dans trois jours.
thing-type.openweathermap.weather-and-forecast.group.forecastDay4.label = Prévisions dans 4 jours
thing-type.openweathermap.weather-and-forecast.group.forecastDay4.description = Représente les prévisions météorologiques dans quatre jours.
thing-type.openweathermap.weather-and-forecast.group.forecastDay5.label = Prévisions dans 5 jours
thing-type.openweathermap.weather-and-forecast.group.forecastDay5.description = Représente les prévisions météorologiques dans cinq jours.
thing-type.openweathermap.uvindex.group.forecastTomorrow.label = Indice UV pour demain
thing-type.openweathermap.uvindex.group.forecastTomorrow.description = Représente les prévisions l'indice UV pour demain.
thing-type.openweathermap.uvindex.group.forecastDay2.label = Indice UV dans 2 jours
thing-type.openweathermap.uvindex.group.forecastDay2.description = Représente les prévisions l'indice UV dans deux jours.
thing-type.openweathermap.uvindex.group.forecastDay3.label = Indice UV dans 3 jours
thing-type.openweathermap.uvindex.group.forecastDay3.description = Représente les prévisions l'indice UV dans trois jours.
thing-type.openweathermap.uvindex.group.forecastDay4.label = Indice UV dans 4 jours
thing-type.openweathermap.uvindex.group.forecastDay4.description = Représente les prévisions l'indice UV dans quatre jours.
thing-type.openweathermap.uvindex.group.forecastDay5.label = Indice UV dans 5 jours
thing-type.openweathermap.uvindex.group.forecastDay5.description = Représente les prévisions l'indice UV dans cinq jours.
# channel types
channel-type.openweathermap.station-id.label = Id station
channel-type.openweathermap.station-id.description = L'identifiant de la station météo ou de la ville.
channel-type.openweathermap.station-name.label = Nom station
channel-type.openweathermap.station-name.description = Le nom de la station météo ou de la ville.
channel-group-type.openweathermap.station.channel.location.label = Localisation
channel-group-type.openweathermap.station.channel.location.description = L'emplacement de la station météo ou de la ville en coordonnées géographiques (latitude / longitude / altitude).
channel-type.openweathermap.time-stamp.label = Heure d'observation
channel-type.openweathermap.time-stamp.description = La date et l'heure d'observation de la météo.
channel-type.openweathermap.time-stamp.state.pattern = %1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS
channel-type.openweathermap.hourly-forecast-time-stamp.label = Heure des prévisions
channel-type.openweathermap.hourly-forecast-time-stamp.description = La date et l'heure des prévisions.
channel-type.openweathermap.hourly-forecast-time-stamp.state.pattern = %1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS
channel-type.openweathermap.daily-forecast-time-stamp.label = Date des prévisions
channel-type.openweathermap.daily-forecast-time-stamp.description = La date des prévisions.
channel-type.openweathermap.daily-forecast-time-stamp.state.pattern = %1$td/%1$tm/%1$tY
channel-type.openweathermap.condition.label = Conditions météorologiques
channel-type.openweathermap.condition.description = Les conditions météorologiques actuelles.
channel-type.openweathermap.forecasted-condition.label = Conditions météorologiques prévues
channel-type.openweathermap.forecasted-condition.description = Les conditions météorologiques prévues.
channel-type.openweathermap.condition-id.label = Id conditions météorologiques
channel-type.openweathermap.condition-id.description = L'identifiant des conditions météorologiques.
channel-type.openweathermap.condition-icon.label = Icône
channel-type.openweathermap.condition-icon.description = L'icône représentant les conditions météorologiques.
channel-type.openweathermap.condition-icon-id.label = Id icône
channel-type.openweathermap.condition-icon-id.description = L'identifiant de l'icône représentant les conditions météorologiques.
channel-type.openweathermap.forecasted-outdoor-temperature.label = Température prévue
channel-type.openweathermap.forecasted-outdoor-temperature.description = La température extérieure prévue.
channel-type.openweathermap.forecasted-min-outdoor-temperature.label = Température minimale
channel-type.openweathermap.forecasted-min-outdoor-temperature.description = La température extérieure minimale prévue.
channel-type.openweathermap.forecasted-max-outdoor-temperature.label = Température maximale
channel-type.openweathermap.forecasted-max-outdoor-temperature.description = La température extérieure maximale prévue.
channel-type.openweathermap.forecasted-barometric-pressure.label = Pression barométrique prévue
channel-type.openweathermap.forecasted-barometric-pressure.description = La pression barométrique prévue.
channel-type.openweathermap.forecasted-atmospheric-humidity.label = Humidité atmosphérique prévue
channel-type.openweathermap.forecasted-atmospheric-humidity.description = L'humidité relative atmosphérique prévue.
channel-type.openweathermap.forecasted-wind-speed.label = Vitesse prévue du vent
channel-type.openweathermap.forecasted-wind-speed.description = La vitesse prévue du vent.
channel-type.openweathermap.forecasted-wind-direction.label = Direction prévue du vent
channel-type.openweathermap.forecasted-wind-direction.description = La direction prévue du vent exprimée sous forme d'angle.
channel-type.openweathermap.gust-speed.label = Vitesse des rafales de vent
channel-type.openweathermap.gust-speed.description = La vitesse actuelle des rafales de vent.
channel-type.openweathermap.forecasted-gust-speed.label = Vitesse prévue des rafales de vent
channel-type.openweathermap.forecasted-gust-speed.description = La vitesse prévue des rafales de vent.
channel-type.openweathermap.cloudiness.label = Nébulosité
channel-type.openweathermap.cloudiness.description = La nébulosité actuelle.
channel-type.openweathermap.forecasted-cloudiness.label = Nébulosité prévue
channel-type.openweathermap.forecasted-cloudiness.description = La nébulosité prévue.
channel-type.openweathermap.visibility.label = Distance de visibilité
channel-type.openweathermap.visibility.description = La distance de visibilité.
channel-type.openweathermap.rain.label = Pluie
channel-type.openweathermap.rain.description = La quantité de pluie dans la dernière heure.
channel-type.openweathermap.forecasted-rain.label = Pluie prévue
channel-type.openweathermap.forecasted-rain.description = La quantité prévue de pluie.
channel-type.openweathermap.snow.label = Neige
channel-type.openweathermap.snow.description = La quantité de neige dans la dernière heure.
channel-type.openweathermap.forecasted-snow.label = Neige prévue
channel-type.openweathermap.forecasted-snow.description = La quantité prévue de neige.
channel-type.openweathermap.uvindex.label = Indice UV
channel-type.openweathermap.uvindex.description = L'indice UV actuel.
channel-type.openweathermap.forecasted-uvindex.label = Indice UV prévu
channel-type.openweathermap.forecasted-uvindex.description = L'indice UV prévu.
# thing status
offline.conf-error-missing-apikey = Le paramètre 'apikey' doit être configuré.
offline.conf-error-invalid-apikey = Clé API invalide. Veuillez consulter https://openweathermap.org/faq#error401 pour plus d''informations.
offline.conf-error-not-supported-refreshInterval = Le paramètre 'refreshInterval' doit être au moins de 10 minutes.
offline.conf-error-not-supported-language = Le paramètre 'language' choisi n''est pas supporté.
offline.conf-error-missing-location = Le paramètre 'location' doit être configuré.
offline.conf-error-parsing-location = Le paramètre 'location' n''a pas pu être séparé en latitude et en longitude.
offline.conf-error-not-supported-number-of-hours = Le paramètre 'forecastHours' doit être compris entre 0 et 120 - par incrément de 3.
offline.conf-error-not-supported-number-of-days = Le paramètre 'forecastDays' doit être compris entre 0 et 16.
offline.conf-error-not-supported-uvindex-number-of-days = Le paramètre 'forecastDays' doit être compris entre 1 et 8.
# discovery result
discovery.openweathermap.weather-and-forecast.api.local.label = Météo locale et prévisions
discovery.openweathermap.uvindex.api.local.label = Indice UV local

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="openweathermap"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- OpenWeatherMap Binding -->
<bridge-type id="weather-api">
<label>OpenWeatherMap Account</label>
<description>Provides access to the OpenWeatherMap API.</description>
<representation-property>apikey</representation-property>
<config-description-ref uri="bridge-type:openweathermap:weather-api"/>
</bridge-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,341 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="openweathermap"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Channel groups for OpenWeatherMap Binding -->
<channel-group-type id="station">
<label>Weather Station</label>
<description>This is a weather station.</description>
<channels>
<channel id="id" typeId="station-id"/>
<channel id="name" typeId="station-name"/>
<channel id="location" typeId="system.location">
<description>Location of the weather station or the city.</description>
</channel>
</channels>
</channel-group-type>
<channel-group-type id="weather">
<label>Current Weather</label>
<description>This is the current weather.</description>
<channels>
<channel id="time-stamp" typeId="time-stamp"/>
<channel id="condition" typeId="condition"/>
<channel id="condition-id" typeId="condition-id"/>
<channel id="icon" typeId="condition-icon"/>
<channel id="icon-id" typeId="condition-icon-id"/>
<channel id="temperature" typeId="system.outdoor-temperature"/>
<channel id="apparent-temperature" typeId="apparent-temperature"/>
<channel id="pressure" typeId="system.barometric-pressure"/>
<channel id="humidity" typeId="system.atmospheric-humidity"/>
<channel id="wind-speed" typeId="system.wind-speed"/>
<channel id="wind-direction" typeId="system.wind-direction"/>
<channel id="gust-speed" typeId="gust-speed"/>
<channel id="cloudiness" typeId="cloudiness"/>
<channel id="rain" typeId="rain"/>
<channel id="snow" typeId="snow"/>
<channel id="visibility" typeId="visibility"/>
</channels>
</channel-group-type>
<channel-group-type id="hourlyForecast">
<label>3 Hour Forecast</label>
<description>This is the 5 day / 3 hour weather forecast.</description>
<channels>
<channel id="time-stamp" typeId="hourly-forecast-time-stamp"/>
<channel id="condition" typeId="forecasted-condition"/>
<channel id="condition-id" typeId="condition-id"/>
<channel id="icon" typeId="condition-icon"/>
<channel id="icon-id" typeId="condition-icon-id"/>
<channel id="temperature" typeId="forecasted-outdoor-temperature"/>
<channel id="min-temperature" typeId="forecasted-min-outdoor-temperature"/>
<channel id="max-temperature" typeId="forecasted-max-outdoor-temperature"/>
<channel id="apparent-temperature" typeId="forecasted-apparent-temperature"/>
<channel id="pressure" typeId="forecasted-barometric-pressure"/>
<channel id="humidity" typeId="forecasted-atmospheric-humidity"/>
<channel id="wind-speed" typeId="forecasted-wind-speed"/>
<channel id="wind-direction" typeId="forecasted-wind-direction"/>
<channel id="gust-speed" typeId="forecasted-gust-speed"/>
<channel id="cloudiness" typeId="forecasted-cloudiness"/>
<channel id="rain" typeId="forecasted-rain"/>
<channel id="snow" typeId="forecasted-snow"/>
</channels>
</channel-group-type>
<channel-group-type id="dailyForecast">
<label>Daily Forecast</label>
<description>This is the 16 day / daily weather forecast.</description>
<channels>
<channel id="time-stamp" typeId="daily-forecast-time-stamp"/>
<channel id="condition" typeId="forecasted-condition"/>
<channel id="condition-id" typeId="condition-id"/>
<channel id="icon" typeId="condition-icon"/>
<channel id="icon-id" typeId="condition-icon-id"/>
<channel id="min-temperature" typeId="forecasted-min-outdoor-temperature"/>
<channel id="max-temperature" typeId="forecasted-max-outdoor-temperature"/>
<channel id="apparent-temperature" typeId="forecasted-apparent-temperature"/>
<channel id="pressure" typeId="forecasted-barometric-pressure"/>
<channel id="humidity" typeId="forecasted-atmospheric-humidity"/>
<channel id="wind-speed" typeId="forecasted-wind-speed"/>
<channel id="wind-direction" typeId="forecasted-wind-direction"/>
<channel id="gust-speed" typeId="forecasted-gust-speed"/>
<channel id="cloudiness" typeId="forecasted-cloudiness"/>
<channel id="rain" typeId="forecasted-rain"/>
<channel id="snow" typeId="forecasted-snow"/>
</channels>
</channel-group-type>
<channel-group-type id="uvindex">
<label>Current UV Index</label>
<description>This is the current UV Index.</description>
<channels>
<channel id="time-stamp" typeId="daily-forecast-time-stamp"/>
<channel id="uvindex" typeId="uvindex"/>
</channels>
</channel-group-type>
<channel-group-type id="uvindexForecast">
<label>Forecasted UV Index</label>
<description>This is the forecasted UV Index.</description>
<channels>
<channel id="time-stamp" typeId="daily-forecast-time-stamp"/>
<channel id="uvindex" typeId="forecasted-uvindex"/>
</channels>
</channel-group-type>
<!-- Channels for OpenWeatherMap Binding -->
<channel-type id="station-id">
<item-type>String</item-type>
<label>Station Id</label>
<description>Id of the weather station or the city.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="station-name">
<item-type>String</item-type>
<label>Station Name</label>
<description>Name of the weather station or the city.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="time-stamp">
<item-type>DateTime</item-type>
<label>Observation Time</label>
<description>Time of data observation.</description>
<category>Time</category>
<state readOnly="true" pattern="%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"/>
</channel-type>
<channel-type id="hourly-forecast-time-stamp">
<item-type>DateTime</item-type>
<label>Forecast Time</label>
<description>Time of data forecasted.</description>
<category>Time</category>
<state readOnly="true" pattern="%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"/>
</channel-type>
<channel-type id="daily-forecast-time-stamp">
<item-type>DateTime</item-type>
<label>Forecast Date</label>
<description>Date of data forecasted.</description>
<category>Time</category>
<state readOnly="true" pattern="%1$tY-%1$tm-%1$td"/>
</channel-type>
<channel-type id="condition">
<item-type>String</item-type>
<label>Weather Condition</label>
<description>Current weather condition.</description>
<category>Sun_Clouds</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="forecasted-condition">
<item-type>String</item-type>
<label>Forecasted Weather Condition</label>
<description>Forecasted weather condition.</description>
<category>Sun_Clouds</category>
<state readOnly="true"/>
</channel-type>
<channel-type id="condition-id" advanced="true">
<item-type>String</item-type>
<label>Weather Condition Id</label>
<description>Id of the weather condition.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="condition-icon">
<item-type>Image</item-type>
<label>Icon</label>
<description>Icon representing the weather condition.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="condition-icon-id" advanced="true">
<item-type>String</item-type>
<label>Icon Id</label>
<description>Id of the icon to create the URL.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="forecasted-outdoor-temperature">
<item-type>Number:Temperature</item-type>
<label>Forecasted Temperature</label>
<description>Forecasted outdoor temperature.</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="forecasted-min-outdoor-temperature">
<item-type>Number:Temperature</item-type>
<label>Minimum Temperature</label>
<description>Minimum forecasted outdoor temperature.</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="forecasted-max-outdoor-temperature">
<item-type>Number:Temperature</item-type>
<label>Maximum Temperature</label>
<description>Maximum forecasted outdoor temperature.</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="apparent-temperature">
<item-type>Number:Temperature</item-type>
<label>Apparent Temperature</label>
<description>Current apparent temperature.</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="forecasted-apparent-temperature">
<item-type>Number:Temperature</item-type>
<label>Forecasted Apparent Temperature</label>
<description>Forecasted apparent temperature.</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="forecasted-barometric-pressure">
<item-type>Number:Pressure</item-type>
<label>Forecasted Pressure</label>
<description>Forecasted barometric pressure.</description>
<category>Pressure</category>
<state readOnly="true" pattern="%.3f %unit%"/>
</channel-type>
<channel-type id="forecasted-atmospheric-humidity">
<item-type>Number:Dimensionless</item-type>
<label>Forecasted Humidity</label>
<description>Forecasted atmospheric relative humidity.</description>
<category>Humidity</category>
<state readOnly="true" pattern="%.0f %%"/>
</channel-type>
<channel-type id="forecasted-wind-speed">
<item-type>Number:Speed</item-type>
<label>Forecasted Wind Speed</label>
<description>Forecasted wind speed.</description>
<category>Wind</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="forecasted-wind-direction">
<item-type>Number:Angle</item-type>
<label>Forecasted Wind Direction</label>
<description>Forecasted wind direction expressed as an angle.</description>
<category>Wind</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="gust-speed" advanced="true">
<item-type>Number:Speed</item-type>
<label>Gust Speed</label>
<description>Current gust speed.</description>
<category>Wind</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="forecasted-gust-speed" advanced="true">
<item-type>Number:Speed</item-type>
<label>Forecasted Gust Speed</label>
<description>Forecasted gust speed.</description>
<category>Wind</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="cloudiness">
<item-type>Number:Dimensionless</item-type>
<label>Cloudiness</label>
<description>Current cloudiness.</description>
<category>Clouds</category>
<state readOnly="true" min="0" max="100" pattern="%d %unit%"/>
</channel-type>
<channel-type id="forecasted-cloudiness">
<item-type>Number:Dimensionless</item-type>
<label>Forecasted Cloudiness</label>
<description>Forecasted cloudiness.</description>
<category>Clouds</category>
<state readOnly="true" min="0" max="100" pattern="%d %unit%"/>
</channel-type>
<channel-type id="rain">
<item-type>Number:Length</item-type>
<label>Rain</label>
<description>Rain volume of the last hour.</description>
<category>Rain</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="forecasted-rain">
<item-type>Number:Length</item-type>
<label>Forecasted Rain</label>
<description>Forecasted rain volume.</description>
<category>Rain</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="snow">
<item-type>Number:Length</item-type>
<label>Snow</label>
<description>Snow volume of the last hour.</description>
<category>Snow</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="forecasted-snow">
<item-type>Number:Length</item-type>
<label>Forecasted Snow</label>
<description>Forecasted snow volume.</description>
<category>Snow</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="uvindex">
<item-type>Number</item-type>
<label>UV Index</label>
<description>Current UV Index.</description>
<state readOnly="true" pattern="%.1f"/>
</channel-type>
<channel-type id="forecasted-uvindex">
<item-type>Number</item-type>
<label>Forecasted UV Index</label>
<description>Forecasted UV Index.</description>
<state readOnly="true" pattern="%.1f"/>
</channel-type>
<channel-type id="visibility">
<item-type>Number:Length</item-type>
<label>Visibility</label>
<description>Current visibility.</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="openweathermap"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- OpenWeatherMap Binding -->
<thing-type id="weather-and-forecast">
<supported-bridge-type-refs>
<bridge-type-ref id="weather-api"/>
</supported-bridge-type-refs>
<label>Weather and Forecast</label>
<description>Provides current weather and forecast data from the OpenWeatherMap API.</description>
<channel-groups>
<channel-group id="station" typeId="station"/>
<channel-group id="current" typeId="weather"/>
<channel-group id="forecastHours03" typeId="hourlyForecast">
<label>3 Hours Forecast</label>
<description>This is the weather forecast for the next 3 hours.</description>
</channel-group>
<channel-group id="forecastHours06" typeId="hourlyForecast">
<label>6 Hours Forecast</label>
<description>This is the weather forecast in 6 hours.</description>
</channel-group>
<channel-group id="forecastHours09" typeId="hourlyForecast">
<label>9 Hours Forecast</label>
<description>This is the weather forecast in 9 hours.</description>
</channel-group>
<channel-group id="forecastHours12" typeId="hourlyForecast">
<label>12 Hours Forecast</label>
<description>This is the weather forecast in 12 hours.</description>
</channel-group>
<channel-group id="forecastHours15" typeId="hourlyForecast">
<label>15 Hours Forecast</label>
<description>This is the weather forecast in 15 hours.</description>
</channel-group>
<channel-group id="forecastHours18" typeId="hourlyForecast">
<label>18 Hours Forecast</label>
<description>This is the weather forecast in 18 hours.</description>
</channel-group>
<channel-group id="forecastHours21" typeId="hourlyForecast">
<label>21 Hours Forecast</label>
<description>This is the weather forecast in 21 hours.</description>
</channel-group>
<channel-group id="forecastHours24" typeId="hourlyForecast">
<label>24 Hours Forecast</label>
<description>This is the weather forecast in 24 hours.</description>
</channel-group>
<channel-group id="forecastToday" typeId="dailyForecast">
<label>Todays Forecast</label>
<description>This is the weather forecast for today.</description>
</channel-group>
<channel-group id="forecastTomorrow" typeId="dailyForecast">
<label>Tomorrows Forecast</label>
<description>This is the weather forecast for tomorrow.</description>
</channel-group>
<channel-group id="forecastDay2" typeId="dailyForecast">
<label>2 Day Forecast</label>
<description>This is the weather forecast in two days.</description>
</channel-group>
<channel-group id="forecastDay3" typeId="dailyForecast">
<label>3 Day Forecast</label>
<description>This is the weather forecast in three days.</description>
</channel-group>
<channel-group id="forecastDay4" typeId="dailyForecast">
<label>4 Day Forecast</label>
<description>This is the weather forecast in four days.</description>
</channel-group>
<channel-group id="forecastDay5" typeId="dailyForecast">
<label>5 Day Forecast</label>
<description>This is the weather forecast in five days.</description>
</channel-group>
</channel-groups>
<representation-property>location</representation-property>
<config-description-ref uri="thing-type:openweathermap:weather-and-forecast"/>
</thing-type>
<thing-type id="uvindex">
<supported-bridge-type-refs>
<bridge-type-ref id="weather-api"/>
</supported-bridge-type-refs>
<label>UV Index</label>
<description>Provides UV Index data from the OpenWeatherMap API.</description>
<channel-groups>
<channel-group id="current" typeId="uvindex"/>
<channel-group id="forecastTomorrow" typeId="uvindexForecast">
<label>Tomorrows Forecast</label>
<description>This is the UV Index forecast for tomorrow.</description>
</channel-group>
<channel-group id="forecastDay2" typeId="uvindexForecast">
<label>2 Day Forecast</label>
<description>This is the UV Index forecast in two days.</description>
</channel-group>
<channel-group id="forecastDay3" typeId="uvindexForecast">
<label>3 Day Forecast</label>
<description>This is the UV Index forecast in three days.</description>
</channel-group>
<channel-group id="forecastDay4" typeId="uvindexForecast">
<label>4 Day Forecast</label>
<description>This is the UV Index forecast in four days.</description>
</channel-group>
<channel-group id="forecastDay5" typeId="uvindexForecast">
<label>5 Day Forecast</label>
<description>This is the UV Index forecast in five days.</description>
</channel-group>
</channel-groups>
<representation-property>location</representation-property>
<config-description-ref uri="thing-type:openweathermap:uvindex"/>
</thing-type>
</thing:thing-descriptions>