mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-13 11:16:36 +00:00
59_Wunderground: add new module for Weather Underground
git-svn-id: https://svn.fhem.de/fhem/trunk@12431 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
a0bd1a5a88
commit
7bca078767
@ -1,5 +1,7 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# 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.
|
# 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
|
- bugfix: 50_HP1000: correct default unit for windSpeed/windGust from m/s to
|
||||||
km/h and transfer unit conversions to new UConv.pm
|
km/h and transfer unit conversions to new UConv.pm
|
||||||
- feature: Pushsafer: new option "picture", "picture2" and "picture3" for
|
- feature: Pushsafer: new option "picture", "picture2" and "picture3" for
|
||||||
|
817
fhem/FHEM/59_Wunderground.pm
Normal file
817
fhem/FHEM/59_Wunderground.pm
Normal file
@ -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://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# 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 <name> Wunderground <api-key> <pws-id>";
|
||||||
|
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
|
||||||
|
|
||||||
|
<a name="Wunderground" id="Wunderground"></a>
|
||||||
|
<h3>Wunderground</h3>
|
||||||
|
<ul>
|
||||||
|
This module gets weather data and forecast from <a href="http://www.wunderground.com/">Weather Underground</a> weather service.
|
||||||
|
<br><br>
|
||||||
|
<a name="Wundergrounddefine"></a>
|
||||||
|
<b>Define</b>
|
||||||
|
<ul><br>
|
||||||
|
<code>define <name> Wunderground <api-key> <pws-id></code>
|
||||||
|
<br><br>
|
||||||
|
Example:
|
||||||
|
<ul><br>
|
||||||
|
<code>define WUweather Wunderground d123ab11bb2c3456 IBAYERNM70</code><br>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
</ul>
|
||||||
|
<br><br>
|
||||||
|
<a name="Wundergroundset"></a>
|
||||||
|
<b>Set</b>
|
||||||
|
<ul>
|
||||||
|
<li>update - refresh data</li>
|
||||||
|
</ul>
|
||||||
|
<br><br>
|
||||||
|
<a name="Wundergroundattr"></a>
|
||||||
|
<b>Attributes</b>
|
||||||
|
<ul>
|
||||||
|
<li>pollInterval - Set regular polling interval in seconds (defaults to 300s)</li>
|
||||||
|
<li>wu_lang - Set data language (default=en)</li>
|
||||||
|
</ul>
|
||||||
|
<br><br>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
=end html
|
||||||
|
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
<a name="Wunderground" id="Wunderground"></a>
|
||||||
|
<h3>Wunderground</h3>
|
||||||
|
<ul>
|
||||||
|
Eine deutsche Version der Dokumentation ist derzeit nicht vorhanden. Die englische Version ist hier zu finden:
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<a href='http://fhem.de/commandref.html#Wunderground'>Wunderground</a>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
|
||||||
|
=cut
|
@ -703,3 +703,6 @@
|
|||||||
|
|
||||||
- Fri Oct 22 2016 (loredo)
|
- Fri Oct 22 2016 (loredo)
|
||||||
- adding new package UConv for unit conversions
|
- adding new package UConv for unit conversions
|
||||||
|
|
||||||
|
- Wed Oct 26 2016 (loredo)
|
||||||
|
- adding new module 59_Wunderground
|
||||||
|
@ -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_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_WWO.pm baumrasen http://forum.fhem.de Unterstuetzende Dienste/Wettermodule
|
||||||
FHEM/59_Weather.pm borisneubert 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_allergy.pm markus-m http://forum.fhem.de Unterstuetzende Dienste
|
||||||
FHEM/60_EM.pm rudolfkoenig http://forum.fhem.de SlowRF
|
FHEM/60_EM.pm rudolfkoenig http://forum.fhem.de SlowRF
|
||||||
FHEM/61_EMWZ.pm rudolfkoenig http://forum.fhem.de SlowRF
|
FHEM/61_EMWZ.pm rudolfkoenig http://forum.fhem.de SlowRF
|
||||||
|
Loading…
x
Reference in New Issue
Block a user