mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-04 22:19:38 +00:00
57_Calendar: asynchronous parsing, disable update
git-svn-id: https://svn.fhem.de/fhem/trunk@14494 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
53b6a9fe23
commit
1afbf43caf
@ -1,5 +1,6 @@
|
|||||||
# 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.
|
||||||
|
- feature: 57_Calendar: asynchronous parsing, disable update
|
||||||
- change: 34_ESPEasy: add IPv6 ULA to local IPs, add regexps to ACLs
|
- change: 34_ESPEasy: add IPv6 ULA to local IPs, add regexps to ACLs
|
||||||
- bugfix: 93_DbLog: V2.16.11, lock SQLite from logging if deleteOldDaysNbl
|
- bugfix: 93_DbLog: V2.16.11, lock SQLite from logging if deleteOldDaysNbl
|
||||||
or reduceLogNbL is running in async mode
|
or reduceLogNbL is running in async mode
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use HttpUtils;
|
use HttpUtils;
|
||||||
|
use Storable qw(freeze thaw);
|
||||||
|
|
||||||
|
|
||||||
##############################################
|
##############################################
|
||||||
@ -882,7 +883,7 @@ sub addproperty($$) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return unless($key);
|
return unless($key);
|
||||||
#main::Debug "addproperty for line $line gives key $key, parts is $parts, parameter os $parameter";
|
#main::Debug "addproperty for key $key";
|
||||||
|
|
||||||
# ignore some properties
|
# ignore some properties
|
||||||
# commented out: it is faster to add the property than to do the check
|
# commented out: it is faster to add the property than to do the check
|
||||||
@ -930,7 +931,6 @@ sub parseSub($$$) {
|
|||||||
#main::Debug "ENTER @ $ln";
|
#main::Debug "ENTER @ $ln";
|
||||||
while($ln< $len) {
|
while($ln< $len) {
|
||||||
my $line= $$icalref[$ln];
|
my $line= $$icalref[$ln];
|
||||||
#main::Debug "parse line $line";
|
|
||||||
$ln++;
|
$ln++;
|
||||||
# check for and handle continuation lines (4.1 on page 12)
|
# check for and handle continuation lines (4.1 on page 12)
|
||||||
while($ln< $len) {
|
while($ln< $len) {
|
||||||
@ -1567,7 +1567,7 @@ sub Calendar_Initialize($) {
|
|||||||
$hash->{SetFn} = "Calendar_Set";
|
$hash->{SetFn} = "Calendar_Set";
|
||||||
$hash->{AttrFn} = "Calendar_Attr";
|
$hash->{AttrFn} = "Calendar_Attr";
|
||||||
$hash->{NotifyFn}= "Calendar_Notify";
|
$hash->{NotifyFn}= "Calendar_Notify";
|
||||||
$hash->{AttrList}= "hideOlderThan hideLaterThan onCreateEvent SSLVerify:0,1 $readingFnAttributes";
|
$hash->{AttrList}= "update:sync,async,none hideOlderThan hideLaterThan onCreateEvent SSLVerify:0,1 $readingFnAttributes";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1620,6 +1620,13 @@ sub Calendar_Undef($$) {
|
|||||||
my ($hash, $arg) = @_;
|
my ($hash, $arg) = @_;
|
||||||
|
|
||||||
Calendar_DisarmTimer($hash);
|
Calendar_DisarmTimer($hash);
|
||||||
|
|
||||||
|
if(exists($hash->{".fhem"}{subprocess})) {
|
||||||
|
my $subprocess= $hash->{".fhem"}{subprocess};
|
||||||
|
$subprocess->terminate();
|
||||||
|
$subprocess->wait();
|
||||||
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1640,6 +1647,10 @@ sub Calendar_Attr(@) {
|
|||||||
if($arg !~ m/^{.*}$/s) {
|
if($arg !~ m/^{.*}$/s) {
|
||||||
return "$arg must be a perl command in curly brackets but you supplied $arg.";
|
return "$arg must be a perl command in curly brackets but you supplied $arg.";
|
||||||
}
|
}
|
||||||
|
} elsif($a[0] eq "update") {
|
||||||
|
my @args= qw/none sync async/;
|
||||||
|
return "Argument for update must be one of " . join(" ", @args) .
|
||||||
|
" instead of $arg." unless($arg ~~ @args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
@ -2089,6 +2100,14 @@ sub Calendar_GetUpdate($$$) {
|
|||||||
#main::Debug "Getting update now: " . $hash->{".fhem"}{lastUpdate};
|
#main::Debug "Getting update now: " . $hash->{".fhem"}{lastUpdate};
|
||||||
#main::Debug "Next Update is at : " . $hash->{".fhem"}{nextUpdate};
|
#main::Debug "Next Update is at : " . $hash->{".fhem"}{nextUpdate};
|
||||||
|
|
||||||
|
# If update is disable, shortcut to time checking and rearming timer.
|
||||||
|
# Why is this here and not in Calendar_Wakeup? Because the next update time needs to be set
|
||||||
|
if(AttrVal($hash->{NAME},"update","") eq "none") {
|
||||||
|
Calendar_CheckTimes($hash, $t);
|
||||||
|
Calendar_RearmTimer($hash, $t);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Log3 $hash, 4, "Calendar $name: Updating...";
|
Log3 $hash, 4, "Calendar $name: Updating...";
|
||||||
my $type = $hash->{".fhem"}{type};
|
my $type = $hash->{".fhem"}{type};
|
||||||
my $url= $hash->{".fhem"}{url};
|
my $url= $hash->{".fhem"}{url};
|
||||||
@ -2164,6 +2183,15 @@ sub Calendar_ProcessUpdate($$$) {
|
|||||||
my $removeall = $param->{removeall};
|
my $removeall = $param->{removeall};
|
||||||
my $t= $param->{t};
|
my $t= $param->{t};
|
||||||
|
|
||||||
|
if(exists($hash->{".fhem"}{subprocess})) {
|
||||||
|
Log3 $hash, 2, "Calendar $name: update in progress, process aborted.";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# not for the developer:
|
||||||
|
# we must be sure that code that starts here ends with Calendar_CheckAndRearm()
|
||||||
|
# no matter what branch is taken in the following
|
||||||
|
|
||||||
delete($hash->{".fhem"}{iCalendar});
|
delete($hash->{".fhem"}{iCalendar});
|
||||||
|
|
||||||
if($errmsg) {
|
if($errmsg) {
|
||||||
@ -2176,40 +2204,119 @@ sub Calendar_ProcessUpdate($$$) {
|
|||||||
if($errmsg or !defined($ics) or ("$ics" eq "") ) {
|
if($errmsg or !defined($ics) or ("$ics" eq "") ) {
|
||||||
Log3 $hash, 1, "Calendar $name: retrieved no or empty data";
|
Log3 $hash, 1, "Calendar $name: retrieved no or empty data";
|
||||||
readingsSingleUpdate($hash, "state", "error (no or empty data)", 1);
|
readingsSingleUpdate($hash, "state", "error (no or empty data)", 1);
|
||||||
|
Calendar_CheckAndRearm($hash, $t);
|
||||||
} else {
|
} else {
|
||||||
Calendar_UpdateCalendar($hash, $t, $ics, $removeall);
|
|
||||||
}
|
|
||||||
|
|
||||||
#main::Debug "Calendar $name: iCalendar=\n$ics";
|
|
||||||
|
|
||||||
Calendar_CheckTimes($hash, $t);
|
|
||||||
Calendar_RearmTimer($hash, $t);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
###################################
|
|
||||||
sub Calendar_UpdateCalendar($$$$) {
|
|
||||||
|
|
||||||
my ($hash, $t, $ics, $removeall) = @_;
|
|
||||||
|
|
||||||
|
|
||||||
# *********************
|
|
||||||
# *** Step 1 Parsing
|
|
||||||
# *********************
|
|
||||||
|
|
||||||
#
|
|
||||||
# 1
|
|
||||||
#
|
|
||||||
|
|
||||||
$hash->{".fhem"}{iCalendar}= $ics; # the plain text iCalendar
|
$hash->{".fhem"}{iCalendar}= $ics; # the plain text iCalendar
|
||||||
|
$hash->{".fhem"}{t}= $t;
|
||||||
|
$hash->{".fhem"}{removeall}= $removeall;
|
||||||
|
if(AttrVal($name, "update", "sync") eq "async") {
|
||||||
|
Calendar_AsynchronousUpdateCalendar($hash);
|
||||||
|
} else {
|
||||||
|
Calendar_SynchronousUpdateCalendar($hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#
|
}
|
||||||
# 2 Parsing
|
|
||||||
#
|
sub Calendar_Cleanup($) {
|
||||||
|
my ($hash)= @_;
|
||||||
|
delete($hash->{".fhem"}{t});
|
||||||
|
delete($hash->{".fhem"}{removeall});
|
||||||
|
delete($hash->{".fhem"}{serialized});
|
||||||
|
delete($hash->{".fhem"}{subprocess});
|
||||||
|
|
||||||
my $name= $hash->{NAME};
|
my $name= $hash->{NAME};
|
||||||
Log3 $hash, 4, "Calendar $name: parsing data";
|
Log3 $hash, 4, "Calendar $name: process ended.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub Calendar_CheckAndRearm($) {
|
||||||
|
|
||||||
|
my ($hash)= @_;
|
||||||
|
my $t= $hash->{".fhem"}{t};
|
||||||
|
Calendar_CheckTimes($hash, $t);
|
||||||
|
Calendar_RearmTimer($hash, $t);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub Calendar_SynchronousUpdateCalendar($) {
|
||||||
|
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name= $hash->{NAME};
|
||||||
|
Log3 $hash, 4, "Calendar $name: parsing data synchronously";
|
||||||
|
my $ical= Calendar_ParseICS($hash->{".fhem"}{iCalendar});
|
||||||
|
Calendar_UpdateCalendar($hash, $ical);
|
||||||
|
Calendar_CheckAndRearm($hash);
|
||||||
|
Calendar_Cleanup($hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
use constant POLLINTERVAL => 1;
|
||||||
|
|
||||||
|
sub Calendar_AsynchronousUpdateCalendar($) {
|
||||||
|
|
||||||
|
require "SubProcess.pm";
|
||||||
|
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name= $hash->{NAME};
|
||||||
|
|
||||||
|
my $subprocess= SubProcess->new({ onRun => \&Calendar_OnRun });
|
||||||
|
$subprocess->{ics}= $hash->{".fhem"}{iCalendar};
|
||||||
|
my $pid= $subprocess->run();
|
||||||
|
|
||||||
|
if(!defined($pid)) {
|
||||||
|
Log3 $hash, 1, "Calendar $name: Cannot parse asynchronously";
|
||||||
|
Calendar_CheckAndRearm($hash);
|
||||||
|
Calendar_Cleanup($hash);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $hash, 4, "Calendar $name: parsing data asynchronously (PID= $pid)";
|
||||||
|
$hash->{".fhem"}{subprocess}= $subprocess;
|
||||||
|
$hash->{".fhem"}{serialized}= "";
|
||||||
|
InternalTimer(gettimeofday()+POLLINTERVAL, "Calendar_PollChild", $hash, 0);
|
||||||
|
|
||||||
|
# go and do your thing while the timer polls and waits for the child to terminate
|
||||||
|
Log3 $hash, 5, "Calendar $name: control passed back to main loop.";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sub Calendar_OnRun() {
|
||||||
|
|
||||||
|
# This routine runs in a process separate from the main process.
|
||||||
|
my $subprocess= shift;
|
||||||
|
my $ical= Calendar_ParseICS($subprocess->{ics});
|
||||||
|
my $serialized= freeze $ical;
|
||||||
|
$subprocess->writeToParent($serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub Calendar_PollChild($) {
|
||||||
|
|
||||||
|
my ($hash)= @_;
|
||||||
|
my $name= $hash->{NAME};
|
||||||
|
my $subprocess= $hash->{".fhem"}{subprocess};
|
||||||
|
my $data= $subprocess->readFromChild();
|
||||||
|
if(!defined($data)) {
|
||||||
|
Log3 $name, 4, "Calendar $name: still waiting (". $subprocess->{lasterror} .").";
|
||||||
|
InternalTimer(gettimeofday()+POLLINTERVAL, "Calendar_PollChild", $hash, 0);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Log3 $name, 4, "Calendar $name: got result from asynchronous parsing.";
|
||||||
|
$subprocess->wait();
|
||||||
|
Log3 $name, 4, "Calendar $name: asynchronous parsing finished.";
|
||||||
|
my $ical= thaw($data);
|
||||||
|
Calendar_UpdateCalendar($hash, $ical);
|
||||||
|
Calendar_CheckAndRearm($hash);
|
||||||
|
Calendar_Cleanup($hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub Calendar_ParseICS($) {
|
||||||
|
|
||||||
#main::Debug "Calendar $name: parsing data";
|
#main::Debug "Calendar $name: parsing data";
|
||||||
|
my ($ics)= @_;
|
||||||
|
my ($error, $state)= (undef, "");
|
||||||
|
|
||||||
# we parse the calendar into a recursive ICal::Entry structure
|
# we parse the calendar into a recursive ICal::Entry structure
|
||||||
my $ical= ICal::Entry->new("root");
|
my $ical= ICal::Entry->new("root");
|
||||||
@ -2218,27 +2325,55 @@ sub Calendar_UpdateCalendar($$$$) {
|
|||||||
#main::Debug "*** Result:";
|
#main::Debug "*** Result:";
|
||||||
#main::Debug $ical->asString();
|
#main::Debug $ical->asString();
|
||||||
|
|
||||||
my @entries= @{$ical->{entries}};
|
my $numentries= scalar @{$ical->{entries}};
|
||||||
if($#entries<0) {
|
if($numentries<= 0) {
|
||||||
eval { require Compress::Zlib; };
|
eval { require Compress::Zlib; };
|
||||||
if($@) {
|
if($@) {
|
||||||
readingsSingleUpdate($hash, "state",
|
$error= "data not in ICal format; maybe gzip data, but cannot load Compress::Zlib";
|
||||||
"error (data not in ICal format or no Compress::Zlib)", 1);
|
|
||||||
Log3 $hash, 1, "Calendar $name: maybe gzip data, but cannot load Compress::Zlib";
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Log3 $hash, 4, "Calendar $name: unzipping data";
|
|
||||||
$ics = Compress::Zlib::memGunzip($ics);
|
$ics = Compress::Zlib::memGunzip($ics);
|
||||||
$ical->parse($ics);
|
$ical->parse($ics);
|
||||||
@entries= @{$ical->{entries}};
|
$numentries= scalar @{$ical->{entries}};
|
||||||
|
if($numentries<= 0) {
|
||||||
|
$error= "data not in ICal format; even not gzip data";
|
||||||
|
} else {
|
||||||
|
$state= "parsed (gzip data)";
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
if($#entries<0) {
|
} else {
|
||||||
Log3 $hash, 1, "Calendar $name: data not in ICal format";
|
$state= "parsed";
|
||||||
readingsSingleUpdate($hash, "state", "error (data not in ICal format)", 1);
|
|
||||||
return 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$ical->{error}= $error;
|
||||||
|
$ical->{state}= $state;
|
||||||
|
return $ical;
|
||||||
|
}
|
||||||
|
|
||||||
|
###################################
|
||||||
|
sub Calendar_UpdateCalendar($$) {
|
||||||
|
|
||||||
|
my ($hash, $ical)= @_;
|
||||||
|
|
||||||
|
# *******************************
|
||||||
|
# *** Step 1 Digest Parser Result
|
||||||
|
# *******************************
|
||||||
|
|
||||||
|
my $name= $hash->{NAME};
|
||||||
|
my $error= $ical->{error};
|
||||||
|
my $state= $ical->{state};
|
||||||
|
|
||||||
|
if(defined($error)) {
|
||||||
|
Log3 $hash, 2, "Calendar $name: error ($error)";
|
||||||
|
readingsSingleUpdate($hash, "state", "error ($error)", 1);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
readingsSingleUpdate($hash, "state", $state, 1);
|
||||||
|
}
|
||||||
|
my $t= $hash->{".fhem"}{t};
|
||||||
|
my $removeall= $hash->{".fhem"}{removeall};
|
||||||
|
|
||||||
|
my @entries= @{$ical->{entries}};
|
||||||
my $root= @{$ical->{entries}}[0];
|
my $root= @{$ical->{entries}}[0];
|
||||||
my $calname= "?";
|
my $calname= "?";
|
||||||
if($root->{type} ne "VCALENDAR") {
|
if($root->{type} ne "VCALENDAR") {
|
||||||
@ -2249,7 +2384,6 @@ sub Calendar_UpdateCalendar($$$$) {
|
|||||||
$calname= $root->value("X-WR-CALNAME");
|
$calname= $root->value("X-WR-CALNAME");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# *********************
|
# *********************
|
||||||
# *** Step 2 Merging
|
# *** Step 2 Merging
|
||||||
# *********************
|
# *********************
|
||||||
@ -2739,6 +2873,14 @@ sub CalendarAsHtml($;$) {
|
|||||||
<b>Attributes</b>
|
<b>Attributes</b>
|
||||||
<br><br>
|
<br><br>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><code>update sync|async|none</code><br>
|
||||||
|
If this attribute is not set or if it is set to <code>sync</code>, the processing of
|
||||||
|
the calendar is done in the foreground. Large calendars will block FHEM on slow
|
||||||
|
systems. If this attribute is set to <code>async</code>, the processing is done in the
|
||||||
|
background and FHEM will not block during updates. If this attribute is set to
|
||||||
|
<code>none</code>, the calendar will not be updated at all.
|
||||||
|
</li><p>
|
||||||
|
|
||||||
<li><code>hideOlderThan <timespec></code><br>
|
<li><code>hideOlderThan <timespec></code><br>
|
||||||
<code>hideLaterThan <timespec></code><br><p>
|
<code>hideLaterThan <timespec></code><br><p>
|
||||||
|
|
||||||
@ -3124,6 +3266,15 @@ sub CalendarAsHtml($;$) {
|
|||||||
<b>Attributes</b>
|
<b>Attributes</b>
|
||||||
<br><br>
|
<br><br>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><code>update sync|async|none</code><br>
|
||||||
|
Wenn dieses Attribut nicht gesetzt ist oder wenn es auf <code>sync</code> gesetzt ist,
|
||||||
|
findet die Verarbeitung des Kalenders im Vordergrund statt. Große Kalender werden FHEM
|
||||||
|
auf langsamen Systemen blockieren. Wenn das Attribut auf <code>async</code> gesetzt ist,
|
||||||
|
findet die Verarbeitung im Hintergrund statt, und FHEM wird während der Verarbeitung
|
||||||
|
nicht blockieren. Wenn dieses Attribut auf <code>none</code> gesetzt ist, wird der
|
||||||
|
Kalender überhaupt nicht aktualisiert.
|
||||||
|
</li><p>
|
||||||
|
|
||||||
<li><code>hideOlderThan <timespec></code><br>
|
<li><code>hideOlderThan <timespec></code><br>
|
||||||
<code>hideLaterThan <timespec></code><br><p>
|
<code>hideLaterThan <timespec></code><br><p>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user