added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
33
bundles/org.openhab.binding.ambientweather/.classpath
Normal file
33
bundles/org.openhab.binding.ambientweather/.classpath
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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.ambientweather/.project
Normal file
23
bundles/org.openhab.binding.ambientweather/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.ambientweather</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>
|
||||
40
bundles/org.openhab.binding.ambientweather/NOTICE
Normal file
40
bundles/org.openhab.binding.ambientweather/NOTICE
Normal file
@@ -0,0 +1,40 @@
|
||||
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
|
||||
|
||||
engine.io-client
|
||||
* License: MIT License
|
||||
* Project: https://socketio.github.io/engine.io-client-java/
|
||||
* Source: https://github.com/socketio/engine.io-client-java
|
||||
|
||||
json
|
||||
* License: JSON License
|
||||
* Project: https://github.com/stleary/JSON-java
|
||||
* Source: https://github.com/stleary/JSON-java
|
||||
|
||||
okhttp
|
||||
* License: Apache License 2.0
|
||||
* Project: https://square.github.io/okhttp/
|
||||
* Source: https://github.com/square/okhttp
|
||||
|
||||
okio
|
||||
* License: Apache License 2.0
|
||||
* Project: https://github.com/square/okio
|
||||
* Source: https://github.com/square/okio
|
||||
|
||||
socket.io-client
|
||||
* License: MIT License
|
||||
* Project: https://socketio.github.io/socket.io-client-java/
|
||||
* Source: https://github.com/socketio/socket.io-client-java
|
||||
259
bundles/org.openhab.binding.ambientweather/README.md
Normal file
259
bundles/org.openhab.binding.ambientweather/README.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# Ambient Weather Binding
|
||||
|
||||
The Ambient Weather binding integrates weather station data that's stored on the Ambient Weather online service.
|
||||
The binding uses Ambient Weather's real-time API, so updates from weather stations are delivered to the binding in near real-time.
|
||||
|
||||
## Supported Things
|
||||
|
||||
The binding currently supports weather data from these weather stations.
|
||||
|
||||
| Thing | ID |
|
||||
|--------------------------|-----------|
|
||||
| Account | bridge |
|
||||
| WS-0900-IP | ws0900ip |
|
||||
| WS-1400-IP / WS-1401-IP | ws1400ip |
|
||||
| WS-2902A | ws2902a |
|
||||
| WS-8482 | ws8482 |
|
||||
| WS-0265 | ws0265 |
|
||||
|
||||
Other stations can be added relatively easily with changes in just several places in the source code.
|
||||
|
||||
## Discovery
|
||||
|
||||
Automatic discovery is currently not supported due to the lack of weather station model information in the Ambient Weather API.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
### Account
|
||||
|
||||
| Parameter | Parameter ID | Required/Optional | Description |
|
||||
|------------------|-------------------|-------------------|-------------|
|
||||
| API Key | apiKey | Required | Obtain the API key on the *My Account* page of your `ambientweather.net` dashboard. |
|
||||
| Application Key | apiKey | Required | Obtain the Application key on the *My Account* page of your `ambientweather.net` dashboard. |
|
||||
|
||||
### Weather Station
|
||||
|
||||
| Parameter | Parameter ID | Required/Optional | Description |
|
||||
|------------------|-------------------|-------------------|-------------|
|
||||
| MAC Address | macAddress | Required | This is the weather station's MAC address. It must be configured in your `ambientweather.net` dashboard. |
|
||||
|
||||
## Channels
|
||||
|
||||
The following channels are supported by the binding. Note that specific weather station models may support only some or all of these channels.
|
||||
|
||||
| Channel Group ID | Channel ID | Item Type | Description |
|
||||
|------------------------------|---------------------------------|-------------------------|---------------------------------------------------------------|
|
||||
| station | name | String | Station name given by the user |
|
||||
| station | location | String | Location of the station given by the user |
|
||||
| | | | |
|
||||
| weatherData\<station-type\> | observationTime | DateTime | Time that the conditions were observed |
|
||||
| weatherData\<station-type\> | batteryIndicator | String | Battery condition indicator (1 Good, 0 Not good) |
|
||||
| weatherData\<station-type\> | temperature | Number:Temperature | Actual observed temperature |
|
||||
| weatherData\<station-type\> | feelingTemperature | Number:Temperature | "Real feel" temperature |
|
||||
| weatherData\<station-type\> | dewPoint | Number:Temperature | Dew point |
|
||||
| weatherData\<station-type\> | relativeHumidity | Number:Dimensionless | Relative humidity |
|
||||
| weatherData\<station-type\> | pressureAbsolute | Number:Pressure | Absolute barometric pressure |
|
||||
| weatherData\<station-type\> | pressureRelative | Number:Pressure | Relative barometric pressure |
|
||||
| weatherData\<station-type\> | pressureTrend | String | 3-hour trend (FALLING RAPIDLY ... STEADY ... RISING RAPIDLY |
|
||||
| weatherData\<station-type\> | windSpeed | Number:Speed | Wind speed |
|
||||
| weatherData\<station-type\> | windDirectionDegrees | Number:Angle | Wind direction in degrees |
|
||||
| weatherData\<station-type\> | windDirection | String | Wind direction |
|
||||
| weatherData\<station-type\> | windSpeedMaxDaily | Number:Speed | Maximum daily wind speed |
|
||||
| weatherData\<station-type\> | windGust | Number:Speed | Wind gust |
|
||||
| weatherData\<station-type\> | windGustMaxDaily | Number:Speed | Maximum daily wind gust |
|
||||
| weatherData\<station-type\> | windSpeedAvg2Minute | Number:Speed | Two-minute wind speed average |
|
||||
| weatherData\<station-type\> | windDirectionDegreesAvg2Min | Number:Angle | Two-minute wind direction average |
|
||||
| weatherData\<station-type\> | windDirectionAvg2Min | String | Two-minute wind direction average |
|
||||
| weatherData\<station-type\> | windSpeedAvg10Minute | Number:Speed | Ten-minute wind speed average |
|
||||
| weatherData\<station-type\> | windDirectionDegreesAvg10Min | Number:Angle | Ten-minute wind direction average |
|
||||
| weatherData\<station-type\> | windDirectionAvg10Min | String | Ten-minute wind direction average |
|
||||
| weatherData\<station-type\> | rainHourlyRate | Number:Speed | Hourly rate of rainfall |
|
||||
| weatherData\<station-type\> | rainDay | Number:Length | Rainfall amount for the day |
|
||||
| weatherData\<station-type\> | rainWeek | Number:Length | Rainfall amount for the week |
|
||||
| weatherData\<station-type\> | rainMonth | Number:Length | Rainfall amount for the month |
|
||||
| weatherData\<station-type\> | rainYear | Number:Length | Rainfall amount for the year |
|
||||
| weatherData\<station-type\> | rainTotal | Number:Length | Rainfall amount since last weather station reset |
|
||||
| weatherData\<station-type\> | rainEvent | Number:Length | Rainfall for most recent rain event |
|
||||
| weatherData\<station-type\> | rainLastTime | DateTime | Last time it rained |
|
||||
| weatherData\<station-type\> | solarRadiation | Number:Intensity | Solar radiation |
|
||||
| weatherData\<station-type\> | uvIndex | Number:Dimensionless | UV index |
|
||||
| | | | |
|
||||
| indoorSensor | temperature | Number:Temperature | Temperature |
|
||||
| indoorSensor | humidity | Number:Dimensionless | Humidity |
|
||||
| indoorSensor | batteryIndicator | String | Battery indicator |
|
||||
| | | | |
|
||||
| remoteSensor\<1-10\> | temperature | Number:Temperature | Temperature |
|
||||
| remoteSensor\<1-10\> | dewPoint | Number:Temperature | Dew Point |
|
||||
| remoteSensor\<1-10\> | feelingTemperature | Number:Temperature | "Real feel" temperature |
|
||||
| remoteSensor\<1-10\> | humidity | Number:Dimensionless | Humidity |
|
||||
| remoteSensor\<1-10\> | batteryIndicator | String | Battery indicator |
|
||||
| remoteSensor\<1-10\> | co2 | Number:Dimensionless | Carbon Dioxide level |
|
||||
| remoteSensor\<1-10\> | relay | Switch | Relay |
|
||||
| remoteSensor\<1-10\> | soilTemperature | Number:Temperature | Soil temperature |
|
||||
| remoteSensor\<1-10\> | soilMoisture | Number:Dimensionless | Soil moisture |
|
||||
| remoteSensor\<1-10\> | soilMoistureLevel | String | Soil moisture level (VERY DRY ... VERY WET) |
|
||||
|
||||
## Example
|
||||
|
||||
### Things
|
||||
|
||||
```
|
||||
Bridge ambientweather:bridge:account "Ambient Weather Account" [ applicationKey="bd7eb3fe87f74e9.....", apiKey="efe88d6202be43e6a40....." ] {
|
||||
Thing ws1400ip 1400 "Ambient Weather WS-1400-IP" [ macAddress="00:ab:cd:00:00:01" ]
|
||||
Thing ws8482 8482 "Ambient Weather WS-8482" [ macAddress="00:ab:cd:00:00:02" ]
|
||||
}
|
||||
```
|
||||
|
||||
### Items
|
||||
|
||||
```
|
||||
// WS-1400-IP Weather Station
|
||||
String WS1400IP_StationName "Station Name [%s]" { channel="ambientweather:ws1400ip:account:1400:station#name" }
|
||||
String WS1400IP_StationLocation "Station Location [%s]" { channel="ambientweather:ws1400ip:account:1400:station#location" }
|
||||
|
||||
DateTime WS1400IP_ObservationTime "Station Observation Time [%s]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#observationTime" }
|
||||
String WS1400IP_StationBattery "Station Battery [MAP(ambient-battery.map):%s]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#batteryIndicator" }
|
||||
Number:Temperature WS1400IP_Temperature "Temperature [%.1f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#temperature" }
|
||||
Number:Temperature WS1400IP_DewPoint "Dew Point [%.1f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#dewPoint" }
|
||||
Number:Temperature WS1400IP_RealFeel "RealFeel [%.1f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#feelingTemperature" }
|
||||
Number:Dimensionless WS1400IP_Humidity "Humidity [%.1f %%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#relativeHumidity" }
|
||||
Number:Pressure WS1400IP_PressureAbsolute "Pressure Absolute [%.1f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#pressureAbsolute" }
|
||||
Number:Pressure WS1400IP_PressureRelative "Pressure Relative [%.1f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#pressureRelative" }
|
||||
String WS1400IP_PressureTrend "Pressure Trend [%s]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#pressureTrend" }
|
||||
|
||||
Number:Speed WS1400IP_WindSpeed "Wind Speed [%.0f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#windSpeed" }
|
||||
Number:Angle WS1400IP_WindDirectionDegrees "Wind Direction Degrees [%d %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#windDirectionDegrees" }
|
||||
String WS1400IP_WindDirection "Wind Direction [%s]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#windDirection" }
|
||||
Number:Speed WS1400IP_WindGust "Wind Gust [%.0f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#windGust" }
|
||||
Number:Speed WS1400IP_WindGustDailyMax "Wind Gust Max Daily [%.0f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#windGustMaxDaily" }
|
||||
|
||||
// Use this if your units are SI
|
||||
Number:Speed WS1400IP_RainHourlyRate "Rain Hourly Rate [%.1f mm/h]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#rainHourlyRate" }
|
||||
// Use this if your units are Imperial
|
||||
Number:Speed WS1400IP_RainHourlyRate "Rain Hourly Rate [%.2f in/h]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#rainHourlyRate" }
|
||||
|
||||
Number:Length WS1400IP_RainDaily "Rain Daily [%.2f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#rainDay" }
|
||||
Number:Length WS1400IP_RainWeekly "Rain Weekly [%.2f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#rainWeek" }
|
||||
Number:Length WS1400IP_RainMonthly "Rain Monthly [%.2f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#rainMonth" }
|
||||
Number:Length WS1400IP_RainYearly "Rain Yearly [%.2f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#rainYear" }
|
||||
Number:Length WS1400IP_RainTotal "Rain Total [%.2f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#rainTotal" }
|
||||
Number:Length WS1400IP_RainEvent "Rain Event [%.2f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#rainEvent" }
|
||||
DateTime WS1400IP_RainLastTime "Rain Last Time [%s]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#rainLastTime" }
|
||||
|
||||
Number:Intensity WS1400IP_SolarRadiation "Solar Radiation [%.0f %unit%]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#solarRadiation" }
|
||||
Number WS1400IP_UVIndex "UV Index [%.0f]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#uvIndex" }
|
||||
String WS1400IP_UVDanger "UV Danger Level [%s]" { channel="ambientweather:ws1400ip:account:1400:weatherDataWs1400ip#uvDanger" }
|
||||
|
||||
Number:Temperature WS1400IP_IndoorTemperature "Indoor Temperature [%.1f %unit%]" { channel="ambientweather:ws1400ip:account:1400:indoorSensor#temperature" }
|
||||
Number:Dimensionless WS1400IP_IndoorHumidity "Indoor Humidity [%.1f %%]" { channel="ambientweather:ws1400ip:account:1400:indoorSensor#relativeHumidity" }
|
||||
String WS1400IP_IndoorBattery "Indoor Battery [MAP(ambient-battery.map):%s]" { channel="ambientweather:ws1400ip:account:1400:indoorSensor#batteryIndicator" }
|
||||
|
||||
|
||||
// WS-8482 Weather Station
|
||||
String WS8482_StationName "Station Name [%s]" { channel="ambientweather:ws8482:ws8482:station#name" }
|
||||
String WS8482_StationLocation "Station Location [%s]" { channel="ambientweather:ws8482:ws8482:station#location" }
|
||||
|
||||
DateTime WS8482_ObservationTime "Observation Time [%s]" { channel="ambientweather:ws8482:account:8482:weatherDataWs8482#observationTime" }
|
||||
Number:Temperature WS8482_IndoorTemperature "Indoor Temperature [%.1f %unit%]" { channel="ambientweather:ws8482:account:8482:weatherDataWs8482#temperature" }
|
||||
Number:Dimensionless WS8482_IndoorHumidity "Indoor Humidity [%.1f %%]" { channel="ambientweather:ws8482:account:8482:weatherDataWs8482#relativeHumidity" }
|
||||
String WS8482_StationBattery "Station Battery [MAP(ambient-battery.map):%s]" { channel="ambientweather:ws8482:account:8482:weatherDataWs8482#batteryIndicator" }
|
||||
|
||||
Number:Temperature WS8482_RemoteTemperature "Remote Temperature [%.1f %unit%]" { channel="ambientweather:ws8482:account:8482:remoteSensor1#temperature" }
|
||||
Number:Dimensionless WS8482_RemoteHumidity "Remote Humidity [%.1f %%]" { channel="ambientweather:ws8482:account:8482:remoteSensor1#relativeHumidity" }
|
||||
String WS8482_RemoteBattery "Remote Battery [MAP(ambient-battery.map):%s]" { channel="ambientweather:ws8482:account:8482:remoteSensor1#batteryIndicator" }
|
||||
|
||||
Number:Temperature WS8482_SoilTemperature "Soil Temperature [%.1f %unit%]" { channel="ambientweather:ws8482:account:8482:remoteSensor2#soilTemperature" }
|
||||
Number:Dimensionless WS8482_SoilMoisture "Soil Moisture [%.1f %%]" { channel="ambientweather:ws8482:account:8482:remoteSensor2#soilMoisture" }
|
||||
String WS8482_SoilMoistureLevel "Soil Moisture Level [%s]" { channel="ambientweather:ws8482:account:8482:remoteSensor2#soilMoistureLevel" }
|
||||
String WS8482_SoilSensorBattery "Remote Battery [MAP(ambient-battery.map):%s]" { channel="ambientweather:ws8482:account:8482:remoteSensor2#batteryIndicator" }
|
||||
```
|
||||
|
||||
### Transforms
|
||||
|
||||
#### File ambient-battery.map
|
||||
|
||||
```
|
||||
-=UNKNOWN
|
||||
NULL=UNKNOWN
|
||||
1=GOOD
|
||||
0=REPLACE
|
||||
```
|
||||
|
||||
### Sitemap
|
||||
|
||||
```
|
||||
Text label="Weather Station" icon="sun_clouds" {
|
||||
Frame {
|
||||
Text item=WS1400IP_ObservationTime label="Observation Time [%1$tm/%1$td %1$tl:%1$tM %1$tp]"
|
||||
}
|
||||
Frame {
|
||||
Text item=WS1400IP_Temperature
|
||||
Text item=WS1400IP_RealFeel
|
||||
Text item=WS1400IP_DewPoint
|
||||
Text item=WS1400IP_Humidity
|
||||
Text item=WS1400IP_PressureRelative
|
||||
Text item=WS1400IP_PressureTrend
|
||||
}
|
||||
Frame {
|
||||
Text item=WS1400IP_WindSpeed
|
||||
Text item=WS1400IP_WindDirection
|
||||
Text item=WS1400IP_WindGust
|
||||
Text item=WS1400IP_WindGustDailyMax
|
||||
}
|
||||
Frame {
|
||||
Text item=WS1400IP_SolarRadiation
|
||||
Text item=WS1400IP_SolarRadiationDailyMax
|
||||
Text item=WS1400IP_UVDanger
|
||||
}
|
||||
Frame {
|
||||
Text item=WS1400IP_RainHourlyRate
|
||||
Text item=WS1400IP_RainDaily
|
||||
Text item=WS1400IP_RainWeekly
|
||||
Text item=WS1400IP_RainMonthly
|
||||
Text item=WS1400IP_RainYearly
|
||||
Text item=WS1400IP_RainTotal
|
||||
Text item=WS1400IP_RainEvent
|
||||
Text item=WS1400IP_RainLastTime label="Last Rain [%1$tm/%1$td %1$tl:%1$tM %1$tp]"
|
||||
}
|
||||
Frame {
|
||||
Text item=WS1400IP_IndoorTemperature
|
||||
Text item=WS1400IP_IndoorHumidity
|
||||
}
|
||||
Frame {
|
||||
Text item=WS1400IP_StationBattery
|
||||
Text item=WS1400IP_IndoorBattery
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How To Add Another Weather Station Type
|
||||
|
||||
Adding support for a new weather station type involves changes to the source code in just a few places.
|
||||
|
||||
#### Add a New Thing Type in AmbientWeatherBindingConstants.java
|
||||
|
||||
Define a new `ThingTypeUID` for the new station and add it to the `SUPPORTED_THING_TYPES_UIDS` Collection.
|
||||
|
||||
#### Create OH-INF/thing/\<station-model\>.xml
|
||||
|
||||
Add thing type and channel group specific to the data elements supported by this weather station.
|
||||
Modeling this after an existing thing type that shares many of the channels is the easiest starting point.
|
||||
You can determine the weather data elements returned for the weather station by putting the binding into debug mode and reviewing the JSON object returned by the Ambient Weather API.
|
||||
|
||||
#### Create Processor Class AmbientWeather<StationModel>Processor
|
||||
|
||||
Add a class in `org.openhab.binding.ambientweather.internal.processor` that defines the channels supported by this station type.
|
||||
Add the following two methods.
|
||||
Again, the easiest approach is to model this class after a class for a similar weather station type.
|
||||
|
||||
##### Method: processInfoUpdate
|
||||
|
||||
Updates the channels for station name and location.
|
||||
|
||||
##### Method: processWeatherData
|
||||
|
||||
Updates channels for weather data.
|
||||
|
||||
#### Update AmbientWeatherProcessorFactory.java
|
||||
|
||||
Add new Processor class definition to `AmbientWeatherProcessorFactory.java`, and add a new case to the switch statement to return the new processor.
|
||||
54
bundles/org.openhab.binding.ambientweather/pom.xml
Normal file
54
bundles/org.openhab.binding.ambientweather/pom.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.ambientweather</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Ambient Weather Binding</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openhab.osgiify</groupId>
|
||||
<artifactId>io.socket.socket.io-client</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.osgiify</groupId>
|
||||
<artifactId>io.socket.engine.io-client</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.servicemix.bundles</groupId>
|
||||
<artifactId>org.apache.servicemix.bundles.okhttp</artifactId>
|
||||
<version>3.8.1_1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.servicemix.bundles</groupId>
|
||||
<artifactId>org.apache.servicemix.bundles.okio</artifactId>
|
||||
<version>1.13.0_1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20180813</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<bnd.importpackage>android.*;resolution:=optional,com.android.org.*;resolution:=optional,dalvik.*;resolution:=optional,javax.annotation.meta.*;resolution:=optional,org.apache.harmony.*;resolution:=optional,org.conscrypt.*;resolution:=optional,sun.security.*;resolution:=optional</bnd.importpackage>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.ambientweather-${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-ambientweather" description="Ambient Weather Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/3.8.1_1</bundle>
|
||||
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okio/1.13.0_1</bundle>
|
||||
<bundle dependency="true">mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.0</bundle>
|
||||
<bundle dependency="true">mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.0</bundle>
|
||||
<bundle dependency="true">mvn:org.json/json/20180813</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.ambientweather/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 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.ambientweather.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 AmbientWeatherBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AmbientWeatherBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "ambientweather";
|
||||
|
||||
// Bridge
|
||||
public static final String THING_TYPE_BRIDGE = "bridge";
|
||||
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()));
|
||||
|
||||
// WS-1400-IP series weather stations
|
||||
public static final String THING_TYPE_WS1400IP = "ws1400ip";
|
||||
public static final ThingTypeUID UID_WS1400IP = new ThingTypeUID(BINDING_ID, THING_TYPE_WS1400IP);
|
||||
|
||||
// WS-2902A series weather stations
|
||||
public static final String THING_TYPE_WS2902A = "ws2902a";
|
||||
public static final ThingTypeUID UID_WS2902A = new ThingTypeUID(BINDING_ID, THING_TYPE_WS2902A);
|
||||
|
||||
// WS-8482 weather station
|
||||
public static final String THING_TYPE_WS8482 = "ws8482";
|
||||
public static final ThingTypeUID UID_WS8482 = new ThingTypeUID(BINDING_ID, THING_TYPE_WS8482);
|
||||
|
||||
// WS-0900-IP series weather stations
|
||||
public static final String THING_TYPE_WS0900IP = "ws0900ip";
|
||||
public static final ThingTypeUID UID_WS0900IP = new ThingTypeUID(BINDING_ID, THING_TYPE_WS0900IP);
|
||||
|
||||
// WS-0265 weather station
|
||||
public static final String THING_TYPE_WS0265 = "ws0265";
|
||||
public static final ThingTypeUID UID_WS0265 = new ThingTypeUID(BINDING_ID, THING_TYPE_WS0265);
|
||||
|
||||
// Collection of weather station thing types
|
||||
public static final Set<ThingTypeUID> SUPPORTED_STATION_THING_TYPES_UIDS = Collections.unmodifiableSet(
|
||||
Stream.of(UID_WS1400IP, UID_WS2902A, UID_WS8482, UID_WS0900IP, UID_WS0265).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_STATION_THING_TYPES_UIDS.stream())
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
// Channel groups for specific weather stations
|
||||
public static final String CHGRP_WS1400IP = "weatherDataWs1400ip";
|
||||
public static final String CHGRP_WS2902A = "weatherDataWs2902a";
|
||||
public static final String CHGRP_WS8482 = "weatherDataWs8482";
|
||||
public static final String CHGRP_WS0900IP = "weatherDataWs0900ip";
|
||||
public static final String CHGRP_WS0265 = "weatherDataWs0265";
|
||||
|
||||
// Channel groups used across weather station types
|
||||
public static final String CHGRP_STATION = "station";
|
||||
public static final String CHGRP_INDOOR_SENSOR = "indoorSensor";
|
||||
public static final String CHGRP_REMOTE_SENSOR = "remoteSensor";
|
||||
|
||||
// Channels Ids for data returned by the ambientweather.net API
|
||||
public static final String CH_NAME = "name";
|
||||
public static final String CH_LOCATION = "location";
|
||||
public static final String CH_BATTERY_INDICATOR = "batteryIndicator";
|
||||
public static final String CH_OBSERVATION_TIME = "observationTime";
|
||||
public static final String CH_TEMPERATURE = "temperature";
|
||||
public static final String CH_FEELING_TEMPERATURE = "feelingTemperature";
|
||||
public static final String CH_DEW_POINT = "dewPoint";
|
||||
public static final String CH_HUMIDITY = "relativeHumidity";
|
||||
public static final String CH_PRESSURE_ABSOLUTE = "pressureAbsolute";
|
||||
public static final String CH_PRESSURE_RELATIVE = "pressureRelative";
|
||||
public static final String CH_WIND_SPEED = "windSpeed";
|
||||
public static final String CH_WIND_DIRECTION_DEGREES = "windDirectionDegrees";
|
||||
public static final String CH_WIND_SPEED_MAX_DAILY = "windSpeedMaxDaily";
|
||||
public static final String CH_WIND_GUST = "windGust";
|
||||
public static final String CH_WIND_GUST_MAX_DAILY = "windGustMaxDaily";
|
||||
public static final String CH_WIND_SPEED_AVG_2MIN = "windSpeedAvg2Minute";
|
||||
public static final String CH_WIND_DIRECTION_AVG_2MIN = "windDirectionDegreesAvg2Min";
|
||||
public static final String CH_WIND_SPEED_AVG_10MIN = "windSpeedAvg10Minute";
|
||||
public static final String CH_WIND_DIRECTION_AVG_10MIN = "windDirectionDegreesAvg10Min";
|
||||
public static final String CH_RAIN_HOURLY_RATE = "rainHourlyRate";
|
||||
public static final String CH_RAIN_DAY = "rainDay";
|
||||
public static final String CH_RAIN_WEEK = "rainWeek";
|
||||
public static final String CH_RAIN_MONTH = "rainMonth";
|
||||
public static final String CH_RAIN_YEAR = "rainYear";
|
||||
public static final String CH_RAIN_TOTAL = "rainTotal";
|
||||
public static final String CH_RAIN_EVENT = "rainEvent";
|
||||
public static final String CH_RAIN_LAST_TIME = "rainLastTime";
|
||||
public static final String CH_SOLAR_RADIATION = "solarRadiation";
|
||||
public static final String CH_UV_INDEX = "uvIndex";
|
||||
public static final String CH_CO2 = "co2";
|
||||
public static final String CH_SOIL_TEMPERATURE = "soilTemperature";
|
||||
public static final String CH_SOIL_MOISTURE = "soilMoisture";
|
||||
public static final String CH_RELAY = "relay";
|
||||
|
||||
// Channel Ids for calculated channels
|
||||
public static final String CH_PRESSURE_TREND = "pressureTrend";
|
||||
public static final String CH_WIND_DIRECTION = "windDirection";
|
||||
public static final String CH_UV_DANGER = "uvDanger";
|
||||
public static final String CH_SOIL_MOISTURE_LEVEL = "soilMoistureLevel";
|
||||
|
||||
// Bridge configuration parameters
|
||||
public static final String CONFIG_API_KEY = "apiKey";
|
||||
public static final String CONFIG_APPLICATION_KEY = "applicationKey";
|
||||
|
||||
// Weather station configuration parameters
|
||||
public static final String CONFIG_MAC_ADDRESS = "macAddress";
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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.ambientweather.internal;
|
||||
|
||||
import static org.openhab.binding.ambientweather.internal.AmbientWeatherBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.ambientweather.internal.handler.AmbientWeatherBridgeHandler;
|
||||
import org.openhab.binding.ambientweather.internal.handler.AmbientWeatherStationHandler;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link AmbientWeatherHandlerFactory} is responsible for creating the
|
||||
* Ambient Weather bridge and station thing handlers.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@Component(configurationPid = "binding.ambientweather", service = ThingHandlerFactory.class)
|
||||
public class AmbientWeatherHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
// Needed for converting UTC time to local time
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
@Activate
|
||||
public AmbientWeatherHandlerFactory(@Reference TimeZoneProvider timeZoneProvider) {
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (SUPPORTED_STATION_THING_TYPES_UIDS.contains(thingTypeUID)) {
|
||||
return new AmbientWeatherStationHandler(thing, timeZoneProvider);
|
||||
}
|
||||
if (SUPPORTED_BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID)) {
|
||||
return new AmbientWeatherBridgeHandler((Bridge) thing);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.config;
|
||||
|
||||
/**
|
||||
* The {@link BridgeConfig} is responsible for storing the
|
||||
* Ambient Weather bridge thing configuration.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
public class BridgeConfig {
|
||||
/**
|
||||
* API key
|
||||
*/
|
||||
public String apiKey;
|
||||
|
||||
/**
|
||||
* Application key
|
||||
*/
|
||||
public String applicationKey;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ambientweather.internal.config;
|
||||
|
||||
/**
|
||||
* The {@link StationConfig} is responsible for storing the
|
||||
* Ambient Weather weather station thing configuration.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
public class StationConfig {
|
||||
/**
|
||||
* MAC address of the device
|
||||
*/
|
||||
public String macAddress;
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.handler;
|
||||
|
||||
import static org.openhab.binding.ambientweather.internal.AmbientWeatherBindingConstants.CONFIG_MAC_ADDRESS;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.ambientweather.internal.config.BridgeConfig;
|
||||
import org.openhab.binding.ambientweather.internal.model.DeviceJson;
|
||||
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.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link AmbientWeatherBridgeHandler} is responsible for handling the
|
||||
* bridge things created to use the Ambient Weather service.
|
||||
*
|
||||
* @author Mark Hilbush - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AmbientWeatherBridgeHandler extends BaseBridgeHandler {
|
||||
// URL to retrieve device list from Ambient Weather
|
||||
private static final String DEVICES_URL = "https://api.ambientweather.net/v1/devices?applicationKey=%APPKEY%&apiKey=%APIKEY%";
|
||||
|
||||
// Timeout of the call to the Ambient Weather devices API
|
||||
public static final int DEVICES_API_TIMEOUT = 20000;
|
||||
|
||||
// Time to wait after failed key validation
|
||||
public static final long KEY_VALIDATION_DELAY = 60L;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AmbientWeatherBridgeHandler.class);
|
||||
|
||||
// Job to validate app and api keys
|
||||
@Nullable
|
||||
private ScheduledFuture<?> validateKeysJob;
|
||||
|
||||
// Application key is granted only by request from developer
|
||||
@Nullable
|
||||
private String applicationKey;
|
||||
|
||||
// API key assigned to user in ambientweather.net dashboard
|
||||
@Nullable
|
||||
private String apiKey;
|
||||
|
||||
// Used Ambient Weather real-time API to retrieve weather data
|
||||
// for weather stations assigned to an API key
|
||||
private AmbientWeatherEventListener listener;
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
private Runnable validateKeysRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.debug("Validating application and API keys");
|
||||
|
||||
String response = null;
|
||||
try {
|
||||
// Query weather stations (devices) from Ambient Weather
|
||||
String url = DEVICES_URL.replace("%APPKEY%", getApplicationKey()).replace("%APIKEY%", getApiKey());
|
||||
logger.debug("Bridge: Querying list of devices from ambient weather service");
|
||||
response = HttpUtil.executeUrl("GET", url, DEVICES_API_TIMEOUT);
|
||||
logger.trace("Bridge: Response = {}", response);
|
||||
// Got a response so the keys are good
|
||||
DeviceJson[] stations = gson.fromJson(response, DeviceJson[].class);
|
||||
logger.debug("Bridge: Application and API keys are valid with {} stations", stations.length);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Connecting to service");
|
||||
// Start up the real-time API listener
|
||||
listener.start(applicationKey, apiKey, gson);
|
||||
} catch (IOException e) {
|
||||
// executeUrl throws IOException when it gets a Not Authorized (401) response
|
||||
logger.debug("Bridge: Got IOException: {}", e.getMessage());
|
||||
setThingOfflineWithCommError(e.getMessage(), "Invalid API or application key");
|
||||
rescheduleValidateKeysJob();
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Bridge: Got IllegalArgumentException: {}", e.getMessage());
|
||||
setThingOfflineWithCommError(e.getMessage(), "Unable to get devices");
|
||||
rescheduleValidateKeysJob();
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.debug("Bridge: Got JsonSyntaxException: {}", e.getMessage());
|
||||
setThingOfflineWithCommError(e.getMessage(), "Error parsing json response");
|
||||
rescheduleValidateKeysJob();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public AmbientWeatherBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
listener = new AmbientWeatherEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
// If there are keys in the config, schedule the job to validate them
|
||||
if (hasApplicationKey() && hasApiKey()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Awaiting key validation");
|
||||
scheduleValidateKeysJob();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if an application key has been provided in the thing config
|
||||
*/
|
||||
private boolean hasApplicationKey() {
|
||||
String configApplicationKey = getConfigAs(BridgeConfig.class).applicationKey;
|
||||
if (StringUtils.isEmpty(configApplicationKey)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing application key");
|
||||
return false;
|
||||
}
|
||||
applicationKey = configApplicationKey;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if an API key has been provided in the thing config
|
||||
*/
|
||||
private boolean hasApiKey() {
|
||||
String configApiKey = getConfigAs(BridgeConfig.class).apiKey;
|
||||
if (StringUtils.isEmpty(configApiKey)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Missing API key");
|
||||
return false;
|
||||
}
|
||||
apiKey = configApiKey;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setThingOfflineWithCommError(String errorDetail, String statusDescription) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, statusDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
cancelValidateKeysJob();
|
||||
listener.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the job to validate the API and Application keys.
|
||||
* A side-effect of this is that we will discover the devices
|
||||
* MAC addresses that are associated with the API key.
|
||||
*/
|
||||
private void scheduleValidateKeysJob() {
|
||||
if (validateKeysJob == null) {
|
||||
validateKeysJob = scheduler.schedule(validateKeysRunnable, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelValidateKeysJob() {
|
||||
if (validateKeysJob != null) {
|
||||
validateKeysJob.cancel(true);
|
||||
validateKeysJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void rescheduleValidateKeysJob() {
|
||||
logger.debug("Bridge: Key validation will run again in {} seconds", KEY_VALIDATION_DELAY);
|
||||
validateKeysJob = scheduler.schedule(validateKeysRunnable, KEY_VALIDATION_DELAY, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep track of the station handlers so that the listener can route data events
|
||||
* to the correct handler.
|
||||
*/
|
||||
@Override
|
||||
public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) {
|
||||
String macAddress = (String) childThing.getConfiguration().get(CONFIG_MAC_ADDRESS);
|
||||
listener.addHandler((AmbientWeatherStationHandler) childHandler, macAddress);
|
||||
logger.debug("Bridge: Station handler initialized for {} with MAC {}", childThing.getUID(), macAddress);
|
||||
listener.resubscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) {
|
||||
String macAddress = (String) childThing.getConfiguration().get(CONFIG_MAC_ADDRESS);
|
||||
listener.removeHandler((AmbientWeatherStationHandler) childHandler, macAddress);
|
||||
logger.debug("Bridge: Station handler disposed for {} with MAC {}", childThing.getUID(), macAddress);
|
||||
}
|
||||
|
||||
// Callback used by EventListener to update bridge status
|
||||
public void markBridgeOffline(String reason) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, reason);
|
||||
}
|
||||
|
||||
// Callback used by EventListener to update bridge status
|
||||
public void markBridgeOnline() {
|
||||
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
|
||||
}
|
||||
|
||||
public @Nullable String getApplicationKey() {
|
||||
return applicationKey;
|
||||
}
|
||||
|
||||
public @Nullable String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// Handler doesn't support any commands
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.handler;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.json.JSONObject;
|
||||
import org.openhab.binding.ambientweather.internal.model.DeviceJson;
|
||||
import org.openhab.binding.ambientweather.internal.model.EventDataGenericJson;
|
||||
import org.openhab.binding.ambientweather.internal.model.EventSubscribedJson;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import io.socket.client.IO;
|
||||
import io.socket.client.Socket;
|
||||
import io.socket.emitter.Emitter;
|
||||
|
||||
/**
|
||||
* The {@link AmbientWeatherEventListener} is responsible for establishing
|
||||
* a socket.io connection with ambientweather.net, subscribing/unsubscribing
|
||||
* to data events, receiving data events through the real-time socket.io API,
|
||||
* and for routing the data events to a weather station thing handler for processing.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
public class AmbientWeatherEventListener {
|
||||
// URL used to get the realtime event stream
|
||||
private static final String REALTIME_URL = "https://api.ambientweather.net/?api=1&applicationKey=%APPKEY%";
|
||||
|
||||
// JSON used to subscribe or unsubscribe from weather data events
|
||||
private static final String SUB_UNSUB_JSON = "{ apiKeys: [ '%APIKEY%' ] }";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AmbientWeatherEventListener.class);
|
||||
|
||||
// Maintain mapping of handler and weather station MAC address
|
||||
private final Map<AmbientWeatherStationHandler, String> handlers = new ConcurrentHashMap<>();
|
||||
|
||||
private String apiKey;
|
||||
|
||||
private String applicationKey;
|
||||
|
||||
// Socket.io socket used to access Ambient Weather real-time API
|
||||
private Socket socket;
|
||||
|
||||
// Identifies if connected to real-time API
|
||||
private boolean isConnected;
|
||||
|
||||
private Gson gson;
|
||||
|
||||
private AmbientWeatherBridgeHandler bridgeHandler;
|
||||
|
||||
public AmbientWeatherEventListener(AmbientWeatherBridgeHandler bridgeHandler) {
|
||||
this.bridgeHandler = bridgeHandler;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the list of handlers to include 'handler' at MAC address 'macAddress'
|
||||
*/
|
||||
public void addHandler(AmbientWeatherStationHandler handler, String macAddress) {
|
||||
logger.debug("Listener: Add station handler to list: {}", handler.getThing().getUID());
|
||||
handlers.put(handler, macAddress);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the list of handlers to remove 'handler' at MAC address 'macAddress'
|
||||
*/
|
||||
public void removeHandler(AmbientWeatherStationHandler handler, String macAddress) {
|
||||
logger.debug("Listener: Remove station handler from list: {}", handler.getThing().getUID());
|
||||
handlers.remove(handler);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send weather station information (station name and location) to the
|
||||
* thing handler associated with the MAC address
|
||||
*/
|
||||
private void sendStationInfoToHandler(String macAddress, String name, String location) {
|
||||
AmbientWeatherStationHandler handler = getHandler(macAddress);
|
||||
if (handler != null) {
|
||||
handler.handleInfoEvent(macAddress, name, location);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an Ambient Weather data event to the station thing handler associated
|
||||
* with the MAC address
|
||||
*/
|
||||
private void sendWeatherDataToHandler(String macAddress, String jsonData) {
|
||||
AmbientWeatherStationHandler handler = getHandler(macAddress);
|
||||
if (handler != null) {
|
||||
handler.handleWeatherDataEvent(jsonData);
|
||||
}
|
||||
}
|
||||
|
||||
private AmbientWeatherStationHandler getHandler(String macAddress) {
|
||||
logger.debug("Listener: Search for MAC {} in handlers list with {} entries", macAddress, handlers.size());
|
||||
for (Map.Entry<AmbientWeatherStationHandler, String> device : handlers.entrySet()) {
|
||||
AmbientWeatherStationHandler handler = device.getKey();
|
||||
String mac = device.getValue();
|
||||
if (mac.equals(macAddress)) {
|
||||
logger.debug("Listener: Found handler for {} with MAC {}", handler.getThing().getUID(), macAddress);
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
logger.debug("Listener: No handler available for event for station with MAC {}", macAddress);
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the event listener for the Ambient Weather real-time API
|
||||
*/
|
||||
public void start(String applicationKey, String apiKey, Gson gson) {
|
||||
logger.debug("Listener: Event listener starting");
|
||||
this.applicationKey = applicationKey;
|
||||
this.apiKey = apiKey;
|
||||
this.gson = gson;
|
||||
connectToService();
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop the event listener for the Ambient Weather real-time API.
|
||||
*/
|
||||
public void stop() {
|
||||
logger.debug("Listener: Event listener stopping");
|
||||
sendUnsubscribe();
|
||||
disconnectFromService();
|
||||
handlers.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiate the connection to the Ambient Weather real-time API
|
||||
*/
|
||||
private synchronized void connectToService() {
|
||||
final String url = REALTIME_URL.replace("%APPKEY%", applicationKey);
|
||||
try {
|
||||
IO.Options options = new IO.Options();
|
||||
options.forceNew = true;
|
||||
options.transports = new String[] { "websocket" };
|
||||
socket = IO.socket(url, options);
|
||||
} catch (URISyntaxException e) {
|
||||
logger.info("Listener: URISyntaxException getting IO socket: {}", e.getMessage());
|
||||
return;
|
||||
}
|
||||
socket.on(Socket.EVENT_CONNECT, onEventConnect);
|
||||
socket.on(Socket.EVENT_CONNECT_ERROR, onEventConnectError);
|
||||
socket.on(Socket.EVENT_CONNECT_TIMEOUT, onEventConnectTimeout);
|
||||
socket.on(Socket.EVENT_DISCONNECT, onEventDisconnect);
|
||||
socket.on(Socket.EVENT_RECONNECT, onEventReconnect);
|
||||
socket.on("data", onData);
|
||||
socket.on("subscribed", onSubscribed);
|
||||
|
||||
logger.debug("Listener: Opening connection to ambient weather service with socket {}", socket.toString());
|
||||
socket.connect();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiate a disconnect from the Ambient Weather real-time API
|
||||
*/
|
||||
private void disconnectFromService() {
|
||||
if (socket != null) {
|
||||
logger.debug("Listener: Disconnecting socket and removing event listeners for {}", socket.toString());
|
||||
socket.disconnect();
|
||||
socket.off();
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to reconnect to the Ambient Weather real-time API
|
||||
*/
|
||||
private void reconnectToService() {
|
||||
logger.debug("Listener: Attempting to reconnect to service");
|
||||
disconnectFromService();
|
||||
connectToService();
|
||||
}
|
||||
|
||||
/*
|
||||
* Socket.io event callbacks
|
||||
*/
|
||||
private Emitter.Listener onEventConnect = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
logger.debug("Listener: Connected! Subscribe to weather data events");
|
||||
isConnected = true;
|
||||
bridgeHandler.markBridgeOnline();
|
||||
sendSubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
private Emitter.Listener onEventDisconnect = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
logger.debug("Listener: Disconnected from the ambient weather service)");
|
||||
handleError(Socket.EVENT_DISCONNECT, args);
|
||||
isConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
private Emitter.Listener onEventConnectError = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
handleError(Socket.EVENT_CONNECT_ERROR, args);
|
||||
}
|
||||
};
|
||||
|
||||
private Emitter.Listener onEventConnectTimeout = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
handleError(Socket.EVENT_CONNECT_TIMEOUT, args);
|
||||
}
|
||||
};
|
||||
|
||||
private Emitter.Listener onEventReconnect = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
logger.debug("Listener: Received reconnect event from service");
|
||||
reconnectToService();
|
||||
}
|
||||
};
|
||||
|
||||
private Emitter.Listener onSubscribed = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
logger.debug("Listener: Received SUBSCRIBED event");
|
||||
// Got a response to a subscribe or unsubscribe command
|
||||
if (args.length > 0) {
|
||||
handleSubscribed(((JSONObject) args[0]).toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private Emitter.Listener onData = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
logger.debug("Listener: Received DATA event");
|
||||
// Got a weather data event from ambientweather.net
|
||||
if (args.length > 0) {
|
||||
handleData(((JSONObject) args[0]).toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Handlers for events
|
||||
*/
|
||||
private void handleError(String event, Object... args) {
|
||||
String reason = "Unknown";
|
||||
if (args.length > 0) {
|
||||
if (args[0] instanceof String) {
|
||||
reason = (String) args[0];
|
||||
} else if (args[0] instanceof Exception) {
|
||||
reason = String.format("Exception=%s Message=%s", args[0].getClass(),
|
||||
((Exception) args[0]).getMessage());
|
||||
}
|
||||
}
|
||||
logger.debug("Listener: Received socket event: {}, Reason: {}", event, reason);
|
||||
bridgeHandler.markBridgeOffline(reason);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the subscribed event, then tell the handler to update the station info channels.
|
||||
*/
|
||||
private void handleSubscribed(String jsonData) {
|
||||
logger.debug("Listener: subscribed={}", jsonData);
|
||||
// Extract the station names and locations and give to handlers
|
||||
try {
|
||||
EventSubscribedJson subscribed = gson.fromJson(jsonData, EventSubscribedJson.class);
|
||||
if (subscribed.invalidApiKeys != null) {
|
||||
logger.info("Listener: Invalid keys!! invalidApiKeys={}", subscribed.invalidApiKeys);
|
||||
bridgeHandler.markBridgeOffline("Invalid API keys");
|
||||
return;
|
||||
}
|
||||
|
||||
if (subscribed.devices != null && subscribed.devices instanceof ArrayList) {
|
||||
// Convert the ArrayList back to JSON, then parse it
|
||||
String innerJson = gson.toJson(subscribed.devices);
|
||||
DeviceJson[] stations = gson.fromJson(innerJson, DeviceJson[].class);
|
||||
|
||||
// Inform handlers of their name and location
|
||||
for (DeviceJson station : stations) {
|
||||
logger.debug("Listener: Subscribed event has station: name = {}, location = {}, MAC = {}",
|
||||
station.info.name, station.info.location, station.macAddress);
|
||||
sendStationInfoToHandler(station.macAddress, station.info.name, station.info.location);
|
||||
}
|
||||
}
|
||||
if (subscribed.isMethodSubscribe()) {
|
||||
logger.debug("Listener: Subscribed to data events. Waiting for data...");
|
||||
} else if (subscribed.isMethodUnsubscribe()) {
|
||||
logger.debug("Listener: Unsubscribed from data events");
|
||||
}
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.debug("Listener: Exception parsing subscribed.devices: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the weather data event, then send to handler to update the channels
|
||||
*/
|
||||
private synchronized void handleData(String jsonData) {
|
||||
logger.debug("Listener: Data: {}", jsonData);
|
||||
try {
|
||||
EventDataGenericJson data = gson.fromJson(jsonData, EventDataGenericJson.class);
|
||||
if (StringUtils.isNotEmpty(data.macAddress)) {
|
||||
sendWeatherDataToHandler(data.macAddress, jsonData);
|
||||
}
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.info("Listener: Exception parsing subscribed event: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Subscribe to weather data events for stations associated with the API key
|
||||
*/
|
||||
private void sendSubscribe() {
|
||||
if (apiKey == null) {
|
||||
return;
|
||||
}
|
||||
final String sub = SUB_UNSUB_JSON.replace("%APIKEY%", apiKey);
|
||||
if (isConnected && socket != null) {
|
||||
logger.debug("Listener: Sending subscribe request");
|
||||
socket.emit("subscribe", new JSONObject(sub));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unsubscribe from weather data events for stations associated with the API key
|
||||
*/
|
||||
private void sendUnsubscribe() {
|
||||
if (apiKey == null) {
|
||||
return;
|
||||
}
|
||||
final String unsub = SUB_UNSUB_JSON.replace("%APIKEY%", apiKey);
|
||||
if (isConnected && socket != null) {
|
||||
logger.debug("Listener: Sending unsubscribe request");
|
||||
socket.emit("unsubscribe", new JSONObject(unsub));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resubscribe when a handler is initialized
|
||||
*/
|
||||
public synchronized void resubscribe() {
|
||||
sendUnsubscribe();
|
||||
sendSubscribe();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.handler;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.ambientweather.internal.config.StationConfig;
|
||||
import org.openhab.binding.ambientweather.internal.processor.ProcessorFactory;
|
||||
import org.openhab.binding.ambientweather.internal.processor.ProcessorNotFoundException;
|
||||
import org.openhab.core.common.AbstractUID;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link AmbientWeatherStationHandler} is responsible for processing
|
||||
* info and weather data updates.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AmbientWeatherStationHandler extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(AmbientWeatherStationHandler.class);
|
||||
|
||||
// MAC address for weather station handled by this thing handler
|
||||
private @Nullable String macAddress;
|
||||
|
||||
// Short name for logging station type
|
||||
private String station;
|
||||
|
||||
// Time zone provider representing time zone configured in openHAB config
|
||||
TimeZoneProvider timeZoneProvider;
|
||||
|
||||
public AmbientWeatherStationHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
|
||||
// Name of station thing type used in logging
|
||||
String s = thing.getThingTypeUID().getAsString();
|
||||
station = s.substring(s.indexOf(AbstractUID.SEPARATOR) + 1).toUpperCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
macAddress = getConfigAs(StationConfig.class).macAddress;
|
||||
logger.debug("Station {}: Initializing station handler for MAC {}", station, macAddress);
|
||||
try {
|
||||
ProcessorFactory.getProcessor(thing).setChannelGroupId();
|
||||
ProcessorFactory.getProcessor(thing).setNumberOfSensors();
|
||||
} catch (ProcessorNotFoundException e) {
|
||||
logger.warn("Station {}: Unable to set channel group Id and/or number of sensors: {}", station,
|
||||
e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, e.getMessage());
|
||||
return;
|
||||
}
|
||||
Thing bridge = getBridge();
|
||||
if (bridge != null) {
|
||||
logger.debug("Station {}: Set station status to match bridge status: {}", station, bridge.getStatus());
|
||||
updateStatus(bridge.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
macAddress = getConfigAs(StationConfig.class).macAddress;
|
||||
logger.debug("Station {}: Disposing station handler for MAC {}", station, macAddress);
|
||||
updateStatus(ThingStatus.OFFLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
ThingStatus bridgeStatus = bridgeStatusInfo.getStatus();
|
||||
logger.debug("Station {}: Detected bridge status changed to '{}', Update my status", station, bridgeStatus);
|
||||
if (bridgeStatus == ThingStatus.OFFLINE) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
} else if (bridgeStatus == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle an update to the station name and location
|
||||
*/
|
||||
public void handleInfoEvent(String mac, String name, String location) {
|
||||
logger.debug("Station {}: Update name={} and location={} for MAC {}", station, name, location, macAddress);
|
||||
try {
|
||||
ProcessorFactory.getProcessor(thing).processInfoUpdate(this, station, name, location);
|
||||
} catch (ProcessorNotFoundException e) {
|
||||
logger.debug("Unable to process info event: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle an update to the weather data.
|
||||
*/
|
||||
public void handleWeatherDataEvent(String jsonData) {
|
||||
logger.debug("Station {}: Processing data event for MAC {}", station, macAddress);
|
||||
try {
|
||||
ProcessorFactory.getProcessor(thing).processWeatherData(this, station, jsonData);
|
||||
} catch (ProcessorNotFoundException e) {
|
||||
logger.debug("Unable to process weather data event: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void updateQuantity(String groupId, String channelId, @Nullable Number value, Unit<?> unit) {
|
||||
String channel = groupId + "#" + channelId;
|
||||
if (value != null && isLinked(channel)) {
|
||||
updateState(channel, new QuantityType<>(value, unit));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateString(String groupId, String channelId, @Nullable String value) {
|
||||
String channel = groupId + "#" + channelId;
|
||||
if (value != null && isLinked(channel)) {
|
||||
updateState(channel, new StringType(value));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateNumber(String groupId, String channelId, @Nullable Number value) {
|
||||
String channel = groupId + "#" + channelId;
|
||||
if (value != null && isLinked(channel)) {
|
||||
if (value instanceof Integer) {
|
||||
updateState(channel, new DecimalType(value.intValue()));
|
||||
} else if (value instanceof Double) {
|
||||
updateState(channel, new DecimalType(value.doubleValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateDate(String groupId, String channelId, @Nullable String date) {
|
||||
String channel = groupId + "#" + channelId;
|
||||
if (date != null && isLinked(channel)) {
|
||||
updateState(channel, getLocalDateTimeType(date, getZoneId()));
|
||||
}
|
||||
}
|
||||
|
||||
private DateTimeType getLocalDateTimeType(String dateTimeString, ZoneId zoneId) {
|
||||
DateTimeType dateTimeType;
|
||||
try {
|
||||
Instant instant = Instant.parse(dateTimeString);
|
||||
ZonedDateTime localDateTime = instant.atZone(zoneId);
|
||||
dateTimeType = new DateTimeType(localDateTime);
|
||||
} catch (DateTimeParseException e) {
|
||||
logger.debug("Error parsing date/time string: {}", e.getMessage());
|
||||
dateTimeType = new DateTimeType();
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Error converting to DateTimeType: {}", e.getMessage());
|
||||
dateTimeType = new DateTimeType();
|
||||
}
|
||||
return dateTimeType;
|
||||
}
|
||||
|
||||
private ZoneId getZoneId() {
|
||||
return timeZoneProvider.getTimeZone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// Handler doesn't support any commands
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.model;
|
||||
|
||||
/**
|
||||
* The {@link DeviceJson} is the JSON object returned
|
||||
* by the Ambient Weather '/v1/devices' command.
|
||||
* Note that there is nothing in the JSON that defines the type (e.g.
|
||||
* WS-1400-IP, WS-8482, etc.) of weather station.
|
||||
*
|
||||
* @author Mark Hilbush - Initial Contribution
|
||||
*/
|
||||
public class DeviceJson {
|
||||
/*
|
||||
* The weather station's MAC address
|
||||
*/
|
||||
public String macAddress;
|
||||
|
||||
/*
|
||||
* API key to which this device is associated
|
||||
*/
|
||||
public String apiKey;
|
||||
|
||||
/*
|
||||
* User-supplied information about the station, such as name and location
|
||||
*/
|
||||
public StationInfoJson info;
|
||||
|
||||
/*
|
||||
* Placeholder for weather data, which varies by station.
|
||||
*/
|
||||
public Object lastData;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.model;
|
||||
|
||||
/**
|
||||
* The {@link EventDataGenericJson} is the JSOn object
|
||||
* returned by the Ambient Weather real-time API.
|
||||
* The contents of the object varies by weather station, and
|
||||
* all we care about here is getting the MAC address. The MAC
|
||||
* address is used to decide which Station Thing Handler
|
||||
* instance gets the weather data object.
|
||||
*
|
||||
* @author Mark Hilbush - Initial Contribution
|
||||
*/
|
||||
public class EventDataGenericJson {
|
||||
/*
|
||||
* The weather station's MAC address
|
||||
*/
|
||||
public String macAddress;
|
||||
|
||||
/*
|
||||
* Placeholder for the rest of the JSON object.
|
||||
*/
|
||||
public Object data;
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.model;
|
||||
|
||||
/**
|
||||
* The {@link EventDataJson} is the JSON object returned
|
||||
* from the Ambient Weather real-time API for the weather stations
|
||||
* whose weather data is hosted on ambientweather.net. This object is
|
||||
* generated whenever the data for the station changes on
|
||||
* ambientweather.net.
|
||||
*
|
||||
* @author Mark Hilbush - Initial Contribution
|
||||
*/
|
||||
public class EventDataJson {
|
||||
/*
|
||||
* The weather station's MAC address
|
||||
*/
|
||||
public String macAddress;
|
||||
|
||||
/*
|
||||
* Date of the weather observation in UTC format
|
||||
*/
|
||||
public long dateutc;
|
||||
|
||||
/*
|
||||
* Date of the weather observation as a string
|
||||
*/
|
||||
public String date;
|
||||
|
||||
/*
|
||||
* Temperature from station in degrees F
|
||||
*/
|
||||
public Double tempf;
|
||||
|
||||
/*
|
||||
* Real-feel temperature
|
||||
*/
|
||||
public Double feelsLike;
|
||||
|
||||
/*
|
||||
* Dew point
|
||||
*/
|
||||
public Double dewPoint;
|
||||
|
||||
/*
|
||||
* Relative humidity from station
|
||||
*/
|
||||
public Double humidity;
|
||||
|
||||
/*
|
||||
* Relative barometric pressure in inches
|
||||
*/
|
||||
public Double baromrelin;
|
||||
|
||||
/*
|
||||
* Absolute barometric pressure in inches
|
||||
*/
|
||||
public Double baromabsin;
|
||||
|
||||
/*
|
||||
* Temperature from indoor sensor
|
||||
*/
|
||||
public Double tempinf;
|
||||
|
||||
/*
|
||||
* Humidity from indoor sensor
|
||||
*/
|
||||
public Double humidityin;
|
||||
|
||||
/*
|
||||
* Battery from indoor sensor
|
||||
*/
|
||||
public String battin;
|
||||
|
||||
/*
|
||||
* Solar radiation
|
||||
*/
|
||||
public Double solarradiation;
|
||||
|
||||
/*
|
||||
* UV Index
|
||||
*/
|
||||
public Integer uv;
|
||||
|
||||
/*
|
||||
* Carbon dioxide
|
||||
*/
|
||||
public Double co;
|
||||
|
||||
/*
|
||||
* Battery level
|
||||
*/
|
||||
public String battout;
|
||||
|
||||
/*
|
||||
* Wind speed
|
||||
* Instantaneous wind speed, mph
|
||||
*/
|
||||
public Double windspeedmph;
|
||||
|
||||
/*
|
||||
* Wind direction
|
||||
* Instantaneous wind direction, 0-360 degrees
|
||||
*/
|
||||
public Integer winddir;
|
||||
|
||||
/*
|
||||
* Wind gust speed
|
||||
* Max wind speed in the last 10 minutes, mph
|
||||
*/
|
||||
public Double windgustmph;
|
||||
|
||||
/*
|
||||
* Wind gust direction
|
||||
* Wind direction at which the wind gust occurred, 0-360 degrees
|
||||
*/
|
||||
public Integer windgustdir;
|
||||
|
||||
/*
|
||||
* Maximum daily wind gust speed
|
||||
* Maximum wind speed in last day, mph
|
||||
*/
|
||||
public Double maxdailygust;
|
||||
|
||||
/*
|
||||
* Wind speed 2-minute average
|
||||
* Average wind speed, 2 minute average, mph
|
||||
* Name must contain underscore
|
||||
*/
|
||||
public Double windspdmph_avg2m;
|
||||
|
||||
/*
|
||||
* Wind direction 2 minute average
|
||||
* Average wind direction, 2 minute average, 0-360 degrees
|
||||
* Name must contain underscore
|
||||
*/
|
||||
public Integer winddir_avg2m;
|
||||
|
||||
/*
|
||||
* Wind speed 10-minute average
|
||||
* Average wind speed, 10 minute average, mph
|
||||
* Name must contain underscore
|
||||
*/
|
||||
public Double windspdmph_avg10m;
|
||||
|
||||
/*
|
||||
* Wind direction 10 minute average
|
||||
* Average wind direction, 10 minute average, 0-360 degrees
|
||||
* Name must contain underscore
|
||||
*/
|
||||
public Integer winddir_avg10m;
|
||||
|
||||
/*
|
||||
* Hourly rain rate in inches/hour
|
||||
*/
|
||||
public Double hourlyrainin;
|
||||
|
||||
/*
|
||||
* Daily rain, inches
|
||||
*/
|
||||
public Double dailyrainin;
|
||||
|
||||
/*
|
||||
* Weekly rain, inches
|
||||
*/
|
||||
public Double weeklyrainin;
|
||||
|
||||
/*
|
||||
* Monthly rain, inches
|
||||
*/
|
||||
public Double monthlyrainin;
|
||||
|
||||
/*
|
||||
* Yearly rain, inches
|
||||
*/
|
||||
public Double yearlyrainin;
|
||||
|
||||
/*
|
||||
* Event rain, inches
|
||||
*/
|
||||
public Double eventrainin;
|
||||
|
||||
/*
|
||||
* Rain in past 24 hours, inches
|
||||
* Can't use since it starts with numbers
|
||||
*/
|
||||
// public double 24hourrainin;
|
||||
|
||||
/*
|
||||
* Total rain since last factory reset, inches
|
||||
*/
|
||||
public Double totalrainin;
|
||||
|
||||
/*
|
||||
* lastRain - Last time hourlyrainin > 0,
|
||||
*/
|
||||
public String lastRain;
|
||||
|
||||
/*
|
||||
* Temperature from remote sensors 1-10
|
||||
*/
|
||||
public Double temp1f;
|
||||
public Double temp2f;
|
||||
public Double temp3f;
|
||||
public Double temp4f;
|
||||
public Double temp5f;
|
||||
public Double temp6f;
|
||||
public Double temp7f;
|
||||
public Double temp8f;
|
||||
public Double temp9f;
|
||||
public Double temp10f;
|
||||
|
||||
/*
|
||||
* Relative humidity from remote sensors 1-10
|
||||
*/
|
||||
public Double humidity1;
|
||||
public Double humidity2;
|
||||
public Double humidity3;
|
||||
public Double humidity4;
|
||||
public Double humidity5;
|
||||
public Double humidity6;
|
||||
public Double humidity7;
|
||||
public Double humidity8;
|
||||
public Double humidity9;
|
||||
public Double humidity10;
|
||||
|
||||
/*
|
||||
* Battery status of remote sensors 1-10
|
||||
* Good/Bad indication, String, 1=Good, 0=Bad
|
||||
*/
|
||||
public String batt1;
|
||||
public String batt2;
|
||||
public String batt3;
|
||||
public String batt4;
|
||||
public String batt5;
|
||||
public String batt6;
|
||||
public String batt7;
|
||||
public String batt8;
|
||||
public String batt9;
|
||||
public String batt10;
|
||||
|
||||
/*
|
||||
* Relay 1-10
|
||||
*/
|
||||
public String relay1;
|
||||
public String relay2;
|
||||
public String relay3;
|
||||
public String relay4;
|
||||
public String relay5;
|
||||
public String relay6;
|
||||
public String relay7;
|
||||
public String relay8;
|
||||
public String relay9;
|
||||
public String relay10;
|
||||
|
||||
/*
|
||||
* Soil temperature from remote sensors 1-10
|
||||
*/
|
||||
public Double soiltemp1;
|
||||
public Double soiltemp2;
|
||||
public Double soiltemp3;
|
||||
public Double soiltemp4;
|
||||
public Double soiltemp5;
|
||||
public Double soiltemp6;
|
||||
public Double soiltemp7;
|
||||
public Double soiltemp8;
|
||||
public Double soiltemp9;
|
||||
public Double soiltemp10;
|
||||
|
||||
/*
|
||||
* Soil moisture from remote sensors 1-10
|
||||
*/
|
||||
public Double soilhum1;
|
||||
public Double soilhum2;
|
||||
public Double soilhum3;
|
||||
public Double soilhum4;
|
||||
public Double soilhum5;
|
||||
public Double soilhum6;
|
||||
public Double soilhum7;
|
||||
public Double soilhum8;
|
||||
public Double soilhum9;
|
||||
public Double soilhum10;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ambientweather.internal.model;
|
||||
|
||||
/**
|
||||
* The {@link EventSubscribedJson} is the JSON object
|
||||
* returned by the Ambient Weather real-time API in response to
|
||||
* the 'subscribe' and 'unsubscribe' commands.
|
||||
*
|
||||
* @author Mark Hilbush - Initial Contribution
|
||||
*/
|
||||
public class EventSubscribedJson {
|
||||
/*
|
||||
* method is "subscribed"
|
||||
*/
|
||||
public String method;
|
||||
|
||||
/*
|
||||
* Array of devices
|
||||
*/
|
||||
public Object devices;
|
||||
|
||||
/*
|
||||
* Array of invalid API Keys
|
||||
*/
|
||||
public Object invalidApiKeys;
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public boolean isMethodSubscribe() {
|
||||
return "subscribe".equals(method);
|
||||
}
|
||||
|
||||
public boolean isMethodUnsubscribe() {
|
||||
return "unsubscribe".equals(method);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.model;
|
||||
|
||||
/**
|
||||
* The {@link StationInfoJson} is the JSON object
|
||||
* returned by the Ambient Weather API that describes the
|
||||
* user-provided name and location of the weather station.
|
||||
*
|
||||
* @author Mark Hilbush - Initial Contribution
|
||||
*/
|
||||
public class StationInfoJson {
|
||||
/*
|
||||
* The name given to the station by the user
|
||||
*/
|
||||
public String name;
|
||||
|
||||
/*
|
||||
* The location given to the station by the user
|
||||
*/
|
||||
public String location;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.processor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.ambientweather.internal.model.EventDataJson;
|
||||
import org.openhab.binding.ambientweather.internal.util.PressureTrend;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link AbstractProcessor} is the generic/error processor
|
||||
* for info and weather updates from weather stations that are currently
|
||||
* not supported by this binding.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AbstractProcessor implements ProcessorInterface {
|
||||
// @formatter:off
|
||||
private static final String[] UV_INDEX = {
|
||||
"LOW",
|
||||
"LOW",
|
||||
"LOW",
|
||||
"MODERATE",
|
||||
"MODERATE",
|
||||
"MODERATE",
|
||||
"HIGH",
|
||||
"HIGH",
|
||||
"VERY HIGH",
|
||||
"VERY HIGH",
|
||||
"VERY HIGH",
|
||||
"EXTREME",
|
||||
"EXTREME",
|
||||
"EXTREME",
|
||||
"EXTREME",
|
||||
"EXTREME"
|
||||
};
|
||||
// @formatter:on
|
||||
|
||||
private static final String[] WIND_DIRECTIONS = new String[] { "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
|
||||
"S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW" };
|
||||
|
||||
protected static final String NOT_APPLICABLE = "N/A";
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
// Used to calculate barometric pressure trend
|
||||
protected PressureTrend pressureTrend = new PressureTrend();
|
||||
|
||||
/*
|
||||
* The channel group Id for this processor
|
||||
*/
|
||||
protected String channelGroupId = "";
|
||||
|
||||
/*
|
||||
* Used to extract remote sensor data from the data event Json
|
||||
*/
|
||||
protected RemoteSensor remoteSensor = new RemoteSensor();
|
||||
|
||||
/*
|
||||
* Parse the event data json string
|
||||
*/
|
||||
protected @Nullable EventDataJson parseEventData(String station, String jsonData) {
|
||||
EventDataJson data = null;
|
||||
try {
|
||||
logger.debug("Station {}: Parsing weather data event json", station);
|
||||
data = ProcessorFactory.getGson().fromJson(jsonData, EventDataJson.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.info("Station {}: Data event cannot be parsed: {}", station, e.getMessage());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the UV Index integer value to a string representation
|
||||
*/
|
||||
protected String convertUVIndexToString(int uvIndex) {
|
||||
if (uvIndex < 0 || uvIndex >= UV_INDEX.length) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return UV_INDEX[uvIndex];
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the wind directions in degrees to a string representation
|
||||
*/
|
||||
protected String convertWindDirectionToString(double windDirectionDegrees) {
|
||||
double step = 360.0 / WIND_DIRECTIONS.length;
|
||||
double b = Math.floor((windDirectionDegrees + (step / 2.0)) / step);
|
||||
return WIND_DIRECTIONS[(int) (b % WIND_DIRECTIONS.length)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.processor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link ProcessorFactory} is responsible for returning the right
|
||||
* processor for handling info update and weather data events.
|
||||
*
|
||||
* There is one processor for each supported station type.
|
||||
* To add support for a new station type, this class needs to be modified to
|
||||
* return a processor for the new thing type. In addition, an
|
||||
* AmbientWeatherXXXXXProcessor class needs to be created for the new station.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ProcessorFactory {
|
||||
// Single Gson instance shared by processors
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
// Supported weather stations
|
||||
private @Nullable static Ws1400ipProcessor WS1400IP_PROCESSOR;
|
||||
private @Nullable static Ws2902aProcessor WS2902A_PROCESSOR;
|
||||
private @Nullable static Ws8482Processor WS8482_PROCESSOR;
|
||||
private @Nullable static Ws0900ipProcessor WS0900IP_PROCESSOR;
|
||||
private @Nullable static Ws0265Processor WS0265_PROCESSOR;
|
||||
|
||||
/**
|
||||
* Individual weather station processors use this one Gson instance,
|
||||
* rather than create their own.
|
||||
*
|
||||
* @return instance of a Gson object
|
||||
*/
|
||||
public static Gson getGson() {
|
||||
return (GSON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a processor for a specific weather station type.
|
||||
*
|
||||
* @param thing
|
||||
* @return instance of a weather station processor
|
||||
* @throws ProcessorNotFoundException
|
||||
*/
|
||||
public static AbstractProcessor getProcessor(Thing thing) throws ProcessorNotFoundException {
|
||||
// Return the processor for this thing type
|
||||
String thingType = thing.getThingTypeUID().getAsString().toLowerCase();
|
||||
switch (thingType) {
|
||||
case "ambientweather:ws1400ip": {
|
||||
Ws1400ipProcessor processor = WS1400IP_PROCESSOR;
|
||||
if (processor == null) {
|
||||
processor = new Ws1400ipProcessor();
|
||||
WS1400IP_PROCESSOR = processor;
|
||||
}
|
||||
return processor;
|
||||
}
|
||||
case "ambientweather:ws2902a": {
|
||||
Ws2902aProcessor processor = WS2902A_PROCESSOR;
|
||||
if (processor == null) {
|
||||
processor = new Ws2902aProcessor();
|
||||
WS2902A_PROCESSOR = processor;
|
||||
}
|
||||
return processor;
|
||||
}
|
||||
case "ambientweather:ws8482": {
|
||||
Ws8482Processor processor = WS8482_PROCESSOR;
|
||||
if (processor == null) {
|
||||
processor = new Ws8482Processor();
|
||||
WS8482_PROCESSOR = processor;
|
||||
}
|
||||
return processor;
|
||||
}
|
||||
case "ambientweather:ws0900ip": {
|
||||
Ws0900ipProcessor processor = WS0900IP_PROCESSOR;
|
||||
if (processor == null) {
|
||||
processor = new Ws0900ipProcessor();
|
||||
WS0900IP_PROCESSOR = processor;
|
||||
}
|
||||
return processor;
|
||||
}
|
||||
case "ambientweather:ws0265": {
|
||||
Ws0265Processor processor = WS0265_PROCESSOR;
|
||||
if (processor == null) {
|
||||
processor = new Ws0265Processor();
|
||||
WS0265_PROCESSOR = processor;
|
||||
}
|
||||
return processor;
|
||||
}
|
||||
}
|
||||
throw new ProcessorNotFoundException("No processor for thing type " + thingType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.processor;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ambientweather.internal.handler.AmbientWeatherStationHandler;
|
||||
|
||||
/**
|
||||
* The {@link ProcessorInterface} is responsible for
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface ProcessorInterface {
|
||||
/*
|
||||
* Set the channel group Id for the station
|
||||
*/
|
||||
public void setChannelGroupId();
|
||||
|
||||
/*
|
||||
* Set the number of remote sensors supported by the station
|
||||
*/
|
||||
public void setNumberOfSensors();
|
||||
|
||||
/*
|
||||
* Updates the info channels (i.e. name and location) for a station
|
||||
*/
|
||||
public void processInfoUpdate(AmbientWeatherStationHandler handler, String station, String name, String location);
|
||||
|
||||
/*
|
||||
* Updates the weather data channels for a station
|
||||
*/
|
||||
public void processWeatherData(AmbientWeatherStationHandler handler, String station, String jsonData);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ambientweather.internal.processor;
|
||||
|
||||
/**
|
||||
* The {@link ProcessorNotFoundException} is thrown when a data event is
|
||||
* received for which there's no processor that can process the event.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
public class ProcessorNotFoundException extends Exception {
|
||||
private static final long serialVersionUID = -7522973666620330850L;
|
||||
|
||||
public ProcessorNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -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.ambientweather.internal.processor;
|
||||
|
||||
import static org.openhab.binding.ambientweather.internal.AmbientWeatherBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.ambientweather.internal.handler.AmbientWeatherStationHandler;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.stream.JsonReader;
|
||||
|
||||
/**
|
||||
* The {@link RemoteSensor} is responsible for updating the remote sensor
|
||||
* channels for the remote sensors attached to a weather station.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RemoteSensor {
|
||||
// Maximum number of remote sensors that can be supported by a station
|
||||
private static final int MAX_SENSORS = 10;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RemoteSensor.class);
|
||||
|
||||
private final TreeMap<Double, String> soilMoistureMap = new TreeMap<>();
|
||||
|
||||
private int numberOfSensors;
|
||||
|
||||
public RemoteSensor() {
|
||||
initializeSoilMoistureMap();
|
||||
}
|
||||
|
||||
public void setNumberOfSensors(int numberOfSensors) {
|
||||
if (numberOfSensors < 1 || numberOfSensors > MAX_SENSORS) {
|
||||
throw new IllegalArgumentException("Invalid number of sensors");
|
||||
}
|
||||
this.numberOfSensors = numberOfSensors;
|
||||
}
|
||||
|
||||
public void updateChannels(AmbientWeatherStationHandler handler, final @Nullable String jsonData) {
|
||||
if (jsonData == null) {
|
||||
throw new IllegalArgumentException("Json data is null");
|
||||
}
|
||||
if (numberOfSensors < 1 || numberOfSensors > MAX_SENSORS) {
|
||||
throw new IllegalStateException("Number of sensors has not been set");
|
||||
}
|
||||
for (int sensorNumber = 1; sensorNumber <= numberOfSensors; sensorNumber++) {
|
||||
updateSensorChannels(handler, sensorNumber, jsonData);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate through the JSON object and update the channels for which
|
||||
* there are remote sensor values
|
||||
*/
|
||||
private void updateSensorChannels(AmbientWeatherStationHandler handler, int i, final @Nullable String jsonData) {
|
||||
String sensorNumber = String.valueOf(i);
|
||||
StringReader stringReader = new StringReader(jsonData);
|
||||
JsonReader reader = new JsonReader(stringReader);
|
||||
try {
|
||||
reader.beginObject();
|
||||
while (reader.hasNext()) {
|
||||
String name = reader.nextName();
|
||||
if (("temp" + sensorNumber + "f").equals(name)) {
|
||||
handler.updateQuantity(CHGRP_REMOTE_SENSOR + sensorNumber, CH_TEMPERATURE, reader.nextDouble(),
|
||||
ImperialUnits.FAHRENHEIT);
|
||||
} else if (("dewPoint" + sensorNumber).equals(name)) {
|
||||
handler.updateQuantity(CHGRP_REMOTE_SENSOR + sensorNumber, CH_DEW_POINT, reader.nextDouble(),
|
||||
ImperialUnits.FAHRENHEIT);
|
||||
} else if (("feelsLike" + sensorNumber).equals(name)) {
|
||||
handler.updateQuantity(CHGRP_REMOTE_SENSOR + sensorNumber, CH_FEELING_TEMPERATURE,
|
||||
reader.nextDouble(), ImperialUnits.FAHRENHEIT);
|
||||
} else if (("humidity" + sensorNumber).equals(name)) {
|
||||
handler.updateQuantity(CHGRP_REMOTE_SENSOR + sensorNumber, CH_HUMIDITY, reader.nextDouble(),
|
||||
SmartHomeUnits.PERCENT);
|
||||
} else if (("soiltemp" + sensorNumber).equals(name)) {
|
||||
handler.updateQuantity(CHGRP_REMOTE_SENSOR + sensorNumber, CH_SOIL_TEMPERATURE, reader.nextDouble(),
|
||||
ImperialUnits.FAHRENHEIT);
|
||||
} else if (("soilhum" + sensorNumber).equals(name)) {
|
||||
Double soilMoisture = reader.nextDouble();
|
||||
handler.updateQuantity(CHGRP_REMOTE_SENSOR + sensorNumber, CH_SOIL_MOISTURE, soilMoisture,
|
||||
SmartHomeUnits.PERCENT);
|
||||
handler.updateString(CHGRP_REMOTE_SENSOR + sensorNumber, CH_SOIL_MOISTURE_LEVEL,
|
||||
convertSoilMoistureToString(soilMoisture));
|
||||
} else if (("batt" + sensorNumber).equals(name)) {
|
||||
handler.updateString(CHGRP_REMOTE_SENSOR + sensorNumber, CH_BATTERY_INDICATOR, reader.nextString());
|
||||
} else {
|
||||
reader.skipValue();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("IOException from JsonReader: {}", e.getMessage(), e);
|
||||
} finally {
|
||||
try {
|
||||
reader.close();
|
||||
stringReader.close();
|
||||
} catch (IOException e) {
|
||||
// Eat the exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeSoilMoistureMap() {
|
||||
soilMoistureMap.put(33.0, "VERY DRY");
|
||||
soilMoistureMap.put(60.0, "DRY");
|
||||
soilMoistureMap.put(80.0, "MOIST");
|
||||
soilMoistureMap.put(93.0, "WET");
|
||||
soilMoistureMap.put(100.0, "VERY WET");
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the soil moisture to a string representation
|
||||
*/
|
||||
private String convertSoilMoistureToString(double soilMoisture) {
|
||||
Double key = soilMoistureMap.ceilingKey(soilMoisture);
|
||||
return key == null ? "UNKNOWN" : soilMoistureMap.get(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.processor;
|
||||
|
||||
import static org.openhab.binding.ambientweather.internal.AmbientWeatherBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ambientweather.internal.handler.AmbientWeatherStationHandler;
|
||||
import org.openhab.binding.ambientweather.internal.model.EventDataJson;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
|
||||
/**
|
||||
* The {@link Ws0265Processor} is responsible for updating
|
||||
* the channels associated with the WS-0265 series weather stations in
|
||||
* response to the receipt of a weather data update from the Ambient
|
||||
* Weather real-time API.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Ws0265Processor extends AbstractProcessor {
|
||||
|
||||
@Override
|
||||
public void setChannelGroupId() {
|
||||
channelGroupId = CHGRP_WS0265;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumberOfSensors() {
|
||||
remoteSensor.setNumberOfSensors(7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInfoUpdate(AmbientWeatherStationHandler handler, String station, String name, String location) {
|
||||
// Update name and location channels
|
||||
handler.updateString(CHGRP_STATION, CH_NAME, name);
|
||||
handler.updateString(CHGRP_STATION, CH_LOCATION, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processWeatherData(AmbientWeatherStationHandler handler, String station, String jsonData) {
|
||||
EventDataJson data = parseEventData(station, jsonData);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the weather data channels
|
||||
handler.updateDate(channelGroupId, CH_OBSERVATION_TIME, data.date);
|
||||
handler.updateQuantity(channelGroupId, CH_TEMPERATURE, data.tempinf, ImperialUnits.FAHRENHEIT);
|
||||
|
||||
// Update the remote sensor channels
|
||||
remoteSensor.updateChannels(handler, jsonData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.processor;
|
||||
|
||||
import static org.openhab.binding.ambientweather.internal.AmbientWeatherBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ambientweather.internal.handler.AmbientWeatherStationHandler;
|
||||
import org.openhab.binding.ambientweather.internal.model.EventDataJson;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
|
||||
/**
|
||||
* The {@link Ws0900ipProcessor} is responsible for updating
|
||||
* the channels associated with the WS-0900-IP weather stations in
|
||||
* response to the receipt of a weather data update from the Ambient
|
||||
* Weather real-time API.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Ws0900ipProcessor extends AbstractProcessor {
|
||||
|
||||
@Override
|
||||
public void setChannelGroupId() {
|
||||
channelGroupId = CHGRP_WS0900IP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumberOfSensors() {
|
||||
// This station doesn't support remote sensors
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInfoUpdate(AmbientWeatherStationHandler handler, String station, String name, String location) {
|
||||
// Update name and location channels
|
||||
handler.updateString(CHGRP_STATION, CH_NAME, name);
|
||||
handler.updateString(CHGRP_STATION, CH_LOCATION, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processWeatherData(AmbientWeatherStationHandler handler, String station, String jsonData) {
|
||||
EventDataJson data = parseEventData(station, jsonData);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the weather data channels
|
||||
handler.updateDate(channelGroupId, CH_OBSERVATION_TIME, data.date);
|
||||
handler.updateString(channelGroupId, CH_BATTERY_INDICATOR, NOT_APPLICABLE);
|
||||
handler.updateQuantity(channelGroupId, CH_TEMPERATURE, data.tempf, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(channelGroupId, CH_DEW_POINT, data.dewPoint, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(channelGroupId, CH_HUMIDITY, data.humidity, SmartHomeUnits.PERCENT);
|
||||
handler.updateQuantity(channelGroupId, CH_PRESSURE_ABSOLUTE, data.baromabsin, ImperialUnits.INCH_OF_MERCURY);
|
||||
handler.updateQuantity(channelGroupId, CH_PRESSURE_RELATIVE, data.baromrelin, ImperialUnits.INCH_OF_MERCURY);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_SPEED, data.windspeedmph, ImperialUnits.MILES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_DIRECTION_DEGREES, data.winddir, SmartHomeUnits.DEGREE_ANGLE);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_GUST, data.windgustmph, ImperialUnits.MILES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_GUST_MAX_DAILY, data.maxdailygust, ImperialUnits.MILES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_HOURLY_RATE, data.hourlyrainin, SmartHomeUnits.INCHES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_DAY, data.dailyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_WEEK, data.weeklyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_MONTH, data.monthlyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_YEAR, data.yearlyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_TOTAL, data.totalrainin, ImperialUnits.INCH);
|
||||
handler.updateDate(channelGroupId, CH_RAIN_LAST_TIME, data.lastRain);
|
||||
|
||||
// Update calculated channels
|
||||
if (data.baromrelin != null) {
|
||||
pressureTrend.put(data.baromrelin);
|
||||
handler.updateString(channelGroupId, CH_PRESSURE_TREND, pressureTrend.getPressureTrend());
|
||||
}
|
||||
if (data.winddir != null) {
|
||||
handler.updateString(channelGroupId, CH_WIND_DIRECTION, convertWindDirectionToString(data.winddir));
|
||||
}
|
||||
|
||||
// Update indoor sensor channels
|
||||
handler.updateQuantity(CHGRP_INDOOR_SENSOR, CH_TEMPERATURE, data.tempinf, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(CHGRP_INDOOR_SENSOR, CH_HUMIDITY, data.humidityin, SmartHomeUnits.PERCENT);
|
||||
handler.updateString(CHGRP_INDOOR_SENSOR, CH_BATTERY_INDICATOR, NOT_APPLICABLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.processor;
|
||||
|
||||
import static org.openhab.binding.ambientweather.internal.AmbientWeatherBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ambientweather.internal.handler.AmbientWeatherStationHandler;
|
||||
import org.openhab.binding.ambientweather.internal.model.EventDataJson;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
|
||||
/**
|
||||
* The {@link Ws1400ipProcessor} is responsible for updating
|
||||
* the channels associated with the WS-1400-IP series weather stations in
|
||||
* response to the receipt of a weather data update from the Ambient
|
||||
* Weather real-time API.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Ws1400ipProcessor extends AbstractProcessor {
|
||||
|
||||
@Override
|
||||
public void setChannelGroupId() {
|
||||
channelGroupId = CHGRP_WS1400IP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumberOfSensors() {
|
||||
remoteSensor.setNumberOfSensors(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInfoUpdate(AmbientWeatherStationHandler handler, String station, String name, String location) {
|
||||
// Update name and location channels
|
||||
handler.updateString(CHGRP_STATION, CH_NAME, name);
|
||||
handler.updateString(CHGRP_STATION, CH_LOCATION, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processWeatherData(AmbientWeatherStationHandler handler, String station, String jsonData) {
|
||||
EventDataJson data = parseEventData(station, jsonData);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the weather data channels
|
||||
handler.updateDate(channelGroupId, CH_OBSERVATION_TIME, data.date);
|
||||
handler.updateString(channelGroupId, CH_BATTERY_INDICATOR, data.battout);
|
||||
handler.updateQuantity(channelGroupId, CH_TEMPERATURE, data.tempf, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(channelGroupId, CH_FEELING_TEMPERATURE, data.feelsLike, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(channelGroupId, CH_DEW_POINT, data.dewPoint, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(channelGroupId, CH_HUMIDITY, data.humidity, SmartHomeUnits.PERCENT);
|
||||
handler.updateQuantity(channelGroupId, CH_PRESSURE_ABSOLUTE, data.baromabsin, ImperialUnits.INCH_OF_MERCURY);
|
||||
handler.updateQuantity(channelGroupId, CH_PRESSURE_RELATIVE, data.baromrelin, ImperialUnits.INCH_OF_MERCURY);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_SPEED, data.windspeedmph, ImperialUnits.MILES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_DIRECTION_DEGREES, data.winddir, SmartHomeUnits.DEGREE_ANGLE);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_GUST, data.windgustmph, ImperialUnits.MILES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_GUST_MAX_DAILY, data.maxdailygust, ImperialUnits.MILES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_SOLAR_RADIATION, data.solarradiation, SmartHomeUnits.IRRADIANCE);
|
||||
handler.updateNumber(channelGroupId, CH_UV_INDEX, data.uv);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_HOURLY_RATE, data.hourlyrainin, SmartHomeUnits.INCHES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_DAY, data.dailyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_WEEK, data.weeklyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_MONTH, data.monthlyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_YEAR, data.yearlyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_TOTAL, data.totalrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_EVENT, data.eventrainin, ImperialUnits.INCH);
|
||||
handler.updateDate(channelGroupId, CH_RAIN_LAST_TIME, data.lastRain);
|
||||
|
||||
// Update calculated channels
|
||||
if (data.baromrelin != null) {
|
||||
pressureTrend.put(data.baromrelin);
|
||||
handler.updateString(channelGroupId, CH_PRESSURE_TREND, pressureTrend.getPressureTrend());
|
||||
}
|
||||
if (data.winddir != null) {
|
||||
handler.updateString(channelGroupId, CH_WIND_DIRECTION, convertWindDirectionToString(data.winddir));
|
||||
}
|
||||
if (data.uv != null) {
|
||||
handler.updateString(channelGroupId, CH_UV_DANGER, convertUVIndexToString(data.uv));
|
||||
}
|
||||
|
||||
// Update indoor sensor channels
|
||||
handler.updateQuantity(CHGRP_INDOOR_SENSOR, CH_TEMPERATURE, data.tempinf, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(CHGRP_INDOOR_SENSOR, CH_HUMIDITY, data.humidityin, SmartHomeUnits.PERCENT);
|
||||
handler.updateString(CHGRP_INDOOR_SENSOR, CH_BATTERY_INDICATOR, data.battin);
|
||||
|
||||
// Update channels for the remote sensors
|
||||
remoteSensor.updateChannels(handler, jsonData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.processor;
|
||||
|
||||
import static org.openhab.binding.ambientweather.internal.AmbientWeatherBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ambientweather.internal.handler.AmbientWeatherStationHandler;
|
||||
import org.openhab.binding.ambientweather.internal.model.EventDataJson;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
|
||||
/**
|
||||
* The {@link Ws2902aProcessor} is responsible for updating
|
||||
* the channels associated with the WS-2902A weather stations in
|
||||
* response to the receipt of a weather data update from the Ambient
|
||||
* Weather real-time API.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Ws2902aProcessor extends AbstractProcessor {
|
||||
|
||||
@Override
|
||||
public void setChannelGroupId() {
|
||||
channelGroupId = CHGRP_WS2902A;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumberOfSensors() {
|
||||
// This station doesn't support remote sensors
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInfoUpdate(AmbientWeatherStationHandler handler, String station, String name, String location) {
|
||||
// Update name and location channels
|
||||
handler.updateString(CHGRP_STATION, CH_NAME, name);
|
||||
handler.updateString(CHGRP_STATION, CH_LOCATION, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processWeatherData(AmbientWeatherStationHandler handler, String station, String jsonData) {
|
||||
EventDataJson data = parseEventData(station, jsonData);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the weather data channels
|
||||
handler.updateDate(channelGroupId, CH_OBSERVATION_TIME, data.date);
|
||||
handler.updateString(channelGroupId, CH_BATTERY_INDICATOR, NOT_APPLICABLE);
|
||||
handler.updateQuantity(channelGroupId, CH_TEMPERATURE, data.tempf, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(channelGroupId, CH_FEELING_TEMPERATURE, data.feelsLike, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(channelGroupId, CH_DEW_POINT, data.dewPoint, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(channelGroupId, CH_HUMIDITY, data.humidity, SmartHomeUnits.PERCENT);
|
||||
handler.updateQuantity(channelGroupId, CH_PRESSURE_ABSOLUTE, data.baromabsin, ImperialUnits.INCH_OF_MERCURY);
|
||||
handler.updateQuantity(channelGroupId, CH_PRESSURE_RELATIVE, data.baromrelin, ImperialUnits.INCH_OF_MERCURY);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_SPEED, data.windspeedmph, ImperialUnits.MILES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_DIRECTION_DEGREES, data.winddir, SmartHomeUnits.DEGREE_ANGLE);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_GUST, data.windgustmph, ImperialUnits.MILES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_WIND_GUST_MAX_DAILY, data.maxdailygust, ImperialUnits.MILES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_SOLAR_RADIATION, data.solarradiation, SmartHomeUnits.IRRADIANCE);
|
||||
handler.updateNumber(channelGroupId, CH_UV_INDEX, data.uv);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_HOURLY_RATE, data.hourlyrainin, SmartHomeUnits.INCHES_PER_HOUR);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_DAY, data.dailyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_WEEK, data.weeklyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_MONTH, data.monthlyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_YEAR, data.yearlyrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_TOTAL, data.totalrainin, ImperialUnits.INCH);
|
||||
handler.updateQuantity(channelGroupId, CH_RAIN_EVENT, data.eventrainin, ImperialUnits.INCH);
|
||||
handler.updateDate(channelGroupId, CH_RAIN_LAST_TIME, data.lastRain);
|
||||
|
||||
// Update calculated channels
|
||||
if (data.baromrelin != null) {
|
||||
pressureTrend.put(data.baromrelin);
|
||||
handler.updateString(channelGroupId, CH_PRESSURE_TREND, pressureTrend.getPressureTrend());
|
||||
}
|
||||
if (data.winddir != null) {
|
||||
handler.updateString(channelGroupId, CH_WIND_DIRECTION, convertWindDirectionToString(data.winddir));
|
||||
}
|
||||
if (data.uv != null) {
|
||||
handler.updateString(channelGroupId, CH_UV_DANGER, convertUVIndexToString(data.uv));
|
||||
}
|
||||
|
||||
// Update indoor sensor channels
|
||||
handler.updateQuantity(CHGRP_INDOOR_SENSOR, CH_TEMPERATURE, data.tempinf, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(CHGRP_INDOOR_SENSOR, CH_HUMIDITY, data.humidityin, SmartHomeUnits.PERCENT);
|
||||
handler.updateString(CHGRP_INDOOR_SENSOR, CH_BATTERY_INDICATOR, NOT_APPLICABLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.processor;
|
||||
|
||||
import static org.openhab.binding.ambientweather.internal.AmbientWeatherBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.ambientweather.internal.handler.AmbientWeatherStationHandler;
|
||||
import org.openhab.binding.ambientweather.internal.model.EventDataJson;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SmartHomeUnits;
|
||||
|
||||
/**
|
||||
* The {@link Ws8482Processor} is responsible for updating
|
||||
* the channels associated with the WS-1400-IP series weather stations in
|
||||
* response to the receipt of a weather data update from the Ambient
|
||||
* Weather real-time API.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Ws8482Processor extends AbstractProcessor {
|
||||
|
||||
@Override
|
||||
public void setChannelGroupId() {
|
||||
channelGroupId = CHGRP_WS8482;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumberOfSensors() {
|
||||
remoteSensor.setNumberOfSensors(7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processInfoUpdate(AmbientWeatherStationHandler handler, String station, String name, String location) {
|
||||
// Update name and location channels
|
||||
handler.updateString(CHGRP_STATION, CH_NAME, name);
|
||||
handler.updateString(CHGRP_STATION, CH_LOCATION, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processWeatherData(AmbientWeatherStationHandler handler, String station, String jsonData) {
|
||||
EventDataJson data = parseEventData(station, jsonData);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the weather data channels
|
||||
handler.updateDate(channelGroupId, CH_OBSERVATION_TIME, data.date);
|
||||
handler.updateString(channelGroupId, CH_BATTERY_INDICATOR, data.battout);
|
||||
handler.updateQuantity(channelGroupId, CH_TEMPERATURE, data.tempinf, ImperialUnits.FAHRENHEIT);
|
||||
handler.updateQuantity(channelGroupId, CH_HUMIDITY, data.humidityin, SmartHomeUnits.PERCENT);
|
||||
|
||||
// Update the remote sensor channels
|
||||
remoteSensor.updateChannels(handler, jsonData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.util;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PressureTrend} is responsible for determining the 3 hour
|
||||
* barometric pressure trend. All calculations are in inches of Mercury.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PressureTrend extends SlidingTimeWindow<Double> {
|
||||
private final Logger logger = LoggerFactory.getLogger(PressureTrend.class);
|
||||
|
||||
// Pressure trend is established after 3 hours
|
||||
private static final long PRESSURE_TREND_PERIOD = 1000 * 60 * 60 * 3;
|
||||
|
||||
// Thresholds used to determine pressure trends
|
||||
private static final double RAPIDLY_THRESHOLD = 0.06;
|
||||
private static final double STEADY_THRESHOLD = 0.02;
|
||||
|
||||
// Pressure trends
|
||||
private static final String RISING_RAPIDLY = "RISING RAPIDLY";
|
||||
private static final String RISING = "RISING";
|
||||
private static final String FALLING_RAPIDLY = "FALLING RAPIDLY";
|
||||
private static final String FALLING = "FALLING";
|
||||
private static final String STEADY = "STEADY";
|
||||
private static final String UNKNOWN = "UNKNOWN";
|
||||
|
||||
public PressureTrend() {
|
||||
super(PRESSURE_TREND_PERIOD);
|
||||
}
|
||||
|
||||
public String getPressureTrend() {
|
||||
long firstTime;
|
||||
long lastTime;
|
||||
double firstValue;
|
||||
double lastValue;
|
||||
|
||||
if (storage.isEmpty()) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
synchronized (storage) {
|
||||
firstTime = storage.firstKey();
|
||||
lastTime = storage.lastKey();
|
||||
firstValue = storage.get(storage.firstKey()).doubleValue();
|
||||
lastValue = storage.get(storage.lastKey()).doubleValue();
|
||||
}
|
||||
if (lastTime - firstTime < period * 0.99) {
|
||||
// Not within 1% of time period
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
double pressureDifference = lastValue - firstValue;
|
||||
String pressureTrend;
|
||||
if (pressureDifference > RAPIDLY_THRESHOLD) {
|
||||
pressureTrend = RISING_RAPIDLY;
|
||||
} else if (pressureDifference > STEADY_THRESHOLD && pressureDifference <= RAPIDLY_THRESHOLD) {
|
||||
pressureTrend = RISING;
|
||||
} else if (pressureDifference > -STEADY_THRESHOLD && pressureDifference <= STEADY_THRESHOLD) {
|
||||
pressureTrend = STEADY;
|
||||
} else if (pressureDifference > -RAPIDLY_THRESHOLD && pressureDifference <= -STEADY_THRESHOLD) {
|
||||
pressureTrend = FALLING;
|
||||
} else {
|
||||
pressureTrend = FALLING_RAPIDLY;
|
||||
}
|
||||
removeOldEntries();
|
||||
return pressureTrend;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.ambientweather.internal.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SlidingTimeWindow} is responsible for managing a set of
|
||||
* time-based values. This class is used to calculate trends over time.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class SlidingTimeWindow<T> {
|
||||
protected long period = 0;
|
||||
protected final SortedMap<Long, T> storage = Collections.synchronizedSortedMap(new TreeMap<>());
|
||||
|
||||
/**
|
||||
* Create a sliding time window for the provided time period
|
||||
*/
|
||||
public SlidingTimeWindow(long period) {
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
public void put(T value) {
|
||||
storage.put(System.currentTimeMillis(), value);
|
||||
}
|
||||
|
||||
public void removeOldEntries() {
|
||||
long old = System.currentTimeMillis() - period;
|
||||
synchronized (storage) {
|
||||
for (Iterator<Long> iterator = storage.keySet().iterator(); iterator.hasNext();) {
|
||||
long time = iterator.next();
|
||||
if (time < old) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="ambientweather" 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>AmbientWeather Binding</name>
|
||||
<description>This binding supports weather stations from Ambient Weather</description>
|
||||
<author>Mark Hilbush</author>
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?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:ambientweather:bridge">
|
||||
<parameter name="applicationKey" type="text" required="true">
|
||||
<label>Application Key</label>
|
||||
<description>Enter the application key</description>
|
||||
</parameter>
|
||||
<parameter name="apiKey" type="text" required="true">
|
||||
<label>API Key</label>
|
||||
<description>Enter the API key</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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:ambientweather:station">
|
||||
<parameter name="macAddress" type="text" required="true">
|
||||
<label>MAC Address</label>
|
||||
<description>Enter the MAC address of the weather station</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,15 @@
|
||||
# binding
|
||||
binding.ambientweather.name = Ambient Weather Binding
|
||||
binding.ambientweather.description = Binding supporting weather stations from Ambient Weather
|
||||
|
||||
# thing types
|
||||
thing-type.ambientweather.bridge.label = Ambient Weather Bridge
|
||||
thing-type.ambientweather.bridge.description = Ambient Weather Bridge
|
||||
thing-type.ambientweather.ws1400ip.label = Ambient Weather WS-1400-IP
|
||||
thing-type.ambientweather.ws1400ip.description = Ambient Weather WS-1400-IP Weather Station
|
||||
thing-type.ambientweather.ws8482.label = Ambient Weather WS-8482
|
||||
thing-type.ambientweather.ws8482.description = Ambient Weather WS-8482 Weather Station
|
||||
thing-type.ambientweather.ws2902a.label = Ambient Weather WS-2902A
|
||||
thing-type.ambientweather.ws2902a.description = Ambient Weather WS-2902A Weather Station
|
||||
thing-type.ambientweather.ws0900ip.label = Ambient Weather WS-0900-IP
|
||||
thing-type.ambientweather.ws0900ip.description = Ambient Weather WS-0900-IP Weather Station
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ambientweather"
|
||||
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">
|
||||
|
||||
<!-- Ambient Weather Account -->
|
||||
<bridge-type id="bridge">
|
||||
<label>Ambient Weather Account</label>
|
||||
<description>Represents an account at Ambient Weather (ambientweather.net)</description>
|
||||
<config-description-ref uri="thing-type:ambientweather:bridge"/>
|
||||
</bridge-type>
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ambientweather"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Channel group types in common for all weather stations -->
|
||||
<!-- The channel group type for a specific station is in the xml file for that station -->
|
||||
<channel-group-type id="station">
|
||||
<label>Station Information</label>
|
||||
<description>Station Information</description>
|
||||
<channels>
|
||||
<channel id="name" typeId="stationName"/>
|
||||
<channel id="location" typeId="location"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="indoorSensor">
|
||||
<label>Indoor Sensor</label>
|
||||
<description>Indoor Sensor</description>
|
||||
<channels>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="relativeHumidity" typeId="relativeHumidity"/>
|
||||
<channel id="batteryIndicator" typeId="batteryIndicator"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="remoteSensor">
|
||||
<label>Remote Sensor</label>
|
||||
<description>Remote Sensor</description>
|
||||
<channels>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="dewPoint" typeId="dewPoint"/>
|
||||
<channel id="feelingTemperature" typeId="feelingTemperature"/>
|
||||
<channel id="relativeHumidity" typeId="relativeHumidity"/>
|
||||
<channel id="soilTemperature" typeId="soilTemperature"/>
|
||||
<channel id="soilMoisture" typeId="soilMoisture"/>
|
||||
<channel id="soilMoistureLevel" typeId="soilMoistureLevel"/>
|
||||
<channel id="batteryIndicator" typeId="batteryIndicator"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,320 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ambientweather"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<channel-type id="stationName">
|
||||
<item-type>String</item-type>
|
||||
<label>Name</label>
|
||||
<description>Weather station name</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="location">
|
||||
<item-type>String</item-type>
|
||||
<label>Location</label>
|
||||
<description>Weather station location</description>
|
||||
<state readOnly="true" pattern="%s"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="observationTime">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Observation Time</label>
|
||||
<description>Observation date and time</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dewPoint">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Dew Point Temperature</label>
|
||||
<description>Dew Point temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="feelingTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Feeling Temperature</label>
|
||||
<description>Feeling temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="relativeHumidity">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Relative Humidity</label>
|
||||
<description>Forecast relative humidity</description>
|
||||
<category>Humidity</category>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="pressure">
|
||||
<item-type>Number:Pressure</item-type>
|
||||
<label>Pressure</label>
|
||||
<description>Barometric Pressure</description>
|
||||
<category>Pressure</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="pressureTrend">
|
||||
<item-type>String</item-type>
|
||||
<label>Pressure Trend</label>
|
||||
<description>Pressure evolution trend (up, down, stable)</description>
|
||||
<category>Pressure</category>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainHourlyRate">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Rain Fall Hourly Rate</label>
|
||||
<description>Rain fall hourly rate</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.7f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainDay">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Rain Fall Day</label>
|
||||
<description>Rain fall during the day</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainWeek">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Rain Fall Week</label>
|
||||
<description>Rain fall amount for the week</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainMonth">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Rain Fall Month</label>
|
||||
<description>Rain fall amount for the month</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainYear">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Rain Fall Year</label>
|
||||
<description>Rain fall amount for the year</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainTotal">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Rain Fall Total</label>
|
||||
<description>Rain fall total amount since factpry reset</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rain24Hour">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Rain Fall 24 Hours</label>
|
||||
<description>Rain fall in last 24 hours</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainEvent">
|
||||
<item-type>Number:Length</item-type>
|
||||
<label>Rain Fall Event</label>
|
||||
<description>Rain fall total for last rain event</description>
|
||||
<category>Rain</category>
|
||||
<state readOnly="true" pattern="%.2f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="rainLastTime">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Rain Last Time</label>
|
||||
<description>Date and time of last rainfall</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windSpeed">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Wind Speed</label>
|
||||
<description>Wind speed</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windSpeedMaxDaily">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Maximum Daily Wind Speed</label>
|
||||
<description>Maximum daily wind speed</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirectionDegrees">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Wind Direction Degrees</label>
|
||||
<description>Wind direction in degrees</description>
|
||||
<state readOnly="true" min="0" max="360" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirection">
|
||||
<item-type>String</item-type>
|
||||
<label>Wind Direction</label>
|
||||
<description>Wind direction</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windGust">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Wind Gust</label>
|
||||
<description>Wind gust</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windGustDirectionDegrees">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Wind Gust Direction Degrees</label>
|
||||
<description>Wind gust direction in degrees</description>
|
||||
<state readOnly="true" min="0" max="360" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windGustDirection">
|
||||
<item-type>String</item-type>
|
||||
<label>Wind Gust Direction</label>
|
||||
<description>Wind gust direction</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windGustMaxDaily">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Maximum Daily Wind Gust</label>
|
||||
<description>Maximum daily wind gust</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windSpeedAvg2Minute">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Wind Speed 2-Minute</label>
|
||||
<description>Wind speed 2-minute average</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirectionDegreesAvg2Minute">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Wind Direction Degrees 2-minute</label>
|
||||
<description>Wind direction 2-minute average in degrees</description>
|
||||
<state readOnly="true" min="0" max="360" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirectionAvg2Minute">
|
||||
<item-type>String</item-type>
|
||||
<label>Wind Direction 2-minute</label>
|
||||
<description>Wind direction 2-minute Average</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windSpeedAvg10Minute">
|
||||
<item-type>Number:Speed</item-type>
|
||||
<label>Wind Speed 10-Minute</label>
|
||||
<description>Wind speed 10-minute average</description>
|
||||
<category>Wind</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirectionDegreesAvg10Minute">
|
||||
<item-type>Number:Angle</item-type>
|
||||
<label>Wind Direction Degrees 10-minute</label>
|
||||
<description>Wind direction 10-minute average in degrees</description>
|
||||
<state readOnly="true" min="0" max="360" pattern="%.0f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="windDirectionAvg10Minute">
|
||||
<item-type>String</item-type>
|
||||
<label>Wind Direction 10-minute</label>
|
||||
<description>Wind direction 10-minute Average</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</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="uvIndex">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>UV Index</label>
|
||||
<description>Ultraviolet radiation index</description>
|
||||
<state readOnly="true" pattern="%.1f"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="uvDanger">
|
||||
<item-type>String</item-type>
|
||||
<label>UV Danger</label>
|
||||
<description>Ultraviolet radiation danger level</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="soilTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Soil Temperature</label>
|
||||
<description>Temperature of the soil</description>
|
||||
<category>Sensor</category>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="soilMoisture">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Soil Moisture</label>
|
||||
<description>Amount of moisture in the soil</description>
|
||||
<category>Sensor</category>
|
||||
<state readOnly="true" min="0" max="100" pattern="%d"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="soilMoistureLevel">
|
||||
<item-type>String</item-type>
|
||||
<label>Soil Moisture Level</label>
|
||||
<description>Soil moisture level from Very Dry to Very Wet</description>
|
||||
<category>Sensor</category>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="co2">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Carbon Dioxide</label>
|
||||
<description>Amount of carbon dioxide</description>
|
||||
<category>Sensor</category>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="relay">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Relay</label>
|
||||
<description>Relay</description>
|
||||
<category>Switch</category>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="batteryIndicator">
|
||||
<item-type>String</item-type>
|
||||
<label>Battery Indicator</label>
|
||||
<description>Indicates if battery is good or needs replacing</description>
|
||||
<category>Battery</category>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ambientweather"
|
||||
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">
|
||||
|
||||
<!-- Ambient Weather WS-0265 -->
|
||||
<thing-type id="ws0265">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>WS-0265</label>
|
||||
<description>Ambient Weather Station WS-0265</description>
|
||||
<channel-groups>
|
||||
<channel-group id="station" typeId="station">
|
||||
<label>Weather Station</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="weatherDataWs0265" typeId="weatherDataWs0265">
|
||||
<label>Weather Data</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor1" typeId="remoteSensor">
|
||||
<label>Remote Sensor 1</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor2" typeId="remoteSensor">
|
||||
<label>Remote Sensor 2</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor3" typeId="remoteSensor">
|
||||
<label>Remote Sensor 3</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor4" typeId="remoteSensor">
|
||||
<label>Remote Sensor 4</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor5" typeId="remoteSensor">
|
||||
<label>Remote Sensor 5</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor6" typeId="remoteSensor">
|
||||
<label>Remote Sensor 6</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor7" typeId="remoteSensor">
|
||||
<label>Remote Sensor 7</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
<config-description-ref uri="thing-type:ambientweather:station"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="weatherDataWs0265">
|
||||
<label>Weather Data</label>
|
||||
<description>Weather Data</description>
|
||||
<channels>
|
||||
<channel id="observationTime" typeId="observationTime"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ambientweather"
|
||||
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">
|
||||
|
||||
<!-- Ambient Weather WS-0900-IP -->
|
||||
<thing-type id="ws0900ip">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>WS-0900-IP</label>
|
||||
<description>Ambient Weather Station WS-0900-IP</description>
|
||||
<channel-groups>
|
||||
<channel-group id="station" typeId="station">
|
||||
<label>Weather Station</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="weatherDataWs0900ip" typeId="weatherDataWs0900ip">
|
||||
<label>Weather Data</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="indoorSensor" typeId="indoorSensor">
|
||||
<label>Indoor Sensor</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
<config-description-ref uri="thing-type:ambientweather:station"/>
|
||||
</thing-type>
|
||||
|
||||
<!-- Channel group type specific to WS-0900-IP -->
|
||||
<channel-group-type id="weatherDataWs0900ip">
|
||||
<label>Weather Data</label>
|
||||
<description>Weather Data</description>
|
||||
<channels>
|
||||
<channel id="observationTime" typeId="observationTime"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="dewPoint" typeId="dewPoint"/>
|
||||
<channel id="relativeHumidity" typeId="relativeHumidity"/>
|
||||
<channel id="pressureAbsolute" typeId="pressure"/>
|
||||
<channel id="pressureRelative" typeId="pressure"/>
|
||||
<channel id="pressureTrend" typeId="pressureTrend"/>
|
||||
<channel id="windDirectionDegrees" typeId="windDirectionDegrees"/>
|
||||
<channel id="windDirection" typeId="windDirection"/>
|
||||
<channel id="windSpeed" typeId="windSpeed"/>
|
||||
<channel id="windGust" typeId="windGust"/>
|
||||
<channel id="windGustMaxDaily" typeId="windGustMaxDaily"/>
|
||||
<channel id="rainHourlyRate" typeId="rainHourlyRate"/>
|
||||
<channel id="rainDay" typeId="rainDay"/>
|
||||
<channel id="rainWeek" typeId="rainWeek"/>
|
||||
<channel id="rainMonth" typeId="rainMonth"/>
|
||||
<channel id="rainYear" typeId="rainYear"/>
|
||||
<channel id="rainTotal" typeId="rainTotal"/>
|
||||
<channel id="rainLastTime" typeId="rainLastTime"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ambientweather"
|
||||
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">
|
||||
|
||||
<!-- Ambient Weather WS-1400-IP -->
|
||||
<thing-type id="ws1400ip">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>WS-1400-IP</label>
|
||||
<description>Ambient Weather Station WS-1400-IP</description>
|
||||
<channel-groups>
|
||||
<channel-group id="station" typeId="station">
|
||||
<label>Weather Station</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="weatherDataWs1400ip" typeId="weatherDataWs1400ip">
|
||||
<label>Weather Data</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="indoorSensor" typeId="indoorSensor">
|
||||
<label>Indoor Sensor</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor1" typeId="remoteSensor">
|
||||
<label>Remote Sensor 1</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor2" typeId="remoteSensor">
|
||||
<label>Remote Sensor 2</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor3" typeId="remoteSensor">
|
||||
<label>Remote Sensor 3</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor4" typeId="remoteSensor">
|
||||
<label>Remote Sensor 4</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor5" typeId="remoteSensor">
|
||||
<label>Remote Sensor 5</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor6" typeId="remoteSensor">
|
||||
<label>Remote Sensor 6</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor7" typeId="remoteSensor">
|
||||
<label>Remote Sensor 7</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor8" typeId="remoteSensor">
|
||||
<label>Remote Sensor 8</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
<config-description-ref uri="thing-type:ambientweather:station"/>
|
||||
</thing-type>
|
||||
|
||||
<!-- Channel group type specific to WS-1400-IP -->
|
||||
<channel-group-type id="weatherDataWs1400ip">
|
||||
<label>Weather Data</label>
|
||||
<description>Weather Data</description>
|
||||
<channels>
|
||||
<channel id="observationTime" typeId="observationTime"/>
|
||||
<channel id="batteryIndicator" typeId="batteryIndicator"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="dewPoint" typeId="dewPoint"/>
|
||||
<channel id="feelingTemperature" typeId="feelingTemperature"/>
|
||||
<channel id="relativeHumidity" typeId="relativeHumidity"/>
|
||||
<channel id="pressureAbsolute" typeId="pressure"/>
|
||||
<channel id="pressureRelative" typeId="pressure"/>
|
||||
<channel id="pressureTrend" typeId="pressureTrend"/>
|
||||
<channel id="windDirectionDegrees" typeId="windDirectionDegrees"/>
|
||||
<channel id="windDirection" typeId="windDirection"/>
|
||||
<channel id="windSpeed" typeId="windSpeed"/>
|
||||
<channel id="windGust" typeId="windGust"/>
|
||||
<channel id="windGustMaxDaily" typeId="windGustMaxDaily"/>
|
||||
<channel id="rainHourlyRate" typeId="rainHourlyRate"/>
|
||||
<channel id="rainDay" typeId="rainDay"/>
|
||||
<channel id="rainWeek" typeId="rainWeek"/>
|
||||
<channel id="rainMonth" typeId="rainMonth"/>
|
||||
<channel id="rainYear" typeId="rainYear"/>
|
||||
<channel id="rainTotal" typeId="rainTotal"/>
|
||||
<channel id="rainEvent" typeId="rainEvent"/>
|
||||
<channel id="rainLastTime" typeId="rainLastTime"/>
|
||||
<channel id="solarRadiation" typeId="solarRadiation"/>
|
||||
<channel id="uvIndex" typeId="uvIndex"/>
|
||||
<channel id="uvDanger" typeId="uvDanger"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ambientweather"
|
||||
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">
|
||||
|
||||
<!-- Ambient Weather WS-2902A -->
|
||||
<thing-type id="ws2902a">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>WS-2902A</label>
|
||||
<description>Ambient Weather Station WS-2902A</description>
|
||||
<channel-groups>
|
||||
<channel-group id="station" typeId="station">
|
||||
<label>Weather Station</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="weatherDataWs2902a" typeId="weatherDataWs2902a">
|
||||
<label>Weather Data</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="indoorSensor" typeId="indoorSensor">
|
||||
<label>Indoor Sensor</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
<config-description-ref uri="thing-type:ambientweather:station"/>
|
||||
</thing-type>
|
||||
|
||||
<!-- Channel group type specific to WS-2902A -->
|
||||
<channel-group-type id="weatherDataWs2902a">
|
||||
<label>Weather Data</label>
|
||||
<description>Weather Data</description>
|
||||
<channels>
|
||||
<channel id="observationTime" typeId="observationTime"/>
|
||||
<channel id="batteryIndicator" typeId="batteryIndicator"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="dewPoint" typeId="dewPoint"/>
|
||||
<channel id="feelingTemperature" typeId="feelingTemperature"/>
|
||||
<channel id="relativeHumidity" typeId="relativeHumidity"/>
|
||||
<channel id="pressureAbsolute" typeId="pressure"/>
|
||||
<channel id="pressureRelative" typeId="pressure"/>
|
||||
<channel id="pressureTrend" typeId="pressureTrend"/>
|
||||
<channel id="windDirectionDegrees" typeId="windDirectionDegrees"/>
|
||||
<channel id="windDirection" typeId="windDirection"/>
|
||||
<channel id="windSpeed" typeId="windSpeed"/>
|
||||
<channel id="windGust" typeId="windGust"/>
|
||||
<channel id="windGustMaxDaily" typeId="windGustMaxDaily"/>
|
||||
<channel id="rainHourlyRate" typeId="rainHourlyRate"/>
|
||||
<channel id="rainDay" typeId="rainDay"/>
|
||||
<channel id="rainWeek" typeId="rainWeek"/>
|
||||
<channel id="rainMonth" typeId="rainMonth"/>
|
||||
<channel id="rainYear" typeId="rainYear"/>
|
||||
<channel id="rainTotal" typeId="rainTotal"/>
|
||||
<channel id="rainEvent" typeId="rainEvent"/>
|
||||
<channel id="rainLastTime" typeId="rainLastTime"/>
|
||||
<channel id="solarRadiation" typeId="solarRadiation"/>
|
||||
<channel id="uvIndex" typeId="uvIndex"/>
|
||||
<channel id="uvDanger" typeId="uvDanger"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ambientweather"
|
||||
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">
|
||||
|
||||
<!-- Ambient Weather WS-8482 -->
|
||||
<thing-type id="ws8482">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>WS-8482</label>
|
||||
<description>Ambient Weather Station WS-8482</description>
|
||||
<channel-groups>
|
||||
<channel-group id="station" typeId="station">
|
||||
<label>Weather Station</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="weatherDataWs8482" typeId="weatherDataWs8482">
|
||||
<label>Weather Data</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor1" typeId="remoteSensor">
|
||||
<label>Remote Sensor 1</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor2" typeId="remoteSensor">
|
||||
<label>Remote Sensor 2</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor3" typeId="remoteSensor">
|
||||
<label>Remote Sensor 3</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor4" typeId="remoteSensor">
|
||||
<label>Remote Sensor 4</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor5" typeId="remoteSensor">
|
||||
<label>Remote Sensor 5</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor6" typeId="remoteSensor">
|
||||
<label>Remote Sensor 6</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
<channel-group id="remoteSensor7" typeId="remoteSensor">
|
||||
<label>Remote Sensor 7</label>
|
||||
<description></description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
<config-description-ref uri="thing-type:ambientweather:station"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="weatherDataWs8482">
|
||||
<label>Weather Data</label>
|
||||
<description>Weather Data</description>
|
||||
<channels>
|
||||
<channel id="observationTime" typeId="observationTime"/>
|
||||
<channel id="batteryIndicator" typeId="batteryIndicator"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="relativeHumidity" typeId="relativeHumidity"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user