From 7bca0787676f82c2160b8c715f881f9ebfeda82f Mon Sep 17 00:00:00 2001 From: jpawlowski Date: Tue, 25 Oct 2016 23:30:36 +0000 Subject: [PATCH] 59_Wunderground: add new module for Weather Underground git-svn-id: https://svn.fhem.de/fhem/trunk@12431 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 2 + fhem/FHEM/59_Wunderground.pm | 817 +++++++++++++++++++++++++++++++++++ fhem/HISTORY | 3 + fhem/MAINTAINER.txt | 1 + 4 files changed, 823 insertions(+) create mode 100644 fhem/FHEM/59_Wunderground.pm diff --git a/fhem/CHANGED b/fhem/CHANGED index 401f4c55e..fe2eeef84 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - new: 59_Wunderground: new weather module to fetch + data from Weather Underground - bugfix: 50_HP1000: correct default unit for windSpeed/windGust from m/s to km/h and transfer unit conversions to new UConv.pm - feature: Pushsafer: new option "picture", "picture2" and "picture3" for diff --git a/fhem/FHEM/59_Wunderground.pm b/fhem/FHEM/59_Wunderground.pm new file mode 100644 index 000000000..67f34b08f --- /dev/null +++ b/fhem/FHEM/59_Wunderground.pm @@ -0,0 +1,817 @@ +# $Id$ +############################################################################## +# +# 59_Wunderground.pm +# +# Copyright by Julian Pawlowski +# e-mail: julian.pawlowski at gmail.com +# +# This file is part of fhem. +# +# Fhem is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Fhem is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . +# +############################################################################## + +# http://api.wunderground.com/weather/api + +package main; + +use strict; +use warnings; +use vars qw(%data); +use HttpUtils; +use Encode; +use UConv; +use Data::Dumper; + +sub Wunderground_Hash2Readings($$;$); + +################################### +sub Wunderground_Initialize($) { + my ($hash) = @_; + + Log3 $hash, 5, "Wunderground_Initialize: Entering"; + + my $webhookFWinstance = + join( ",", devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ); + + $hash->{SetFn} = "Wunderground_Set"; + $hash->{DefFn} = "Wunderground_Define"; + $hash->{AttrFn} = "Wunderground_Attr"; + $hash->{UndefFn} = "Wunderground_Undefine"; + $hash->{DbLog_splitFn} = "Wunderground_DbLog_split"; + $hash->{parseParams} = 1; + + $hash->{AttrList} = +"disable:0,1 timeout:1,2,3,4,5 pollInterval:300,450,600,750,900 wu_lang:en,de,at,ch,nl,fr,pl " + . $readingFnAttributes; + + return; +} + +##################################### +sub Wunderground_GetStatus($;$) { + my ( $hash, $delay ) = @_; + my $name = $hash->{NAME}; + $hash->{INTERVAL} = AttrVal( $name, "pollInterval", "300" ); + my $lang = AttrVal( $name, "wu_lang", "en" ); + my $interval = ( + $delay + ? $delay + : $hash->{INTERVAL} + ); + + Log3 $name, 5, + "Wunderground $name: called function Wunderground_GetStatus()"; + + RemoveInternalTimer($hash); + InternalTimer( gettimeofday() + $interval, + "Wunderground_GetStatus", $hash, 0 ); + + return + if ( $delay || AttrVal( $name, "disable", 0 ) == 1 ); + + if ( $lang eq "de" ) { + $hash->{LANG} = "DL"; + } + elsif ( $lang eq "at" ) { + $hash->{LANG} = "OS"; + } + elsif ( $lang eq "ch" ) { + $hash->{LANG} = "SW"; + } + else { + $hash->{LANG} = uc($lang); + } + + Wunderground_SendCommand( $hash, + "astronomy/conditions/forecast/lang:" . $hash->{LANG} ); + + return; +} + +################################### +sub Wunderground_Set($$$) { + my ( $hash, $a, $h ) = @_; + my $name = $hash->{NAME}; + + Log3 $name, 5, "Wunderground $name: called function Wunderground_Set()"; + + return "Argument is missing" if ( int(@$a) < 1 ); + + my $usage = "Unknown argument " . @$a[1] . ", choose one of update:noArg"; + + my $cmd = ''; + my $result; + + # update + if ( lc( @$a[1] ) eq "statusrequest" ) { + Log3 $name, 3, "Wunderground set $name " . @$a[1]; + Wunderground_GetStatus($hash); + } + + # return usage hint + else { + return $usage; + } + + return $result; +} + +################################### +sub Wunderground_Define($$$) { + my ( $hash, $a, $h ) = @_; + my $name = $hash->{NAME}; + my $infix = "Wunderground"; + + Log3 $name, 5, "Wunderground $name: called function Wunderground_Define()"; + + eval { + require JSON; + import JSON qw( decode_json ); + }; + return "Please install Perl JSON to use module Wunderground" + if ($@); + + if ( int(@$a) < 2 ) { + my $msg = "Wrong syntax: define Wunderground "; + Log3 $name, 4, $msg; + return $msg; + } + + $hash->{TYPE} = "Wunderground"; + + $hash->{API_KEY} = @$a[2]; + $hash->{PWS_ID} = @$a[3]; + + # start the status update timer + Wunderground_GetStatus( $hash, 2 ); + + return; +} + +################################### +sub Wunderground_Attr(@) { + my ( $cmd, $name, $attrName, $attrVal ) = @_; + my $hash = $defs{$name}; + + Log3 $name, 5, "Wunderground $name: called function Wunderground_Attr()"; + + return +"Invalid value for attribute $attrName: minimum value is 1 second, maximum 5 seconds" + if ( $attrVal + && $attrName eq "timeout" + && ( $attrVal < 1 || $attrVal > 5 ) ); + + return + "Invalid value for attribute $attrName: minimum value is 300 seconds" + if ( $attrVal && $attrName eq "pollInterval" && $attrVal < 300 ); + + return undef; +} + +############################################################################################################ +# +# Begin of helper functions +# +############################################################################################################ + +################################### +sub Wunderground_SendCommand($$) { + my ( $hash, $features ) = @_; + my $name = $hash->{NAME}; + my $apikey = $hash->{API_KEY}; + my $pws = $hash->{PWS_ID}; + my $URL = + "https://api.wunderground.com/api/%APIKEY%/%FEATURES%/q/PWS:%PWSID%.json"; + + Log3 $name, 5, + "Wunderground $name: called function Wunderground_SendCommand()"; + + $URL =~ s/%APIKEY%/$apikey/; + $URL =~ s/%FEATURES%/$features/; + $URL =~ s/%PWSID%/$pws/; + + Log3 $name, 5, "Wunderground $name: GET " . urlDecode($URL); + + HttpUtils_NonblockingGet( + { + url => $URL, + timeout => AttrVal( $name, "timeout", "3" ), + hash => $hash, + method => "GET", + header => +"agent: FHEM-Wunderground/1.0.0\r\nUser-Agent: FHEM-Wunderground/1.0.0\r\nAccept: application/json", + httpversion => "1.1", + callback => \&Wunderground_ReceiveCommand, + } + ); + + return; +} + +################################### +sub Wunderground_ReceiveCommand($$$) { + my ( $param, $err, $data ) = @_; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + my $lastQueryResult = + ReadingsVal( $name, "lastQueryResult", "Initialized" ); + my $return; + + Log3 $name, 5, + "Wunderground $name: called function Wunderground_ReceiveCommand()"; + + readingsBeginUpdate($hash); + + # service not reachable + if ($err) { + Log3 $name, 4, "Wunderground $name: RCV TIMEOUT: $err"; + + $lastQueryResult = "unavailable"; + } + + # data received + elsif ($data) { + Log3 $name, 4, "Wunderground $name: RCV"; + + if ( $data ne "" ) { + + # fix malformed JSON ... + $data =~ s/^[\s\r\n0-9a-zA-Z]*//; + $data =~ s/[\s\r\n0-9a-zA-Z]*$//; + $data =~ s/[\r\n]+[0-9a-zA-Z]+[\r\n]+//g; + + eval '$return = decode_json( Encode::encode_utf8($data) ); 1'; + if ($@) { + + Log3 $name, 5, +"Wunderground $name: RES ERROR - unable to parse malformed JSON: $@\n" + . $data; + + return undef; + } + else { + Log3 $name, 5, "Wunderground $name: RES"; + } + } + + $lastQueryResult = "ok"; + + ####################### + # process return data + # + if ( $return && ref($return) eq "HASH" ) { + Wunderground_Hash2Readings( $hash, $return ); + } + } + + readingsBulkUpdateIfChanged( $hash, "lastQueryResult", $lastQueryResult ); + readingsEndUpdate( $hash, 1 ); + + return; +} + +################################### +sub Wunderground_Hash2Readings($$;$) { + my ( $hash, $h, $r ) = @_; + my $name = $hash->{NAME}; + my $loop = 0; + $loop = 1 if ( defined($r) ); + + if ( ref($h) eq "HASH" ) { + foreach my $k ( keys %{$h} ) { + next + if ( $k eq "response" + || $k eq "image" + || $k eq "station_id" + || $k =~ /^.*_string$/ ); + + my $reading; + my $cr = $k; + + # hash level1 renaming + $cr = "" if ( $cr eq "current_observation" ); + + # custom reading + $cr = "condition" if ( $cr eq "weather" ); + $cr = "dewpoint" if ( $cr eq "dewpoint_c" ); + $cr = "heatIndex" if ( $cr eq "heat_index_c" ); + $cr = "heatIndex_f" if ( $cr eq "heat_index_f" ); + $cr = "humidity" if ( $cr eq "relative_humidity" ); + $cr = "pressure" if ( $cr eq "pressure_mb" ); + $cr = "pressureTrend" if ( $cr eq "pressure_trend" ); + $cr = "rain" if ( $cr eq "precip_1hr_metric" ); + $cr = "rainDay" if ( $cr eq "precip_today_metric" ); + $cr = "rain_in" if ( $cr eq "precip_1hr_in" ); + $cr = "rainDay_in" if ( $cr eq "precip_today_in" ); + $cr = "temperature" if ( $cr eq "temp_c" ); + $cr = "temperature_f" if ( $cr eq "temp_f" ); + $cr = "temperatureFeel" if ( $cr eq "feelslike_c" ); + $cr = "temperatureFeel_f" if ( $cr eq "feelslike_f" ); + $cr = "uvIndex" if ( $cr eq "UV" ); + $cr = "windDir" if ( $cr eq "wind_degrees" ); + $cr = "windGust" if ( $cr eq "wind_gust_kph" ); + $cr = "windGust_mph" if ( $cr eq "wind_gust_mph" ); + $cr = "windSpeed" if ( $cr eq "wind_kph" ); + $cr = "windSpeed_mph" if ( $cr eq "wind_mph" ); + $cr = "windChill" if ( $cr eq "windchill_c" ); + $cr = "windChill_f" if ( $cr eq "windchill_f" ); + $cr = "visibility" if ( $cr eq "visibility_km" ); + + next + if ( $cr =~ /^sun_phase(.*)$/ + || $cr eq "date" + || $cr eq "wind_dir" ); + next if ( $r && $r =~ /^display_location.*$/ ); + + # observation_* + if ( $cr =~ /^observation_.*$/ ) { + $hash->{LAST_OBSERVATION} = $h->{observation_epoch}; + next; + } + + # local_* + elsif ( $cr =~ /^local_.*$/ ) { + $hash->{LAST} = $h->{local_epoch}; + next; + } + + # moon_phase + elsif ( $cr =~ /^moon_phase(.*)$/ ) { + my $sunrise = $h->{moon_phase}{sunrise}{hour} . ":" + . $h->{moon_phase}{sunrise}{minute}; + my $sunset = $h->{moon_phase}{sunset}{hour} . ":" + . $h->{moon_phase}{sunset}{minute}; + my $moonrise = $h->{moon_phase}{moonrise}{hour} . ":" + . $h->{moon_phase}{moonrise}{minute}; + my $moonset = $h->{moon_phase}{moonset}{hour} . ":" + . $h->{moon_phase}{moonset}{minute}; + + $sunrise =~ s/^(\d):(\d\d)$/0$1:$2/; + $sunrise =~ s/^(\d\d):(\d)$/$1:0$2/; + $sunset =~ s/^(\d):(\d\d)$/0$1:$2/; + $sunset =~ s/^(\d\d):(\d)$/$1:0$2/; + $moonrise =~ s/^(\d):(\d\d)$/0$1:$2/; + $moonrise =~ s/^(\d\d):(\d)$/$1:0$2/; + $moonset =~ s/^(\d):(\d\d)$/0$1:$2/; + $moonset =~ s/^(\d\d):(\d)$/$1:0$2/; + + readingsBulkUpdate( $hash, "sunrise", $sunrise ); + readingsBulkUpdate( $hash, "sunset", $sunset ); + readingsBulkUpdate( $hash, "moonrise", $moonrise ); + readingsBulkUpdate( $hash, "moonset", $moonset ); + + readingsBulkUpdate( $hash, "moonAge", + $h->{moon_phase}{ageOfMoon} ); + readingsBulkUpdate( $hash, "moonPct", + $h->{moon_phase}{percentIlluminated} ); + readingsBulkUpdate( $hash, "moonPhase", + $h->{moon_phase}{phaseofMoon} ); + } + + # simpleforecast + elsif ($r + && $r =~ /^forecast\/simpleforecast\/forecastday(\d+)$/ ) + { + my $period = $h->{period} - 1; + $reading = "fc" . $period . "_"; + + readingsBulkUpdate( $hash, $reading . "condition", + $h->{conditions} ); + readingsBulkUpdate( + $hash, + $reading . "high", + $h->{high}{celsius} + ); + readingsBulkUpdate( + $hash, + $reading . "high_f", + $h->{high}{fahrenheit} + ); + readingsBulkUpdate( $hash, $reading . "humidity", + $h->{avehumidity} ); + readingsBulkUpdate( $hash, $reading . "humidityMin", + $h->{minhumidity} ); + readingsBulkUpdate( $hash, $reading . "humidityMax", + $h->{maxhumidity} ); + readingsBulkUpdate( $hash, $reading . "icon", $h->{icon} ); + readingsBulkUpdate( $hash, $reading . "icon_url", + $h->{icon_url} ); + readingsBulkUpdate( + $hash, + $reading . "low", + $h->{low}{celsius} + ); + readingsBulkUpdate( + $hash, + $reading . "low_f", + $h->{low}{fahrenheit} + ); + readingsBulkUpdate( $hash, $reading . "pop", $h->{pop} ); + readingsBulkUpdate( + $hash, + $reading . "rainDay", + $h->{qpf_allday}{mm} + ); + readingsBulkUpdate( + $hash, + $reading . "rainDay_in", + $h->{qpf_allday}{in} + ); + readingsBulkUpdate( + $hash, + $reading . "rainNight", + $h->{qpf_night}{mm} + ); + readingsBulkUpdate( + $hash, + $reading . "rainNight_in", + $h->{qpf_night}{in} + ); + readingsBulkUpdate( + $hash, + $reading . "snowDay", + $h->{snow_allday}{cm} + ); + readingsBulkUpdate( + $hash, + $reading . "snowDay_in", + $h->{snow_allday}{in} + ); + readingsBulkUpdate( + $hash, + $reading . "snowNight", + $h->{snow_night}{cm} + ); + readingsBulkUpdate( + $hash, + $reading . "snowNight_in", + $h->{snow_night}{in} + ); + readingsBulkUpdate( + $hash, + $reading . "windDir", + $h->{avewind}{degrees} + ); + readingsBulkUpdate( + $hash, + $reading . "windDirMax", + $h->{maxwind}{degrees} + ); + readingsBulkUpdate( + $hash, + $reading . "windSpeed", + $h->{avewind}{kph} + ); + readingsBulkUpdate( + $hash, + $reading . "windSpeed_mph", + $h->{avewind}{mph} + ); + readingsBulkUpdate( + $hash, + $reading . "windSpeedMax", + $h->{maxwind}{kph} + ); + readingsBulkUpdate( + $hash, + $reading . "windSpeedMax_mph", + $h->{maxwind}{mph} + ); + } + + # txt_forecast + elsif ($r + && $r =~ /^forecast\/txt_forecast\/forecastday(\d+)$/ ) + { + my $period = $h->{period}; + my $night = + ( $period eq "1" + || $period eq "3" + || $period eq "5" + || $period eq "7" ? "N" : "" ); + + if ( $period < 2 ) { + $period = "0"; + } + elsif ( $period < 4 ) { + $period = "1"; + } + elsif ( $period < 6 ) { + $period = "2"; + } + elsif ( $period < 8 ) { + $period = "3"; + } + + $reading = "fc" . $period . "_"; + + readingsBulkUpdate( $hash, $reading . "icon$night", + $h->{icon} ); + readingsBulkUpdate( $hash, $reading . "icon_url$night", + $h->{icon_url} ); + readingsBulkUpdate( $hash, $reading . "pop$night", $h->{pop} ); + readingsBulkUpdate( $hash, $reading . "text$night", + $h->{fcttext_metric} ); + readingsBulkUpdate( $hash, $reading . "text_f$night", + $h->{fcttext} ); + readingsBulkUpdate( $hash, $reading . "title$night", + $h->{title} ); + } + + elsif ( ref( $h->{$k} ) eq "HASH" || ref( $h->{$k} ) eq "ARRAY" ) { + $reading .= $r . "/" if ( $r && $r ne "" ); + $reading .= $cr; + + Wunderground_Hash2Readings( $hash, $h->{$k}, $reading ); + } + + else { + $reading .= $r . "_." if ( $r && $r ne "" ); + $reading .= $cr; + my $value = $h->{$k}; + $value = "" if ( !defined($value) || $value eq "NA" ); + + $value = "0" + if ( $reading =~ /^wind(Gust|Speed).*$/ + && $value eq "-9999" ); + + $value =~ s/^(\d+)%$/$1/; + + readingsBulkUpdate( $hash, $reading, $value ); + } + } + } + elsif ( ref($h) eq "ARRAY" ) { + my $i = 0; + + foreach ( @{$h} ) { + if ( ref($_) eq "HASH" || ref($_) eq "ARRAY" ) { + Wunderground_Hash2Readings( $hash, $_, $r . $i ); + } + else { + readingsBulkUpdate( $hash, $r . $i, $_ ); + } + + $i++; + } + } +} + +################################### +sub Wunderground_Undefine($$$) { + my ( $hash, $a, $h ) = @_; + my $name = $hash->{NAME}; + + if ( defined( $hash->{fhem}{infix} ) ) { + Wunderground_removeExtension( $hash->{fhem}{infix} ); + } + + Log3 $name, 5, + "Wunderground $name: called function Wunderground_Undefine()"; + + # Stop the internal GetStatus-Loop and exit + RemoveInternalTimer($hash); + + # release reverse pointer + delete $modules{Wunderground}{defptr}{$name}; + + return; +} + +################################### +sub Wunderground_DbLog_split($$) { + my ( $event, $device ) = @_; + my ( $reading, $value, $unit ) = ""; + my $hash = $defs{$device}; + + if ( $event =~ +/^(windCompasspoint.*|.*_sum10m|.*_avg2m|uvCondition):\s([\w\.,]+)\s*(.*)/ + ) + { + return undef; + } + elsif ( $event =~ +/^(dewpoint|dewpointIndoor|temperature|temperatureIndoor|windChill):\s([\w\.,]+)\s*(.*)/ + ) + { + $reading = $1; + $value = $2; + $unit = "°C"; + } + elsif ( $event =~ + /^(dewpoint_f|temperature_f|windChill_f):\s([\w\.,]+)\s*(.*)/ ) + { + $reading = $1; + $value = $2; + $unit = "°F"; + } + elsif ( $event =~ /^(.*humidity.*):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "%"; + } + elsif ( $event =~ /^(solarradiation):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "W/m2"; + } + elsif ( $event =~ /^(pressureTrend):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = ""; + $value = "0" if ( $2 eq "=" ); + $value = "1" if ( $2 eq "+" ); + $value = "2" if ( $2 eq "-" ); + return undef if ( $value eq "" ); + $unit = ""; + } + elsif ( $event =~ /^(pressure|pressureAbs):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "hPa"; + } + elsif ( $event =~ /^(pressure_in|pressureAbs_in):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "inHg"; + } + elsif ( $event =~ /^(pressure_mm|pressureAbs_mm):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "mmHg"; + } + elsif ( $event =~ /^(rain):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "mm/h"; + } + elsif ( $event =~ /^(rain_in):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "in/h"; + } + elsif ( $event =~ /^(rain|.*rainDay|.*rainNight):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "mm"; + } + elsif ( + $event =~ /^(rain_in|.*rainDay_in|.*rainNight_in):\s([\w\.,]+)\s*(.*)/ ) + { + $reading = $1; + $value = $2; + $unit = "in"; + } + elsif ( $event =~ /^(uvIndex):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "UVI"; + } + elsif ( $event =~ /^(.*windDir.*):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "°"; + } + elsif ( $event =~ + /^(.*windGust|.*windSpeed|.*windSpeedMax):\s([\w\.,]+)\s*(.*)/ ) + { + $reading = $1; + $value = $2; + $unit = "km/h"; + } + elsif ( $event =~ +/^(.*windGust_mph|.*windSpeed_mph|.*windSpeedMax_mph):\s([\w\.,]+)\s*(.*)/ + ) + { + $reading = $1; + $value = $2; + $unit = "mph"; + } + elsif ( $event =~ /^(windGust_bft|windSpeed_bft):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "Bft"; + } + elsif ( $event =~ /^(windGust_mps|windSpeed_mps):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "m/s"; + } + elsif ( $event =~ /^(windGust_fts|windSpeed_fts):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "ft/s"; + } + elsif ( $event =~ /^(windGust_kn|windSpeed_kn):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = "kn"; + } + elsif ( $event =~ /^(Activity):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $value = "1" if ( $2 eq "alive" ); + $value = "0" if ( $2 eq "dead" ); + $unit = ""; + } + elsif ( $event =~ /^(.*condition):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = ""; + $value = "0" if ( $2 eq "clear" ); + $value = "1" if ( $2 eq "sunny" ); + $value = "2" if ( $2 eq "cloudy" ); + $value = "3" if ( $2 eq "rain" ); + return undef if ( $value eq "" ); + $unit = ""; + } + elsif ( $event =~ /^(humidityCondition):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = ""; + $value = "0" if ( $2 eq "dry" ); + $value = "1" if ( $2 eq "low" ); + $value = "2" if ( $2 eq "optimal" ); + $value = "3" if ( $2 eq "wet" ); + $value = "4" if ( $2 eq "rain" ); + return undef if ( $value eq "" ); + $unit = ""; + } + elsif ( $event =~ /(.+):\s([\w\.,]+)\s*(.*)/ ) { + $reading = $1; + $value = $2; + $unit = $3; + } + + Log3 $device, 5, +"Wunderground $device: Splitting event $event > reading=$reading value=$value unit=$unit"; + + return ( $reading, $value, $unit ); +} + +1; + +=pod +=item device +=item summary Get weather data and forecast from Weather Underground +=item summary_DE Ruft Wetterdaten und Vorhersage von Weather Underground ab +=begin html + + +

Wunderground

+
    + This module gets weather data and forecast from Weather Underground weather service. +

    + + Define +

      + define <name> Wunderground <api-key> <pws-id> +

      + Example: +

        + define WUweather Wunderground d123ab11bb2c3456 IBAYERNM70
        +
      +
      +
    +

    + + Set +
      +
    • update - refresh data
    • +
    +

    + + Attributes +
      +
    • pollInterval - Set regular polling interval in seconds (defaults to 300s)
    • +
    • wu_lang - Set data language (default=en)
    • +
    +

    +
+ +=end html + +=begin html_DE + + +

Wunderground

+
    + Eine deutsche Version der Dokumentation ist derzeit nicht vorhanden. Die englische Version ist hier zu finden: +
+ + +=end html_DE + +=cut diff --git a/fhem/HISTORY b/fhem/HISTORY index a0f26e6f8..d358bab94 100644 --- a/fhem/HISTORY +++ b/fhem/HISTORY @@ -703,3 +703,6 @@ - Fri Oct 22 2016 (loredo) - adding new package UConv for unit conversions + +- Wed Oct 26 2016 (loredo) + - adding new module 59_Wunderground diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index 3097296b6..537d320b5 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -235,6 +235,7 @@ FHEM/59_Twilight.pm dietmar63 http://forum.fhem.de Unterstue FHEM/59_PROPLANTA.pm tupol http://forum.fhem.de Unterstuetzende Dienste/Wettermodule (Link als PM an tupol) FHEM/59_WWO.pm baumrasen http://forum.fhem.de Unterstuetzende Dienste/Wettermodule FHEM/59_Weather.pm borisneubert http://forum.fhem.de Unterstuetzende Dienste/Wettermodule +FHEM/59_Wunderground.pm loredo http://forum.fhem.de Unterstuetzende Dienste/Wettermodule FHEM/60_allergy.pm markus-m http://forum.fhem.de Unterstuetzende Dienste FHEM/60_EM.pm rudolfkoenig http://forum.fhem.de SlowRF FHEM/61_EMWZ.pm rudolfkoenig http://forum.fhem.de SlowRF