added migrated 2.x add-ons

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

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.openweathermap</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ds.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== 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/openhab/openhab-addons

View File

@@ -0,0 +1,325 @@
---
layout: documentation
---
{% include base.html %}
# OpenWeatherMap Binding
This binding integrates the [OpenWeatherMap weather API](https://openweathermap.org/api).
## Supported Things
There are three 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.
### Current UV Index And Forceast
The third thing `uvindex` supports the [current UV Index](https://openweathermap.org/api/uvi#current) and [forecasted UV Index](https://openweathermap.org/api/uvi#forecast) for a specific location.
It requires coordinates of the location of your interest.
You can add as much `uvindex` things for different locations to your setup as you like to observe.
## Discovery
If a system location is set, a "Local Weather And Forecast" (`weather-and-forecast`) thing and "Local UV Index" (`uvindex`) thing will be automatically discovered for this location.
Once the system location will be changed, the background discovery updates the configuration of both things 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`, `se`, `sk`, `sl`, `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.
### Current UV Index And Forecast
| Parameter | Description |
|----------------|--------------------------------------------------------------------------------------------------------------------------------|
| location | Location of weather in geographical coordinates (latitude/longitude/altitude). **Mandatory** |
| forecastDays | Number of days for UV Index forecast (including todays forecast). Optional, the default value is 6 (min="1", max="8", step="1"). |
Once the parameter `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 | icon-id | String | Id of the icon representing the current weather condition. **Advanced** |
| current | temperature | Number:Temperature | Current temperature. |
| current | apparent-temperature | Number:Temperature | Current apparent 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 of the last hour. |
| current | snow | Number:Length | Snow volume of the last hour. |
| current | visibility | Number:Length | Current visibility. |
**Attention**: Rain item is showing "1h" in the case when data are received from weather stations directly.
The fact is that some METAR stations do not have precipitation indicators or do not measure precipitation conditions due to some other technical reasons.
In this case, we use model data.
So, rain item is showing "3h" when the API response based on model data.
The "3h" value will be divided by three to always have an estimated value for one hour.
### 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 | icon-id | String | Id of the icon representing the forecasted weather condition. **Advanced** |
| forecastHours03, forecastHours06, ... forecastHours120 | temperature | Number:Temperature | Forecasted temperature. |
| forecastHours03, forecastHours06, ... forecastHours120 | apparent-temperature | Number:Temperature | Forecasted apparent temperature. |
| forecastHours03, forecastHours06, ... forecastHours120 | min-temperature | Number:Temperature | Minimum forecasted temperature. |
| forecastHours03, forecastHours06, ... forecastHours120 | max-temperature | Number:Temperature | Maximum 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. |
| forecastHours03, forecastHours06, ... forecastHours120 | snow | Number:Length | Expected snow volume. |
### Daily Forecast
| Channel Group ID | Channel ID | Item Type | Description |
|------------------------------------------------------------------|----------------------|----------------------|----------------------------------------------------------------------------|
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | time-stamp | DateTime | Date 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 | icon-id | String | Id of the icon representing the forecasted weather condition. **Advanced** |
| forecastToday, forecastTomorrow, forecastDay2, ... forecastDay16 | apparent-temperature | Number:Temperature | Forecasted apparent temperature. |
| 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. |
### UV Index
| Channel Group ID | Channel ID | Item Type | Description |
|-----------------------------------------------------------|------------|-----------|--------------------------------------|
| current, forecastTomorrow, forecastDay2, ... forecastDay7 | time-stamp | DateTime | Date of data observation / forecast. |
| current, forecastTomorrow, forecastDay2, ... forecastDay7 | uvindex | Number | Current or forecasted UV Index. |
## 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]
Thing uvindex local "Local UV Index" [location="XXX,YYY", forecastDays=7]
}
```
### 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]" <location> { 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]" <time> { channel="openweathermap:weather-and-forecast:api:local:current#time-stamp" }
String localCurrentCondition "Current condition [%s]" <sun_clouds> { 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%]" <temperature> { channel="openweathermap:weather-and-forecast:api:local:current#temperature" }
Number:Temperature localCurrentApparentTemperature "Current apparent temperature [%.1f %unit%]" <temperature> { channel="openweathermap:weather-and-forecast:api:local:current#apparent-temperature" }
Number:Pressure localCurrentPressure "Current barometric pressure [%.1f %unit%]" <pressure> { channel="openweathermap:weather-and-forecast:api:local:current#pressure" }
Number:Dimensionless localCurrentHumidity "Current atmospheric humidity [%d %unit%]" <humidity> { channel="openweathermap:weather-and-forecast:api:local:current#humidity" }
Number:Speed localCurrentWindSpeed "Current wind speed [%.1f km/h]" <wind> { channel="openweathermap:weather-and-forecast:api:local:current#wind-speed" }
Number:Angle localCurrentWindDirection "Current wind direction [%d %unit%]" <wind> { channel="openweathermap:weather-and-forecast:api:local:current#wind-direction" }
Number:Dimensionless localCurrentCloudiness "Current cloudiness [%d %unit%]" <clouds> { channel="openweathermap:weather-and-forecast:api:local:current#cloudiness" }
Number:Length localCurrentRainVolume "Current rain volume [%.1f %unit%]" <rain> { channel="openweathermap:weather-and-forecast:api:local:current#rain" }
Number:Length localCurrentSnowVolume "Current snow volume [%.1f %unit%]" <snow> { channel="openweathermap:weather-and-forecast:api:local:current#snow" }
Number:Length localCurrentVisibility "Current visibility [%.1f km]" <visibility> { channel="openweathermap:weather-and-forecast:api:local:current#visibility" }
DateTime localDailyForecastTodayTimestamp "Timestamp of forecast [%1$tY-%1$tm-%1$td]" <time> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#time-stamp" }
String localDailyForecastTodayCondition "Condition for today [%s]" <sun_clouds> { 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%]" <temperature> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#min-temperature" }
Number:Temperature localDailyForecastTodayMaxTemperature "Maximum temperature for today [%.1f %unit%]" <temperature> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#max-temperature" }
Number:Pressure localDailyForecastTodayPressure "Barometric pressure for today [%.1f %unit%]" <pressure> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#pressure" }
Number:Dimensionless localDailyForecastTodayHumidity "Atmospheric humidity for today [%d %unit%]" <humidity> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#humidity" }
Number:Speed localDailyForecastTodayWindSpeed "Wind speed for today [%.1f km/h]" <wind> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#wind-speed" }
Number:Angle localDailyForecastTodayWindDirection "Wind direction for today [%d %unit%]" <wind> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#wind-direction" }
Number:Dimensionless localDailyForecastTodayCloudiness "Cloudiness for today [%d %unit%]" <clouds> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#cloudiness" }
Number:Length localDailyForecastTodayRainVolume "Rain volume for today [%.1f %unit%]" <rain> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#rain" }
Number:Length localDailyForecastTodaySnowVolume "Snow volume for today [%.1f %unit%]" <snow> { channel="openweathermap:weather-and-forecast:api:local:forecastToday#snow" }
DateTime localDailyForecastTomorrowTimestamp "Timestamp of forecast [%1$tY-%1$tm-%1$td]" <time> { channel="openweathermap:weather-and-forecast:api:local:forecastTomorrow#time-stamp" }
String localDailyForecastTomorrowCondition "Condition for tomorrow [%s]" <sun_clouds> { 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%]" <temperature> { channel="openweathermap:weather-and-forecast:api:local:forecastTomorrow#min-temperature" }
Number:Temperature localDailyForecastTomorrowMaxTemperature "Maximum temperature for tomorrow [%.1f %unit%]" <temperature> { channel="openweathermap:weather-and-forecast:api:local:forecastTomorrow#max-temperature" }
...
DateTime localDailyForecastDay2Timestamp "Timestamp of forecast [%1$tY-%1$tm-%1$td]" <time> { channel="openweathermap:weather-and-forecast:api:local:forecastDay2#time-stamp" }
String localDailyForecastDay2Condition "Condition in 2 days [%s]" <sun_clouds> { 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%]" <temperature> { channel="openweathermap:weather-and-forecast:api:local:forecastDay2#min-temperature" }
Number:Temperature localDailyForecastDay2MaxTemperature "Maximum temperature in 2 days [%.1f %unit%]" <temperature> { channel="openweathermap:weather-and-forecast:api:local:forecastDay2#max-temperature" }
...
String miamiCurrentCondition "Current condition in Miami [%s]" <sun_clouds> { 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%]" <temperature> { channel="openweathermap:weather-and-forecast:api:miami:current#temperature" }
...
String miamiHourlyForecast03Condition "Condition in Miami for the next three hours [%s]" <sun_clouds> { 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%]" <temperature> { channel="openweathermap:weather-and-forecast:api:miami:forecastHours03#temperature" }
...
String miamiHourlyForecast06Condition "Condition in Miami for hours 3 to 6 [%s]" <sun_clouds> { 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%]" <temperature> { channel="openweathermap:weather-and-forecast:api:miami:forecastHours06#temperature" }
...
DateTime localCurrentUVIndexTimestamp "Timestamp of last measurement [%1$tY-%1$tm-%1$td]" <time> { channel="openweathermap:uvindex:api:local:current#time-stamp" }
Number localCurrentUVIndex "Current UV Index [%d]" { channel="openweathermap:uvindex:api:local:current#uvindex" }
DateTime localForecastTomorrowUVIndexTimestamp "Timestamp of forecast [%1$tY-%1$tm-%1$td]" <time> { channel="openweathermap:uvindex:api:local:forecastTomorrow#time-stamp" }
Number localForecastTomorrowUVIndex "UV Index for tomorrow [%d]" { channel="openweathermap:uvindex:api:local:forecastTomorrow#uvindex" }
...
```
### 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=localCurrentApparentTemperature
Text item=localCurrentPressure
Text item=localCurrentHumidity
Text item=localCurrentWindSpeed
Text item=localCurrentWindDirection
Text item=localCurrentCloudiness
Text item=localCurrentRainVolume
Text item=localCurrentSnowVolume
Text item=localCurrentVisibility
}
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
...
}
Frame label="UV Index" {
Text item=localCurrentUVIndexTimestamp
Text item=localCurrentUVIndex
Text item=localForecastTomorrowUVIndexTimestamp
Text item=localForecastTomorrowUVIndex
...
}
}
```

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.openweathermap</artifactId>
<name>openHAB Add-ons :: Bundles :: OpenWeatherMap Binding</name>
</project>

View File

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

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelGroupTypeUID;
/**
* The {@link OpenWeatherMapBindingConstants} class defines common constants, which are used across the whole binding.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapBindingConstants {
public static final String BINDING_ID = "openweathermap";
public static final String API = "api";
public static final String LOCAL = "local";
// Bridge
public static final ThingTypeUID THING_TYPE_WEATHER_API = new ThingTypeUID(BINDING_ID, "weather-api");
// Thing
public static final ThingTypeUID THING_TYPE_WEATHER_AND_FORECAST = new ThingTypeUID(BINDING_ID,
"weather-and-forecast");
public static final ThingTypeUID THING_TYPE_UVINDEX = new ThingTypeUID(BINDING_ID, "uvindex");
// List of all properties
public static final String CONFIG_API_KEY = "apikey";
public static final String CONFIG_LANGUAGE = "language";
public static final String CONFIG_LOCATION = "location";
// Channel group types
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_STATION = new ChannelGroupTypeUID(BINDING_ID, "station");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_HOURLY_FORECAST = new ChannelGroupTypeUID(BINDING_ID,
"hourlyForecast");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_DAILY_FORECAST = new ChannelGroupTypeUID(BINDING_ID,
"dailyForecast");
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_UVINDEX = new ChannelGroupTypeUID(BINDING_ID, "uvindex");
// List of all channel groups
public static final String CHANNEL_GROUP_STATION = "station";
public static final String CHANNEL_GROUP_CURRENT_WEATHER = "current";
public static final String CHANNEL_GROUP_FORECAST_TODAY = "forecastToday";
public static final String CHANNEL_GROUP_FORECAST_TOMORROW = "forecastTomorrow";
public static final String CHANNEL_GROUP_CURRENT_UVINDEX = "current";
// List of all channels
public static final String CHANNEL_STATION_ID = "id";
public static final String CHANNEL_STATION_NAME = "name";
public static final String CHANNEL_STATION_LOCATION = "location";
public static final String CHANNEL_TIME_STAMP = "time-stamp";
public static final String CHANNEL_CONDITION = "condition";
public static final String CHANNEL_CONDITION_ID = "condition-id";
public static final String CHANNEL_CONDITION_ICON = "icon";
public static final String CHANNEL_CONDITION_ICON_ID = "icon-id";
public static final String CHANNEL_TEMPERATURE = "temperature";
public static final String CHANNEL_APPARENT_TEMPERATURE = "apparent-temperature";
public static final String CHANNEL_MIN_TEMPERATURE = "min-temperature";
public static final String CHANNEL_MAX_TEMPERATURE = "max-temperature";
public static final String CHANNEL_PRESSURE = "pressure";
public static final String CHANNEL_HUMIDITY = "humidity";
public static final String CHANNEL_WIND_SPEED = "wind-speed";
public static final String CHANNEL_WIND_DIRECTION = "wind-direction";
public static final String CHANNEL_GUST_SPEED = "gust-speed";
public static final String CHANNEL_CLOUDINESS = "cloudiness";
public static final String CHANNEL_RAIN = "rain";
public static final String CHANNEL_SNOW = "snow";
public static final String CHANNEL_VISIBILITY = "visibility";
public static final String CHANNEL_UVINDEX = "uvindex";
// List of all configuration
public static final String CONFIG_FORECAST_DAYS = "forecastDays";
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.config;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
/**
* The {@link OpenWeatherMapAPIConfiguration} is the class used to match the {@link OpenWeatherMapAPIHandler}s
* configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapAPIConfiguration {
// supported languages (see https://openweathermap.org/current#multi)
public static final Set<String> SUPPORTED_LANGUAGES = Collections.unmodifiableSet(Stream.of("ar", "bg", "ca", "cz",
"de", "el", "en", "es", "fa", "fi", "fr", "gl", "hr", "hu", "it", "ja", "kr", "la", "lt", "mk", "nl", "pl",
"pt", "ro", "ru", "se", "sk", "sl", "tr", "ua", "vi", "zh_cn", "zh_tw").collect(Collectors.toSet()));
public @Nullable String apikey;
public int refreshInterval;
public @Nullable String language;
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler;
/**
* The {@link OpenWeatherMapLocationConfiguration} is the class used to match the {@link AbstractOpenWeatherMapHandler}s
* configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapLocationConfiguration {
public @NonNullByDefault({}) String location;
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapUVIndexHandler;
/**
* The {@link OpenWeatherMapUVIndexConfiguration} is the class used to match the
* {@link OpenWeatherMapUVIndexHandler}s configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapUVIndexConfiguration extends OpenWeatherMapLocationConfiguration {
public int forecastDays;
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapWeatherAndForecastHandler;
/**
* The {@link OpenWeatherMapWeatherAndForecastConfiguration} is the class used to match the
* {@link OpenWeatherMapWeatherAndForecastHandler}s configuration.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapWeatherAndForecastConfiguration extends OpenWeatherMapLocationConfiguration {
public int forecastHours;
public int forecastDays;
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.connection;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link OpenWeatherMapCommunicationException} is a communication exception for the connections to OpenWeatherMap
* API.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapCommunicationException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* Constructs a new exception with null as its detail message.
*/
public OpenWeatherMapCommunicationException() {
super();
}
/**
* Constructs a new exception with the specified detail message.
*
* @param message Detail message
*/
public OpenWeatherMapCommunicationException(String message) {
super(message);
}
/**
* Constructs a new exception with the specified cause.
*
* @param cause The cause
*/
public OpenWeatherMapCommunicationException(Throwable cause) {
super(cause);
}
/**
* Constructs a new exception with the specified detail message and cause.
*
* @param message Detail message
* @param cause The cause
*/
public OpenWeatherMapCommunicationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.connection;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link OpenWeatherMapConfigurationException} is a configuration exception for the connections to OpenWeatherMap
* API.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class OpenWeatherMapConfigurationException extends IllegalArgumentException {
private static final long serialVersionUID = 1L;
/**
* Constructs a new exception with null as its detail message.
*/
public OpenWeatherMapConfigurationException() {
super();
}
/**
* Constructs a new exception with the specified detail message.
*
* @param message Detail message
*/
public OpenWeatherMapConfigurationException(String message) {
super(message);
}
/**
* Constructs a new exception with the specified cause.
*
* @param cause The cause
*/
public OpenWeatherMapConfigurationException(Throwable cause) {
super(cause);
}
/**
* Constructs a new exception with the specified detail message and cause.
*
* @param message Detail message
* @param cause The cause
*/
public OpenWeatherMapConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto;
import org.openhab.binding.openweathermap.internal.dto.base.City;
import org.openhab.binding.openweathermap.internal.dto.forecast.daily.List;
/**
* The {@link OpenWeatherMapJsonDailyForecastData} is the Java class used to map the JSON response to an OpenWeatherMap
* request.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class OpenWeatherMapJsonDailyForecastData {
private City city;
private String cod;
private Double message;
private Integer cnt;
private java.util.List<List> list;
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public String getCod() {
return cod;
}
public void setCod(String cod) {
this.cod = cod;
}
public Double getMessage() {
return message;
}
public void setMessage(Double message) {
this.message = message;
}
public Integer getCnt() {
return cnt;
}
public void setCnt(Integer cnt) {
this.cnt = cnt;
}
public java.util.List<List> getList() {
return list;
}
public void setList(java.util.List<List> list) {
this.list = list;
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto;
import org.openhab.binding.openweathermap.internal.dto.base.City;
import org.openhab.binding.openweathermap.internal.dto.forecast.hourly.List;
/**
* The {@link OpenWeatherMapJsonHourlyForecastData} is the Java class used to map the JSON response to an OpenWeatherMap
* request.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class OpenWeatherMapJsonHourlyForecastData {
private String cod;
private Double message;
private Integer cnt;
private java.util.List<List> list;
private City city;
public String getCod() {
return cod;
}
public void setCod(String cod) {
this.cod = cod;
}
public Double getMessage() {
return message;
}
public void setMessage(Double message) {
this.message = message;
}
public Integer getCnt() {
return cnt;
}
public void setCnt(Integer cnt) {
this.cnt = cnt;
}
public java.util.List<List> getList() {
return list;
}
public void setList(java.util.List<List> list) {
this.list = list;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link OpenWeatherMapJsonUVIndexData} is the Java class used to map the JSON response to an OpenWeatherMap
* request.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class OpenWeatherMapJsonUVIndexData {
private Double lat;
private Double lon;
@SerializedName("date_iso")
private String dateIso;
private Integer date;
private Double value;
public Double getLat() {
return lat;
}
public void setLat(Double lat) {
this.lat = lat;
}
public Double getLon() {
return lon;
}
public void setLon(Double lon) {
this.lon = lon;
}
public String getDateIso() {
return dateIso;
}
public void setDateIso(String dateIso) {
this.dateIso = dateIso;
}
public Integer getDate() {
return date;
}
public void setDate(Integer date) {
this.date = date;
}
public Double getValue() {
return value;
}
public void setValue(Double value) {
this.value = value;
}
}

View File

@@ -0,0 +1,160 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto;
import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.dto.base.Clouds;
import org.openhab.binding.openweathermap.internal.dto.base.Coord;
import org.openhab.binding.openweathermap.internal.dto.base.Rain;
import org.openhab.binding.openweathermap.internal.dto.base.Snow;
import org.openhab.binding.openweathermap.internal.dto.base.Weather;
import org.openhab.binding.openweathermap.internal.dto.base.Wind;
import org.openhab.binding.openweathermap.internal.dto.weather.Main;
import org.openhab.binding.openweathermap.internal.dto.weather.Sys;
/**
* The {@link OpenWeatherMapJsonWeatherData} is the Java class used to map the JSON response to an OpenWeatherMap
* request.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class OpenWeatherMapJsonWeatherData {
private Coord coord;
private List<Weather> weather;
private String base;
private Main main;
private @Nullable Integer visibility;
private Wind wind;
private Clouds clouds;
private @Nullable Rain rain;
private @Nullable Snow snow;
private Integer dt;
private Sys sys;
private Integer id;
private String name;
private Integer cod;
public Coord getCoord() {
return coord;
}
public void setCoord(Coord coord) {
this.coord = coord;
}
public List<Weather> getWeather() {
return weather;
}
public void setWeather(List<Weather> weather) {
this.weather = weather;
}
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
public Main getMain() {
return main;
}
public void setMain(Main main) {
this.main = main;
}
public @Nullable Integer getVisibility() {
return visibility;
}
public void setVisibility(Integer visibility) {
this.visibility = visibility;
}
public Wind getWind() {
return wind;
}
public void setWind(Wind wind) {
this.wind = wind;
}
public Clouds getClouds() {
return clouds;
}
public void setClouds(Clouds clouds) {
this.clouds = clouds;
}
public @Nullable Rain getRain() {
return rain;
}
public void setRain(Rain rain) {
this.rain = rain;
}
public @Nullable Snow getSnow() {
return snow;
}
public void setSnow(Snow snow) {
this.snow = snow;
}
public Integer getDt() {
return dt;
}
public void setDt(Integer dt) {
this.dt = dt;
}
public Sys getSys() {
return sys;
}
public void setSys(Sys sys) {
this.sys = sys;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getCod() {
return cod;
}
public void setCod(Integer cod) {
this.cod = cod;
}
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.base;
/**
* Generated Plain Old Java Objects class for {@link City} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class City {
private Integer id;
private Coord coord;
private String country;
private Integer population;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Coord getCoord() {
return coord;
}
public void setCoord(Coord coord) {
this.coord = coord;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Integer getPopulation() {
return population;
}
public void setPopulation(Integer population) {
this.population = population;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.base;
/**
* Generated Plain Old Java Objects class for {@link Clouds} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Clouds {
private Integer all;
public Integer getAll() {
return all;
}
public void setAll(Integer all) {
this.all = all;
}
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.base;
/**
* Generated Plain Old Java Objects class for {@link Coord} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Coord {
private Double lon;
private Double lat;
public Double getLon() {
return lon;
}
public void setLon(Double lon) {
this.lon = lon;
}
public Double getLat() {
return lat;
}
public void setLat(Double lat) {
this.lat = lat;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.base;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link Rain} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Rain {
@SerializedName("1h")
private @Nullable Double oneHour;
@SerializedName("3h")
private @Nullable Double threeHours;
public @Nullable Double get1h() {
return oneHour;
}
public void set1h(Double oneHour) {
this.oneHour = oneHour;
}
public @Nullable Double get3h() {
return threeHours;
}
public void set3h(Double threeHours) {
this.threeHours = threeHours;
}
public Double getVolume() {
return oneHour != null ? oneHour : threeHours != null ? threeHours / 3 : 0;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.base;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link Snow} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Snow {
@SerializedName("1h")
private @Nullable Double oneHour;
@SerializedName("3h")
private @Nullable Double threeHours;
public @Nullable Double get1h() {
return oneHour;
}
public void set1h(Double oneHour) {
this.oneHour = oneHour;
}
public @Nullable Double get3h() {
return threeHours;
}
public void set3h(Double threeHours) {
this.threeHours = threeHours;
}
public Double getVolume() {
return oneHour != null ? oneHour : threeHours != null ? threeHours / 3 : 0;
}
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.base;
/**
* Generates Plain Old Java Objects class for {@link Weather} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Weather {
private Integer id;
private String main;
private String description;
private String icon;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getMain() {
return main;
}
public void setMain(String main) {
this.main = main;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.base;
import org.eclipse.jdt.annotation.Nullable;
/**
* Generated Plain Old Java Objects class for {@link Wind} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Wind {
private Double speed;
private Double deg;
private @Nullable Double gust;
public Double getSpeed() {
return speed;
}
public void setSpeed(Double speed) {
this.speed = speed;
}
public Double getDeg() {
return deg;
}
public void setDeg(Double deg) {
this.deg = deg;
}
public @Nullable Double getGust() {
return gust;
}
public void setGust(Double gust) {
this.gust = speed;
}
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.forecast.daily;
/**
* Generated Plain Old Java Objects class for {@link FeelsLikeTemp} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class FeelsLikeTemp {
private Double day;
private Double night;
private Double eve;
private Double morn;
public Double getDay() {
return day;
}
public void setDay(Double day) {
this.day = day;
}
public Double getNight() {
return night;
}
public void setNight(Double night) {
this.night = night;
}
public Double getEve() {
return eve;
}
public void setEve(Double eve) {
this.eve = eve;
}
public Double getMorn() {
return morn;
}
public void setMorn(Double morn) {
this.morn = morn;
}
}

View File

@@ -0,0 +1,135 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.forecast.daily;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.dto.base.Weather;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link List} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class List {
private Integer dt;
private Temp temp;
@SerializedName("feels_like")
private @Nullable FeelsLikeTemp feelsLikeTemp;
private Double pressure;
private Integer humidity;
private java.util.List<Weather> weather;
private Double speed;
private Double deg;
private @Nullable Double gust;
private Integer clouds;
private @Nullable Double rain;
private @Nullable Double snow;
public Integer getDt() {
return dt;
}
public void setDt(Integer dt) {
this.dt = dt;
}
public Temp getTemp() {
return temp;
}
public void setTemp(Temp temp) {
this.temp = temp;
}
public @Nullable FeelsLikeTemp getFeelsLikeTemp() {
return feelsLikeTemp;
}
public void setFeelsLikeTemp(FeelsLikeTemp feelsLikeTemp) {
this.feelsLikeTemp = feelsLikeTemp;
}
public Double getPressure() {
return pressure;
}
public void setPressure(Double pressure) {
this.pressure = pressure;
}
public Integer getHumidity() {
return humidity;
}
public void setHumidity(Integer humidity) {
this.humidity = humidity;
}
public java.util.List<Weather> getWeather() {
return weather;
}
public void setWeather(java.util.List<Weather> weather) {
this.weather = weather;
}
public Double getSpeed() {
return speed;
}
public void setSpeed(Double speed) {
this.speed = speed;
}
public Double getDeg() {
return deg;
}
public void setDeg(Double deg) {
this.deg = deg;
}
public @Nullable Double getGust() {
return gust;
}
public void setGust(Double gust) {
this.gust = speed;
}
public Integer getClouds() {
return clouds;
}
public void setClouds(Integer clouds) {
this.clouds = clouds;
}
public @Nullable Double getRain() {
return rain;
}
public void setRain(Double rain) {
this.rain = rain;
}
public @Nullable Double getSnow() {
return snow;
}
public void setSnow(Double snow) {
this.snow = snow;
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.forecast.daily;
/**
* Generated Plain Old Java Objects class for {@link Temp} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Temp {
private Double day;
private Double min;
private Double max;
private Double night;
private Double eve;
private Double morn;
public Double getDay() {
return day;
}
public void setDay(Double day) {
this.day = day;
}
public Double getMin() {
return min;
}
public void setMin(Double min) {
this.min = min;
}
public Double getMax() {
return max;
}
public void setMax(Double max) {
this.max = max;
}
public Double getNight() {
return night;
}
public void setNight(Double night) {
this.night = night;
}
public Double getEve() {
return eve;
}
public void setEve(Double eve) {
this.eve = eve;
}
public Double getMorn() {
return morn;
}
public void setMorn(Double morn) {
this.morn = morn;
}
}

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.forecast.hourly;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.openweathermap.internal.dto.base.Clouds;
import org.openhab.binding.openweathermap.internal.dto.base.Rain;
import org.openhab.binding.openweathermap.internal.dto.base.Snow;
import org.openhab.binding.openweathermap.internal.dto.base.Weather;
import org.openhab.binding.openweathermap.internal.dto.base.Wind;
import org.openhab.binding.openweathermap.internal.dto.weather.Main;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link List} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class List {
private Integer dt;
private Main main;
private java.util.List<Weather> weather;
private Clouds clouds;
private Wind wind;
private @Nullable Rain rain;
private @Nullable Snow snow;
private Sys sys;
@SerializedName("dt_txt")
private String dtTxt;
public Integer getDt() {
return dt;
}
public void setDt(Integer dt) {
this.dt = dt;
}
public Main getMain() {
return main;
}
public void setMain(Main main) {
this.main = main;
}
public java.util.List<Weather> getWeather() {
return weather;
}
public void setWeather(java.util.List<Weather> weather) {
this.weather = weather;
}
public Clouds getClouds() {
return clouds;
}
public void setClouds(Clouds clouds) {
this.clouds = clouds;
}
public Wind getWind() {
return wind;
}
public void setWind(Wind wind) {
this.wind = wind;
}
public @Nullable Rain getRain() {
return rain;
}
public void setRain(Rain rain) {
this.rain = rain;
}
public @Nullable Snow getSnow() {
return snow;
}
public void setSnow(Snow snow) {
this.snow = snow;
}
public Sys getSys() {
return sys;
}
public void setSys(Sys sys) {
this.sys = sys;
}
public String getDtTxt() {
return dtTxt;
}
public void setDtTxt(String dtTxt) {
this.dtTxt = dtTxt;
}
}

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.forecast.hourly;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link Main} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Main {
private Double temp;
@SerializedName("feels_like")
private @Nullable Double feelsLikeTemp;
@SerializedName("temp_min")
private Double tempMin;
@SerializedName("temp_max")
private Double tempMax;
private Double pressure;
@SerializedName("sea_level")
private Double seaLevel;
@SerializedName("grnd_level")
private Double grndLevel;
private Integer humidity;
@SerializedName("temp_kf")
private Integer tempKf;
public Double getTemp() {
return temp;
}
public void setTemp(Double temp) {
this.temp = temp;
}
public @Nullable Double getFeelsLikeTemp() {
return feelsLikeTemp;
}
public void setFeelsLikeTemp(Double feelsLikeTemp) {
this.feelsLikeTemp = feelsLikeTemp;
}
public Double getTempMin() {
return tempMin;
}
public void setTempMin(Double tempMin) {
this.tempMin = tempMin;
}
public Double getTempMax() {
return tempMax;
}
public void setTempMax(Double tempMax) {
this.tempMax = tempMax;
}
public Double getPressure() {
return pressure;
}
public void setPressure(Double pressure) {
this.pressure = pressure;
}
public Double getSeaLevel() {
return seaLevel;
}
public void setSeaLevel(Double seaLevel) {
this.seaLevel = seaLevel;
}
public Double getGrndLevel() {
return grndLevel;
}
public void setGrndLevel(Double grndLevel) {
this.grndLevel = grndLevel;
}
public Integer getHumidity() {
return humidity;
}
public void setHumidity(Integer humidity) {
this.humidity = humidity;
}
public Integer getTempKf() {
return tempKf;
}
public void setTempKf(Integer tempKf) {
this.tempKf = tempKf;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.forecast.hourly;
/**
* Generated Plain Old Java Objects class for {@link Sys} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Sys {
private String pod;
public String getPod() {
return pod;
}
public void setPod(String pod) {
this.pod = pod;
}
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.weather;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* Generated Plain Old Java Objects class for {@link Main} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Main {
private Double temp;
@SerializedName("feels_like")
private @Nullable Double feelsLikeTemp;
private Double pressure;
private Integer humidity;
@SerializedName("temp_min")
private Double tempMin;
@SerializedName("temp_max")
private Double tempMax;
public Double getTemp() {
return temp;
}
public void setTemp(Double temp) {
this.temp = temp;
}
public @Nullable Double getFeelsLikeTemp() {
return feelsLikeTemp;
}
public void setFeelsLikeTemp(Double feelsLikeTemp) {
this.feelsLikeTemp = feelsLikeTemp;
}
public Double getPressure() {
return pressure;
}
public void setPressure(Double pressure) {
this.pressure = pressure;
}
public Integer getHumidity() {
return humidity;
}
public void setHumidity(Integer humidity) {
this.humidity = humidity;
}
public Double getTempMin() {
return tempMin;
}
public void setTempMin(Double tempMin) {
this.tempMin = tempMin;
}
public Double getTempMax() {
return tempMax;
}
public void setTempMax(Double tempMax) {
this.tempMax = tempMax;
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.dto.weather;
/**
* Generated Plain Old Java Objects class for {@link Sys} from JSON.
*
* @author Christoph Weitkamp - Initial contribution
*/
public class Sys {
private Integer type;
private Integer id;
private Double message;
private String country;
private Integer sunrise;
private Integer sunset;
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getMessage() {
return message;
}
public void setMessage(Double message) {
this.message = message;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Integer getSunrise() {
return sunrise;
}
public void setSunrise(Integer sunrise) {
this.sunrise = sunrise;
}
public Integer getSunset() {
return sunset;
}
public void setSunset(Integer sunset) {
this.sunset = sunset;
}
}

View File

@@ -0,0 +1,123 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.openweathermap.internal.factory;
import static org.openhab.binding.openweathermap.internal.OpenWeatherMapBindingConstants.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.openweathermap.internal.discovery.OpenWeatherMapDiscoveryService;
import org.openhab.binding.openweathermap.internal.handler.AbstractOpenWeatherMapHandler;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapUVIndexHandler;
import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapWeatherAndForecastHandler;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.LocationProvider;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link OpenWeatherMapHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.openweathermap")
public class OpenWeatherMapHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.concat(OpenWeatherMapAPIHandler.SUPPORTED_THING_TYPES.stream(),
AbstractOpenWeatherMapHandler.SUPPORTED_THING_TYPES.stream()).collect(Collectors.toSet()));
private final Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
private final HttpClient httpClient;
private final LocaleProvider localeProvider;
private final LocationProvider locationProvider;
private final TranslationProvider i18nProvider;
private final TimeZoneProvider timeZoneProvider;
@Activate
public OpenWeatherMapHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
final @Reference LocaleProvider localeProvider, final @Reference LocationProvider locationProvider,
final @Reference TranslationProvider i18nProvider, final @Reference TimeZoneProvider timeZoneProvider) {
this.httpClient = httpClientFactory.getCommonHttpClient();
this.localeProvider = localeProvider;
this.locationProvider = locationProvider;
this.i18nProvider = i18nProvider;
this.timeZoneProvider = timeZoneProvider;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_WEATHER_API.equals(thingTypeUID)) {
OpenWeatherMapAPIHandler handler = new OpenWeatherMapAPIHandler((Bridge) thing, httpClient, localeProvider);
// register discovery service
OpenWeatherMapDiscoveryService discoveryService = new OpenWeatherMapDiscoveryService(handler,
locationProvider, localeProvider, i18nProvider);
discoveryServiceRegs.put(handler.getThing().getUID(), bundleContext
.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
return handler;
} else if (THING_TYPE_WEATHER_AND_FORECAST.equals(thingTypeUID)) {
return new OpenWeatherMapWeatherAndForecastHandler(thing, timeZoneProvider);
} else if (THING_TYPE_UVINDEX.equals(thingTypeUID)) {
return new OpenWeatherMapUVIndexHandler(thing, timeZoneProvider);
}
return null;
}
@Override
protected synchronized void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof OpenWeatherMapAPIHandler) {
ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(thingHandler.getThing().getUID());
if (serviceReg != null) {
// remove discovery service, if bridge handler is removed
OpenWeatherMapDiscoveryService discoveryService = (OpenWeatherMapDiscoveryService) bundleContext
.getService(serviceReg.getReference());
serviceReg.unregister();
if (discoveryService != null) {
discoveryService.deactivate();
}
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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