added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
32
bundles/org.openhab.binding.weathercompany/.classpath
Normal 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>
|
||||
23
bundles/org.openhab.binding.weathercompany/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.weathercompany</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</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>
|
||||
20
bundles/org.openhab.binding.weathercompany/NOTICE
Normal file
@@ -0,0 +1,20 @@
|
||||
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
|
||||
|
||||
== Third-party Content
|
||||
|
||||
Weather Company Icons
|
||||
* License: WeatherUnderground Terms of Use
|
||||
* Project: https://www.wunderground.com/company/legal
|
||||
* Source: https://docs.google.com/document/d/1dNCf6nF6cjm4oOxQxjtqNuAvG_iEe5f9MQH1xlCeV4s
|
||||
352
bundles/org.openhab.binding.weathercompany/README.md
Normal file
@@ -0,0 +1,352 @@
|
||||
# The Weather Company Binding
|
||||
|
||||
Provides 5-day weather forecast and *Personal Weather Station* (PWS) current
|
||||
observations from **The Weather Company**.
|
||||
This service is available only for PWS users who upload their PWS
|
||||
weather data to WeatherUnderground.
|
||||
|
||||
## Supported Things
|
||||
|
||||
The following thing types are supported:
|
||||
|
||||
| Thing | ID | Description |
|
||||
|--------------|----------------------|--------------|
|
||||
| Account | account | Maintains API key for accessing Weather Company API |
|
||||
| Forecast | weather-forecast | Provides the Weather Company 5-day forecast |
|
||||
| Observations | weather-observations | Provides the Personal Weather Station current observations |
|
||||
|
||||
## Discovery
|
||||
|
||||
Once an Account thing is configured with a valid API key, the binding will auto-discover
|
||||
a *Local Weather Forecast* thing if the location (i.e. latitude and longitude)
|
||||
and locale are set in the openHAB configuration.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
### Account
|
||||
|
||||
The following configuration parameters are available on the Account thing:
|
||||
|
||||
| Parameter | Parameter ID | Required/Optional | Description |
|
||||
|------------------|-------------------|-------------------|-------------|
|
||||
| API Key | apiKey | Required | Get the API key from your Weather Underground PWS page. Old Weather Underground API keys will not work with this binding. |
|
||||
|
||||
### Weather Forecast
|
||||
|
||||
The following configuration parameters are available on the Weather Forecast thing:
|
||||
|
||||
| Parameter | Parameter ID | Required/Optional | Description |
|
||||
|------------------|-------------------|----------------------------------------|-------------|
|
||||
| Location Type | locationType | Required | The following location types are available: Postal Code (postalCode), Geocode (geocode), IATA Code (iataCode). |
|
||||
| Postal Code | postalCode | Required for Postal Code location type | Available for the following countries: US, UK, DE, FR, IT, CA. The format is a concatenation of the postal code and the 2-character country code (e.g. 10001:US, W6C:CA). |
|
||||
| Geocode | geocode | Required for Geocode location type | Specify latitude and longitude of the location for which the forecast is desired (e.g. 25.762272,-80.216425). |
|
||||
| IATA Code | iataCode | Required for IATA Code location type | Three character airport code (e.g. BWI, FCO). |
|
||||
| Language | language | Optional | Languages supported by The Weather Company API. If the language not specified in the thing configuration, the binding will try to select the language based on the locale set in openHAB. |
|
||||
| Refresh Interval | refreshInterval | Required | Frequency with which forecast will be updated. Defaults to 30 minutes. Minimum is 2 minutes. |
|
||||
|
||||
### Weather Observations
|
||||
|
||||
The following configuration parameters are available on the Weather Observations thing:
|
||||
|
||||
| Parameter | Parameter ID | Required/Optional | Description |
|
||||
|------------------|-------------------|-------------------|-------------|
|
||||
| Station ID | pwsStationId | Required | This is the Personal Weather Station (PWS) station ID on Weather Underground. |
|
||||
| Refresh Interval | refreshInterval | Required | Frequency with which PWS observations will be updated. Defaults to 30 minutes. Minimum is 2 minutes. |
|
||||
|
||||
## Channels
|
||||
|
||||
#### Channels for Personal Weather Station (PWS) Current Observations
|
||||
|
||||
| Channel ID | Item Type | Description |
|
||||
|---------------------------------|-------------------------|------------------------------------------|
|
||||
| observationTimeLocal | DateTime | Time when conditions were observed |
|
||||
| neighborhood | String | Neighborhood |
|
||||
| currentTemperature | Number:Temperature | Current temperature |
|
||||
| currentTemperatureDewPoint | Number:Temperature | Current dew point temperature |
|
||||
| currentTemperatureHeatIndex | Number:Temperature | Current heat index temperature |
|
||||
| currentTemperatureWindChill | Number:Temperature | Current wind chill temperature |
|
||||
| currentHumidity | Number:Dimensionless | Current relative humidity |
|
||||
| currentPressure | Number:Pressure | Current atmospheric pressure |
|
||||
| currentPrecipitationRate | Number:Speed | Current precipitation rate |
|
||||
| currentPrecipitationTotal | Number:Length | Current precipitation total |
|
||||
| currentSolarRadiation | Number:Intensity | Current solar radiation |
|
||||
| currentUv | Number | Current UV index |
|
||||
| currentWindSpeed | Number:Speed | Current wind speed |
|
||||
| currentWindSpeedGust | Number:Speed | Current wind speed gust |
|
||||
| currentWindDirection | Number:Angle | Current wind direction |
|
||||
| stationId | String | Station Id |
|
||||
| country | String | Country |
|
||||
| location | Location | Latitude & longitude of weather station |
|
||||
| elevation | Number:Length | Elevation of weather station |
|
||||
| qcStatus | Number | QC status |
|
||||
| softwareType | String | Software type |
|
||||
|
||||
#### Channels for Daily Forecast (Today, Tomorrow, Day 2, Day 3, Day 4, Day 5)
|
||||
|
||||
| Channel Group ID | Channel ID | Item Type | Description |
|
||||
|------------------------------|---------------------------------|-------------------------|----------------------------------------------------|
|
||||
| forecastDay(0-5) | dayOfWeek | String | Day of week (Sunday, Monday, etc.) |
|
||||
| forecastDay(0-5) | narrative | String | Narrative forecast for the 24-hour period |
|
||||
| forecastDay(0-5) | temperatureMax | Number:Temperature | Daily maximum temperature |
|
||||
| forecastDay(0-5) | temperatureMin | Number:Temperature | Daily minimum temperature |
|
||||
| forecastDay(0-5) | precipitationRain | Number:Length | The forecasted measurable liquid precipitation |
|
||||
| forecastDay(0-5) | precipitationSnow | Number:Length | The forecasted measurable precipitation as snow |
|
||||
| forecastDay(0-5) | validTimeLocal | DateTime | Time the forecast is valid in local apparent time |
|
||||
| forecastDay(0-5) | expirationTimeLocal | DateTime | Time the forecast expires |
|
||||
|
||||
#### Channels for Daypart Forecast (Today, Tonight, Tomorrow, Tomorrow Night, etc.)
|
||||
|
||||
| Channel Group ID | Channel ID | Item Type | Description |
|
||||
|------------------------------|---------------------------------|-------------------------|----------------------------------------------------------------|
|
||||
| forcastDay(0-5)(Day\|Night) | dayPartName | String | Name of 12 hour daypart (e.g. Today, Tonight) |
|
||||
| forcastDay(0-5)(Day\|Night) | dayOrNight | String | Day or night indicator (D or N) |
|
||||
| forcastDay(0-5)(Day\|Night) | narrative | String | The narrative forecast for the daypart period |
|
||||
| forcastDay(0-5)(Day\|Night) | wxPhraseShort | String | Sensible weather phrase |
|
||||
| forcastDay(0-5)(Day\|Night) | wxPhraseLong | String | Sensible weather phrase |
|
||||
| forcastDay(0-5)(Day\|Night) | temperature | Number:Temperature | Maximum temperature for daytime, minimum temperature nighttime |
|
||||
| forcastDay(0-5)(Day\|Night) | temperatureHeatIndex | Number:Temperature | Maximum heat index |
|
||||
| forcastDay(0-5)(Day\|Night) | temperatureWindChill | Number:Temperature | Minimum wind chill |
|
||||
| forcastDay(0-5)(Day\|Night) | relativeHumidity | Number:Dimensionless | The relative humidity of the air |
|
||||
| forcastDay(0-5)(Day\|Night) | cloudCover | String | Daytime average cloud cover expressed as a percentage |
|
||||
| forcastDay(0-5)(Day\|Night) | windSpeed | Number:Speed | The maximum forecasted wind speed |
|
||||
| forcastDay(0-5)(Day\|Night) | windDirection | Number:Angle | Average wind direction in degrees magnetic notation |
|
||||
| forcastDay(0-5)(Day\|Night) | windDirectionCardinal | String | Average wind direction in cardinal notation |
|
||||
| forcastDay(0-5)(Day\|Night) | windPhrase | String | A phrase that describes the wind direction and speed |
|
||||
| forcastDay(0-5)(Day\|Night) | precipitationChance | Number:Dimensionless | Maximum probability of precipitation |
|
||||
| forcastDay(0-5)(Day\|Night) | precipitationType | String | Type of precipitation to display (e.g. rain, snow) |
|
||||
| forcastDay(0-5)(Day\|Night) | precipitationRain | Number:Length | The forecasted measurable liquid precipitation |
|
||||
| forcastDay(0-5)(Day\|Night) | precipitationSnow | Number:Length | The forecasted measurable precipitation as snow |
|
||||
| forcastDay(0-5)(Day\|Night) | snowRange | String | Snow accumulation amount for the forecast period |
|
||||
| forcastDay(0-5)(Day\|Night) | thunderCategory | String | The description of probability of thunderstorm activity |
|
||||
| forcastDay(0-5)(Day\|Night) | thunderIndex | Number | An enumeration of thunderstorm probability |
|
||||
| forcastDay(0-5)(Day\|Night) | uvDescription | String | Level of risk of skin damage due to exposure |
|
||||
| forcastDay(0-5)(Day\|Night) | uvIndex | Number | Maximum UV index for the forecast period |
|
||||
| forcastDay(0-5)(Day\|Night) | iconCode | Number | Key to the weather icon lookup |
|
||||
| forcastDay(0-5)(Day\|Night) | iconCodeExtend | String | Code representing full set sensible weather |
|
||||
| forcastDay(0-5)(Day\|Night) | iconImage | Image | Image representing forecast condition |
|
||||
| forcastDay(0-5)(Day\|Night) | qualifierPhrase | String | Describes special weather criteria |
|
||||
| forcastDay(0-5)(Day\|Night) | qualifierCode | String | Code for special weather criteria |
|
||||
|
||||
### Local Language Support
|
||||
|
||||
The following channels will be translated to local language based on the language setting in the thing configuration.
|
||||
|
||||
- dayOfWeek
|
||||
- daypartName
|
||||
- narrative
|
||||
- qualifierPhrase
|
||||
- uvDescription
|
||||
- windDirectionCardinal
|
||||
- windPhrase
|
||||
- wxPhraseLong
|
||||
|
||||
|
||||
## Full Example
|
||||
|
||||
### Thing Example
|
||||
|
||||
```
|
||||
Bridge weathercompany:account:myaccount [ apiKey="0123456789" ] {
|
||||
Thing weather-forecast myweather "My Forecast" @ "Home" [locationType="postalCode",postalCode="10001:US",language="en-US",refreshInterval=30]
|
||||
Thing weather-observations myobservations "My Observations" @ "Home" [pwsStationId="KFLMIAMI208",refreshInterval=30]
|
||||
Thing weather-forecast chitown "Chicago Forecast" @ "Ohare Airport" [locationType="iataCode",iataCode="ORD",language="en-US",refreshInterval=30]
|
||||
Thing weather-forecast miami "Miami Weather" @ "South Beach" [locationType="postalCode",postalCode="33139:US",language="es-US",refreshInterval=30]
|
||||
Thing weather-observations patagonia "Torres del Paine Weather" @ "Patagonia" [pwsStationId="IPUNTAAR4",refreshInterval=30]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Items Example
|
||||
|
||||
```
|
||||
// PWS Current Observations
|
||||
Number:Temperature WC_PWS_Temperature "Temperature [%.1f %unit%]" <temperature> { channel="weathercompany:weather-observations:myaccount:myobservations:currentTemperature" }
|
||||
Number:Temperature WC_PWS_TemperatureDewPoint "Dew Point Temperature [%.1f %unit%]" <temperature> { channel="weathercompany:weather-observations:myaccount:myobservations:currentTemperatureDewPoint" }
|
||||
Number:Temperature WC_PWS_TemperatureHeatIndex "Heat Index Temperature [%.1f %unit%]" <temperature> { channel="weathercompany:weather-observations:myaccount:myobservations:currentTemperatureHeatIndex" }
|
||||
Number:Temperature WC_PWS_TemperatureWindChill "Wind Chill Temperature [%.1f %unit%]" <temperature> { channel="weathercompany:weather-observations:myaccount:myobservations:currentTemperatureWindChill" }
|
||||
Number:Dimensionless WC_PWS_RelativeHumidity "Relative Humidity [%.1f %unit%]" <humidity> { channel="weathercompany:weather-observations:myaccount:myobservations:currentHumidity" }
|
||||
Number:Pressure WC_PWS_Pressure "Pressure [%.2f %unit%]" <pressure> { channel="weathercompany:weather-observations:myaccount:myobservations:currentPressure" }
|
||||
// Use this for SI units
|
||||
//Number:Speed WC_PWS_PrecipitationRate "Precipitation Rate [%.1f mm/h]" <rain> { channel="weathercompany:weather-observations:myaccount:myobservations:currentPrecipitationRate" }
|
||||
// Use this for Imperial units
|
||||
Number:Speed WC_PWS_PrecipitationRate "Precipitation Rate [%.2f in/h]" <rain> { channel="weathercompany:weather-observations:myaccount:myobservations:currentPrecipitationRate" }
|
||||
Number:Length WC_PWS_PrecipitationTotal "Precipitation Total [%.1f %unit%]" <rain> { channel="weathercompany:weather-observations:myaccount:myobservations:currentPrecipitationTotal" }
|
||||
Number:Intensity WC_PWS_SolarRadiation "Solar Radiation [%.1f %unit%]" <sun> { channel="weathercompany:weather-observations:myaccount:myobservations:currentSolarRadiation" }
|
||||
Number WC_PWS_UV "UV Index [%.0f]" <sun> { channel="weathercompany:weather-observations:myaccount:myobservations:currentUv" }
|
||||
Number:Angle WC_PWS_WindDirection "Wind Direction [%.0f %unit%]" <wind> { channel="weathercompany:weather-observations:myaccount:myobservations:currentWindDirection" }
|
||||
Number:Speed WC_PWS_WindSpeed "Wind Speed [%.0f %unit%]" <wind> { channel="weathercompany:weather-observations:myaccount:myobservations:currentWindSpeed" }
|
||||
Number:Speed WC_PWS_WindSpeedGust "Wind Speed Gust [%.0f %unit%]" <wind> { channel="weathercompany:weather-observations:myaccount:myobservations:currentWindSpeedGust" }
|
||||
String WC_PWS_Country "Country [%s]" <none> { channel="weathercompany:weather-observations:myaccount:myobservations:country" }
|
||||
Location WC_PWS_Location "Lat/Lon [%s]" <none> { channel="weathercompany:weather-observations:myaccount:myobservations:location" }
|
||||
Number:Length WC_PWS_Elevation "Elevation [%.0f %unit%]" <none> { channel="weathercompany:weather-observations:myaccount:myobservations:elevation" }
|
||||
String WC_PWS_Neighborhood "Neighborhood [%s]" <none> { channel="weathercompany:weather-observations:myaccount:myobservations:neighborhood" }
|
||||
DateTime WC_PWS_ObservationTimeLocal "Observation Time [%1$tA, %1$tm/%1$td/%1$tY %1$tl:%1$tM %1$tp]" <time> { channel="weathercompany:weather-observations:myaccount:myobservations:observationTimeLocal" }
|
||||
Number WC_PWS_QcStatus "QC Status [%.0f %unit%]" <none> { channel="weathercompany:weather-observations:myaccount:myobservations:qcStatus" }
|
||||
String WC_PWS_SoftwareType "Software Type [%s]" <none> { channel="weathercompany:weather-observations:myaccount:myobservations:softwareType" }
|
||||
String WC_PWS_StationId "Station Id [%s]" <none> { channel="weathercompany:weather-observations:myaccount:myobservations:stationId" }
|
||||
|
||||
// Day 0 - Today
|
||||
String WC_Day0_DayOfWeek "Day of Week [%s]" <time> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0#dayOfWeek" }
|
||||
DateTime WC_Day0_ValidTimeLocal "Valid At [%1$tA, %1$tm/%1$td/%1$tY]" <time> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0#validTimeLocal" }
|
||||
DateTime WC_Day0_ExpirationTimeLocal "Expires At [%1$tA, %1$tm/%1$td/%1$tY %1$tl:%1$tM %1$tp]" <time> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0#expirationTimeLocal" }
|
||||
String WC_Day0_Narrative "Narrative [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0#narrative" }
|
||||
Number:Temperature WC_Day0_TemperatureMin "Low Temperature [%.1f %unit%]" <temperature> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0#temperatureMin" }
|
||||
Number:Temperature WC_Day0_TemperatureMax "High Temperature [%.1f %unit%]" <temperature> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0#temperatureMax" }
|
||||
Number:Length WC_Day0_PrecipitationRain "Forecasted Rainfall Amount [%.2f %unit%]" <rain> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0#precipitationRain" }
|
||||
Number:Length WC_Day0_PrecipitationSnow "Forecasted Snowfall Amount [%.2f %unit%]" <snow> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0#precipitationSnow" }
|
||||
|
||||
// Day 0 Day
|
||||
String WC_Day0_Day_DaypartName "Daypart Name [%s]" <time> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#daypartName" }
|
||||
String WC_Day0_Day_DayOrNight "Day or Night [%s]" <time> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#dayOrNight" }
|
||||
String WC_Day0_Day_Narrative "Narrative [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#narrative" }
|
||||
String WC_Day0_Day_WxPhraseShort "Wx Phrase Short [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#wxPhraseShort" }
|
||||
String WC_Day0_Day_WxPhraseLong "Wx Phrase Long [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#wxPhraseLong" }
|
||||
String WC_Day0_Day_QualifierPhrase "Qualifier Phrase [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#qualifierPhrase" }
|
||||
String WC_Day0_Day_QualifierCode "Qualifier Code [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#qualifierCode" }
|
||||
Number:Temperature WC_Day0_Day_Temperature "Temperature [%.1f %unit%]" <temperature> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#temperature" }
|
||||
Number:Temperature WC_Day0_Day_TemperatureHeatIndex "Temperature Heat Index [%.1f %unit%]" <temperature> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#temperatureHeatIndex" }
|
||||
Number:Temperature WC_Day0_Day_TemperatureWindChill "Temperature Wind Chill [%.1f %unit%]" <temperature> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#temperatureWindChill" }
|
||||
Number:Dimensionless WC_Day0_Day_RelativeHumidity "Relative Humidity [%.0f %unit%]" <humidity> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#relativeHumidity" }
|
||||
Number:Dimensionless WC_Day0_Day_CloudCover "Cloud Cover [%.0f %unit%]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#cloudCover" }
|
||||
Number:Speed WC_Day0_Day_WindSpeed "Wind Speed [%.0f %unit%]" <wind> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#windSpeed" }
|
||||
Number:Angle WC_Day0_Day_WindDirection "Wind Direction [%.0f %unit%]" <wind> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#windDirection" }
|
||||
String WC_Day0_Day_WindDirectionCardinal "Wind Direction Cardinal [%s]" <wind> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#windDirectionCardinal" }
|
||||
String WC_Day0_Day_WindPhrase "Wind Phrase [%s]" <wind> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#windPhrase" }
|
||||
Number:Dimensionless WC_Day0_Day_PrecipitationChance "Precipitation Chance [%.0f %unit%]" <rain> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#precipitationChance" }
|
||||
String WC_Day0_Day_PrecipitationType "Precipitation Type [%s]" <rain> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#precipitationType" }
|
||||
Number:Length WC_Day0_Day_PrecipitationRain "Precipitation Rain [%.2f %unit%]" <rain> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#precipitationRain" }
|
||||
Number:Length WC_Day0_Day_PrecipitationSnow "Precipitation Snow [%.2f %unit%]" <snow> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#precipitationSnow" }
|
||||
String WC_Day0_Day_SnowRange "Snow Range [%s]" <snow> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#snowRange" }
|
||||
String WC_Day0_Day_ThunderCategory "Thunder Category [%s]" <c_thunder> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#thunderCategory" }
|
||||
Number WC_Day0_Day_ThunderIndex "Thunder Index [%.0f %unit%]" <c_thunder> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#thunderIndex" }
|
||||
String WC_Day0_Day_UVDescription "UV Description [%s]" <sun> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#uvDescription" }
|
||||
Number WC_Day0_Day_UVIndex "UV Index [%.0f %unit%]" <sun> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#uvIndex" }
|
||||
Number WC_Day0_Day_IconCode "Icon Code [%.0f %unit%]" { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#iconCode" }
|
||||
Number WC_Day0_Day_IconCodeExtend "Icon Code Extend [%.0f %unit%]" { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#iconCodeExtend" }
|
||||
Image WC_Day0_Day_IconImage "Icon Image [%s]" { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Day#iconImage" }
|
||||
|
||||
// Day 0 Night
|
||||
String WC_Day0_Night_DaypartName "Daypart Name [%s]" <time> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#daypartName" }
|
||||
String WC_Day0_Night_DayOrNight "Day or Night [%s]" <time> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#dayOrNight" }
|
||||
String WC_Day0_Night_Narrative "Narrative [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#narrative" }
|
||||
String WC_Day0_Night_WxPhraseShort "Wx Phrase Short [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#wxPhraseShort" }
|
||||
String WC_Day0_Night_WxPhraseLong "Wx Phrase Long [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#wxPhraseLong" }
|
||||
String WC_Day0_Night_QualifierPhrase "Qualifier Phrase [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#qualifierPhrase" }
|
||||
String WC_Day0_Night_QualifierCode "Qualifier Code [%s]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#qualifierCode" }
|
||||
Number:Temperature WC_Day0_Night_Temperature "Temperature [%.1f %unit%]" <temperature> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#temperature" }
|
||||
Number:Temperature WC_Day0_Night_TemperatureHeatIndex "Temperature Heat Index [%.1f %unit%]" <temperature> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#temperatureHeatIndex" }
|
||||
Number:Temperature WC_Day0_Night_TemperatureWindChill "Temperature Wind Chill [%.1f %unit%]" <temperature> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#temperatureWindChill" }
|
||||
Number:Dimensionless WC_Day0_Night_RelativeHumidity "Relative Humidity [%.0f %unit%]" <humidity> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#relativeHumidity" }
|
||||
Number:Dimensionless WC_Day0_Night_CloudCover "Cloud Cover [%.0f %unit%]" <sun_clouds> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#cloudCover" }
|
||||
Number:Speed WC_Day0_Night_WindSpeed "Wind Speed [%.0f %unit%]" <wind> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#windSpeed" }
|
||||
Number:Angle WC_Day0_Night_WindDirection "Wind Direction [%.0f %unit%]" <wind> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#windDirection" }
|
||||
String WC_Day0_Night_WindDirectionCardinal "Wind Direction Cardinal [%s]" <wind> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#windDirectionCardinal" }
|
||||
String WC_Day0_Night_WindPhrase "Wind Phrase [%s]" <wind> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#windPhrase" }
|
||||
Number:Dimensionless WC_Day0_Night_PrecipitationChance "Precipitation Chance [%.0f %unit%]" <rain> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#precipitationChance" }
|
||||
String WC_Day0_Night_PrecipitationType "Precipitation Type [%s]" <rain> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#precipitationType" }
|
||||
Number:Length WC_Day0_Night_PrecipitationRain "Precipitation Rain [%.2f %unit%]" <rain> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#precipitationRain" }
|
||||
Number:Length WC_Day0_Night_PrecipitationSnow "Precipitation Snow [%.2f %unit%]" <snow> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#precipitationSnow" }
|
||||
String WC_Day0_Night_SnowRange "Snow Range [%s]" <snow> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#snowRange" }
|
||||
String WC_Day0_Night_ThunderCategory "Thunder Category [%s]" <c_thunder> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#thunderCategory" }
|
||||
Number WC_Day0_Night_ThunderIndex "Thunder Index [%.0f %unit%]" <c_thunder> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#thunderIndex" }
|
||||
String WC_Day0_Night_UVDescription "UV Description [%s]" <sun> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#uvDescription" }
|
||||
Number WC_Day0_Night_UVIndex "UV Index [%.0f %unit%]" <sun> { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#uvIndex" }
|
||||
Number WC_Day0_Night_IconCode "Icon Code [%.0f %unit%]" { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#iconCode" }
|
||||
Number WC_Day0_Night_IconCodeExtend "Icon Code Extend [%.0f %unit%]" { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#iconCodeExtend" }
|
||||
Image WC_Day0_Night_IconImage "Icon Image [%s]" { channel="weathercompany:weather-forecast:myaccount:myweather:forecastDay0Night#iconImage" }
|
||||
```
|
||||
|
||||
### Sitemap Example
|
||||
|
||||
```
|
||||
Frame {
|
||||
Text label="The Weather Company Forecast" {
|
||||
Frame label="PWS Current Conditions" {
|
||||
Text item=WC_PWS_Neighborhood
|
||||
Text item=WC_PWS_ObservationTimeLocal
|
||||
Text item=WC_PWS_Temperature
|
||||
Text item=WC_PWS_TemperatureDewPoint
|
||||
Text item=WC_PWS_TemperatureHeatIndex
|
||||
Text item=WC_PWS_TemperatureWindChill
|
||||
Text item=WC_PWS_RelativeHumidity
|
||||
Text item=WC_PWS_Pressure
|
||||
Text item=WC_PWS_PrecipitationRate
|
||||
Text item=WC_PWS_PrecipitationTotal
|
||||
Text item=WC_PWS_SolarRadiation
|
||||
Text item=WC_PWS_UV
|
||||
Text item=WC_PWS_WindDirection
|
||||
Text item=WC_PWS_WindSpeed
|
||||
Text item=WC_PWS_WindSpeedGust
|
||||
Text item=WC_PWS_StationId
|
||||
Text item=WC_PWS_Country
|
||||
Text item=WC_PWS_Location
|
||||
Text item=WC_PWS_Elevation
|
||||
Text item=WC_PWS_QcStatus
|
||||
Text item=WC_PWS_SoftwareType
|
||||
}
|
||||
Frame label="Day 0 (Today)" {
|
||||
Text item=WC_Day0_DayOfWeek
|
||||
Text item=WC_Day0_Narrative
|
||||
Text item=WC_Day0_TemperatureMin
|
||||
Text item=WC_Day0_TemperatureMax
|
||||
Text item=WC_Day0_PrecipitationRain
|
||||
Text item=WC_Day0_PrecipitationSnow
|
||||
Text item=WC_Day0_ValidTimeLocal
|
||||
Text item=WC_Day0_ExpirationTimeLocal
|
||||
}
|
||||
Frame {
|
||||
Text item=WC_Day0_Day_DaypartName
|
||||
Text item=WC_Day0_Day_Narrative
|
||||
Text item=WC_Day0_Day_WxPhraseShort
|
||||
Text item=WC_Day0_Day_WxPhraseLong
|
||||
Text item=WC_Day0_Day_QualifierCode
|
||||
Text item=WC_Day0_Day_QualifierPhrase
|
||||
Text item=WC_Day0_Day_Temperature
|
||||
Text item=WC_Day0_Day_TemperatureHeatIndex
|
||||
Text item=WC_Day0_Day_TemperatureWindChill
|
||||
Text item=WC_Day0_Day_RelativeHumidity
|
||||
Text item=WC_Day0_Day_PrecipitationChance
|
||||
Text item=WC_Day0_Day_PrecipitationType
|
||||
Text item=WC_Day0_Day_PrecipitationRain
|
||||
Text item=WC_Day0_Day_PrecipitationSnow
|
||||
Text item=WC_Day0_Day_SnowRange
|
||||
Text item=WC_Day0_Day_CloudCover
|
||||
Text item=WC_Day0_Day_WindSpeed
|
||||
Text item=WC_Day0_Day_WindDirection
|
||||
Text item=WC_Day0_Day_WindDirectionCardinal
|
||||
Text item=WC_Day0_Day_WindSpeed
|
||||
Text item=WC_Day0_Day_ThunderCategory
|
||||
Text item=WC_Day0_Day_ThunderIndex
|
||||
Text item=WC_Day0_Day_UVDescription
|
||||
Text item=WC_Day0_Day_UVIndex
|
||||
Image item=WC_Day0_Day_IconImage
|
||||
}
|
||||
Frame {
|
||||
Text item=WC_Day0_Night_DaypartName
|
||||
Text item=WC_Day0_Night_Narrative
|
||||
Text item=WC_Day0_Night_WxPhraseShort
|
||||
Text item=WC_Day0_Night_WxPhraseLong
|
||||
Text item=WC_Day0_Night_QualifierCode
|
||||
Text item=WC_Day0_Night_QualifierPhrase
|
||||
Text item=WC_Day0_Night_Temperature
|
||||
Text item=WC_Day0_Night_TemperatureHeatIndex
|
||||
Text item=WC_Day0_Night_TemperatureWindChill
|
||||
Text item=WC_Day0_Night_RelativeHumidity
|
||||
Text item=WC_Day0_Night_PrecipitationChance
|
||||
Text item=WC_Day0_Night_PrecipitationType
|
||||
Text item=WC_Day0_Night_PrecipitationRain
|
||||
Text item=WC_Day0_Night_PrecipitationSnow
|
||||
Text item=WC_Day0_Night_SnowRange
|
||||
Text item=WC_Day0_Night_CloudCover
|
||||
Text item=WC_Day0_Night_WindSpeed
|
||||
Text item=WC_Day0_Night_WindDirection
|
||||
Text item=WC_Day0_Night_WindDirectionCardinal
|
||||
Text item=WC_Day0_Night_WindSpeed
|
||||
Text item=WC_Day0_Night_ThunderCategory
|
||||
Text item=WC_Day0_Night_ThunderIndex
|
||||
Text item=WC_Day0_Night_UVDescription
|
||||
Text item=WC_Day0_Night_UVIndex
|
||||
Image item=WC_Day0_Night_IconImage
|
||||
}
|
||||
}
|
||||
```
|
||||
17
bundles/org.openhab.binding.weathercompany/pom.xml
Normal 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/maven-v4_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.weathercompany</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: WeatherCompany Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.weathercompany-${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-weathercompany" description="Weather Company Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.weathercompany/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* 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.weathercompany.internal;
|
||||
|
||||
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.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link WeatherCompanyBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherCompanyBindingConstants {
|
||||
private static final String BINDING_ID = "weathercompany";
|
||||
|
||||
// Bridge
|
||||
public static final String THING_TYPE_BRIDGE = "account";
|
||||
public static final ThingTypeUID UID_BRIDGE = new ThingTypeUID(BINDING_ID, THING_TYPE_BRIDGE);
|
||||
public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_THING_TYPES_UIDS = Collections
|
||||
.unmodifiableSet(Stream.of(UID_BRIDGE).collect(Collectors.toSet()));
|
||||
|
||||
// Thing Types
|
||||
public static final ThingTypeUID THING_TYPE_WEATHER_OBSERVATIONS = new ThingTypeUID(BINDING_ID,
|
||||
"weather-observations");
|
||||
public static final ThingTypeUID THING_TYPE_WEATHER_FORECAST = new ThingTypeUID(BINDING_ID, "weather-forecast");
|
||||
|
||||
// Collection of weather station thing types
|
||||
public static final Set<ThingTypeUID> SUPPORTED_WEATHER_THING_TYPES_UIDS = Collections.unmodifiableSet(
|
||||
Stream.of(THING_TYPE_WEATHER_OBSERVATIONS, THING_TYPE_WEATHER_FORECAST).collect(Collectors.toSet()));
|
||||
|
||||
// Collection of all supported thing types
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(
|
||||
Stream.concat(SUPPORTED_BRIDGE_THING_TYPES_UIDS.stream(), SUPPORTED_WEATHER_THING_TYPES_UIDS.stream())
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
// Thing configuration
|
||||
public static final String CONFIG_LOCATION_TYPE = "locationType";
|
||||
public static final String CONFIG_LOCATION_TYPE_GEOCODE = "geocode";
|
||||
public static final String CONFIG_LOCATION_TYPE_POSTAL_CODE = "postalCode";
|
||||
public static final String CONFIG_LOCATION_TYPE_IATA_CODE = "iataCode";
|
||||
public static final String CONFIG_GEOCODE = "geocode";
|
||||
public static final String CONFIG_POSTAL_CODE = "postalCode";
|
||||
public static final String CONFIG_IATA_CODE = "iataCode";
|
||||
public static final String CONFIG_LANGUAGE = "language";
|
||||
public static final String CONFIG_LANGUAGE_DEFAULT = "en-US";
|
||||
public static final String CONFIG_PWS_STATION_ID = "pwsStationId";
|
||||
|
||||
// Used to sanitize the API key in the URL in debug log messages
|
||||
public static final String REPLACE_API_KEY = "XXXXXXXXXXXXXXXXXXXXX";
|
||||
|
||||
// List of pwsObservations channel IDs
|
||||
public static final String CH_PWS_HUMIDITY = "currentHumidity";
|
||||
public static final String CH_PWS_PRECIPTATION_RATE = "currentPrecipitationRate";
|
||||
public static final String CH_PWS_PRECIPITATION_TOTAL = "currentPrecipitationTotal";
|
||||
public static final String CH_PWS_PRESSURE = "currentPressure";
|
||||
public static final String CH_PWS_SOLAR_RADIATION = "currentSolarRadiation";
|
||||
public static final String CH_PWS_TEMP = "currentTemperature";
|
||||
public static final String CH_PWS_TEMP_DEW_POINT = "currentTemperatureDewPoint";
|
||||
public static final String CH_PWS_TEMP_HEAT_INDEX = "currentTemperatureHeatIndex";
|
||||
public static final String CH_PWS_TEMP_WIND_CHILL = "currentTemperatureWindChill";
|
||||
public static final String CH_PWS_UV = "currentUv";
|
||||
public static final String CH_PWS_WIND_DIRECTION = "currentWindDirection";
|
||||
public static final String CH_PWS_WIND_SPEED = "currentWindSpeed";
|
||||
public static final String CH_PWS_WIND_GUST = "currentWindSpeedGust";
|
||||
public static final String CH_PWS_COUNTRY = "country";
|
||||
public static final String CH_PWS_LOCATION = "location";
|
||||
public static final String CH_PWS_ELEVATION = "elevation";
|
||||
public static final String CH_PWS_NEIGHBORHOOD = "neighborhood";
|
||||
public static final String CH_PWS_OBSERVATION_TIME_LOCAL = "observationTimeLocal";
|
||||
public static final String CH_PWS_QC_STATUS = "qcStatus";
|
||||
public static final String CH_PWS_SOFTWARE_TYPE = "softwareType";
|
||||
public static final String CH_PWS_STATION_ID = "stationId";
|
||||
|
||||
// Channel group forecastDay
|
||||
public static final String CH_GROUP_FORECAST_DAY = "forecastDay";
|
||||
|
||||
// List of forecastDay channel IDs
|
||||
public static final String CH_DAY_OF_WEEK = "dayOfWeek";
|
||||
public static final String CH_VALID_TIME_LOCAL = "validTimeLocal";
|
||||
public static final String CH_EXPIRATION_TIME_LOCAL = "expirationTimeLocal";
|
||||
public static final String CH_NARRATIVE = "narrative";
|
||||
public static final String CH_TEMP_MIN = "temperatureMin";
|
||||
public static final String CH_TEMP_MAX = "temperatureMax";
|
||||
public static final String CH_PRECIP_RAIN = "precipitationRain";
|
||||
public static final String CH_PRECIP_SNOW = "precipitationSnow";
|
||||
|
||||
// Channel group forecastDaypart
|
||||
public static final String CH_GROUP_FORECAST_DAYPART_DAY = "Day";
|
||||
public static final String CH_GROUP_FORECAST_DAYPART_NIGHT = "Night";
|
||||
|
||||
// List of forecastDaypart channel IDs
|
||||
public static final String CH_DP_NAME = "daypartName";
|
||||
public static final String CH_DP_DAY_OR_NIGHT = "dayOrNight";
|
||||
public static final String CH_DP_NARRATIVE = "narrative";
|
||||
public static final String CH_DP_WX_PHRASE_SHORT = "wxPhraseShort";
|
||||
public static final String CH_DP_WX_PHRASE_LONG = "wxPhraseLong";
|
||||
public static final String CH_DP_TEMP = "temperature";
|
||||
public static final String CH_DP_TEMP_HEAT_INDEX = "temperatureHeatIndex";
|
||||
public static final String CH_DP_TEMP_WIND_CHILL = "temperatureWindChill";
|
||||
public static final String CH_DP_HUMIDITY = "relativeHumidity";
|
||||
public static final String CH_DP_CLOUD_COVER = "cloudCover";
|
||||
public static final String CH_DP_WIND_SPEED = "windSpeed";
|
||||
public static final String CH_DP_WIND_DIR = "windDirection";
|
||||
public static final String CH_DP_WIND_DIR_CARDINAL = "windDirectionCardinal";
|
||||
public static final String CH_DP_WIND_PHRASE = "WindPhrase";
|
||||
public static final String CH_DP_PRECIP_CHANCE = "precipitationChance";
|
||||
public static final String CH_DP_PRECIP_TYPE = "precipitationType";
|
||||
public static final String CH_DP_PRECIP_RAIN = "precipitationRain";
|
||||
public static final String CH_DP_PRECIP_SNOW = "precipitationSnow";
|
||||
public static final String CH_DP_SNOW_RANGE = "snowRange";
|
||||
public static final String CH_DP_THUNDER_CATEGORY = "thunderCategory";
|
||||
public static final String CH_DP_THUNDER_INDEX = "thunderIndex";
|
||||
public static final String CH_DP_UV_DESCRIPTION = "uvDescription";
|
||||
public static final String CH_DP_UV_INDEX = "uvIndex";
|
||||
public static final String CH_DP_ICON_CODE = "iconCode";
|
||||
public static final String CH_DP_ICON_CODE_EXTEND = "iconCodeExtend";
|
||||
public static final String CH_DP_ICON_IMAGE = "iconImage";
|
||||
public static final String CH_DP_QUALIFIER_PHRASE = "qualifierPhrase";
|
||||
public static final String CH_DP_QUALIFIER_CODE = "qualifierCode";
|
||||
}
|
||||
@@ -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.weathercompany.internal;
|
||||
|
||||
import static org.openhab.binding.weathercompany.internal.WeatherCompanyBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.weathercompany.internal.discovery.WeatherCompanyDiscoveryService;
|
||||
import org.openhab.binding.weathercompany.internal.handler.WeatherCompanyBridgeHandler;
|
||||
import org.openhab.binding.weathercompany.internal.handler.WeatherCompanyForecastHandler;
|
||||
import org.openhab.binding.weathercompany.internal.handler.WeatherCompanyObservationsHandler;
|
||||
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.UnitProvider;
|
||||
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 WeatherCompanyHandlerFactory} is responsible for creating thing handlers.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.weathercompany", service = ThingHandlerFactory.class)
|
||||
public class WeatherCompanyHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
|
||||
private TimeZoneProvider timeZoneProvider;
|
||||
private UnitProvider unitProvider;
|
||||
private HttpClient httpClient;
|
||||
private LocationProvider locationProvider;
|
||||
private LocaleProvider localeProvider;
|
||||
|
||||
@Activate
|
||||
public WeatherCompanyHandlerFactory(@Reference TimeZoneProvider timeZoneProvider,
|
||||
@Reference UnitProvider unitProvider, @Reference HttpClientFactory httpClientFactory,
|
||||
@Reference LocationProvider locationProvider, @Reference LocaleProvider localeProvider) {
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
this.unitProvider = unitProvider;
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.locationProvider = locationProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
@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_FORECAST.equals(thingTypeUID)) {
|
||||
return new WeatherCompanyForecastHandler(thing, timeZoneProvider, httpClient, unitProvider, localeProvider);
|
||||
} else if (THING_TYPE_WEATHER_OBSERVATIONS.equals(thingTypeUID)) {
|
||||
return new WeatherCompanyObservationsHandler(thing, timeZoneProvider, httpClient, unitProvider,
|
||||
localeProvider);
|
||||
} else if (SUPPORTED_BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID)) {
|
||||
WeatherCompanyBridgeHandler handler = new WeatherCompanyBridgeHandler((Bridge) thing);
|
||||
registerDeviceDiscoveryService(handler);
|
||||
return handler;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void removeHandler(ThingHandler thingHandler) {
|
||||
if (thingHandler instanceof WeatherCompanyBridgeHandler) {
|
||||
ThingUID thingUID = thingHandler.getThing().getUID();
|
||||
unregisterDeviceDiscoveryService(thingUID);
|
||||
}
|
||||
super.removeHandler(thingHandler);
|
||||
}
|
||||
|
||||
private synchronized void registerDeviceDiscoveryService(WeatherCompanyBridgeHandler bridgeHandler) {
|
||||
WeatherCompanyDiscoveryService discoveryService = new WeatherCompanyDiscoveryService(bridgeHandler,
|
||||
locationProvider, localeProvider);
|
||||
discoveryService.activate(null);
|
||||
this.discoveryServiceRegs.put(bridgeHandler.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
|
||||
private void unregisterDeviceDiscoveryService(ThingUID bridgeUID) {
|
||||
ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(bridgeUID);
|
||||
if (serviceReg != null) {
|
||||
WeatherCompanyDiscoveryService discoveryService = (WeatherCompanyDiscoveryService) bundleContext
|
||||
.getService(serviceReg.getReference());
|
||||
serviceReg.unregister();
|
||||
if (discoveryService != null) {
|
||||
discoveryService.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.weathercompany.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link WeatherCompanyBridgeConfig} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherCompanyBridgeConfig {
|
||||
/**
|
||||
* API key
|
||||
*/
|
||||
public @Nullable String apiKey;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.weathercompany.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link WeatherCompanyForecastConfig} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherCompanyForecastConfig {
|
||||
/**
|
||||
* Type of location (postal, geocode, etc.)
|
||||
*/
|
||||
public @Nullable String locationType;
|
||||
|
||||
/**
|
||||
* Postal code
|
||||
*/
|
||||
public @Nullable String postalCode;
|
||||
|
||||
/**
|
||||
* Geocode
|
||||
*/
|
||||
public @Nullable String geocode;
|
||||
|
||||
/**
|
||||
* IATA code
|
||||
*/
|
||||
public @Nullable String iataCode;
|
||||
|
||||
/**
|
||||
* Language
|
||||
*/
|
||||
public @Nullable String language;
|
||||
|
||||
/**
|
||||
* Interval with which forecast will be updated.
|
||||
*/
|
||||
public int refreshInterval;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WeatherCompanyConfig: { locationType=" + locationType + ", postalCode=" + postalCode + ", geocode="
|
||||
+ geocode + ", iataCode=" + iataCode + ", language=" + language + ", refreshInterval=" + refreshInterval
|
||||
+ " }";
|
||||
}
|
||||
}
|
||||
@@ -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.weathercompany.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link WeatherCompanyObservationsConfig} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherCompanyObservationsConfig {
|
||||
/**
|
||||
* Personal Weather Station Id
|
||||
*/
|
||||
public @Nullable String pwsStationId;
|
||||
|
||||
/**
|
||||
* Interval with which forecast will be updated.
|
||||
*/
|
||||
public int refreshInterval;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WeatherCompanyConfig: { pwsStationId=" + pwsStationId + ", refreshInterval=" + refreshInterval + " }";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* 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.weathercompany.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.weathercompany.internal.WeatherCompanyBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
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.weathercompany.internal.handler.WeatherCompanyAbstractHandler;
|
||||
import org.openhab.binding.weathercompany.internal.handler.WeatherCompanyBridgeHandler;
|
||||
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.library.types.PointType;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link WeatherCompanyDiscoveryService} creates things based on the location
|
||||
* configured in openHAB
|
||||
*
|
||||
* @author Mark Hilbush - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherCompanyDiscoveryService extends AbstractDiscoveryService {
|
||||
// Thing for local weather created during discovery
|
||||
private static final String LOCAL = "local";
|
||||
private static final String LOCAL_WEATHER = "Local Forecast";
|
||||
|
||||
private static final int DISCOVER_TIMEOUT_SECONDS = 4;
|
||||
private static final int DISCOVERY_INTERVAL_SECONDS = 1200;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WeatherCompanyDiscoveryService.class);
|
||||
|
||||
private final LocationProvider locationProvider;
|
||||
private final LocaleProvider localeProvider;
|
||||
private final WeatherCompanyBridgeHandler bridgeHandler;
|
||||
|
||||
private @Nullable ScheduledFuture<?> discoveryJob;
|
||||
|
||||
/**
|
||||
* Creates a WeatherCompanyDiscoveryService with discovery enabled
|
||||
*/
|
||||
public WeatherCompanyDiscoveryService(WeatherCompanyBridgeHandler bridgeHandler, LocationProvider locationProvider,
|
||||
LocaleProvider localeProvider) {
|
||||
super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, true);
|
||||
this.bridgeHandler = bridgeHandler;
|
||||
this.locationProvider = locationProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate(@Nullable Map<String, @Nullable Object> configProperties) {
|
||||
super.activate(configProperties);
|
||||
logger.debug("Discovery: Activating discovery service for {}", bridgeHandler.getThing().getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
logger.debug("Discovery: Deactivating discovery service for {}", bridgeHandler.getThing().getUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Discovery: Starting Weather Company discovery scan");
|
||||
createDiscoveryResult();
|
||||
stopScan();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
ScheduledFuture<?> job = discoveryJob;
|
||||
if (job == null || job.isCancelled()) {
|
||||
job = scheduler.scheduleWithFixedDelay(() -> {
|
||||
createDiscoveryResult();
|
||||
}, 15, DISCOVERY_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
||||
logger.debug("Discovery: Scheduled Weather Company discovery job to run every {} seconds",
|
||||
DISCOVERY_INTERVAL_SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
ScheduledFuture<?> job = discoveryJob;
|
||||
if (job != null && !job.isCancelled()) {
|
||||
job.cancel(true);
|
||||
discoveryJob = null;
|
||||
logger.debug("Discovery: Stopped Weather Company device background discovery");
|
||||
}
|
||||
}
|
||||
|
||||
private void createDiscoveryResult() {
|
||||
PointType location = locationProvider.getLocation();
|
||||
if (location == null) {
|
||||
logger.debug("Discovery: Can't create discovery result because location is not set in openHAB");
|
||||
return;
|
||||
}
|
||||
Map<String, Object> properties = new HashMap<>(3);
|
||||
properties.put(CONFIG_LOCATION_TYPE, CONFIG_LOCATION_TYPE_GEOCODE);
|
||||
properties.put(CONFIG_GEOCODE, String.format("%s,%s", location.getLatitude(), location.getLongitude()));
|
||||
properties.put(CONFIG_LANGUAGE, WeatherCompanyAbstractHandler.lookupLanguage(localeProvider.getLocale()));
|
||||
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
|
||||
ThingUID localWeatherThing = new ThingUID(THING_TYPE_WEATHER_FORECAST, bridgeUID, LOCAL);
|
||||
thingDiscovered(DiscoveryResultBuilder.create(localWeatherThing).withBridge(bridgeUID).withLabel(LOCAL_WEATHER)
|
||||
.withProperties(properties).build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
/**
|
||||
* 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.weathercompany.internal.handler;
|
||||
|
||||
import static org.openhab.binding.weathercompany.internal.WeatherCompanyBindingConstants.CONFIG_LANGUAGE_DEFAULT;
|
||||
import static org.openhab.core.library.unit.MetricPrefix.MILLI;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.measure.Unit;
|
||||
import javax.measure.spi.SystemOfUnits;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
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.StringType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
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.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
/**
|
||||
* The {@link WeatherCompanyAbstractHandler} contains common utilities used by
|
||||
* handlers.
|
||||
*
|
||||
* Weather Company API documentation is located here
|
||||
* - https://docs.google.com/document/d/1eKCnKXI9xnoMGRRzOL1xPCBihNV2rOet08qpE_gArAY/edit
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class WeatherCompanyAbstractHandler extends BaseThingHandler {
|
||||
protected static final int WEATHER_COMPANY_API_TIMEOUT_SECONDS = 15;
|
||||
protected static final int REFRESH_JOB_INITIAL_DELAY_SECONDS = 6;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WeatherCompanyAbstractHandler.class);
|
||||
|
||||
protected final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
protected final Map<String, State> weatherDataCache = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
// Provided by handler factory
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
private final HttpClient httpClient;
|
||||
private final SystemOfUnits systemOfUnits;
|
||||
|
||||
public WeatherCompanyAbstractHandler(Thing thing, TimeZoneProvider timeZoneProvider, HttpClient httpClient,
|
||||
UnitProvider unitProvider) {
|
||||
super(thing);
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
this.httpClient = httpClient;
|
||||
this.systemOfUnits = unitProvider.getMeasurementSystem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isBridgeOnline() {
|
||||
boolean bridgeStatus = false;
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
|
||||
bridgeStatus = true;
|
||||
}
|
||||
return bridgeStatus;
|
||||
}
|
||||
|
||||
protected String getApiKey() {
|
||||
String apiKey = "unknown";
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
|
||||
WeatherCompanyBridgeHandler handler = (WeatherCompanyBridgeHandler) bridge.getHandler();
|
||||
if (handler != null) {
|
||||
String key = handler.getApiKey();
|
||||
if (key != null) {
|
||||
apiKey = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set either Imperial or Metric SI for the API call
|
||||
*/
|
||||
protected String getUnitsQueryString() {
|
||||
return isImperial() ? "e" : "s";
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the units configured in the system
|
||||
*/
|
||||
protected boolean isImperial() {
|
||||
return systemOfUnits instanceof ImperialUnits ? true : false;
|
||||
}
|
||||
|
||||
protected void updateChannel(String channelId, State state) {
|
||||
// Only update channel if it's linked
|
||||
if (isLinked(channelId)) {
|
||||
updateState(channelId, state);
|
||||
weatherDataCache.put(channelId, state);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the state to the passed value. If value is null, set the state to UNDEF
|
||||
*/
|
||||
protected State undefOrString(@Nullable String value) {
|
||||
return value == null ? UnDefType.UNDEF : new StringType(value);
|
||||
}
|
||||
|
||||
protected State undefOrDate(@Nullable Integer value) {
|
||||
return value == null ? UnDefType.UNDEF : getLocalDateTimeType(value);
|
||||
}
|
||||
|
||||
protected State undefOrDate(@Nullable String value) {
|
||||
return value == null ? UnDefType.UNDEF : getLocalDateTimeType(value);
|
||||
}
|
||||
|
||||
protected State undefOrDecimal(@Nullable Number value) {
|
||||
return value == null ? UnDefType.UNDEF : new DecimalType(value.doubleValue());
|
||||
}
|
||||
|
||||
protected State undefOrQuantity(@Nullable Number value, Unit<?> unit) {
|
||||
return value == null ? UnDefType.UNDEF : new QuantityType<>(value, unit);
|
||||
}
|
||||
|
||||
protected State undefOrPoint(@Nullable Number lat, @Nullable Number lon) {
|
||||
return lat != null && lon != null
|
||||
? new PointType(new DecimalType(lat.doubleValue()), new DecimalType(lon.doubleValue()))
|
||||
: UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
/*
|
||||
* The API will request units based on openHAB's SystemOfUnits setting. Therefore,
|
||||
* when setting the QuantityType state, make sure we use the proper unit.
|
||||
*/
|
||||
protected Unit<?> getTempUnit() {
|
||||
return isImperial() ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS;
|
||||
}
|
||||
|
||||
protected Unit<?> getSpeedUnit() {
|
||||
return isImperial() ? ImperialUnits.MILES_PER_HOUR : SIUnits.KILOMETRE_PER_HOUR;
|
||||
}
|
||||
|
||||
protected Unit<?> getLengthUnit() {
|
||||
return isImperial() ? ImperialUnits.INCH : MILLI(SIUnits.METRE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the The Weather Channel API request
|
||||
*/
|
||||
protected @Nullable String executeApiRequest(@Nullable String url) {
|
||||
if (url == null) {
|
||||
logger.debug("Handler: Can't execute request because url is null");
|
||||
return null;
|
||||
}
|
||||
Request request = httpClient.newRequest(url);
|
||||
request.timeout(WEATHER_COMPANY_API_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
request.method(HttpMethod.GET);
|
||||
request.header(HttpHeader.ACCEPT, "application/json");
|
||||
request.header(HttpHeader.ACCEPT_ENCODING, "gzip");
|
||||
|
||||
String errorMsg;
|
||||
try {
|
||||
ContentResponse contentResponse = request.send();
|
||||
switch (contentResponse.getStatus()) {
|
||||
case HttpStatus.OK_200:
|
||||
String response = contentResponse.getContentAsString();
|
||||
String cacheControl = contentResponse.getHeaders().get(HttpHeader.CACHE_CONTROL);
|
||||
logger.debug("Cache-Control header is {}", cacheControl);
|
||||
return response;
|
||||
case HttpStatus.NO_CONTENT_204:
|
||||
errorMsg = "HTTP response 400: No content. Check configuration";
|
||||
break;
|
||||
case HttpStatus.BAD_REQUEST_400:
|
||||
errorMsg = "HTTP response 400: Bad request";
|
||||
break;
|
||||
case HttpStatus.UNAUTHORIZED_401:
|
||||
errorMsg = "HTTP response 401: Unauthorized";
|
||||
break;
|
||||
case HttpStatus.FORBIDDEN_403:
|
||||
errorMsg = "HTTP response 403: Invalid API key";
|
||||
break;
|
||||
case HttpStatus.NOT_FOUND_404:
|
||||
errorMsg = "HTTP response 404: Endpoint not found";
|
||||
break;
|
||||
case HttpStatus.METHOD_NOT_ALLOWED_405:
|
||||
errorMsg = "HTTP response 405: Method not allowed";
|
||||
break;
|
||||
case HttpStatus.NOT_ACCEPTABLE_406:
|
||||
errorMsg = "HTTP response 406: Not acceptable";
|
||||
break;
|
||||
case HttpStatus.REQUEST_TIMEOUT_408:
|
||||
errorMsg = "HTTP response 408: Request timeout";
|
||||
break;
|
||||
case HttpStatus.INTERNAL_SERVER_ERROR_500:
|
||||
errorMsg = "HTTP response 500: Internal server error";
|
||||
break;
|
||||
case HttpStatus.BAD_GATEWAY_502:
|
||||
case HttpStatus.SERVICE_UNAVAILABLE_503:
|
||||
case HttpStatus.GATEWAY_TIMEOUT_504:
|
||||
errorMsg = String.format("HTTP response %d: Service unavailable or gateway issue",
|
||||
contentResponse.getStatus());
|
||||
break;
|
||||
default:
|
||||
errorMsg = String.format("HTTP GET failed: %d, %s", contentResponse.getStatus(),
|
||||
contentResponse.getReason());
|
||||
break;
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
errorMsg = "TimeoutException: Call to Weather Company API timed out";
|
||||
} catch (ExecutionException e) {
|
||||
errorMsg = String.format("ExecutionException: %s", e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
errorMsg = String.format("InterruptedException: %s", e.getMessage());
|
||||
}
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMsg);
|
||||
logger.debug("{}", errorMsg);
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert UTC Unix epoch seconds to local time
|
||||
*/
|
||||
protected DateTimeType getLocalDateTimeType(long epochSeconds) {
|
||||
Instant instant = Instant.ofEpochSecond(epochSeconds);
|
||||
ZonedDateTime localDateTime = instant.atZone(getZoneId());
|
||||
DateTimeType dateTimeType = new DateTimeType(localDateTime);
|
||||
return dateTimeType;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert UTC time string to local time
|
||||
* Input string is of form 2018-12-02T10:47:00.000Z
|
||||
*/
|
||||
protected State getLocalDateTimeType(String dateTimeString) {
|
||||
State dateTimeType;
|
||||
try {
|
||||
Instant instant = Instant.parse(dateTimeString);
|
||||
ZonedDateTime localDateTime = instant.atZone(getZoneId());
|
||||
dateTimeType = new DateTimeType(localDateTime);
|
||||
} catch (DateTimeParseException e) {
|
||||
logger.debug("Error parsing date/time string: {}", e.getMessage());
|
||||
dateTimeType = UnDefType.UNDEF;
|
||||
}
|
||||
return dateTimeType;
|
||||
}
|
||||
|
||||
private ZoneId getZoneId() {
|
||||
return timeZoneProvider.getTimeZone();
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by discovery service to get TWC language based on
|
||||
* the locale configured in openHAB
|
||||
*/
|
||||
public static String lookupLanguage(Locale locale) {
|
||||
String ohLanguage = locale.getLanguage() + "-" + locale.getCountry();
|
||||
for (String language : WEATHER_CHANNEL_LANGUAGES) {
|
||||
if (language.equals(ohLanguage)) {
|
||||
return language;
|
||||
}
|
||||
}
|
||||
return CONFIG_LANGUAGE_DEFAULT;
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
private static final String[] WEATHER_CHANNEL_LANGUAGES = {
|
||||
"ar-AE",
|
||||
"az-AZ",
|
||||
"bg-BG",
|
||||
"bn-BD",
|
||||
"bn-IN",
|
||||
"bs-BA",
|
||||
"ca-ES",
|
||||
"cs-CZ",
|
||||
"da-DK",
|
||||
"de-DE",
|
||||
"el-GR",
|
||||
"en-GB",
|
||||
"en-IN",
|
||||
"en-US",
|
||||
"es-AR",
|
||||
"es-ES",
|
||||
"es-LA",
|
||||
"es-MX",
|
||||
"es-UN",
|
||||
"es-US",
|
||||
"et-EE",
|
||||
"fa-IR",
|
||||
"fi-FI",
|
||||
"fr-CA",
|
||||
"fr-FR",
|
||||
"gu-IN",
|
||||
"he-IL",
|
||||
"hi-IN",
|
||||
"hr-HR",
|
||||
"hu-HU",
|
||||
"in-ID",
|
||||
"is-IS",
|
||||
"it-IT",
|
||||
"iw-IL",
|
||||
"ja-JP",
|
||||
"jv-ID",
|
||||
"ka-GE",
|
||||
"kk-KZ",
|
||||
"kn-IN",
|
||||
"ko-KR",
|
||||
"lt-LT",
|
||||
"lv-LV",
|
||||
"mk-MK",
|
||||
"mn-MN",
|
||||
"ms-MY",
|
||||
"nl-NL",
|
||||
"no-NO",
|
||||
"pl-PL",
|
||||
"pt-BR",
|
||||
"pt-PT",
|
||||
"ro-RO",
|
||||
"ru-RU",
|
||||
"si-LK",
|
||||
"sk-SK",
|
||||
"sl-SI",
|
||||
"sq-AL",
|
||||
"sr-BA",
|
||||
"sr-ME",
|
||||
"sr-RS",
|
||||
"sv-SE",
|
||||
"sw-KE",
|
||||
"ta-IN",
|
||||
"ta-LK",
|
||||
"te-IN",
|
||||
"tg-TJ",
|
||||
"th-TH",
|
||||
"tk-TM",
|
||||
"tl-PH",
|
||||
"tr-TR",
|
||||
"uk-UA",
|
||||
"ur-PK",
|
||||
"uz-UZ",
|
||||
"vi-VN",
|
||||
"zh-CN",
|
||||
"zh-HK",
|
||||
"zh-TW"
|
||||
};
|
||||
//@formatter:on
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 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.weathercompany.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang.exception.ExceptionUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpResponseException;
|
||||
import org.openhab.binding.weathercompany.internal.config.WeatherCompanyBridgeConfig;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link WeatherCompanyBridgeHandler} is responsible for validating the API key
|
||||
* used to access the Weather Company API.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherCompanyBridgeHandler extends BaseBridgeHandler {
|
||||
private static final long KEY_VALIDATION_FREQ_SECONDS = 120L;
|
||||
private static final String BASE_URL = "https://api.weather.com/v3/location/search?query=chicago&locationType=locid&language=en-US&format=json&apiKey=";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WeatherCompanyBridgeHandler.class);
|
||||
|
||||
private @Nullable Future<?> validateApiKeyJob;
|
||||
|
||||
private final Runnable validateApiKeyRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.debug("Bridge: Attempting to validate API key");
|
||||
try {
|
||||
String url = BASE_URL + getConfigAs(WeatherCompanyBridgeConfig.class).apiKey;
|
||||
String response = HttpUtil.executeUrl("GET", url, 10000);
|
||||
// If we get a response, we know the API key is valid
|
||||
logger.debug("Bridge: Got a successful response to key validation: '{}'", response);
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
cancelValidateApiKeyJob();
|
||||
} catch (IOException e) {
|
||||
Throwable rootcause = ExceptionUtils.getRootCause(e);
|
||||
if (rootcause instanceof HttpResponseException
|
||||
&& rootcause.getMessage().contains("Authentication challenge without")) {
|
||||
logger.debug("Bridge: HttpResponseException: API key is not valid");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "API key is invalid");
|
||||
} else {
|
||||
logger.debug("Bridge: IOException trying to validate Api key: {}", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public WeatherCompanyBridgeHandler(Bridge thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
scheduleValidateApiKeyJob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
cancelValidateApiKeyJob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// Bridge doesn't handle any commands
|
||||
}
|
||||
|
||||
public @Nullable String getApiKey() {
|
||||
return getConfigAs(WeatherCompanyBridgeConfig.class).apiKey;
|
||||
}
|
||||
|
||||
private void scheduleValidateApiKeyJob() {
|
||||
cancelValidateApiKeyJob();
|
||||
validateApiKeyJob = scheduler.scheduleWithFixedDelay(validateApiKeyRunnable, 0L, KEY_VALIDATION_FREQ_SECONDS,
|
||||
TimeUnit.SECONDS);
|
||||
logger.debug("Bridge: Scheduling job to validate API key");
|
||||
}
|
||||
|
||||
private void cancelValidateApiKeyJob() {
|
||||
if (validateApiKeyJob != null) {
|
||||
validateApiKeyJob.cancel(true);
|
||||
validateApiKeyJob = null;
|
||||
logger.debug("Bridge: Canceling job to validate API key");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
/**
|
||||
* 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.weathercompany.internal.handler;
|
||||
|
||||
import static org.openhab.binding.weathercompany.internal.WeatherCompanyBindingConstants.*;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.weathercompany.internal.config.WeatherCompanyForecastConfig;
|
||||
import org.openhab.binding.weathercompany.internal.model.DayPartDTO;
|
||||
import org.openhab.binding.weathercompany.internal.model.ForecastDTO;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.library.types.RawType;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
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.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link WeatherCompanyForecastHandler} is responsible for pulling weather forecast
|
||||
* information from the Weather Company API.
|
||||
*
|
||||
* API documentation is located here
|
||||
* - https://docs.google.com/document/d/1eKCnKXI9xnoMGRRzOL1xPCBihNV2rOet08qpE_gArAY/edit
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherCompanyForecastHandler extends WeatherCompanyAbstractHandler {
|
||||
private static final String BASE_FORECAST_URL = "https://api.weather.com/v3/wx/forecast/daily/5day";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WeatherCompanyForecastHandler.class);
|
||||
|
||||
private final LocaleProvider localeProvider;
|
||||
|
||||
private int refreshIntervalSeconds;
|
||||
private String locationQueryString = "";
|
||||
private String languageQueryString = "";
|
||||
|
||||
private @Nullable Future<?> refreshForecastJob;
|
||||
|
||||
private final Runnable refreshRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refreshForecast();
|
||||
}
|
||||
};
|
||||
|
||||
public WeatherCompanyForecastHandler(Thing thing, TimeZoneProvider timeZoneProvider, HttpClient httpClient,
|
||||
UnitProvider unitProvider, LocaleProvider localeProvider) {
|
||||
super(thing, timeZoneProvider, httpClient, unitProvider);
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Forecast handler initializing with configuration: {}",
|
||||
getConfigAs(WeatherCompanyForecastConfig.class).toString());
|
||||
|
||||
refreshIntervalSeconds = getConfigAs(WeatherCompanyForecastConfig.class).refreshInterval * 60;
|
||||
if (isValidLocation()) {
|
||||
weatherDataCache.clear();
|
||||
setLanguage();
|
||||
scheduleRefreshJob();
|
||||
updateStatus(isBridgeOnline() ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
cancelRefreshJob();
|
||||
updateStatus(ThingStatus.OFFLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command.equals(RefreshType.REFRESH)) {
|
||||
State state = weatherDataCache.get(channelUID.getId());
|
||||
if (state != null) {
|
||||
updateChannel(channelUID.getId(), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidLocation() {
|
||||
boolean validLocation = false;
|
||||
String locationType = getConfigAs(WeatherCompanyForecastConfig.class).locationType;
|
||||
if (locationType == null) {
|
||||
return validLocation;
|
||||
}
|
||||
switch (locationType) {
|
||||
case CONFIG_LOCATION_TYPE_POSTAL_CODE:
|
||||
String postalCode = StringUtils.trimToNull(getConfigAs(WeatherCompanyForecastConfig.class).postalCode);
|
||||
if (postalCode == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Postal code is not set");
|
||||
} else {
|
||||
locationQueryString = "&postalKey=" + postalCode.replace(" ", "");
|
||||
validLocation = true;
|
||||
}
|
||||
break;
|
||||
case CONFIG_LOCATION_TYPE_GEOCODE:
|
||||
String geocode = StringUtils.trimToNull(getConfigAs(WeatherCompanyForecastConfig.class).geocode);
|
||||
if (geocode == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Geocode is not set");
|
||||
} else {
|
||||
locationQueryString = "&geocode=" + geocode.replace(" ", "");
|
||||
validLocation = true;
|
||||
}
|
||||
break;
|
||||
case CONFIG_LOCATION_TYPE_IATA_CODE:
|
||||
String iataCode = StringUtils.trimToNull(getConfigAs(WeatherCompanyForecastConfig.class).iataCode);
|
||||
if (iataCode == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "IATA code is not set");
|
||||
} else {
|
||||
locationQueryString = "&iataCode=" + iataCode.replace(" ", "").toUpperCase();
|
||||
validLocation = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Location Type is not set");
|
||||
break;
|
||||
}
|
||||
return validLocation;
|
||||
}
|
||||
|
||||
private void setLanguage() {
|
||||
String language = StringUtils.trimToNull(getConfigAs(WeatherCompanyForecastConfig.class).language);
|
||||
if (language == null) {
|
||||
// Nothing in the thing config, so try to get a match from the openHAB locale
|
||||
String derivedLanguage = WeatherCompanyAbstractHandler.lookupLanguage(localeProvider.getLocale());
|
||||
languageQueryString = "&language=" + derivedLanguage;
|
||||
logger.debug("Language not set in thing config, using {}", derivedLanguage);
|
||||
} else {
|
||||
// Use what is set in the thing config
|
||||
languageQueryString = "&language=" + language;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Build the URL for requesting the 5-day forecast. It's important to request
|
||||
* the desired language AND units so that the forecast narrative contains
|
||||
* the consistent language and units (e.g. wind gusts to 30 mph).
|
||||
*/
|
||||
private String buildForecastUrl() {
|
||||
String apiKey = getApiKey();
|
||||
StringBuilder sb = new StringBuilder(BASE_FORECAST_URL);
|
||||
// Set response type as JSON
|
||||
sb.append("?format=json");
|
||||
// Set language from config
|
||||
sb.append(languageQueryString);
|
||||
// Set API key from config
|
||||
sb.append("&apiKey=").append(apiKey);
|
||||
// Set the units to Imperial or Metric
|
||||
sb.append("&units=").append(getUnitsQueryString());
|
||||
// Set the location from config
|
||||
sb.append(locationQueryString);
|
||||
String url = sb.toString();
|
||||
logger.debug("Forecast URL is {}", url.replace(apiKey, REPLACE_API_KEY));
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
private synchronized void refreshForecast() {
|
||||
if (!isBridgeOnline()) {
|
||||
// If bridge is not online, API has not been validated yet
|
||||
logger.debug("Handler: Can't refresh forecast because bridge is not online");
|
||||
return;
|
||||
}
|
||||
logger.debug("Handler: Requesting forecast from The Weather Company API");
|
||||
String response = executeApiRequest(buildForecastUrl());
|
||||
if (response == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
logger.trace("Handler: Parsing forecast response: {}", response);
|
||||
ForecastDTO forecast = gson.fromJson(response, ForecastDTO.class);
|
||||
logger.debug("Handler: Successfully parsed daily forecast response object");
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updateDailyForecast(forecast);
|
||||
updateDaypartForecast(forecast.daypart);
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.debug("Handler: Error parsing daily forecast response object", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error parsing daily forecast");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDailyForecast(ForecastDTO forecast) {
|
||||
for (int day = 0; day < forecast.dayOfWeek.length; day++) {
|
||||
logger.debug("Processing daily forecast for '{}'", forecast.dayOfWeek[day]);
|
||||
updateDaily(day, CH_DAY_OF_WEEK, undefOrString(forecast.dayOfWeek[day]));
|
||||
updateDaily(day, CH_NARRATIVE, undefOrString(forecast.narrative[day]));
|
||||
updateDaily(day, CH_VALID_TIME_LOCAL, undefOrDate(forecast.validTimeUtc[day]));
|
||||
updateDaily(day, CH_EXPIRATION_TIME_LOCAL, undefOrDate(forecast.expirationTimeUtc[day]));
|
||||
updateDaily(day, CH_TEMP_MAX, undefOrQuantity(forecast.temperatureMax[day], getTempUnit()));
|
||||
updateDaily(day, CH_TEMP_MIN, undefOrQuantity(forecast.temperatureMin[day], getTempUnit()));
|
||||
updateDaily(day, CH_PRECIP_RAIN, undefOrQuantity(forecast.qpf[day], getLengthUnit()));
|
||||
updateDaily(day, CH_PRECIP_SNOW, undefOrQuantity(forecast.qpfSnow[day], getLengthUnit()));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDaypartForecast(Object daypartObject) {
|
||||
DayPartDTO[] dayparts;
|
||||
try {
|
||||
String innerJson = gson.toJson(daypartObject);
|
||||
logger.debug("Parsing daypartsObject: {}", innerJson);
|
||||
dayparts = gson.fromJson(innerJson.toString(), DayPartDTO[].class);
|
||||
logger.debug("Handler: Successfully parsed daypart forecast object");
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.debug("Handler: Error parsing daypart forecast object: {}", e.getMessage(), e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error parsing daypart forecast");
|
||||
return;
|
||||
}
|
||||
logger.debug("There are {} daypart forecast entries", dayparts.length);
|
||||
if (dayparts.length == 0) {
|
||||
logger.debug("There is no daypart forecast object in this message");
|
||||
return;
|
||||
}
|
||||
logger.debug("There are {} daypartName entries in this forecast", dayparts[0].daypartName.length);
|
||||
for (int i = 0; i < dayparts[0].daypartName.length; i++) {
|
||||
// Note: All dayparts[0] (i.e. today day) values are null after 3 pm local time
|
||||
DayPartDTO dp = dayparts[0];
|
||||
// Even daypart indexes are Day (D); odd daypart indexes are Night (N)
|
||||
String dOrN = dp.dayOrNight[i] == null ? (i % 2 == 0 ? "D" : "N") : dp.dayOrNight[i];
|
||||
logger.debug("Processing daypart forecast for '{}'", dp.daypartName[i]);
|
||||
updateDaypart(i, dOrN, CH_DP_NAME, undefOrString(dp.daypartName[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_DAY_OR_NIGHT, undefOrString(dayparts[0].dayOrNight[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_NARRATIVE, undefOrString(dayparts[0].narrative[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_WX_PHRASE_SHORT, undefOrString(dayparts[0].wxPhraseShort[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_WX_PHRASE_LONG, undefOrString(dayparts[0].wxPhraseLong[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_QUALIFIER_PHRASE, undefOrString(dayparts[0].qualifierPhrase[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_QUALIFIER_CODE, undefOrString(dayparts[0].qualifierCode[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_TEMP, undefOrQuantity(dp.temperature[i], getTempUnit()));
|
||||
updateDaypart(i, dOrN, CH_DP_TEMP_HEAT_INDEX, undefOrQuantity(dp.temperatureHeatIndex[i], getTempUnit()));
|
||||
updateDaypart(i, dOrN, CH_DP_TEMP_WIND_CHILL, undefOrQuantity(dp.temperatureWindChill[i], getTempUnit()));
|
||||
updateDaypart(i, dOrN, CH_DP_HUMIDITY, undefOrQuantity(dp.relativeHumidity[i], SmartHomeUnits.PERCENT));
|
||||
updateDaypart(i, dOrN, CH_DP_CLOUD_COVER, undefOrQuantity(dp.cloudCover[i], SmartHomeUnits.PERCENT));
|
||||
updateDaypart(i, dOrN, CH_DP_PRECIP_CHANCE, undefOrQuantity(dp.precipChance[i], SmartHomeUnits.PERCENT));
|
||||
updateDaypart(i, dOrN, CH_DP_PRECIP_TYPE, undefOrString(dp.precipType[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_PRECIP_RAIN, undefOrQuantity(dp.qpf[i], getLengthUnit()));
|
||||
updateDaypart(i, dOrN, CH_DP_PRECIP_SNOW, undefOrQuantity(dp.qpfSnow[i], getLengthUnit()));
|
||||
updateDaypart(i, dOrN, CH_DP_SNOW_RANGE, undefOrString(dp.snowRange[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_WIND_SPEED, undefOrQuantity(dp.windSpeed[i], getSpeedUnit()));
|
||||
updateDaypart(i, dOrN, CH_DP_WIND_DIR_CARDINAL, undefOrString(dp.windDirectionCardinal[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_WIND_PHRASE, undefOrString(dp.windPhrase[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_WIND_DIR, undefOrQuantity(dp.windDirection[i], SmartHomeUnits.DEGREE_ANGLE));
|
||||
updateDaypart(i, dOrN, CH_DP_THUNDER_CATEGORY, undefOrString(dp.thunderCategory[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_THUNDER_INDEX, undefOrDecimal(dp.thunderIndex[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_UV_DESCRIPTION, undefOrString(dp.uvDescription[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_UV_INDEX, undefOrDecimal(dp.uvIndex[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_ICON_CODE, undefOrDecimal(dp.iconCode[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_ICON_CODE_EXTEND, undefOrDecimal(dp.iconCodeExtend[i]));
|
||||
updateDaypart(i, dOrN, CH_DP_ICON_IMAGE, getIconImage(dp.iconCode[i]));
|
||||
}
|
||||
}
|
||||
|
||||
private State getIconImage(Integer iconCode) {
|
||||
// First try to get the image associated with the icon code
|
||||
byte[] image = getImage("icons" + File.separator + String.format("%02d", iconCode) + ".png");
|
||||
if (image != null) {
|
||||
return new RawType(image, "image/png");
|
||||
}
|
||||
// Next try to get the N/A image
|
||||
image = getImage("icons" + File.separator + "na.png");
|
||||
if (image != null) {
|
||||
return new RawType(image, "image/png");
|
||||
}
|
||||
// Couldn't get any icon image, so set to UNDEF
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
private byte @Nullable [] getImage(String iconPath) {
|
||||
byte[] data = null;
|
||||
URL url = FrameworkUtil.getBundle(getClass()).getResource(iconPath);
|
||||
logger.trace("Path to icon image resource is: {}", url);
|
||||
if (url != null) {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
InputStream is = url.openStream();
|
||||
BufferedImage image = ImageIO.read(is);
|
||||
ImageIO.write(image, "png", out);
|
||||
out.flush();
|
||||
data = out.toByteArray();
|
||||
} catch (IOException e) {
|
||||
logger.debug("I/O exception occurred getting image data: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private void updateDaily(int day, String channelId, State state) {
|
||||
updateChannel(CH_GROUP_FORECAST_DAY + String.valueOf(day) + "#" + channelId, state);
|
||||
}
|
||||
|
||||
private void updateDaypart(int daypartIndex, String dayOrNight, String channelId, State state) {
|
||||
int day = daypartIndex / 2;
|
||||
String dON = dayOrNight.equals("D") ? CH_GROUP_FORECAST_DAYPART_DAY : CH_GROUP_FORECAST_DAYPART_NIGHT;
|
||||
updateChannel(CH_GROUP_FORECAST_DAY + String.valueOf(day) + dON + "#" + channelId, state);
|
||||
}
|
||||
|
||||
/*
|
||||
* The refresh job updates the daily forecast on the
|
||||
* refresh interval set in the thing config
|
||||
*/
|
||||
private void scheduleRefreshJob() {
|
||||
logger.debug("Handler: Scheduling forecast refresh job in {} seconds", REFRESH_JOB_INITIAL_DELAY_SECONDS);
|
||||
cancelRefreshJob();
|
||||
refreshForecastJob = scheduler.scheduleWithFixedDelay(refreshRunnable, REFRESH_JOB_INITIAL_DELAY_SECONDS,
|
||||
refreshIntervalSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void cancelRefreshJob() {
|
||||
if (refreshForecastJob != null) {
|
||||
refreshForecastJob.cancel(true);
|
||||
logger.debug("Handler: Canceling forecast refresh job");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* 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.weathercompany.internal.handler;
|
||||
|
||||
import static org.openhab.binding.weathercompany.internal.WeatherCompanyBindingConstants.*;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.weathercompany.internal.config.WeatherCompanyObservationsConfig;
|
||||
import org.openhab.binding.weathercompany.internal.model.PwsObservationsDTO;
|
||||
import org.openhab.binding.weathercompany.internal.model.PwsObservationsDTO.Observations;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
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.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link WeatherCompanyObservationsHandler} is responsible for pulling Personal
|
||||
* Weather Station (PWS) observations from the Weather Company API.
|
||||
*
|
||||
* API documentation is located here
|
||||
* - https://docs.google.com/document/d/1eKCnKXI9xnoMGRRzOL1xPCBihNV2rOet08qpE_gArAY/edit
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WeatherCompanyObservationsHandler extends WeatherCompanyAbstractHandler {
|
||||
private static final String BASE_PWS_URL = "https://api.weather.com/v2/pws/observations/current";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WeatherCompanyObservationsHandler.class);
|
||||
|
||||
private int refreshIntervalSeconds;
|
||||
|
||||
private @Nullable Future<?> refreshObservationsJob;
|
||||
|
||||
private final Runnable refreshRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refreshPwsObservations();
|
||||
}
|
||||
};
|
||||
|
||||
public WeatherCompanyObservationsHandler(Thing thing, TimeZoneProvider timeZoneProvider, HttpClient httpClient,
|
||||
UnitProvider unitProvider, LocaleProvider localeProvider) {
|
||||
super(thing, timeZoneProvider, httpClient, unitProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing observations handler with configuration: {}",
|
||||
getConfigAs(WeatherCompanyObservationsConfig.class).toString());
|
||||
|
||||
refreshIntervalSeconds = getConfigAs(WeatherCompanyObservationsConfig.class).refreshInterval * 60;
|
||||
weatherDataCache.clear();
|
||||
scheduleRefreshJob();
|
||||
updateStatus(isBridgeOnline() ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
cancelRefreshJob();
|
||||
updateStatus(ThingStatus.OFFLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command.equals(RefreshType.REFRESH)) {
|
||||
State state = weatherDataCache.get(channelUID.getId());
|
||||
if (state != null) {
|
||||
updateChannel(channelUID.getId(), state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Build the URL for requesting the PWS current observations
|
||||
*/
|
||||
private @Nullable String buildPwsUrl() {
|
||||
if (StringUtils.isEmpty(getConfigAs(WeatherCompanyObservationsConfig.class).pwsStationId)) {
|
||||
return null;
|
||||
}
|
||||
String apiKey = getApiKey();
|
||||
StringBuilder sb = new StringBuilder(BASE_PWS_URL);
|
||||
// Set to use Imperial units. UoM will convert to the other units
|
||||
sb.append("?units=e");
|
||||
// Get temperatures with one decimal point precision
|
||||
sb.append("&numericPrecision=decimal");
|
||||
// Set response type as JSON
|
||||
sb.append("&format=json");
|
||||
// Set PWS station Id from config
|
||||
sb.append("&stationId=").append(getConfigAs(WeatherCompanyObservationsConfig.class).pwsStationId);
|
||||
// Set API key from config
|
||||
sb.append("&apiKey=").append(apiKey);
|
||||
String url = sb.toString();
|
||||
logger.debug("PWS observations URL is {}", url.replace(apiKey, REPLACE_API_KEY));
|
||||
return url;
|
||||
}
|
||||
|
||||
private synchronized void refreshPwsObservations() {
|
||||
if (!isBridgeOnline()) {
|
||||
// If bridge is not online, API has not been validated yet
|
||||
logger.debug("Handler: Can't refresh PWS observations because bridge is not online");
|
||||
return;
|
||||
}
|
||||
logger.debug("Handler: Requesting PWS observations from The Weather Company API");
|
||||
String response = executeApiRequest(buildPwsUrl());
|
||||
if (response == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
logger.debug("Handler: Parsing PWS observations response: {}", response);
|
||||
PwsObservationsDTO pwsObservations = gson.fromJson(response, PwsObservationsDTO.class);
|
||||
logger.debug("Handler: Successfully parsed PWS observations response object");
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updatePwsObservations(pwsObservations);
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.debug("Handler: Error parsing pws observations response object: {}", e.getMessage(), e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error parsing PWS observations");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePwsObservations(PwsObservationsDTO pwsObservations) {
|
||||
if (pwsObservations.observations.length == 0) {
|
||||
logger.debug("Handler: PWS observation object contains no observations!");
|
||||
return;
|
||||
}
|
||||
Observations obs = pwsObservations.observations[0];
|
||||
logger.debug("Handler: Processing observations from station {} at {}", obs.stationID, obs.obsTimeLocal);
|
||||
updateChannel(CH_PWS_TEMP, undefOrQuantity(obs.imperial.temp, ImperialUnits.FAHRENHEIT));
|
||||
updateChannel(CH_PWS_TEMP_HEAT_INDEX, undefOrQuantity(obs.imperial.heatIndex, ImperialUnits.FAHRENHEIT));
|
||||
updateChannel(CH_PWS_TEMP_WIND_CHILL, undefOrQuantity(obs.imperial.windChill, ImperialUnits.FAHRENHEIT));
|
||||
updateChannel(CH_PWS_TEMP_DEW_POINT, undefOrQuantity(obs.imperial.dewpt, ImperialUnits.FAHRENHEIT));
|
||||
updateChannel(CH_PWS_HUMIDITY, undefOrQuantity(obs.humidity, SmartHomeUnits.PERCENT));
|
||||
updateChannel(CH_PWS_PRESSURE, undefOrQuantity(obs.imperial.pressure, ImperialUnits.INCH_OF_MERCURY));
|
||||
updateChannel(CH_PWS_PRECIPTATION_RATE,
|
||||
undefOrQuantity(obs.imperial.precipRate, SmartHomeUnits.INCHES_PER_HOUR));
|
||||
updateChannel(CH_PWS_PRECIPITATION_TOTAL, undefOrQuantity(obs.imperial.precipTotal, ImperialUnits.INCH));
|
||||
updateChannel(CH_PWS_WIND_SPEED, undefOrQuantity(obs.imperial.windSpeed, ImperialUnits.MILES_PER_HOUR));
|
||||
updateChannel(CH_PWS_WIND_GUST, undefOrQuantity(obs.imperial.windGust, ImperialUnits.MILES_PER_HOUR));
|
||||
updateChannel(CH_PWS_WIND_DIRECTION, undefOrQuantity(obs.winddir, SmartHomeUnits.DEGREE_ANGLE));
|
||||
updateChannel(CH_PWS_SOLAR_RADIATION, undefOrQuantity(obs.solarRadiation, SmartHomeUnits.IRRADIANCE));
|
||||
updateChannel(CH_PWS_UV, undefOrDecimal(obs.uv));
|
||||
updateChannel(CH_PWS_OBSERVATION_TIME_LOCAL, undefOrDate(obs.obsTimeUtc));
|
||||
updateChannel(CH_PWS_NEIGHBORHOOD, undefOrString(obs.neighborhood));
|
||||
updateChannel(CH_PWS_STATION_ID, undefOrString(obs.stationID));
|
||||
updateChannel(CH_PWS_COUNTRY, undefOrString(obs.country));
|
||||
updateChannel(CH_PWS_LOCATION, undefOrPoint(obs.lat, obs.lon));
|
||||
updateChannel(CH_PWS_ELEVATION, undefOrQuantity(obs.imperial.elev, ImperialUnits.FOOT));
|
||||
updateChannel(CH_PWS_QC_STATUS, undefOrDecimal(obs.qcStatus));
|
||||
updateChannel(CH_PWS_SOFTWARE_TYPE, undefOrString(obs.softwareType));
|
||||
}
|
||||
|
||||
/*
|
||||
* The refresh job updates the PWS current observations
|
||||
* on the refresh interval set in the thing config
|
||||
*/
|
||||
private void scheduleRefreshJob() {
|
||||
logger.debug("Handler: Scheduling observations refresh job in {} seconds", REFRESH_JOB_INITIAL_DELAY_SECONDS);
|
||||
cancelRefreshJob();
|
||||
refreshObservationsJob = scheduler.scheduleWithFixedDelay(refreshRunnable, REFRESH_JOB_INITIAL_DELAY_SECONDS,
|
||||
refreshIntervalSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void cancelRefreshJob() {
|
||||
if (refreshObservationsJob != null) {
|
||||
refreshObservationsJob.cancel(true);
|
||||
logger.debug("Handler: Canceling observations refresh job");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.weathercompany.internal.model;
|
||||
|
||||
/**
|
||||
* The {@link DayPartDTO} is the JSON object that contains the n-day forecast.
|
||||
*
|
||||
* The daypart object as well as the temperatureMax field will
|
||||
* appear as null in the API after 3:00 pm Local Apparent Time.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
public class DayPartDTO {
|
||||
/*
|
||||
* The name of a 12 hour daypart not including day names in the
|
||||
* first 48 hours (Today, Tonight)
|
||||
*/
|
||||
public String[] daypartName;
|
||||
|
||||
/*
|
||||
* Day or night indicator (D, N)
|
||||
*/
|
||||
public String[] dayOrNight;
|
||||
|
||||
/*
|
||||
* The narrative forecast for the daypart period
|
||||
*/
|
||||
public String[] narrative;
|
||||
|
||||
/*
|
||||
* Sensible weather phrase
|
||||
*/
|
||||
public String[] wxPhraseLong;
|
||||
|
||||
/*
|
||||
* Sensible weather phrase
|
||||
*/
|
||||
public String[] wxPhraseShort;
|
||||
|
||||
/*
|
||||
* The maximum temperature between 7am and 7pm for daytime temperature and the minimum temperature
|
||||
* between 7pm and 7am for night-time temperature. Minimum temperature also
|
||||
*/
|
||||
public Double[] temperature;
|
||||
|
||||
/*
|
||||
* Maximum heat index.
|
||||
* An apparent temperature. It represents what the air temperature “feels like” on exposed human skin
|
||||
* due to the combined effect of warm temperatures and high humidity.
|
||||
* When the temperature is 70°F or higher, the Feels Like value represents the computed Heat Index.
|
||||
* For temperatures between 40°F and 70°F, the Feels Like value and Temperature are the same,
|
||||
* regardless of wind speed and humidity, so use the Temperature value
|
||||
*/
|
||||
public Double[] temperatureHeatIndex;
|
||||
|
||||
/*
|
||||
* Minimum wind chill.
|
||||
* An apparent temperature. It represents what the air temperature “feels like” on exposed human skin
|
||||
* due to the combined effect of the cold temperatures and wind speed.
|
||||
* When the temperature is 61°F or lower the Feels Like value represents the computed Wind Chill so display the
|
||||
* Wind Chill value.
|
||||
* For temperatures between 61°F and 75°F, the Feels Like value and Temperature are the same, regardless
|
||||
* of wind speed and humidity, so display the Temperature value.
|
||||
*/
|
||||
public Double[] temperatureWindChill;
|
||||
|
||||
/*
|
||||
* The relative humidity of the air, which is defined as the ratio of the amount of water vapor
|
||||
* in the air to the amount of vapor required to bring the air to saturation at a constant
|
||||
* temperature. Relative humidity is always expressed as a percentage
|
||||
*/
|
||||
public Double[] relativeHumidity;
|
||||
|
||||
/*
|
||||
* Daytime average cloud cover expressed as a percentage
|
||||
*/
|
||||
public Integer[] cloudCover;
|
||||
|
||||
/*
|
||||
* The maximum forecasted wind speed.
|
||||
* The wind is treated as a vector; hence, winds must have direction and magnitude (speed).
|
||||
* The wind information reported in the hourly current conditions corresponds to a 10-minute average
|
||||
* called the sustained wind speed. Sudden or brief variations in the wind speed are known
|
||||
* as “wind gusts” and are reported in a separate data field. Wind directions are always expressed
|
||||
* as "from whence the wind blows" meaning that a North wind blows from North to South. If you face
|
||||
* North in a North wind the wind is at your face. Face southward and the North wind is at your back
|
||||
*/
|
||||
public Double[] windSpeed;
|
||||
|
||||
/*
|
||||
* Average wind direction in magnetic notation
|
||||
*/
|
||||
public Integer[] windDirection;
|
||||
|
||||
/*
|
||||
* Average wind direction in cardinal notation
|
||||
* N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW, CALM, VAR
|
||||
*/
|
||||
public String[] windDirectionCardinal;
|
||||
|
||||
/*
|
||||
* The phrase that describes the wind direction and speed for a 12 hour daypart
|
||||
*/
|
||||
public String[] windPhrase;
|
||||
|
||||
/*
|
||||
* Maximum probability of precipitation
|
||||
*/
|
||||
public Integer[] precipChance;
|
||||
|
||||
/*
|
||||
* Type of precipitation to display with the probability of precipitation
|
||||
* data element. (rain, snow, precip)
|
||||
*/
|
||||
public String[] precipType;
|
||||
|
||||
/*
|
||||
* The description of probability thunderstorm activity in an area for 12 hour daypart
|
||||
* 0 = "No thunder"; 1 = "Thunder possible"; 2 = "Thunder expected"; 3 = "Severe thunderstorms possible";
|
||||
* 4 = "Severe thunderstorms likely"; 5 = "High risk of severe thunderstorms"
|
||||
*/
|
||||
public String[] thunderCategory;
|
||||
|
||||
/*
|
||||
* The enumeration of thunderstorm probability within an area for a 12 hour daypart
|
||||
*/
|
||||
public Integer[] thunderIndex;
|
||||
|
||||
/*
|
||||
* The forecasted measurable precipitation (liquid or liquid equivalent) during 12 or 24 hour period
|
||||
*/
|
||||
public Double[] qpf;
|
||||
|
||||
/*
|
||||
* The forecasted measurable precipitation as snow during the 12 or 24 hour forecast period
|
||||
*/
|
||||
public Double[] qpfSnow;
|
||||
|
||||
/*
|
||||
* A phrase associated to the qualifier code describing special weather criteria
|
||||
*/
|
||||
public String[] qualifierPhrase;
|
||||
|
||||
/*
|
||||
* ????
|
||||
*/
|
||||
public String[] qualifierCode;
|
||||
|
||||
/*
|
||||
* Snow accumulation amount for the 12 hour forecast period
|
||||
*/
|
||||
public String[] snowRange;
|
||||
|
||||
/*
|
||||
* The UV Index Description which complements the UV Index value by providing an associated
|
||||
* level of risk of skin damage due to exposure.
|
||||
* -2 = Not Available, -1 = No Report, 0 to 2 = Low, 3 to 5 = Moderate,
|
||||
* 6 to 7 = High, 8 to 10 = Very High, 11 to 16 = Extreme
|
||||
*/
|
||||
public String[] uvDescription;
|
||||
|
||||
/*
|
||||
* Maximum UV index for the 12 hour forecast period
|
||||
*/
|
||||
public Integer[] uvIndex;
|
||||
|
||||
/*
|
||||
* This number is the key to the weather icon lookup. The data field shows the icon
|
||||
* number that is matched to represent the observed weather conditions
|
||||
*/
|
||||
public Integer[] iconCode;
|
||||
|
||||
/*
|
||||
* Code representing full set sensible weather
|
||||
*/
|
||||
public Integer[] iconCodeExtend;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* 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.weathercompany.internal.model;
|
||||
|
||||
/**
|
||||
* The {@link ForecastDTO} is the JSON object that contains the n-day forecast.
|
||||
*
|
||||
* The daypart object as well as the temperatureMax field OUTSIDE of the daypart object will
|
||||
* appear as null in the API after 3:00pm Local Apparent Time.
|
||||
*
|
||||
* Standard HTTP Cache-Control headers are used to define caching length. The TTL value is
|
||||
* provided in the HTTP Header as an absolute time value using the “Expires” parameter.
|
||||
* Example: “Expires: Fri, 12 Jul 2013 12:00:00 GMT”. The response provides a data element
|
||||
* expirationTimeUtc, this should be used to expire and remove a record from your system
|
||||
*
|
||||
* Translated fields:
|
||||
* - dayOfWeek
|
||||
* - daypartName
|
||||
* - moonPhase
|
||||
* - narrative
|
||||
* - qualifierPhrase
|
||||
* - uvDescription
|
||||
* - windDirectionCardinal
|
||||
* - windPhrase
|
||||
* - wxPhraseLong
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
public class ForecastDTO {
|
||||
/*
|
||||
* Day of week
|
||||
*/
|
||||
public String[] dayOfWeek;
|
||||
|
||||
/*
|
||||
* For the purposes of this product day(D) = 7am to 7pm and night(N) = 7pm to 7am
|
||||
*/
|
||||
public Object daypart;
|
||||
|
||||
/*
|
||||
* The narrative forecast for the 24-hour period.
|
||||
*/
|
||||
public String[] narrative;
|
||||
|
||||
/*
|
||||
* Daily maximum temperature
|
||||
*/
|
||||
public Double[] temperatureMax;
|
||||
|
||||
/*
|
||||
* Daily minimum temperature
|
||||
*/
|
||||
public Double[] temperatureMin;
|
||||
|
||||
/*
|
||||
* The forecasted measurable precipitation (liquid or liquid equivalent) during 12 or 24 hour period.
|
||||
*/
|
||||
public Double[] qpf;
|
||||
|
||||
/*
|
||||
* The forecasted measurable precipitation as snow during the 12 or 24 hour forecast period.
|
||||
*/
|
||||
public Double[] qpfSnow;
|
||||
|
||||
/*
|
||||
* Time forecast is valid in local apparent time.
|
||||
* ISO 8601 - YYYY-MM-DDTHH:MM:SS-NNNN; NNNN=GMT offset
|
||||
*/
|
||||
public String[] validTimeLocal;
|
||||
|
||||
/*
|
||||
* Time forecast is valid in UNIX seconds
|
||||
*/
|
||||
public Integer[] validTimeUtc;
|
||||
|
||||
/*
|
||||
* Expiration time in UNIX seconds
|
||||
*/
|
||||
public Integer[] expirationTimeUtc;
|
||||
|
||||
/*
|
||||
* The local time of the sunrise. It reflects any local daylight savings conventions.
|
||||
* For a few Arctic and Antarctic regions, the Sunrise and Sunset data values may be the
|
||||
* same (each with a value of 12:01am) to reflect conditions where a sunrise or sunset does not occur.
|
||||
* ISO 8601 - YYYY-MM-DDTHH:MM:SS-NNNN; NNNN=GMT offset
|
||||
*/
|
||||
public String[] sunriseTimeLocal;
|
||||
|
||||
/*
|
||||
* Sunrise time in UNIX epoch value
|
||||
*/
|
||||
public Integer[] sunriseTimeUtc;
|
||||
|
||||
/*
|
||||
* The local time of the sunset. It reflects any local daylight savings conventions.
|
||||
* For a few Arctic and Antarctic regions, the Sunrise and Sunset data values may be the
|
||||
* same (each with a value of 12:01am) to reflect conditions where a sunrise or sunset does not occur.
|
||||
* ISO 8601 - YYYY-MM-DDTHH:MM:SS-NNNN; NNNN=GMT offset
|
||||
*/
|
||||
public String[] sunsetTimeLocal;
|
||||
|
||||
/*
|
||||
* Sunset time in UNIX epoch value
|
||||
*/
|
||||
public Integer[] sunsetTimeUtc;
|
||||
|
||||
/*
|
||||
* Description phrase for the current lunar phase
|
||||
*/
|
||||
public String[] moonPhase;
|
||||
|
||||
/*
|
||||
* 3 character short code for lunar phases WNG, WXC, FQ, WNC, LQ, F, WXG, N()
|
||||
*/
|
||||
public String[] moonPhaseCode;
|
||||
|
||||
/*
|
||||
* Day number within monthly lunar cycle
|
||||
*/
|
||||
public Integer[] moonPhaseDay;
|
||||
|
||||
/*
|
||||
* First moonrise in local time. It reflects daylight savings time conventions
|
||||
* ISO 8601 - YYYY-MM-DDTHH:MM:SS-NNNN; NNNN=GMT offset
|
||||
*/
|
||||
public String[] moonriseTimeLocal;
|
||||
|
||||
/*
|
||||
* Moonrise time in UNIX epoch value
|
||||
*/
|
||||
public Integer[] moonriseTimeUtc;
|
||||
|
||||
/*
|
||||
* First Moonset in local time. It reflects daylight savings time conventions
|
||||
* ISO 8601 - YYYY-MM-DDTHH:MM:SS-NNNN; NNNN=GMT offset
|
||||
*/
|
||||
public String[] moonSetTimeLocal;
|
||||
|
||||
/*
|
||||
* Moonset time in UNIX epoch value
|
||||
*/
|
||||
public Integer[] moonsetTimeUtc;
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* 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.weathercompany.internal.model;
|
||||
|
||||
/**
|
||||
* The {@link PwsObservationsDTO} contains the most recent weather condition
|
||||
* observations from the Personl Weather Station (PWS).
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
public class PwsObservationsDTO {
|
||||
/*
|
||||
* An array of length 1 of observations that represent the
|
||||
* most recent PWS observations
|
||||
*/
|
||||
public Observations[] observations;
|
||||
|
||||
public class Observations {
|
||||
/*
|
||||
* Object containing fields that use a defined unit of measure.
|
||||
* The object label is dependent on the units parameter assigned
|
||||
* in the request
|
||||
*/
|
||||
public Imperial imperial;
|
||||
|
||||
/*
|
||||
* Two-character country code
|
||||
*/
|
||||
public String country;
|
||||
|
||||
/*
|
||||
* Time in UNIX seconds
|
||||
*/
|
||||
public long epoch;
|
||||
|
||||
/*
|
||||
* The relative humidity of the air
|
||||
*/
|
||||
public Double humidity;
|
||||
|
||||
/*
|
||||
* Latitude of the PWS
|
||||
*/
|
||||
public Double lat;
|
||||
|
||||
/*
|
||||
* Longitude of the PWS
|
||||
*/
|
||||
public Double lon;
|
||||
|
||||
/*
|
||||
* Neighborhood associated with the PWS location
|
||||
*/
|
||||
public String neighborhood;
|
||||
|
||||
/*
|
||||
* Time observation is valid in local apparent time by timezone - tz
|
||||
* Format "2019-03-06 17:44:44"
|
||||
*/
|
||||
public String obsTimeLocal;
|
||||
|
||||
/*
|
||||
* GMT (UTC) time
|
||||
* Format ISO 8601 - yyyy-MM-dd'T'HH:mm:ssZZ
|
||||
*/
|
||||
public String obsTimeUtc;
|
||||
|
||||
/*
|
||||
* Quality control indicator:
|
||||
* -1: No quality control check performed
|
||||
* 0: This observation was marked as possibly incorrect by our quality control algorithm
|
||||
* 1: This observation passed quality control checks
|
||||
*/
|
||||
public Integer qcStatus;
|
||||
|
||||
/*
|
||||
* Frequency of data report updates in minutes
|
||||
*/
|
||||
public Object realtimeFrequency;
|
||||
|
||||
/*
|
||||
* Software type of the PWS
|
||||
*/
|
||||
public String softwareType;
|
||||
|
||||
/*
|
||||
* Solar radiation in Watts/meter2
|
||||
*/
|
||||
public Double solarRadiation;
|
||||
|
||||
/*
|
||||
* ID as registered by wunderground.com
|
||||
*/
|
||||
public String stationID;
|
||||
|
||||
/*
|
||||
* UV reading of the intensity of solar radiation
|
||||
*/
|
||||
public Double uv;
|
||||
|
||||
/*
|
||||
* Wind direction in degrees
|
||||
*/
|
||||
public Integer winddir;
|
||||
}
|
||||
|
||||
public class Imperial {
|
||||
/*
|
||||
* The temperature which air must be cooled at constant pressure to reach saturation
|
||||
*/
|
||||
public Double dewpt;
|
||||
|
||||
/*
|
||||
* Elevation of the PWS in feet
|
||||
*/
|
||||
public Double elev;
|
||||
|
||||
/*
|
||||
* An apparent temperature. It represents what the air
|
||||
* temperature “feels like” on exposed human skin due to the combined effect
|
||||
* of warm temperatures and high humidity.
|
||||
*/
|
||||
public Double heatIndex;
|
||||
|
||||
/*
|
||||
* Instantaneous precipitation rate. How much rain would fall if the
|
||||
* precipitation intensity did not change for one hour
|
||||
*/
|
||||
public Double precipRate;
|
||||
|
||||
/*
|
||||
* Accumulated precipitation for today from midnight to present.
|
||||
*/
|
||||
public Double precipTotal;
|
||||
|
||||
/*
|
||||
* Mean Sea Level Pressure, the equivalent pressure reading at sea level recorded at this station
|
||||
*/
|
||||
public Double pressure;
|
||||
|
||||
/*
|
||||
* Temperature
|
||||
*/
|
||||
public Double temp;
|
||||
|
||||
/*
|
||||
* An apparent temperature. It represents what the air temperature “feels like” on exposed human
|
||||
* skin due to the combined effect of the cold temperatures and wind speed.
|
||||
*/
|
||||
public Double windChill;
|
||||
|
||||
/*
|
||||
* Sudden and temporary variations of the average Wind Speed.
|
||||
*/
|
||||
public Double windGust;
|
||||
|
||||
/*
|
||||
* The wind is treated as a vector; hence, winds must have direction and magnitude (speed).
|
||||
* The wind information reported in the hourly current conditions corresponds to
|
||||
* a 10-minute average called the sustained wind speed
|
||||
*/
|
||||
public Double windSpeed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="weathercompany" 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>WeatherCompany Binding</name>
|
||||
<description>This is the binding for the WeatherCompany weather forecast API.</description>
|
||||
<author>Mark Hilbush</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,145 @@
|
||||
<?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-description uri="thing-type:weathercompany:account">
|
||||
<parameter name="apiKey" type="text" required="true">
|
||||
<label>API Key</label>
|
||||
<description>Enter the API key</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="thing-type:weathercompany:weather-observations">
|
||||
<parameter name="pwsStationId" type="text" required="true">
|
||||
<label>PWS Station ID</label>
|
||||
<description>ID of your Personal Weather Station (e.g. KILCHICA52)</description>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" min="2" required="false" unit="min">
|
||||
<label>Refresh interval</label>
|
||||
<description>Specifies the refresh interval in minutes</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="thing-type:weathercompany:weather-forecast">
|
||||
<parameter name="locationType" type="text" required="true">
|
||||
<label>Location Type</label>
|
||||
<description>Location type (Postal Code, Geocode)</description>
|
||||
<default>postal</default>
|
||||
<options>
|
||||
<option value="postalCode">Postal Code</option>
|
||||
<option value="geocode">Geocode</option>
|
||||
<option value="iataCode">IATA Code</option>
|
||||
</options>
|
||||
</parameter>
|
||||
<parameter name="postalCode" type="text" required="false">
|
||||
<label>Postal code</label>
|
||||
<description>Postal code of form postal:country (e.g. 10001:US)</description>
|
||||
</parameter>
|
||||
<parameter name="geocode" type="text" required="false">
|
||||
<context>location</context>
|
||||
<label>Location</label>
|
||||
<description><![CDATA[
|
||||
<p>The location of this weather forecast.</p>
|
||||
<p>Coordinates as <latitude>,<longitude>[<altitude>]</p>
|
||||
<p>Example: "52.5200066,13.4049540" (Berlin)</p>
|
||||
]]>
|
||||
</description>
|
||||
</parameter>
|
||||
<parameter name="iataCode" type="text" required="false">
|
||||
<label>IATA Code</label>
|
||||
<description>IATA airport code (e.g. BWI)</description>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" min="2" required="false" unit="min">
|
||||
<label>Refresh interval</label>
|
||||
<description>Specifies the refresh interval in minutes</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="language" type="text" required="false">
|
||||
<label>Language</label>
|
||||
<description>Language to be used by the Weather Company service</description>
|
||||
<options>
|
||||
<option value="sq-AL">Albanian - (Albania)</option>
|
||||
<option value="ar-AE">Arabic - (United Arab Emirates)</option>
|
||||
<option value="az-AZ">Azerbaijani - (Azerbaijan)</option>
|
||||
<option value="bn-BD">Bengali, Bangla - (Bangladesh)</option>
|
||||
<option value="bn-IN">Bengali, Bangla - (India)</option>
|
||||
<option value="bs-BA">Bosnian - (Bosnia and Herzegovina)</option>
|
||||
<option value="bg-BG">Bulgarian - (Bulgaria)</option>
|
||||
<option value="ca-ES">Catalan - (Spain)</option>
|
||||
<option value="zh-CN">Chinese - (China)</option>
|
||||
<option value="zh-HK">Chinese - (Hong Kong)</option>
|
||||
<option value="zh-TW">Chinese - (Taiwan)</option>
|
||||
<option value="hr-HR">Croatian - (Croatia)</option>
|
||||
<option value="cs-CZ">Czech - (Czechia)</option>
|
||||
<option value="da-DK">Danish - (Denmark)</option>
|
||||
<option value="nl-NL">Dutch - (Netherlands)</option>
|
||||
<option value="en-GB">English (Great Britain)</option>
|
||||
<option value="en-IN">English - (India)</option>
|
||||
<option value="en-US">English - (United States of America)</option>
|
||||
<option value="et-EE">Estonian - (Estonia)</option>
|
||||
<option value="fi-FI">Finnish - (Finland)</option>
|
||||
<option value="fr-CA">French - (Canada)</option>
|
||||
<option value="fr-FR">French - (France)</option>
|
||||
<option value="ka-GE">Georgian - (Georgia)</option>
|
||||
<option value="de-DE">German - (Germany)</option>
|
||||
<option value="el-GR">Greek (modern) - (Greece)</option>
|
||||
<option value="gu-IN">Gujarati - (India)</option>
|
||||
<option value="he-IL">Hebrew (modern) - (Israel)</option>
|
||||
<option value="iw-IL">Hebrew - (Israel)</option>
|
||||
<option value="hi-IN">Hindi - (India)</option>
|
||||
<option value="hu-HU">Hungarian - (Hungary)</option>
|
||||
<option value="is-IS">Icelandic - (Iceland)</option>
|
||||
<option value="in-ID">Indonesian - (Indonesia)</option>
|
||||
<option value="it-IT">Italian - (Italy)</option>
|
||||
<option value="ja-JP">Japanese - (Japan)</option>
|
||||
<option value="jv-ID">Javanese - (Indonesia)</option>
|
||||
<option value="kn-IN">Kannada - (India)</option>
|
||||
<option value="kk-KZ">Kazakh - (Kazakhstan)</option>
|
||||
<option value="ko-KR">Korean - (South Korea)</option>
|
||||
<option value="lv-LV">Latvian - (Latvia)</option>
|
||||
<option value="lt-LT">Lithuanian - (Lithuania)</option>
|
||||
<option value="mk-MK">Macedonian - (Macedonia)</option>
|
||||
<option value="ms-MY">Malay - (Malaysia)</option>
|
||||
<option value="mn-MN">Mongolian - (Mongolia)</option>
|
||||
<option value="no-NO">Norwegian - (Norway)</option>
|
||||
<option value="fa-IR">Persian (Farsi) - (Iran)</option>
|
||||
<option value="pl-PL">Polish - (Poland)</option>
|
||||
<option value="pt-BR">Portuguese - (Brazil)</option>
|
||||
<option value="pt-PT">Portuguese - (Portugal)</option>
|
||||
<option value="ro-RO">Romanian - (Romania)</option>
|
||||
<option value="ru-RU">Russian - (Russia)</option>
|
||||
<option value="sr-BA">Serbian - (Bosnia and Herzegovina)</option>
|
||||
<option value="sr-ME">Serbian - (Montenegro)</option>
|
||||
<option value="sr-RS">Serbian - (Serbia)</option>
|
||||
<option value="si-LK">Sinhalese, Sinhala - (Sri Lanka)</option>
|
||||
<option value="sk-SK">Slovak - (Slovakia)</option>
|
||||
<option value="sl-SI">Slovenian - (Slovenia)</option>
|
||||
<option value="es-AR">Spanish - (Argentina)</option>
|
||||
<option value="es-ES">Spanish - (Spain)</option>
|
||||
<option value="es-LA">Spanish - (Latin America)</option>
|
||||
<option value="es-MX">Spanish - (Mexico)</option>
|
||||
<option value="es-UN">Spanish - (International)</option>
|
||||
<option value="es-US">Spanish - (United States of America)</option>
|
||||
<option value="sw-KE">Swahili - (Kenya)</option>
|
||||
<option value="sv-SE">Swedish - (Sweden)</option>
|
||||
<option value="tl-PH">Tagalog - (Philippines)</option>
|
||||
<option value="tg-TJ">Tajik - (Tajikistan)</option>
|
||||
<option value="ta-IN">Tamil - (India)</option>
|
||||
<option value="ta-LK">Tamil - (Sri Lanka)</option>
|
||||
<option value="te-IN">Telugu - (India)</option>
|
||||
<option value="th-TH">Thai - (Thailand)</option>
|
||||
<option value="tr-TR">Turkish - (Turkey)</option>
|
||||
<option value="tk-TM">Turkmen - (Turkmenistan)</option>
|
||||
<option value="uk-UA">Ukrainian - (Ukraine)</option>
|
||||
<option value="ur-PK">Urdu - (Pakistan)</option>
|
||||
<option value="uz-UZ">Uzbek - (Uzbekistan)</option>
|
||||
<option value="vi-VN">Vietnamese - (Viet Nam)</option>
|
||||
</options>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,7 @@
|
||||
# binding
|
||||
binding.weathercompany.name = Weather Company Binding
|
||||
binding.weathercompany.description = Weather forecast from The Weather Company
|
||||
|
||||
# thing types
|
||||
thing-type.weathercompany.weather.label = Weather
|
||||
thing-type.weathercompany.weather.description = Weather forecast from The Weather Company
|
||||
@@ -0,0 +1,512 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="weathercompany"
|
||||
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">
|
||||
|
||||
<bridge-type id="account">
|
||||
<label>Weather Company Account</label>
|
||||
<description>Represents an account at the Weather Company service</description>
|
||||
<config-description-ref uri="thing-type:weathercompany:account"/>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="weather-observations">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="account"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Weather Company Observations</label>
|
||||
<description>Current observations from Personal Weather Station</description>
|
||||
<channels>
|
||||
<channel id="neighborhood" typeId="neighborhood"/>
|
||||
<channel id="observationTimeLocal" typeId="observationTimeLocal"/>
|
||||
<channel id="currentTemperature" typeId="temperature">
|
||||
<label>Temperature</label>
|
||||
</channel>
|
||||
<channel id="currentTemperatureDewPoint" typeId="temperature">
|
||||
<label>Dew Point Temperature</label>
|
||||
</channel>
|
||||
<channel id="currentTemperatureHeatIndex" typeId="temperature">
|
||||
<label>Heat Index Temperature</label>
|
||||
</channel>
|
||||
<channel id="currentTemperatureWindChill" typeId="temperature">
|
||||
<label>Wind Chill Temperature</label>
|
||||
</channel>
|
||||
<channel id="currentHumidity" typeId="relativeHumidity">
|
||||
<label>Humidity</label>
|
||||
</channel>
|
||||
<channel id="currentPressure" typeId="pressure">
|
||||
<label>Pressure</label>
|
||||
</channel>
|
||||
<channel id="currentPrecipitationRate" typeId="precipitationRate">
|
||||
<label>Precipitation Rate</label>
|
||||
</channel>
|
||||
<channel id="currentPrecipitationTotal" typeId="precipitationTotal">
|
||||
<label>Precipitation Total</label>
|
||||
</channel>
|
||||
<channel id="currentSolarRadiation" typeId="solarRadiation">
|
||||
<label>Solar Radiation</label>
|
||||
</channel>
|
||||
<channel id="currentUv" typeId="uvIndex">
|
||||
<label>UV Index</label>
|
||||
</channel>
|
||||
<channel id="currentWindDirection" typeId="windDirection">
|
||||
<label>Wind Direction</label>
|
||||
</channel>
|
||||
<channel id="currentWindSpeed" typeId="windSpeed">
|
||||
<label>Wind Speed</label>
|
||||
</channel>
|
||||
<channel id="currentWindSpeedGust" typeId="windSpeed">
|
||||
<label>Wind Gust Speed</label>
|
||||
</channel>
|
||||
<channel id="stationId" typeId="stationId"/>
|
||||
<channel id="country" typeId="country"/>
|
||||
<channel id="location" typeId="location"/>
|
||||
<channel id="elevation" typeId="elevation"/>
|
||||
<channel id="qcStatus" typeId="qcStatus"/>
|
||||
<channel id="softwareType" typeId="softwareType"/>
|
||||
</channels>
|
||||
<config-description-ref uri="thing-type:weathercompany:weather-observations"/>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="weather-forecast">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="account"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Weather Company Forecast</label>
|
||||
<description>Weather Company weather forecast</description>
|
||||
<channel-groups>
|
||||
<!-- Daily forecasts -->
|
||||
<channel-group id="forecastDay0" typeId="forecastDay">
|
||||
<label>Weather Forecast Today</label>
|
||||
<description>This is the weather forecast for today</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay1" typeId="forecastDay">
|
||||
<label>Weather Forecast Tomorrow</label>
|
||||
<description>This is the weather forecast for tomorrow</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay2" typeId="forecastDay">
|
||||
<label>Weather Forecast Day 2</label>
|
||||
<description>This is the weather forecast in two days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay3" typeId="forecastDay">
|
||||
<label>Weather Forecast Day 3</label>
|
||||
<description>This is the weather forecast in three days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay4" typeId="forecastDay">
|
||||
<label>Weather Forecast Day 4</label>
|
||||
<description>This is the weather forecast in four days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay5" typeId="forecastDay">
|
||||
<label>Weather Forecast Day 5</label>
|
||||
<description>This is the weather forecast in five days</description>
|
||||
</channel-group>
|
||||
|
||||
<!-- Daytime and night-time forecasts -->
|
||||
<channel-group id="forecastDay0Day" typeId="forecastDaypart">
|
||||
<label>Daytime Weather Forecast for Today</label>
|
||||
<description>This is the daytime weather forecast for today</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay0Night" typeId="forecastDaypart">
|
||||
<label>Night-time Weather Forecast for Tonight</label>
|
||||
<description>This is the night-time weather forecast for tonight</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay1Day" typeId="forecastDaypart">
|
||||
<label>Daytime Weather Forecast for Tomorrow</label>
|
||||
<description>This is the daytime weather forecast for tomorrow</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay1Night" typeId="forecastDaypart">
|
||||
<label>Night-time Weather Forecast for Tomorrow</label>
|
||||
<description>This is the night-time weather forecast for tomorrow night</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay2Day" typeId="forecastDaypart">
|
||||
<label>Daytime Weather Forecast Day 2</label>
|
||||
<description>This is the daytime weather forecast in 2 days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay2Night" typeId="forecastDaypart">
|
||||
<label>Night-time Weather Forecast Day 2</label>
|
||||
<description>This is the night-time weather forecast in 2 days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay3Day" typeId="forecastDaypart">
|
||||
<label>Daytime Weather Forecast Day 3</label>
|
||||
<description>This is the daytime weather forecast in 3 days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay3Night" typeId="forecastDaypart">
|
||||
<label>Night-time Weather Forecast Day 3</label>
|
||||
<description>This is the night-time weather forecast in 3 days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay4Day" typeId="forecastDaypart">
|
||||
<label>Daytime Weather Forecast Day 3</label>
|
||||
<description>This is the daytime weather forecast in 4 days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay4Night" typeId="forecastDaypart">
|
||||
<label>Night-time Weather Forecast Day 4</label>
|
||||
<description>This is the night-time weather forecast in 4 days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay5Day" typeId="forecastDaypart">
|
||||
<label>Daytime Weather Forecast Day 5</label>
|
||||
<description>This is the daytime weather forecast in 5 days</description>
|
||||
</channel-group>
|
||||
<channel-group id="forecastDay5Night" typeId="forecastDaypart">
|
||||
<label>Night-time Weather Forecast Day 5</label>
|
||||
<description>This is the night-time weather forecast in 5 days</description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
<config-description-ref uri="thing-type:weathercompany:weather-forecast"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="forecastDay">
|
||||
<label>Weather Forecast Day</label>
|
||||
<description>This is the weather forecast</description>
|
||||
<channels>
|
||||
<channel id="dayOfWeek" typeId="dayOfWeek"/>
|
||||
<channel id="narrative" typeId="narrative"/>
|
||||
<channel id="temperatureMax" typeId="temperature">
|
||||
<label>Maximum Temperature</label>
|
||||
</channel>
|
||||
<channel id="temperatureMin" typeId="temperature">
|
||||
<label>Minimum Temperature</label>
|
||||
</channel>
|
||||
<channel id="precipitationRain" typeId="precipitationRain"/>
|
||||
<channel id="precipitationSnow" typeId="precipitationSnow"/>
|
||||
<channel id="validTimeLocal" typeId="validTimeLocal"/>
|
||||
<channel id="expirationTimeLocal" typeId="expirationTimeLocal"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="forecastDaypart">
|
||||
<label>Weather Forecast Day and Night</label>
|
||||
<description>This is the weather forecast for day and night</description>
|
||||
<channels>
|
||||
<channel id="daypartName" typeId="daypartName"/>
|
||||
<channel id="dayOrNight" typeId="dayOrNight"/>
|
||||
<channel id="narrative" typeId="narrative"/>
|
||||
<channel id="qualifierCode" typeId="qualifierCode"/>
|
||||
<channel id="qualifierPhrase" typeId="qualifierPhrase"/>
|
||||
<channel id="wxPhraseShort" typeId="wxPhraseShort"/>
|
||||
<channel id="wxPhraseLong" typeId="wxPhraseLong"/>
|
||||
<channel id="temperature" typeId="temperature">
|
||||
<label>Temperature</label>
|
||||
</channel>
|
||||
<channel id="temperatureHeatIndex" typeId="temperature">
|
||||
<label>Heat Index</label>
|
||||
</channel>
|
||||
<channel id="temperatureWindChill" typeId="temperature">
|
||||
<label>Wind Chill</label>
|
||||
</channel>
|
||||
<channel id="relativeHumidity" typeId="relativeHumidity"/>
|
||||
<channel id="windSpeed" typeId="windSpeed"/>
|
||||
<channel id="windDirection" typeId="windDirection"/>
|
||||
<channel id="windDirectionCardinal" typeId="windDirectionCardinal"/>
|
||||
<channel id="windPhrase" typeId="windPhrase"/>
|
||||
<channel id="cloudCover" typeId="cloudCover"/>
|
||||
<channel id="precipitationChance" typeId="precipitationChance"/>
|
||||
<channel id="precipitationType" typeId="precipitationType"/>
|
||||
<channel id="precipitationRain" typeId="precipitationRain"/>
|
||||
<channel id="precipitationSnow" typeId="precipitationSnow"/>
|
||||
<channel id="snowRange" typeId="snowRange"/>
|
||||
<channel id="uvDescription" typeId="uvDescription"/>
|
||||
<channel id="uvIndex" typeId="uvIndex"/>
|
||||
<channel id="thunderCategory" typeId="thunderCategory"/>
|
||||
<channel id="thunderIndex" typeId="thunderIndex"/>
|
||||
<channel id="iconCode" typeId="iconCode"/>
|
||||
<channel id="iconCodeExtend" typeId="iconCodeExtend"/>
|
||||
<channel id="iconImage" typeId="iconImage"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="cloudCover">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Cloud Cover</label>
|
||||
<description>Cloud cover</description>
|
||||
<state readOnly="true" pattern="%.0f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="country" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Country</label>
|
||||
<description>Country where PWS is located</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dayOfWeek">
|
||||
<item-type>String</item-type>
|
||||
<label>Day of Week</label>
|
||||
<description>Day of week</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dayOrNight" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Day or Night Name</label>
|
||||
<description>Day or night</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="daypartName">
|
||||
<item-type>String</item-type>
|
||||
<label>Daypart Name</label>
|
||||
<description>Daypart name</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="expirationTimeLocal" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Expiration Time</label>
|
||||
<description>Expiration time</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="iconCode" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Icon Code</label>
|
||||
<description>Icon code</description>
|
||||
<state readOnly="true" pattern="%.0f"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="iconCodeExtend" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Icon Code Extend</label>
|
||||
<description>Icon code extend</description>
|
||||
<state readOnly="true" pattern="%.0f"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="iconImage">
|
||||
<item-type>Image</item-type>
|
||||
<label>Icon Image</label>
|
||||
<description>Icon image</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="location" advanced="true">
|
||||
<item-type>Location</item-type>
|
||||
<label>Weather Station Location</label>
|
||||
<description>Latitude and longitude of weather station</description>
|
||||
<state readOnly="true" pattern="%2$s°N, %3$s°W"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="elevation" advanced="true">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Weather Station Elevation</label>
|
||||
<description>Elevation above ground of weather station</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="narrative">
|
||||
<item-type>String</item-type>
|
||||
<label>Narrative</label>
|
||||
<description>The narrative forecast for the 24-hour period</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="neighborhood">
|
||||
<item-type>String</item-type>
|
||||
<label>Neighborhood</label>
|
||||
<description>Neighborhood</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="observationTimeLocal" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Observation Time</label>
|
||||
<description>Local time when conditions were observed</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="precipitationChance">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Precipitation Chance</label>
|
||||
<description>Chance of precipitation</description>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="precipitationRain">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Precipitation Rain</label>
|
||||
<description>Forecasted measurable liquid precipitation during 24 hour period</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="precipitationRate">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Precipitation Rate</label>
|
||||
<description>Precipitation rate</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.7f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="precipitationSnow">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Precipitation Snow</label>
|
||||
<description>Forecasted measurable snow precipitation during 24 hour period</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="precipitationTotal">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Precipitation Total</label>
|
||||
<description>Precipitation total</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="precipitationType">
|
||||
<item-type>String</item-type>
|
||||
<label>Precipitation Type</label>
|
||||
<description>Precipitation type</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="pressure">
|
||||
<item-type>Number:Pressure</item-type>
|
||||
<label>Barometric Pressure</label>
|
||||
<description>Barometric Pressure</description>
|
||||
<category>Pressure</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="qcStatus" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>QC Status</label>
|
||||
<description>QC status</description>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="qualifierPhrase" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Qualifier Phrase</label>
|
||||
<description>Qualifier phrase</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="qualifierCode" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Qualifier Code</label>
|
||||
<description>Qualifier code</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="relativeHumidity">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Relative Humidity</label>
|
||||
<description>Forecasted relative humidity</description>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="snowRange">
|
||||
<item-type>String</item-type>
|
||||
<label>Snow Range</label>
|
||||
<description>Snow range</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="softwareType" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Software Type</label>
|
||||
<description>Software type</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="solarRadiation">
|
||||
<item-type>Number:Intensity</item-type>
|
||||
<label>Solar Radiation</label>
|
||||
<description>Solar radiation</description>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="stationId">
|
||||
<item-type>String</item-type>
|
||||
<label>Station Id</label>
|
||||
<description>Station Id</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Forecasted temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="thunderCategory" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Thunder Category</label>
|
||||
<description>Thunder category</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="thunderIndex" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Thunder Index</label>
|
||||
<description>Thunder index</description>
|
||||
<state readOnly="true" pattern="%.0f"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="uvDescription">
|
||||
<item-type>String</item-type>
|
||||
<label>UV Description</label>
|
||||
<description>UV description</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="uvIndex" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>UV Index</label>
|
||||
<description>UV index</description>
|
||||
<state readOnly="true" pattern="%.0f"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="validTimeLocal" advanced="true">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Valid Time</label>
|
||||
<description>Time forecast is valid in local apparent time</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirection" advanced="true">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Wind Direction</label>
|
||||
<description>Wind direction</description>
|
||||
<state readOnly="true" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirectionCardinal">
|
||||
<item-type>String</item-type>
|
||||
<label>Wind Direction Cardinal</label>
|
||||
<description>Wind direction cardinal</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windPhrase">
|
||||
<item-type>String</item-type>
|
||||
<label>Wind Phrase</label>
|
||||
<description>Wind phrase</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windSpeed">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Wind Speed</label>
|
||||
<description>Wind speed</description>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="wxPhraseLong">
|
||||
<item-type>String</item-type>
|
||||
<label>Wx Phrase Long</label>
|
||||
<description>Wx phrase long</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="wxPhraseShort" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Wx Phrase Short</label>
|
||||
<description>Wx phrase short</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.7 KiB |