From eac6ac6dcb921de431efaf675b25c232c099378e Mon Sep 17 00:00:00 2001 From: Daniel Demus Date: Sun, 19 Jun 2022 15:01:54 +0000 Subject: [PATCH] [wundergroundupdatereceiver] Initial contribution (#10105) * [wundergroundupdatereceiver] Initial implementation Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Code review Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Fix some description formatting Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Add some more channel types per request Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Add more headers to response Signed-off-by: Daniel Demus Based on the observed headers from actual traffic to wunderground.com. * [wundergroundupdatereceiver] Discovery service Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Upgrade to 3.2, fix group name constants Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Add a list of channel types Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Add dateutc as synthetic DateTime channel Also add som emore constants Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Do more programmatic configuration Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] More readme Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Update copyright year Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Handle multiple instance request parameters Signed-off-by: Daniel Demus Ie. those that can have an index number in the name, fx. temp1f, temp2f * [wundergroundupdatereceiver] Add unmapped but mappable channels To support multiple devices any new parameters that are submitted cause a new channel to be created Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Don't pass superfluous config to channels Also documentation additions Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Add generated i18n file Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Re-case test method names Signed-off-by: Daniel Demus * [wundergroundupdatereceiver] Re-word thing file section of README Signed-off-by: Daniel Demus Co-authored-by: Daniel Demus Signed-off-by: Andras Uhrin --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + .../NOTICE | 13 + .../README.md | 229 +++++++ .../pom.xml | 17 + .../src/main/feature/feature.xml | 9 + ...rgroundUpdateReceiverBindingConstants.java | 228 +++++++ ...ndergroundUpdateReceiverConfiguration.java | 30 + ...rgroundUpdateReceiverDiscoveryService.java | 129 ++++ .../WundergroundUpdateReceiverHandler.java | 214 +++++++ ...dergroundUpdateReceiverHandlerFactory.java | 85 +++ ...rgroundUpdateReceiverParameterMapping.java | 198 ++++++ ...dergroundUpdateReceiverProfileAdvisor.java | 56 ++ .../WundergroundUpdateReceiverServlet.java | 219 +++++++ ...ergroundUpdateReceiverServletControls.java | 28 + ...ateReceiverUnknownChannelTypeProvider.java | 83 +++ .../main/resources/OH-INF/binding/binding.xml | 11 + .../wundergroundupdatereceiver.properties | 140 +++++ .../wundergroundupdatereceiver_da.properties | 129 ++++ .../OH-INF/thing/channel-group-types.xml | 54 ++ .../resources/OH-INF/thing/channel-types.xml | 580 ++++++++++++++++++ .../resources/OH-INF/thing/thing-types.xml | 41 ++ ...undUpdateReceiverDiscoveryServiceTest.java | 256 ++++++++ ...WundergroundUpdateReceiverServletTest.java | 489 +++++++++++++++ bundles/pom.xml | 1 + 25 files changed, 3245 insertions(+) create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/NOTICE create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/README.md create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/pom.xml create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverBindingConstants.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverConfiguration.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryService.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverHandler.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverHandlerFactory.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverParameterMapping.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverProfileAdvisor.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServlet.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletControls.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverUnknownChannelTypeProvider.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/resources/OH-INF/binding/binding.xml create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/resources/OH-INF/i18n/wundergroundupdatereceiver.properties create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/resources/OH-INF/i18n/wundergroundupdatereceiver_da.properties create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/resources/OH-INF/thing/channel-group-types.xml create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/resources/OH-INF/thing/channel-types.xml create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/main/resources/OH-INF/thing/thing-types.xml create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverDiscoveryServiceTest.java create mode 100644 bundles/org.openhab.binding.wundergroundupdatereceiver/src/test/java/org/openhab/binding/wundergroundupdatereceiver/internal/WundergroundUpdateReceiverServletTest.java diff --git a/CODEOWNERS b/CODEOWNERS index c18e5a7cd82d6..a47dfc347a7d3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -354,6 +354,7 @@ /bundles/org.openhab.binding.wlanthermo/ @CSchlipp /bundles/org.openhab.binding.wled/ @Skinah /bundles/org.openhab.binding.wolfsmartset/ @BoBiene +/bundles/org.openhab.binding.wundergroundupdatereceiver/ @danieldemus /bundles/org.openhab.binding.xmltv/ @clinique /bundles/org.openhab.binding.xmppclient/ @pavel-gololobov /bundles/org.openhab.binding.yamahamusiccast/ @coop-git diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index fd390e952b768..0f5280a1a6f9e 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1771,6 +1771,11 @@ org.openhab.binding.wolfsmartset ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.wundergroundupdatereceiver + ${project.version} + org.openhab.addons.bundles org.openhab.binding.xmltv diff --git a/bundles/org.openhab.binding.wundergroundupdatereceiver/NOTICE b/bundles/org.openhab.binding.wundergroundupdatereceiver/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.wundergroundupdatereceiver/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.wundergroundupdatereceiver/README.md b/bundles/org.openhab.binding.wundergroundupdatereceiver/README.md new file mode 100644 index 0000000000000..1ff4297ff3c4e --- /dev/null +++ b/bundles/org.openhab.binding.wundergroundupdatereceiver/README.md @@ -0,0 +1,229 @@ +# Wunderground Update Receiver Binding + +Many personal weather stations or similar devices are only capable of submitting measurements to the wunderground.com update site. + +This binding enables acting as a receiver of updates from devices that post measurements to https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php. +If the hostname is configurable - as on weather stations based on the Fine Offset Electronics WH2600-IP - this is simple, otherwise you have to set up dns such that it resolves the above hostname to your server, without preventing the server from resolving the proper ip if you want to forward the request. + +The server thus listens at http(s)://:/weatherstation/updateweatherstation.php and the device needs to be pointed at this address. +If you can't configure the device itself to submit to an alternate hostname you would need to set up a dns server that resolves rtupdate.wunderground.com to the IP-address of your server and provide as dns to the device does DHCP. +Make sure not to use this dns server instance for any other DHCP clients. + +The request is in itself simple to parse, so by redirecting it to your openHAB server you can intercept the values and use them to control items in your home. +E.g. use measured wind-speed to close an awning or turn on the sprinkler system after some time without rain. +This binding allows you to mix and match products from various manufacturers that otherwise have a closed system. + +If you wish to pass the measurements on to rtupdate.wunderground.com, you can use a simple rule that triggers on the `wundergroundupdatereceiver:wundergroundUpdateReceiver::metadata#last-query-trigger` to do so. +It can also be used to submit the same measurements to multiple weather services via multiple rules. + +## Supported Things + +Any device that sends weather measurement updates to the wunderground.com update URLs is supported. +It is easiest to use with devices that have a configurable target address, but can be made to work with any internet-connected device, that gets its dns server via DHCP or where the DNS server can be set. + +## Discovery + +The binding starts listening at the above-mentioned URI as soon as it is initialized. +Any request with an unregistered stationId is recorded and if auto-discovery is enabled appears in the inbox, otherwise can be registered when a manual scan is initiated. +For each request parameter a channel is generated, based on a list of known parameters from https://support.weather.com/s/article/PWS-Upload-Protocol?language=en_US and other observed parameters from various devices. +If you have a device that submits a parameter that is unknown in the current version of the binding please feel free to submit an issue to have it added. + +While discovery is active, either in the background or during a manual scan, any request parameters that don't have a channel associated with them are added to the thing's channels. +This supports using multiple devices that submit measurements to the same station ID. +The thing is the wunderground account, not the individual devices submitting measurements. + +## Thing Configuration + +The only configurable value is the station id, which should match the one configured on the device. +If you don't plan on submitting measurements to wunderground.com, it can be any unique non-empty string value, otherwise it must be the actual station ID. + +## Channels + +Each measurement type the wunderground.com update service accepts has a channel. +The channels must be named exactly as the request parameter they receive. +I.e. the wind speed channel must be named `windspeedmph` as that is the request parameter name defined by Wunderground in their API. +The channel name set up in the binding should be considered an id with no semantic content other than pointing to the wounderground API. +Additionally there is a receipt timestamp and a trigger channel. + +### Request parameters are mapped to one of the following channel-types: + +#### Normal channel-types: + +| Request parameter | Channel type id | Type | Label | Description | Group | +|-------------------|------------------------------|----------------------|--------------------------------|----------------------------------------------------------------------------------------|-------------| +| winddir | wind-direction | Number:Angle | Current Wind Direction | Current wind direction | Wind | +| windspeedmph | wind-speed | Number:Speed | Current Wind Speed | Current wind speed, using software specific time period. | Wind | +| windgustmph | wind-gust-speed | Number:Speed | Current Gust Speed | Current wind gust speed, using software specific time period. | Wind | +| windgustdir | wind-gust-direction | Number:Angle | Gust Direction | Current wind gust direction expressed as an angle using software specific time period. | Wind | +| tempf | temperature | Number:Temperature | Outdoor Temperature | Current outdoor temperature | Temperature | +| indoortempf | indoor-temperature | Number:Temperature | Indoor Temperature | Indoor temperature. | Temperature | +| rainin | rain | Number:Length | Hourly Rain | Rain over the past hour. | Rain | +| dailyrainin | rain-daily | Number:Length | Daily Rain | Rain since the start of the day. | Rain | +| solarradiation | solarradiation | Number:Intensity | Solar Radiation | Solar radiation | Sun | +| UV | uv | Number:Dimensionless | UV Index | UV index. | Sun | +| humidity | humidity | Number:Dimensionless | Humidity | Humidity in %. | Humidity | +| indoorhumidity | indoor-humidity | Number:Dimensionless | Indoor Humidity | Indoor humidity in %. | Humidity | +| baromin | + +#### Advanced channel-types: + +| Request parameter | Channel type id | Type | Label | Description | Group | +|-------------------|------------------------------|----------------------|--------------------------------|-----------------------------------------------------------------------------------------------------|-------------| +| windspdmph_avg2m | wind-speed-avg-2min | Number:Speed | Wind Speed 2min Average | 2 minute average wind speed. | Wind | +| winddir_avg2m | wind-direction-avg-2min | Number:Angle | Wind Direction 2min Average | 2 minute average wind direction. | Wind | +| windgustmph_10m | wind-gust-speed-10min | Number:Speed | Gust Speed 10min Average | 10 minute average gust speed. | Wind | +| windgustdir_10m | wind-gust-direction-10min | Number:Angle | Gust Direction 10min Average | 10 minute average gust direction. | Wind | +| windchillf | wind-chill | Number:Temperature | Wind Chill | The apparent wind chill temperature. | Temperature | +| soiltempf | soil-temperature | Number:Temperature | Soil Temperature | Soil temperature. | Temperature | +| weeklyrainin | rain-weekly | Number:Length | Weekly Rain | Rain since the start of this week. | Rain | +| monthlyrainin | rain-monthly | Number:Length | Monthly Rain | Rain since the start if this month. | Rain | +| yearlyrainin | rain-yearly | Number:Length | Yearly Rain | Rain since the start of this year. | Rain | +| weather | metar | String | METAR Weather Report | METAR formatted weather report | Sun_Clouds | +| clouds | clouds | String | Cloud Cover | METAR style cloud cover. | Sun_Clouds | +| visibility | visibility | Number:Length | Visibility | Visibility. | Sun_Clouds | +| dewptf | dew-point | Number:Temperature | Dew Point | Outdoor dew point. | Humidity | +| soilmoisture | soil-moisture | Number:Dimensionless | Soil Moisture | Soil moisture in %. | Moisture | +| leafwetness | leafwetness | Number:Dimensionless | Leaf Wetness | Leaf wetness in %. | Moisture | +| AqNO | nitric-oxide | Number:Dimensionless | Nitric Oxide | Nitric Oxide ppm. | Pollution | +| AqNO2T | nitrogen-dioxide-measured | Number:Dimensionless | Nitrogen Dioxide | Nitrogen Dioxide, true measure ppb. | Pollution | +| AqNO2 | nitrogen-dioxide-nox-no | Number:Dimensionless | NO2 X computed | NO2 computed, NOx-NO ppb. | Pollution | +| AqNO2Y | nitrogen-dioxide-noy-no | Number:Dimensionless | NO2 Y computed, NOy-NO ppb | NO2 computed, NOy-NO ppb. | Pollution | +| AqNOX | nitrogen-oxides | Number:Dimensionless | Nitrogen Oxides | Nitrogen Oxides ppb. | Pollution | +| AqNOY | total-reactive-nitrogen | Number:Dimensionless | Total Reactive Nitrogen | Total reactive nitrogen. | Pollution | +| AqNO3 | no3-ion | Number:Density | NO3 ion | NO3 ion (nitrate, not adjusted for ammonium ion) µG/m3. | Pollution | +| AqSO4 | so4-ion | Number:Density | SO4 ion | SO4 ion (sulfate, not adjusted for ammonium ion) µG/m3. | Pollution | +| AqSO2 | sulfur-dioxide | Number:Dimensionless | Sulfur Dioxide | Sulfur Dioxide, conventional ppb. | Pollution | +| AqSO2T | sulfur-dioxide-trace-levels | Number:Dimensionless | Sulfur Dioxide Trace Levels | Sulfur Dioxide, trace levels ppb. | Pollution | +| AqCO | carbon-monoxide | Number:Dimensionless | Carbon Monoxide | Carbon Monoxide, conventional ppm. | Pollution | +| AqCOT | carbon-monoxide-trace-levels | Number:Dimensionless | Carbon Monoxide Trace Levels | Carbon Monoxide, trace levels ppb. | Pollution | +| AqEC | elemental-carbon | Number:Density | Elemental Carbon | Elemental Carbon, PM2.5 µG/m3. | Pollution | +| AqOC | organic-carbon | Number:Density | Organic Carbon | Organic Carbon, not adjusted for oxygen and hydrogen, PM2.5 µG/m3. | Pollution | +| AqBC | black-carbon | Number:Density | Black Carbon | Black Carbon at 880 nm, µG/m3. | Pollution | +| AqUV-AETH | aethalometer | Number:Density | Second Channel of Aethalometer | second channel of Aethalometer at 370 nm, µG/m3. | Pollution | +| AqPM2.5 | pm2_5-mass | Number:Density | PM2.5 Mass | PM2.5 mass, µG/m3. | Pollution | +| AqPM10 | pm10-mass | Number:Density | PM10 Mass | PM10 mass, µG/m3. | Pollution | +| AqOZONE | ozone | Number:Dimensionless | Ozone | Ozone, ppb. | Pollution | + +#### Metadata channel-types: + +| Request parameter | Channel type id | Type | Label | Description | Group | +|-------------------|------------------------------|----------------------|-----------------------------------|-----------------------------------------------------------------------------------------------------|-------------| +| dateutc | dateutc | String | Last Updated | The date and time of the last update in UTC as submitted by the weather station. This can be 'now'. | Metadata | +| softwaretype | softwaretype | String | Software Type | A software type string from the weather station | Metadata | +| rtfreq | realtime-frequency | Number | Realtime Frequency | How often does the weather station submit measurements | Metadata | +| lowbatt | system:low-battery | Switch | Low Battery | Low battery warning with possible values on (low battery) and off (battery ok) | Metadata | + +#### Synthetic channel-types. These are programmatically added: + +| Channel type id | Type | Channel type | Label | Description | Group | +|------------------------|----------------------|--------------|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| +| dateutc-datetime | dateutc-datetime | state | Last Updated as DateTime | The date and time of the last update in UTC as submitted by the weather station converted to a DateTime value. In case of 'now', the current time is used. | Metadata | +| last-received-datetime | DateTime | state | Last Received | The date and time of the last update. | Metadata | +| last-query-state | String | state | The last query | The part of the last query after the first unurlencoded '?' | Metadata | +| last-query-trigger | String | trigger | The last query | The part of the last query after the first unurlencoded '?' | Metadata | + +The trigger channel's payload is the last querystring, so the following dsl rule script would send the measurements on to wunderground.com: + +``` +val requestQuery = receivedEvent +sendHttpGetRequest("https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php?" + requestQuery) +``` + +The PASSWORD, action and realtime parameters are ignored, as they are wunderground technical constants, that devices must send. +The ID parameter is used to identify the correct thing the request pertains to, i.e. the stationId configuration value. + +As described by the wunderground specification a device can submit multiple values for the outdoor temperature, soil temperature, soil moisture and leaf wetness channels by inserting an index number into the name of the request parameter, fx. tempf can be temp1f, temp2f, etc. +This is supported by the discovery mechanism, creating a channel for each of the values. + +# Examples + +### Thing file + +Configuration using thing and item files is not the recommended method, as you have to manually replicate the configuration discovery produces. +Channels _must_ be named as the request parameters in the channel type table, otherwise the binding will not be able to update with values from requests. +So the request parameter names submitted by your particular device(s) need to be found before being able to write appropriate thing files. +You need to intercept a request from your devices(s) using something like wireshark. +Both thing and item files must be created manually to produce a result practically identical to the one produced through automatic discovery. + +Assuming you have intercepted a request such as `https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php?ID=MYSTATIONID&PASSWORD=XXXXXX&windspeedmph=3.11&dateutc=2021-02-07%2014:04:03&softwaretype=WH2600%20V2.2.8&action=updateraw&realtime=1&rtfreq=5`, you can configure a thing to intercept the request thus: + +``` +Thing wundergroundupdatereceiver:wundergroundUpdateReceiver:ATHINGID "Foo" [stationId="MYSTATIONID"] { + Channels: + Type wind-speed : windspeedmph [] + Type dateutc : dateutc [] + Type softwaretype : softwaretype [] + Type realtime-frequency : rtfreq [] + Type dateutc-datetime : dateutc-datetime [] + Type last-received-datetime : last-received-datetime [] + Type last-query-state : last-query-state [] + Type last-query-trigger : last-query-trigger [] +} +``` + +The pattern for a given channel is `Type : []` from the channel types table. +Casing of the request parameter is significant. +None of the current channels take config. + +### Item file + +``` +Number:Speed WuBinding_WeatherStation_WindSpeed "Current Wind Speed [%.2f %unit%]" { channel="wundergroundupdatereceiver:wundergroundUpdateReceiver:ATHINGID:windspeedmph" } +DateTime WuBinding_LastRecieved "Last Recieved Time [%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS]"