diff --git a/docs/_includes/documentation-menu.html b/docs/_includes/documentation-menu.html
index a511ab97ccb..07eabce8ae0 100644
--- a/docs/_includes/documentation-menu.html
+++ b/docs/_includes/documentation-menu.html
@@ -37,6 +37,7 @@
MQTT Generic
NTP Time Server
OneWire
+ OpenWeatherMap
Philips Hue
Serial Button
Sonos
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/.classpath b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/.classpath
new file mode 100644
index 00000000000..7f457fa4138
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/.project b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/.project
new file mode 100644
index 00000000000..70b64d0bd07
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/.project
@@ -0,0 +1,33 @@
+
+
+ org.eclipse.smarthome.binding.openweathermap
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ org.eclipse.pde.ds.core.builder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/binding/binding.xml b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/binding/binding.xml
new file mode 100644
index 00000000000..a0b623789d5
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/binding/binding.xml
@@ -0,0 +1,10 @@
+
+
+
+ OpenWeatherMap Binding
+ OpenWeatherMap - Current weather and forecasts in your city.
+ Christoph Weitkamp
+
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/config/config.xml b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/config/config.xml
new file mode 100644
index 00000000000..d2908c03c42
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/config/config.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+ password
+ API Key
+ API key to access the OpenWeatherMap API.
+
+
+ Refresh Interval
+ Specifies the refresh interval (in minutes).
+ 60
+
+
+ Language
+ Language to be used by the OpenWeatherMap API.
+
+ Arabic
+ Bulgarian
+ Catalan
+ Chinese - Simplified
+ Chinese - Traditional
+ Croatian
+ Czech
+ Dutch
+ English
+ Finnish
+ French
+ Galician
+ German
+ Greek
+ Hungarian
+ Italian
+ Japanese
+ Korean
+ Latvian
+ Lithuanian
+ Macedonian
+ Persian (Farsi)
+ Polish
+ Portuguese
+ Romanian
+ Russian
+ Slovak
+ Slovenian
+ Spanish
+ Swedish
+ Turkish
+ Ukrainian
+ Vietnamese
+
+
+
+
+
+
+ location
+ Location of Weather
+ Location of weather in geographical coordinates (latitude/longitude/altitude).
+
+
+ Number of Hours
+ Number of hours for hourly forecast.
+ 24
+
+
+ Number of Days
+ Number of days for daily forecast.
+ 6
+
+
+
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/i18n/openweathermap.properties b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/i18n/openweathermap.properties
new file mode 100644
index 00000000000..731c05dc3f4
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/i18n/openweathermap.properties
@@ -0,0 +1,13 @@
+# 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.
+
+# discovery result
+discovery.openweathermap.weather-and-forecast.api.local.label = Local Weather And Forecast
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/i18n/openweathermap_de.properties b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/i18n/openweathermap_de.properties
new file mode 100644
index 00000000000..6bacd3b2e2d
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/i18n/openweathermap_de.properties
@@ -0,0 +1,213 @@
+# 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.sk = Slowakisch
+bridge-type.config.openweathermap.weather-api.language.option.sl = Slowenisch
+bridge-type.config.openweathermap.weather-api.language.option.sw = Schwedisch
+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 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.
+
+# 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.current.label = Aktuelles Wetter
+channel-group-type.openweathermap.current.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 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.
+
+# 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 Wettervorhersage 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 Wettervorhersage 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.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.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.rain.label = Regen
+channel-type.openweathermap.rain.description = Zeigt den kumulierten Regen der letzten drei Stunden an.
+
+channel-type.openweathermap.forecasted-rain.label = Vorhergesagter Regen
+channel-type.openweathermap.forecasted-rain.description = Zeigt den vorhergesagten, kumulierten Regen der nächsten drei Stunden an.
+
+channel-type.openweathermap.snow.label = Schnee
+channel-type.openweathermap.snow.description = Zeigt den kumulierten Schnee der letzten drei Stunden an.
+
+channel-type.openweathermap.forecasted-snow.label = Vorhergesagter Schnee
+channel-type.openweathermap.forecasted-snow.description = Zeigt den vorhergesagten, kumulierten Schnee der nächsten drei Stunden 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.
+
+# discovery result
+discovery.openweathermap.weather-and-forecast.api.local.label = Lokales Wetter und Wettervorhersage
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/thing/bridge-types.xml b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/thing/bridge-types.xml
new file mode 100644
index 00000000000..1f8af9f6c9b
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/thing/bridge-types.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ OpenWeatherMap Account
+ Provides access to the OpenWeatherMap API.
+
+ apikey
+
+
+
+
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/thing/channel-types.xml b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/thing/channel-types.xml
new file mode 100644
index 00000000000..b8abd07b55a
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/thing/channel-types.xml
@@ -0,0 +1,270 @@
+
+
+
+
+
+ Weather Station
+ This is a weather station.
+
+
+
+
+ Location of the weather station or the city.
+
+
+
+
+
+ Current Weather
+ This is the current weather.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3 Hour Forecast
+ This is the 5 day / 3 hour weather forecast.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Daily Forecast
+ This is the 16 day / daily weather forecast.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ String
+ Station Id
+ Id of the weather station or the city.
+
+
+
+
+ String
+ Station Name
+ Name of the weather station or the city.
+
+
+
+
+ DateTime
+ Observation Time
+ Time of data observation.
+ Time
+
+
+
+
+ DateTime
+ Forecast Time
+ Time of data forecasted.
+ Time
+
+
+
+
+ DateTime
+ Forecast Date
+ Date of data forecasted.
+ Time
+
+
+
+
+ String
+ Weather Condition
+ Current weather condition.
+ Sun_Clouds
+
+
+
+
+ String
+ Forecasted Weather Condition
+ Forecasted weather condition.
+ Sun_Clouds
+
+
+
+
+ String
+ Weather Condition Id
+ Id of the weather condition.
+
+
+
+
+ Image
+ Icon
+ Icon representing the weather condition.
+
+
+
+
+ Number:Temperature
+ Forecasted Temperature
+ Forecasted outdoor temperature.
+ Temperature
+
+
+
+
+ Number:Temperature
+ Minimum Temperature
+ Minimum forecasted outdoor temperature.
+ Temperature
+
+
+
+
+ Number:Temperature
+ Maximum Temperature
+ Maximum forecasted outdoor temperature.
+ Temperature
+
+
+
+
+ Number:Pressure
+ Forecasted Pressure
+ Forecasted barometric pressure.
+ Pressure
+
+
+
+
+ Number:Dimensionless
+ Forecasted Humidity
+ Forecasted atmospheric relative humidity.
+ Humidity
+
+
+
+
+ Number:Speed
+ Forecasted Wind Speed
+ Forecasted wind speed.
+ Wind
+
+
+
+
+ Number:Angle
+ Forecasted Wind Direction
+ Forecasted wind direction expressed as an angle.
+ Wind
+
+
+
+
+ Number:Speed
+ Gust Speed
+ Current gust speed.
+ Wind
+
+
+
+
+ Number:Speed
+ Forecasted Gust Speed
+ Forecasted gust speed.
+ Wind
+
+
+
+
+ Number:Dimensionless
+ Cloudiness
+ Current cloudiness.
+ Clouds
+
+
+
+
+ Number:Dimensionless
+ Forecasted Cloudiness
+ Forecasted cloudiness.
+ Clouds
+
+
+
+
+ Number:Length
+ Rain
+ Rain volume for the last 3 hours.
+ Rain
+
+
+
+
+ Number:Length
+ Forecasted Rain
+ Forecasted rain volume for the next 3 hours.
+ Rain
+
+
+
+
+ Number:Length
+ Snow
+ Snow volume for the last 3 hours.
+ Snow
+
+
+
+
+ Number:Length
+ Forecasted Snow
+ Forecasted snow volume for the next 3 hours.
+ Snow
+
+
+
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/thing/thing-types.xml b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/thing/thing-types.xml
new file mode 100644
index 00000000000..008dd3e18c7
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/ESH-INF/thing/thing-types.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+ Weather And Forecast
+ Provides current weather and forecast data from the OpenWeatherMap API.
+
+
+
+
+
+ 3 Hours Forecast
+ This is the weather forecast for the next 3 hours.
+
+
+ 6 Hours Forecast
+ This is the weather forecast in 6 hours.
+
+
+ 9 Hours Forecast
+ This is the weather forecast in 9 hours.
+
+
+ 12 Hours Forecast
+ This is the weather forecast in 12 hours.
+
+
+ 15 Hours Forecast
+ This is the weather forecast in 15 hours.
+
+
+ 18 Hours Forecast
+ This is the weather forecast in 18 hours.
+
+
+ 21 Hours Forecast
+ This is the weather forecast in 21 hours.
+
+
+ 24 Hours Forecast
+ This is the weather forecast in 24 hours.
+
+
+ Todays Forecast
+ This is the weather forecast for today.
+
+
+ Tomorrows Forecast
+ This is the weather forecast for tomorrow.
+
+
+ 2 Day Forecast
+ This is the weather forecast in two days.
+
+
+ 3 Day Forecast
+ This is the weather forecast in three days.
+
+
+ 4 Day Forecast
+ This is the weather forecast in four days.
+
+
+ 5 Day Forecast
+ This is the weather forecast in five days.
+
+
+
+ location
+
+
+
+
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/META-INF/MANIFEST.MF b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..60598cf76d6
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/META-INF/MANIFEST.MF
@@ -0,0 +1,37 @@
+Manifest-Version: 1.0
+Bundle-ActivationPolicy: lazy
+Bundle-ClassPath: .
+Bundle-ManifestVersion: 2
+Bundle-Name: Eclipse SmartHome OpenWeatherMap Binding
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-SymbolicName: org.eclipse.smarthome.binding.openweathermap;singleton:=true
+Bundle-Vendor: Eclipse.org/SmartHome
+Bundle-Version: 0.10.0.qualifier
+Import-Package:
+ com.google.gson,
+ com.google.gson.annotations,
+ javax.measure,
+ javax.measure.quantity,
+ org.apache.commons.lang,
+ org.eclipse.jdt.annotation;resolution:=optional,
+ org.eclipse.jetty.client,
+ org.eclipse.jetty.client.api,
+ org.eclipse.jetty.http,
+ org.eclipse.jetty.util,
+ org.eclipse.smarthome.config.core,
+ org.eclipse.smarthome.config.discovery,
+ org.eclipse.smarthome.core.cache,
+ org.eclipse.smarthome.core.i18n,
+ org.eclipse.smarthome.core.library.types,
+ org.eclipse.smarthome.core.library.unit,
+ org.eclipse.smarthome.core.thing,
+ org.eclipse.smarthome.core.thing.binding,
+ org.eclipse.smarthome.core.thing.binding.builder,
+ org.eclipse.smarthome.core.thing.type,
+ org.eclipse.smarthome.core.types,
+ org.eclipse.smarthome.io.net.http,
+ org.osgi.framework,
+ org.osgi.service.component,
+ org.slf4j
+Service-Component: OSGI-INF/*.xml
+Automatic-Module-Name: org.eclipse.smarthome.binding.openweathermap
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/NOTICE b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/NOTICE
new file mode 100644
index 00000000000..b8675cd02e8
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/NOTICE
@@ -0,0 +1,19 @@
+This content is produced and maintained by the Eclipse SmartHome project.
+
+* Project home: https://eclipse.org/smarthome/
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/eclipse/smarthome
+
+== Copyright Holders
+
+See the NOTICE file distributed with the source code at
+https://github.com/eclipse/smarthome/blob/master/NOTICE
+for detailed information regarding copyright ownership.
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/OSGI-INF/.gitignore b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/OSGI-INF/.gitignore
new file mode 100644
index 00000000000..6722cd96e78
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/OSGI-INF/.gitignore
@@ -0,0 +1 @@
+*.xml
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/README.md b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/README.md
new file mode 100644
index 00000000000..0835df86797
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/README.md
@@ -0,0 +1,269 @@
+---
+layout: documentation
+---
+
+{% include base.html %}
+
+# OpenWeatherMap Binding
+
+This binding integrates the [OpenWeatherMap weather API](https://openweathermap.org/api).
+
+## Supported Things
+
+There are two supported things.
+
+### OpenWeatherMap Account
+
+First one is a bridge `weather-api` which represents the OpenWeatherMap account.
+The bridge holds the mandatory API key to access the OpenWeatherMap API and several global configuration parameters.
+If your system language is supported by the OpenWeatherMap API it will be used as default language for the requested data.
+
+### Current Weather And Forecast
+
+The second thing `weather-and-forecast` supports the [current weather](https://openweathermap.org/current), [5 day / 3 hour forecast](https://openweathermap.org/forecast5) and optional [16 day / daily forecast](https://openweathermap.org/forecast16) services for a specific location.
+It requires coordinates of the location of your interest.
+You can add as many `weather-and-forecast` things for different locations to your setup as you like to observe.
+**Attention**: The daily forecast is only available for [paid accounts](https://openweathermap.org/price).
+The binding tries to request daily forecast data from the OpenWeatherMap API.
+If the request fails, all daily forecast channel groups will be removed from the thing and further request will be omitted.
+
+## Discovery
+
+If a system location is set, a "Local Weather And Forecast" (`weather-and-forecast`) thing will be automatically discovered for this location.
+Once the system location will be changed, the background discovery updates the configuration of "Local Weather And Forecast" accordingly.
+
+## Thing Configuration
+
+### OpenWeatherMap Account
+
+| Parameter | Description |
+|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| apikey | API key to access the OpenWeatherMap API. **Mandatory** |
+| refreshInterval | Specifies the refresh interval (in minutes). Optional, the default value is 60, the minimum value is 10. |
+| language | Language to be used by the OpenWeatherMap API. Optional, valid values are: `ar`, `bg`, `ca`, `de`, `el`, `en`, `es`, `fa`, `fi`, `fr`, `gl`, `hr`, `hu`, `it`, `ja`, `kr`, `la`, `lt`, `mk`, `nl`, `pl`, `pt`, `ro`, `ru`, `sk`, `sl`, `sw`, `tr`, `ua`, `vi`, `zh_cn`, `zh_tw`. |
+
+### Current Weather And Forecast
+
+| Parameter | Description |
+|----------------|--------------------------------------------------------------------------------------------------------------------------------|
+| location | Location of weather in geographical coordinates (latitude/longitude/altitude). **Mandatory** |
+| forecastHours | Number of hours for hourly forecast. Optional, the default value is 24 (min="0", max="120", step="3"). |
+| forecastDays | Number of days for daily forecast (including todays forecast). Optional, the default value is 6 (min="0", max="16", step="1"). |
+
+Once the parameters `forecastHours` or `forecastDays` will be changed, the available channel groups on the thing will be created or removed accordingly.
+
+## Channels
+
+### Station
+
+| Channel Group ID | Channel ID | Item Type | Description |
+|------------------|------------|-----------|----------------------------------------------|
+| station | id | String | Id of the weather station or the city. |
+| station | name | String | Name of the weather station or the city. |
+| station | location | Location | Location of the weather station or the city. |
+
+### Current Weather
+
+| Channel Group ID | Channel ID | Item Type | Description |
+|------------------|-----------------|----------------------|---------------------------------------------------|
+| current | time-stamp | DateTime | Time of data observation. |
+| current | condition | String | Current weather condition. |
+| current | condition-id | String | Id of the current weather condition. **Advanced** |
+| current | icon | Image | Icon representing the current weather condition. |
+| current | temperature | Number:Temperature | Current temperature. |
+| current | pressure | Number:Pressure | Current barometric pressure. |
+| current | humidity | Number:Dimensionless | Current atmospheric humidity. |
+| current | wind-speed | Number:Speed | Current wind speed. |
+| current | wind-direction | Number:Angle | Current wind direction. |
+| current | gust-speed | Number:Speed | Current gust speed. **Advanced** |
+| current | cloudiness | Number:Dimensionless | Current cloudiness. |
+| current | rain | Number:Length | Rain volume for the last three hours. |
+| current | snow | Number:Length | Snow volume for the last three hours. |
+
+### 3 Hour Forecast
+
+| Channel Group ID | Channel ID | Item Type | Description |
+|--------------------------------------------------------|----------------|----------------------|------------------------------------------------------|
+| forecastHours03, forecastHours06, ... forecastHours120 | time-stamp | DateTime | Time of data forecasted. |
+| forecastHours03, forecastHours06, ... forecastHours120 | condition | String | Forecast weather condition. |
+| forecastHours03, forecastHours06, ... forecastHours120 | condition-id | String | Id of the forecasted weather condition. **Advanced** |
+| forecastHours03, forecastHours06, ... forecastHours120 | icon | Image | Icon representing the forecasted weather condition. |
+| forecastHours03, forecastHours06, ... forecastHours120 | temperature | Number:Temperature | Forecasted temperature. |
+| forecastHours03, forecastHours06, ... forecastHours120 | pressure | Number:Pressure | Forecasted barometric pressure. |
+| forecastHours03, forecastHours06, ... forecastHours120 | humidity | Number:Dimensionless | Forecasted atmospheric humidity. |
+| forecastHours03, forecastHours06, ... forecastHours120 | wind-speed | Number:Speed | Forecasted wind speed. |
+| forecastHours03, forecastHours06, ... forecastHours120 | wind-direction | Number:Angle | Forecasted wind direction. |
+| forecastHours03, forecastHours06, ... forecastHours120 | gust-speed | Number:Speed | Forecasted gust speed. **Advanced** |
+| forecastHours03, forecastHours06, ... forecastHours120 | cloudiness | Number:Dimensionless | Forecasted cloudiness. |
+| forecastHours03, forecastHours06, ... forecastHours120 | rain | Number:Length | Expected rain volume for the next 3 hours. |
+| forecastHours03, forecastHours06, ... forecastHours120 | snow | Number:Length | Expected snow volume for the next 3 hours. |
+
+### Daily Forecast
+
+| Channel Group ID | Channel ID | Item Type | Description |
+|------------------------------------------------------------------|-----------------|----------------------|------------------------------------------------------|
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | time-stamp | DateTime | Time of data forecasted. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition | String | Forecast weather condition. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | condition-id | String | Id of the forecasted weather condition. **Advanced** |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | icon | Image | Icon representing the forecasted weather condition. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | min-temperature | Number:Temperature | Minimum forecasted temperature of a day. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | max-temperature | Number:Temperature | Maximum forecasted temperature of a day. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | pressure | Number:Pressure | Forecasted barometric pressure. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | humidity | Number:Dimensionless | Forecasted atmospheric humidity. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-speed | Number:Speed | Forecasted wind speed. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | wind-direction | Number:Angle | Forecasted wind direction. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | gust-speed | Number:Speed | Forecasted gust speed. **Advanced** |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | cloudiness | Number:Dimensionless | Forecasted cloudiness. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | rain | Number:Length | Expected rain volume of a day. |
+| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | snow | Number:Length | Expected snow volume of a day. |
+
+## Full Example
+
+### Things
+
+demo.things
+
+```java
+Bridge openweathermap:weather-api:api "OpenWeatherMap Account" [apikey="AAA", refreshInterval=30, language="de"] {
+ Thing weather-and-forecast local "Local Weather And Forecast" [location="XXX,YYY", forecastHours=0, forecastDays=7]
+ Thing weather-and-forecast miami "Weather And Forecast In Miami" [location="25.782403,-80.264563", forecastHours=24, forecastDays=0]
+}
+```
+
+### Items
+
+demo.items
+
+```java
+String localStationId "ID [%s]" { channel="openweathermap:weather-and-forecast:api:local:station#id" }
+String localStationName "Name [%s]" { channel="openweathermap:weather-and-forecast:api:local:station#name" }
+Location localStationLocation "Location [%2$s°N %3$s°E]" { channel="openweathermap:weather-and-forecast:api:local:station#location" }
+
+DateTime localLastMeasurement "Timestamp of last measurement [%1$tY-%1$tm-%1$tdT%1$tH:%1$tM:%1$tS]" { channel="openweathermap:weather-and-forecast:api:local:current#time-stamp" }
+String localCurrentCondition "Current condition [%s]" { channel="openweathermap:weather-and-forecast:api:local:current#condition" }
+Image localCurrentConditionIcon "Icon" { channel="openweathermap:weather-and-forecast:api:local:current#icon" }
+Number:Temperature localCurrentTemperature "Current temperature [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:current#temperature" }
+Number:Pressure localCurrentPressure "Current barometric pressure [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:current#pressure" }
+Number:Dimensionless localCurrentHumidity "Current atmospheric humidity [%d %unit%]" { channel="openweathermap:weather-and-forecast:api:local:current#humidity" }
+Number:Speed localCurrentWindSpeed "Current wind speed [%.1f km/h]" { channel="openweathermap:weather-and-forecast:api:local:current#wind-speed" }
+Number:Angle localCurrentWindDirection "Current wind direction [%d %unit%]" { channel="openweathermap:weather-and-forecast:api:local:current#wind-direction" }
+Number:Dimensionless localCurrentCloudiness "Current cloudiness [%d %unit%]" { channel="openweathermap:weather-and-forecast:api:local:current#cloudiness" }
+Number:Length localCurrentRainVolume "Current rain volume [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:current#rain" }
+Number:Length localCurrentSnowVolume "Current snow volume [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:current#snow" }
+
+DateTime localDailyForecastTodayTimestamp "Timestamp of forecast [%1$tY-%1$tm-%1$td]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#time-stamp" }
+String localDailyForecastTodayCondition "Condition for today [%s]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#condition" }
+Image localDailyForecastTodayConditionIcon "Icon" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#icon" }
+Number:Temperature localDailyForecastTodayMinTemperature "Minimum temperature for today [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#min-temperature" }
+Number:Temperature localDailyForecastTodayMaxTemperature "Maximum temperature for today [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#max-temperature" }
+Number:Pressure localDailyForecastTodayPressure "Barometric pressure for today [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#pressure" }
+Number:Dimensionless localDailyForecastTodayHumidity "Atmospheric humidity for today [%d %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#humidity" }
+Number:Speed localDailyForecastTodayWindSpeed "Wind speed for today [%.1f km/h]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#wind-speed" }
+Number:Angle localDailyForecastTodayWindDirection "Wind direction for today [%d %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#wind-direction" }
+Number:Dimensionless localDailyForecastTodaytCloudiness "Cloudiness for today [%d %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#cloudiness" }
+Number:Length localDailyForecastTodayRainVolume "Rain volume for today [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#rain" }
+Number:Length localDailyForecastTodaySnowVolume "Snow volume for today [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastToday#snow" }
+
+DateTime localDailyForecastTomorrowTimestamp "Timestamp of forecast [%1$tY-%1$tm-%1$td]" { channel="openweathermap:weather-and-forecast:api:local:forecastTomorrow#time-stamp" }
+String localDailyForecastTomorrowCondition "Condition for tomorrow [%s]" { channel="openweathermap:weather-and-forecast:api:local:forecastTomorrow#condition" }
+Image localDailyForecastTomorrowConditionIcon "Icon" { channel="openweathermap:weather-and-forecast:api:local:forecastTomorrow#icon" }
+Number:Temperature localDailyForecastTomorrowMinTemperature "Minimum temperature for tomorrow [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastTomorrow#min-temperature" }
+Number:Temperature localDailyForecastTomorrowMaxTemperature "Maximum temperature for tomorrow [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastTomorrow#max-temperature" }
+...
+
+DateTime localDailyForecastDay2Timestamp "Timestamp of forecast [%1$tY-%1$tm-%1$td]" { channel="openweathermap:weather-and-forecast:api:local:forecastDay2#time-stamp" }
+String localDailyForecastDay2Condition "Condition in 2 days [%s]" { channel="openweathermap:weather-and-forecast:api:local:forecastDay2#condition" }
+Image localDailyForecastDay2ConditionIcon "Icon" { channel="openweathermap:weather-and-forecast:api:local:forecastDay2#icon" }
+Number:Temperature localDailyForecastDay2MinTemperature "Minimum temperature in 2 days [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastDay2#min-temperature" }
+Number:Temperature localDailyForecastDay2MaxTemperature "Maximum temperature in 2 days [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:local:forecastDay2#max-temperature" }
+...
+
+String miamiCurrentCondition "Current condition in Miami [%s]" { channel="openweathermap:weather-and-forecast:api:miami:current#condition" }
+Image miamiCurrentConditionIcon "Icon" { channel="openweathermap:weather-and-forecast:api:miami:current#icon" }
+Number:Temperature miamiCurrentTemperature "Current temperature in Miami [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:miami:current#temperature" }
+...
+
+String miamiHourlyForecast03Condition "Condition in Miami for the next three hours [%s]" { channel="openweathermap:weather-and-forecast:api:miami:forecastHours03#condition" }
+Image miamiHourlyForecast03ConditionIcon "Icon" { channel="openweathermap:weather-and-forecast:api:miami:forecastHours03#icon" }
+Number:Temperature miamiHourlyForecast03Temperature "Temperature in Miami for the next three hours [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:miami:forecastHours03#temperature" }
+...
+String miamiHourlyForecast06Condition "Condition in Miami for hours 3 to 6 [%s]" { channel="openweathermap:weather-and-forecast:api:miami:forecastHours06#condition" }
+Image miamiHourlyForecast06ConditionIcon "Icon" { channel="openweathermap:weather-and-forecast:api:miami:forecastHours06#icon" }
+Number:Temperature miamiHourlyForecast06Temperature "Temperature in Miami for hours 3 to 6 [%.1f %unit%]" { channel="openweathermap:weather-and-forecast:api:miami:forecastHours06#temperature" }
+...
+```
+
+### Sitemap
+
+demo.sitemap
+
+```perl
+sitemap demo label="OpenWeatherMap" {
+ Frame label="Local Weather Station" {
+ Text item=localStationId
+ Text item=localStationName
+ Mapview item=localStationLocation
+ }
+ Frame label="Current local weather" {
+ Text item=localLastMeasurement
+ Text item=localCurrentCondition
+ Image item=localCurrentConditionIcon
+ Text item=localCurrentTemperature
+ Text item=localCurrentPressure
+ Text item=localCurrentHumidity
+ Text item=localCurrentWindSpeed
+ Text item=localCurrentWindDirection
+ Text item=localCurrentCloudiness
+ Text item=localCurrentRainVolume
+ Text item=localCurrentSnowVolume
+ }
+ Frame label="Local forecast for today" {
+ Text item=localDailyForecastTodayTimestamp
+ Text item=localDailyForecastTodayCondition
+ Image item=localDailyForecastTodayConditionIcon
+ Text item=localDailyForecastTodayMinTemperature
+ Text item=localDailyForecastTodayMaxTemperature
+ Text item=localDailyForecastTodayPressure
+ Text item=localDailyForecastTodayHumidity
+ Text item=localDailyForecastTodayWindSpeed
+ Text item=localDailyForecastTodayWindDirection
+ Text item=localDailyForecastTodayCloudiness
+ Text item=localDailyForecastTodayRainVolume
+ Text item=localDailyForecastTodaySnowVolume
+ }
+ Frame label="Local forecast for tomorrow" {
+ Text item=localDailyForecastTomorrowTimestamp
+ Text item=localDailyForecastTomorrowCondition
+ Image item=localDailyForecastTomorrowConditionIcon
+ Text item=localDailyForecastTomorrowMinTemperature
+ Text item=localDailyForecastTomorrowMaxTemperature
+ ...
+ }
+ Frame label="Local forecast in 2 days" {
+ Text item=localDailyForecastDay2Timestamp
+ Text item=localDailyForecastDay2Condition
+ Image item=localDailyForecastDay2ConditionIcon
+ Text item=localDailyForecastDay2MinTemperature
+ Text item=localDailyForecastDay2MaxTemperature
+ ...
+ }
+ Frame label="Current weather in Miami" {
+ Text item=miamiCurrentCondition
+ Image item=miamiCurrentConditionIcon
+ Text item=miamiCurrentTemperature
+ ...
+ }
+ Frame label="Forecast in Miami for the next three hours" {
+ Text item=miamiHourlyForecast03Condition
+ Image item=miamiHourlyForecast03ConditionIcon
+ Text item=miamiHourlyForecast03Temperature
+ ...
+ }
+ Frame label="Forecast weather in Miami for the hours 3 to 6" {
+ Text item=miamiHourlyForecast06Condition
+ Image item=miamiHourlyForecast06ConditionIcon
+ Text item=miamiHourlyForecast06Temperature
+ ...
+ }
+}
+```
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/build.properties b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/build.properties
new file mode 100644
index 00000000000..a6cfff567c9
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/build.properties
@@ -0,0 +1,7 @@
+source..=src/main/java/
+output..=target/classes
+bin.includes=META-INF/,\
+ .,\
+ OSGI-INF/,\
+ ESH-INF/,\
+ NOTICE
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/pom.xml b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/pom.xml
new file mode 100644
index 00000000000..5129622d160
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/pom.xml
@@ -0,0 +1,18 @@
+
+
+
+ 4.0.0
+
+
+ pom
+ org.eclipse.smarthome.binding
+ 0.10.0-SNAPSHOT
+
+
+ org.eclipse.smarthome.binding.openweathermap
+ 0.10.0-SNAPSHOT
+ eclipse-plugin
+
+ Eclipse SmartHome OpenWeatherMap Binding
+
+
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/OpenWeatherMapBindingConstants.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/OpenWeatherMapBindingConstants.java
new file mode 100644
index 00000000000..3f09a7bc6d5
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/OpenWeatherMapBindingConstants.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.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");
+
+ // 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");
+
+ // 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";
+
+ // 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_TEMPERATURE = "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";
+
+ // List of all configuration
+ public static final String CONFIG_FORECAST_DAYS = "forecastDays";
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/config/OpenWeatherMapAPIConfiguration.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/config/OpenWeatherMapAPIConfiguration.java
new file mode 100644
index 00000000000..9d70d34b650
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/config/OpenWeatherMapAPIConfiguration.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.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;
+
+/**
+ * The {@link OpenWeatherMapAPIConfiguration} is the class used to match the bridge configuration.
+ *
+ * @author Christoph Weitkamp - Initial contribution
+ */
+@NonNullByDefault
+public class OpenWeatherMapAPIConfiguration {
+
+ // supported languages (see https://openweathermap.org/current#multi)
+ public static final Set 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", "sk", "sl", "sw", "tr", "ua", "vi", "zh_cn", "zh_tw").collect(Collectors.toSet()));
+
+ private @Nullable String apikey;
+ private int refreshInterval;
+ private @Nullable String language;
+
+ public @Nullable String getApikey() {
+ return apikey;
+ }
+
+ public void setApikey(String apikey) {
+ this.apikey = apikey;
+ }
+
+ public int getRefreshInterval() {
+ return refreshInterval;
+ }
+
+ public void setRefreshInterval(int refreshInterval) {
+ this.refreshInterval = refreshInterval;
+ }
+
+ public @Nullable String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/config/OpenWeatherMapLocationConfiguration.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/config/OpenWeatherMapLocationConfiguration.java
new file mode 100644
index 00000000000..1339b40ce01
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/config/OpenWeatherMapLocationConfiguration.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.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 {
+
+ private @NonNullByDefault({}) String location;
+
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/config/OpenWeatherMapWeatherAndForecastConfiguration.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/config/OpenWeatherMapWeatherAndForecastConfiguration.java
new file mode 100644
index 00000000000..7560cfb9c24
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/config/OpenWeatherMapWeatherAndForecastConfiguration.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.smarthome.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 {
+
+ private int forecastHours;
+ private int forecastDays;
+
+ public int getForecastHours() {
+ return forecastHours;
+ }
+
+ public void setForecastHours(int forecastHours) {
+ this.forecastHours = forecastHours;
+ }
+
+ public int getForecastDays() {
+ return forecastDays;
+ }
+
+ public void setForecastDays(int forecastDays) {
+ this.forecastDays = forecastDays;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/connection/OpenWeatherMapCommunicationException.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/connection/OpenWeatherMapCommunicationException.java
new file mode 100644
index 00000000000..eab5421a9ff
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/connection/OpenWeatherMapCommunicationException.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.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);
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/connection/OpenWeatherMapConfigurationException.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/connection/OpenWeatherMapConfigurationException.java
new file mode 100644
index 00000000000..aa16185d444
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/connection/OpenWeatherMapConfigurationException.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.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);
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/connection/OpenWeatherMapConnection.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/connection/OpenWeatherMapConnection.java
new file mode 100644
index 00000000000..efc157faca0
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/connection/OpenWeatherMapConnection.java
@@ -0,0 +1,273 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.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.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.lang.StringUtils;
+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.eclipse.smarthome.binding.openweathermap.internal.config.OpenWeatherMapAPIConfiguration;
+import org.eclipse.smarthome.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.OpenWeatherMapJsonDailyForecastData;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.OpenWeatherMapJsonHourlyForecastData;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.OpenWeatherMapJsonWeatherData;
+import org.eclipse.smarthome.core.cache.ExpiringCacheMap;
+import org.eclipse.smarthome.core.library.types.PointType;
+import org.eclipse.smarthome.core.library.types.RawType;
+import org.eclipse.smarthome.io.net.http.HttpUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+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 UTF_8_ENCODING = "UTF-8";
+
+ 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";
+ // Weather icons (see https://openweathermap.org/weather-conditions)
+ private static final String ICON_URL = "https://openweathermap.org/img/w/";
+
+ private final OpenWeatherMapAPIHandler handler;
+ private final HttpClient httpClient;
+
+ private static final ExpiringCacheMap IMAGE_CACHE = new ExpiringCacheMap<>(
+ TimeUnit.DAYS.toMillis(7));
+ private final ExpiringCacheMap 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.getRefreshInterval()));
+ }
+
+ /**
+ * 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 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 params = getRequestParams(handler.getOpenWeatherMapAPIConfig(), location);
+ params.put(PARAM_FORECAST_CNT, Integer.toString(count));
+
+ return gson.fromJson(getResponseFromCache(buildURL(DAILY_FORECAST_URL, params)),
+ OpenWeatherMapJsonDailyForecastData.class);
+ }
+
+ public static @Nullable RawType getWeatherIcon(String iconKey) {
+ if (StringUtils.isEmpty(iconKey)) {
+ throw new IllegalArgumentException("Cannot download weather icon as icon key is null.");
+ }
+
+ return downloadWeatherIconFromCache(ICON_URL + iconKey + ".png");
+ }
+
+ private static @Nullable RawType downloadWeatherIconFromCache(String url) {
+ // TODO store / cache icon file in a local folder
+ return IMAGE_CACHE.putIfAbsentAndGet(url, () -> downloadWeatherIcon(url));
+ }
+
+ private static @Nullable RawType downloadWeatherIcon(String url) {
+ return HttpUtil.downloadImage(url);
+ }
+
+ private Map getRequestParams(OpenWeatherMapAPIConfiguration config, @Nullable PointType location) {
+ if (location == null) {
+ throw new OpenWeatherMapConfigurationException("@text/offline.conf-error-missing-location");
+ }
+
+ Map params = new HashMap<>();
+ // API key (see http://openweathermap.org/appid)
+ params.put(PARAM_APPID, StringUtils.trimToEmpty(config.getApikey()));
+
+ // 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 = StringUtils.trimToEmpty(config.getLanguage());
+ if (!language.isEmpty()) {
+ params.put(PARAM_LANG, language.toLowerCase());
+ }
+ return params;
+ }
+
+ private String buildURL(String url, Map requestParams) {
+ return requestParams.keySet().stream().map(key -> key + "=" + encodeParam(requestParams.get(key)))
+ .collect(joining("&", url + "?", StringUtils.EMPTY));
+ }
+
+ private String encodeParam(String value) {
+ try {
+ return URLEncoder.encode(value, UTF_8_ENCODING);
+ } catch (UnsupportedEncodingException e) {
+ logger.debug("UnsupportedEncodingException occurred during execution: {}", e.getLocalizedMessage(), e);
+ return StringUtils.EMPTY;
+ }
+ }
+
+ private 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 = StringUtils.EMPTY;
+ 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) {
+ JsonObject jsonResponse = parser.parse(response).getAsJsonObject();
+ if (jsonResponse.has("message")) {
+ return jsonResponse.get("message").getAsString();
+ }
+ return response;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/discovery/OpenWeatherMapDiscoveryService.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/discovery/OpenWeatherMapDiscoveryService.java
new file mode 100644
index 00000000000..5091e26364f
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/discovery/OpenWeatherMapDiscoveryService.java
@@ -0,0 +1,136 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.discovery;
+
+import static org.eclipse.smarthome.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.eclipse.smarthome.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler;
+import org.eclipse.smarthome.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
+import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService;
+import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder;
+import org.eclipse.smarthome.core.i18n.LocaleProvider;
+import org.eclipse.smarthome.core.i18n.LocationProvider;
+import org.eclipse.smarthome.core.i18n.TranslationProvider;
+import org.eclipse.smarthome.core.library.types.PointType;
+import org.eclipse.smarthome.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 = 30;
+ 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 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();
+ }
+
+ @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(this::scanForNewLocation, 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() {
+ 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 {
+ createResults(currentLocation);
+ }
+ }
+
+ private void createResults(PointType location) {
+ String locationString = location.toFullString();
+ ThingUID bridgeUID = bridgeHandler.getThing().getUID();
+ createWeatherAndForecastResult(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());
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/factory/OpenWeatherMapHandlerFactory.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/factory/OpenWeatherMapHandlerFactory.java
new file mode 100644
index 00000000000..cc5db9c8a95
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/factory/OpenWeatherMapHandlerFactory.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.factory;
+
+import static org.eclipse.smarthome.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.eclipse.smarthome.binding.openweathermap.internal.discovery.OpenWeatherMapDiscoveryService;
+import org.eclipse.smarthome.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler;
+import org.eclipse.smarthome.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
+import org.eclipse.smarthome.binding.openweathermap.internal.handler.OpenWeatherMapWeatherAndForecastHandler;
+import org.eclipse.smarthome.config.discovery.DiscoveryService;
+import org.eclipse.smarthome.core.i18n.LocaleProvider;
+import org.eclipse.smarthome.core.i18n.LocationProvider;
+import org.eclipse.smarthome.core.i18n.TranslationProvider;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.ThingUID;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory;
+import org.eclipse.smarthome.io.net.http.HttpClientFactory;
+import org.osgi.framework.ServiceRegistration;
+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 SUPPORTED_THING_TYPES_UIDS = Collections
+ .unmodifiableSet(Stream.concat(OpenWeatherMapAPIHandler.SUPPORTED_THING_TYPES.stream(),
+ AbstractOpenWeatherMapHandler.SUPPORTED_THING_TYPES.stream()).collect(Collectors.toSet()));
+
+ private final Map> discoveryServiceRegs = new HashMap<>();
+ private @NonNullByDefault({}) HttpClient httpClient;
+ private @NonNullByDefault({}) LocaleProvider localeProvider;
+ private @NonNullByDefault({}) LocationProvider locationProvider;
+ private @NonNullByDefault({}) TranslationProvider i18nProvider;
+
+ @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);
+ }
+
+ 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();
+ }
+ }
+ }
+ }
+
+ @Reference
+ protected void setHttpClientFactory(HttpClientFactory httpClientFactory) {
+ this.httpClient = httpClientFactory.getCommonHttpClient();
+ }
+
+ protected void unsetHttpClientFactory(HttpClientFactory httpClientFactory) {
+ this.httpClient = null;
+ }
+
+ @Reference
+ protected void setLocationProvider(LocationProvider locationProvider) {
+ this.locationProvider = locationProvider;
+ }
+
+ protected void unsetLocationProvider(LocationProvider locationProvider) {
+ this.locationProvider = null;
+ }
+
+ @Reference
+ protected void setLocaleProvider(LocaleProvider localeProvider) {
+ this.localeProvider = localeProvider;
+ }
+
+ protected void unsetLocaleProvider(LocaleProvider localeProvider) {
+ this.localeProvider = null;
+ }
+
+ @Reference
+ protected void setTranslationProvider(TranslationProvider i18nProvider) {
+ this.i18nProvider = i18nProvider;
+ }
+
+ protected void unsetTranslationProvider(TranslationProvider i18nProvider) {
+ this.i18nProvider = null;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/handler/AbstractOpenWeatherMapHandler.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/handler/AbstractOpenWeatherMapHandler.java
new file mode 100644
index 00000000000..3099e3630cc
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/handler/AbstractOpenWeatherMapHandler.java
@@ -0,0 +1,190 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.handler;
+
+import static org.eclipse.smarthome.binding.openweathermap.internal.OpenWeatherMapBindingConstants.THING_TYPE_WEATHER_AND_FORECAST;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.measure.Unit;
+
+import org.apache.commons.lang.StringUtils;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.binding.openweathermap.internal.config.OpenWeatherMapLocationConfiguration;
+import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapCommunicationException;
+import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapConfigurationException;
+import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
+import org.eclipse.smarthome.core.library.types.DateTimeType;
+import org.eclipse.smarthome.core.library.types.DecimalType;
+import org.eclipse.smarthome.core.library.types.PointType;
+import org.eclipse.smarthome.core.library.types.QuantityType;
+import org.eclipse.smarthome.core.library.types.RawType;
+import org.eclipse.smarthome.core.library.types.StringType;
+import org.eclipse.smarthome.core.thing.Channel;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.ThingStatusInfo;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.eclipse.smarthome.core.types.RefreshType;
+import org.eclipse.smarthome.core.types.State;
+import org.eclipse.smarthome.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 SUPPORTED_THING_TYPES = Collections
+ .singleton(THING_TYPE_WEATHER_AND_FORECAST);
+
+ // keeps track of the parsed location
+ protected @Nullable PointType location;
+
+ public AbstractOpenWeatherMapHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public void initialize() {
+ logger.debug("Initialize OpenWeatherMap handler {}.", getThing().getUID());
+ OpenWeatherMapLocationConfiguration config = getConfigAs(OpenWeatherMapLocationConfiguration.class);
+
+ boolean configValid = true;
+ if (StringUtils.trimToNull(config.getLocation()) == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "@text/offline.conf-error-missing-location");
+ configValid = false;
+ }
+
+ try {
+ location = new PointType(config.getLocation());
+ } catch (IllegalArgumentException e) {
+ location = null;
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "@text/offline.conf-error-parsing-location");
+ 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 (isLinked(channelUID.getId()) && channelUID.isInGroup() && channelUID.getGroupId() != null) {
+ 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()), ZoneId.systemDefault()));
+ }
+
+ 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);
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/handler/OpenWeatherMapAPIHandler.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/handler/OpenWeatherMapAPIHandler.java
new file mode 100644
index 00000000000..12ad96bb957
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/handler/OpenWeatherMapAPIHandler.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.handler;
+
+import static org.eclipse.smarthome.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang.StringUtils;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.smarthome.binding.openweathermap.internal.config.OpenWeatherMapAPIConfiguration;
+import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
+import org.eclipse.smarthome.config.core.Configuration;
+import org.eclipse.smarthome.core.i18n.LocaleProvider;
+import org.eclipse.smarthome.core.thing.Bridge;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.ThingTypeUID;
+import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler;
+import org.eclipse.smarthome.core.thing.binding.ThingHandler;
+import org.eclipse.smarthome.core.types.Command;
+import org.eclipse.smarthome.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 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.");
+ config = getConfigAs(OpenWeatherMapAPIConfiguration.class);
+
+ boolean configValid = true;
+ if (StringUtils.trimToNull(config.getApikey()) == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "@text/offline.conf-error-missing-apikey");
+ configValid = false;
+ }
+ int refreshInterval = config.getRefreshInterval();
+ if (refreshInterval < 10) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "@text/offline.conf-error-not-supported-refreshInterval");
+ configValid = false;
+ }
+ String language = config.getLanguage();
+ if (language != null) {
+ language = StringUtils.trimToEmpty(language);
+ 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);
+
+ if (refreshJob == null || refreshJob.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.");
+ if (refreshJob != null && !refreshJob.isCancelled()) {
+ logger.debug("Stop refresh job.");
+ if (refreshJob.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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/handler/OpenWeatherMapWeatherAndForecastHandler.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/handler/OpenWeatherMapWeatherAndForecastHandler.java
new file mode 100644
index 00000000000..859ecba5525
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/handler/OpenWeatherMapWeatherAndForecastHandler.java
@@ -0,0 +1,459 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.handler;
+
+import static org.eclipse.smarthome.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
+import static org.eclipse.smarthome.core.library.unit.MetricPrefix.*;
+import static org.eclipse.smarthome.core.library.unit.SIUnits.*;
+
+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.eclipse.smarthome.binding.openweathermap.internal.config.OpenWeatherMapWeatherAndForecastConfiguration;
+import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapCommunicationException;
+import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapConfigurationException;
+import org.eclipse.smarthome.binding.openweathermap.internal.connection.OpenWeatherMapConnection;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.OpenWeatherMapJsonDailyForecastData;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.OpenWeatherMapJsonHourlyForecastData;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.OpenWeatherMapJsonWeatherData;
+import org.eclipse.smarthome.config.core.Configuration;
+import org.eclipse.smarthome.core.library.unit.SmartHomeUnits;
+import org.eclipse.smarthome.core.thing.Channel;
+import org.eclipse.smarthome.core.thing.ChannelGroupUID;
+import org.eclipse.smarthome.core.thing.ChannelUID;
+import org.eclipse.smarthome.core.thing.Thing;
+import org.eclipse.smarthome.core.thing.ThingStatus;
+import org.eclipse.smarthome.core.thing.ThingStatusDetail;
+import org.eclipse.smarthome.core.thing.binding.ThingHandlerCallback;
+import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder;
+import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder;
+import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID;
+import org.eclipse.smarthome.core.types.State;
+import org.eclipse.smarthome.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) {
+ super(thing);
+ }
+
+ @Override
+ public void initialize() {
+ super.initialize();
+ logger.debug("Initialize OpenWeatherMapWeatherAndForecastHandler handler {}.", getThing().getUID());
+ OpenWeatherMapWeatherAndForecastConfiguration config = getConfigAs(
+ OpenWeatherMapWeatherAndForecastConfiguration.class);
+
+ boolean configValid = true;
+ int newForecastHours = config.getForecastHours();
+ 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.getForecastDays();
+ 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 toBeAddedChannels = new ArrayList<>();
+ List 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());
+
+ updateStatus(ThingStatus.UNKNOWN);
+ }
+ }
+
+ @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);
+ List channels = getThing().getChannels().stream()
+ .filter(c -> c.getUID().getGroupId().startsWith(CHANNEL_GROUP_FORECAST_TODAY)
+ || c.getUID().getGroupId().startsWith(CHANNEL_GROUP_FORECAST_TOMORROW)
+ || 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();
+ if (weatherData != null) {
+ State state = UnDefType.UNDEF;
+ switch (channelId) {
+ case CHANNEL_STATION_ID:
+ state = getStringTypeState(weatherData.getId().toString());
+ break;
+ case CHANNEL_STATION_NAME:
+ state = getStringTypeState(weatherData.getName());
+ break;
+ case CHANNEL_STATION_LOCATION:
+ state = getPointTypeState(weatherData.getCoord().getLat(), weatherData.getCoord().getLon());
+ break;
+ case CHANNEL_TIME_STAMP:
+ state = getDateTimeTypeState(weatherData.getDt());
+ break;
+ case CHANNEL_CONDITION:
+ state = getStringTypeState(weatherData.getWeather().get(0).getDescription());
+ break;
+ case CHANNEL_CONDITION_ID:
+ state = getStringTypeState(weatherData.getWeather().get(0).getId().toString());
+ break;
+ case CHANNEL_CONDITION_ICON:
+ state = getRawTypeState(
+ OpenWeatherMapConnection.getWeatherIcon(weatherData.getWeather().get(0).getIcon()));
+ break;
+ case CHANNEL_TEMPERATURE:
+ state = getQuantityTypeState(weatherData.getMain().getTemp(), CELSIUS);
+ break;
+ case CHANNEL_PRESSURE:
+ state = getQuantityTypeState(weatherData.getMain().getPressure(), HECTO(PASCAL));
+ break;
+ case CHANNEL_HUMIDITY:
+ state = getQuantityTypeState(weatherData.getMain().getHumidity(), SmartHomeUnits.PERCENT);
+ break;
+ case CHANNEL_WIND_SPEED:
+ state = getQuantityTypeState(weatherData.getWind().getSpeed(), SmartHomeUnits.METRE_PER_SECOND);
+ break;
+ case CHANNEL_WIND_DIRECTION:
+ state = getQuantityTypeState(weatherData.getWind().getDeg(), SmartHomeUnits.DEGREE_ANGLE);
+ break;
+ case CHANNEL_GUST_SPEED:
+ state = getQuantityTypeState(weatherData.getWind().getGust(), SmartHomeUnits.METRE_PER_SECOND);
+ break;
+ case CHANNEL_CLOUDINESS:
+ state = getQuantityTypeState(weatherData.getClouds().getAll(), SmartHomeUnits.PERCENT);
+ break;
+ case CHANNEL_RAIN:
+ state = getQuantityTypeState(
+ weatherData.getRain() == null || weatherData.getRain().get3h() == null ? 0
+ : weatherData.getRain().get3h(),
+ MILLI(METRE));
+ break;
+ case CHANNEL_SNOW:
+ state = getQuantityTypeState(
+ weatherData.getSnow() == null || weatherData.getSnow().get3h() == null ? 0
+ : weatherData.getSnow().get3h(),
+ 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 updateHourlyForecastChannel(ChannelUID channelUID, int count) {
+ String channelId = channelUID.getIdWithoutGroup();
+ String channelGroupId = channelUID.getGroupId();
+ if (hourlyForecastData != null && hourlyForecastData.getList().size() > count) {
+ org.eclipse.smarthome.binding.openweathermap.internal.model.forecast.hourly.List forecastData = hourlyForecastData
+ .getList().get(count);
+ State state = UnDefType.UNDEF;
+ switch (channelId) {
+ case CHANNEL_TIME_STAMP:
+ state = getDateTimeTypeState(forecastData.getDt());
+ break;
+ case CHANNEL_CONDITION:
+ state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
+ break;
+ case CHANNEL_CONDITION_ID:
+ state = getStringTypeState(forecastData.getWeather().get(0).getId().toString());
+ break;
+ case CHANNEL_CONDITION_ICON:
+ state = getRawTypeState(
+ OpenWeatherMapConnection.getWeatherIcon(forecastData.getWeather().get(0).getIcon()));
+ break;
+ case CHANNEL_TEMPERATURE:
+ state = getQuantityTypeState(forecastData.getMain().getTemp(), CELSIUS);
+ break;
+ case CHANNEL_PRESSURE:
+ state = getQuantityTypeState(forecastData.getMain().getPressure(), HECTO(PASCAL));
+ break;
+ case CHANNEL_HUMIDITY:
+ state = getQuantityTypeState(forecastData.getMain().getHumidity(), SmartHomeUnits.PERCENT);
+ break;
+ case CHANNEL_WIND_SPEED:
+ state = getQuantityTypeState(forecastData.getWind().getSpeed(), SmartHomeUnits.METRE_PER_SECOND);
+ break;
+ case CHANNEL_WIND_DIRECTION:
+ state = getQuantityTypeState(forecastData.getWind().getDeg(), SmartHomeUnits.DEGREE_ANGLE);
+ break;
+ case CHANNEL_GUST_SPEED:
+ state = getQuantityTypeState(forecastData.getWind().getGust(), SmartHomeUnits.METRE_PER_SECOND);
+ break;
+ case CHANNEL_CLOUDINESS:
+ state = getQuantityTypeState(forecastData.getClouds().getAll(), SmartHomeUnits.PERCENT);
+ break;
+ case CHANNEL_RAIN:
+ state = getQuantityTypeState(
+ forecastData.getRain() == null || forecastData.getRain().get3h() == null ? 0
+ : forecastData.getRain().get3h(),
+ MILLI(METRE));
+ break;
+ case CHANNEL_SNOW:
+ state = getQuantityTypeState(
+ forecastData.getSnow() == null || forecastData.getSnow().get3h() == null ? 0
+ : forecastData.getSnow().get3h(),
+ 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();
+ if (dailyForecastData != null && dailyForecastData.getList().size() > count) {
+ org.eclipse.smarthome.binding.openweathermap.internal.model.forecast.daily.List forecastData = dailyForecastData
+ .getList().get(count);
+ State state = UnDefType.UNDEF;
+ switch (channelId) {
+ case CHANNEL_TIME_STAMP:
+ state = getDateTimeTypeState(forecastData.getDt());
+ break;
+ case CHANNEL_CONDITION:
+ state = getStringTypeState(forecastData.getWeather().get(0).getDescription());
+ break;
+ case CHANNEL_CONDITION_ID:
+ state = getStringTypeState(forecastData.getWeather().get(0).getId().toString());
+ break;
+ case CHANNEL_CONDITION_ICON:
+ state = getRawTypeState(
+ OpenWeatherMapConnection.getWeatherIcon(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_PRESSURE:
+ state = getQuantityTypeState(forecastData.getPressure(), HECTO(PASCAL));
+ break;
+ case CHANNEL_HUMIDITY:
+ state = getQuantityTypeState(forecastData.getHumidity(), SmartHomeUnits.PERCENT);
+ break;
+ case CHANNEL_WIND_SPEED:
+ state = getQuantityTypeState(forecastData.getSpeed(), SmartHomeUnits.METRE_PER_SECOND);
+ break;
+ case CHANNEL_WIND_DIRECTION:
+ state = getQuantityTypeState(forecastData.getDeg(), SmartHomeUnits.DEGREE_ANGLE);
+ break;
+ case CHANNEL_GUST_SPEED:
+ state = getQuantityTypeState(forecastData.getGust(), SmartHomeUnits.METRE_PER_SECOND);
+ break;
+ case CHANNEL_CLOUDINESS:
+ state = getQuantityTypeState(forecastData.getClouds(), SmartHomeUnits.PERCENT);
+ break;
+ case CHANNEL_RAIN:
+ state = getQuantityTypeState(forecastData.getRain() == null ? 0 : forecastData.getRain(),
+ MILLI(METRE));
+ break;
+ case CHANNEL_SNOW:
+ state = getQuantityTypeState(forecastData.getSnow() == null ? 0 : forecastData.getSnow(),
+ 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);
+ }
+ }
+
+ private List createChannelsForGroup(String channelGroupId, ChannelGroupTypeUID channelGroupTypeUID) {
+ logger.debug("Building channel group '{}' for thing '{}'.", channelGroupId, getThing().getUID());
+ List channels = new ArrayList<>();
+ ThingHandlerCallback callback = getCallback();
+ if (callback != null) {
+ for (ChannelBuilder channelBuilder : callback.createChannelBuilders(
+ new ChannelGroupUID(getThing().getUID(), channelGroupId), channelGroupTypeUID)) {
+ channels.add(channelBuilder.build());
+ }
+ }
+ return channels;
+ }
+
+ private List removeChannelsOfGroup(String channelGroupId) {
+ logger.debug("Removing channel group '{}' from thing '{}'.", channelGroupId, getThing().getUID());
+ return getThing().getChannelsOfGroup(channelGroupId);
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/OpenWeatherMapJsonDailyForecastData.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/OpenWeatherMapJsonDailyForecastData.java
new file mode 100644
index 00000000000..5526d0cfe50
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/OpenWeatherMapJsonDailyForecastData.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model;
+
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.City;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.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;
+
+ 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 getList() {
+ return list;
+ }
+
+ public void setList(java.util.List list) {
+ this.list = list;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/OpenWeatherMapJsonHourlyForecastData.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/OpenWeatherMapJsonHourlyForecastData.java
new file mode 100644
index 00000000000..09cb3be053d
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/OpenWeatherMapJsonHourlyForecastData.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model;
+
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.City;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ 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 getList() {
+ return list;
+ }
+
+ public void setList(java.util.List list) {
+ this.list = list;
+ }
+
+ public City getCity() {
+ return city;
+ }
+
+ public void setCity(City city) {
+ this.city = city;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/OpenWeatherMapJsonWeatherData.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/OpenWeatherMapJsonWeatherData.java
new file mode 100644
index 00000000000..e1e0446fe44
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/OpenWeatherMapJsonWeatherData.java
@@ -0,0 +1,160 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Clouds;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Coord;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Rain;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Snow;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Weather;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Wind;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.weather.Main;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ private String base;
+ private Main main;
+ private 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 getWeather() {
+ return weather;
+ }
+
+ public void setWeather(List 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 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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/City.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/City.java
new file mode 100644
index 00000000000..692e996d7ac
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/City.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Clouds.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Clouds.java
new file mode 100644
index 00000000000..d66d65c36da
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Clouds.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Coord.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Coord.java
new file mode 100644
index 00000000000..43cb7662ad2
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Coord.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Rain.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Rain.java
new file mode 100644
index 00000000000..d3b517ad8b9
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Rain.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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("3h")
+ private @Nullable Double threeHours;
+
+ public @Nullable Double get3h() {
+ return threeHours;
+ }
+
+ public void set3h(Double threeHours) {
+ this.threeHours = threeHours;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Snow.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Snow.java
new file mode 100644
index 00000000000..ea0ba0e810b
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Snow.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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("3h")
+ private @Nullable Double threeHours;
+
+ public @Nullable Double get3h() {
+ return threeHours;
+ }
+
+ public void set3h(Double threeHours) {
+ this.threeHours = threeHours;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Weather.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Weather.java
new file mode 100644
index 00000000000..392cc24f6ad
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Weather.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Wind.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Wind.java
new file mode 100644
index 00000000000..8be6eec2b35
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/base/Wind.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/daily/List.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/daily/List.java
new file mode 100644
index 00000000000..bd523b9dfcc
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/daily/List.java
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.forecast.daily;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Weather;
+
+/**
+ * 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;
+ private Double pressure;
+ private Integer humidity;
+ private java.util.List 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 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 getWeather() {
+ return weather;
+ }
+
+ public void setWeather(java.util.List 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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/daily/Temp.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/daily/Temp.java
new file mode 100644
index 00000000000..928c6c8722a
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/daily/Temp.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/hourly/List.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/hourly/List.java
new file mode 100644
index 00000000000..cbf1219f3e6
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/hourly/List.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.forecast.hourly;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Clouds;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Rain;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Snow;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Weather;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.base.Wind;
+import org.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ 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 getWeather() {
+ return weather;
+ }
+
+ public void setWeather(java.util.List 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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/hourly/Main.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/hourly/Main.java
new file mode 100644
index 00000000000..c9f30dfadff
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/hourly/Main.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.forecast.hourly;
+
+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("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 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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/hourly/Sys.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/hourly/Sys.java
new file mode 100644
index 00000000000..678102ffe59
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/forecast/hourly/Sys.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/weather/Main.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/weather/Main.java
new file mode 100644
index 00000000000..6b6f2df758f
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/weather/Main.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.weather;
+
+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;
+ 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 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;
+ }
+}
diff --git a/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/weather/Sys.java b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/weather/Sys.java
new file mode 100644
index 00000000000..088133c7d30
--- /dev/null
+++ b/extensions/binding/org.eclipse.smarthome.binding.openweathermap/src/main/java/org/eclipse/smarthome/binding/openweathermap/internal/model/weather/Sys.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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.eclipse.smarthome.binding.openweathermap.internal.model.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;
+ }
+}
diff --git a/extensions/binding/pom.xml b/extensions/binding/pom.xml
index e36364d1334..7274f48237b 100644
--- a/extensions/binding/pom.xml
+++ b/extensions/binding/pom.xml
@@ -44,6 +44,7 @@
org.eclipse.smarthome.binding.ntp.test
org.eclipse.smarthome.binding.onewire
org.eclipse.smarthome.binding.onewire.test
+ org.eclipse.smarthome.binding.openweathermap
org.eclipse.smarthome.binding.serialbutton
org.eclipse.smarthome.binding.sonos
org.eclipse.smarthome.binding.tradfri
diff --git a/features/karaf/esh-ext/src/main/feature/feature.xml b/features/karaf/esh-ext/src/main/feature/feature.xml
index 068a4a155c4..1c26a86175d 100644
--- a/features/karaf/esh-ext/src/main/feature/feature.xml
+++ b/features/karaf/esh-ext/src/main/feature/feature.xml
@@ -125,6 +125,11 @@
mvn:org.eclipse.smarthome.binding/org.eclipse.smarthome.binding.onewire/${project.version}
+
+ esh-base
+ mvn:org.eclipse.smarthome.binding/org.eclipse.smarthome.binding.openweathermap/${project.version}
+
+
esh-base
esh-io-transport-serial
diff --git a/features/org.eclipse.smarthome.feature.runtime.binding/feature.xml b/features/org.eclipse.smarthome.feature.runtime.binding/feature.xml
index 41696da5e57..89ba6b77e7d 100644
--- a/features/org.eclipse.smarthome.feature.runtime.binding/feature.xml
+++ b/features/org.eclipse.smarthome.feature.runtime.binding/feature.xml
@@ -136,6 +136,13 @@
version="0.0.0"
unpack="false"/>
+
+