2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-10 09:16:53 +00:00

59_Weather: add limit forecast, selection between daily and hourly forecast

git-svn-id: https://svn.fhem.de/fhem/trunk@18386 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
LeonGaultier 2019-01-23 06:43:45 +00:00
parent 853466dc7a
commit 9a398ab052
2 changed files with 570 additions and 286 deletions

View File

@ -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.
- feature: 59_Weather: add limit forecast, selection between daily and hourly
forecast
- feature: 49_SSCam: V8.6.1, new attribute snapReadingRotate, time format in
readings and galleries depends from global language
attribute, minor bug fixes

View File

@ -22,7 +22,6 @@
#
##############################################################################
package main;
use strict;
@ -34,33 +33,138 @@ use vars qw($FW_ss);
# use Data::Dumper; # for Debug only
my %pressure_trend_txt_en = ( 0 => "steady", 1 => "rising", 2 => "falling" );
my %pressure_trend_txt_de = ( 0 => "gleichbleibend", 1 => "steigend", 2 => "fallend" );
my %pressure_trend_txt_de =
( 0 => "gleichbleibend", 1 => "steigend", 2 => "fallend" );
my %pressure_trend_txt_nl = ( 0 => "stabiel", 1 => "stijgend", 2 => "dalend" );
my %pressure_trend_txt_fr = ( 0 => "stable", 1 => "croissant", 2 => "décroissant" );
my %pressure_trend_txt_fr =
( 0 => "stable", 1 => "croissant", 2 => "décroissant" );
my %pressure_trend_txt_pl = ( 0 => "stabilne", 1 => "rośnie", 2 => "spada" );
my %pressure_trend_txt_it = ( 0 => "stabile", 1 => "in aumento", 2 => "in diminuzione" );
my %pressure_trend_txt_it =
( 0 => "stabile", 1 => "in aumento", 2 => "in diminuzione" );
my %pressure_trend_sym = ( 0 => "=", 1 => "+", 2 => "-" );
my @directions_txt_en = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW');
my @directions_txt_de = ('N', 'NNO', 'NO', 'ONO', 'O', 'OSO', 'SO', 'SSO', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW');
my @directions_txt_nl = ('N', 'NNO', 'NO', 'ONO', 'O', 'OZO', 'ZO', 'ZZO', 'Z', 'ZZW', 'ZW', 'WZW', 'W', 'WNW', 'NW', 'NNW');
my @directions_txt_fr = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSO', 'SO', 'OSO', 'O', 'ONO', 'NO', 'NNO');
my @directions_txt_pl = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW');
my @directions_txt_it = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSO', 'SO', 'OSO', 'O', 'ONO', 'NO', 'NNO');
my @directions_txt_en = (
'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'
);
my @directions_txt_de = (
'N', 'NNO', 'NO', 'ONO', 'O', 'OSO', 'SO', 'SSO',
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'
);
my @directions_txt_nl = (
'N', 'NNO', 'NO', 'ONO', 'O', 'OZO', 'ZO', 'ZZO',
'Z', 'ZZW', 'ZW', 'WZW', 'W', 'WNW', 'NW', 'NNW'
);
my @directions_txt_fr = (
'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSO', 'SO', 'OSO', 'O', 'ONO', 'NO', 'NNO'
);
my @directions_txt_pl = (
'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'
);
my @directions_txt_it = (
'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSO', 'SO', 'OSO', 'O', 'ONO', 'NO', 'NNO'
);
my %wdays_txt_en = ('Mon' => 'Mon', 'Tue' => 'Tue', 'Wed'=> 'Wed', 'Thu' => 'Thu', 'Fri' => 'Fri', 'Sat' => 'Sat', 'Sun' => 'Sun');
my %wdays_txt_de = ('Mon' => 'Mo', 'Tue' => 'Di', 'Wed'=> 'Mi', 'Thu' => 'Do', 'Fri' => 'Fr', 'Sat' => 'Sa', 'Sun' => 'So');
my %wdays_txt_nl = ('Mon' => 'Maa', 'Tue' => 'Din', 'Wed'=> 'Woe', 'Thu' => 'Don', 'Fri' => 'Vri', 'Sat' => 'Zat', 'Sun' => 'Zon');
my %wdays_txt_fr= ('Mon' => 'Lun', 'Tue' => 'Mar', 'Wed'=> 'Mer', 'Thu' => 'Jeu', 'Fri' => 'Ven', 'Sat' => 'Sam', 'Sun' => 'Dim');
my %wdays_txt_pl = ('Mon' => 'Pon', 'Tue' => 'Wt', 'Wed'=> 'Śr', 'Thu' => 'Czw', 'Fri' => 'Pt', 'Sat' => 'Sob', 'Sun' => 'Nie');
my %wdays_txt_it = ('Mon' => 'Lun', 'Tue' => 'Mar', 'Wed'=> 'Mer', 'Thu' => 'Gio', 'Fri' => 'Ven', 'Sat' => 'Sab', 'Sun' => 'Dom');
my %wdays_txt_en = (
'Mon' => 'Mon',
'Tue' => 'Tue',
'Wed' => 'Wed',
'Thu' => 'Thu',
'Fri' => 'Fri',
'Sat' => 'Sat',
'Sun' => 'Sun'
);
my %wdays_txt_de = (
'Mon' => 'Mo',
'Tue' => 'Di',
'Wed' => 'Mi',
'Thu' => 'Do',
'Fri' => 'Fr',
'Sat' => 'Sa',
'Sun' => 'So'
);
my %wdays_txt_nl = (
'Mon' => 'Maa',
'Tue' => 'Din',
'Wed' => 'Woe',
'Thu' => 'Don',
'Fri' => 'Vri',
'Sat' => 'Zat',
'Sun' => 'Zon'
);
my %wdays_txt_fr = (
'Mon' => 'Lun',
'Tue' => 'Mar',
'Wed' => 'Mer',
'Thu' => 'Jeu',
'Fri' => 'Ven',
'Sat' => 'Sam',
'Sun' => 'Dim'
);
my %wdays_txt_pl = (
'Mon' => 'Pon',
'Tue' => 'Wt',
'Wed' => 'Śr',
'Thu' => 'Czw',
'Fri' => 'Pt',
'Sat' => 'Sob',
'Sun' => 'Nie'
);
my %wdays_txt_it = (
'Mon' => 'Lun',
'Tue' => 'Mar',
'Wed' => 'Mer',
'Thu' => 'Gio',
'Fri' => 'Ven',
'Sat' => 'Sab',
'Sun' => 'Dom'
);
my %status_items_txt_en = ( 0 => "Wind", 1 => "Humidity", 2 => "Temperature", 3 => "Right Now", 4 => "Weather forecast for " );
my %status_items_txt_de = ( 0 => "Wind", 1 => "Feuchtigkeit", 2 => "Temperatur", 3 => "Jetzt Sofort", 4 => "Wettervorhersage für " );
my %status_items_txt_nl = ( 0 => "Wind", 1 => "Vochtigheid", 2 => "Temperatuur", 3 => "Direct", 4 => "Weersvoorspelling voor " );
my %status_items_txt_fr = ( 0 => "Vent", 1 => "Humidité", 2 => "Température", 3 => "Maintenant", 4 => "Prévisions météo pour " );
my %status_items_txt_pl = ( 0 => "Wiatr", 1 => "Wilgotność", 2 => "Temperatura", 3 => "Teraz", 4 => "Prognoza pogody w " );
my %status_items_txt_it = ( 0 => "Vento", 1 => "Umidità", 2 => "Temperatura", 3 => "Adesso", 4 => "Previsioni del tempo per " );
my %status_items_txt_en = (
0 => "Wind",
1 => "Humidity",
2 => "Temperature",
3 => "Right Now",
4 => "Weather forecast for "
);
my %status_items_txt_de = (
0 => "Wind",
1 => "Feuchtigkeit",
2 => "Temperatur",
3 => "Jetzt Sofort",
4 => "Wettervorhersage für "
);
my %status_items_txt_nl = (
0 => "Wind",
1 => "Vochtigheid",
2 => "Temperatuur",
3 => "Direct",
4 => "Weersvoorspelling voor "
);
my %status_items_txt_fr = (
0 => "Vent",
1 => "Humidité",
2 => "Température",
3 => "Maintenant",
4 => "Prévisions météo pour "
);
my %status_items_txt_pl = (
0 => "Wiatr",
1 => "Wilgotność",
2 => "Temperatura",
3 => "Teraz",
4 => "Prognoza pogody w "
);
my %status_items_txt_it = (
0 => "Vento",
1 => "Umidità",
2 => "Temperatura",
3 => "Adesso",
4 => "Previsioni del tempo per "
);
my %wdays_txt_i18n;
my @directions_txt_i18n;
@ -68,14 +172,31 @@ my %pressure_trend_txt_i18n;
my %status_items_txt_i18n;
my @iconlist = (
'storm', 'storm', 'storm', 'thunderstorm', 'thunderstorm', 'rainsnow',
'sleet', 'snow', 'drizzle', 'drizzle', 'icy' ,'chance_of_rain',
'chance_of_rain', 'snowflurries', 'chance_of_snow', 'heavysnow', 'snow', 'sleet',
'sleet', 'dust', 'fog', 'haze', 'smoke', 'flurries',
'windy', 'icy', 'cloudy', 'mostlycloudy_night', 'mostlycloudy', 'partly_cloudy_night',
'partly_cloudy', 'sunny', 'sunny', 'mostly_clear_night', 'mostly_sunny', 'heavyrain',
'sunny', 'scatteredthunderstorms', 'scatteredthunderstorms', 'scatteredthunderstorms', 'scatteredshowers', 'heavysnow',
'chance_of_snow', 'heavysnow', 'partly_cloudy', 'heavyrain', 'chance_of_snow', 'scatteredshowers');
'storm', 'storm',
'storm', 'thunderstorm',
'thunderstorm', 'rainsnow',
'sleet', 'snow',
'drizzle', 'drizzle',
'icy', 'chance_of_rain',
'chance_of_rain', 'snowflurries',
'chance_of_snow', 'heavysnow',
'snow', 'sleet',
'sleet', 'dust',
'fog', 'haze',
'smoke', 'flurries',
'windy', 'icy',
'cloudy', 'mostlycloudy_night',
'mostlycloudy', 'partly_cloudy_night',
'partly_cloudy', 'sunny',
'sunny', 'mostly_clear_night',
'mostly_sunny', 'heavyrain',
'sunny', 'scatteredthunderstorms',
'scatteredthunderstorms', 'scatteredthunderstorms',
'scatteredshowers', 'heavysnow',
'chance_of_snow', 'heavysnow',
'partly_cloudy', 'heavyrain',
'chance_of_snow', 'scatteredshowers'
);
###################################
sub Weather_LanguageInitialize($) {
@ -86,27 +207,32 @@ sub Weather_LanguageInitialize($) {
@directions_txt_i18n = @directions_txt_de;
%pressure_trend_txt_i18n = %pressure_trend_txt_de;
%status_items_txt_i18n = %status_items_txt_de;
} elsif($lang eq "nl") {
}
elsif ( $lang eq "nl" ) {
%wdays_txt_i18n = %wdays_txt_nl;
@directions_txt_i18n = @directions_txt_nl;
%pressure_trend_txt_i18n = %pressure_trend_txt_nl;
%status_items_txt_i18n = %status_items_txt_nl;
} elsif($lang eq "fr") {
}
elsif ( $lang eq "fr" ) {
%wdays_txt_i18n = %wdays_txt_fr;
@directions_txt_i18n = @directions_txt_fr;
%pressure_trend_txt_i18n = %pressure_trend_txt_fr;
%status_items_txt_i18n = %status_items_txt_fr;
} elsif($lang eq "pl") {
}
elsif ( $lang eq "pl" ) {
%wdays_txt_i18n = %wdays_txt_pl;
@directions_txt_i18n = @directions_txt_pl;
%pressure_trend_txt_i18n = %pressure_trend_txt_pl;
%status_items_txt_i18n = %status_items_txt_pl;
} elsif($lang eq "it") {
}
elsif ( $lang eq "it" ) {
%wdays_txt_i18n = %wdays_txt_it;
@directions_txt_i18n = @directions_txt_it;
%pressure_trend_txt_i18n = %pressure_trend_txt_it;
%status_items_txt_i18n = %status_items_txt_it;
} else {
}
else {
%wdays_txt_i18n = %wdays_txt_en;
@directions_txt_i18n = @directions_txt_en;
%pressure_trend_txt_i18n = %pressure_trend_txt_en;
@ -121,12 +247,12 @@ sub Weather_DebugCodes($) {
Debug "Weather Code List, see http://developer.yahoo.com/weather/#codes";
for ( my $c = 0 ; $c <= 47 ; $c++ ) {
Debug sprintf("%2d %30s %30s", $c, $iconlist[$c], $YahooCodes_i18n[$c]);
Debug
sprintf( "%2d %30s %30s", $c, $iconlist[$c], $YahooCodes_i18n[$c] );
}
}
#####################################
sub Weather_Initialize($) {
my ($hash) = @_;
@ -137,6 +263,8 @@ sub Weather_Initialize($) {
$hash->{SetFn} = 'Weather_Set';
$hash->{AttrList} =
'disable:0,1 '
. 'forecast:hourly,daily,every,off '
. 'forecastLimit '
. $readingFnAttributes;
$hash->{NotifyFn} = 'Weather_Notify';
@ -151,7 +279,6 @@ sub degrees_to_direction($@) {
return $directions_txt_i18n[$mod];
}
sub Weather_ReturnWithError($$) {
my ( $hash, $responseRef ) = @_;
my $name = $hash->{NAME};
@ -160,9 +287,14 @@ sub Weather_ReturnWithError($$) {
readingsBulkUpdate( $hash, 'lastError', $responseRef->{status} );
foreach my $r ( keys %{$responseRef} ) {
readingsBulkUpdate($hash, $r, $responseRef->{$r}) if ( ref($responseRef->{$r}) ne 'HASH' );
readingsBulkUpdate( $hash, $r, $responseRef->{$r} )
if ( ref( $responseRef->{$r} ) ne 'HASH' );
}
readingsBulkUpdate($hash, 'state', 'API Maintainer: ' . $responseRef->{apiMaintainer} . ' ErrorMsg: ' . $responseRef->{status});
readingsBulkUpdate( $hash, 'state',
'API Maintainer: '
. $responseRef->{apiMaintainer}
. ' ErrorMsg: '
. $responseRef->{status} );
readingsEndUpdate( $hash, 1 );
my $next = 60; # $next= $hash->{INTERVAL};
@ -190,101 +322,177 @@ sub Weather_WriteReadings($$) {
readingsBeginUpdate($hash);
# delete some unused readings
delete($hash->{READINGS}->{temp_f}) if(defined($hash->{READINGS}->{temp_f}));
delete($hash->{READINGS}->{unit_distance}) if(defined($hash->{READINGS}->{unit_distance}));
delete($hash->{READINGS}->{unit_speed}) if(defined($hash->{READINGS}->{unit_speed}));
delete($hash->{READINGS}->{unit_pressuree}) if(defined($hash->{READINGS}->{unit_pressuree}));
delete($hash->{READINGS}->{unit_temperature}) if(defined($hash->{READINGS}->{unit_temperature}));
delete( $hash->{READINGS}->{temp_f} )
if ( defined( $hash->{READINGS}->{temp_f} ) );
delete( $hash->{READINGS}->{unit_distance} )
if ( defined( $hash->{READINGS}->{unit_distance} ) );
delete( $hash->{READINGS}->{unit_speed} )
if ( defined( $hash->{READINGS}->{unit_speed} ) );
delete( $hash->{READINGS}->{unit_pressuree} )
if ( defined( $hash->{READINGS}->{unit_pressuree} ) );
delete( $hash->{READINGS}->{unit_temperature} )
if ( defined( $hash->{READINGS}->{unit_temperature} ) );
# housekeeping information
readingsBulkUpdate( $hash, 'lastError', '' );
foreach my $r ( keys %{$dataRef} ) {
readingsBulkUpdate( $hash, $r, $dataRef->{$r} )
if ( ref($dataRef->{$r}) ne 'HASH' and ref($dataRef->{$r}) ne 'ARRAY' );
if ( ref( $dataRef->{$r} ) ne 'HASH'
and ref( $dataRef->{$r} ) ne 'ARRAY' );
readingsBulkUpdate( $hash, '.license', $dataRef->{license}->{text} );
}
### current
if ( defined($dataRef->{current}) and ref( $dataRef->{current} ) eq 'HASH' ) {
if ( defined( $dataRef->{current} )
and ref( $dataRef->{current} ) eq 'HASH' )
{
while ( my ( $r, $v ) = each %{ $dataRef->{current} } ) {
readingsBulkUpdate( $hash, $r, $v )
if ( ref($dataRef->{$r}) ne 'HASH' and ref($dataRef->{$r}) ne 'ARRAY' );
if ( ref( $dataRef->{$r} ) ne 'HASH'
and ref( $dataRef->{$r} ) ne 'ARRAY' );
}
readingsBulkUpdate($hash, 'icon', $iconlist[$dataRef->{current}->{code}]);
readingsBulkUpdate( $hash, 'icon',
$iconlist[ $dataRef->{current}->{code} ] );
if ( defined( $dataRef->{current}->{wind_direction} )
and $dataRef->{current}->{wind_direction}
and defined( $dataRef->{current}->{wind_speed} )
and $dataRef->{current}->{wind_speed}
)
and $dataRef->{current}->{wind_speed} )
{
my $wdir= degrees_to_direction($dataRef->{current}->{wind_direction}, @directions_txt_i18n);
readingsBulkUpdate($hash, 'wind_condition', 'Wind: ' . $wdir . ' ' . $dataRef->{current}->{wind_speed} . ' km/h');
my $wdir =
degrees_to_direction( $dataRef->{current}->{wind_direction},
@directions_txt_i18n );
readingsBulkUpdate( $hash, 'wind_condition',
'Wind: '
. $wdir . ' '
. $dataRef->{current}->{wind_speed}
. ' km/h' );
}
}
### forecast
if ( ref( $dataRef->{forecast} ) eq 'HASH' ) {
if ( ref( $dataRef->{forecast} ) eq 'HASH'
and AttrVal( $name, 'forecast', 'every' ) ne 'off' )
{
## hourly
if ( defined($dataRef->{forecast}->{hourly})
if (
defined( $dataRef->{forecast}->{hourly} )
and ref( $dataRef->{forecast}->{hourly} ) eq 'ARRAY'
and scalar( @{ $dataRef->{forecast}->{hourly} } ) > 0 )
and scalar( @{ $dataRef->{forecast}->{hourly} } ) > 0
and ( AttrVal( $name, 'forecast', 'every' ) eq 'every'
or AttrVal( $name, 'forecast', 'hourly' ) eq 'hourly' )
)
{
my $i = 0;
my $limit = AttrVal( $name, 'forecastLimit', -1 );
foreach my $fc ( @{ $dataRef->{forecast}->{hourly} } ) {
$i++;
my $f = "hfc" . $i . "_";
while ( my ( $r, $v ) = each %{$fc} ) {
readingsBulkUpdate( $hash, $f . $r, $v )
if ( ref($dataRef->{$r}) ne 'HASH' and ref($dataRef->{$r}) ne 'ARRAY' );
if ( ref( $dataRef->{$r} ) ne 'HASH'
and ref( $dataRef->{$r} ) ne 'ARRAY' );
}
readingsBulkUpdate($hash, $f . 'icon', $iconlist[$dataRef->{forecast}->{hourly}[$i-1]{code}]);
readingsBulkUpdate(
$hash,
$f . 'icon',
$iconlist[ $dataRef->{forecast}->{hourly}[ $i - 1 ]{code} ]
);
if ( defined($dataRef->{forecast}->{hourly}[$i-1]{wind_direction})
if (
defined(
$dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_direction}
)
and $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_direction}
and defined($dataRef->{forecast}->{hourly}[$i-1]{wind_speed})
and defined(
$dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_speed}
)
and $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_speed}
)
{
my $wdir= degrees_to_direction($dataRef->{forecast}->{hourly}[$i-1]{wind_direction}, @directions_txt_i18n);
readingsBulkUpdate($hash, $f . 'wind_condition', 'Wind: ' . $wdir . ' ' . $dataRef->{forecast}->{hourly}[$i-1]{wind_speed} . ' km/h');
my $wdir = degrees_to_direction(
$dataRef->{forecast}
->{hourly}[ $i - 1 ]{wind_direction},
@directions_txt_i18n
);
readingsBulkUpdate(
$hash,
$f . 'wind_condition',
'Wind: '
. $wdir . ' '
. $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_speed}
. ' km/h'
);
}
last if ( $i == $limit and $limit > 0 );
}
}
## daily
if ( defined($dataRef->{forecast}->{daily}) and ref( $dataRef->{forecast}->{daily} ) eq 'ARRAY'
and scalar( @{ $dataRef->{forecast}->{daily} } ) > 0 )
if (
defined( $dataRef->{forecast}->{daily} )
and ref( $dataRef->{forecast}->{daily} ) eq 'ARRAY'
and scalar( @{ $dataRef->{forecast}->{daily} } ) > 0
and ( AttrVal( $name, 'forecast', 'every' ) eq 'every'
or AttrVal( $name, 'forecast', 'daily' ) eq 'daily' )
)
{
my $i = 0;
my $limit = AttrVal( $name, 'forecastLimit', -1 );
foreach my $fc ( @{ $dataRef->{forecast}->{daily} } ) {
$i++;
my $f = "fc" . $i . "_";
while ( my ( $r, $v ) = each %{$fc} ) {
readingsBulkUpdate( $hash, $f . $r, $v )
if ( ref($dataRef->{$r}) ne 'HASH' and ref($dataRef->{$r}) ne 'ARRAY' );
if ( ref( $dataRef->{$r} ) ne 'HASH'
and ref( $dataRef->{$r} ) ne 'ARRAY' );
}
readingsBulkUpdate($hash, $f . 'icon', $iconlist[$dataRef->{forecast}->{daily}[$i-1]{code}]);
readingsBulkUpdate(
$hash,
$f . 'icon',
$iconlist[ $dataRef->{forecast}->{daily}[ $i - 1 ]{code} ]
);
if ( defined($dataRef->{forecast}->{daily}[$i-1]{wind_direction})
if (
defined(
$dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction}
)
and $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction}
and defined($dataRef->{forecast}->{daily}[$i-1]{wind_speed})
and defined(
$dataRef->{forecast}->{daily}[ $i - 1 ]{wind_speed}
)
and $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_speed}
)
{
my $wdir= degrees_to_direction($dataRef->{forecast}->{daily}[$i-1]{wind_direction}, @directions_txt_i18n);
readingsBulkUpdate($hash, $f . 'wind_condition', 'Wind: ' . $wdir . ' ' . $dataRef->{forecast}->{daily}[$i-1]{wind_speed} . ' km/h');
my $wdir = degrees_to_direction(
$dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction},
@directions_txt_i18n
);
readingsBulkUpdate(
$hash,
$f . 'wind_condition',
'Wind: '
. $wdir . ' '
. $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_speed}
. ' km/h'
);
}
last if ( $i == $limit and $limit > 0 );
}
}
}
my $val= 'T: ' . $dataRef->{current}->{temperature} . ' °C'
.' ' . substr($status_items_txt_i18n{1}, 0, 1) . ': ' . $dataRef->{current}->{humidity} . ' %'
.' ' . substr($status_items_txt_i18n{0}, 0, 1) . ': ' . $dataRef->{current}->{wind} . ' km/h'
.' P: ' . $dataRef->{current}->{pressure} . ' hPa';
my $val = 'T: '
. $dataRef->{current}->{temperature} . ' °C' . ' '
. substr( $status_items_txt_i18n{1}, 0, 1 ) . ': '
. $dataRef->{current}->{humidity} . ' %' . ' '
. substr( $status_items_txt_i18n{0}, 0, 1 ) . ': '
. $dataRef->{current}->{wind} . ' km/h' . ' P: '
. $dataRef->{current}->{pressure} . ' hPa';
Log3 $hash, 4, "$name: $val";
readingsBulkUpdate( $hash, 'state', $val );
@ -302,13 +510,15 @@ sub Weather_GetUpdate($) {
my $name = $hash->{NAME};
if ( $attr{$name} && $attr{$name}->{disable} ) {
Log3 $hash, 5, "Weather $name: retrieval of weather data is disabled by attribute.";
Log3 $hash, 5,
"Weather $name: retrieval of weather data is disabled by attribute.";
readingsBeginUpdate($hash);
readingsBulkUpdate( $hash, "pubDateComment", "disabled by attribute" );
readingsBulkUpdate( $hash, "validity", "stale" );
readingsEndUpdate( $hash, 1 );
Weather_RearmTimer( $hash, gettimeofday() + $hash->{INTERVAL} );
} else {
}
else {
# Weather_RetrieveData($name, 0);
$hash->{fhem}->{api}->setRetrieveData;
}
@ -327,7 +537,8 @@ sub Weather_Get($@) {
if ( defined( $hash->{READINGS}->{$reading} ) ) {
$value = $hash->{READINGS}->{$reading}->{VAL};
} else {
}
else {
my $rt = '';
if ( defined( $hash->{READINGS} ) ) {
$rt = join( ":noArg ", sort keys %{ $hash->{READINGS} } );
@ -350,7 +561,8 @@ sub Weather_Set($@) {
Weather_DisarmTimer($hash);
Weather_GetUpdate($hash);
return undef;
} else {
}
else {
return "Unknown argument $cmd, choose one of update:noArg";
}
}
@ -386,7 +598,8 @@ sub Weather_Notify($$) {
#$delay= 3; # delay removed until further notice
Log3 $hash, 5, "Weather $name: FHEM initialization or rereadcfg triggered update, delay $delay seconds.";
Log3 $hash, 5,
"Weather $name: FHEM initialization or rereadcfg triggered update, delay $delay seconds.";
Weather_RearmTimer( $hash, gettimeofday() + $delay );
return undef;
@ -396,7 +609,8 @@ sub Weather_Notify($$) {
sub Weather_Define($$) {
my ( $hash, $def ) = @_;
my $usage= "syntax: define <name> Weather [API=<API>] [apikey=<apikey>] [location=<location>] [interval=<interval>] [lang=<lang>]";
my $usage =
"syntax: define <name> Weather [API=<API>] [apikey=<apikey>] [location=<location>] [interval=<interval>] [lang=<lang>]";
# defaults
my $API = "DarkSkyAPI,cachemaxage:600";
@ -411,7 +625,6 @@ sub Weather_Define($$) {
return $usage unless ( scalar @a == 2 );
my $name = $a[0];
my $location = $h{location} if exists $h{location};
my $apikey = $h{apikey} if exists $h{apikey};
my $lang = $h{lang} if exists $h{lang};
@ -421,16 +634,21 @@ sub Weather_Define($$) {
# evaluate API options
my ( $api, $apioptions ) = split( ',', $API, 2 );
$apioptions = "" unless ( defined($apioptions) );
eval {
require "$api.pm";
};
eval { require "$api.pm"; };
return "$name: cannot load API $api: $@" if ($@);
$hash->{NOTIFYDEV} = "global";
$hash->{fhem}->{interfaces} = "temperature;humidity;wind";
$hash->{LOCATION} = ( (defined($location) and $location) ? $location : AttrVal( 'global', 'latitude', 'error' ).','.AttrVal( 'global', 'longitude', 'error' ) );
$hash->{LOCATION} =
( ( defined($location) and $location )
? $location
: AttrVal( 'global', 'latitude', 'error' ) . ','
. AttrVal( 'global', 'longitude', 'error' ) );
$hash->{INTERVAL} = $interval;
$hash->{LANG} = ( (defined($lang) and $lang) ? $lang : lc(AttrVal('global','language','de')) );
$hash->{LANG} =
( ( defined($lang) and $lang )
? $lang
: lc( AttrVal( 'global', 'language', 'de' ) ) );
$hash->{API} = $api;
$hash->{MODEL} = $api;
$hash->{APIKEY} = $apikey;
@ -443,7 +661,15 @@ sub Weather_Define($$) {
Weather_LanguageInitialize( $hash->{LANG} );
my $apistring = $api . '::Weather';
$hash->{fhem}->{api} = $apistring->new( { devName => $hash->{NAME}, apikey => $hash->{APIKEY}, location => $hash->{LOCATION}, apioptions => $hash->{APIOPTIONS}, language => $hash->{LANG} } );
$hash->{fhem}->{api} = $apistring->new(
{
devName => $hash->{NAME},
apikey => $hash->{APIKEY},
location => $hash->{LOCATION},
apioptions => $hash->{APIOPTIONS},
language => $hash->{LANG}
}
);
Weather_GetUpdate($hash) if ($init_done);
@ -478,11 +704,11 @@ sub WeatherIconIMGTag($) {
#####################################
sub WeatherAsHtmlV($;$) {
my ($d,$items) = @_;
sub WeatherAsHtmlV($;$$) {
my ( $d, $items, $f ) = @_;
$d = "<none>" if ( !$d );
$items = 9 if( !$items );
$items = 6 if ( !$items );
return "$d is not a Weather instance<br>"
if ( !$defs{$d} || $defs{$d}->{TYPE} ne "Weather" );
@ -490,21 +716,44 @@ sub WeatherAsHtmlV($;$) {
my $width = int( ICONSCALE * ICONWIDTH );
my $ret = '<table class="weather">';
$ret .= sprintf('<tr><td class="weatherIcon" width=%d>%s</td><td class="weatherValue">%s<br>%s°C %s%%<br>%s</td></tr>',
my $fc;
if (
defined($f)
and ( $f eq 'h'
or $f eq 'd' )
)
{
$fc = ( $f eq 'd' ? 'fc' : 'hfc' );
}
else {
$fc = (
(
defined( $h->{READINGS}->{fc1_day_of_week} )
and $h->{READINGS}->{fc1_day_of_week}
) ? 'fc' : 'hfc'
);
}
$ret .= sprintf(
'<tr><td class="weatherIcon" width=%d>%s</td><td class="weatherValue">%s<br>%s°C %s%%<br>%s</td></tr>',
$width,
WeatherIconIMGTag( ReadingsVal( $d, "icon", "" ) ),
ReadingsVal( $d, "condition", "" ),
ReadingsVal($d, "temp_c", ""), ReadingsVal($d, "humidity", ""),
ReadingsVal($d, "wind_condition", ""));
ReadingsVal( $d, "temp_c", "" ),
ReadingsVal( $d, "humidity", "" ),
ReadingsVal( $d, "wind_condition", "" )
);
my $fc = ( (defined($h->{READINGS}->{fc1_day_of_week}) and $h->{READINGS}->{fc1_day_of_week}) ? 'fc' : 'hfc' );
for ( my $i = 1 ; $i < $items ; $i++ ) {
$ret .= sprintf('<tr><td class="weatherIcon" width=%d>%s</td><td class="weatherValue"><span class="weatherDay">%s: %s</span><br><span class="weatherMin">min %s°C</span> <span class="weatherMax">max %s°C</span></td></tr>',
$ret .= sprintf(
'<tr><td class="weatherIcon" width=%d>%s</td><td class="weatherValue"><span class="weatherDay">%s: %s</span><br><span class="weatherMin">min %s°C</span> <span class="weatherMax">max %s°C</span></td></tr>',
$width,
WeatherIconIMGTag( ReadingsVal( $d, "${fc}${i}_icon", "" ) ),
ReadingsVal( $d, "${fc}${i}_day_of_week", "" ),
ReadingsVal( $d, "${fc}${i}_condition", "" ),
ReadingsVal($d, "${fc}${i}_low_c", ""), ReadingsVal($d, "${fc}${i}_high_c", ""));
ReadingsVal( $d, "${fc}${i}_low_c", " - " ),
ReadingsVal( $d, "${fc}${i}_high_c", " - " )
);
}
$ret .= "</table>";
@ -517,50 +766,79 @@ sub WeatherAsHtml($;$) {
WeatherAsHtmlV( $d, $i );
}
sub WeatherAsHtmlH($;$) {
my ($d,$items) = @_;
sub WeatherAsHtmlH($;$$) {
my ( $d, $items, $f ) = @_;
$d = "<none>" if ( !$d );
$items = 9 if( !$items );
$items = 6 if ( !$items );
return "$d is not a Weather instance<br>"
if ( !$defs{$d} || $defs{$d}->{TYPE} ne "Weather" );
my $h = $defs{$d};
my $width = int( ICONSCALE * ICONWIDTH );
my $format= '<td><table border=1><tr><td class="weatherIcon" width=%d>%s</td></tr><tr><td class="weatherValue">%s</td></tr><tr><td class="weatherValue">%s°C %s%%</td></tr><tr><td class="weatherValue">%s</td></tr></table></td>';
my $format =
'<td><table border=1><tr><td class="weatherIcon" width=%d>%s</td></tr><tr><td class="weatherValue">%s</td></tr><tr><td class="weatherValue">%s°C %s%%</td></tr><tr><td class="weatherValue">%s</td></tr></table></td>';
my $ret = '<table class="weather">';
my $fc = ( (defined($h->{READINGS}->{fc1_day_of_week}) and $h->{READINGS}->{fc1_day_of_week}) ? 'fc' : 'hfc' );
my $fc;
if (
defined($f)
and ( $f eq 'h'
or $f eq 'd' )
)
{
$fc = ( $f eq 'd' ? 'fc' : 'hfc' );
}
else {
$fc = (
(
defined( $h->{READINGS}->{fc1_day_of_week} )
and $h->{READINGS}->{fc1_day_of_week}
) ? 'fc' : 'hfc'
);
}
# icons
$ret .= sprintf('<tr><td class="weatherIcon" width=%d>%s</td>', $width, WeatherIconIMGTag(ReadingsVal($d, "icon", "")));
$ret .= sprintf( '<tr><td class="weatherIcon" width=%d>%s</td>',
$width, WeatherIconIMGTag( ReadingsVal( $d, "icon", "" ) ) );
for ( my $i = 1 ; $i < $items ; $i++ ) {
$ret .= sprintf('<td class="weatherIcon" width=%d>%s</td>', $width, WeatherIconIMGTag(ReadingsVal($d, "${fc}${i}_icon", "")));
$ret .= sprintf( '<td class="weatherIcon" width=%d>%s</td>',
$width,
WeatherIconIMGTag( ReadingsVal( $d, "${fc}${i}_icon", "" ) ) );
}
$ret .= '</tr>';
# condition
$ret .= sprintf('<tr><td class="weatherDay">%s</td>', ReadingsVal($d, "condition", ""));
$ret .= sprintf( '<tr><td class="weatherDay">%s</td>',
ReadingsVal( $d, "condition", "" ) );
for ( my $i = 1 ; $i < $items ; $i++ ) {
$ret .= sprintf('<td class="weatherDay">%s: %s</td>', ReadingsVal($d, "${fc}${i}_day_of_week", ""),
ReadingsVal($d, "${fc}${i}_condition", ""));
$ret .= sprintf(
'<td class="weatherDay">%s: %s</td>',
ReadingsVal( $d, "${fc}${i}_day_of_week", "" ),
ReadingsVal( $d, "${fc}${i}_condition", "" )
);
}
$ret .= '</tr>';
# temp/hum | min
$ret .= sprintf('<tr><td class="weatherMin">%s°C %s%%</td>', ReadingsVal($d, "temp_c", ""), ReadingsVal($d, "humidity", ""));
$ret .= sprintf(
'<tr><td class="weatherMin">%s°C %s%%</td>',
ReadingsVal( $d, "temp_c", "" ),
ReadingsVal( $d, "humidity", "" )
);
for ( my $i = 1 ; $i < $items ; $i++ ) {
$ret .= sprintf('<td class="weatherMin">min %s°C</td>', ReadingsVal($d, "${fc}${i}_low_c", ""));
$ret .= sprintf( '<td class="weatherMin">min %s°C</td>',
ReadingsVal( $d, "${fc}${i}_low_c", " - " ) );
}
$ret .= '</tr>';
# wind | max
$ret .= sprintf('<tr><td class="weatherMax">%s</td>', ReadingsVal($d, "wind_condition", ""));
$ret .= sprintf( '<tr><td class="weatherMax">%s</td>',
ReadingsVal( $d, "wind_condition", "" ) );
for ( my $i = 1 ; $i < $items ; $i++ ) {
$ret .= sprintf('<td class="weatherMax">max %s°C</td>', ReadingsVal($d, "${fc}${i}_high_c", ""));
$ret .= sprintf( '<td class="weatherMax">max %s°C</td>',
ReadingsVal( $d, "${fc}${i}_high_c", " - " ) );
}
$ret .= "</tr></table>";
@ -572,14 +850,14 @@ sub WeatherAsHtmlD($;$) {
if ($FW_ss) {
WeatherAsHtmlV( $d, $i );
} else {
}
else {
WeatherAsHtmlH( $d, $i );
}
}
#####################################
1;
=pod
@ -632,7 +910,7 @@ sub WeatherAsHtmlD($;$) {
Examples:
<pre>
define Forecast Weather apikey=987498ghjgf864
define MyWeather Weather api=OpenWeatherMapAPI,cachemaxage:600 apikey=09878945fdskv876 location=52.4545,13.4545 interval=3600 lang=de
define MyWeather Weather API=OpenWeatherMapAPI,cachemaxage:600 apikey=09878945fdskv876 location=52.4545,13.4545 interval=3600 lang=de
</pre>
@ -841,9 +1119,11 @@ sub WeatherAsHtmlD($;$) {
Darstellung des Wetterberichtes. Die letztgenannte Funktion w&auml;hlt
automatisch eine Ausrichtung, die abh&auml;ngig davon ist, ob ein
Smallcreen Style ausgew&auml;hlt ist (vertikale Darstellung) oder
nicht (horizontale Darstellung). Alle vier Funnktionen akzeptieren
nicht (horizontale Darstellung). Alle vier Funktionen akzeptieren
einen zus&auml;tzlichen optionalen Paramter um die Anzahl der
darzustellenden Icons anzugeben.<br><br>
darzustellenden Icons anzugeben.<br>
Zus&auml;tzlich erlauben die Funktionen 2 und 3 noch einen dritten Parameter (d oder h) welcher die Forecast-Art (h-Hourly oder d-Daily) mit an gibt.<br>
Wird der dritte Parameter verwendet muss auch der zweite Parameter f&uuml;r die Anzahl der darzustellenden Icons gesetzt werden.<br><br>
Beispiel:
<pre>
define MyWeatherWeblink weblink htmlCode { WeatherAsHtmlH("MyWeather") }
@ -910,6 +1190,8 @@ sub WeatherAsHtmlD($;$) {
gem&auml;&szlig Plan doch es werden keine Daten vom
API angefordert.</li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
<li>forecast - every/hourly/daily/off, Anzeige von forecast Daten. Alle, nur Stundenforecast, nur Tageforecast, keine.</li>
<li>forecastLimit - Anzahl der Forecast-Datens&auml;tze welche als Reading geschrieben werden sollen.</li>
</ul>
<br>
</ul>