2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

- time and internet helper routines added to fhem.pl

- 57_Calendar.pm updated (got rid of Time::Local, verbose STATE, allow almost literal copy of Google ICAL URL)

git-svn-id: https://svn.fhem.de/fhem/trunk@1625 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
borisneubert 2012-06-17 14:31:17 +00:00
parent ed0d76f444
commit 7d22c1ae61
4 changed files with 163 additions and 27 deletions

View File

@ -39,12 +39,12 @@
new global attribute <backupcmd> added
new global attribute <backup_before_update> added
- feature: new module 57_Calendar.pm (Boris)
- feature: new module 57_Calendar.pm (Boris)
- feature: new parameter <changed> for updatefhem added (M. Fischer)
new global attribute <exclude_from_update> added (M. Fischer)
- feature: optional telnet password added / telnet port is optional
- feature: holiday returns all matches, not only the first.
- change: CULflash separated from updatefhem to a new module (M. Fischer)
- feature: time and internet helper routines added to fhem.pl (Boris)
- 2011-12-31 (5.2)
- bugfix: applying smallscreen attributes to firefox/opera

View File

@ -8,10 +8,12 @@
##############################################
# $Id $
# Todos:
# Support recurring events
# update documentation (get MyCalendar full all, use URL-encoded URLs for Google Calendar
use strict;
use warnings;
use Time::Local;
##############################################
@ -88,7 +90,7 @@ sub parseSub {
last if($line =~ m/^END:.*$/);
if($line =~ m/^BEGIN:(.*)$/) {
my $entry= ICal::Entry->new($1);
push $self->{entries}, $entry;
push @{$self->{entries}}, $entry;
$ln= $entry->parseSub($ln,@ical);
} else {
$self->addproperty($line);
@ -159,7 +161,7 @@ sub setMode {
my ($self,$mode)= @_;
$self->{_previousMode}= $self->{_mode};
$self->{_mode}= $mode;
main::debug "After setMode $mode: Modes(" . $self->uid() . ") " . $self->{_previousMode} . " -> " . $self->{_mode};
#main::debug "After setMode $mode: Modes(" . $self->uid() . ") " . $self->{_previousMode} . " -> " . $self->{_mode};
return $mode;
}
@ -231,14 +233,14 @@ sub modeChanged {
# 20120520: a date string has no time zone associated
sub tm {
my ($t)= @_;
#debug "convert $t";
#main::debug "convert $t";
my ($year,$month,$day)= (substr($t,0,4), substr($t,4,2),substr($t,6,2));
if(length($t)>8) {
my ($hour,$minute,$second)= (substr($t,9,2), substr($t,11,2),substr($t,13,2));
return Time::Local::timegm($second,$minute,$hour,$day,$month-1,$year-1900);
return main::fhemTimeGm($second,$minute,$hour,$day,$month-1,$year-1900);
} else {
#debug "$day $month $year";
return Time::Local::timelocal(0,0,0,$day,$month-1,$year-1900);
#main::debug "$day $month $year";
return main::fhemTimeLocal(0,0,0,$day,$month-1,$year-1900);
}
}
@ -257,7 +259,7 @@ sub tm {
sub d {
my ($d)= @_;
main::debug "Duration $d";
#main::debug "Duration $d";
my $sign= 1;
my $t= 0;
@ -266,6 +268,7 @@ sub d {
$sign= -1 if($c[0] eq "-");
shift @c if($c[0] =~ m/[\+\-]/);
my ($dw,$dt)= split("T", $c[0]);
$dt="" unless defined($dt);
if($dw =~ m/(\d+)D$/) {
$t+= 86400*$1; # days
} elsif($dw =~ m/(\d+)W$/) {
@ -281,7 +284,7 @@ sub d {
sub dt {
my ($t0,$value,$parts)= @_;
main::debug "t0= $t0 parts= $parts value= $value";
#main::debug "t0= $t0 parts= $parts value= $value";
if(defined($parts) && $parts =~ m/VALUE=DATE/) {
return tm($value);
} else {
@ -416,12 +419,12 @@ sub new {
sub uids {
my ($self)= @_;
return keys $self->{events};
return keys %{$self->{events}};
}
sub events {
my ($self)= @_;
return values $self->{events};
return values %{$self->{events}};
}
sub event {
@ -503,6 +506,7 @@ sub updateFromCalendar {
package main;
#####################################
sub Calendar_Initialize($) {
@ -556,7 +560,7 @@ sub Calendar_CheckTimes($) {
my @endedevents= grep { $_->isEnded($t) } @allevents;
my $event;
main::debug "Updating modes...";
#main::debug "Updating modes...";
foreach $event (@upcomingevents) { $event->setMode("upcoming"); }
foreach $event (@alarmedevents) { $event->setMode("alarm"); }
foreach $event (@startedevents) { $event->setMode("start"); }
@ -595,16 +599,11 @@ sub Calendar_GetUpdate($) {
my $url= $hash->{fhem}{url};
# split into hostname and filename, TODO: enable https
if($url =~ m,^http://(.+?)(/.+)$,) {
# well-formed, host now in $1, filename now in $2
#main::debug "Get $url";
} else {
Log 1, "Calendar " . $hash->{NAME} . ": $url is not a valid URL.";
my $ics= GetFileFromURL($url);
if(!defined($ics)) {
Log 1, "Calendar " . $hash->{NAME} . ": Could not retrieve $url";
return 0;
}
my $ics= GetHttpFile("$1:80",$2);
return 0 if($ics eq "");
# we parse the calendar into a recursive ICal::Entry structure
my $ical= ICal::Entry->new("root");
@ -612,6 +611,15 @@ sub Calendar_GetUpdate($) {
#main::debug "*** Result:\n";
#main::debug $ical->asString();
my @entries= @{$ical->{entries}};
if($#entries<0) {
Log 1, "Calendar " . $hash->{NAME} . ": Not an ical file at $url";
$hash->{STATE}= "Not an ical file at URL";
return 0;
} else {
$hash->{STATE}= "Active";
}
# we now create the events from it
#main::debug "Creating events...";
my $eventsObj= $hash->{fhem}{events};
@ -706,7 +714,6 @@ sub Calendar_Get($@) {
}
#####################################
sub Calendar_Define($$) {

View File

@ -1405,13 +1405,16 @@ A line ending with \ will be concatenated with the next one, so long lines
start with <code>http://</code>, not <code>https://</code>, and the file at the given URL
must be in ICal format.<br><br>
Note for users of Google Calendar: You can literally use the private ICAL URL from your Google Calendar with the
<code>https://</code> replaced by <code>http://</code>.<br><br>
The optional parameter <code>interval</code> is the time between subsequent updates
in seconds. It defaults to 3600 (1 hour).<br><br>
Examples:
<pre>
define MyCalendar Calendar ical url http://www.google.com/calendar/ical/john.doe@example.com/private-foo4711/basic.ics
define YourCalendar Calendar ical url http://www.google.com/calendar/ical/jane.doe@example.com/private-bar0815/basic.ics 86400
define MyCalendar Calendar ical url http://www.google.com/calendar/ical/john.doe%40example.com/private-foo4711/basic.ics
define YourCalendar Calendar ical url http://www.google.com/calendar/ical/jane.doe%40example.com/private-bar0815/basic.ics 86400
</pre>
</ul>
<br>

View File

@ -3005,7 +3005,55 @@ readingsUpdate($$$) {
return $rv;
}
##################
###############################################################################
#
# date and time routines
#
##############################################################################
sub
fhemTzOffset($) {
# see http://stackoverflow.com/questions/2143528/whats-the-best-way-to-get-the-utc-offset-in-perl
my $t = shift;
my @l = localtime($t);
my @g = gmtime($t);
# the offset is positive if the local timezone is ahead of GMT, e.g. we get 2*3600 seconds for CET DST vs GMT
return 60*(($l[2] - $g[2] + ((($l[5]<<9)|$l[7]) <=> (($g[5]<<9)|$g[7])) * 24) * 60 + $l[1] - $g[1]);
}
sub
fhemTimeGm($$$$$$) {
# see http://de.wikipedia.org/wiki/Unixzeit
my ($sec,$min,$hour,$mday,$month,$year) = @_;
# $mday= 1..
# $month= 0..11
# $year is year-1900
$year+= 1900;
my $isleapyear= $year % 4 ? 0 : $year % 100 ? 1 : $year % 400 ? 0 : 1;
my $leapyears= int((($year-1)-1968)/4 - (($year-1)-1900)/100 + (($year-1)-1600)/400);
#Debug sprintf("%02d.%02d.%04d %02d:%02d:%02d", $mday,$month+1,$year,$hour,$min,$sec);
if ( $^O eq 'MacOS' ) {
$year-= 1904;
} else {
$year-= 1970; # the Unix Epoch
}
my @d= (0,31,59,90,120,151,181,212,243,273,304,334); # no leap day
# add one day in leap years if month is later than February
$mday++ if($month>1 && $isleapyear);
return $sec+60*($min+60*($hour+24*($d[$month]+$mday-1+365*$year+$leapyears)));
}
sub
fhemTimeLocal($$$$$$) {
my $t= fhemTimeGm($_[0],$_[1],$_[2],$_[3],$_[4],$_[5]);
return $t-fhemTzOffset($t);
}
sub
secSince2000()
{
@ -3013,9 +3061,87 @@ secSince2000()
my $t = time();
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($t);
$t -= 946684800; # seconds between 01.01.2000, 00:00 and THE EPOCH (1970)
$t -= 1*3600; # Timezone offset from UTC * 3600 (MEZ=1). FIXME/HARDCODED
$t += 3600 if $isdst;
$t -= fhemTzOffset($t);
return $t;
}
###############################################################################
#
# internet stuff
#
##############################################################################
sub
urlEncode($) {
$_= $_[0];
s/([\x00-\x2F,\x3A-\x40,\x5B-\x60,\x7B-\xFF])/sprintf("%%%02x",ord($1))/eg;
return $_;
}
sub
GetFileFromURL($@)
{
my ($url,$timeout)= @_;
$timeout = 2.0 if(!defined($timeout));
if($url !~ /^(http):\/\/([^:\/]+)(:\d+)?(\/.*)$/) {
Log 1, "GetFileFromURL $url: malformed URL";
return undef;
}
my ($protocol,$host,$port,$path)= ($1,$2,$3,$4);
#Debug "Protocol $protocol, host $host port $port, path $path";
if(defined($port)) {
$port=~ s/^://;
} else {
$port= 80;
}
$path= '/' unless defined($path);
my $hostport= "$host:$port";
#Debug "Protocol $protocol, host:port $hostport, path $path";
if($protocol ne "http") {
Log 1, "GetFileFromURL $url: invalid protocol";
return undef;
}
my $conn = IO::Socket::INET->new(PeerAddr => "$hostport");
if(!$conn) {
Log 1, "GetFileFromURL $url: Can't connect to $hostport\n";
undef $conn;
return undef;
}
my $req = "GET $path HTTP/1.0\r\nHost: $hostport\r\n\r\n\r\n";
syswrite $conn, $req;
shutdown $conn, 1; # stopped writing data
my ($buf, $ret) = ("", "");
$conn->timeout($timeout);
for(;;) {
my ($rout, $rin) = ('', '');
vec($rin, $conn->fileno(), 1) = 1;
my $nfound = select($rout=$rin, undef, undef, $timeout);
if($nfound <= 0) {
Log 1, "GetFileFromURL $url: Select timeout/error: $!";
undef $conn;
return undef;
}
my $len = sysread($conn,$buf,65536);
last if(!defined($len) || $len <= 0);
$ret .= $buf;
}
$ret=~ s/(.*?)\r\n\r\n//s; # Not greedy: switch off the header.
Log 4, "GetFileFromURL $url: Got file, length: ".length($ret);
undef $conn;
return $ret;
}
##############################################################################
1;