| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071 |
- # $Id: 59_Wunderground.pm 13339 2017-02-05 16:55:54Z loredo $
- ##############################################################################
- #
- # 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 utf8;
- use Encode qw(encode_utf8 decode_utf8);
- use Unit;
- 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} = "Unit_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 wu_pws:1,0 wu_bestfct:1,0 stateReadings stateReadingsFormat:0,1 "
- . "wu_features:multiple-strict,alerts,almanac,astronomy,conditions,currenthurricane,forecast,forecast10day,hourly,hourly10day "
- . $readingFnAttributes;
- $hash->{readingsDesc} = {
- 'UV' => { rtype => 'uvi' },
- 'dewpoint' => { rtype => 'c', formula_symbol => 'Td', },
- 'dewpoint_f' => { rtype => 'f', formula_symbol => 'Td', },
- 'fc0_high_c' =>
- { rtype => 'c', format => '%i', formula_symbol => 'Th', },
- 'fc0_high_f' =>
- { rtype => 'f', format => '%i', formula_symbol => 'Th', },
- 'fc0_humidity' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc0_humidity_max' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc0_humidity_min' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc0_icon_url' => { rtype => 'url_http' },
- 'fc0_icon_url_night' => { rtype => 'url_http' },
- 'fc0_low_c' =>
- { rtype => 'c', format => '%i', formula_symbol => 'Tl', },
- 'fc0_low_f' =>
- { rtype => 'f', format => '%i', formula_symbol => 'Tl', },
- 'fc0_rain_day' => { rtype => 'mm' },
- 'fc0_rain_day_in' => { rtype => 'in' },
- 'fc0_rain_night' => { rtype => 'mm' },
- 'fc0_rain_night_in' => { rtype => 'in' },
- 'fc0_snow_day' => { rtype => 'cm' },
- 'fc0_snow_day_in' => { rtype => 'in' },
- 'fc0_snow_night' => { rtype => 'cm' },
- 'fc0_snow_night_in' => { rtype => 'in' },
- 'fc0_title' => { rtype => 'weekday', showLong => 1 },
- 'fc0_title_night' => { rtype => 'weekday_night', showLong => 1 },
- 'fc0_wind_direction' => { rtype => 'compasspoint' },
- 'fc0_wind_direction_max' => { rtype => 'compasspoint' },
- 'fc0_wind_speed' => { rtype => 'kmph', formula_symbol => 'Ws' },
- 'fc0_wind_speed_max' => { rtype => 'kmph', formula_symbol => 'Ws' },
- 'fc0_wind_speed_max_mph' => { rtype => 'mph', formula_symbol => 'Ws' },
- 'fc0_wind_speed_mph' => { rtype => 'mph', formula_symbol => 'Ws' },
- 'fc1_high_c' =>
- { rtype => 'c', format => '%i', formula_symbol => 'Th', },
- 'fc1_high_f' =>
- { rtype => 'f', format => '%i', formula_symbol => 'Th', },
- 'fc1_humidity' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc1_humidity_max' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc1_humidity_min' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc1_icon_url' => { rtype => 'url_http' },
- 'fc1_icon_url_night' => { rtype => 'url_http' },
- 'fc1_low_c' =>
- { rtype => 'c', format => '%i', formula_symbol => 'Tl', },
- 'fc1_low_f' =>
- { rtype => 'f', format => '%i', formula_symbol => 'Tl', },
- 'fc1_rain_day' => { rtype => 'mm' },
- 'fc1_rain_day_in' => { rtype => 'in' },
- 'fc1_rain_night' => { rtype => 'mm' },
- 'fc1_rain_night_in' => { rtype => 'in' },
- 'fc1_snow_day' => { rtype => 'cm' },
- 'fc1_snow_day_in' => { rtype => 'in' },
- 'fc1_snow_night' => { rtype => 'cm' },
- 'fc1_snow_night_in' => { rtype => 'in' },
- 'fc1_title' => { rtype => 'weekday', showLong => 1 },
- 'fc1_title_night' => { rtype => 'weekday_night', showLong => 1 },
- 'fc1_wind_direction' => { rtype => 'compasspoint' },
- 'fc1_wind_direction_max' => { rtype => 'compasspoint' },
- 'fc1_wind_speed' => { rtype => 'kmph', formula_symbol => 'Ws' },
- 'fc1_wind_speed_max' => { rtype => 'kmph', formula_symbol => 'Ws' },
- 'fc1_wind_speed_max_mph' => { rtype => 'mph', formula_symbol => 'Ws' },
- 'fc1_wind_speed_mph' => { rtype => 'mph', formula_symbol => 'Ws' },
- 'fc2_high_c' =>
- { rtype => 'c', format => '%i', formula_symbol => 'Th', },
- 'fc2_high_f' =>
- { rtype => 'f', format => '%i', formula_symbol => 'Th', },
- 'fc2_humidity' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc2_humidity_max' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc2_humidity_min' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc2_icon_url' => { rtype => 'url_http' },
- 'fc2_icon_url_night' => { rtype => 'url_http' },
- 'fc2_low_c' =>
- { rtype => 'c', format => '%i', formula_symbol => 'Tl', },
- 'fc2_low_f' =>
- { rtype => 'f', format => '%i', formula_symbol => 'Tl', },
- 'fc2_rain_day' => { rtype => 'mm' },
- 'fc2_rain_day_in' => { rtype => 'in' },
- 'fc2_rain_night' => { rtype => 'mm' },
- 'fc2_rain_night_in' => { rtype => 'in' },
- 'fc2_snow_day' => { rtype => 'cm' },
- 'fc2_snow_day_in' => { rtype => 'in' },
- 'fc2_snow_night' => { rtype => 'cm' },
- 'fc2_snow_night_in' => { rtype => 'in' },
- 'fc2_title' => { rtype => 'weekday', showLong => 1 },
- 'fc2_title_night' => { rtype => 'weekday_night', showLong => 1, },
- 'fc2_wind_direction' => { rtype => 'compasspoint' },
- 'fc2_wind_direction_max' => { rtype => 'compasspoint' },
- 'fc2_wind_speed' => { rtype => 'kmph', formula_symbol => 'Ws' },
- 'fc2_wind_speed_max' => { rtype => 'kmph', formula_symbol => 'Ws' },
- 'fc2_wind_speed_max_mph' => { rtype => 'mph', formula_symbol => 'Ws' },
- 'fc2_wind_speed_mph' => { rtype => 'mph', formula_symbol => 'Ws' },
- 'fc3_high_c' =>
- { rtype => 'c', format => '%i', formula_symbol => 'Th', },
- 'fc3_high_f' =>
- { rtype => 'f', format => '%i', formula_symbol => 'Th', },
- 'fc3_humidity' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc3_humidity_max' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc3_humidity_min' => { rtype => 'pct', formula_symbol => 'H' },
- 'fc3_icon_url' => { rtype => 'url_http' },
- 'fc3_icon_url_night' => { rtype => 'url_http' },
- 'fc3_low_c' =>
- { rtype => 'c', format => '%i', formula_symbol => 'Tl', },
- 'fc3_low_f' =>
- { rtype => 'f', format => '%i', formula_symbol => 'Tl', },
- 'fc3_rain_day' => { rtype => 'mm' },
- 'fc3_rain_day_in' => { rtype => 'in' },
- 'fc3_rain_night' => { rtype => 'mm' },
- 'fc3_rain_night_in' => { rtype => 'in' },
- 'fc3_snow_day' => { rtype => 'cm' },
- 'fc3_snow_day_in' => { rtype => 'in' },
- 'fc3_snow_night' => { rtype => 'cm' },
- 'fc3_snow_night_in' => { rtype => 'in' },
- 'fc3_title' => { rtype => 'weekday', showLong => 1 },
- 'fc3_title_night' => { rtype => 'weekday_night', showLong => 1 },
- 'fc3_wind_direction' => { rtype => 'compasspoint' },
- 'fc3_wind_direction_max' => { rtype => 'compasspoint' },
- 'fc3_wind_speed' => { rtype => 'kmph', formula_symbol => 'Ws' },
- 'fc3_wind_speed_max' => { rtype => 'kmph', formula_symbol => 'Ws' },
- 'fc3_wind_speed_max_mph' => { rtype => 'mph', formula_symbol => 'Ws' },
- 'fc3_wind_speed_mph' => { rtype => 'mph', formula_symbol => 'Ws' },
- 'feelslike_c' => { rtype => 'c', formula_symbol => 'Tf', },
- 'feelslike_f' => { rtype => 'f', formula_symbol => 'Tf', },
- 'forecast_url' => { rtype => 'url_http' },
- 'history_url' => { rtype => 'url_http' },
- 'humidity' => { rtype => 'pct', formula_symbol => 'H' },
- 'icon_url' => { rtype => 'url_http' },
- 'israining' => { rtype => 'bool', },
- 'lastQueryResult' => { rtype => 'oknok' },
- 'moon_age' => { rtype => 'd' },
- 'moon_pct' => { rtype => 'pct' },
- 'moon_rise' => { rtype => 'time' },
- 'moon_set' => { rtype => 'time' },
- 'ob_url' => { rtype => 'url_http' },
- 'pressure' => { rtype => 'hpamb' },
- 'pressure_in' => { rtype => 'inhg' },
- 'pressure_trend' => { rtype => 'trend' },
- 'rain' => { rtype => 'mm' },
- 'rain_day' => { rtype => 'mm' },
- 'rain_day_in' => { rtype => 'in' },
- 'rain_in' => { rtype => 'in' },
- 'solarradiation' => { rtype => 'wpsm' },
- 'sunrise' => { rtype => 'time' },
- 'sunset' => { rtype => 'time' },
- 'temp_c' => { rtype => 'c' },
- 'temp_f' => { rtype => 'f' },
- 'visibility' => {
- rtype => 'km',
- scope => { empty_replace => '--.-' }
- },
- 'visibility_mi' => {
- rtype => 'mi',
- scope => { empty_replace => '--.-' }
- },
- 'wind_chill' => {
- rtype => 'c',
- formula_symbol => 'Wc',
- scope => { empty_replace => '--.-' },
- },
- 'wind_chill_f' => {
- rtype => 'f',
- formula_symbol => 'Wc',
- scope => { empty_replace => '--.-' }
- },
- 'wind_direction' => { rtype => 'compasspoint' },
- 'wind_gust' => { rtype => 'kmph', formula_symbol => 'Wg' },
- 'wind_gust_mph' => { rtype => 'mph', formula_symbol => 'Wg' },
- 'wind_speed' => { rtype => 'kmph', formula_symbol => 'Ws' },
- 'wind_speed_mph' => { rtype => 'mph', formula_symbol => 'Ws' }
- };
- return;
- }
- #####################################
- sub Wunderground_GetStatus($;$) {
- my ( $hash, $delay ) = @_;
- my $name = $hash->{NAME};
- $hash->{INTERVAL} = AttrVal( $name, "pollInterval", "300" );
- my $bestfct = AttrVal( $name, "wu_bestfct", undef );
- my $features =
- AttrVal( $name, "wu_features", "astronomy,conditions,forecast" );
- my $lang = AttrVal( $name, "wu_lang", "en" );
- my $pws = AttrVal( $name, "wu_pws", undef );
- 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 );
- my $langmap = {
- at => 'OS',
- de => 'DL',
- 'en-gb' => 'LI',
- gb => 'LI',
- uk => 'LI',
- };
- if ( defined( $langmap->{ lc($lang) } ) ) {
- $hash->{LANG} = $langmap->{ lc($lang) };
- }
- else {
- $hash->{LANG} = uc($lang);
- }
- $features =~ s/,{2,}/,/g;
- $features =~ s/\s//g;
- $features =~ s/,/\//g;
- $features .= "/lang:" . $hash->{LANG};
- $features .= "/pws:$pws" if ( defined($pws) );
- $features .= "/bestfct:$bestfct" if ( defined($bestfct) );
- Wunderground_SendCommand( $hash, $features );
- 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 "update" ) {
- 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->{QUERY} = @$a[3];
- $hash->{QUERY} = "pws:" . $hash->{QUERY}
- if ( $hash->{QUERY} =~ /^[A-Z]{3,}\d{2,}$/ );
- if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
- fhem 'attr ' . $name . ' stateReadings temp_c humidity';
- fhem 'attr ' . $name . ' stateReadingsFormat 1';
- fhem 'attr ' . $name . ' wu_features astronomy,conditions,forecast';
- }
- # 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 $query = $hash->{QUERY};
- my $URL =
- "https://api.wunderground.com/api/%APIKEY%/%FEATURES%/q/%QUERY%.json";
- Log3 $name, 5,
- "Wunderground $name: called function Wunderground_SendCommand()";
- $URL =~ s/%APIKEY%/$apikey/;
- $URL =~ s/%FEATURES%/$features/;
- $URL =~ s/%QUERY%/$query/;
- 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 $state = "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 = "undefined";
- #######################
- # process return data
- #
- if ( $return && ref($return) eq "HASH" ) {
- $lastQueryResult = Wunderground_Hash2Readings( $hash, $return );
- }
- }
- # state
- my $stateReadings = AttrVal( $name, "stateReadings", "" );
- my $stateReadingsLang = AttrVal( $name, "stateReadingsLang", "en" );
- my $stateReadingsFormat = AttrVal( $name, "stateReadingsFormat", "0" );
- # $state =
- # makeSTATE( $name, $stateReadings,
- # $stateReadingsLang, $stateReadingsFormat );
- $state = makeSTATE( $name, $stateReadings, $stateReadingsFormat );
- readingsBulkUpdate( $hash, "state", $state );
- readingsBulkUpdateIfChanged( $hash, "lastQueryResult", $lastQueryResult );
- readingsEndUpdate( $hash, 1 );
- return;
- }
- ###################################
- sub Wunderground_Hash2Readings($$;$) {
- my ( $hash, $h, $r ) = @_;
- my $name = $hash->{NAME};
- my $lang = AttrVal( $name, "wu_lang", "en" );
- my $loop = 0;
- $loop = 1 if ( defined($r) );
- if ( ref($h) eq "HASH" ) {
- foreach my $k ( keys %{$h} ) {
- # error
- return $h->{response}{error}{type}
- if ( $k eq "response"
- && defined( $h->{response}{error}{type} ) );
- next
- if ( $k eq "image"
- || $k eq "response"
- || $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 = "humidity" if ( $cr eq "relative_humidity" );
- $cr = "pressure" if ( $cr eq "pressure_mb" );
- $cr = "rain" if ( $cr eq "precip_1hr_metric" );
- $cr = "rain_day" if ( $cr eq "precip_today_metric" );
- $cr = "rain_in" if ( $cr eq "precip_1hr_in" );
- $cr = "rain_day_in" if ( $cr eq "precip_today_in" );
- $cr = "wind_direction" if ( $cr eq "wind_degrees" );
- $cr = "wind_gust" if ( $cr eq "wind_gust_kph" );
- $cr = "wind_gust_mph" if ( $cr eq "wind_gust_mph" );
- $cr = "wind_speed" if ( $cr eq "wind_kph" );
- $cr = "wind_speed_mph" if ( $cr eq "wind_mph" );
- $cr = "wind_chill" if ( $cr eq "windchill_c" );
- $cr = "wind_chill_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, "moon_age",
- $h->{moon_phase}{ageOfMoon} );
- readingsBulkUpdate( $hash, "moon_pct",
- $h->{moon_phase}{percentIlluminated} );
- readingsBulkUpdate( $hash, "moon_phase",
- $h->{moon_phase}{phaseofMoon} );
- }
- # hourly_forecast
- elsif ($r
- && $r =~ /^hourly_forecast(\d+)$/ )
- {
- my $period = $r;
- $period =~ s/[^\d]//g;
- $reading = "hfc" . $period . "_";
- readingsBulkUpdate( $hash, $reading . "condition",
- $h->{conditions} );
- readingsBulkUpdate(
- $hash,
- $reading . "dewpoint_c",
- $h->{dewpoint}{metric}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "dewpoint_f",
- $h->{dewpoint}{english}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "feelslike_c",
- $h->{feelslike}{metric}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "feelslike_f",
- $h->{feelslike}{english}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "heatindex_c",
- $h->{heatindex}{metric}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "heatindex_f",
- $h->{heatindex}{english}
- );
- readingsBulkUpdate( $hash, $reading . "humidity",
- $h->{humidity} );
- readingsBulkUpdate( $hash, $reading . "icon", $h->{icon} );
- readingsBulkUpdate( $hash, $reading . "icon_url",
- $h->{icon_url} );
- readingsBulkUpdate(
- $hash,
- $reading . "mslp_c",
- $h->{mslp}{metric}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "mslp_f",
- $h->{mslp}{english}
- );
- readingsBulkUpdate( $hash, $reading . "pop", $h->{pop} );
- readingsBulkUpdate(
- $hash,
- $reading . "temp_c",
- $h->{temp}{metric}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "temp_f",
- $h->{temp}{english}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "rain",
- $h->{qpf}{metric}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "rain_in",
- $h->{qpf}{english}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "snow",
- $h->{snow}{metric}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "snow_in",
- $h->{snow}{english}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_direction",
- $h->{wdir}{degrees}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_chill",
- $h->{windchill}{metric}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_chill_f",
- $h->{windchill}{english}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_speed",
- $h->{wspd}{metric}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_speed_mph",
- $h->{wspd}{english}
- );
- readingsBulkUpdate( $hash, $reading . "sky", $h->{sky} );
- readingsBulkUpdate( $hash, $reading . "UV", $h->{uvi} );
- my $time = $h->{FCTTIME}{hour} . ":" . $h->{FCTTIME}{min};
- $time =~ s/^(\d):(\d\d)$/0$1:$2/;
- readingsBulkUpdate( $hash, $reading . "time", $time );
- }
- # 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_c",
- $h->{high}{celsius}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "high_f",
- $h->{high}{fahrenheit}
- );
- readingsBulkUpdate( $hash, $reading . "humidity",
- $h->{avehumidity} );
- readingsBulkUpdate( $hash, $reading . "humidity_min",
- $h->{minhumidity} );
- readingsBulkUpdate( $hash, $reading . "humidity_max",
- $h->{maxhumidity} );
- readingsBulkUpdate( $hash, $reading . "icon", $h->{icon} );
- readingsBulkUpdate( $hash, $reading . "icon_url",
- $h->{icon_url} );
- readingsBulkUpdate(
- $hash,
- $reading . "low_c",
- $h->{low}{celsius}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "low_f",
- $h->{low}{fahrenheit}
- );
- readingsBulkUpdate( $hash, $reading . "pop", $h->{pop} );
- readingsBulkUpdate(
- $hash,
- $reading . "rain_day",
- $h->{qpf_allday}{mm}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "rain_day_in",
- $h->{qpf_allday}{in}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "rain_night",
- $h->{qpf_night}{mm}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "rain_night_in",
- $h->{qpf_night}{in}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "snow_day",
- $h->{snow_allday}{cm}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "snow_day_in",
- $h->{snow_allday}{in}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "snow_night",
- $h->{snow_night}{cm}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "snow_night_in",
- $h->{snow_night}{in}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_direction",
- $h->{avewind}{degrees}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_direction_max",
- $h->{maxwind}{degrees}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_speed",
- $h->{avewind}{kph}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_speed_mph",
- $h->{avewind}{mph}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_speed_max",
- $h->{maxwind}{kph}
- );
- readingsBulkUpdate(
- $hash,
- $reading . "wind_speed_max_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" ? "_night" : "" );
- if ( $period < 2 ) {
- $period = "0";
- }
- elsif ( $period < 4 ) {
- $period = "1";
- }
- elsif ( $period < 6 ) {
- $period = "2";
- }
- elsif ( $period < 8 ) {
- $period = "3";
- }
- $reading = "fc" . $period . "_";
- my $symbol_c =
- Encode::encode_utf8( chr(0x202F) . chr(0x00B0) . 'C' );
- my $symbol_f =
- Encode::encode_utf8( chr(0x202F) . chr(0x00B0) . 'F' );
- my $symbol_pct = Encode::encode_utf8( chr(0x202F) . '%' );
- my $symbol_kmh = Encode::encode_utf8( chr(0x00A0) . 'km/h' );
- my $symbol_mph = Encode::encode_utf8( chr(0x00A0) . 'mph' );
- $h->{fcttext_metric} =~ s/(\d)C/$1$symbol_c/g;
- $h->{fcttext} =~ s/(\d)F/$1$symbol_f/g;
- $h->{fcttext_metric} =~ s/(\d)\s*%/$1$symbol_pct/g;
- $h->{fcttext} =~ s/(\d)\s*%/$1$symbol_pct/g;
- $h->{fcttext_metric} =~ s/(\d)\s*km\/h/$1$symbol_kmh/g;
- $h->{fcttext} =~ s/(\d)\s*km\/h/$1$symbol_kmh/g;
- $h->{fcttext_metric} =~ s/(\d)\s*mph/$1$symbol_mph/g;
- $h->{fcttext} =~ s/(\d)\s*mph/$1$symbol_mph/g;
- 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 . "title$night",
- $h->{title} );
- readingsBulkUpdate( $hash, $reading . "text$night",
- $h->{fcttext_metric} );
- readingsBulkUpdate( $hash, $reading . "text_f$night",
- $h->{fcttext} );
- $hash->{readingDesc}{"title$night"}{lang} = $lang if ($lang);
- $hash->{readingDesc}{"text$night"}{lang} = $lang if ($lang);
- $hash->{readingDesc}{"text_f$night"}{lang} = $lang if ($lang);
- }
- # almanac/temp_high
- elsif ($r
- && $r =~ /^almanac\/temp_high(.*)$/ )
- {
- $reading = "almanac_high_";
- readingsBulkUpdate( $hash,
- $reading . "year", $h->{recordyear} );
- readingsBulkUpdate( $hash, $reading . "c", $h->{normal}{C} );
- readingsBulkUpdate( $hash, $reading . "f", $h->{normal}{F} );
- readingsBulkUpdate( $hash, $reading . "record_c",
- $h->{record}{C} );
- readingsBulkUpdate( $hash, $reading . "record_f",
- $h->{record}{F} );
- }
- elsif ($r
- && $r =~ /^almanac\/temp_low(.*)$/ )
- {
- $reading = "almanac_low_";
- readingsBulkUpdate( $hash,
- $reading . "year", $h->{recordyear} );
- readingsBulkUpdate( $hash, $reading . "c", $h->{normal}{C} );
- readingsBulkUpdate( $hash, $reading . "f", $h->{normal}{F} );
- readingsBulkUpdate( $hash, $reading . "record_c",
- $h->{record}{C} );
- readingsBulkUpdate( $hash, $reading . "record_f",
- $h->{record}{F} );
- }
- 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 =~ m/^n\/?a$/i );
- if ( $reading eq "icon" || $reading eq "icon_url" ) {
- my $sunrise = ReadingsVal( $hash->{NAME}, "sunrise", 0 );
- my $sunset = ReadingsVal( $hash->{NAME}, "sunset", 0 );
- $sunrise = time_str2num("1970-01-01 $sunrise:00")
- if ($sunrise);
- $sunset = time_str2num("1970-01-01 $sunset:00")
- if ($sunset);
- my (
- $second, $minute, $hour,
- $dayOfMonth, $month, $yearOffset,
- $dayOfWeek, $dayOfYear, $daylightSavings
- ) = localtime();
- my $currentTime = ( ( $hour * 3600 ) + ( $minute * 60 ) );
- # for icon and icon_url, make sure to add nt_ prefix
- # during the night
- if (
- ( $currentTime > 43200 && $currentTime >= $sunset )
- || ( $currentTime <= 43200
- && $currentTime < $sunrise )
- )
- {
- $value = "nt_" . $value if ( $reading eq "icon" );
- $value = $1 . "nt_" . $2 . $3
- if ( $reading eq "icon_url"
- && $value =~ /^(.*\/)([A-Za-z0-9]+)(\.[a-z]+)$/ );
- }
- # for icon and icon_url, make sure to remove nt_ prefix
- # during the daytime
- else {
- $value =~ s/^nt_// if ( $reading eq "icon" );
- $value =~ s/\/nt_// if ( $reading eq "icon_url" );
- }
- }
- elsif ( $reading eq "almanac_.airport_code" ) {
- $reading = "almanac_airport_code";
- }
- $value = "0"
- if ( $reading =~ /^wind_(gust|speed).*$/
- && looks_like_number($value)
- && $value < 0 );
- $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++;
- }
- }
- return "ok" if ( !$loop );
- }
- ###################################
- 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;
- }
- 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> <query></code>
- <br><br>
- Example:
- <ul><br>
- <code>
- define WUweather Wunderground d123ab11bb2c3456 IBAYERNM70<br>
- define WUweather Wunderground d123ab11bb2c3456 Germany/Berlin<br>
- </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 (default=300)</li>
- <li>wu_bestfct - Use Weather Undergrond Best Forecast for forecast (default=1)</li>
- <li>wu_features - One or more of the data features to be fetched (default=astronomy,conditions,forecast)</li>
- <li>wu_lang - Returns the API response in the specified language (default=en)</li>
- <li>wu_pws - Use personal weather stations for conditions (default=1)</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
|