Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[openweathermap] Add support for One Call API #7308 #8395

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bd8b00d
Fix missing 2.5.9-SNAPSHOT changes (#8333)
kaikreuzer Aug 23, 2020
a820b7b
Added One Call API access. Beta version, fairly tested
Aug 31, 2020
c99e19f
Added One Call API access. Alpha version, not yet fully tested
Aug 31, 2020
a9c1ef0
Small cleanups
Sep 1, 2020
d9e049c
Added author comment to DTO classes
Sep 4, 2020
1097b07
Moved comment to the right place
Sep 4, 2020
0524072
Added One Call API access. Beta version, fairly tested
Aug 31, 2020
b0c42e4
Added One Call API access. Alpha version, not yet fully tested
Aug 31, 2020
4c098b1
Small cleanups
Sep 1, 2020
aceb882
Added author comment to DTO classes
Sep 4, 2020
b8c7ab0
Moved comment to the right place
Sep 4, 2020
52ed7de
Merge branch '2.5.x' of https://github.com/Wolfgang1966/openhab-addon…
Sep 5, 2020
809a7fc
Revert "Fix missing 2.5.9-SNAPSHOT changes (#8333)"
Sep 5, 2020
30852bd
Solved findings from Hilbrand
Sep 5, 2020
d0857cc
advanced channel groups are not supported :-(
Sep 5, 2020
13feefc
Worked on review comments from cweitkamp plus some other improvements
Sep 7, 2020
ac151a0
Reduced number of channel groups in config
Sep 7, 2020
542cb4e
Nothing changed, just retrigger failed travis build
Sep 9, 2020
5eaba63
Small changes to the README.md as requested.
Sep 10, 2020
128dc1c
Applied review comments from cweitkamp
Sep 20, 2020
3c838f9
Correct usage of percentage
Sep 27, 2020
956c27a
Further changes requested from cweitkamp, and some fixes
Oct 4, 2020
8b57dfb
Removed null check, corrected thing add/remove logic
Nov 2, 2020
dfbc877
Removed nonexistent channel
Nov 18, 2020
0942a86
Adapted description
Nov 18, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
422 changes: 416 additions & 6 deletions bundles/org.openhab.binding.openweathermap/README.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion bundles/org.openhab.binding.openweathermap/pom.xml
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

Expand Down
Expand Up @@ -36,6 +36,10 @@ public class OpenWeatherMapBindingConstants {
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");
// One Call API forecast
public static final ThingTypeUID THING_TYPE_ONECALL_WEATHER_AND_FORECAST = new ThingTypeUID(BINDING_ID, "onecall");
// One Call API historical data
public static final ThingTypeUID THING_TYPE_ONECALL_HISTORY = new ThingTypeUID(BINDING_ID, "onecall-history");

// List of all properties
public static final String CONFIG_API_KEY = "apikey";
Expand All @@ -49,38 +53,66 @@ public class OpenWeatherMapBindingConstants {
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");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_ONECALL_MINUTELY_FORECAST = new ChannelGroupTypeUID(
BINDING_ID, "oneCallMinutely");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_ONECALL_HOURLY_FORECAST = new ChannelGroupTypeUID(
BINDING_ID, "oneCallHourly");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_ONECALL_DAILY_FORECAST = new ChannelGroupTypeUID(
BINDING_ID, "oneCallDaily");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_ONECALL_CURRENT = new ChannelGroupTypeUID(BINDING_ID,
"oneCallCurrent");

// 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";
public static final String CHANNEL_GROUP_ONECALL_CURRENT = "current";
public static final String CHANNEL_GROUP_ONECALL_HISTORY = "history";
public static final String CHANNEL_GROUP_ONECALL_TODAY = "forecastToday";
public static final String CHANNEL_GROUP_ONECALL_TOMORROW = "forecastTomorrow";

// 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_SUNRISE = "sunrise";
public static final String CHANNEL_SUNSET = "sunset";
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_APPARENT_MORNING = "apparent-morning";
public static final String CHANNEL_APPARENT_DAY = "apparent-day";
public static final String CHANNEL_APPARENT_EVENING = "apparent-evening";
public static final String CHANNEL_APPARENT_NIGHT = "apparent-night";
public static final String CHANNEL_MIN_TEMPERATURE = "min-temperature";
public static final String CHANNEL_MAX_TEMPERATURE = "max-temperature";
public static final String CHANNEL_MORNING_TEMPERATURE = "morning-temperature";
public static final String CHANNEL_DAY_TEMPERATURE = "day-temperature";
public static final String CHANNEL_EVENING_TEMPERATURE = "evening-temperature";
public static final String CHANNEL_NIGHT_TEMPERATURE = "night-temperature";
public static final String CHANNEL_DEW_POINT = "dew-point";
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_PRECIP_PROBABILITY = "precip-probability";
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";
public static final String CHANNEL_PRECIPITATION = "precipitation";

// List of all configuration
public static final String CONFIG_FORECAST_DAYS = "forecastDays";

// relative number of days in history
public static final String CONFIG_HISTORY_DAYS = "historyDays";
}
@@ -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;

/**
* The {@link OpenWeatherMapOneCallConfiguration} is the class used to match the
* {@link org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapOneCallHandler}s configuration.
*
* @author Wolfgang Klimt - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapOneCallConfiguration extends OpenWeatherMapLocationConfiguration {
public int forecastMinutes;
public int forecastHours;
public int forecastDays;
}
@@ -0,0 +1,26 @@
/**
* 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;

/**
* The {@link OpenWeatherMapOneCallHistoryConfiguration} is the class used to match the
* {@link org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapOneCallHistoryHandler}s configuration.
*
* @author Wolfgang Klimt - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapOneCallHistoryConfiguration extends OpenWeatherMapLocationConfiguration {
public int historyDay;
}
Expand Up @@ -19,10 +19,9 @@
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.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
Expand All @@ -41,6 +40,8 @@
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.dto.onecall.OpenWeatherMapOneCallAPIData;
import org.openhab.binding.openweathermap.internal.dto.onecallhist.OpenWeatherMapOneCallHistAPIData;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
import org.openhab.binding.openweathermap.internal.utils.ByteArrayFileCache;
import org.slf4j.Logger;
Expand Down Expand Up @@ -72,6 +73,8 @@ public class OpenWeatherMapConnection {
private static final String PARAM_LON = "lon";
private static final String PARAM_LANG = "lang";
private static final String PARAM_FORECAST_CNT = "cnt";
private static final String PARAM_HISTORY_DATE = "dt";
private static final String PARAM_EXCLUDE = "exclude";

// Current weather data (see https://openweathermap.org/current)
private static final String WEATHER_URL = "https://api.openweathermap.org/data/2.5/weather";
Expand All @@ -84,6 +87,9 @@ public class OpenWeatherMapConnection {
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";
// One Call API (see https://openweathermap.org/api/one-call-api )
private static final String ONECALL_URL = "https://api.openweathermap.org/data/2.5/onecall";
private static final String ONECALL_HISTORY_URL = "https://api.openweathermap.org/data/2.5/onecall/timemachine";

private final OpenWeatherMapAPIHandler handler;
private final HttpClient httpClient;
Expand Down Expand Up @@ -239,6 +245,65 @@ public OpenWeatherMapConnection(OpenWeatherMapAPIHandler handler, HttpClient htt
return HttpUtil.downloadImage(url);
}

/**
* Get Weather data from the OneCall API for the given location. See https://openweathermap.org/api/one-call-api for
* details
*
* @param location location represented as {@link PointType}
Wolfgang1966 marked this conversation as resolved.
Show resolved Hide resolved
* @param excludeMinutely if true, will not fetch minutely forecast data from the server
* @param excludeHourly if true, will not fethh hourly forecast data from the server
* @param excludeDaily if true, will not fetch hourly forecast data from the server
* @return
* @throws JsonSyntaxException
* @throws OpenWeatherMapCommunicationException
* @throws OpenWeatherMapConfigurationException
*/
public synchronized @Nullable OpenWeatherMapOneCallAPIData getOneCallAPIData(@Nullable PointType location,
boolean excludeMinutely, boolean excludeHourly, boolean excludeDaily)
throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
Map<String, String> params = getRequestParams(handler.getOpenWeatherMapAPIConfig(), location);
StringBuilder exclude = new StringBuilder("");
if (excludeMinutely) {
exclude.append("minutely");
}
if (excludeHourly) {
exclude.append(exclude.length() > 0 ? "," : "").append("hourly");
}
if (excludeDaily) {
exclude.append(exclude.length() > 0 ? "," : "").append("daily");
}
logger.debug("Exclude: '{}'", exclude);
if (exclude.length() > 0) {
params.put(PARAM_EXCLUDE, exclude.toString());
}
return gson.fromJson(getResponseFromCache(buildURL(ONECALL_URL, params)), OpenWeatherMapOneCallAPIData.class);
}

/**
* Get the historical weather data from the OneCall API for the given location and the given number of days in the
* past.
* As of now, OpenWeatherMap supports this function for up to 5 days in the past. However, this may change in the
* future,
* so we don't enforce this limit here. See https://openweathermap.org/api/one-call-api for details
*
* @param location location represented as {@link PointType}
* @param days number of days in the past, relative to the current time.
* @return
* @throws JsonSyntaxException
* @throws OpenWeatherMapCommunicationException
* @throws OpenWeatherMapConfigurationException
*/
public synchronized @Nullable OpenWeatherMapOneCallHistAPIData getOneCallHistAPIData(@Nullable PointType location,
int days)
throws JsonSyntaxException, OpenWeatherMapCommunicationException, OpenWeatherMapConfigurationException {
Map<String, String> params = getRequestParams(handler.getOpenWeatherMapAPIConfig(), location);
// the API requests the history as timestamp in Unix time format.
params.put(PARAM_HISTORY_DATE,
Long.toString(ZonedDateTime.now(ZoneId.of("UTC")).minusDays(days).toEpochSecond()));
return gson.fromJson(getResponseFromCache(buildURL(ONECALL_HISTORY_URL, params)),
OpenWeatherMapOneCallHistAPIData.class);
}

private Map<String, String> getRequestParams(OpenWeatherMapAPIConfiguration config, @Nullable PointType location) {
if (location == null) {
throw new OpenWeatherMapConfigurationException("@text/offline.conf-error-missing-location");
Expand Down
Expand Up @@ -128,6 +128,8 @@ private void createResults(PointType location) {
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
createWeatherAndForecastResult(locationString, bridgeUID);
createUVIndexResult(locationString, bridgeUID);
createOneCallResult(locationString, bridgeUID);
createOneCallHistoryResult(locationString, bridgeUID);
}

private void createWeatherAndForecastResult(String location, ThingUID bridgeUID) {
Expand All @@ -141,4 +143,17 @@ private void createUVIndexResult(String location, ThingUID bridgeUID) {
.withLabel("Local UV Index").withProperty(CONFIG_LOCATION, location)
.withRepresentationProperty(CONFIG_LOCATION).withBridge(bridgeUID).build());
}

private void createOneCallResult(String location, ThingUID bridgeUID) {
thingDiscovered(
DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_ONECALL_WEATHER_AND_FORECAST, bridgeUID, LOCAL))
.withLabel("One Call API weather and forecast").withProperty(CONFIG_LOCATION, location)
.withRepresentationProperty(CONFIG_LOCATION).withBridge(bridgeUID).build());
}

private void createOneCallHistoryResult(String location, ThingUID bridgeUID) {
thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_ONECALL_HISTORY, bridgeUID, LOCAL))
.withLabel("One Call API history data").withProperty(CONFIG_LOCATION, location)
.withRepresentationProperty(CONFIG_LOCATION).withBridge(bridgeUID).build());
}
}