# $Id$
################################################################################
# 59_WUup.pm
#
# Copyright: mahowi
# e-mail: mahowi@gmx.net
#
# Based on 55_weco.pm by betateilchen
#
# 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 .
#
################################################################################
package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
use HttpUtils;
use UConv;
my $version = "0.9.5";
# Declare functions
sub WUup_Initialize($);
sub WUup_Define($$$);
sub WUup_Undef($$);
sub WUup_Set($@);
sub WUup_Attr(@);
sub WUup_stateRequestTimer($);
sub WUup_send($);
sub WUup_receive($);
################################################################################
#
# Main routines
#
################################################################################
sub WUup_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = "WUup_Define";
$hash->{UndefFn} = "WUup_Undef";
$hash->{SetFn} = "WUup_Set";
$hash->{AttrFn} = "WUup_Attr";
$hash->{AttrList} =
"disable:1 "
. "disabledForIntervals "
. "interval "
. "unit_windspeed:km/h,m/s "
. "wubaromin wudailyrainin wudewptf wuhumidity wurainin wusoilmoisture "
. "wusoiltempf wusolarradiation wutempf wuUV wuwinddir wuwinddir_avg2m "
. "wuwindgustdir wuwindgustdir_10m wuwindgustmph wuwindgustmph_10m "
. "wuwindspdmph_avg2m wuwindspeedmph "
. $readingFnAttributes;
$hash->{VERSION} = $version;
}
sub WUup_Define($$$) {
my ( $hash, $def ) = @_;
my @a = split( "[ \t][ \t]*", $def );
return "syntax: define WUup "
if ( int(@a) != 4 );
my $name = $hash->{NAME};
$hash->{VERSION} = $version;
$hash->{INTERVAL} = 300;
$hash->{helper}{stationid} = $a[2];
$hash->{helper}{password} = $a[3];
$hash->{helper}{softwaretype} = 'FHEM';
$hash->{helper}{url} =
"https://weatherstation.wunderground.com/weatherstation/updateweatherstation.php";
$hash->{helper}{url_rf} =
"https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php";
readingsSingleUpdate( $hash, "state", "defined", 1 );
$attr{$name}{room} = "Weather" if ( !defined( $attr{$name}{room} ) );
$attr{$name}{unit_windspeed} = "km/h"
if ( !defined( $attr{$name}{unit_windspeed} ) );
RemoveInternalTimer($hash);
if ($init_done) {
WUup_stateRequestTimer($hash);
}
else {
InternalTimer( gettimeofday(), "WUup_stateRequestTimer", $hash, 0 );
}
Log3 $name, 3, "WUup ($name): defined";
return undef;
}
sub WUup_Undef($$) {
my ( $hash, $arg ) = @_;
RemoveInternalTimer($hash);
return undef;
}
sub WUup_Set($@) {
my ( $hash, $name, $cmd, @args ) = @_;
return "\"set $name\" needs at least one argument" unless ( defined($cmd) );
if ( $cmd eq "update" ) {
WUup_stateRequestTimer($hash);
}
else {
return "Unknown argument $cmd, choose one of update:noArg";
}
}
sub WUup_Attr(@) {
my ( $cmd, $name, $attrName, $attrVal ) = @_;
my $hash = $defs{$name};
if ( $attrName eq "disable" ) {
if ( $cmd eq "set" and $attrVal eq "1" ) {
readingsSingleUpdate( $hash, "state", "disabled", 1 );
Log3 $name, 3, "WUup ($name) - disabled";
}
elsif ( $cmd eq "del" ) {
readingsSingleUpdate( $hash, "state", "active", 1 );
Log3 $name, 3, "WUup ($name) - enabled";
}
}
if ( $attrName eq "disabledForIntervals" ) {
if ( $cmd eq "set" ) {
readingsSingleUpdate( $hash, "state", "unknown", 1 );
Log3 $name, 3, "WUup ($name) - disabledForIntervals";
}
elsif ( $cmd eq "del" ) {
readingsSingleUpdate( $hash, "state", "active", 1 );
Log3 $name, 3, "WUup ($name) - enabled";
}
}
if ( $attrName eq "interval" ) {
if ( $cmd eq "set" ) {
if ( $attrVal < 3 ) {
Log3 $name, 1,
"WUup ($name) - interval too small, please use something >= 3 (sec), default is 300 (sec).";
return
"interval too small, please use something >= 3 (sec), default is 300 (sec)";
}
else {
$hash->{INTERVAL} = $attrVal;
Log3 $name, 4, "WUup ($name) - set interval to $attrVal";
}
}
elsif ( $cmd eq "del" ) {
$hash->{INTERVAL} = 300;
Log3 $name, 4, "WUup ($name) - set interval to default";
}
}
return undef;
}
sub WUup_stateRequestTimer($) {
my ($hash) = @_;
my $name = $hash->{NAME};
if ( !IsDisabled($name) ) {
readingsSingleUpdate( $hash, "state", "active", 1 )
if (
(
ReadingsVal( $name, "state", 0 ) eq "defined"
or ReadingsVal( $name, "state", 0 ) eq "disabled"
or ReadingsVal( $name, "state", 0 ) eq "Unknown"
)
);
WUup_send($hash);
}
else {
readingsSingleUpdate( $hash, "state", "disabled", 1 );
}
InternalTimer( gettimeofday() + $hash->{INTERVAL},
"WUup_stateRequestTimer", $hash, 1 );
Log3 $name, 5,
"Sub WUup_stateRequestTimer ($name) - Request Timer is called";
}
sub WUup_send($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $version = $hash->{VERSION};
my $url = "";
if ( $hash->{INTERVAL} < 300 ) {
$url = $hash->{helper}{url_rf};
}
else {
$url = $hash->{helper}{url};
}
$url .= "?ID=" . $hash->{helper}{stationid};
$url .= "&PASSWORD=" . $hash->{helper}{password};
my $datestring = strftime "%F+%T", gmtime;
$datestring =~ s/:/%3A/g;
$url .= "&dateutc=" . $datestring;
$attr{$name}{unit_windspeed} = "km/h"
if ( !defined( $attr{$name}{unit_windspeed} ) );
my ( $data, $d, $r, $o );
my $a = $attr{$name};
while ( my ( $key, $value ) = each(%$a) ) {
next if substr( $key, 0, 2 ) ne 'wu';
$key = substr( $key, 2, length($key) - 2 );
( $d, $r, $o ) = split( ":", $value );
if ( defined($r) ) {
$o = ( defined($o) ) ? $o : 0;
$value = ReadingsVal( $d, $r, 0 ) + $o;
}
if ( $key =~ /\w+f$/ ) {
$value = UConv::c2f( $value, 4 );
}
elsif ( $key =~ /\w+mph.*/ ) {
if ( $attr{$name}{unit_windspeed} eq "m/s" ) {
Log3 $name, 5, "WUup ($name) - windspeed unit is m/s";
$value = UConv::kph2mph( ( UConv::mps2kph( $value, 4 ) ), 4 );
}
else {
Log3 $name, 5, "WUup ($name) - windspeed unit is km/h";
$value = UConv::kph2mph( $value, 4 );
}
}
elsif ( $key eq "baromin" ) {
$value = UConv::hpa2inhg( $value, 4 );
}
elsif ( $key =~ /.*rainin$/ ) {
$value = UConv::mm2in( $value, 4 );
}
$data .= "&$key=$value";
}
readingsBeginUpdate($hash);
if ( defined($data) ) {
readingsBulkUpdate( $hash, "data", $data );
Log3 $name, 4, "WUup ($name) - data sent: $data";
$url .= $data;
$url .= "&softwaretype=" . $hash->{helper}{softwaretype};
$url .= "&action=updateraw";
if ( $hash->{INTERVAL} < 300 ) {
$url .= "&realtime=1&rtfreq=" . $hash->{INTERVAL};
}
my $param = {
url => $url,
timeout => 6,
hash => $hash,
method => "GET",
header =>
"agent: FHEM-WUup/$version\r\nUser-Agent: FHEM-WUup/$version",
callback => \&WUup_receive
};
Log3 $name, 5, "WUup ($name) - full URL: $url";
HttpUtils_NonblockingGet($param);
# my $response = GetFileFromURL($url);
# readingsBulkUpdate( $hash, "response", $response );
# Log3 $name, 4, "WUup ($name) - server response: $response";
}
else {
CommandDeleteReading( undef, "$name data" );
CommandDeleteReading( undef, "$name response" );
Log3 $name, 3, "WUup ($name) - no data";
readingsBulkUpdate( $hash, "state", "defined" );
}
readingsEndUpdate( $hash, 1 );
return;
}
sub WUup_receive($) {
my ( $param, $err, $data ) = @_;
my $hash = $param->{hash};
my $name = $hash->{NAME};
if ( $err ne "" ) {
Log3 $name, 3,
"WUup ($name) - error while requesting " . $param->{url} . " - $err";
readingsSingleUpdate( $hash, "state", "ERROR", undef );
readingsSingleUpdate( $hash, "response", $err, undef );
}
elsif ( $data ne "" ) {
Log3 $name, 4, "WUup ($name) - server response: $data";
readingsSingleUpdate( $hash, "state", "active", undef );
readingsSingleUpdate( $hash, "response", $data, undef );
}
}
1;
################################################################################
#
# Documentation
#
################################################################################
#
# Changelog:
#
# 2017-01-23 initial release
# 2017-02-10 added german docu
# 2017-02-22 fixed bug when module cannot get reenabled after disabling
# added disabledForIntervals
# changed attribute WUInterval to interval
# default interval 300
# 2017-02-23 added attribute unit_windspeed
# converted units rounded to 4 decimal places
# 2017-03-16 implemented non-blocking mode
# 2017-08-16 integrated RapidFire mode (thanks to Scooty66)
# 2017-10-10 added windspdmph_avg2m, winddir_avg2m, windgustmph_10m,
# windgustdir_10m (thanks to Aeroschmelz for reminding me)
# timeout raised to 6s, fixed state error (thanks to mumpitzstuff)
# 2017-10-16 fixed attributes
# 2017-10-19 added set-command "update"
#
################################################################################
=pod
=item helper
=item summary sends weather data to Weather Underground
=item summary_DE sendet Wetterdaten zu Weather Underground
=begin html
WUup
Define
define <name> WUup <stationId> <password>
This module provides connection to
www.wunderground.com
to send data from your own weather station.
Set-Commands
- update - send data to Weather Underground
Get-Commands
Attributes
- readingFnAttributes
- interval - Interval (seconds) to send data to
www.wunderground.com.
Will be adjusted to 300 (which is the default) if set to a value lower than 3.
If lower than 300, RapidFire mode will be used.
- disable - disables the module
- disabledForIntervals
- unit_windspeed - change the units of your windspeed readings (m/s or km/h)
- wu.... - Attribute name corresponding to
parameter name from api.
Each of these attributes contains information about weather data to be sent
in format
sensorName:readingName
Example: attr WUup wutempf outside:temperature
will
define the attribute wutempf and
reading "temperature" from device "outside" will be sent to
network as parameter "tempf" (which indicates current temperature)
Units get converted to angloamerican system automatically
(°C -> °F; km/h(m/s) -> mph; mm -> in; hPa -> inHg)
The following information is supported:
- winddir - [0-360 instantaneous wind direction]
- windspeedmph - [mph instantaneous wind speed]
- windgustmph - [mph current wind gust, using software specific time period]
- windgustdir - [0-360 using software specific time period]
- windspdmph_avg2m - [mph 2 minute average wind speed mph]
- winddir_avg2m - [0-360 2 minute average wind direction]
- windgustmph_10m - [mph past 10 minutes wind gust mph]
- windgustdir_10m - [0-360 past 10 minutes wind gust direction]
- humidity - [% outdoor humidity 0-100%]
- dewptf- [F outdoor dewpoint F]
- tempf - [F outdoor temperature]
- rainin - [rain inches over the past hour)] -- the accumulated rainfall in the past 60 min
- dailyrainin - [rain inches so far today in local time]
- baromin - [barometric pressure inches]
- soiltempf - [F soil temperature]
- soilmoisture - [%]
- solarradiation - [W/m²]
- UV - [index]
Readings/Events:
- data - data string transmitted to www.wunderground.com
- response - response string received from server
Notes
- Find complete api description
here
- Have fun!
=end html
=begin html_DE
WUup
Define
define <name> WUup <stationId> <password>
Dieses Modul stellt eine Verbindung zu www.wunderground.com
her, um Daten einer eigenen Wetterstation zu versenden..
Set-Befehle
- update - sende Daten an Weather Underground
Get-Befehle
Attribute
- readingFnAttributes
- interval - Sendeinterval in Sekunden. Wird auf 300 (Default-Wert)
eingestellt, wenn der Wert kleiner als 3 ist.
Wenn der Wert kleiner als 300 ist, wird der RapidFire Modus verwendet.
- disable - deaktiviert das Modul
- disabledForIntervals
- unit_windspeed - gibt die Einheit der Readings für die
Windgeschwindigkeiten an (m/s oder km/h)
- wu.... - Attributname entsprechend dem
Parameternamen aus der API.
Jedes dieser Attribute enthält Informationen über zu sendende Wetterdaten
im Format sensorName:readingName
.
Beispiel: attr WUup wutempf outside:temperature
definiert
das Attribut wutempf und sendet das Reading "temperature" vom Gerät "outside" als Parameter "tempf"
(welches die aktuelle Temperatur angibt).
Einheiten werden automatisch ins anglo-amerikanische System umgerechnet.
(°C -> °F; km/h(m/s) -> mph; mm -> in; hPa -> inHg)
Unterstützte Angaben
- Winddir - [0-360 momentane Windrichtung]
- Windspeedmph - [mph momentane Windgeschwindigkeit]
- Windgustmph - [mph aktuellen Böe, mit Software-spezifischem Zeitraum]
- Windgustdir - [0-360 mit Software-spezifischer Zeit]
- Windspdmph_avg2m - [mph durchschnittliche Windgeschwindigkeit innerhalb 2 Minuten]
- Winddir_avg2m - [0-360 durchschnittliche Windrichtung innerhalb 2 Minuten]
- Windgustmph_10m - [mph Böen der vergangenen 10 Minuten]
- Windgustdir_10m - [0-360 Richtung der Böen der letzten 10 Minuten]
- Feuchtigkeit - [% Außenfeuchtigkeit 0-100%]
- Dewptf- [F Taupunkt im Freien]
- Tempf - [F Außentemperatur]
- Rainin - [in Regen in der vergangenen Stunde]
- Dailyrainin - [in Regenmenge bisher heute]
- Baromin - [inHg barometrischer Druck]
- Soiltempf - [F Bodentemperatur]
- Bodenfeuchtigkeit - [%]
- Solarradiation - [W/m²]
- UV - [Index]
Readings/Events:
- data - Daten, die zu www.wunderground.com gesendet werden
- response - Antwort, die vom Server empfangen wird
Notizen
- Die komplette API-Beschreibung findet sich
hier
- Viel Spaß!
=end html_DE
=cut