2020-01-19 14:21:41 +00:00
########################################################################################################################
2022-07-15 18:56:52 +00:00
# $Id: 57_SSCal.pm 24736 2021-07-12 15:43:19Z DS_Starter $
2020-01-19 14:21:41 +00:00
#########################################################################################################################
# 57_SSCal.pm
#
2022-07-16 06:21:46 +00:00
# (c) 2019 - 2022 by Heiko Maaz
2020-01-19 14:21:41 +00:00
# e-mail: Heiko dot Maaz at t-online dot de
#
# This Module integrate the Synology Calendar into FHEM
#
# This script 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/>.
#
#########################################################################################################################
#
# Definition: define <name> SSCal <ServerAddr> [ServerPort] [Protocol]
#
# Example: define SynCal SSCal 192.168.2.20 [5000] [HTTP(S)]
#
2020-05-16 15:35:34 +00:00
package FHEM::SSCal ; ## no critic 'package'
2020-01-19 14:21:41 +00:00
use strict ;
use warnings ;
2020-04-25 17:52:39 +00:00
eval "use JSON;1;" or my $ SSCalMM = "JSON" ; ## no critic 'eval' # Debian: apt-get install libjson-perl
2020-01-19 14:21:41 +00:00
use Data::Dumper ; # Perl Core module
2020-05-16 15:35:34 +00:00
use GPUtils qw( GP_Import GP_Export ) ; # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
2020-12-16 14:40:38 +00:00
use FHEM::SynoModules::API qw( apistatic ) ; # API Modul
use FHEM::SynoModules::SMUtils qw( completeAPI
showAPIinfo
showModuleInfo
addSendqueue
listSendqueue
purgeSendqueue
checkSendRetry
startFunctionDelayed
evaljson
getClHash
delClHash
delReadings
setCredentials
getCredentials
showStoredCredentials
setReadingErrorState
setReadingErrorNone
login
logout
moduleVersion
updQueueLength
trim
jboolmap
) ; # Hilfsroutinen Modul
use FHEM::SynoModules::ErrCodes qw( expErrors ) ; # Error Code Modul
2020-01-19 14:21:41 +00:00
use MIME::Base64 ;
2020-05-16 15:35:34 +00:00
use POSIX qw( strftime ) ;
use Time::HiRes qw( gettimeofday ) ;
2020-01-19 14:21:41 +00:00
use HttpUtils ;
use Encode ;
2020-12-16 14:40:38 +00:00
use utf8 ;
2020-01-19 14:21:41 +00:00
use Blocking ;
no if $] >= 5.017011 , warnings = > 'experimental::smartmatch' ;
2020-04-25 17:52:39 +00:00
eval "use FHEM::Meta;1" or my $ modMetaAbsent = 1 ; ## no critic 'eval'
2020-01-19 14:21:41 +00:00
# no if $] >= 5.017011, warnings => 'experimental';
2020-05-16 15:35:34 +00:00
# Run before module compilation
BEGIN {
# Import from main::
GP_Import (
qw(
AnalyzePerlCommand
asyncOutput
AttrVal
BlockingCall
BlockingKill
CancelDelayedShutdown
CommandSet
CommandAttr
CommandDelete
CommandDefine
CommandGet
CommandSetReading
CommandTrigger
data
defs
devspec2array
fhemTimeLocal
FmtDateTime
FmtTime
FW_makeImage
getKeyValue
HttpUtils_NonblockingGet
init_done
InternalTimer
IsDisabled
Log3
modules
readingFnAttributes
ReadingsVal
RemoveInternalTimer
readingsDelete
readingsBeginUpdate
readingsBulkUpdate
readingsBulkUpdateIfChanged
readingsEndUpdate
readingsSingleUpdate
ReadingsTimestamp
sortTopicNum
setKeyValue
TimeNow
)
) ;
# Export to main context with different name
# my $pkg = caller(0);
# my $main = $pkg;
# $main =~ s/^(?:.+::)?([^:]+)$/main::$1\_/gx;
# foreach (@_) {
# *{ $main . $_ } = *{ $pkg . '::' . $_ };
# }
GP_Export (
qw(
Initialize
)
) ;
}
2020-01-19 14:21:41 +00:00
# Versions History intern
2020-05-16 15:35:34 +00:00
my % vNotesIntern = (
2022-07-16 06:21:46 +00:00
"2.4.10" = > "16.07.2022 fix problem recurring MONTHLY appointment by day, " .
"forum: https://forum.fhem.de/index.php/topic,106963.msg1228098.html#msg1228098 " ,
2021-07-11 18:33:25 +00:00
"2.4.9" = > "11.07.2021 set adaption of AUTH for DSM7 compatibility " ,
2020-12-16 14:40:38 +00:00
"2.4.8" = > "16.12.2020 accep umlauts in calendar name " ,
"2.4.7" = > "08.12.2020 fix handle code recognition in createAtDevices as single line " ,
"2.4.6" = > "06.11.2020 bugfix weekly byDay " ,
"2.4.5" = > "03.11.2020 fix commandref wiki link " ,
"2.4.4" = > "06.10.2020 use addSendqueue from SMUtils, delete local addSendqueue " ,
"2.4.3" = > "04.10.2020 use showStoredCredentials from SMUtils " ,
"2.4.2" = > "03.10.2020 get from SMUtils: completeAPI showAPIinfo evaljson setReadingErrorState setReadingErrorNone showModuleInfo " .
"login logout getClHash delClHash trim moduleVersion updQueueLength delReadings checkSendRetry startFunctionDelayed " .
"minor fix in periodicCall " ,
2020-05-20 17:25:50 +00:00
"2.4.1" = > "20.05.2020 new function 'evalTimeAndWrite' " ,
"2.4.0" = > "19.05.2020 more changes according to PBP, switch to packages, fix cannot delete (and display in table) EventId of block 0 " ,
2020-04-25 17:52:39 +00:00
"2.3.0" = > "25.04.2020 set compatibility to Calendar package 2.3.4-0631, some changes according to PBP " ,
"2.2.3" = > "24.03.2020 minor code change " ,
"2.2.2" = > "08.03.2020 review commandref " ,
2020-03-04 21:33:37 +00:00
"2.2.1" = > "04.03.2020 expand composite event 'compositeBlockNumbers' by 'none' " ,
"2.2.0" = > "03.03.2020 new composite event 'compositeBlockNumbers' " ,
2020-03-02 23:36:47 +00:00
"2.1.0" = > "01.03.2020 expand composite Event, bugfix API if entry with 'is_all_day' and at first position in 'data' " ,
2020-02-28 15:09:57 +00:00
"2.0.0" = > "28.02.2020 check in release " ,
2020-02-27 20:23:56 +00:00
"1.15.0" = > "27.02.2020 fix recurrence WEEKLY by DAY, MONTHLY by MONTHDAY and BYDAY, create commandref " ,
2020-02-23 08:34:54 +00:00
"1.14.0" = > "23.02.2020 new setter \"calUpdate\" consistent for both models, calEventList and calToDoList are obsolete " ,
2020-02-22 11:02:26 +00:00
"1.13.0" = > "22.02.2020 manage recurring entries if one/more of a series entry is deleted or changed and their reminder times " ,
2020-02-18 08:48:21 +00:00
"1.12.0" = > "15.02.2020 create At-devices from calendar entries if FHEM-commands or Perl-routines detected in \"Summary\", minor fixes " ,
2020-05-16 15:35:34 +00:00
"1.11.0" = > "14.02.2020 new function doCompositeEvents to create Composite Events for special notify use in FHEM " ,
2020-02-13 12:05:29 +00:00
"1.10.0" = > "13.02.2020 new key cellStyle for attribute tableSpecs, avoid FHEM crash when are design failures in tableSpecs " ,
2020-02-11 23:05:45 +00:00
"1.9.0" = > "11.02.2020 new reading Weekday with localization, more field selection for overview table " ,
2020-05-16 15:35:34 +00:00
"1.8.0" = > "09.02.2020 evaluate icons for DaysLeft, Map and State in sub evalTableSpecs , fix no table is shown after FHEM restart " ,
2020-02-09 10:21:37 +00:00
"1.7.0" = > "09.02.2020 respect global language setting for some presentation, new attributes tableSpecs & tableColumnMap, days left in overview " .
"formatting overview table, feature smallScreen for tableSpecs, rename attributes to tableFields, " .
2020-05-16 15:35:34 +00:00
"tableInDetail, tableInRoom, correct enddate/time if is_all_day incl. bugfix API, function boolean " .
2020-02-09 15:05:13 +00:00
"to avoid fhem crash if an older JSON module is installed " ,
2020-02-04 14:19:14 +00:00
"1.6.1" = > "03.02.2020 rename attributes to \"calOverviewInDetail\",\"calOverviewInRoom\", bugfix of gps extraction " ,
2020-02-08 09:18:56 +00:00
"1.6.0" = > "03.02.2020 new attribute \"tableFields\" to show specified fields in calendar overview in detail/room view, " .
2020-02-03 14:16:18 +00:00
"Model Diary/Tasks defined, periodic call of ToDo-Liists now possible " ,
2020-02-03 21:40:18 +00:00
"1.5.0" = > "02.02.2020 new attribute \"calOverviewInDetail\",\"calOverviewInRoom\" to control calendar overview in room or detail view " ,
2020-05-16 15:35:34 +00:00
"1.4.0" = > "02.02.2020 get calAsHtml command or use sub calAsHtml(\$name) " ,
"1.3.1" = > "01.02.2020 add errauthlist hash for login/logout API error codes " ,
2020-02-01 13:38:57 +00:00
"1.3.0" = > "01.02.2020 new command \"cleanCompleteTasks\" to delete completed tasks, \"deleteEventId\" to delete an event id, " .
"new get command \"apiInfo\" - detect and show API info, avoid empty readings " ,
2020-01-31 15:27:30 +00:00
"1.2.0" = > "29.01.2020 get tasks from calendar with set command 'calToDoList' " ,
"1.1.14" = > "29.01.2020 ignore calendars of type ne 'Event' for set calEventList " ,
2020-01-20 19:06:58 +00:00
"1.1.13" = > "20.01.2020 change save and read credentials routine " ,
2020-01-19 14:21:41 +00:00
"1.1.12" = > "19.01.2020 add attribute interval, automatic event fetch " ,
"1.1.11" = > "18.01.2020 status information added: upcoming, alarmed, started, ended " ,
"1.1.10" = > "17.01.2020 attribute asyncMode for parsing events in BlockingCall, some fixes " ,
"1.1.9" = > "14.01.2020 preparation of asynchronous calendar event extraction, some fixes " ,
"1.1.8" = > "13.01.2020 can proces WEEKLY general recurring events, use \$data{SSCal}{\$name}{eventlist} as Hash of Events " ,
"1.1.7" = > "12.01.2020 can proces WEEKLY recurring events BYDAY " ,
"1.1.6" = > "11.01.2020 can proces DAILY recurring events " ,
"1.1.5" = > "10.01.2020 can proces MONTHLY recurring events BYDAY " ,
"1.1.4" = > "07.01.2020 can proces MONTHLY recurring events BYMONTHDAY " ,
"1.1.3" = > "06.01.2020 can proces YEARLY recurring events " ,
"1.1.2" = > "04.01.2020 logout if new credentials are set " ,
"1.1.1" = > "03.01.2020 add array of 'evt_notify_setting' " ,
"1.1.0" = > "01.01.2020 logout command " ,
"1.0.0" = > "18.12.2019 initial "
) ;
2020-12-16 14:40:38 +00:00
my % hset = ( # Hash für Set-Funktion (needcred => 1: Funktion benötigt gesetzte Credentials)
credentials = > { fn = > \ & _setcredentials , needcred = > 0 } ,
calUpdate = > { fn = > \ & _setcalUpdate , needcred = > 1 } ,
deleteEventId = > { fn = > \ & _setdeleteEventId , needcred = > 1 } ,
eraseReadings = > { fn = > \ & _seteraseReadings , needcred = > 0 } ,
listSendqueue = > { fn = > \ & listSendqueue , needcred = > 0 } ,
logout = > { fn = > \ & _setlogout , needcred = > 0 } ,
purgeSendqueue = > { fn = > \ & purgeSendqueue , needcred = > 0 } ,
restartSendqueue = > { fn = > \ & _setrestartSendqueue , needcred = > 1 } ,
cleanCompleteTasks = > { fn = > \ & _setcleanCompleteTasks , needcred = > 1 } ,
) ;
my % hget = ( # Hash für Get-Funktion (needcred => 1: Funktion benötigt gesetzte Credentials)
apiInfo = > { fn = > \ & _getapiInfo , needcred = > 1 } ,
calAsHtml = > { fn = > \ & _getcalAsHtml , needcred = > 0 } ,
getCalendars = > { fn = > \ & _getgetCalendars , needcred = > 1 } ,
storedCredentials = > { fn = > \ & _getstoredCredentials , needcred = > 1 } ,
versionNotes = > { fn = > \ & _getversionNotes , needcred = > 0 } ,
) ;
2021-07-11 18:33:25 +00:00
my % hvada = ( # Funktionshash Version Adaption
"a01" = > { AUTH = > "6" , INFO = > "1" , CAL = > "2" ,
EVENT = > "3" , SHARE = > "1" , TODO = > "1" } ,
) ;
2020-01-19 14:21:41 +00:00
# Versions History extern
2020-05-16 15:35:34 +00:00
my % vNotesExtern = (
2020-12-16 14:40:38 +00:00
"2.4.0" = > "19.05.2020 Switched to Perl packages. Use <i>FHEM::SSCal::calAsHtml</i> from now instead of " .
"<i>SSCal_calAsHtml</i> in definition of weblink devices and own code. " ,
2020-04-25 17:52:39 +00:00
"2.3.0" = > "25.04.2020 The module compatibility is set to Synology Calendar package 2.3.4-0631. " ,
2020-01-19 14:21:41 +00:00
"1.0.0" = > "18.12.2019 initial "
) ;
2020-04-25 17:52:39 +00:00
# Hints EN
2020-05-16 15:35:34 +00:00
my % vHintsExt_en = (
2020-04-25 17:52:39 +00:00
"1" = > "The module implements the Internet Calendaring and Scheduling Core Object Specification (iCalendar) " .
"according to <a href=\"https://tools.ietf.org/html/rfc5545\">RFC5545</a>. "
) ;
# Hints DE
2020-05-16 15:35:34 +00:00
my % vHintsExt_de = (
2020-04-25 17:52:39 +00:00
"1" = > "Das Modul implementiert die Internet Calendaring and Scheduling Core Object Specification (iCalendar) " .
"gemäß <a href=\"https://tools.ietf.org/html/rfc5545\">RFC5545</a>. "
) ;
2020-12-16 14:40:38 +00:00
# Standardvariablen
my $ splitstr = "!_ESC_!" ; # Split-String zur Übergabe in getCredentials, login & Co.
my $ queueStartFn = "FHEM::SSCal::getApiSites" ; # Startfunktion zur Queue-Abarbeitung
2020-01-19 14:21:41 +00:00
################################################################
2020-05-16 15:35:34 +00:00
sub Initialize {
2020-01-19 14:21:41 +00:00
my ( $ hash ) = @ _ ;
2020-05-16 15:35:34 +00:00
$ hash - > { DefFn } = \ & Define ;
$ hash - > { UndefFn } = \ & Undef ;
$ hash - > { DeleteFn } = \ & Delete ;
$ hash - > { SetFn } = \ & Set ;
$ hash - > { GetFn } = \ & Get ;
$ hash - > { AttrFn } = \ & Attr ;
$ hash - > { DelayedShutdownFn } = \ & DelayedShutdown ;
2020-01-19 14:21:41 +00:00
2020-02-02 18:47:04 +00:00
# Darstellung FHEMWEB
2020-05-16 15:35:34 +00:00
# $hash->{FW_summaryFn} = \&FWsummaryFn;
2020-02-02 18:47:04 +00:00
$ hash - > { FW_addDetailToSummary } = 1 ; # zusaetzlich zu der Device-Summary auch eine Neue mit dem Inhalt von DetailFn angezeigt
2020-05-16 15:35:34 +00:00
$ hash - > { FW_detailFn } = \ & FWdetailFn ;
2020-02-02 18:47:04 +00:00
$ hash - > { FW_deviceOverview } = 1 ;
$ hash - > { AttrList } = "asyncMode:1,0 " .
2020-02-16 21:48:39 +00:00
"createATDevs:1,0 " .
2020-04-25 17:52:39 +00:00
"cutOlderDays " .
"cutLaterDays " .
2020-01-19 14:21:41 +00:00
"disable:1,0 " .
2020-02-07 14:59:41 +00:00
"tableSpecs:textField-long " .
2020-04-25 17:52:39 +00:00
"filterCompleteTask:1,2,3 " .
"filterDueTask:1,2,3 " .
2020-01-19 14:21:41 +00:00
"interval " .
2020-02-07 14:59:41 +00:00
"loginRetries:1,2,3,4,5,6,7,8,9,10 " .
2020-04-25 17:52:39 +00:00
"tableColumnMap:icon,data,text " .
2020-01-19 14:21:41 +00:00
"showRepeatEvent:true,false " .
"showPassInLog:1,0 " .
2020-02-08 09:18:56 +00:00
"tableInDetail:0,1 " .
"tableInRoom:0,1 " .
2020-02-12 16:28:05 +00:00
"tableFields:multiple-strict,Symbol,Begin,End,DaysLeft,DaysLeftLong,Weekday,Timezone,Summary,Description,Status,Completion,Location,Map,Calendar,EventId " .
2020-01-19 14:21:41 +00:00
"timeout " .
"usedCalendars:--wait#for#Calendar#list-- " .
$ readingFnAttributes ;
2020-05-16 15:35:34 +00:00
FHEM::Meta:: InitMod ( __FILE__ , $ hash ) if ( ! $ modMetaAbsent ) ; # für Meta.pm (https://forum.fhem.de/index.php/topic,97589.0.html)
2020-01-19 14:21:41 +00:00
return ;
}
################################################################
# define SyncalBot SSCal 192.168.2.10 [5000] [HTTP(S)]
2020-02-01 13:38:57 +00:00
# [1] [2] [3] [4]
2020-01-19 14:21:41 +00:00
#
################################################################
2020-05-16 15:35:34 +00:00
sub Define {
2020-01-19 14:21:41 +00:00
my ( $ hash , $ def ) = @ _ ;
my $ name = $ hash - > { NAME } ;
return "Error: Perl module " . $ SSCalMM . " is missing. Install it on Debian with: sudo apt-get install libjson-perl" if ( $ SSCalMM ) ;
my @ a = split ( "[ \t][ \t]*" , $ def ) ;
if ( int ( @ a ) < 2 ) {
2020-02-03 14:16:18 +00:00
return "You need to specify more parameters.\n" . "Format: define <name> SSCal <ServerAddress> [Port] [HTTP(S)] [Tasks]" ;
2020-01-19 14:21:41 +00:00
}
2020-02-03 14:16:18 +00:00
shift @ a ; shift @ a ;
2020-04-25 17:52:39 +00:00
my $ addr = ( $ a [ 0 ] && $ a [ 0 ] ne "Tasks" ) ? $ a [ 0 ] : "" ;
2020-02-03 14:16:18 +00:00
my $ port = ( $ a [ 1 ] && $ a [ 1 ] ne "Tasks" ) ? $ a [ 1 ] : 5000 ;
my $ prot = ( $ a [ 2 ] && $ a [ 2 ] ne "Tasks" ) ? lc ( $ a [ 2 ] ) : "http" ;
my $ model = "Diary" ;
$ model = "Tasks" if ( grep { $ _ eq "Tasks" } @ a ) ;
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
$ hash - > { SERVERADDR } = $ addr ;
$ hash - > { SERVERPORT } = $ port ;
2020-01-19 14:21:41 +00:00
$ hash - > { MODEL } = "Calendar" ;
2020-12-16 14:40:38 +00:00
$ hash - > { PROTOCOL } = $ prot ;
2020-02-03 14:16:18 +00:00
$ hash - > { MODEL } = $ model ;
2020-01-19 14:21:41 +00:00
$ hash - > { RESEND } = "next planned SendQueue start: immediately by next entry" ;
2020-12-16 14:40:38 +00:00
$ hash - > { HELPER } { MODMETAABSENT } = 1 if ( $ modMetaAbsent ) ; # Modul Meta.pm nicht vorhanden
$ hash - > { HELPER } { CALFETCHED } = 0 ; # vorhandene Kalender sind noch nicht abgerufen
CommandAttr ( undef , "$name room SSCal" ) ;
CommandAttr ( undef , "$name event-on-update-reading .*Summary,state" ) ;
my $ params = {
hash = > $ hash ,
notes = > \ % vNotesIntern ,
useAPI = > 1 ,
useSMUtils = > 1 ,
useErrCodes = > 1
} ;
use version 0.77 ; our $ VERSION = moduleVersion ( $ params ) ; # Versionsinformationen setzen
getCredentials ( $ hash , 1 , "credentials" , $ splitstr ) ; # Credentials lesen
$ data { SSCal } { $ name } { sendqueue } { index } = 0 ; # Index der Sendequeue initialisieren
2020-01-19 14:21:41 +00:00
readingsBeginUpdate ( $ hash ) ;
2020-04-25 17:52:39 +00:00
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , "none" ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , "none" ) ;
2020-12-16 14:40:38 +00:00
readingsBulkUpdateIfChanged ( $ hash , "QueueLength" , 0 ) ; # Länge Sendqueue initialisieren
readingsBulkUpdate ( $ hash , "nextUpdate" , "undefined" ) ; # Abrufmode initialisieren
readingsBulkUpdate ( $ hash , "state" , "Initialized" ) ; # Init state
2020-01-19 14:21:41 +00:00
readingsEndUpdate ( $ hash , 1 ) ;
2020-12-16 14:40:38 +00:00
initOnBoot ( $ name ) ; # initiale Routinen nach Start ausführen , verzögerter zufälliger Start
2020-01-19 14:21:41 +00:00
2020-04-25 17:52:39 +00:00
return ;
2020-01-19 14:21:41 +00:00
}
################################################################
# Die Undef-Funktion wird aufgerufen wenn ein Gerät mit delete
# gelöscht wird oder bei der Abarbeitung des Befehls rereadcfg,
# der ebenfalls alle Geräte löscht und danach das
# Konfigurationsfile neu einliest.
# Funktion: typische Aufräumarbeiten wie das
# saubere Schließen von Verbindungen oder das Entfernen von
# internen Timern, sofern diese im Modul zum Pollen verwendet
# wurden.
################################################################
2020-05-16 15:35:34 +00:00
sub Undef {
2020-01-19 14:21:41 +00:00
my ( $ hash , $ arg ) = @ _ ;
my $ name = $ hash - > { NAME } ;
BlockingKill ( $ hash - > { HELPER } { RUNNING_PID } ) if ( $ hash - > { HELPER } { RUNNING_PID } ) ;
delete $ data { SSCal } { $ name } ;
2020-12-16 14:40:38 +00:00
2020-01-19 14:21:41 +00:00
RemoveInternalTimer ( $ name ) ;
2020-04-25 17:52:39 +00:00
return ;
2020-01-19 14:21:41 +00:00
}
#######################################################################################################
# Mit der X_DelayedShutdown Funktion kann eine Definition das Stoppen von FHEM verzögern um asynchron
# hinter sich aufzuräumen.
# Je nach Rückgabewert $delay_needed wird der Stopp von FHEM verzögert (0|1).
# Sobald alle nötigen Maßnahmen erledigt sind, muss der Abschluss mit CancelDelayedShutdown($name) an
# FHEM zurückgemeldet werden.
#######################################################################################################
2020-05-16 15:35:34 +00:00
sub DelayedShutdown {
2020-01-19 14:21:41 +00:00
my ( $ hash ) = @ _ ;
my $ name = $ hash - > { NAME } ;
if ( $ hash - > { HELPER } { SID } ) {
2020-12-16 14:40:38 +00:00
logout ( $ hash , $ data { SSCal } { $ name } { calapi } , $ splitstr ) ; # Session alter User beenden falls vorhanden
2020-01-19 14:21:41 +00:00
return 1 ;
}
return 0 ;
}
#################################################################
# Wenn ein Gerät in FHEM gelöscht wird, wird zuerst die Funktion
# X_Undef aufgerufen um offene Verbindungen zu schließen,
# anschließend wird die Funktion X_Delete aufgerufen.
# Funktion: Aufräumen von dauerhaften Daten, welche durch das
# Modul evtl. für dieses Gerät spezifisch erstellt worden sind.
# Es geht hier also eher darum, alle Spuren sowohl im laufenden
# FHEM-Prozess, als auch dauerhafte Daten bspw. im physikalischen
# Gerät zu löschen die mit dieser Gerätedefinition zu tun haben.
#################################################################
2020-05-16 15:35:34 +00:00
sub Delete {
2020-01-19 14:21:41 +00:00
my ( $ hash , $ arg ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my $ index = $ hash - > { TYPE } . "_" . $ hash - > { NAME } . "_credentials" ;
2020-12-16 14:40:38 +00:00
setKeyValue ( $ index , undef ) ; # gespeicherte Credentials löschen
2020-01-19 14:21:41 +00:00
2020-04-25 17:52:39 +00:00
return ;
2020-01-19 14:21:41 +00:00
}
################################################################
2020-12-16 14:40:38 +00:00
sub Attr { ## no critic 'complexity'
2020-01-19 14:21:41 +00:00
my ( $ cmd , $ name , $ aName , $ aVal ) = @ _ ;
2020-02-03 14:16:18 +00:00
my $ hash = $ defs { $ name } ;
2020-04-25 17:52:39 +00:00
my $ model = $ hash - > { MODEL } ;
2020-04-25 19:53:14 +00:00
my ( $ do , $ val ) ;
2020-01-19 14:21:41 +00:00
# $cmd can be "del" or "set"
# $name is device name
# aName and aVal are Attribute name and value
2020-04-25 17:52:39 +00:00
if ( $ cmd eq "set" ) {
if ( $ aName =~ /filterCompleteTask|filterDueTask/x && $ model ne "Tasks" ) {
2020-05-16 15:35:34 +00:00
return qq{ The attribute "$aName" is only valid for devices of MODEL "Tasks" ! Please set this attribute in a device of this MODEL. } ;
2020-04-25 17:52:39 +00:00
}
2020-02-05 23:12:14 +00:00
2020-04-25 17:52:39 +00:00
if ( $ aName =~ /showRepeatEvent/x && $ model ne "Diary" ) {
2020-05-16 15:35:34 +00:00
return qq{ The attribute "$aName" is only valid for devices of MODEL "Diary" ! Please set this attribute in a device of this MODEL. } ;
2020-04-25 17:52:39 +00:00
}
2020-02-05 23:12:14 +00:00
2020-04-25 17:52:39 +00:00
if ( $ aName =~ /tableSpecs/x ) {
2020-05-17 10:59:56 +00:00
return qq{ The attribute "$aName" has wrong syntax. The value must be set into " { } ". } if ( $ aVal !~ m/^\s*?\{.*\}\s*?$/xs ) ;
2020-04-25 17:52:39 +00:00
}
2020-02-12 21:12:47 +00:00
my $ attrVal = $ aVal ;
2020-05-17 10:59:56 +00:00
if ( $ attrVal =~ m/^\{.*\}$/xs && $ attrVal =~ m/=>/x ) {
2020-04-25 17:52:39 +00:00
$ attrVal =~ s/\@/\\\@/gx ;
$ attrVal =~ s/\$/\\\$/gx ;
my $ av = eval $ attrVal ; ## no critic 'eval'
2020-02-05 23:12:14 +00:00
if ( $@ ) {
Log3 ( $ name , 2 , "$name - Error while evaluate: " . $@ ) ;
2020-02-08 09:18:56 +00:00
return $@ ;
2020-12-16 14:40:38 +00:00
}
else {
2020-02-05 23:12:14 +00:00
$ attrVal = $ av if ( ref ( $ av ) eq "HASH" ) ;
}
}
$ hash - > { HELPER } { $ aName } = $ attrVal ;
2020-12-16 14:40:38 +00:00
}
else {
2020-02-05 23:12:14 +00:00
delete $ hash - > { HELPER } { $ aName } ;
}
2020-01-19 14:21:41 +00:00
if ( $ aName eq "disable" ) {
if ( $ cmd eq "set" ) {
$ do = $ aVal ? 1 : 0 ;
}
$ do = 0 if ( $ cmd eq "del" ) ;
2020-04-25 17:52:39 +00:00
2020-01-19 14:21:41 +00:00
$ val = ( $ do == 1 ? "disabled" : "initialized" ) ;
2020-04-25 17:52:39 +00:00
if ( $ do == 1 ) {
RemoveInternalTimer ( $ name ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-05-16 15:35:34 +00:00
InternalTimer ( gettimeofday ( ) + 2 , "FHEM::SSCal::initOnBoot" , $ name , 0 ) if ( $ init_done ) ;
2020-04-25 17:52:39 +00:00
}
2020-01-19 14:21:41 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate ( $ hash , "state" , $ val ) ;
readingsEndUpdate ( $ hash , 1 ) ;
}
if ( $ cmd eq "set" ) {
2020-04-25 17:52:39 +00:00
if ( $ aName =~ m/timeout|cutLaterDays|cutOlderDays|interval/x ) {
2020-05-16 15:35:34 +00:00
unless ( $ aVal =~ /^\d+$/x ) { return qq{ The value of $aName is not valid. Use only integers 1-9 ! } ; }
2020-01-19 14:21:41 +00:00
}
2020-04-25 17:52:39 +00:00
if ( $ aName =~ m/interval/x ) {
2020-05-16 15:35:34 +00:00
RemoveInternalTimer ( $ name , "FHEM::SSCal::periodicCall" ) ;
2020-12-16 14:40:38 +00:00
InternalTimer ( gettimeofday ( ) + 1.0 , "FHEM::SSCal::periodicCall" , $ name , 0 ) ;
2020-01-19 14:21:41 +00:00
}
}
2020-04-25 17:52:39 +00:00
return ;
2020-01-19 14:21:41 +00:00
}
2020-12-16 14:40:38 +00:00
#############################################################################################
# Setter
#############################################################################################
sub Set {
2020-01-19 14:21:41 +00:00
my ( $ hash , @ a ) = @ _ ;
return "\"set X\" needs at least an argument" if ( @ a < 2 ) ;
my $ name = $ a [ 0 ] ;
my $ opt = $ a [ 1 ] ;
my $ prop = $ a [ 2 ] ;
my $ prop1 = $ a [ 3 ] ;
2020-12-16 14:40:38 +00:00
2020-02-03 14:16:18 +00:00
my $ model = $ hash - > { MODEL } ;
2020-01-19 14:21:41 +00:00
my ( $ success , $ setlist ) ;
return if ( IsDisabled ( $ name ) ) ;
2020-02-16 21:48:39 +00:00
my $ idxlist = join ( "," , sort keys % { $ data { SSCal } { $ name } { sendqueue } { entries } } ) ;
2020-02-01 13:38:57 +00:00
# alle aktuell angezeigten Event Id's ermitteln
my ( @ idarray , $ evids ) ;
2020-04-25 19:53:14 +00:00
for my $ key ( keys % { $ defs { $ name } { READINGS } } ) {
2020-05-16 15:35:34 +00:00
next if $ key !~ /_EventId$/x ;
2020-02-01 13:38:57 +00:00
push ( @ idarray , $ defs { $ name } { READINGS } { $ key } { VAL } ) ;
}
if ( @ idarray ) {
my % seen ;
2020-04-25 17:52:39 +00:00
my @ unique = sort { $ a <=> $ b } grep { ! $ seen { $ _ } + + } @ idarray ; # distinct / unique the keys
2020-02-01 13:38:57 +00:00
$ evids = join ( "," , @ unique ) ;
}
2020-12-16 14:40:38 +00:00
$ setlist = "Unknown argument $opt, choose one of " ;
if ( ! $ hash - > { CREDENTIALS } ) { # initiale setlist für neue Devices
$ setlist . = "credentials " ;
}
if ( $ hash - > { CREDENTIALS } ) { # Model Terminkalender & Aufgabenliste
$ setlist . = "calUpdate " .
"credentials " .
"eraseReadings:noArg " .
"listSendqueue:noArg " .
"logout:noArg " .
"restartSendqueue:noArg " .
( $ evids ? "deleteEventId:$evids " : "deleteEventId:noArg " ) .
( $ idxlist ? "purgeSendqueue:-all-,-permError-,$idxlist " : "purgeSendqueue:-all-,-permError- " )
;
2020-01-19 14:21:41 +00:00
}
2020-12-16 14:40:38 +00:00
if ( $ hash - > { CREDENTIALS } && $ model eq "Tasks" ) { # Model Aufgabenliste
$ setlist . = "cleanCompleteTasks:noArg " ;
}
my $ params = {
hash = > $ hash ,
name = > $ name ,
opt = > $ opt ,
prop = > $ prop ,
prop1 = > $ prop1 ,
aref = > \ @ a ,
} ;
if ( $ hset { $ opt } && defined & { $ hset { $ opt } { fn } } ) {
my $ ret = q{ } ;
2020-04-25 17:52:39 +00:00
2020-12-16 14:40:38 +00:00
if ( ! $ hash - > { CREDENTIALS } && $ hset { $ opt } { needcred } ) {
return qq{ Credentials of $name are not set. Make sure they are set with "set $name credentials <username> <password>" } ;
2020-04-25 17:52:39 +00:00
}
2020-12-16 14:40:38 +00:00
$ ret = & { $ hset { $ opt } { fn } } ( $ params ) ;
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
return $ ret ;
}
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
return "$setlist" ;
}
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
######################################################################################
# Setter credentials
######################################################################################
sub _setcredentials {
my $ paref = shift ;
my $ hash = $ paref - > { hash } ;
my $ name = $ paref - > { name } ;
my $ opt = $ paref - > { opt } ;
my $ prop = $ paref - > { prop } ;
my $ prop1 = $ paref - > { prop1 } ;
return qq{ The command "$opt" needs an argument. } if ( ! $ prop ) ;
if ( $ hash - > { HELPER } { SID } ) {
logout ( $ hash , $ data { SSCal } { $ name } { calapi } , $ splitstr ) ; # Session alter User beenden falls vorhanden
}
my ( $ success ) = setCredentials ( $ hash , "credentials" , $ prop , $ prop1 , $ splitstr ) ;
if ( $ success ) {
my $ params = {
name = > $ name ,
opmode = > "listcal" ,
api = > "CAL" ,
method = > "list" ,
params = > "&is_todo=true&is_evt=true" ,
} ;
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
addSendqueue ( $ params ) ;
getApiSites ( $ name ) ;
return "credentials saved successfully" ;
}
else {
return "Error while saving credentials - see logfile for details" ;
}
2020-02-23 08:34:54 +00:00
2020-12-16 14:40:38 +00:00
return ;
}
2020-02-23 08:34:54 +00:00
2020-12-16 14:40:38 +00:00
######################################################################################
# Setter calUpdate
# Termine einer Cal_id (Liste) in Zeitgrenzen abrufen
######################################################################################
sub _setcalUpdate {
my $ paref = shift ;
my $ hash = $ paref - > { hash } ;
my $ name = $ paref - > { name } ;
my $ opt = $ paref - > { opt } ;
my $ aref = $ paref - > { aref } ;
if ( ! $ hash - > { HELPER } { CALFETCHED } ) {
return qq{ Obtain the Calendar list first with "get $name getCalendars" command. } ;
}
my $ model = $ hash - > { MODEL } ;
my $ cals = AttrVal ( $ name , "usedCalendars" , "" ) ;
my @ a = @$ aref ;
shift @ a ; shift @ a ;
my $ c = join ( " " , @ a ) ;
$ cals = $ c ? $ c : $ cals ;
if ( ! $ cals ) {
return qq{ Please set attribute "usedCalendars" or specify the Calendar(s) you want read in "$opt" command. } ;
}
# Kalender aufsplitten und zu jedem die ID ermitteln
my @ ca = split ( "," , $ cals ) ;
2020-01-31 15:27:30 +00:00
2020-12-16 14:40:38 +00:00
my ( $ oids , @ cas ) ;
my $ caltype = $ model eq "Diary" ? "Event" : "ToDo" ;
for my $ cal ( @ ca ) {
my $ oid = $ hash - > { HELPER } { CALENDARS } { "$cal" } { id } ;
next if ( ! $ oid ) ;
2020-04-25 17:52:39 +00:00
2020-12-16 14:40:38 +00:00
if ( $ hash - > { HELPER } { CALENDARS } { "$cal" } { type } ne $ caltype ) {
Log3 ( $ name , 3 , qq{ $name - The Calendar "$cal" is not of type "$caltype" and will be ignored. } ) ;
next ;
}
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
$ oids . = "," if ( $ oids ) ;
$ oids . = '"' . $ oid . '"' ;
push ( @ cas , $ cal ) ;
2020-04-25 17:52:39 +00:00
2020-12-16 14:40:38 +00:00
if ( ! $ oid ) {
Log3 ( $ name , 2 , qq{ $name - WARNING - The Calendar "$cal" seems to be unknown because its ID couldn't be found. } ) ;
2020-02-01 13:38:57 +00:00
}
2020-12-16 14:40:38 +00:00
}
if ( ! $ oids ) {
return qq{ No Calendar of type "$caltype" was selected or its ID(s) couldn't be found. } ;
}
Log3 ( $ name , 5 , "$name - Calendar selection for add queue: " . join ( ',' , @ cas ) ) ;
if ( $ model eq "Diary" ) { # Modell Terminkalender
my ( $ err , $ tstart , $ tend ) = timeEdge ( $ name ) ;
2020-04-25 17:52:39 +00:00
2020-12-16 14:40:38 +00:00
if ( $ err ) {
Log3 ( $ name , 2 , "$name - ERROR in timestamp: $err" ) ;
my $ errorcode = "910" ;
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , $ err ) ;
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , $ errorcode ) ;
readingsBulkUpdate ( $ hash , "state" , "Error" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
return "ERROR in timestamp: $err" ;
}
my $ lr = AttrVal ( $ name , "showRepeatEvent" , "true" ) ;
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
my $ params = {
name = > $ name ,
opmode = > "eventlist" ,
api = > "EVENT" ,
method = > "list" ,
params = > "&cal_id_list=[$oids]&start=$tstart&end=$tend&list_repeat=$lr" ,
} ;
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
addSendqueue ( $ params ) ;
getApiSites ( $ name ) ;
}
else { # Modell Aufgabenliste
my $ limit = "" ; # Limit of matched tasks
my $ offset = 0 ; # offset of mnatched tasks
my $ filterdue = AttrVal ( $ name , "filterDueTask" , 3 ) ; # show tasks with and without due time
my $ filtercomplete = AttrVal ( $ name , "filterCompleteTask" , 3 ) ; # show completed and not completed tasks
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
my $ params = {
name = > $ name ,
opmode = > "todolist" ,
api = > "TODO" ,
method = > "list" ,
params = > "&cal_id_list=[$oids]&limit=$limit&offset=$offset&filter_due=$filterdue&filter_complete=$filtercomplete" ,
} ;
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
addSendqueue ( $ params ) ;
getApiSites ( $ name ) ;
}
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
return ;
}
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
######################################################################################
# Setter cleanCompleteTasks
# erledigte Aufgaben einer Cal_id (Liste) löschen
######################################################################################
sub _setcleanCompleteTasks {
my $ paref = shift ;
my $ hash = $ paref - > { hash } ;
my $ name = $ paref - > { name } ;
my $ opt = $ paref - > { opt } ;
my $ aref = $ paref - > { aref } ;
if ( ! $ hash - > { HELPER } { CALFETCHED } ) {
return qq{ Obtain the Calendar list first with "get $name getCalendars" command. } ;
}
my $ cals = AttrVal ( $ name , "usedCalendars" , "" ) ;
my @ a = @$ aref ;
shift @ a ; shift @ a ;
my $ c = join ( " " , @ a ) ;
$ cals = $ c ? $ c : $ cals ;
if ( ! $ cals ) {
return qq{ Please set attribute "usedCalendars" to specify the used calendars. } ;
}
# Kalender aufsplitten und zu jedem die ID ermitteln
my @ ca = split ( "," , $ cals ) ;
my $ oids ;
for my $ cal ( @ ca ) {
my $ oid = $ hash - > { HELPER } { CALENDARS } { "$cal" } { id } ;
next if ( ! $ oid ) ;
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
if ( $ hash - > { HELPER } { CALENDARS } { "$cal" } { type } ne "ToDo" ) {
Log3 ( $ name , 3 , qq{ $name - The Calendar "$cal" is not of type "ToDo" and will be ignored. } ) ;
next ;
}
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
$ oids . = "," if ( $ oids ) ;
$ oids . = '"' . $ oid . '"' ;
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
if ( ! $ oid ) {
Log3 ( $ name , 2 , qq{ $name - WARNING - The Calendar "$cal" seems to be unknown because its ID couldn't be found. } ) ;
2020-01-19 14:21:41 +00:00
}
}
2020-12-16 14:40:38 +00:00
if ( ! $ oids ) {
return qq{ No Calendar of type "ToDo" was selected or its ID(s) couldn't be found. } ;
}
Log3 ( $ name , 5 , "$name - Calendar selection for add queue: $cals" ) ;
# <Name, operation mode, API (siehe $data{SSCal}{$name}{calapi}), auszuführende API-Methode, spezifische API-Parameter>
my $ params = {
name = > $ name ,
opmode = > "cleanCompleteTasks" ,
api = > "TODO" ,
method = > "clean_complete" ,
params = > "&cal_id_list=[$oids]" ,
} ;
addSendqueue ( $ params ) ;
getApiSites ( $ name ) ;
2020-01-19 14:21:41 +00:00
return ;
}
2020-12-16 14:40:38 +00:00
######################################################################################
# Setter deleteEventId
######################################################################################
sub _setdeleteEventId {
my $ paref = shift ;
my $ hash = $ paref - > { hash } ;
my $ name = $ paref - > { name } ;
my $ prop = $ paref - > { prop } // return "You must specify an event id (Reading EventId) what is to be deleted." ;
my $ eventid = $ prop ;
my $ bnr ; # Blocknummer ermitteln
my @ allrds = keys % { $ defs { $ name } { READINGS } } ;
for my $ key ( @ allrds ) {
next if $ key !~ /_EventId$/x ;
if ( $ defs { $ name } { READINGS } { $ key } { VAL } == $ eventid ) { # Blocknummer ermittelt
$ bnr = ( split ( "_" , $ key ) ) [ 0 ] ;
}
}
if ( ! defined $ bnr ) {
return "The blocknumber of specified event id could not be identified. Make sure you have specified a valid event id." ;
}
my $ sum = ReadingsVal ( $ name , $ bnr . "_01_Summary" , "" ) ; # die Summary zur Event Id ermitteln
my $ calname = ReadingsVal ( $ name , $ bnr . "_90_calName" , "" ) ; # Kalendername und dessen id und Typ ermitteln
my $ calid = $ hash - > { HELPER } { CALENDARS } { "$calname" } { id } ;
my $ caltype = $ hash - > { HELPER } { CALENDARS } { "$calname" } { type } ;
my $ api = ( $ caltype eq "Event" ) ? "EVENT" : "TODO" ; # Kalender-API in Abhängigkeit des Kalendertyps wählen
Log3 ( $ name , 3 , qq{ $name - The event "$sum" with id "$eventid" will be deleted in calendar "$calname". } ) ;
# <Name, operation mode, API (siehe $data{SSCal}{$name}{calapi}), auszuführende API-Methode, spezifische API-Parameter>
my $ params = {
name = > $ name ,
opmode = > "deleteEventId" ,
api = > $ api ,
method = > "delete" ,
params = > "delete" , "&evt_id=$eventid" ,
} ;
addSendqueue ( $ params ) ;
getApiSites ( $ name ) ;
return ;
}
######################################################################################
# Setter restartSendqueue
######################################################################################
sub _setrestartSendqueue {
my $ paref = shift ;
my $ name = $ paref - > { name } ;
my $ ret = getApiSites ( $ name ) ;
if ( $ ret ) {
return $ ret ;
}
else {
return "The SendQueue has been restarted." ;
}
return ;
}
######################################################################################
# Setter eraseReadings
######################################################################################
sub _seteraseReadings {
my $ paref = shift ;
my $ name = $ paref - > { name } ;
delReadings ( $ name , 0 ) ; # Readings löschen
return ;
}
######################################################################################
# Setter logout
######################################################################################
sub _setlogout {
my $ paref = shift ;
my $ hash = $ paref - > { hash } ;
my $ name = $ paref - > { name } ;
logout ( $ hash , $ data { SSCal } { $ name } { calapi } , $ splitstr ) ;
2021-07-11 18:33:25 +00:00
delete $ data { SSCal } { $ name } { calapi } { PARSET } ; # erzwinge Abruf API beim nächsten Login
2020-12-16 14:40:38 +00:00
return ;
}
######################################################################################
# Getter
######################################################################################
sub Get {
2020-01-19 14:21:41 +00:00
my ( $ hash , @ a ) = @ _ ;
return "\"get X\" needs at least an argument" if ( @ a < 2 ) ;
my $ name = shift @ a ;
my $ opt = shift @ a ;
2020-04-25 17:52:39 +00:00
my $ arg = shift @ a ;
2020-12-16 14:40:38 +00:00
2020-04-25 17:52:39 +00:00
my $ getlist ;
2020-01-19 14:21:41 +00:00
if ( ! $ hash - > { CREDENTIALS } ) {
2020-12-16 14:40:38 +00:00
return ;
}
else {
2020-04-25 17:52:39 +00:00
$ getlist = "Unknown argument $opt, choose one of " .
2020-02-01 13:38:57 +00:00
"apiInfo:noArg " .
2020-02-02 18:47:04 +00:00
"calAsHtml:noArg " .
2020-01-19 14:21:41 +00:00
"getCalendars:noArg " .
2020-04-25 17:52:39 +00:00
"storedCredentials:noArg " .
2020-01-19 14:21:41 +00:00
"versionNotes "
;
2020-04-25 17:52:39 +00:00
}
2020-12-16 14:40:38 +00:00
return if ( IsDisabled ( $ name ) ) ;
my $ params = {
hash = > $ hash ,
name = > $ name ,
opt = > $ opt ,
arg = > $ arg ,
} ;
2020-02-02 18:47:04 +00:00
2020-12-16 14:40:38 +00:00
if ( $ hget { $ opt } && defined & { $ hget { $ opt } { fn } } ) {
my $ ret = q{ } ;
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
if ( ! $ hash - > { CREDENTIALS } && $ hget { $ opt } { needcred } ) {
return qq{ Credentials of $name are not set. Make sure they are set with "set $name credentials <username> <password>" } ;
2020-04-25 17:52:39 +00:00
}
2020-12-16 14:40:38 +00:00
$ ret = & { $ hget { $ opt } { fn } } ( $ params ) ;
2020-01-19 14:21:41 +00:00
2020-04-25 17:52:39 +00:00
return $ ret ;
}
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
return $ getlist ;
}
######################################################################################
# Getter storedCredentials
######################################################################################
sub _getstoredCredentials {
my $ paref = shift ;
my $ hash = $ paref - > { hash } ;
my $ out = showStoredCredentials ( $ hash , 1 , $ splitstr ) ;
return $ out ;
}
######################################################################################
# Getter apiInfo
# Informationen der verwendeten API abrufen und anzeigen
######################################################################################
sub _getapiInfo {
my $ paref = shift ;
my $ hash = $ paref - > { hash } ;
my $ name = $ paref - > { name } ;
delClHash ( $ name ) ;
getClHash ( $ hash , 1 ) ; # übergebenen CL-Hash (FHEMWEB) in Helper eintragen
# Schema: <Name, operation mode, API (siehe $data{SSCal}{$name}{calapi}), auszuführende API-Methode, spezifische API-Parameter>
my $ params = {
name = > $ name ,
opmode = > "apiInfo" ,
api = > "" ,
method = > "" ,
params = > "" ,
} ;
addSendqueue ( $ params ) ;
getApiSites ( $ name ) ;
return ;
}
######################################################################################
# Getter getCalendars
# Liste aller Kalender abrufen und anzeigen
######################################################################################
sub _getgetCalendars {
my $ paref = shift ;
my $ hash = $ paref - > { hash } ;
my $ name = $ paref - > { name } ;
delClHash ( $ name ) ;
getClHash ( $ hash , 1 ) ; # übergebenen CL-Hash (FHEMWEB) in Helper eintragen
# Schema: <Name, operation mode, API (siehe $data{SSCal}{$name}{calapi}), auszuführende API-Methode, spezifische API-Parameter>
my $ params = {
name = > $ name ,
opmode = > "listcal" ,
api = > "CAL" ,
method = > "list" ,
params = > "&is_todo=true&is_evt=true" ,
} ;
addSendqueue ( $ params ) ;
getApiSites ( $ name ) ;
return ;
}
######################################################################################
# Getter calAsHtml
######################################################################################
sub _getcalAsHtml {
my $ paref = shift ;
my $ name = $ paref - > { name } ;
my $ out = calAsHtml ( $ name ) ;
return $ out ;
}
######################################################################################
# Getter versionNotes
######################################################################################
sub _getversionNotes {
my $ paref = shift ;
$ paref - > { hintextde } = \ % vHintsExt_de ;
$ paref - > { hintexten } = \ % vHintsExt_en ;
$ paref - > { notesext } = \ % vNotesExtern ;
my $ ret = showModuleInfo ( $ paref ) ;
return $ ret ;
2020-01-19 14:21:41 +00:00
}
2020-02-02 18:47:04 +00:00
######################################################################################
# Kalenderübersicht in Detailanzeige darstellen
######################################################################################
2020-05-16 15:35:34 +00:00
sub FWdetailFn {
2020-02-02 18:47:04 +00:00
my ( $ FW_wname , $ d , $ room , $ pageHash ) = @ _ ;
my $ hash = $ defs { $ d } ;
my $ ret = "" ;
2020-05-16 15:35:34 +00:00
$ hash - > { ".calhtml" } = FHEM::SSCal:: calAsHtml ( $ d , $ FW_wname ) ;
2020-02-02 18:47:04 +00:00
2020-02-08 09:18:56 +00:00
if ( $ hash - > { ".calhtml" } ne "" && ! $ room && AttrVal ( $ d , "tableInDetail" , 1 ) ) { # Anzeige Übersicht in Detailansicht
2020-02-02 18:47:04 +00:00
$ ret . = $ hash - > { ".calhtml" } ;
return $ ret ;
}
2020-02-08 09:18:56 +00:00
if ( $ hash - > { ".calhtml" } ne "" && $ room && AttrVal ( $ d , "tableInRoom" , 1 ) ) { # Anzeige in Raumansicht zusätzlich zur Statuszeile
2020-02-02 18:47:04 +00:00
$ ret = $ hash - > { ".calhtml" } ;
return $ ret ;
}
2020-04-25 17:52:39 +00:00
return ;
2020-02-02 18:47:04 +00:00
}
2020-01-19 14:21:41 +00:00
######################################################################################
# initiale Startroutinen nach Restart FHEM
######################################################################################
2020-05-16 15:35:34 +00:00
sub initOnBoot {
2020-01-19 14:21:41 +00:00
my ( $ name ) = @ _ ;
my $ hash = $ defs { $ name } ;
my ( $ ret ) ;
2020-05-16 15:35:34 +00:00
RemoveInternalTimer ( $ name , "FHEM::SSCal::initOnBoot" ) ;
2020-01-19 14:21:41 +00:00
if ( $ init_done ) {
2020-04-25 17:52:39 +00:00
CommandGet ( undef , "$name getCalendars" ) ; # Kalender Liste initial abrufen
2020-12-16 14:40:38 +00:00
}
else {
2020-05-16 15:35:34 +00:00
InternalTimer ( gettimeofday ( ) + 3 , "FHEM::SSCal::initOnBoot" , $ name , 0 ) ;
2020-01-19 14:21:41 +00:00
}
return ;
}
#############################################################################################
# regelmäßiger Intervallabruf
#############################################################################################
2020-05-16 15:35:34 +00:00
sub periodicCall {
2020-12-16 14:40:38 +00:00
my $ name = shift ;
2020-01-19 14:21:41 +00:00
my $ hash = $ defs { $ name } ;
2020-12-16 14:40:38 +00:00
RemoveInternalTimer ( $ name , "FHEM::SSCal::periodicCall" ) ;
2020-01-19 14:21:41 +00:00
my $ interval = AttrVal ( $ name , "interval" , 0 ) ;
2020-12-16 14:40:38 +00:00
2020-01-19 14:21:41 +00:00
my $ new ;
if ( ! $ interval ) {
$ hash - > { MODE } = "Manual" ;
2020-12-16 14:40:38 +00:00
readingsSingleUpdate ( $ hash , "nextUpdate" , "manual" , 1 ) ;
return ;
}
else {
2020-02-23 18:28:00 +00:00
$ new = gettimeofday ( ) + $ interval ;
2020-01-19 14:21:41 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate ( $ hash , "nextUpdate" , "Automatic - next polltime: " . FmtTime ( $ new ) ) ; # Abrufmode initial auf "Manual" setzen
readingsEndUpdate ( $ hash , 1 ) ;
2020-12-16 14:40:38 +00:00
$ hash - > { MODE } = "Automatic" ;
2020-01-19 14:21:41 +00:00
}
if ( $ hash - > { CREDENTIALS } && ! IsDisabled ( $ name ) ) {
2020-02-23 08:34:54 +00:00
CommandSet ( undef , "$name calUpdate" ) ; # Einträge aller gewählter Kalender oder Aufgabenlisten abrufen (in Queue stellen)
2020-01-19 14:21:41 +00:00
}
2020-05-16 15:35:34 +00:00
InternalTimer ( $ new , "FHEM::SSCal::periodicCall" , $ name , 0 ) ;
2020-01-19 14:21:41 +00:00
return ;
}
2020-12-16 14:40:38 +00:00
####################################################################################
# Einstiegsfunktion Queue Abarbeitung
####################################################################################
2020-05-16 15:35:34 +00:00
sub getApiSites {
2020-12-16 14:40:38 +00:00
my $ name = shift ;
my $ hash = $ defs { $ name } ;
my $ addr = $ hash - > { SERVERADDR } ;
my $ port = $ hash - > { SERVERPORT } ;
my $ prot = $ hash - > { PROTOCOL } ;
my ( $ url , $ idxset , $ ret ) ;
2020-01-19 14:21:41 +00:00
$ hash - > { HELPER } { LOGINRETRIES } = 0 ;
2020-05-16 15:35:34 +00:00
my ( $ err , $ tstart , $ tend ) = timeEdge ( $ name ) ;
2020-01-19 14:21:41 +00:00
$ tstart = FmtDateTime ( $ tstart ) ;
2020-12-16 14:40:38 +00:00
$ tend = FmtDateTime ( $ tend ) ;
2020-01-19 14:21:41 +00:00
if ( ! keys % { $ data { SSCal } { $ name } { sendqueue } { entries } } ) {
$ ret = "Sendqueue is empty. Nothing to do ..." ;
Log3 ( $ name , 4 , "$name - $ret" ) ;
return $ ret ;
}
# den nächsten Eintrag aus "SendQueue" selektieren und ausführen wenn nicht forbidSend gesetzt ist
2020-04-25 19:53:14 +00:00
for my $ idx ( sort { $ a <=> $ b } keys % { $ data { SSCal } { $ name } { sendqueue } { entries } } ) {
2020-01-19 14:21:41 +00:00
if ( ! $ data { SSCal } { $ name } { sendqueue } { entries } { $ idx } { forbidSend } ) {
$ hash - > { OPIDX } = $ idx ;
$ hash - > { OPMODE } = $ data { SSCal } { $ name } { sendqueue } { entries } { $ idx } { opmode } ;
$ idxset = 1 ;
last ;
}
}
2020-12-16 14:40:38 +00:00
Log3 ( $ name , 4 , "$name - ####################################################" ) ;
Log3 ( $ name , 4 , "$name - ### start Calendar operation $hash->{OPMODE} " ) ;
Log3 ( $ name , 4 , "$name - ####################################################" ) ;
2020-01-19 14:21:41 +00:00
if ( ! $ idxset ) {
2020-12-16 14:40:38 +00:00
$ ret = qq{ Only entries with "forbidSend" are in Sendqueue. Escaping ... } ;
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 4 , "$name - $ret" ) ;
return $ ret ;
}
2020-12-16 14:40:38 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate ( $ hash , "state" , "running" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 4 , "$name - Time selection start: " . $ tstart ) ;
Log3 ( $ name , 4 , "$name - Time selection end: " . $ tend ) ;
2020-12-16 14:40:38 +00:00
if ( $ hash - > { OPMODE } eq "apiInfo" ) {
$ data { SSCal } { $ name } { calapi } { PARSET } = 0 ; # erzwinge Abruf API
}
if ( $ data { SSCal } { $ name } { calapi } { PARSET } ) { # API-Hashwerte sind bereits gesetzt -> Abruf überspringen
2020-04-25 17:52:39 +00:00
Log3 ( $ name , 4 , "$name - API hash values already set - ignore get apisites" ) ;
2020-05-16 15:35:34 +00:00
return checkSID ( $ name ) ;
2020-01-19 14:21:41 +00:00
}
my $ timeout = AttrVal ( $ name , "timeout" , 20 ) ;
Log3 ( $ name , 5 , "$name - HTTP-Call will be done with timeout: $timeout s" ) ;
2020-12-16 14:40:38 +00:00
# API initialisieren und abrufen
####################################
$ data { SSCal } { $ name } { calapi } = apistatic ( "calendar" ) ; # API Template im HELPER instanziieren
Log3 ( $ name , 4 , "$name - API imported:\n" . Dumper $ data { SSCal } { $ name } { calapi } ) ;
my @ ak ;
for my $ key ( keys % { $ data { SSCal } { $ name } { calapi } } ) {
next if ( $ key =~ /^PARSET$/x ) ;
push @ ak , $ data { SSCal } { $ name } { calapi } { $ key } { NAME } ;
}
my $ apis = join "," , @ ak ;
2020-01-19 14:21:41 +00:00
2020-05-17 10:59:56 +00:00
my $ calapi = $ data { SSCal } { $ name } { calapi } ;
2020-12-16 14:40:38 +00:00
$ url = "$prot://$addr:$port/webapi/$data{SSCal}{$name}{calapi}{INFO}{PATH}?" .
"api=$data{SSCal}{$name}{calapi}{INFO}{NAME}" .
"&method=Query" .
"&version=$data{SSCal}{$name}{calapi}{INFO}{VER}" .
"&query=$apis" ;
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 4 , "$name - Call-Out: $url" ) ;
2020-12-16 14:40:38 +00:00
my $ param = {
url = > $ url ,
timeout = > $ timeout ,
hash = > $ hash ,
method = > "GET" ,
header = > "Accept: application/json" ,
callback = > \ & FHEM::SSCal:: getApiSites_parse
} ;
2020-01-19 14:21:41 +00:00
HttpUtils_NonblockingGet ( $ param ) ;
return ;
}
####################################################################################
# Auswertung Abruf apisites
####################################################################################
2020-12-16 14:40:38 +00:00
sub getApiSites_parse { ## no critic 'complexity'
2020-01-19 14:21:41 +00:00
my ( $ param , $ err , $ myjson ) = @ _ ;
2020-02-01 13:38:57 +00:00
my $ hash = $ param - > { hash } ;
my $ name = $ hash - > { NAME } ;
my $ opmode = $ hash - > { OPMODE } ;
2020-01-19 14:21:41 +00:00
my ( $ error , $ errorcode , $ success ) ;
2020-12-16 14:40:38 +00:00
if ( $ err ne "" ) { # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 2 , "$name - ERROR message: $err" ) ;
2020-12-16 14:40:38 +00:00
setReadingErrorState ( $ hash , $ err ) ;
checkSendRetry ( $ name , 1 , $ queueStartFn ) ;
2020-01-19 14:21:41 +00:00
return ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ myjson ne "" ) {
( $ success ) = evaljson ( $ hash , $ myjson ) ;
2020-04-25 17:52:39 +00:00
2020-12-16 14:40:38 +00:00
if ( ! $ success ) {
Log3 ( $ name , 4 , "$name - Data returned: " . $ myjson ) ;
checkSendRetry ( $ name , 1 , $ queueStartFn ) ;
2020-01-19 14:21:41 +00:00
return ;
}
2020-12-16 14:40:38 +00:00
my $ jdata = decode_json ( $ myjson ) ;
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
Log3 ( $ name , 5 , "$name - JSON returned: " . Dumper $ jdata ) ;
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
$ success = $ jdata - > { 'success' } ;
2020-01-19 14:21:41 +00:00
if ( $ success ) {
2020-12-16 14:40:38 +00:00
my $ completed = completeAPI ( $ jdata , $ data { SSCal } { $ name } { calapi } ) ; # übergibt Referenz zum instanziierten API-Hash
if ( ! $ completed ) {
$ errorcode = "9001" ;
$ error = expErrors ( $ hash , $ errorcode ) ; # Fehlertext zum Errorcode ermitteln
setReadingErrorState ( $ hash , $ error , $ errorcode ) ;
Log3 ( $ name , 2 , "$name - ERROR - $error" ) ;
checkSendRetry ( $ name , 1 , $ queueStartFn ) ;
return ;
}
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
# Downgrades für nicht kompatible API-Versionen. Hier nur nutzen wenn API zentral downgraded werden soll
Log3 ( $ name , 4 , "$name - ------- Begin of adaption section -------" ) ;
2020-01-19 14:21:41 +00:00
2021-07-11 18:33:25 +00:00
my $ adavs = "a01" ; # adaptierte Version
2020-04-25 17:52:39 +00:00
2021-07-11 18:33:25 +00:00
if ( $ adavs ) {
for my $ av ( sort keys % { $ hvada { $ adavs } } ) {
$ data { SSCal } { $ name } { calapi } { $ av } { VER } = $ hvada { $ adavs } { $ av } ;
$ data { SSCal } { $ name } { calapi } { $ av } { MOD } = "yes" ;
Log3 ( $ name , 4 , "$name - Version of $data{SSCal}{$name}{calapi}{$av}{NAME} adapted to: $data{SSCal}{$name}{calapi}{$av}{VER}" ) ;
}
2020-12-16 14:40:38 +00:00
}
2020-04-25 17:52:39 +00:00
Log3 ( $ name , 4 , "$name - ------- End of adaption section -------" ) ;
2020-12-16 14:40:38 +00:00
setReadingErrorNone ( $ hash , 1 ) ;
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
Log3 ( $ name , 4 , "$name - API completed:\n" . Dumper $ data { SSCal } { $ name } { calapi } ) ;
if ( $ opmode eq "apiInfo" ) { # API Infos in Popup anzeigen
showAPIinfo ( $ hash , $ data { SSCal } { $ name } { calapi } ) ; # übergibt Referenz zum instanziierten API-Hash)
readingsSingleUpdate ( $ hash , "state" , "done" , 1 ) ;
checkSendRetry ( $ name , 0 , $ queueStartFn ) ;
2020-02-01 13:38:57 +00:00
return ;
2020-12-16 14:40:38 +00:00
}
}
else {
2020-01-19 14:21:41 +00:00
$ errorcode = "806" ;
2020-12-16 14:40:38 +00:00
$ error = expErrors ( $ hash , $ errorcode ) ; # Fehlertext zum Errorcode ermitteln
2020-01-19 14:21:41 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , $ errorcode ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , $ error ) ;
2020-12-16 14:40:38 +00:00
readingsBulkUpdate ( $ hash , "state" , "Error" ) ;
2020-01-19 14:21:41 +00:00
readingsEndUpdate ( $ hash , 1 ) ;
Log3 ( $ name , 2 , "$name - ERROR - the API-Query couldn't be executed successfully" ) ;
2020-12-16 14:40:38 +00:00
checkSendRetry ( $ name , 1 , $ queueStartFn ) ;
2020-01-19 14:21:41 +00:00
return ;
}
2020-04-25 17:52:39 +00:00
}
2020-01-19 14:21:41 +00:00
2020-05-16 15:35:34 +00:00
return checkSID ( $ name ) ;
2020-01-19 14:21:41 +00:00
}
#############################################################################################
# Ausführung Operation
#############################################################################################
2020-05-16 15:35:34 +00:00
sub calOp {
2020-01-19 14:21:41 +00:00
my ( $ name ) = @ _ ;
my $ hash = $ defs { $ name } ;
2020-12-16 14:40:38 +00:00
my $ prot = $ hash - > { PROTOCOL } ;
my $ addr = $ hash - > { SERVERADDR } ;
my $ port = $ hash - > { SERVERPORT } ;
2020-01-19 14:21:41 +00:00
my $ sid = $ hash - > { HELPER } { SID } ;
my ( $ url , $ timeout , $ param , $ error , $ errorcode ) ;
my $ idx = $ hash - > { OPIDX } ;
my $ opmode = $ hash - > { OPMODE } ;
my $ method = $ data { SSCal } { $ name } { sendqueue } { entries } { $ idx } { method } ;
my $ api = $ data { SSCal } { $ name } { sendqueue } { entries } { $ idx } { api } ;
my $ params = $ data { SSCal } { $ name } { sendqueue } { entries } { $ idx } { params } ;
2020-05-17 10:59:56 +00:00
my $ calapi = $ data { SSCal } { $ name } { calapi } ;
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 4 , "$name - start SendQueue entry index \"$idx\" ($hash->{OPMODE}) for operation." ) ;
$ timeout = AttrVal ( $ name , "timeout" , 20 ) ;
Log3 ( $ name , 5 , "$name - HTTP-Call will be done with timeout: $timeout s" ) ;
2020-12-16 14:40:38 +00:00
$ url = "$prot://$addr:$port/webapi/" . $ calapi - > { $ api } { PATH } . "?api=" . $ calapi - > { $ api } { NAME } . "&version=" . $ calapi - > { $ api } { VER } . "&method=$method" . $ params . "&_sid=$sid" ;
2020-02-01 13:38:57 +00:00
2020-12-16 14:40:38 +00:00
if ( $ opmode eq "deleteEventId" && $ api eq "EVENT" ) { # Workaround !!! Methode delete funktioniert nicht mit SYNO.Cal.Event version > 1
2020-05-17 10:59:56 +00:00
$ url = "$prot://$addr:$port/webapi/" . $ calapi - > { $ api } { PATH } . "?api=" . $ calapi - > { $ api } { NAME } . "&version=1&method=$method" . $ params . "&_sid=$sid" ;
2020-02-01 13:38:57 +00:00
}
2020-01-19 14:21:41 +00:00
my $ part = $ url ;
if ( AttrVal ( $ name , "showPassInLog" , "0" ) == 1 ) {
Log3 ( $ name , 4 , "$name - Call-Out: $url" ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-04-25 17:52:39 +00:00
$ part =~ s/$sid/<secret>/x ;
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 4 , "$name - Call-Out: $part" ) ;
}
$ param = {
url = > $ url ,
timeout = > $ timeout ,
hash = > $ hash ,
method = > "GET" ,
header = > "Accept: application/json" ,
2020-05-16 15:35:34 +00:00
callback = > \ & FHEM::SSCal:: calOp_parse
2020-01-19 14:21:41 +00:00
} ;
2020-04-25 17:52:39 +00:00
HttpUtils_NonblockingGet ( $ param ) ;
return ;
2020-01-19 14:21:41 +00:00
}
#############################################################################################
2020-05-16 15:35:34 +00:00
# Callback from calOp
2020-01-19 14:21:41 +00:00
#############################################################################################
2020-12-16 14:40:38 +00:00
sub calOp_parse { ## no critic 'complexity'
2020-01-19 14:21:41 +00:00
my ( $ param , $ err , $ myjson ) = @ _ ;
my $ hash = $ param - > { hash } ;
my $ name = $ hash - > { NAME } ;
2020-12-16 14:40:38 +00:00
my $ prot = $ hash - > { PROTOCOL } ;
my $ addr = $ hash - > { SERVERADDR } ;
my $ port = $ hash - > { SERVERPORT } ;
2020-01-19 14:21:41 +00:00
my $ opmode = $ hash - > { OPMODE } ;
my $ am = AttrVal ( $ name , "asyncMode" , 0 ) ;
my ( $ ts , $ data , $ success , $ error , $ errorcode , $ cherror , $ r ) ;
2020-12-16 14:40:38 +00:00
if ( $ err ne "" ) { # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 2 , "$name - ERROR message: $err" ) ;
$ errorcode = "none" ;
2020-05-17 10:59:56 +00:00
$ errorcode = "800" if ( $ err =~ /:\smalformed\sor\sunsupported\sURL$/xs ) ;
2020-01-19 14:21:41 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , $ err ) ;
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , $ errorcode ) ;
readingsBulkUpdate ( $ hash , "state" , "Error" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
2020-12-16 14:40:38 +00:00
checkSendRetry ( $ name , 1 , $ queueStartFn ) ;
2020-01-19 14:21:41 +00:00
return ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ myjson ne "" ) { # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes)
( $ success ) = evaljson ( $ hash , $ myjson ) ;
if ( ! $ success ) {
Log3 ( $ name , 4 , "$name - Data returned: " . $ myjson ) ;
checkSendRetry ( $ name , 1 , $ queueStartFn ) ;
2020-01-19 14:21:41 +00:00
return ;
}
$ data = decode_json ( $ myjson ) ;
Log3 ( $ name , 5 , "$name - JSON returned: " . Dumper $ data ) ;
$ success = $ data - > { 'success' } ;
if ( $ success ) {
2020-05-16 15:35:34 +00:00
if ( $ opmode eq "listcal" ) { ## no critic 'Cascading' # alle Kalender abrufen
2020-01-19 14:21:41 +00:00
my % calendars = ( ) ;
2020-04-25 17:52:39 +00:00
my ( $ cals , $ dnm , $ typ , $ oid , $ des , $ prv , $ psi ) ;
my $ i = 0 ;
2020-01-19 14:21:41 +00:00
my $ out = "<html>" ;
$ out . = "<b>Synology Calendar List</b> <br><br>" ;
$ out . = "<table class=\"roomoverview\" style=\"text-align:left; border:1px solid; padding:5px; border-spacing:5px; margin-left:auto; margin-right:auto;\">" ;
$ out . = "<tr><td> <b>Calendar</b> </td><td> <b>ID</b> </td><td> <b>Type</b> </td><td> <b>Description</b> </td><td> <b>Privilege</b> </td><td> <b>Public share ID</b> </td><td></tr>" ;
$ out . = "<tr><td> </td><td> </td><td> </td><td> </td><td> </td><td></tr>" ;
while ( $ data - > { data } [ $ i ] ) {
2020-12-16 14:40:38 +00:00
$ dnm = encode ( "UTF-8" , $ data - > { data } [ $ i ] { cal_displayname } ) ;
2020-04-25 17:52:39 +00:00
next if ( ! $ dnm ) ;
$ typ = "Event" if ( $ data - > { data } [ $ i ] { is_evt } ) ;
$ typ = "ToDo" if ( $ data - > { data } [ $ i ] { is_todo } ) ;
$ oid = $ data - > { data } [ $ i ] { original_cal_id } ;
$ des = encode ( "UTF-8" , $ data - > { data } [ $ i ] { cal_description } ) ;
$ prv = $ data - > { data } [ $ i ] { cal_privilege } ;
2020-01-19 14:21:41 +00:00
$ psi = $ data - > { data } [ $ i ] { cal_public_sharing_id } ;
2020-04-25 17:52:39 +00:00
$ psi = $ psi ? $ psi: "" ;
$ calendars { $ dnm } { id } = $ oid ;
$ calendars { $ dnm } { description } = $ des ;
$ calendars { $ dnm } { privilege } = $ prv ;
$ calendars { $ dnm } { publicshareid } = $ psi ;
$ calendars { $ dnm } { type } = $ typ ;
$ cals . = "," if ( $ cals ) ;
$ cals . = $ dnm ;
$ out . = "<tr><td> $dnm </td><td> $oid </td><td> $typ</td><td> $des </td><td> $prv </td><td> $psi </td><td></tr>" ;
$ i + + ;
2020-01-19 14:21:41 +00:00
}
2020-04-25 17:52:39 +00:00
2020-01-19 14:21:41 +00:00
$ out . = "</table>" ;
$ out . = "</html>" ;
2020-04-25 17:52:39 +00:00
2020-01-19 14:21:41 +00:00
$ hash - > { HELPER } { CALENDARS } = \ % calendars if ( % calendars ) ;
$ hash - > { HELPER } { CALFETCHED } = 1 ;
my @ newa ;
my $ list = $ modules { $ hash - > { TYPE } } { AttrList } ;
my @ deva = split ( " " , $ list ) ;
2020-04-25 19:53:14 +00:00
for ( @ deva ) {
2020-05-16 15:35:34 +00:00
push @ newa , $ _ if ( $ _ !~ /usedCalendars:/x ) ;
2020-01-19 14:21:41 +00:00
}
2020-05-16 15:35:34 +00:00
$ cals =~ s/\s/#/gx if ( $ cals ) ;
2020-04-25 17:52:39 +00:00
2020-01-19 14:21:41 +00:00
push @ newa , ( $ cals ? "usedCalendars:multiple-strict,$cals " : "usedCalendars:--no#Calendar#selectable--" ) ;
$ hash - > { ".AttrList" } = join ( " " , @ newa ) ; # Device spezifische AttrList, überschreibt Modul AttrList !
2020-04-25 17:52:39 +00:00
# Ausgabe Popup der User-Daten (nach readingsEndUpdate positionieren sonst
# "Connection lost, trying reconnect every 5 seconds" wenn > 102400 Zeichen)
asyncOutput ( $ hash - > { HELPER } { CL } { 1 } , "$out" ) ;
delete ( $ hash - > { HELPER } { CL } ) ;
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
checkSendRetry ( $ name , 0 , $ queueStartFn ) ;
2020-01-19 14:21:41 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , "none" ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , "none" ) ;
readingsBulkUpdate ( $ hash , "state" , "done" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ opmode eq "eventlist" ) { # Events der ausgewählten Kalender aufbereiten
2020-02-01 13:38:57 +00:00
delete $ data { SSCal } { $ name } { eventlist } ; # zentrales Event/ToDo Hash löschen
2020-04-25 17:52:39 +00:00
delete $ data { SSCal } { $ name } { vcalendar } ; # zentrales VCALENDAR Hash löschen
2020-01-19 14:21:41 +00:00
$ hash - > { eventlist } = $ data ; # Data-Hashreferenz im Hash speichern
if ( $ am ) { # Extrahieren der Events asynchron (nicht-blockierend)
Log3 ( $ name , 4 , "$name - Event parse mode: asynchronous" ) ;
my $ timeout = AttrVal ( $ name , "timeout" , 20 ) + 180 ;
2020-05-16 15:35:34 +00:00
$ hash - > { HELPER } { RUNNING_PID } = BlockingCall ( "FHEM::SSCal::extractEventlist" , $ name , "FHEM::SSCal::createReadings" , $ timeout , "FHEM::SSCal::blockingTimeout" , $ hash ) ;
2020-12-16 14:40:38 +00:00
$ hash - > { HELPER } { RUNNING_PID } { loglevel } = 5 if ( $ hash - > { HELPER } { RUNNING_PID } ) ; # Forum #77057
}
else { # Extrahieren der Events synchron (blockierend)
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 4 , "$name - Event parse mode: synchronous" ) ;
2020-05-16 15:35:34 +00:00
extractEventlist ( $ name ) ;
2020-12-16 14:40:38 +00:00
}
}
elsif ( $ opmode eq "todolist" ) { # ToDo's der ausgewählten Tasks-Kalender aufbereiten
delete $ data { SSCal } { $ name } { eventlist } ; # zentrales Event/ToDo Hash löschen
$ hash - > { eventlist } = $ data ; # Data-Hashreferenz im Hash speichern
2020-01-31 15:27:30 +00:00
2020-12-16 14:40:38 +00:00
if ( $ am ) { # Extrahieren der ToDos asynchron (nicht-blockierend)
2020-01-31 15:27:30 +00:00
Log3 ( $ name , 4 , "$name - Task parse mode: asynchronous" ) ;
my $ timeout = AttrVal ( $ name , "timeout" , 20 ) + 180 ;
2020-05-16 15:35:34 +00:00
$ hash - > { HELPER } { RUNNING_PID } = BlockingCall ( "FHEM::SSCal::extractToDolist" , $ name , "FHEM::SSCal::createReadings" , $ timeout , "FHEM::SSCal::blockingTimeout" , $ hash ) ;
2020-01-31 15:27:30 +00:00
$ hash - > { HELPER } { RUNNING_PID } { loglevel } = 5 if ( $ hash - > { HELPER } { RUNNING_PID } ) ; # Forum #77057
2020-12-16 14:40:38 +00:00
}
else { # Extrahieren der ToDos synchron (blockierend)
2020-01-31 15:27:30 +00:00
Log3 ( $ name , 4 , "$name - Task parse mode: synchronous" ) ;
2020-05-16 15:35:34 +00:00
extractToDolist ( $ name ) ;
2020-12-16 14:40:38 +00:00
}
}
elsif ( $ opmode eq "cleanCompleteTasks" ) { # abgeschlossene ToDos wurden gelöscht
2020-02-01 13:38:57 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , "none" ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , "none" ) ;
readingsBulkUpdate ( $ hash , "state" , "done" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
Log3 ( $ name , 3 , "$name - All completed tasks were deleted from selected ToDo lists" ) ;
2020-12-16 14:40:38 +00:00
checkSendRetry ( $ name , 0 , $ queueStartFn ) ;
}
elsif ( $ opmode eq "deleteEventId" ) { # ein Kalendereintrag mit Event Id wurde gelöscht
2020-02-01 13:38:57 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , "none" ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , "none" ) ;
readingsBulkUpdate ( $ hash , "state" , "done" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
Log3 ( $ name , 3 , "$name - The specified event id was deleted" ) ;
# Queuedefinition sichern vor checkretry
my $ idx = $ hash - > { OPIDX } ;
my $ api = $ data { SSCal } { $ name } { sendqueue } { entries } { $ idx } { api } ;
2020-12-16 14:40:38 +00:00
checkSendRetry ( $ name , 0 , $ queueStartFn ) ;
2020-02-01 13:38:57 +00:00
# Kalendereinträge neu einlesen nach dem löschen Event Id
2020-12-16 14:40:38 +00:00
CommandSet ( undef , "$name calUpdate" ) ;
}
}
else {
2020-01-19 14:21:41 +00:00
# die API-Operation war fehlerhaft
# Errorcode aus JSON ermitteln
$ errorcode = $ data - > { error } - > { code } ;
$ cherror = $ data - > { error } - > { errors } ; # vom cal gelieferter Fehler
2020-12-16 14:40:38 +00:00
$ error = expErrors ( $ hash , $ errorcode ) ; # Fehlertext zum Errorcode ermitteln
2020-01-19 14:21:41 +00:00
if ( $ error =~ /not found/ ) {
2020-05-16 15:35:34 +00:00
$ error . = " New error: " . ( $ cherror // "' '" ) ;
2020-01-19 14:21:41 +00:00
}
2020-04-25 17:52:39 +00:00
2020-01-19 14:21:41 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , $ errorcode ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , $ error ) ;
readingsBulkUpdate ( $ hash , "state" , "Error" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
Log3 ( $ name , 2 , "$name - ERROR - Operation $opmode was not successful. Errorcode: $errorcode - $error" ) ;
2020-12-16 14:40:38 +00:00
checkSendRetry ( $ name , 1 , $ queueStartFn ) ;
2020-01-19 14:21:41 +00:00
}
undef $ data ;
undef $ myjson ;
}
return ;
}
#############################################################################################
2020-01-31 15:27:30 +00:00
# Extrahiert empfangene Kalendertermine (Events)
2020-01-19 14:21:41 +00:00
#############################################################################################
2020-05-17 10:59:56 +00:00
sub extractEventlist { ## no critic 'complexity'
2022-07-15 18:56:52 +00:00
my $ name = shift ;
my $ hash = $ defs { $ name } ;
2022-07-16 06:21:46 +00:00
my $ data = delete $ hash - > { eventlist } ; # zentrales Eventhash löschen !
2022-07-15 18:56:52 +00:00
my $ am = AttrVal ( $ name , "asyncMode" , 0 ) ;
2020-01-19 14:21:41 +00:00
2020-03-02 23:36:47 +00:00
my ( $ tz , $ bdate , $ btime , $ bts , $ edate , $ etime , $ ets , $ ci , $ bi , $ ei , $ startEndDiff , $ excl , $ es , $ em , $ eh ) ;
2020-05-16 15:35:34 +00:00
my ( $ nbdate , $ nbtime , $ nbts , $ nedate , $ netime , $ nets ) ;
2020-01-19 14:21:41 +00:00
my @ row_array ;
2022-07-16 06:21:46 +00:00
my ( undef , $ tstart , $ tend ) = timeEdge ( $ name ) ; # Sollstart- und Sollendezeit der Kalenderereignisse ermitteln
my $ datetimestart = FmtDateTime ( $ tstart ) ;
my $ datetimeend = FmtDateTime ( $ tend ) ;
2020-01-19 14:21:41 +00:00
2020-02-19 20:24:56 +00:00
my $ n = 0 ; # Zusatz f. lfd. Nr. zur Unterscheidung exakt zeitgleicher Events
2020-04-25 19:53:14 +00:00
for my $ key ( keys % { $ data - > { data } } ) {
2020-01-19 14:21:41 +00:00
my $ i = 0 ;
while ( $ data - > { data } { $ key } [ $ i ] ) {
my $ ignore = 0 ;
my $ done = 0 ;
2020-05-20 17:25:50 +00:00
my $ next = 0 ;
2020-04-25 17:52:39 +00:00
( $ nbdate , $ nedate ) = ( "" , "" ) ;
2020-02-19 20:24:56 +00:00
2020-05-17 10:59:56 +00:00
my $ uid = $ data - > { data } { $ key } [ $ i ] { ical_uid } ; # UID des Events
2022-07-16 06:21:46 +00:00
2020-05-16 15:35:34 +00:00
extractIcal ( $ name , $ data - > { data } { $ key } [ $ i ] ) ; # VCALENDAR Extrakt in {HELPER}{VCALENDAR} importieren
2020-01-19 14:21:41 +00:00
2020-02-19 20:24:56 +00:00
my $ isallday = $ data - > { data } { $ key } [ $ i ] { is_all_day } ;
2020-05-16 15:35:34 +00:00
( $ bi , $ tz , $ bdate , $ btime , $ bts , $ excl ) = explodeDateTime ( $ hash , $ data - > { data } { $ key } [ $ i ] { dtstart } , 0 , 0 , 0 ) ; # Beginn des Events
( $ ei , undef , $ edate , $ etime , $ ets , undef ) = explodeDateTime ( $ hash , $ data - > { data } { $ key } [ $ i ] { dtend } , $ isallday , 0 , 0 ) ; # Ende des Events
2020-02-09 10:21:37 +00:00
2020-05-16 15:35:34 +00:00
my ( $ byear , $ bmonth , $ bmday ) = $ bdate =~ /(\d{4})-(\d{2})-(\d{2})/x ;
$ nbtime = $ btime ;
2020-01-19 14:21:41 +00:00
2020-05-16 15:35:34 +00:00
my ( $ eyear , $ emonth , $ emday ) = $ edate =~ /(\d{4})-(\d{2})-(\d{2})/x ;
$ netime = $ etime ;
2020-02-09 10:21:37 +00:00
# Bugfix API - wenn is_all_day und an erster Stelle im 'data' Ergebnis des API-Calls ist Endedate/time nicht korrekt !
2020-04-25 17:52:39 +00:00
if ( $ isallday && ( $ bdate ne $ edate ) && $ netime =~ /^00:59:59$/x ) {
2020-03-02 23:36:47 +00:00
( $ es , $ em , $ eh , $ emday , $ emonth , $ eyear , undef , undef , undef ) = localtime ( $ ets -= 3600 ) ;
$ eyear = sprintf ( "%02d" , $ eyear += 1900 ) ;
$ emonth = sprintf ( "%02d" , $ emonth += 1 ) ;
$ emday = sprintf ( "%02d" , $ emday ) ;
$ netime = $ eh . $ em . $ es ;
2020-04-25 17:52:39 +00:00
$ nbtime =~ s/://gx ;
2020-02-09 10:21:37 +00:00
2020-05-16 15:35:34 +00:00
( $ bi , undef , $ bdate , $ btime , $ bts , $ excl ) = explodeDateTime ( $ hash , $ byear . $ bmonth . $ bmday . "T" . $ nbtime , 0 , 0 , 0 ) ;
( $ ei , undef , $ edate , $ etime , $ ets , undef ) = explodeDateTime ( $ hash , $ eyear . $ emonth . $ emday . "T" . $ netime , 0 , 0 , 0 ) ;
2020-02-09 10:21:37 +00:00
}
$ startEndDiff = $ ets - $ bts ; # Differenz Event Ende / Start in Sekunden
2020-04-25 17:52:39 +00:00
2020-01-19 14:21:41 +00:00
if ( ! $ data - > { data } { $ key } [ $ i ] { is_repeat_evt } ) { # einmaliger Event
Log3 ( $ name , 5 , "$name - Single event Begin: $bdate, End: $edate" ) ;
if ( $ ets < $ tstart || $ bts > $ tend ) {
Log3 ( $ name , 4 , "$name - Ignore single event -> $data->{data}{$key}[$i]{summary} start: $bdate $btime, end: $edate $etime" ) ;
2022-07-16 06:21:46 +00:00
2020-01-19 14:21:41 +00:00
$ ignore = 1 ;
$ done = 0 ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ excl ) {
2020-02-19 20:24:56 +00:00
Log3 ( $ name , 4 , "$name - Ignored by Ical compare -> $data->{data}{$key}[$i]{summary} start: $bdate $btime, end: $edate $etime" ) ;
2022-07-16 06:21:46 +00:00
2020-02-19 20:24:56 +00:00
$ ignore = 1 ;
$ done = 0 ;
2020-12-16 14:40:38 +00:00
}
else {
2020-05-20 17:25:50 +00:00
writeValuesToArray ( { name = > $ name ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
begintime = > $ btime ,
begints = > $ bts ,
enddate = > $ edate ,
endtime = > $ etime ,
endts = > $ ets ,
sumarrayref = > \ @ row_array ,
uid = > $ uid
} ) ;
2020-01-19 14:21:41 +00:00
$ ignore = 0 ;
$ done = 1 ;
2020-12-16 14:40:38 +00:00
}
}
elsif ( $ data - > { data } { $ key } [ $ i ] { is_repeat_evt } ) { # Event ist wiederholend
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 5 , "$name - Recurring event Begin: $bdate, End: $edate" ) ;
my ( $ freq , $ count , $ interval , $ until , $ uets , $ bymonthday , $ byday ) ;
my $ rr = $ data - > { data } { $ key } [ $ i ] { evt_repeat_setting } { repeat_rule } ;
# Format: FREQ=YEARLY;COUNT=1;INTERVAL=2;BYMONTHDAY=15;BYMONTH=10;UNTIL=2020-12-31T00:00:00
2022-07-16 06:21:46 +00:00
my @ para = split ";" , $ rr ;
2020-01-19 14:21:41 +00:00
2020-04-25 19:53:14 +00:00
for my $ par ( @ para ) {
2022-07-16 06:21:46 +00:00
my ( $ p1 , $ p2 ) = split "=" , $ par ;
2020-01-19 14:21:41 +00:00
if ( $ p1 eq "FREQ" ) {
$ freq = $ p2 ;
2020-12-16 14:40:38 +00:00
}
if ( $ p1 eq "COUNT" ) { # Event endet automatisch nach x Wiederholungen
2020-02-18 08:48:21 +00:00
$ count = $ p2 ;
2020-12-16 14:40:38 +00:00
}
if ( $ p1 eq "INTERVAL" ) { # Wiederholungsintervall
2020-01-19 14:21:41 +00:00
$ interval = $ p2 ;
2020-12-16 14:40:38 +00:00
}
if ( $ p1 eq "UNTIL" ) { # festes Intervallende angegeben
2020-01-19 14:21:41 +00:00
$ until = $ p2 ;
2020-04-25 17:52:39 +00:00
$ until =~ s/[-:]//gx ;
2020-05-19 14:55:16 +00:00
$ uets = ( explodeDateTime ( $ hash , $ until , 0 , 0 , 0 ) ) [ 4 ] ;
2020-12-16 14:40:38 +00:00
}
if ( $ p1 eq "BYMONTHDAY" ) { # Wiederholungseigenschaft -> Tag des Monats z.B. 13 (Tag 13)
2020-01-19 14:21:41 +00:00
$ bymonthday = $ p2 ;
2020-12-16 14:40:38 +00:00
}
if ( $ p1 eq "BYDAY" ) { # Wiederholungseigenschaft -> Wochentag z.B. 2WE,-1SU,4FR (kann auch Liste bei WEEKLY sein)
$ byday = $ p2 ;
2020-01-19 14:21:41 +00:00
}
}
2020-05-19 14:55:16 +00:00
if ( defined $ uets && $ uets < $ tstart ) {
Log3 ( $ name , 4 , "$name - Ignore recurring event -> $data->{data}{$key}[$i]{summary} , interval end \"$nedate $netime\" is less than selection start \"$datetimestart\"" ) ;
$ ignore = 1 ;
}
2020-12-16 14:40:38 +00:00
$ count = $ count // 9999999 ; # $count "unendlich" wenn kein COUNT angegeben
$ interval = $ interval // 1 ;
$ bymonthday = $ bymonthday // "" ;
$ byday = $ byday // "" ;
$ until = $ until // "" ;
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 4 , "$name - Recurring params - FREQ: $freq, COUNT: $count, INTERVAL: $interval, BYMONTHDAY: $bymonthday, BYDAY: $byday, UNTIL: $until" ) ;
2020-04-25 17:52:39 +00:00
$ count - - ; # Korrektur Anzahl Wiederholungen, COUNT ist Gesamtzahl der Ausführungen !
2020-01-19 14:21:41 +00:00
if ( $ freq eq "YEARLY" ) { # jährliche Wiederholung
for ( $ ci = - 1 ; $ ci < ( $ count * $ interval ) ; $ ci += $ interval ) {
$ byear += ( $ ci >= 0 ? 1 : 0 ) ;
$ eyear += ( $ ci >= 0 ? 1 : 0 ) ;
2020-04-25 17:52:39 +00:00
$ nbtime =~ s/://gx ;
$ netime =~ s/://gx ;
2020-01-19 14:21:41 +00:00
2020-02-19 20:24:56 +00:00
my $ dtstart = $ byear . $ bmonth . $ bmday . "T" . $ nbtime ;
2020-05-16 15:35:34 +00:00
( $ bi , undef , $ nbdate , $ nbtime , $ nbts , $ excl ) = explodeDateTime ( $ hash , $ byear . $ bmonth . $ bmday . "T" . $ nbtime , 0 , $ uid , $ dtstart ) ; # Beginn des Wiederholungsevents
( $ ei , undef , $ nedate , $ netime , $ nets , undef ) = explodeDateTime ( $ hash , $ eyear . $ emonth . $ emday . "T" . $ netime , 0 , $ uid , $ dtstart ) ; # Ende des Wiederholungsevents
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 5 , "$name - YEARLY event - Begin: $nbdate $nbtime, End: $nedate $netime" ) ;
2020-05-20 17:25:50 +00:00
( $ ignore , $ done , $ n , $ next ) = evalTimeAndWrite ( { recurring = > 'YEARLY' ,
name = > $ name ,
excl = > $ excl ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
newbdate = > $ nbdate ,
begintime = > $ btime ,
newbtime = > $ nbtime ,
begints = > $ bts ,
enddate = > $ edate ,
newedate = > $ nedate ,
endtime = > $ etime ,
newetime = > $ netime ,
endts = > $ ets ,
newendts = > $ nets ,
sumarrayref = > \ @ row_array ,
untilts = > $ uets ,
newbegints = > $ nbts ,
tstart = > $ tstart ,
tend = > $ tend ,
until = > $ until ,
uid = > $ uid
} ) ;
next if ( $ next ) ;
2020-01-19 14:21:41 +00:00
last if ( ( defined $ uets && ( $ uets < $ nbts ) ) || $ nbts > $ tend ) ;
}
}
if ( $ freq eq "MONTHLY" ) { # monatliche Wiederholung
if ( $ bymonthday ) { # Wiederholungseigenschaft am Tag X des Monats
for ( $ ci = - 1 ; $ ci < ( $ count * $ interval ) ; $ ci += $ interval ) {
2020-02-24 18:11:19 +00:00
$ bmonth += $ interval if ( $ ci >= 0 ) ;
2020-01-19 14:21:41 +00:00
$ byear += int ( $ bmonth / 13 ) ;
$ bmonth % = 12 if ( $ bmonth > 12 ) ;
$ bmonth = sprintf ( "%02d" , $ bmonth ) ;
2020-02-24 18:11:19 +00:00
$ emonth += $ interval if ( $ ci >= 0 ) ;
2020-01-19 14:21:41 +00:00
$ eyear += int ( $ emonth / 13 ) ;
$ emonth % = 12 if ( $ emonth > 12 ) ;
$ emonth = sprintf ( "%02d" , $ emonth ) ;
2020-04-25 17:52:39 +00:00
$ nbtime =~ s/://gx ;
$ netime =~ s/://gx ;
2020-01-19 14:21:41 +00:00
2020-02-19 20:24:56 +00:00
my $ dtstart = $ byear . $ bmonth . $ bmday . "T" . $ nbtime ;
2020-05-16 15:35:34 +00:00
( $ bi , undef , $ nbdate , $ nbtime , $ nbts , $ excl ) = explodeDateTime ( $ hash , $ byear . $ bmonth . $ bmday . "T" . $ nbtime , 0 , $ uid , $ dtstart ) ; # Beginn des Wiederholungsevents
( $ ei , undef , $ nedate , $ netime , $ nets , undef ) = explodeDateTime ( $ hash , $ eyear . $ emonth . $ emday . "T" . $ netime , 0 , $ uid , $ dtstart ) ; # Ende des Wiederholungsevents
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 5 , "$name - MONTHLY event - Begin: $nbdate $nbtime, End: $nedate $netime" ) ;
2020-05-20 17:25:50 +00:00
( $ ignore , $ done , $ n , $ next ) = evalTimeAndWrite ( { recurring = > 'MONTHLY' ,
name = > $ name ,
excl = > $ excl ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
newbdate = > $ nbdate ,
begintime = > $ btime ,
newbtime = > $ nbtime ,
begints = > $ bts ,
enddate = > $ edate ,
newedate = > $ nedate ,
endtime = > $ etime ,
newetime = > $ netime ,
endts = > $ ets ,
newendts = > $ nets ,
sumarrayref = > \ @ row_array ,
untilts = > $ uets ,
newbegints = > $ nbts ,
tstart = > $ tstart ,
tend = > $ tend ,
until = > $ until ,
uid = > $ uid
} ) ;
next if ( $ next ) ;
2020-01-19 14:21:41 +00:00
last if ( ( defined $ uets && ( $ uets < $ nbts ) ) || $ nbts > $ tend ) ;
}
}
2020-12-16 14:40:38 +00:00
2020-01-19 14:21:41 +00:00
if ( $ byday ) { # Wiederholungseigenschaft -> Wochentag z.B. 2WE,-1SU,4FR (kann auch Liste bei WEEKLY sein)
2020-12-16 14:40:38 +00:00
my ( $ nbhh , $ nbmm , $ nbss , $ nehh , $ nemm , $ ness , $ numOfRatedDay , $ rDaysToAddOrSub , $ rNewTime ) ;
2022-07-16 06:21:46 +00:00
my @ ByDays = split "," , $ byday ; # Array der Wiederholungstage
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
for my $ rByDay ( @ ByDays ) {
2020-01-19 14:21:41 +00:00
my $ rByDayLength = length ( $ rByDay ) ; # die Länge des Strings
2020-04-25 17:52:39 +00:00
my $ rDayStr ; # Tag auf den das Datum gesetzt werden soll
my $ rDayInterval ; # z.B. 2 = 2nd Tag des Monats oder -1 = letzter Tag des Monats
2022-07-16 06:21:46 +00:00
2020-01-19 14:21:41 +00:00
if ( $ rByDayLength > 2 ) {
$ rDayStr = substr ( $ rByDay , - 2 ) ;
$ rDayInterval = int ( substr ( $ rByDay , 0 , $ rByDayLength - 2 ) ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-01-19 14:21:41 +00:00
$ rDayStr = $ rByDay ;
$ rDayInterval = 1 ;
}
2022-07-16 06:21:46 +00:00
my $ numOfAppointmentDay = _weekdayNumber ( $ rDayStr ) ; # liefert Nr des Wochentages: SU = 0 ... SA = 6
2020-01-19 14:21:41 +00:00
for ( $ ci = - 1 ; $ ci < ( $ count ) ; $ ci + + ) {
2020-12-16 14:40:38 +00:00
if ( $ rDayInterval > 0 ) { # Angabe "jeder x Wochentag" ist positiv (-2 wäre z.B. vom Ende des Monats zu zähelen)
2020-02-27 20:23:56 +00:00
$ bmonth += $ interval if ( $ ci >= 0 ) ;
2020-01-19 14:21:41 +00:00
$ byear += int ( $ bmonth / 13 ) ;
$ bmonth % = 12 if ( $ bmonth > 12 ) ;
$ bmonth = sprintf ( "%02d" , $ bmonth ) ;
2022-07-16 06:21:46 +00:00
( $ nbhh , $ nbmm , $ nbss ) = split ":" , $ nbtime ;
2020-01-19 14:21:41 +00:00
my $ firstOfNextMonth = fhemTimeLocal ( $ nbss , $ nbmm , $ nbhh , 1 , $ bmonth - 1 , $ byear - 1900 ) ;
2020-12-16 14:40:38 +00:00
( $ nbss , $ nbmm , $ nbhh , $ bmday , $ bmonth , $ byear , $ numOfRatedDay , undef , undef ) = localtime ( $ firstOfNextMonth ) ; # den 1. des Monats sowie die dazu gehörige Nr. des Wochentages
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
if ( $ numOfRatedDay <= $ numOfAppointmentDay ) { # Nr Wochentag des 1. des Monats <= als Wiederholungstag
$ rDaysToAddOrSub = $ numOfAppointmentDay - $ numOfRatedDay ;
}
else {
$ rDaysToAddOrSub = 7 - $ numOfRatedDay + $ numOfAppointmentDay ;
2020-01-19 14:21:41 +00:00
}
$ rDaysToAddOrSub += ( 7 * ( $ rDayInterval - 1 ) ) ; # addiere Tagesintervall, z.B. 4th Freitag ...
2020-05-16 15:35:34 +00:00
$ rNewTime = plusNSeconds ( $ firstOfNextMonth , 86400 * $ rDaysToAddOrSub , 1 ) ;
( $ nbss , $ nbmm , $ nbhh , $ bmday , $ bmonth , $ byear , $ ness , $ nemm , $ nehh , $ emday , $ emonth , $ eyear ) = DTfromStartandDiff ( $ rNewTime , $ startEndDiff ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 2 , "$name - WARNING - negative values for BYDAY are currently not implemented and will be ignored" ) ;
$ ignore = 1 ;
$ done = 0 ;
$ n + + ;
next ;
}
$ nbtime = $ nbhh . $ nbmm . $ nbss ;
$ netime = $ nehh . $ nemm . $ ness ;
2020-02-19 20:24:56 +00:00
my $ dtstart = $ byear . $ bmonth . $ bmday . "T" . $ nbtime ;
2020-05-16 15:35:34 +00:00
( $ bi , undef , $ nbdate , $ nbtime , $ nbts , $ excl ) = explodeDateTime ( $ hash , $ byear . $ bmonth . $ bmday . "T" . $ nbtime , 0 , $ uid , $ dtstart ) ; # Beginn des Wiederholungsevents
( $ ei , undef , $ nedate , $ netime , $ nets , undef ) = explodeDateTime ( $ hash , $ eyear . $ emonth . $ emday . "T" . $ netime , 0 , $ uid , $ dtstart ) ; # Ende des Wiederholungsevents
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 5 , "$name - MONTHLY event - Begin: $nbdate $nbtime, End: $nedate $netime" ) ;
2020-05-20 17:25:50 +00:00
( $ ignore , $ done , $ n , $ next ) = evalTimeAndWrite ( { recurring = > 'MONTHLY' ,
name = > $ name ,
excl = > $ excl ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
newbdate = > $ nbdate ,
begintime = > $ btime ,
newbtime = > $ nbtime ,
begints = > $ bts ,
enddate = > $ edate ,
newedate = > $ nedate ,
endtime = > $ etime ,
newetime = > $ netime ,
endts = > $ ets ,
newendts = > $ nets ,
sumarrayref = > \ @ row_array ,
untilts = > $ uets ,
newbegints = > $ nbts ,
tstart = > $ tstart ,
tend = > $ tend ,
until = > $ until ,
uid = > $ uid
} ) ;
next if ( $ next ) ;
2020-01-19 14:21:41 +00:00
last if ( ( defined $ uets && ( $ uets < $ nbts ) ) || $ nbts > $ tend ) ;
}
}
}
}
2020-12-16 14:40:38 +00:00
if ( $ freq eq "WEEKLY" ) { # wöchentliche Wiederholung
if ( $ byday ) { # Wiederholungseigenschaft -> Wochentag z.B. 2WE,-1SU,4FR (kann auch Liste bei WEEKLY sein)
my ( $ nbhh , $ nbmm , $ nbss , $ nehh , $ nemm , $ ness , $ rNewTime ) ;
my ( $ numOfRatedDay , $ rDaysToAddOrSub ) ;
2022-07-16 06:21:46 +00:00
my @ ByDays = split "," , $ byday ; # Array der Wiederholungstage
2020-12-16 14:40:38 +00:00
2020-01-19 14:21:41 +00:00
my $ btsstart = $ bts ;
2020-02-24 17:48:34 +00:00
$ ci = - 1 ;
2020-01-19 14:21:41 +00:00
2020-02-24 17:48:34 +00:00
while ( $ ci < $ count ) {
$ rNewTime = $ btsstart ;
2020-12-16 14:40:38 +00:00
for my $ rByDay ( @ ByDays ) {
$ ci + + ;
my $ numOfAppointmentDay = _weekdayNumber ( $ rByDay ) ; # liefert Nr des Wochentages: SU = 0 ... SA = 6
2020-01-19 14:21:41 +00:00
2020-12-16 14:40:38 +00:00
( $ nbss , $ nbmm , $ nbhh , $ bmday , $ bmonth , $ byear , $ numOfRatedDay , undef , undef ) = localtime ( $ rNewTime ) ;
2020-01-19 14:21:41 +00:00
2022-07-16 06:21:46 +00:00
( $ nbhh , $ nbmm , $ nbss ) = split ":" , $ nbtime ;
2020-02-24 17:48:34 +00:00
2020-12-16 14:40:38 +00:00
if ( $ numOfRatedDay <= $ numOfAppointmentDay ) { # Nr nächster Wochentag <= Planwochentag
$ rDaysToAddOrSub = $ numOfAppointmentDay - $ numOfRatedDay ;
}
else {
$ rDaysToAddOrSub = - $ numOfRatedDay + $ numOfAppointmentDay + ( 7 * ( $ interval - 1 ) ) ;
2020-01-19 14:21:41 +00:00
}
2020-02-24 17:48:34 +00:00
2020-05-16 15:35:34 +00:00
$ rNewTime = plusNSeconds ( $ rNewTime , 86400 * $ rDaysToAddOrSub , 1 ) ;
( $ nbss , $ nbmm , $ nbhh , $ bmday , $ bmonth , $ byear , $ ness , $ nemm , $ nehh , $ emday , $ emonth , $ eyear ) = DTfromStartandDiff ( $ rNewTime , $ startEndDiff ) ;
2020-02-24 17:48:34 +00:00
2020-01-19 14:21:41 +00:00
$ nbtime = $ nbhh . $ nbmm . $ nbss ;
$ netime = $ nehh . $ nemm . $ ness ;
2020-02-24 17:48:34 +00:00
2020-02-19 20:24:56 +00:00
my $ dtstart = $ byear . $ bmonth . $ bmday . "T" . $ nbtime ;
2020-05-16 15:35:34 +00:00
( $ bi , undef , $ nbdate , $ nbtime , $ nbts , $ excl ) = explodeDateTime ( $ hash , $ byear . $ bmonth . $ bmday . "T" . $ nbtime , 0 , $ uid , $ dtstart ) ; # Beginn des Wiederholungsevents
( $ ei , undef , $ nedate , $ netime , $ nets , undef ) = explodeDateTime ( $ hash , $ eyear . $ emonth . $ emday . "T" . $ netime , 0 , $ uid , $ dtstart ) ; # Ende des Wiederholungsevents
2020-02-24 17:48:34 +00:00
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 5 , "$name - WEEKLY event - Begin: $nbdate $nbtime, End: $nedate $netime" ) ;
2020-05-20 17:25:50 +00:00
( $ ignore , $ done , $ n , $ next ) = evalTimeAndWrite ( { recurring = > 'WEEKLY' ,
name = > $ name ,
excl = > $ excl ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
newbdate = > $ nbdate ,
begintime = > $ btime ,
newbtime = > $ nbtime ,
begints = > $ bts ,
enddate = > $ edate ,
newedate = > $ nedate ,
endtime = > $ etime ,
newetime = > $ netime ,
endts = > $ ets ,
newendts = > $ nets ,
sumarrayref = > \ @ row_array ,
untilts = > $ uets ,
newbegints = > $ nbts ,
tstart = > $ tstart ,
tend = > $ tend ,
until = > $ until ,
uid = > $ uid
} ) ;
next if ( $ next ) ;
2020-12-16 14:40:38 +00:00
last if ( ( defined $ uets && ( $ uets < $ nbts ) ) || $ nbts > $ tend ) ;
2020-01-19 14:21:41 +00:00
}
2020-12-16 14:40:38 +00:00
last if ( ( defined $ uets && ( $ uets < $ nbts ) ) || $ nbts > $ tend ) ;
$ btsstart += ( 7 * 86400 * $ interval ) ; # addiere Tagesintervall, z.B. 4th Freitag ...
}
}
else {
2020-02-24 17:48:34 +00:00
my ( $ nbhh , $ nbmm , $ nbss , $ nehh , $ nemm , $ ness ) ;
2020-01-19 14:21:41 +00:00
my $ rNewTime = $ bts ;
2020-02-19 20:24:56 +00:00
for ( $ ci = - 1 ; $ ci < ( $ count * $ interval ) ; $ ci += $ interval ) {
2020-12-16 14:40:38 +00:00
$ rNewTime += $ interval * 604800 if ( $ ci >= 0 ) ; # Wochenintervall addieren
2020-01-19 14:21:41 +00:00
2020-05-16 15:35:34 +00:00
( $ nbss , $ nbmm , $ nbhh , $ bmday , $ bmonth , $ byear , $ ness , $ nemm , $ nehh , $ emday , $ emonth , $ eyear ) = DTfromStartandDiff ( $ rNewTime , $ startEndDiff ) ;
2020-01-19 14:21:41 +00:00
$ nbtime = $ nbhh . $ nbmm . $ nbss ;
$ netime = $ nehh . $ nemm . $ ness ;
2020-02-19 20:24:56 +00:00
my $ dtstart = $ byear . $ bmonth . $ bmday . "T" . $ nbtime ;
2020-05-16 15:35:34 +00:00
( $ bi , undef , $ nbdate , $ nbtime , $ nbts , $ excl ) = explodeDateTime ( $ hash , $ byear . $ bmonth . $ bmday . "T" . $ nbtime , 0 , $ uid , $ dtstart ) ; # Beginn des Wiederholungsevents
( $ ei , undef , $ nedate , $ netime , $ nets , undef ) = explodeDateTime ( $ hash , $ eyear . $ emonth . $ emday . "T" . $ netime , 0 , $ uid , $ dtstart ) ; # Ende des Wiederholungsevents
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 5 , "$name - WEEKLY event - Begin: $nbdate $nbtime, End: $nedate $netime" ) ;
2020-05-20 17:25:50 +00:00
( $ ignore , $ done , $ n , $ next ) = evalTimeAndWrite ( { recurring = > 'WEEKLY' ,
name = > $ name ,
excl = > $ excl ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
newbdate = > $ nbdate ,
begintime = > $ btime ,
newbtime = > $ nbtime ,
begints = > $ bts ,
enddate = > $ edate ,
newedate = > $ nedate ,
endtime = > $ etime ,
newetime = > $ netime ,
endts = > $ ets ,
newendts = > $ nets ,
sumarrayref = > \ @ row_array ,
untilts = > $ uets ,
newbegints = > $ nbts ,
tstart = > $ tstart ,
tend = > $ tend ,
until = > $ until ,
uid = > $ uid
} ) ;
next if ( $ next ) ;
2020-01-19 14:21:41 +00:00
last if ( ( defined $ uets && ( $ uets < $ nbts ) ) || $ nbts > $ tend ) ;
}
2020-04-25 17:52:39 +00:00
}
}
2020-01-19 14:21:41 +00:00
if ( $ freq eq "DAILY" ) { # tägliche Wiederholung
my ( $ nbhh , $ nbmm , $ nbss , $ nehh , $ nemm , $ ness ) ;
for ( $ ci = - 1 ; $ ci < ( $ count * $ interval ) ; $ ci += $ interval ) {
2020-02-27 21:24:26 +00:00
$ bts += 86400 * $ interval if ( $ ci >= 0 ) ;
2020-01-19 14:21:41 +00:00
2020-05-16 15:35:34 +00:00
( $ nbss , $ nbmm , $ nbhh , $ bmday , $ bmonth , $ byear , $ ness , $ nemm , $ nehh , $ emday , $ emonth , $ eyear ) = DTfromStartandDiff ( $ bts , $ startEndDiff ) ;
2020-01-19 14:21:41 +00:00
$ nbtime = $ nbhh . $ nbmm . $ nbss ;
$ netime = $ nehh . $ nemm . $ ness ;
2020-02-19 20:24:56 +00:00
my $ dtstart = $ byear . $ bmonth . $ bmday . "T" . $ nbtime ;
2020-05-16 15:35:34 +00:00
( $ bi , undef , $ nbdate , $ nbtime , $ nbts , $ excl ) = explodeDateTime ( $ hash , $ byear . $ bmonth . $ bmday . "T" . $ nbtime , 0 , $ uid , $ dtstart ) ; # Beginn des Wiederholungsevents
( $ ei , undef , $ nedate , $ netime , $ nets , undef ) = explodeDateTime ( $ hash , $ eyear . $ emonth . $ emday . "T" . $ netime , 0 , $ uid , $ dtstart ) ; # Ende des Wiederholungsevents
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 5 , "$name - DAILY event - Begin: $nbdate $nbtime, End: $nedate $netime" ) ;
2020-05-20 17:25:50 +00:00
( $ ignore , $ done , $ n , $ next ) = evalTimeAndWrite ( { recurring = > 'DAILY' ,
name = > $ name ,
excl = > $ excl ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
newbdate = > $ nbdate ,
begintime = > $ btime ,
newbtime = > $ nbtime ,
begints = > $ bts ,
enddate = > $ edate ,
newedate = > $ nedate ,
endtime = > $ etime ,
newetime = > $ netime ,
endts = > $ ets ,
newendts = > $ nets ,
sumarrayref = > \ @ row_array ,
untilts = > $ uets ,
newbegints = > $ nbts ,
tstart = > $ tstart ,
tend = > $ tend ,
until = > $ until ,
uid = > $ uid
} ) ;
next if ( $ next ) ;
2020-01-19 14:21:41 +00:00
last if ( ( defined $ uets && ( $ uets < $ nbts ) ) || $ nbts > $ tend ) ;
2020-04-25 17:52:39 +00:00
}
}
2020-01-19 14:21:41 +00:00
}
if ( $ ignore == 1 ) {
$ i + + ;
next ;
}
if ( ! $ done ) { # für Testzwecke mit $ignore = 0 und $done = 0
2020-05-16 15:35:34 +00:00
$ bdate = $ nbdate ? $ nbdate : $ bdate ;
$ btime = $ nbtime ? $ nbtime : $ btime ;
$ bts = $ nbts ? $ nbts : $ bts ;
2020-01-19 14:21:41 +00:00
2020-05-16 15:35:34 +00:00
$ edate = $ nedate ? $ nedate : $ edate ;
$ etime = $ netime ? $ netime : $ etime ;
$ ets = $ nets ? $ nets : $ ets ;
2020-01-19 14:21:41 +00:00
2020-05-20 17:25:50 +00:00
writeValuesToArray ( { name = > $ name ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
begintime = > $ btime ,
begints = > $ bts ,
enddate = > $ edate ,
endtime = > $ etime ,
endts = > $ ets ,
sumarrayref = > \ @ row_array ,
uid = > $ uid
} ) ;
2020-01-19 14:21:41 +00:00
}
2020-05-17 10:59:56 +00:00
2020-01-19 14:21:41 +00:00
$ i + + ;
$ n + + ;
}
$ n + + ;
}
# encoding result
my $ rowlist = join ( '_ESC_' , @ row_array ) ;
$ rowlist = encode_base64 ( $ rowlist , "" ) ;
2020-04-25 17:52:39 +00:00
return "$name|$rowlist" if ( $ am ) ; # asynchroner Mode mit BlockingCall
2020-05-16 15:35:34 +00:00
return createReadings ( "$name|$rowlist" ) ; # synchoner Mode
2020-01-19 14:21:41 +00:00
}
2020-12-16 14:40:38 +00:00
#############################################################################################
# liefert Nr eines gegebenen Wochentages, z.B. SU = 0 ... SA = 6
#############################################################################################
sub _weekdayNumber {
my $ dayString = shift ;
my @ weekdays = qw( SU MO TU WE TH FR SA ) ;
my ( $ weekdayNum ) = grep { $ weekdays [ $ _ ] eq $ dayString } 0 .. $# weekdays ;
return $ weekdayNum ;
}
2020-01-31 15:27:30 +00:00
#############################################################################################
# Extrahiert empfangene Tasks aus ToDo-Kalender (Aufgabenliste)
#############################################################################################
2020-05-16 15:35:34 +00:00
sub extractToDolist { ## no critic 'complexity'
2020-01-31 15:27:30 +00:00
my ( $ name ) = @ _ ;
my $ hash = $ defs { $ name } ;
my $ data = delete $ hash - > { eventlist } ;
my $ am = AttrVal ( $ name , "asyncMode" , 0 ) ;
my ( $ val , $ tz , $ td , $ d , $ t , $ uts ) ;
2020-04-25 19:53:14 +00:00
my ( $ bdate , $ btime , $ bts , $ edate , $ etime , $ ets , $ ci , $ bi , $ ei , $ startEndDiff , $ excl ) ;
2020-05-16 15:35:34 +00:00
my ( $ nbdate , $ nbtime , $ nbts , $ nedate , $ netime , $ nets ) ;
2020-01-31 15:27:30 +00:00
my @ row_array ;
2020-05-16 15:35:34 +00:00
my ( undef , $ tstart , $ tend ) = timeEdge ( $ name ) ; # Sollstart- und Sollendezeit der Kalenderereignisse ermitteln
2020-01-31 15:27:30 +00:00
my $ datetimestart = FmtDateTime ( $ tstart ) ;
my $ datetimeend = FmtDateTime ( $ tend ) ;
my $ n = 0 ;
2020-04-25 19:53:14 +00:00
for my $ key ( keys % { $ data - > { data } } ) {
2020-01-31 15:27:30 +00:00
my $ i = 0 ;
while ( $ data - > { data } { $ key } [ $ i ] ) {
my $ ignore = 0 ;
my $ done = 0 ;
2020-04-25 17:52:39 +00:00
( $ nbdate , $ nedate ) = ( "" , "" ) ;
2020-02-19 20:24:56 +00:00
2020-04-25 17:52:39 +00:00
my $ uid = $ data - > { data } { $ key } [ $ i ] { ical_uid } ; # UID des Events
2020-01-31 15:27:30 +00:00
2020-05-16 15:35:34 +00:00
( $ bi , $ tz , $ bdate , $ btime , $ bts , $ excl ) = explodeDateTime ( $ hash , $ data - > { data } { $ key } [ $ i ] { due } , 0 , 0 , 0 ) ; # Fälligkeit des Tasks (falls gesetzt)
( $ ei , undef , $ edate , $ etime , $ ets , undef ) = explodeDateTime ( $ hash , $ data - > { data } { $ key } [ $ i ] { due } , 0 , 0 , 0 ) ; # Ende = Fälligkeit des Tasks (falls gesetzt)
2020-01-31 15:27:30 +00:00
2020-02-09 10:21:37 +00:00
if ( $ bdate && $ edate ) { # nicht jede Aufgabe hat Date / Time gesetzt
2020-05-16 15:35:34 +00:00
my ( $ byear , $ bmonth , $ bmday ) = $ bdate =~ /(\d{4})-(\d{2})-(\d{2})/x ;
$ nbtime = $ btime ;
2020-01-31 15:27:30 +00:00
2020-05-16 15:35:34 +00:00
my ( $ eyear , $ emonth , $ emday ) = $ edate =~ /(\d{4})-(\d{2})-(\d{2})/x ;
$ netime = $ etime ;
2020-01-31 15:27:30 +00:00
}
if ( ! $ data - > { data } { $ key } [ $ i ] { is_repeat_evt } ) { # einmaliger Task (momentan gibt es keine Wiederholungstasks)
Log3 ( $ name , 5 , "$name - Single task Begin: $bdate, End: $edate" ) if ( $ bdate && $ edate ) ;
if ( ( $ ets && $ ets < $ tstart ) || ( $ bts && $ bts > $ tend ) ) {
Log3 ( $ name , 4 , "$name - Ignore single task -> $data->{data}{$key}[$i]{summary} start: $bdate $btime, end: $edate $etime" ) ;
$ ignore = 1 ;
2020-12-16 14:40:38 +00:00
$ done = 0 ;
}
else {
2020-05-20 17:25:50 +00:00
writeValuesToArray ( { name = > $ name ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
begintime = > $ btime ,
begints = > $ bts ,
enddate = > $ edate ,
endtime = > $ etime ,
endts = > $ ets ,
sumarrayref = > \ @ row_array ,
uid = > $ uid
} ) ;
2020-01-31 15:27:30 +00:00
$ ignore = 0 ;
$ done = 1 ;
}
}
if ( $ ignore == 1 ) {
$ i + + ;
next ;
}
if ( ! $ done ) { # für Testzwecke mit $ignore = 0 und $done = 0
2020-05-16 15:35:34 +00:00
$ bdate = $ nbdate ? $ nbdate : $ bdate ;
$ btime = $ nbtime ? $ nbtime : $ btime ;
$ bts = $ nbts ? $ nbts : $ bts ;
2020-01-31 15:27:30 +00:00
2020-05-16 15:35:34 +00:00
$ edate = $ nedate ? $ nedate : $ edate ;
$ etime = $ netime ? $ netime : $ etime ;
$ ets = $ nets ? $ nets : $ ets ;
2020-01-31 15:27:30 +00:00
2020-05-20 17:25:50 +00:00
writeValuesToArray ( { name = > $ name ,
eventno = > $ n ,
calref = > $ data - > { data } { $ key } [ $ i ] ,
timezone = > $ tz ,
begindate = > $ bdate ,
begintime = > $ btime ,
begints = > $ bts ,
enddate = > $ edate ,
endtime = > $ etime ,
endts = > $ ets ,
sumarrayref = > \ @ row_array ,
uid = > $ uid
} ) ;
2020-01-31 15:27:30 +00:00
}
$ i + + ;
$ n + + ;
}
$ n + + ;
}
# encoding result
my $ rowlist = join ( '_ESC_' , @ row_array ) ;
$ rowlist = encode_base64 ( $ rowlist , "" ) ;
2020-04-25 17:52:39 +00:00
return "$name|$rowlist" if ( $ am ) ; # asynchroner Mode mit BlockingCall
2020-05-20 17:25:50 +00:00
return createReadings ( "$name|$rowlist" ) ; # synchoner Mode
}
#############################################################################################
#
2020-12-16 14:40:38 +00:00
# bewertet Zeitgrenzen und excludierende Parameter - ruft Schreiben der Daten in Ergebnis
2020-05-20 17:25:50 +00:00
# Array auf wenn Prüfung positiv
#
#############################################################################################
sub evalTimeAndWrite {
my ( $ argref ) = @ _ ;
my $ name = $ argref - > { name } ;
my $ n = $ argref - > { eventno } ;
my $ calref = $ argref - > { calref } ;
my $ excl = $ argref - > { excl } ;
my $ tz = $ argref - > { timezone } ;
my $ bdate = $ argref - > { begindate } ;
my $ nbdate = $ argref - > { newbdate } ;
my $ btime = $ argref - > { begintime } ;
my $ nbtime = $ argref - > { newbtime } ;
my $ bts = $ argref - > { begints } ;
my $ edate = $ argref - > { enddate } ;
my $ nedate = $ argref - > { newedate } ;
my $ etime = $ argref - > { endtime } ;
my $ netime = $ argref - > { newetime } ;
my $ ets = $ argref - > { endts } ;
my $ uets = $ argref - > { untilts } ;
my $ nbts = $ argref - > { newbegints } ;
my $ tstart = $ argref - > { tstart } ;
my $ tend = $ argref - > { tend } ;
my $ nets = $ argref - > { newendts } ;
my $ aref = $ argref - > { sumarrayref } ;
my $ uid = $ argref - > { uid } ;
my $ recurring = $ argref - > { recurring } ;
my $ until = $ argref - > { until } ;
my $ ignore = 0 ;
my $ done = 0 ;
my $ next = 0 ;
if ( defined $ uets && ( $ uets < $ nbts ) ) { # Event Ende (UNTIL) kleiner aktueller Select Start
2020-12-16 14:40:38 +00:00
Log3 ( $ name , 4 , "$name - Ignore " . $ recurring . " event - UNTIL out of time LIMIT (" . $ calref - > { summary } . " , start: $nbdate $nbtime, end: $nedate $netime, until: $until)" ) ;
2020-05-20 17:25:50 +00:00
$ ignore = 1 ;
2020-12-16 14:40:38 +00:00
$ done = 0 ;
}
elsif ( $ nets < $ tstart || $ nbts > $ tend ) { # Event Ende kleiner Select Start oder Beginn Event größer als Select Ende
Log3 ( $ name , 4 , "$name - Ignore " . $ recurring . " event - out of selected time LIMITS (" . $ calref - > { summary } . " , start: $nbdate $nbtime, end: $nedate $netime)" ) ;
2020-05-20 17:25:50 +00:00
$ ignore = 1 ;
2020-12-16 14:40:38 +00:00
$ done = 0 ;
}
elsif ( $ nbts < $ bts ) {
Log3 ( $ name , 4 , "$name - Ignore " . $ recurring . " event - calculated BEGIN is before DTSTART (" . $ calref - > { summary } . " , start: $nbdate $nbtime, end: $nedate $netime)" ) ;
2020-05-20 17:25:50 +00:00
$ ignore = 1 ;
2020-12-16 14:40:38 +00:00
$ done = 0 ;
}
elsif ( $ excl ) {
Log3 ( $ name , 4 , "$name - " . $ recurring . " recurring event - is DELETED (" . $ calref - > { summary } . " , start: $nbdate $nbtime, end: $nedate $netime)" ) ;
$ ignore = 1 ;
$ done = 0 ;
}
else {
$ bdate = $ nbdate // $ bdate ;
$ btime = $ nbtime // $ btime ;
$ bts = $ nbts // $ bts ;
2020-05-20 17:25:50 +00:00
2020-12-16 14:40:38 +00:00
$ edate = $ nedate // $ edate ;
$ etime = $ netime // $ etime ;
$ ets = $ nets // $ ets ;
2020-05-20 17:25:50 +00:00
writeValuesToArray ( { name = > $ name ,
eventno = > $ n ,
calref = > $ calref ,
timezone = > $ tz ,
begindate = > $ bdate ,
begintime = > $ btime ,
begints = > $ bts ,
enddate = > $ edate ,
endtime = > $ etime ,
endts = > $ ets ,
sumarrayref = > $ aref ,
uid = > $ uid
} ) ;
$ ignore = 0 ;
$ done = 1 ;
$ next = 1 ;
$ n + + ;
}
return ( $ ignore , $ done , $ n , $ next ) ;
}
#############################################################################################
# schreibe Key/Value Pairs in zentrales Valuearray zur Readingerstellung
# $n = Zusatz f. lfd. Nr. zur Unterscheidung exakt
# zeitgleicher Events
# $vh = Referenz zum Kalenderdatenhash
# $aref = Rferenz zum Ergebnisarray
# $uid = UID des Ereignisses als Schlüssel im VCALENDER Hash
# (Berechnung der Vorwarnzeitenzeiten)
#
# Ergebisarray Aufbau:
# 0 1 2
# (Index aus BeginTimestamp + lfNr) , (Blockindex_Reading) , (Wert)
#
#############################################################################################
sub writeValuesToArray { ## no critic 'complexity'
my ( $ argref ) = @ _ ;
my $ name = $ argref - > { name } ;
my $ n = $ argref - > { eventno } ;
my $ vh = $ argref - > { calref } ;
my $ tz = $ argref - > { timezone } ;
my $ bdate = $ argref - > { begindate } ;
my $ btime = $ argref - > { begintime } ;
my $ bts = $ argref - > { begints } ;
my $ edate = $ argref - > { enddate } ;
my $ etime = $ argref - > { endtime } ;
my $ ets = $ argref - > { endts } ;
my $ aref = $ argref - > { sumarrayref } ;
my $ uid = $ argref - > { uid } ;
my $ hash = $ defs { $ name } ;
my $ lang = AttrVal ( "global" , "language" , "EN" ) ;
my $ ts = time ( ) ; # Istzeit Timestamp
my $ om = $ hash - > { OPMODE } ; # aktuelle Operation Mode
my $ status = "initialized" ;
my ( $ val , $ uts , $ td , $ dleft , $ bWday , $ chts ) ;
my ( $ upcoming , $ alarmed , $ started , $ ended ) = ( 0 , 0 , 0 , 0 ) ;
$ upcoming = isUpcoming ( $ ts , 0 , $ bts ) ; # initiales upcoming
$ started = isStarted ( $ ts , $ bts , $ ets ) ;
$ ended = isEnded ( $ ts , $ ets ) ;
if ( $ bdate && $ btime ) {
push ( @$ aref , $ bts + $ n . " 05_Begin " . $ bdate . " " . $ btime . "\n" ) ;
my ( $ ny , $ nm , $ nd , undef ) = split ( /[\s-]/x , TimeNow ( ) ) ; # Datum Jetzt
my ( $ by , $ bm , $ bd ) = split ( "-" , $ bdate ) ; # Beginn Datum
my $ ntimes = fhemTimeLocal ( 00 , 00 , 00 , $ nd , $ nm - 1 , $ ny - 1900 ) ;
my $ btimes = fhemTimeLocal ( 00 , 00 , 00 , $ bd , $ bm - 1 , $ by - 1900 ) ;
if ( $ btimes >= $ ntimes ) {
$ dleft = int ( ( $ btimes - $ ntimes ) / 86400 ) ;
}
my @ days ;
( undef , undef , undef , undef , undef , undef , $ bWday , undef , undef ) = localtime ( $ btimes ) ;
if ( $ lang eq "DE" ) {
2020-12-16 14:40:38 +00:00
@ days = qw( Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag ) ;
}
else {
2020-05-20 17:25:50 +00:00
@ days = qw( Sunday Monday Tuesday Wednesday Thursday Friday Saturday ) ;
}
$ bWday = $ days [ $ bWday ] ;
}
push ( @$ aref , $ bts + $ n . " 10_End " . $ edate . " " . $ etime . "\n" ) if ( $ edate && $ etime ) ;
push ( @$ aref , $ bts + $ n . " 15_Timezone " . $ tz . "\n" ) if ( $ tz ) ;
push ( @$ aref , $ bts + $ n . " 20_daysLeft " . $ dleft . "\n" ) if ( defined $ dleft ) ;
push ( @$ aref , $ bts + $ n . " 25_daysLeftLong " . "in " . $ dleft . " Tagen\n" ) if ( defined $ dleft ) ;
push ( @$ aref , $ bts + $ n . " 30_Weekday " . $ bWday . "\n" ) if ( defined $ bWday ) ;
# Vorwarnzeiten für veränderte Serientermine korrigieren/anpassen
my $ origdtstart = strftime "%Y%m%dT%H%M%S" , localtime ( $ bts ) ;
my $ isRecurrence = 0 ;
my $ isAlldaychanded ; # 0 -> Ganztagsevent wurde in Serienelement geändert in kein Ganztagsevent
for ( keys % { $ data { SSCal } { $ name } { vcalendar } { "$uid" } { VALM } { RECURRENCEID } } ) { # $isRecurrence = 1 setzen wenn für die aktuelle Originalstartzeit ($bts) eine RECURRENCEID vorliegt -> Veränderung ist vorhanden
next if ( ! $ data { SSCal } { $ name } { vcalendar } { "$uid" } { VALM } { RECURRENCEID } { $ _ } ) ;
$ isRecurrence = 1 if ( $ data { SSCal } { $ name } { vcalendar } { "$uid" } { VALM } { RECURRENCEID } { $ _ } eq $ origdtstart ) ;
}
my $ l = length ( keys % { $ data { SSCal } { $ name } { vcalendar } { "$uid" } { VALM } { TIMEVALUE } } ) ; # Anzahl Stellen (Länge) des aktuellen VALM TIMEVALUE Hashes
my $ ens = 0 ;
for ( keys % { $ data { SSCal } { $ name } { vcalendar } { "$uid" } { VALM } { TIMEVALUE } } ) {
my $ z = $ _ ;
$ val = encode ( "UTF-8" , $ data { SSCal } { $ name } { vcalendar } { "$uid" } { VALM } { TIMEVALUE } { $ z } ) ;
if ( ! $ data { SSCal } { $ name } { vcalendar } { "$uid" } { VALM } { RECURRENCEID } { $ z } && ! $ isRecurrence ) { # wenn keine Veränderung vorhanden ist ({RECURRENCEID}{index}=undef) gelten die Erinnerungszeiten Standarderinnerungszeiten
( $ uts , $ td ) = evtNotTime ( $ name , $ val , $ bts ) ;
push ( @$ aref , $ bts + $ n . " 80_" . sprintf ( "%0$l.0f" , $ ens ) . "_notifyDateTime " . $ td . "\n" ) ;
$ alarmed = isAlarmed ( $ ts , $ uts , $ bts ) if ( ! $ alarmed ) ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ data { SSCal } { $ name } { vcalendar } { "$uid" } { VALM } { RECURRENCEID } { $ z } &&
2020-05-20 17:25:50 +00:00
$ data { SSCal } { $ name } { vcalendar } { "$uid" } { VALM } { RECURRENCEID } { $ z } eq $ origdtstart ) {
2020-12-16 14:40:38 +00:00
my ( $ y , $ m , $ d ) = $ bdate =~ /^(\d{4})-(\d{2})-(\d{2})/x ; # Timestamp für Berechnung Erinnerungszeit Begindatum/Zeit ...
2020-05-20 17:25:50 +00:00
my ( $ h , $ mi , $ s ) = $ btime =~ /^(\d{2}):(\d{2}):(\d{2})/x ;
eval { $ chts = fhemTimeLocal ( $ s , $ mi , $ h , $ d , $ m - 1 , $ y - 1900 ) ; # ... neu aus bdate/btime ableiten wegen Änderung durch Recurrance-id
1 ;
} or do {
my $ err = ( split ( " at" , $@ ) ) [ 0 ] ;
Log3 ( $ name , 3 , "$name - ERROR - invalid date/time format in 'writeValuesToArray' detected: $err " ) ;
} ;
( $ uts , $ td ) = evtNotTime ( $ name , $ val , $ chts ) ;
push ( @$ aref , $ bts + $ n . " 80_" . sprintf ( "%0$l.0f" , $ ens ) . "_notifyDateTime " . $ td . "\n" ) ;
$ alarmed = isAlarmed ( $ ts , $ uts , $ chts ) if ( ! $ alarmed ) ;
$ isAlldaychanded = 0 ;
}
$ ens + + ;
}
# restliche Keys extrahieren
for my $ p ( keys % { $ vh } ) {
$ vh - > { $ p } = "" if ( ! defined $ vh - > { $ p } ) ;
2020-12-16 14:40:38 +00:00
$ vh - > { $ p } = jboolmap ( $ vh - > { $ p } , "bin" ) ; # boolsche Werte binär dekodieren
2020-05-20 17:25:50 +00:00
next if ( $ vh - > { $ p } eq "" ) ;
$ val = encode ( "UTF-8" , $ vh - > { $ p } ) ;
push ( @$ aref , $ bts + $ n . " 01_Summary " . $ val . "\n" ) if ( $ p eq "summary" ) ;
push ( @$ aref , $ bts + $ n . " 03_Description " . $ val . "\n" ) if ( $ p eq "description" ) ;
push ( @$ aref , $ bts + $ n . " 35_Location " . $ val . "\n" ) if ( $ p eq "location" ) ;
if ( $ p eq "gps" ) {
my ( $ address , $ lng , $ lat ) = ( "" , "" , "" ) ;
for my $ r ( keys % { $ vh - > { gps } } ) {
$ vh - > { $ p } { $ r } = "" if ( ! defined $ vh - > { $ p } { $ r } ) ;
next if ( $ vh - > { $ p } { $ r } eq "" ) ;
if ( $ r eq "address" ) {
$ address = encode ( "UTF-8" , $ vh - > { $ p } { $ r } ) if ( $ vh - > { $ p } { $ r } ) ;
}
if ( $ r eq "gps" ) {
$ lng = encode ( "UTF-8" , $ vh - > { $ p } { $ r } { lng } ) ;
$ lat = encode ( "UTF-8" , $ vh - > { $ p } { $ r } { lat } ) ;
}
}
push ( @$ aref , $ bts + $ n . " 40_gpsAddress " . $ address . "\n" ) ;
$ val = "lat=" . $ lat . ",lng=" . $ lng ;
push ( @$ aref , $ bts + $ n . " 45_gpsCoordinates " . $ val . "\n" ) ;
}
push ( @$ aref , $ bts + $ n . " 50_isAllday " . ( defined $ isAlldaychanded ? $ isAlldaychanded : $ val ) . "\n" ) if ( $ p eq "is_all_day" ) ;
push ( @$ aref , $ bts + $ n . " 55_isRepeatEvt " . $ val . "\n" ) if ( $ p eq "is_repeat_evt" ) ;
if ( $ p eq "due" ) {
my ( undef , undef , $ duedate , $ duetime , $ duets , undef ) = explodeDateTime ( $ hash , $ val , 0 , 0 , 0 ) ;
push ( @$ aref , $ bts + $ n . " 60_dueDateTime " . $ duedate . " " . $ duetime . "\n" ) ;
push ( @$ aref , $ bts + $ n . " 65_dueTimestamp " . $ duets . "\n" ) ;
}
push ( @$ aref , $ bts + $ n . " 85_percentComplete " . $ val . "\n" ) if ( $ p eq "percent_complete" && $ om eq "todolist" ) ;
push ( @$ aref , $ bts + $ n . " 90_calName " . getCalFromId ( $ hash , $ val ) . "\n" ) if ( $ p eq "original_cal_id" ) ;
if ( $ p eq "evt_repeat_setting" ) {
for my $ r ( keys % { $ vh - > { evt_repeat_setting } } ) {
$ vh - > { $ p } { $ r } = "" if ( ! defined $ vh - > { $ p } { $ r } ) ;
next if ( $ vh - > { $ p } { $ r } eq "" ) ;
$ val = encode ( "UTF-8" , $ vh - > { $ p } { $ r } ) ;
push ( @$ aref , $ bts + $ n . " 70_repeatRule " . $ val . "\n" ) if ( $ r eq "repeat_rule" ) ;
}
}
push ( @$ aref , $ bts + $ n . " 95_IcalUID " . $ val . "\n" ) if ( $ p eq "ical_uid" ) ;
push ( @$ aref , $ bts + $ n . " 98_EventId " . $ val . "\n" ) if ( $ p eq "evt_id" ) ;
}
$ status = "upcoming" if ( $ upcoming ) ;
$ status = "alarmed" if ( $ alarmed ) ;
$ status = "started" if ( $ started ) ;
$ status = "ended" if ( $ ended ) ;
push ( @$ aref , $ bts + $ n . " 17_Status " . $ status . "\n" ) ;
push ( @$ aref , $ bts + $ n . " 99_---------------------- " . "--------------------------------------------------------------------" . "\n" ) ;
return ;
2020-01-31 15:27:30 +00:00
}
2020-01-19 14:21:41 +00:00
#############################################################################################
2020-02-14 13:20:59 +00:00
# - füllt zentrales $data{SSCal}{$name}{eventlist} Valuehash
# - erstellt Readings aus $data{SSCal}{$name}{eventlist}
# - ruft Routine auf um zusätzliche Steuerungsevents zu erstellen
2020-01-19 14:21:41 +00:00
#############################################################################################
2020-05-16 15:35:34 +00:00
sub createReadings {
2020-01-19 14:21:41 +00:00
my ( $ string ) = @ _ ;
my @ a = split ( "\\|" , $ string ) ;
my $ name = $ a [ 0 ] ;
my $ hash = $ defs { $ name } ;
2020-04-25 17:52:39 +00:00
my $ rowlist = $ a [ 1 ] ? decode_base64 ( $ a [ 1 ] ) : "" ;
2020-01-19 14:21:41 +00:00
2020-02-16 21:48:39 +00:00
my @ abnr ;
2020-01-31 15:27:30 +00:00
if ( $ rowlist ) {
my @ row_array = split ( "_ESC_" , $ rowlist ) ;
# zentrales Datenhash füllen (erzeugt dadurch sortierbare Keys)
2020-04-25 19:53:14 +00:00
for my $ row ( @ row_array ) {
2020-01-31 15:27:30 +00:00
chomp $ row ;
my @ r = split ( " " , $ row , 3 ) ;
$ data { SSCal } { $ name } { eventlist } { $ r [ 0 ] } { $ r [ 1 ] } = $ r [ 2 ] ;
}
2020-01-19 14:21:41 +00:00
}
# Readings der Eventliste erstellen
if ( $ data { SSCal } { $ name } { eventlist } ) {
2020-12-16 14:40:38 +00:00
my $ l = length ( keys % { $ data { SSCal } { $ name } { eventlist } } ) ; # Anzahl Stellen des max. Index ermitteln
2020-02-14 13:20:59 +00:00
2020-04-25 17:52:39 +00:00
readingsBeginUpdate ( $ hash ) ;
2020-12-16 14:40:38 +00:00
$ data { SSCal } { $ name } { lstUpdtTs } = $ hash - > { ".updateTime" } ; # letzte Updatezeit speichern (Unix Format)
2020-01-19 14:21:41 +00:00
my $ k = 0 ;
2020-04-25 19:53:14 +00:00
for my $ idx ( sort keys % { $ data { SSCal } { $ name } { eventlist } } ) {
2020-12-16 14:40:38 +00:00
my $ idxstr = sprintf ( "%0$l.0f" , $ k ) ; # Blocknummer erstellen
push ( @ abnr , $ idxstr ) ; # Array aller vorhandener Blocknummern erstellen
2020-04-25 17:52:39 +00:00
2020-04-25 19:53:14 +00:00
for my $ r ( keys % { $ data { SSCal } { $ name } { eventlist } { $ idx } } ) {
2020-12-16 14:40:38 +00:00
if ( $ r =~ /.*Timestamp$/x ) { # Readings mit Unix Timestamps versteckt erstellen
2020-01-19 14:21:41 +00:00
readingsBulkUpdate ( $ hash , "." . $ idxstr . "_" . $ r , $ data { SSCal } { $ name } { eventlist } { $ idx } { $ r } ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-01-19 14:21:41 +00:00
readingsBulkUpdate ( $ hash , $ idxstr . "_" . $ r , $ data { SSCal } { $ name } { eventlist } { $ idx } { $ r } ) ;
}
}
$ k += 1 ;
}
2020-12-16 14:40:38 +00:00
readingsEndUpdate ( $ hash , 1 ) ;
}
else {
delReadings ( $ name , 0 ) ; # alle Kalender-Readings löschen
2020-01-19 14:21:41 +00:00
}
2020-12-16 14:40:38 +00:00
doCompositeEvents ( $ name , \ @ abnr , $ data { SSCal } { $ name } { eventlist } ) ; # spezifische Controlevents erstellen
2020-03-04 21:33:37 +00:00
2020-12-16 14:40:38 +00:00
checkSendRetry ( $ name , 0 , $ queueStartFn ) ;
2020-01-19 14:21:41 +00:00
$ data { SSCal } { $ name } { lastUpdate } = FmtDateTime ( $ data { SSCal } { $ name } { lstUpdtTs } ) if ( $ data { SSCal } { $ name } { lstUpdtTs } ) ;
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , "none" ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , "none" ) ;
2020-02-23 18:28:00 +00:00
readingsBulkUpdate ( $ hash , "lastUpdate" , FmtTime ( time ) ) ;
2020-01-19 14:21:41 +00:00
readingsBulkUpdate ( $ hash , "state" , "done" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
2020-05-16 15:35:34 +00:00
delReadings ( $ name , 1 ) if ( $ data { SSCal } { $ name } { lstUpdtTs } ) ; # Readings löschen wenn Timestamp nicht "lastUpdate"
2020-02-16 21:48:39 +00:00
if ( AttrVal ( $ name , "createATDevs" , 0 ) ) {
2020-05-16 15:35:34 +00:00
createAtDevices ( $ name , \ @ abnr , $ data { SSCal } { $ name } { eventlist } ) ; # automatisch at-Devics mit FHEM/Perl-Kommandos erstellen
2020-02-16 21:48:39 +00:00
}
2020-01-19 14:21:41 +00:00
return ;
}
2020-02-14 13:20:59 +00:00
#############################################################################################
# erstellt zusätzliche Steuerungsevents um einfach per Notify
# Gerätesteuerungen aus Kalender einträgen zu generieren
#
# $abnr - Referenz zum Array aller vorhandener Blocknummern
# $evref - Referenz zum zentralen Valuehash ($data{SSCal}{$name}{eventlist})
#
#############################################################################################
2020-05-16 15:35:34 +00:00
sub doCompositeEvents {
2020-02-14 13:20:59 +00:00
my ( $ name , $ abnr , $ evref ) = @ _ ;
my $ hash = $ defs { $ name } ;
2020-03-02 23:36:47 +00:00
my ( $ summary , $ desc , $ begin , $ status , $ isrepeat , $ id , $ event ) ;
2020-02-14 13:20:59 +00:00
2020-03-04 21:33:37 +00:00
if ( @ { $ abnr } ) {
my $ nrs = join ( " " , @ { $ abnr } ) ;
$ event = "compositeBlockNumbers: $nrs" ;
2020-12-16 14:40:38 +00:00
}
else {
2020-03-04 21:33:37 +00:00
$ event = "compositeBlockNumbers: none" ;
}
CommandTrigger ( undef , "$name $event" ) ;
2020-04-25 19:53:14 +00:00
for my $ bnr ( @ { $ abnr } ) {
2020-03-02 23:36:47 +00:00
$ summary = ReadingsVal ( $ name , $ bnr . "_01_Summary" , "" ) ;
2020-02-17 19:03:52 +00:00
$ desc = ReadingsVal ( $ name , $ bnr . "_03_Description" , "" ) ;
$ begin = ReadingsVal ( $ name , $ bnr . "_05_Begin" , "" ) ;
2020-04-25 17:52:39 +00:00
$ status = ReadingsVal ( $ name , $ bnr . "_17_Status" , "" ) ;
2020-02-17 19:03:52 +00:00
$ isrepeat = ReadingsVal ( $ name , $ bnr . "_55_isRepeatEvt" , 0 ) ;
$ id = ReadingsVal ( $ name , $ bnr . "_98_EventId" , "" ) ;
2020-02-14 13:20:59 +00:00
2020-04-25 17:52:39 +00:00
$ begin =~ s/\s/T/x ; # Formatierung nach ISO8601 (YYYY-MM-DDTHH:MM:SS) für at-Device
2020-03-04 21:33:37 +00:00
2020-04-25 17:52:39 +00:00
if ( $ begin ) { # einen Composite-Event erstellen wenn Beginnzeit gesetzt ist
$ event = "composite: $bnr $id $isrepeat $begin $status " . ( $ desc ? $ desc: $ summary ) ;
CommandTrigger ( undef , "$name $event" ) ;
}
2020-02-14 13:20:59 +00:00
}
return ;
}
2020-02-16 21:48:39 +00:00
#############################################################################################
# erstellt automatisch AT-Devices aus Kalendereinträgen die FHEM-Befehle oder
2020-02-17 19:03:52 +00:00
# Perl-Routinen in "Description" enthalten.
2020-02-16 21:48:39 +00:00
# FHEM-Befehle sind in { } und Perl-Routinen in {{ }} einzufassen.
#
# $abnr - Referenz zum Array aller vorhandener Blocknummern
# $evref - Referenz zum zentralen Valuehash ($data{SSCal}{$name}{eventlist})
#
#############################################################################################
2020-05-16 15:35:34 +00:00
sub createAtDevices {
2020-02-16 21:48:39 +00:00
my ( $ name , $ abnr , $ evref ) = @ _ ;
my $ hash = $ defs { $ name } ;
2020-02-17 19:03:52 +00:00
my ( $ desc , $ begin , $ status , $ isrepeat , $ id , @ devs , $ err , $ summary , $ location ) ;
2020-02-16 21:48:39 +00:00
2020-02-17 21:26:20 +00:00
my $ room = AttrVal ( $ name , "room" , "" ) ;
my $ assoc = "" ;
2020-12-16 14:40:38 +00:00
readingsDelete ( $ hash , ".associatedWith" ) ; # Deviceassoziationen löschen
2020-02-16 21:48:39 +00:00
2020-02-17 20:00:18 +00:00
@ devs = devspec2array ( "TYPE=at:FILTER=NAME=SSCal.$name.*" ) ;
2020-04-25 19:53:14 +00:00
for ( @ devs ) {
2020-02-17 20:00:18 +00:00
next if ( ! $ defs { $ _ } ) ;
Log3 ( $ name , 4 , "$name - delete device: $_" ) ;
CommandDelete ( undef , $ _ ) ;
}
2020-02-16 21:48:39 +00:00
2020-04-25 19:53:14 +00:00
for my $ bnr ( @ { $ abnr } ) {
2020-02-16 21:48:39 +00:00
$ summary = ReadingsVal ( $ name , $ bnr . "_01_Summary" , "" ) ;
2020-02-17 19:03:52 +00:00
$ desc = ReadingsVal ( $ name , $ bnr . "_03_Description" , "" ) ;
2020-02-16 21:48:39 +00:00
$ begin = ReadingsVal ( $ name , $ bnr . "_05_Begin" , "" ) ;
2020-04-25 17:52:39 +00:00
$ status = ReadingsVal ( $ name , $ bnr . "_17_Status" , "" ) ;
2020-12-16 14:40:38 +00:00
$ location = ReadingsVal ( $ name , $ bnr . "_35_Location" , $ room ) ; # Location wird als room gesetzt
2020-02-17 19:03:52 +00:00
$ id = ReadingsVal ( $ name , $ bnr . "_98_EventId" , "" ) ;
2020-02-16 21:48:39 +00:00
2020-12-16 14:40:38 +00:00
if ( $ begin && $ status =~ /upcoming|alarmed/x && $ desc =~ /^\s*\{(.*)\}\s*$/xs ) { # ein at-Device erstellen wenn Voraussetzungen erfüllt
2020-02-16 21:48:39 +00:00
my $ cmd = $ 1 ;
2020-12-16 14:40:38 +00:00
$ begin =~ s/\s/T/x ; # Formatierung nach ISO8601 (YYYY-MM-DDTHH:MM:SS) für at-Devices
2020-02-16 21:48:39 +00:00
my $ ao = $ begin ;
2020-04-25 17:52:39 +00:00
$ ao =~ s/[-:]//gx ;
2020-12-16 14:40:38 +00:00
my $ atn = "SSCal.$name.$id.$ao" ; # Name neues at-Device
2020-04-25 17:52:39 +00:00
Log3 ( $ name , 4 , "$name - Command detected. Create device \"$atn\" with type \"at\"." ) ;
2020-02-17 21:26:20 +00:00
$ err = CommandDefine ( undef , "$atn at $begin $cmd" ) ;
2020-12-16 14:40:38 +00:00
2020-02-17 19:03:52 +00:00
if ( $ err ) {
2020-04-25 17:52:39 +00:00
Log3 ( $ name , 1 , "$name - Error during create \"$atn\": $err" ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-02-17 21:26:20 +00:00
CommandSetReading ( undef , "$atn .associatedWith $name" ) ;
CommandAttr ( undef , "$atn room $location" ) ;
2020-02-28 09:46:25 +00:00
CommandAttr ( undef , "$atn alias $summary" ) ;
CommandAttr ( undef , "$atn comment created automatically by SSCal \"$name\" " ) ;
2020-02-17 21:26:20 +00:00
$ assoc . = " $atn" ;
2020-02-17 19:03:52 +00:00
}
2020-04-25 17:52:39 +00:00
}
2020-02-16 21:48:39 +00:00
}
2020-02-17 21:26:20 +00:00
CommandSetReading ( undef , "$name .associatedWith $assoc" ) ;
2020-02-16 21:48:39 +00:00
return ;
}
2020-01-19 14:21:41 +00:00
####################################################################################################
# Abbruchroutine BlockingCall
####################################################################################################
2020-05-16 15:35:34 +00:00
sub blockingTimeout {
2020-01-19 14:21:41 +00:00
my ( $ hash , $ cause ) = @ _ ;
2020-04-25 17:52:39 +00:00
my $ name = $ hash - > { NAME } ;
2020-01-19 14:21:41 +00:00
$ cause = $ cause ? $ cause: "Timeout: process terminated" ;
Log3 ( $ name , 1 , "$name -> BlockingCall $hash->{HELPER}{RUNNING_PID}{fn} pid:$hash->{HELPER}{RUNNING_PID}{pid} $cause" ) ;
2020-12-16 14:40:38 +00:00
checkSendRetry ( $ name , 0 , $ queueStartFn ) ;
2020-01-19 14:21:41 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdateIfChanged ( $ hash , "Error" , $ cause ) ;
readingsBulkUpdateIfChanged ( $ hash , "Errorcode" , "none" ) ;
readingsBulkUpdate ( $ hash , "state" , "Error" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
delete ( $ hash - > { HELPER } { RUNNING_PID } ) ;
return ;
}
#############################################################################################
# liefert aus Unix Timestamp Beginn $bts und einer Differenz (Sekunden) das Beginn und
# Endedatum in der Form:
# Beginn: SS,MM,HH,Tag(01-31),Monat(01-12),Jahr(YYYY)
# Ende: SS,MM,HH,Tag(01-31),Monat(01-12),Jahr(YYYY)
#############################################################################################
2020-05-16 15:35:34 +00:00
sub DTfromStartandDiff {
2020-01-19 14:21:41 +00:00
my ( $ bts , $ diff ) = @ _ ;
my ( $ nbss , $ nbmm , $ nbhh , $ bmday , $ bmonth , $ byear , $ bWday , $ bYday , $ bisdst ) ;
my ( $ ness , $ nemm , $ nehh , $ emday , $ emonth , $ eyear , $ eWday , $ eYday , $ eisdst ) ;
( $ nbss , $ nbmm , $ nbhh , $ bmday , $ bmonth , $ byear , $ bWday , $ bYday , $ bisdst ) = localtime ( $ bts ) ;
$ nbss = sprintf ( "%02d" , $ nbss ) ;
$ nbmm = sprintf ( "%02d" , $ nbmm ) ;
$ nbhh = sprintf ( "%02d" , $ nbhh ) ;
$ bmday = sprintf ( "%02d" , $ bmday ) ;
$ bmonth = sprintf ( "%02d" , $ bmonth + 1 ) ;
$ byear += 1900 ;
( $ ness , $ nemm , $ nehh , $ emday , $ emonth , $ eyear , $ eWday , $ eYday , $ eisdst ) = localtime ( $ bts + $ diff ) ;
$ ness = sprintf ( "%02d" , $ ness ) ;
$ nemm = sprintf ( "%02d" , $ nemm ) ;
$ nehh = sprintf ( "%02d" , $ nehh ) ;
$ emday = sprintf ( "%02d" , $ emday ) ;
$ emonth = sprintf ( "%02d" , $ emonth + 1 ) ;
$ eyear += 1900 ;
return ( $ nbss , $ nbmm , $ nbhh , $ bmday , $ bmonth , $ byear , $ ness , $ nemm , $ nehh , $ emday , $ emonth , $ eyear ) ;
}
2020-02-19 20:24:56 +00:00
#############################################################################################
# extrahiere Key/Value Paare des VCALENDAR
# $vh = Referenz zum Kalenderdatenhash
#
# Ergebis Hash:
# {
# 'RECURRENCEID' => {
# '0' => undef,
# '2' => 'TZID=Europe/Berlin:20200222T191500 ',
# '1' => 'TZID=Europe/Berlin:20200219T191500 '
# },
# 'DTSTART' => {
# '0' => 'TZID=Europe/Berlin:20200218T191500',
# '1' => 'TZID=Europe/Berlin:20200219T191800',
# '2' => 'TZID=Europe/Berlin:20200222T192000'
# },
# 'EXDATES' => {
# '1' => undef,
# '2' => undef,
# '0' => '20200221T191500 20200220T191500 '
# },
# 'SEQUENCE' => {
# '0' => '4',
# '2' => '5',
# '1' => '5'
# }
# }
#
2020-02-22 11:02:26 +00:00
# Auswertung mit $data{SSCal}{$name}{vcalendar}{"$uid"}
2020-02-19 20:24:56 +00:00
#
#############################################################################################
2020-12-16 14:40:38 +00:00
sub extractIcal { ## no critic 'complexity'
2020-02-19 20:24:56 +00:00
my ( $ name , $ vh ) = @ _ ;
my $ hash = $ defs { $ name } ;
2020-02-22 11:02:26 +00:00
my % vcal ;
my % valm ;
2020-02-19 20:24:56 +00:00
my % icals ;
2020-02-22 11:02:26 +00:00
my ( $ uid , $ k , $ v , $ n ) ;
2020-02-19 20:24:56 +00:00
2020-02-22 11:02:26 +00:00
if ( $ vh - > { evt_ical } ) {
2020-05-16 15:35:34 +00:00
my @ ical = split ( /\015\012/x , $ vh - > { evt_ical } ) ;
2020-02-22 11:02:26 +00:00
my $ do = 0 ;
$ n = 0 ;
2020-04-25 19:53:14 +00:00
for ( @ ical ) {
2020-05-16 15:35:34 +00:00
if ( $ _ =~ m/^([-A-Z]*;)/x ) {
2020-02-22 11:02:26 +00:00
( $ k , $ v ) = split ( ";" , $ _ , 2 ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-02-22 11:02:26 +00:00
( $ k , $ v ) = split ( ":" , $ _ , 2 ) ;
}
2020-02-19 20:24:56 +00:00
2020-02-22 11:02:26 +00:00
$ v = "" if ( ! $ v ) ;
if ( "$k:$v" eq "BEGIN:VEVENT" ) { $ do = 1 ; } ;
if ( "$k:$v" eq "END:VEVENT" ) { $ do = 0 ; $ n + + ; } ;
if ( $ do ) {
$ vcal { $ n } { UID } = $ v if ( $ k eq "UID" ) ;
$ vcal { $ n } { SEQUENCE } = $ v if ( $ k eq "SEQUENCE" ) ;
if ( $ k eq "DTSTART" ) {
2020-05-16 15:35:34 +00:00
$ v = icalTimecheck ( $ name , $ v ) ;
2020-02-22 11:02:26 +00:00
$ vcal { $ n } { DTSTART } = $ v ;
}
if ( $ k eq "DTEND" ) {
2020-05-16 15:35:34 +00:00
$ v = icalTimecheck ( $ name , $ v ) ;
2020-02-22 11:02:26 +00:00
$ vcal { $ n } { DTEND } = $ v ;
}
if ( $ k eq "RECURRENCE-ID" ) {
2020-05-16 15:35:34 +00:00
$ v = icalTimecheck ( $ name , $ v ) ;
2020-02-22 11:02:26 +00:00
$ vcal { $ n } { RECURRENCEID } = $ v ;
}
if ( $ k eq "EXDATE" ) {
2020-05-16 15:35:34 +00:00
$ v = icalTimecheck ( $ name , $ v ) ;
2020-02-22 11:02:26 +00:00
$ vcal { $ n } { EXDATES } . = $ v . " " ;
}
}
}
}
$ n = 0 ;
while ( $ vh - > { evt_notify_setting } [ $ n ] ) {
2020-04-25 19:53:14 +00:00
for ( keys % { $ vh - > { evt_notify_setting } [ $ n ] } ) {
2020-04-25 17:52:39 +00:00
if ( $ _ eq "recurrence-id" ) {
2020-05-16 15:35:34 +00:00
$ valm { $ n } { RECURRENCEID } = icalTimecheck ( $ name , $ vh - > { evt_notify_setting } [ $ n ] { $ _ } ) ;
2020-04-25 17:52:39 +00:00
}
if ( $ _ eq "time_value" ) {
$ valm { $ n } { TIMEVALUE } = $ vh - > { evt_notify_setting } [ $ n ] { $ _ } ;
}
}
$ n + + ;
2020-02-19 20:24:56 +00:00
}
$ n = 0 ;
2020-02-22 11:02:26 +00:00
# VCALENDER Einträge konsolidieren
while ( $ vcal { $ n } ) {
2020-04-25 17:52:39 +00:00
$ uid = $ vcal { $ n } { UID } ;
$ icals { $ uid } { SEQUENCE } { $ n } = $ vcal { $ n } { SEQUENCE } ;
$ icals { $ uid } { DTSTART } { $ n } = $ vcal { $ n } { DTSTART } ;
2020-02-22 11:02:26 +00:00
$ icals { $ uid } { DTEND } { $ n } = $ vcal { $ n } { DTEND } ;
2020-05-16 15:35:34 +00:00
$ icals { $ uid } { EXDATES } { $ n } = trim ( $ vcal { $ n } { EXDATES } ) ;
2020-04-25 17:52:39 +00:00
$ icals { $ uid } { RECURRENCEID } { $ n } = $ vcal { $ n } { RECURRENCEID } ;
$ n + + ;
2020-02-19 20:24:56 +00:00
}
2020-02-22 11:02:26 +00:00
$ n = 0 ;
# VALARM Einträge konsolidieren
$ uid = $ vh - > { ical_uid } ;
while ( $ valm { $ n } ) {
2020-04-25 17:52:39 +00:00
$ icals { $ uid } { VALM } { RECURRENCEID } { $ n } = $ valm { $ n } { RECURRENCEID } ;
$ icals { $ uid } { VALM } { TIMEVALUE } { $ n } = $ valm { $ n } { TIMEVALUE } ;
$ n + + ;
2020-02-22 11:02:26 +00:00
}
2020-02-23 08:34:54 +00:00
$ data { SSCal } { $ name } { vcalendar } = \ % icals ; # Achtung: bei asynch Mode ist $data{SSCal}{$name}{vcalendar} nur im BlockingCall verfügbar !!
2020-02-19 20:24:56 +00:00
2020-02-22 11:02:26 +00:00
Log3 ( $ name , 5 , "$name - VCALENDAR extract of UID \"$uid\":\n" . Dumper $ data { SSCal } { $ name } { vcalendar } { "$uid" } ) ;
2020-02-19 20:24:56 +00:00
return ;
}
#############################################################################################
2020-05-16 15:35:34 +00:00
# Checked und korrigiert Zeitformate aus VCALENDAR um sie mit API-Werten vergleichbar
2020-02-19 20:24:56 +00:00
# zu machen
#############################################################################################
2020-05-16 15:35:34 +00:00
sub icalTimecheck {
2020-04-25 17:52:39 +00:00
my $ name = shift ;
my $ v = shift ;
2020-02-19 20:24:56 +00:00
2020-04-25 17:52:39 +00:00
return if ( ! $ v ) ;
2020-02-22 11:02:26 +00:00
2020-04-25 17:52:39 +00:00
my ( $ sec , $ min , $ hour , $ mday , $ month , $ year , $ zulu , $ tstamp , $ d , $ t , $ isdst , $ tz ) ;
2020-02-19 20:24:56 +00:00
$ zulu = 0 ;
2020-04-25 17:52:39 +00:00
$ v = ( split ( ":" , $ v ) ) [ - 1 ] if ( $ v =~ /:/x ) ;
$ zulu = 3600 if ( $ v =~ /Z$/x ) ; # Zulu-Zeit wenn EXDATE mit "Z" endet -> +1 Stunde
2020-02-19 20:24:56 +00:00
( $ d , $ t ) = split ( "T" , $ v ) ;
$ year = substr ( $ d , 0 , 4 ) ;
$ month = substr ( $ d , 4 , 2 ) ;
$ mday = substr ( $ d , 6 , 2 ) ;
if ( $ t ) {
2020-04-25 17:52:39 +00:00
$ hour = substr ( $ t , 0 , 2 ) ;
$ min = substr ( $ t , 2 , 2 ) ;
$ sec = substr ( $ t , 4 , 2 ) ;
$ t = $ hour . $ min . $ sec ;
2020-12-16 14:40:38 +00:00
}
else {
2020-04-25 17:52:39 +00:00
$ hour = "00" ;
$ min = "00" ;
$ sec = "00" ;
2020-02-19 20:24:56 +00:00
}
2020-05-16 15:35:34 +00:00
eval { $ tstamp = fhemTimeLocal ( $ sec , $ min , $ hour , $ mday , $ month - 1 , $ year - 1900 ) ;
1 ;
} or do {
my $ err = ( split ( " at" , $@ ) ) [ 0 ] ;
Log3 ( $ name , 3 , "$name - ERROR - invalid date/time format in 'icalTimecheck' detected: $err " ) ;
} ;
2020-04-25 17:52:39 +00:00
$ isdst = ( localtime ( $ tstamp ) ) [ 8 ] ;
2020-02-19 20:24:56 +00:00
$ zulu = 7200 if ( $ isdst && $ zulu ) ; # wenn Sommerzeit und Zulu-Zeit -> +1 Stunde
$ tstamp += $ zulu ;
$ v = strftime "%Y%m%dT%H%M%S" , localtime ( $ tstamp ) ;
return $ v ;
}
2020-01-19 14:21:41 +00:00
#############################################################################################
# Ist Event bevorstehend ?
# Rückkehrwert 1 wenn aktueller Timestamp $ts vor Alarmzeit $ats und vor Startzeit $bts,
# sonst 0
#############################################################################################
2020-05-16 15:35:34 +00:00
sub isUpcoming {
2020-01-19 14:21:41 +00:00
my ( $ ts , $ ats , $ bts ) = @ _ ;
if ( $ ats ) {
return $ ts < $ ats ? 1 : 0 ;
2020-12-16 14:40:38 +00:00
}
else {
2020-01-19 14:21:41 +00:00
return $ ts < $ bts ? 1 : 0 ;
}
}
#############################################################################################
# Ist Event Alarmzeit erreicht ?
# Rückkehrwert 1 wenn aktueller Timestamp $ts zwischen Alarmzeit $ats und Startzeit $bts,
# sonst 0
#############################################################################################
2020-05-16 15:35:34 +00:00
sub isAlarmed {
2020-01-19 14:21:41 +00:00
my ( $ ts , $ ats , $ bts ) = @ _ ;
2020-04-25 17:52:39 +00:00
return $ ats ? ( ( $ ats <= $ ts && $ ts < $ bts ) ? 1 : 0 ) : 0 ;
2020-01-19 14:21:41 +00:00
}
#############################################################################################
# Ist Event gestartet ?
# Rückkehrwert 1 wenn aktueller Timestamp $ts zwischen Startzeit $bts und Endezeit $ets,
# sonst 0
#############################################################################################
2020-05-16 15:35:34 +00:00
sub isStarted {
2020-01-19 14:21:41 +00:00
my ( $ ts , $ bts , $ ets ) = @ _ ;
return 0 unless ( $ bts ) ;
return 0 if ( $ ts < $ bts ) ;
if ( defined ( $ ets ) ) {
return 0 if ( $ ts >= $ ets ) ;
}
return 1 ;
}
#############################################################################################
# Ist Event beendet ?
# Rückkehrwert 1 wenn aktueller Timestamp $ts größer Endezeit $ets,
# sonst 0
#############################################################################################
2020-05-16 15:35:34 +00:00
sub isEnded {
2020-01-19 14:21:41 +00:00
my ( $ ts , $ ets ) = @ _ ;
return 0 unless ( $ ets && $ ts ) ;
2020-04-25 17:52:39 +00:00
return $ ets <= $ ts ? 1 : 0 ;
2020-01-19 14:21:41 +00:00
}
#############################################################################################
# check SID
#############################################################################################
2020-05-16 15:35:34 +00:00
sub checkSID {
2020-12-16 14:40:38 +00:00
my $ name = shift ;
my $ hash = $ defs { $ name } ;
2020-01-19 14:21:41 +00:00
# SID holen bzw. login
2020-12-16 14:40:38 +00:00
my $ subref = \ & calOp ;
2020-01-19 14:21:41 +00:00
if ( ! $ hash - > { HELPER } { SID } ) {
Log3 ( $ name , 3 , "$name - no session ID found - get new one" ) ;
2020-12-16 14:40:38 +00:00
login ( $ hash , $ data { SSCal } { $ name } { calapi } , $ subref , $ name , $ splitstr ) ;
2020-04-25 17:52:39 +00:00
return ;
2020-01-19 14:21:41 +00:00
}
2020-05-16 15:35:34 +00:00
return calOp ( $ name ) ;
2020-01-19 14:21:41 +00:00
}
#############################################################################################
# Start- und Endezeit ermitteln
#############################################################################################
2020-05-16 15:35:34 +00:00
sub timeEdge {
2020-01-19 14:21:41 +00:00
my ( $ name ) = @ _ ;
my $ hash = $ defs { $ name } ;
my ( $ error , $ t1 , $ t2 ) = ( "" , "" , "" ) ;
my ( $ mday , $ mon , $ year ) ;
my $ t = time ( ) ;
my $ corr = 86400 ; # Korrekturbetrag
my $ cutOlderDays = AttrVal ( $ name , "cutOlderDays" , 5 ) . "d" ;
my $ cutLaterDays = AttrVal ( $ name , "cutLaterDays" , 5 ) . "d" ;
# start of time window
2020-05-16 15:35:34 +00:00
( $ error , $ t1 ) = GetSecondsFromTimeSpec ( $ cutOlderDays ) ;
2020-01-19 14:21:41 +00:00
if ( $ error ) {
2020-04-25 17:52:39 +00:00
Log3 $ hash , 2 , "$name: attribute cutOlderDays: $error" ;
return ( $ error , "" , "" ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-04-25 17:52:39 +00:00
$ t1 = $ t - $ t1 ;
( undef , undef , undef , $ mday , $ mon , $ year , undef , undef , undef ) = localtime ( $ t1 ) ; # Istzeit Ableitung
$ t1 = fhemTimeLocal ( 00 , 00 , 00 , $ mday , $ mon , $ year ) ;
2020-01-19 14:21:41 +00:00
}
# end of time window
2020-05-16 15:35:34 +00:00
( $ error , $ t2 ) = GetSecondsFromTimeSpec ( $ cutLaterDays ) ;
2020-01-19 14:21:41 +00:00
if ( $ error ) {
2020-04-25 17:52:39 +00:00
Log3 $ hash , 2 , "$name: attribute cutLaterDays: $error" ;
return ( $ error , "" , "" ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-04-25 17:52:39 +00:00
$ t2 = $ t + $ t2 + $ corr ;
( undef , undef , undef , $ mday , $ mon , $ year , undef , undef , undef ) = localtime ( $ t2 ) ; # Istzeit Ableitung
$ t2 = fhemTimeLocal ( 00 , 00 , 00 , $ mday , $ mon , $ year ) ;
2020-01-19 14:21:41 +00:00
}
return ( "" , $ t1 , $ t2 ) ;
}
#############################################################################################
# Erinnerungstermin relativ zur Beginnzeit $bts ermitteln
# Alarmformat: 'time_value' => '-P2D'
# 'time_value' => '-PT1H'
# 'time_value' => '-PT5M'
# 'time_value' => 'PT0S'
2020-01-31 15:27:30 +00:00
# 'time_value' => 'PT6H'
# 'time_value' => '-P1DT15H'
2020-01-19 14:21:41 +00:00
#
# Rückgabe: $uts: Unix-Timestamp
# $ts: Timstamp als YYYY-MM-DD HH:MM:SS
#
#############################################################################################
2020-05-16 15:35:34 +00:00
sub evtNotTime {
2020-01-19 14:21:41 +00:00
my ( $ name , $ tv , $ bts ) = @ _ ;
my $ hash = $ defs { $ name } ;
my ( $ uts , $ ts ) = ( "" , "" ) ;
my ( $ corr ) ;
return ( "" , "" ) if ( ! $ tv || ! $ bts ) ;
2020-04-25 17:52:39 +00:00
if ( $ tv =~ /^-P(\d)+D$/x ) {
2020-01-31 15:27:30 +00:00
$ corr = - 1 * $ 1 * 86400 ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ tv =~ /^-PT(\d+)H$/x ) {
2020-01-31 15:27:30 +00:00
$ corr = - 1 * $ 1 * 3600 ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ tv =~ /^-PT(\d+)M$/x ) {
2020-01-31 15:27:30 +00:00
$ corr = - 1 * $ 1 * 60 ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ tv =~ /^PT(\d+)S$/x ) {
2020-01-19 14:21:41 +00:00
$ corr = $ 1 ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ tv =~ /^PT(\d+)M$/x ) {
2020-01-31 15:27:30 +00:00
$ corr = $ 1 * 60 ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ tv =~ /^PT(\d+)H$/x ) {
2020-01-31 15:27:30 +00:00
$ corr = $ 1 * 3600 ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ tv =~ /^-P(\d)+DT(\d+)H$/x ) {
2020-01-31 15:27:30 +00:00
$ corr = - 1 * ( $ 1 * 86400 + $ 2 * 3600 ) ;
2020-01-19 14:21:41 +00:00
}
if ( defined $ corr ) {
2020-01-31 15:27:30 +00:00
$ uts = $ bts + $ corr ;
2020-01-19 14:21:41 +00:00
$ ts = FmtDateTime ( $ uts ) ;
}
return ( $ uts , $ ts ) ;
}
#############################################################################################
# Unix timestamp aus Zeitdifferenz berechnen
#############################################################################################
2020-05-16 15:35:34 +00:00
sub GetSecondsFromTimeSpec {
2020-01-19 14:21:41 +00:00
my ( $ tspec ) = @ _ ;
# days
2020-04-25 17:52:39 +00:00
if ( $ tspec =~ m/^([0-9]+)d$/x ) {
2020-01-19 14:21:41 +00:00
return ( "" , $ 1 * 86400 ) ;
}
# seconds
2020-04-25 17:52:39 +00:00
if ( $ tspec =~ m/^([0-9]+)s?$/x ) {
2020-01-19 14:21:41 +00:00
return ( "" , $ 1 ) ;
}
# D:HH:MM:SS
2020-04-25 17:52:39 +00:00
if ( $ tspec =~ m/^([0-9]+):([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/x ) {
2020-01-19 14:21:41 +00:00
return ( "" , $ 4 + 60 * ( $ 3 + 60 * ( $ 2 + 24 * $ 1 ) ) ) ;
}
# HH:MM:SS
2020-04-25 17:52:39 +00:00
if ( $ tspec =~ m/^([0-9]+):([0-5][0-9]):([0-5][0-9])$/x ) {
2020-01-19 14:21:41 +00:00
return ( "" , $ 3 + 60 * ( $ 2 + ( 60 * $ 1 ) ) ) ;
}
# HH:MM
2020-04-25 17:52:39 +00:00
if ( $ tspec =~ m/^([0-9]+):([0-5][0-9])$/x ) {
2020-01-19 14:21:41 +00:00
return ( "" , 60 * ( $ 2 + 60 * $ 1 ) ) ;
}
return ( "Wrong time specification $tspec" , undef ) ;
}
################################################################
# Kalendername aus Kalender-Id liefern
################################################################
2020-05-16 15:35:34 +00:00
sub getCalFromId {
2020-01-19 14:21:41 +00:00
my ( $ hash , $ cid ) = @ _ ;
my $ cal = "" ;
2020-05-16 15:35:34 +00:00
$ cid = trim ( $ cid ) ;
2020-01-19 14:21:41 +00:00
2020-04-25 19:53:14 +00:00
for my $ calname ( keys % { $ hash - > { HELPER } { CALENDARS } } ) {
2020-01-19 14:21:41 +00:00
my $ oid = $ hash - > { HELPER } { CALENDARS } { "$calname" } { id } ;
next if ( ! $ oid ) ;
2020-05-16 15:35:34 +00:00
$ oid = trim ( $ oid ) ;
2020-01-19 14:21:41 +00:00
if ( $ oid eq $ cid ) {
$ cal = $ calname ;
last ;
}
}
return $ cal ;
}
################################################################
# addiert Anzahl ($n) Sekunden ($s) zu $t1
################################################################
2020-05-16 15:35:34 +00:00
sub plusNSeconds {
2020-01-19 14:21:41 +00:00
my ( $ t1 , $ s , $ n ) = @ _ ;
$ n = 1 unless defined ( $ n ) ;
my $ t2 = $ t1 + $ n * $ s ;
return $ t2 ;
}
#############################################################################################
# Datum/Zeit extrahieren
# Eingangsformat: TZID=Europe/Berlin:20191216T133000 oder
# 20191216T133000
# Rückgabe: invalid, Zeitzone, Date(YYYY-MM-DD), Time (HH:MM:SS), UnixTimestamp
# (invalid =1 wenn Datum ungültig, ist nach RFC 5545 diese Wiederholung
# zu ignorieren und auch nicht zu zählen !)
2020-03-02 23:36:47 +00:00
# $dtstart: man benötigt originales DTSTART für den Vergleich bei Recurring Terminen
2020-01-19 14:21:41 +00:00
#############################################################################################
2020-05-17 20:44:49 +00:00
sub explodeDateTime { ## no critic 'complexity'
2020-02-19 20:24:56 +00:00
my ( $ hash , $ dt , $ isallday , $ uid , $ dtstart ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my ( $ tz , $ t ) = ( "" , "" ) ;
my ( $ d , $ tstamp ) = ( "" , 0 ) ;
my $ invalid = 0 ;
my $ corrsec = 0 ; # Korrektursekunde
my $ excl = 0 ; # 1 wenn der Eintrag exkludiert werden soll
2020-01-31 15:27:30 +00:00
2020-02-19 20:24:56 +00:00
my ( $ sec , $ min , $ hour , $ mday , $ month , $ year , $ checkbegin , $ changed , $ changet , $ changedt , $ z ) ;
return ( $ invalid , $ tz , $ d , $ t , $ tstamp , $ excl ) if ( ! $ dt ) ;
$ corrsec = 1 if ( $ isallday ) ; # wenn Ganztagsevent, Endetermin um 1 Sekunde verkürzen damit Termin am selben Tag 23:59:59 endet (sonst Folgetag 00:00:00)
2020-02-09 10:21:37 +00:00
2020-04-25 17:52:39 +00:00
if ( $ dt =~ /^TZID=.*$/x ) {
2020-01-19 14:21:41 +00:00
( $ tz , $ dt ) = split ( ":" , $ dt ) ;
$ tz = ( split ( "=" , $ tz ) ) [ 1 ] ;
}
2020-02-19 20:24:56 +00:00
if ( $ dtstart ) {
2020-04-25 17:52:39 +00:00
$ dtstart = ( split ( ":" , $ dtstart ) ) [ - 1 ] if ( $ dtstart =~ /:/x ) ;
2020-02-19 20:24:56 +00:00
# check ob recurring date excluded werden muss (Serienelement gelöscht)
2020-02-22 11:02:26 +00:00
my $ exdates = $ data { SSCal } { $ name } { vcalendar } { "$uid" } { EXDATES } { 0 } ;
2020-02-19 20:24:56 +00:00
my % seen ;
if ( $ exdates ) {
my @ exd = split ( " " , $ exdates ) ;
2020-05-17 20:44:49 +00:00
# grep { !$seen{$_}++ } @exd;
2020-05-21 06:11:31 +00:00
for ( @ exd ) { $ seen { $ _ } + + if ( ! $ seen { $ _ } ) } ;
2020-02-19 20:24:56 +00:00
}
$ excl = 1 if ( $ seen { $ dtstart } ) ; # check erfolgreich -> exclude recurring date weil (Serienelement gelöscht)
# prüfen ob Serienelement verändert wurde
2020-12-16 14:40:38 +00:00
if ( $ dt eq $ dtstart ) {
$ checkbegin = 1
}
else {
$ checkbegin = 0
} ;
2020-02-19 20:24:56 +00:00
if ( $ checkbegin ) {
# prüfen ob DTSTART verändert
2020-04-25 19:53:14 +00:00
for ( keys % { $ data { SSCal } { $ name } { vcalendar } { "$uid" } { RECURRENCEID } } ) {
2020-02-22 11:02:26 +00:00
next if ( ! $ data { SSCal } { $ name } { vcalendar } { "$uid" } { RECURRENCEID } { $ _ } ) ;
$ z = $ _ if ( $ data { SSCal } { $ name } { vcalendar } { "$uid" } { RECURRENCEID } { $ _ } eq $ dtstart ) ;
2020-02-19 20:24:56 +00:00
}
if ( $ z ) {
2020-02-22 11:02:26 +00:00
$ changedt = $ data { SSCal } { $ name } { vcalendar } { "$uid" } { DTSTART } { $ z } ;
2020-05-16 15:35:34 +00:00
my ( $ y , $ m , $ dd , $ h , $ mi , $ s ) = $ changedt =~ /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/x ;
2020-12-16 14:40:38 +00:00
$ changed = $ y . "-" . $ m . "-" . $ dd ; # einmalig geändertes Datum
$ changet = $ h . ":" . $ mi . ":" . $ s ; # einmalig geänderte Zeit
2020-02-19 20:24:56 +00:00
}
2020-12-16 14:40:38 +00:00
}
else { # prüfen ob DTEND verändert
2020-04-25 19:53:14 +00:00
for ( keys % { $ data { SSCal } { $ name } { vcalendar } { "$uid" } { RECURRENCEID } } ) {
2020-02-22 11:02:26 +00:00
next if ( ! $ data { SSCal } { $ name } { vcalendar } { "$uid" } { RECURRENCEID } { $ _ } ) ;
$ z = $ _ if ( $ data { SSCal } { $ name } { vcalendar } { "$uid" } { RECURRENCEID } { $ _ } eq $ dtstart ) ;
2020-02-19 20:24:56 +00:00
}
if ( $ z ) {
2020-02-22 11:02:26 +00:00
$ changedt = $ data { SSCal } { $ name } { vcalendar } { "$uid" } { DTEND } { $ z } ;
2020-05-16 15:35:34 +00:00
my ( $ y , $ m , $ dd , $ h , $ mi , $ s ) = $ changedt =~ /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/x ;
2020-12-16 14:40:38 +00:00
$ changed = $ y . "-" . $ m . "-" . $ dd ; # einmalig geändertes Datum
$ changet = $ h . ":" . $ mi . ":" . $ s ; # einmalig geänderte Zeit
2020-02-19 20:24:56 +00:00
}
}
}
2020-01-19 14:21:41 +00:00
( $ d , $ t ) = split ( "T" , $ dt ) ;
$ year = substr ( $ d , 0 , 4 ) ;
$ month = substr ( $ d , 4 , 2 ) ;
$ mday = substr ( $ d , 6 , 2 ) ;
$ d = $ year . "-" . $ month . "-" . $ mday ;
if ( $ t ) {
$ hour = substr ( $ t , 0 , 2 ) ;
$ min = substr ( $ t , 2 , 2 ) ;
$ sec = substr ( $ t , 4 , 2 ) ;
$ t = $ hour . ":" . $ min . ":" . $ sec ;
2020-12-16 14:40:38 +00:00
}
else {
2020-01-19 14:21:41 +00:00
$ hour = "00" ;
$ min = "00" ;
$ sec = "00" ;
$ t = "00:00:00" ;
}
2020-05-16 15:35:34 +00:00
unless ( ( $ d . " " . $ t ) =~ /^(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})/x ) {
2020-01-19 14:21:41 +00:00
Log3 ( $ name , 2 , "$name - ERROR - invalid DateTime format for explodeDateTime: $d $t" ) ;
}
2020-02-09 10:21:37 +00:00
if ( $ corrsec ) { # Termin um 1 Sekunde verkürzen damit Termin am selben Tag 23:59:59 endet (sonst Folgetag 00:00:00)
2020-05-16 15:35:34 +00:00
eval { $ tstamp = fhemTimeLocal ( $ sec , $ min , $ hour , $ mday , $ month - 1 , $ year - 1900 ) ;
1 ;
} or do {
my $ err = ( split ( " at" , $@ ) ) [ 0 ] ;
Log3 ( $ name , 3 , "$name - WARNING - invalid date/time format detected: $err. It will be ignored." ) ;
$ invalid = 1 ;
} ;
if ( ! $ invalid ) {
$ tstamp -= $ corrsec ;
my $ nt = FmtDateTime ( $ tstamp ) ;
( $ year , $ month , $ mday , $ hour , $ min , $ sec ) = $ nt =~ /^(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})/x ;
$ d = $ year . "-" . $ month . "-" . $ mday ;
$ t = $ hour . ":" . $ min . ":" . $ sec ;
}
2020-02-09 10:21:37 +00:00
}
2020-05-16 15:35:34 +00:00
eval { $ tstamp = fhemTimeLocal ( $ sec , $ min , $ hour , $ mday , $ month - 1 , $ year - 1900 ) ;
1 ;
} or do {
2020-02-19 20:24:56 +00:00
my $ err = ( split ( " at" , $@ ) ) [ 0 ] ;
Log3 ( $ name , 3 , "$name - WARNING - invalid format of recurring event: $err. It will be ignored due to RFC 5545 standard." ) ;
2020-01-19 14:21:41 +00:00
$ invalid = 1 ;
2020-05-16 15:35:34 +00:00
} ;
2020-01-19 14:21:41 +00:00
2020-04-25 17:52:39 +00:00
$ d = $ changed // $ d ; # mit einmalig geänderten Datum ersetzen
$ t = $ changet // $ t ; # mit einmalig geänderter Zeit ersetzen
2020-02-19 20:24:56 +00:00
return ( $ invalid , $ tz , $ d , $ t , $ tstamp , $ excl ) ;
2020-01-19 14:21:41 +00:00
}
2020-02-02 18:47:04 +00:00
#############################################################################################
# Kalenderliste als HTML-Tabelle zurückgeben
#############################################################################################
2020-05-16 15:35:34 +00:00
sub calAsHtml { ## no critic 'complexity'
2020-02-08 11:45:46 +00:00
my ( $ name , $ FW_wname ) = @ _ ;
my $ hash = $ defs { $ name } ;
my $ lang = AttrVal ( "global" , "language" , "EN" ) ;
my $ mi = AttrVal ( $ name , "tableColumnMap" , "icon" ) ;
2020-02-12 16:28:05 +00:00
my ( $ symbol , $ begin , $ begind , $ begint , $ end , $ endd , $ endt , $ summary , $ location , $ status , $ desc , $ gps , $ gpsa , $ gpsc ) ;
my ( $ di , $ cal , $ completion , $ tz , $ dleft , $ dleftlong , $ weekday , $ edleft , $ id , $ isallday ) ;
2020-02-13 17:45:33 +00:00
my ( $ colSymbolAlign , $ colBeginAlign , $ colEndAlign , $ colDayAlign , $ colDLongAlign , $ colWeekdayAlign , $ colTzAlign , $ colSummaryAlign , $ colDescAlign , $ colStatusAlign , $ colCompAlign , $ colLocAlign , $ colMapAlign , $ colCalAlign , $ colIdAlign ) ;
2020-02-09 10:21:37 +00:00
# alle Readings in Array einlesen
my @ allrds = keys % { $ defs { $ name } { READINGS } } ;
2020-02-02 18:47:04 +00:00
2020-02-08 11:45:46 +00:00
# Sprachsteuerung
2020-02-05 23:12:14 +00:00
my $ de = 0 ;
if ( $ lang eq "DE" ) { $ de = 1 } ;
2020-02-03 14:16:18 +00:00
2020-02-08 11:45:46 +00:00
# Entscheidung ob Tabelle für Small Screen optimiert
my $ small = 0 ;
2020-02-11 09:11:52 +00:00
if ( $ FW_wname && $ hash - > { HELPER } { tableSpecs } { smallScreenStyles } ) { # Aufruf durch FHEMWEB und smallScreen-Eigenschaft gesetzt
2020-02-08 11:45:46 +00:00
my % specs ;
my $ FW_style = AttrVal ( $ FW_wname , "stylesheetPrefix" , "default" ) ;
2020-02-11 09:11:52 +00:00
my @ scspecs = split ( "," , $ hash - > { HELPER } { tableSpecs } { smallScreenStyles } ) ; # Eigenschaft smallScreen in Array lesen
2020-05-17 20:44:49 +00:00
# grep { !$specs{$_}++ } @scspecs;
2020-05-21 06:11:31 +00:00
for ( @ scspecs ) { $ specs { $ _ } + + if ( ! $ specs { $ _ } ) } ;
2020-05-17 20:44:49 +00:00
$ small = 1 if ( $ specs { $ FW_style } ) ; # Tabelle für small-Style anpassen
2020-02-08 11:45:46 +00:00
}
# Auswahl der darzustellenden Tabellenfelder
2020-02-03 14:16:18 +00:00
my % seen ;
2020-02-08 09:18:56 +00:00
my @ cof = split ( "," , AttrVal ( $ name , "tableFields" , "Begin,End,Summary,Status,Location" ) ) ;
2020-02-13 12:05:29 +00:00
grep { ! $ seen { $ _ } + + } @ cof ;
# Gestaltung Headerzeile
2020-05-16 15:35:34 +00:00
my $ nohead = 0 ; # Unterdrückung Anzeige Headerzeile: 0 - nein, 1 - Ja
2020-05-17 20:44:49 +00:00
eval { $ nohead = evalTableSpecs ( {
href = > $ hash ,
def = > $ nohead ,
base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { noHeader } ,
bnr = > "" ,
readref = > \ @ allrds ,
rdtype = > "string"
}
) ; 1 ;
2020-05-16 15:35:34 +00:00
} or do {
Log3 ( $ name , 1 , "$name - Syntax error in attribute \"tableSpecs\" near \"cellStyle\": $@" ) ;
} ;
my $ headalign = "center" ; # Ausrichtung der Headerzeile, default: center
2020-05-17 20:44:49 +00:00
eval { $ headalign = evalTableSpecs ( {
href = > $ hash ,
def = > $ headalign ,
base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { headerAlign } ,
bnr = > "" ,
readref = > \ @ allrds ,
rdtype = > "string"
}
) ; 1 ;
2020-05-16 15:35:34 +00:00
} or do {
Log3 ( $ name , 1 , "$name - Syntax error in attribute \"tableSpecs\" near \"cellStyle\": $@" ) ;
} ;
$ headalign = "cal" . $ headalign ;
2020-02-02 18:47:04 +00:00
2020-02-13 17:45:33 +00:00
# Tabelle erstellen
my $ out = "<html>" ;
2020-02-05 23:12:14 +00:00
$ out . = "<style>TD.cal {padding-left:10px; padding-right:10px; border-spacing:5px; margin-left:auto; margin-right:auto;}</style>" ;
$ out . = "<style>TD.calbold {font-weight: bold;} </style>" ;
$ out . = "<style>TD.calright {text-align: right;} </style>" ;
2020-02-07 14:59:41 +00:00
$ out . = "<style>TD.calleft {text-align: left;} </style>" ;
2020-02-05 23:12:14 +00:00
$ out . = "<style>TD.calcenter {text-align: center;} </style>" ;
$ out . = "<style>TD.calw150 {width: 150px;} </style>" ;
2020-02-02 18:47:04 +00:00
2020-02-13 17:45:33 +00:00
# Wenn Table class=block alleine steht, zieht es bei manchen Styles die Ausgabe auf 100% Seitenbreite
# lässt sich durch einbetten in eine zusätzliche Table roomoverview eindämmen
$ out . = "<table class='roomoverview'>" ;
$ out . = "<tr>" ;
$ out . = "<td style='text-align: center; padding-left:1px; padding-right:1px; margin:0px'>" ;
2020-02-05 23:12:14 +00:00
$ out . = "<table class='block'>" ;
2020-02-07 21:20:54 +00:00
2020-02-13 17:45:33 +00:00
# Tabellenheader
2020-02-13 12:05:29 +00:00
if ( ! $ nohead ) {
2020-04-25 17:52:39 +00:00
$ out . = "<tr class='odd'>" ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Symbol' : 'Symbol' ) . " </td>" if ( $ seen { Symbol } ) ;
if ( $ small ) { # nur ein Datumfeld umbrechbar
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Start' : 'Begin' ) . " </td>" if ( $ seen { Begin } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Ende' : 'End' ) . " </td>" if ( $ seen { End } ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-04-25 17:52:39 +00:00
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Start' : 'Begin' ) . " </td>" if ( $ seen { Begin } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? '----' : '----' ) . " </td>" if ( $ seen { Begin } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Ende' : 'End' ) . " </td>" if ( $ seen { End } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? '----' : '----' ) . " </td>" if ( $ seen { End } ) ;
}
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Resttage' : 'Days left' ) . " </td>" if ( $ seen { DaysLeft } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Terminziel' : 'Goal' ) . " </td>" if ( $ seen { DaysLeftLong } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Wochentag' : 'Weekday' ) . " </td>" if ( $ seen { Weekday } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Zeitzone' : 'Timezone' ) . " </td>" if ( $ seen { Timezone } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Zusammenfassung' : 'Summary' ) . " </td>" if ( $ seen { Summary } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Beschreibung' : 'Description' ) . " </td>" if ( $ seen { Description } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Status' : 'State' ) . " </td>" if ( $ seen { Status } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Erfüllung (%)' : 'Completion (%)' ) . " </td>" if ( $ seen { Completion } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Ort' : 'Location' ) . " </td>" if ( $ seen { Location } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Karte' : 'Map' ) . " </td>" if ( $ seen { Map } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'Kalender' : 'Calendar' ) . " </td>" if ( $ seen { Calendar } ) ;
$ out . = "<td class='cal calbold $headalign'> " . ( ( $ de ) ? 'ID' : 'ID' ) . " </td>" if ( $ seen { EventId } ) ;
$ out . = "</tr>" ;
2020-02-07 21:20:54 +00:00
}
2020-02-02 18:47:04 +00:00
my $ maxbnr ;
2020-04-25 19:53:14 +00:00
for my $ key ( keys % { $ defs { $ name } { READINGS } } ) {
2020-05-16 15:35:34 +00:00
my ( $ bno ) = $ key =~ /^(\d+)_\d+_EventId$/x ;
next if ( ! defined $ bno ) ;
$ maxbnr = $ bno if ( ! $ maxbnr || $ bno > $ maxbnr ) ;
2020-02-02 18:47:04 +00:00
}
2020-02-11 09:11:52 +00:00
2020-02-04 20:19:15 +00:00
return "" if ( ! defined $ maxbnr ) ;
2020-02-11 09:11:52 +00:00
my $ l = length ( $ maxbnr ) ;
2020-02-02 18:47:04 +00:00
my $ k ;
for ( $ k = 0 ; $ k <= $ maxbnr ; $ k + + ) {
2020-02-11 09:11:52 +00:00
my $ bnr = sprintf ( "%0$l.0f" , $ k ) ; # Prestring erstellen
last if ( ! ReadingsVal ( $ name , $ bnr . "_98_EventId" , "" ) ) ; # keine Ausgabe wenn es keine EventId mit Blocknummer 0 gibt -> kein Event/Aufgabe vorhanden
2020-02-02 18:47:04 +00:00
2020-04-25 17:52:39 +00:00
( $ begind , $ begint , $ endd , $ endt , $ gps ) = ( "" , "" , "" , "" , "" ) ;
# Readings auslesen
2020-02-11 09:11:52 +00:00
$ summary = ReadingsVal ( $ name , $ bnr . "_01_Summary" , "" ) ;
$ desc = ReadingsVal ( $ name , $ bnr . "_03_Description" , "" ) ;
$ begin = ReadingsVal ( $ name , $ bnr . "_05_Begin" , "" ) ;
$ end = ReadingsVal ( $ name , $ bnr . "_10_End" , "" ) ;
2020-04-25 17:52:39 +00:00
$ tz = ReadingsVal ( $ name , $ bnr . "_15_Timezone" , "" ) ;
2020-02-11 09:11:52 +00:00
$ status = ReadingsVal ( $ name , $ bnr . "_17_Status" , "" ) ;
$ dleft = ReadingsVal ( $ name , $ bnr . "_20_daysLeft" , "" ) ;
2020-02-11 23:05:45 +00:00
$ dleftlong = ReadingsVal ( $ name , $ bnr . "_25_daysLeftLong" , "" ) ;
2020-02-12 07:51:27 +00:00
$ weekday = ReadingsVal ( $ name , $ bnr . "_30_Weekday" , "" ) ;
2020-02-11 09:11:52 +00:00
$ location = ReadingsVal ( $ name , $ bnr . "_35_Location" , "" ) ;
$ gpsa = ReadingsVal ( $ name , $ bnr . "_40_gpsAddress" , "" ) ;
$ gpsc = ReadingsVal ( $ name , $ bnr . "_45_gpsCoordinates" , "" ) ;
2020-04-25 17:52:39 +00:00
$ completion = ReadingsVal ( $ name , $ bnr . "_85_percentComplete" , "" ) ;
2020-02-11 09:11:52 +00:00
$ cal = ReadingsVal ( $ name , $ bnr . "_90_calName" , "" ) ;
$ id = ReadingsVal ( $ name , $ bnr . "_98_EventId" , "" ) ;
$ isallday = ReadingsVal ( $ name , $ bnr . "_50_isAllday" , "" ) ;
2020-02-02 18:47:04 +00:00
2020-02-04 20:19:15 +00:00
if ( $ gpsc ) {
2020-04-25 17:52:39 +00:00
my $ micon ;
if ( $ mi eq "icon" ) {
2020-02-11 09:11:52 +00:00
# Karten-Icon auswählen
2020-04-25 17:52:39 +00:00
$ di = "it_i-net" ;
2020-05-17 20:44:49 +00:00
eval { $ micon = evalTableSpecs ( {
href = > $ hash ,
def = > $ di ,
base = > $ hash - > { HELPER } { tableSpecs } { columnMapIcon } ,
bnr = > $ bnr ,
readref = > \ @ allrds ,
rdtype = > "image"
}
) ; 1 ;
2020-05-16 15:35:34 +00:00
} or do {
Log3 ( $ name , 1 , "$name - Syntax error in attribute \"tableSpecs\" near \"columnMapIcon\": $@" )
2020-12-16 14:40:38 +00:00
} ;
}
elsif ( $ mi eq "data" ) {
$ micon = join ( " " , split ( "," , $ gpsc ) ) ;
}
elsif ( $ mi eq "text" ) { # Karten-Text auswählen
2020-04-25 17:52:39 +00:00
my $ dt = "link" ;
2020-05-17 20:44:49 +00:00
eval { $ micon = evalTableSpecs ( {
href = > $ hash ,
def = > $ dt ,
base = > $ hash - > { HELPER } { tableSpecs } { columnMapText } ,
bnr = > $ bnr ,
readref = > \ @ allrds ,
rdtype = > "string"
}
) ; 1 ;
2020-05-16 15:35:34 +00:00
} or do {
Log3 ( $ name , 1 , "$name - Syntax error in attribute \"tableSpecs\" near \"columnMapText\": $@" ) ;
2020-12-16 14:40:38 +00:00
} ;
}
else {
2020-04-25 17:52:39 +00:00
$ micon = "" ;
}
2020-02-04 20:19:15 +00:00
my ( $ lat , $ lng ) = split ( "," , $ gpsc ) ;
2020-02-04 19:35:38 +00:00
$ lat = ( split ( "=" , $ lat ) ) [ 1 ] ;
$ lng = ( split ( "=" , $ lng ) ) [ 1 ] ;
2020-04-25 17:52:39 +00:00
# Kartenanbieter auswählen
my $ up = "GoogleMaps" ;
2020-05-17 20:44:49 +00:00
eval { $ up = evalTableSpecs ( {
href = > $ hash ,
def = > $ up ,
base = > $ hash - > { HELPER } { tableSpecs } { columnMapProvider } ,
bnr = > $ bnr ,
readref = > \ @ allrds ,
rdtype = > "string"
}
) ; 1 ;
2020-05-16 15:35:34 +00:00
} or do {
Log3 ( $ name , 1 , "$name - Syntax error in attribute \"tableSpecs\" near \"columnMapProvider\": $@" ) ;
} ;
2020-04-25 17:52:39 +00:00
if ( $ up eq "GoogleMaps" ) { # Kartenprovider: Google Maps
$ gps = "<a href='https://www.google.de/maps/place/$gpsa/\@$lat,$lng' target='_blank'> $micon </a>" ;
2020-12-16 14:40:38 +00:00
}
elsif ( $ up eq "OpenStreetMap" ) {
2020-02-13 12:05:29 +00:00
$ gps = "<a href='https://www.openstreetmap.org/?mlat=$lat&mlon=$lng&zoom=14' target='_blank'> $micon </a>" ; # Kartenprovider: OpenstreetMap
}
2020-04-25 17:52:39 +00:00
}
2020-02-04 15:35:22 +00:00
2020-12-16 14:40:38 +00:00
if ( $ begin ne "" ) { # Datum sprachabhängig konvertieren bzw. heute/morgen setzen
my ( $ ny , $ nm , $ nd , undef ) = split ( /[\s-]/x , TimeNow ( ) ) ; # Jetzt
2020-05-16 15:35:34 +00:00
my ( $ by , $ bm , $ bd , $ bt ) = split ( /[\s-]/x , $ begin ) ;
my ( $ ey , $ em , $ ed , $ et ) = split ( /[\s-]/x , $ end ) ;
2020-02-05 23:12:14 +00:00
my $ ntimes = fhemTimeLocal ( 00 , 00 , 00 , $ nd , $ nm - 1 , $ ny - 1900 ) ;
my $ btimes = fhemTimeLocal ( 00 , 00 , 00 , $ bd , $ bm - 1 , $ by - 1900 ) ;
my $ etimes = fhemTimeLocal ( 00 , 00 , 00 , $ ed , $ em - 1 , $ ey - 1900 ) ;
2020-02-07 14:59:41 +00:00
if ( $ de ) {
2020-04-25 17:52:39 +00:00
$ begind = "$bd.$bm.$by" ;
$ endd = "$ed.$em.$ey" ;
2020-12-16 14:40:38 +00:00
}
else {
2020-04-25 17:52:39 +00:00
$ begind = "$by-$bm-$bd" ;
$ endd = "$ey-$em-$ed" ;
}
my ( $ a , $ b , undef ) = split ( ":" , $ bt ) ;
2020-02-09 18:55:42 +00:00
$ begint = "$a:$b" ;
my ( $ c , $ d , undef ) = split ( ":" , $ et ) ;
2020-04-25 17:52:39 +00:00
$ endt = "$c:$d" ;
2020-02-09 18:55:42 +00:00
2020-04-25 17:52:39 +00:00
$ edleft = "" ;
2020-02-09 18:55:42 +00:00
2020-04-25 17:52:39 +00:00
if ( $ etimes >= $ ntimes ) {
$ edleft = int ( ( $ etimes - $ ntimes ) / 86400 ) ;
}
2020-02-05 23:12:14 +00:00
2020-02-09 10:21:37 +00:00
$ begind = ( ( $ de ) ? 'heute ' : 'today ' ) if ( $ dleft eq "0" ) ;
2020-02-07 14:59:41 +00:00
$ endd = ( ( $ de ) ? 'heute ' : 'today ' ) if ( $ edleft eq "0" ) ;
2020-02-09 10:21:37 +00:00
$ begind = ( ( $ de ) ? 'morgen ' : 'tomorrow ' ) if ( $ dleft eq "1" ) ;
2020-02-07 14:59:41 +00:00
$ endd = ( ( $ de ) ? 'morgen ' : 'tomorrow ' ) if ( $ edleft eq "1" ) ;
2020-02-09 18:49:02 +00:00
if ( ( $ begind eq $ endd ) && ! $ isallday ) {
$ endd = "" ; # bei "Ende" nur Uhrzeit angeben wenn Termin am gleichen Tag beginnt/endet aber kein Ganztagstermin ist
2020-12-16 14:40:38 +00:00
}
elsif ( ( $ begind eq $ endd ) && $ isallday ) {
2020-02-09 18:49:02 +00:00
$ begint = "" ;
$ endt = "" ;
}
}
2020-02-11 09:11:52 +00:00
# Icon für Spalte Resttage spezifizieren
2020-05-17 20:44:49 +00:00
eval { $ dleft = evalTableSpecs ( {
href = > $ hash ,
def = > $ dleft ,
base = > $ hash - > { HELPER } { tableSpecs } { columnDaysLeftIcon } ,
bnr = > $ bnr ,
readref = > \ @ allrds ,
rdtype = > "image"
}
) ; 1 ;
2020-05-16 15:35:34 +00:00
} or do {
Log3 ( $ name , 1 , "$name - Syntax error in attribute \"tableSpecs\" near \"columnDaysLeftIcon\": $@" ) ;
} ;
2020-02-11 09:11:52 +00:00
# Icon für Spalte Status spezifizieren
2020-05-17 20:44:49 +00:00
eval { $ status = evalTableSpecs ( {
href = > $ hash ,
def = > $ status ,
base = > $ hash - > { HELPER } { tableSpecs } { columnStateIcon } ,
bnr = > $ bnr ,
readref = > \ @ allrds ,
rdtype = > "image"
}
) ; 1 ;
2020-05-16 15:35:34 +00:00
} or do {
Log3 ( $ name , 1 , "$name - Syntax error in attribute \"tableSpecs\" near \"columnStateIcon\": $@" ) ;
} ;
2020-04-25 17:52:39 +00:00
# Icon für Spalte "Symbol" bestimmen
$ di = ( $ hash - > { MODEL } eq "Diary" ) ? "time_calendar" : "time_note" ;
2020-05-17 20:44:49 +00:00
eval { $ symbol = evalTableSpecs ( {
href = > $ hash ,
def = > $ di ,
base = > $ hash - > { HELPER } { tableSpecs } { columnSymbolIcon } ,
bnr = > $ bnr ,
readref = > \ @ allrds ,
rdtype = > "image"
}
) ; 1 ;
2020-05-16 15:35:34 +00:00
} or do {
Log3 ( $ name , 1 , "$name - Syntax error in attribute \"tableSpecs\" near \"columnSymbolIcon\": $@" ) ;
} ;
2020-04-25 17:52:39 +00:00
# Gestaltung Spaltentext
my $ coldefalign = "center" ; # Ausrichtung der Spalte, default: center
eval {
2020-05-17 20:44:49 +00:00
$ coldefalign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colSymbolAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnSymbolAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colBeginAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnBeginAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colEndAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnEndAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colDayAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnDaysLeftAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colDLongAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnDaysLeftLongAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colWeekdayAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnWeekdayAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colTzAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnTimezoneAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colSummaryAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnSummaryAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colDescAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnDescriptionAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colStatusAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnStatusAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colCompAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnCompletionAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colLocAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnLocationAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colMapAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnMapAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colCalAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnCalendarAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
$ colIdAlign = "cal" . evalTableSpecs ( { href = > $ hash , def = > $ coldefalign , base = > $ hash - > { HELPER } { tableSpecs } { cellStyle } { columnEventIdAlign } , bnr = > "" , readref = > \ @ allrds , rdtype = > "string" } ) ;
2020-05-16 15:35:34 +00:00
1 ;
} or do {
Log3 ( $ name , 1 , "$name - Syntax error in attribute \"tableSpecs\" near \"cellStyle\": $@" )
2020-02-13 17:45:33 +00:00
} ;
2020-05-16 15:35:34 +00:00
2020-04-25 17:52:39 +00:00
my $ colalign = $ coldefalign ;
# TabellenBody
2020-02-04 21:09:59 +00:00
$ out . = "<tr class='" . ( $ k & 1 ? "odd" : "even" ) . "'>" ;
2020-04-25 17:52:39 +00:00
$ out . = "<td class='cal $colSymbolAlign' > $symbol </td>" if ( $ seen { Symbol } ) ;
2020-02-07 21:20:54 +00:00
if ( $ small ) {
2020-02-13 17:45:33 +00:00
$ out . = "<td class='cal $colBeginAlign' > " . $ begind . " " . $ begint . "</td>" if ( $ seen { Begin } ) ;
$ out . = "<td class='cal $colEndAlign' > " . $ endd . " " . $ endt . "</td>" if ( $ seen { End } ) ;
2020-12-16 14:40:38 +00:00
}
else {
2020-02-13 17:45:33 +00:00
$ out . = "<td class='cal $colBeginAlign' > $begind </td>" if ( $ seen { Begin } ) ;
$ out . = "<td class='cal $colBeginAlign' > $begint </td>" if ( $ seen { Begin } ) ;
$ out . = "<td class='cal $colEndAlign' > $endd </td>" if ( $ seen { End } ) ;
$ out . = "<td class='cal $colEndAlign' > $endt </td>" if ( $ seen { End } ) ;
2020-02-07 21:20:54 +00:00
}
2020-02-13 17:45:33 +00:00
$ out . = "<td class='cal $colDayAlign' > $dleft </td>" if ( $ seen { DaysLeft } ) ;
$ out . = "<td class='cal $colDLongAlign' > $dleftlong </td>" if ( $ seen { DaysLeftLong } ) ;
$ out . = "<td class='cal $colWeekdayAlign'> $weekday </td>" if ( $ seen { Weekday } ) ;
2020-04-25 17:52:39 +00:00
$ out . = "<td class='cal $colTzAlign' > $tz </td>" if ( $ seen { Timezone } ) ;
2020-02-13 17:45:33 +00:00
$ out . = "<td class='cal $colSummaryAlign'> $summary </td>" if ( $ seen { Summary } ) ;
$ out . = "<td class='cal $colDescAlign' > $desc </td>" if ( $ seen { Description } ) ;
$ out . = "<td class='cal $colStatusAlign' > $status </td>" if ( $ seen { Status } ) ;
2020-04-25 17:52:39 +00:00
$ out . = "<td class='cal $colCompAlign' > $completion </td>" if ( $ seen { Completion } ) ;
2020-02-13 17:45:33 +00:00
$ out . = "<td class='cal $colLocAlign' > $location </td>" if ( $ seen { Location } ) ;
$ out . = "<td class='cal $colMapAlign' > $gps </td>" if ( $ seen { Map } ) ;
$ out . = "<td class='cal $colCalAlign' > $cal </td>" if ( $ seen { Calendar } ) ;
$ out . = "<td class='cal $colIdAlign' > $id </td>" if ( $ seen { EventId } ) ;
2020-02-02 18:47:04 +00:00
$ out . = "</tr>" ;
}
2020-02-13 17:45:33 +00:00
$ out . = "</table>" ;
$ out . = "</td>" ;
$ out . = "</tr>" ;
2020-02-02 18:47:04 +00:00
$ out . = "</table>" ;
$ out . = "</html>" ;
return $ out ;
}
2020-02-11 09:11:52 +00:00
######################################################################################
# Evaluiere Eigenschaften von Attribut "tableSpecs"
#
# $hash: Devicehash
# $default: Standardwert - wird wieder zurückgegeben wenn kein Funktionsergebnis
# $specs: Basisschlüssel (z.B. $hash->{HELPER}{tableSpecs}{columnDaysLeft})
2020-02-12 13:54:45 +00:00
# $allreads: Referenz zum ARRAY was alle vorhandenen Readings des Devices enthält
2020-02-11 09:11:52 +00:00
# $bnr: Blocknummer Readings
2020-02-12 13:54:45 +00:00
# $rdtype: erwarteter Datentyp als Rückgabe (image, string)
2020-02-11 09:11:52 +00:00
#
######################################################################################
2020-05-17 10:59:56 +00:00
sub evalTableSpecs { ## no critic 'complexity'
2020-05-17 20:44:49 +00:00
# my ($hash,$default,$specs,$bnr,$allrds,$rdtype) = @_;
my ( $ argref ) = @ _ ;
my $ hash = $ argref - > { href } ;
my $ default = $ argref - > { def } ;
my $ specs = $ argref - > { base } ;
my $ bnr = $ argref - > { bnr } ;
my $ allrds = $ argref - > { readref } ;
my $ rdtype = $ argref - > { rdtype } ;
my $ name = $ hash - > { NAME } ;
2020-02-11 09:11:52 +00:00
my $ check ;
2020-04-25 17:52:39 +00:00
$ rdtype = $ rdtype // "string" ; # "string" als default Rückgabe Datentyp
2020-02-12 13:54:45 +00:00
2020-02-11 09:11:52 +00:00
# anonymous sub für Abarbeitung Perl-Kommandos
2020-04-25 17:52:39 +00:00
$ check = sub {
2020-05-16 15:35:34 +00:00
my ( $ cmd ) = @ _ ;
my $ ret = AnalyzePerlCommand ( undef , $ cmd ) ;
2020-02-11 09:11:52 +00:00
return $ ret ;
} ;
2020-05-16 15:35:34 +00:00
no warnings ; ## no critic 'warnings'
2020-02-11 09:11:52 +00:00
if ( $ specs ) { # Eigenschaft muß Wert haben
my ( $ rn , $ reading , $ uval , $ ui , $ rval ) ;
if ( ref ( $ specs ) eq "ARRAY" ) { # Wenn Schlüssel ein ARRAY enthält
my $ i = 0 ;
2020-04-25 17:52:39 +00:00
while ( $ specs - > [ $ i ] ) {
2020-02-11 09:11:52 +00:00
my $ n = keys % { $ specs - > [ $ i ] } ; # Anzahl Elemente (Entscheidungskriterien) in Hash
2020-04-25 17:52:39 +00:00
for my $ k ( keys % { $ specs - > [ $ i ] } ) {
2020-02-11 09:11:52 +00:00
if ( $ k eq "icon" ) {
2020-04-25 17:52:39 +00:00
$ ui = $ specs - > [ $ i ] { $ k } ;
2020-02-11 09:11:52 +00:00
$ n - - ;
next ;
}
2020-04-25 17:52:39 +00:00
for my $ r ( @ { $ allrds } ) { # alle vorhandenen Readings evaluieren
if ( $ r =~ m/$k$/x ) {
2020-02-11 09:11:52 +00:00
( undef , $ rn , $ reading ) = split ( "_" , $ r ) ; # Readingnummer evaluieren
$ uval = $ specs - > [ $ i ] { $ k } ; # Vergleichswert
last ;
}
}
$ rval = ReadingsVal ( $ name , $ bnr . "_" . $ rn . "_" . $ reading , "empty" ) ;
$ rval = "\"" . $ rval . "\"" ;
2020-04-25 17:52:39 +00:00
if ( eval ( $ rval . $ uval ) ) { ## no critic 'eval'
2020-02-11 09:11:52 +00:00
$ ui = $ specs - > [ $ i ] { icon } ;
$ n - - ;
2020-12-16 14:40:38 +00:00
}
else {
2020-02-11 09:11:52 +00:00
$ ui = "" ;
}
}
if ( $ n == 0 && $ ui ) {
2020-04-25 17:52:39 +00:00
$ default = $ ui ; # Defaultwert mit Select ersetzen wenn alle Bedingungen erfüllt
2020-02-11 09:11:52 +00:00
}
$ i + + ;
2020-12-16 14:40:38 +00:00
}
}
elsif ( ref ( $ specs ) eq "HASH" ) { # Wenn Schlüssel ein HASH enthält
2020-04-25 17:52:39 +00:00
my $ n = keys % { $ specs } ; # Anzahl Elemente (Entscheidungskriterien) in Hash
for my $ k ( keys % { $ specs } ) {
if ( $ k eq "icon" ) {
$ ui = $ specs - > { $ k } ;
$ n - - ;
next ;
}
for my $ r ( @ { $ allrds } ) { # alle vorhandenen Readings evaluieren
if ( $ r =~ m/$k$/x ) {
( undef , $ rn , $ reading ) = split ( "_" , $ r ) ; # Readingnummer evaluieren
$ uval = $ specs - > { $ k } ; # Vergleichswert
last ;
}
}
$ rval = ReadingsVal ( $ name , $ bnr . "_" . $ rn . "_" . $ reading , "empty" ) ;
$ rval = "\"" . $ rval . "\"" ;
if ( eval ( $ rval . $ uval ) ) { ## no critic 'eval'
$ ui = $ specs - > { icon } ;
$ n - - ;
2020-12-16 14:40:38 +00:00
}
else {
2020-04-25 17:52:39 +00:00
$ ui = "" ;
}
}
if ( $ n == 0 && $ ui ) {
$ default = $ ui ; # Defaultwert mit Select ersetzen wenn alle Bedingungen erfüllt
}
2020-12-16 14:40:38 +00:00
}
else { # ref Wert der Eigenschaft ist nicht HASH oder ARRAY
2020-05-17 20:44:49 +00:00
if ( $ specs =~ m/\{.*\}/xs ) { # den Wert als Perl-Funktion ausführen wenn in {}
2020-05-17 10:59:56 +00:00
$ specs =~ s/\$NAME/$name/xg ; # Platzhalter $NAME, $BNR ersetzen
$ specs =~ s/\$BNR/$bnr/xg ;
2020-02-11 09:11:52 +00:00
$ default = $ check - > ( $ specs ) ;
2020-12-16 14:40:38 +00:00
}
else { # einfache key-value Zuweisung
2020-04-25 17:52:39 +00:00
eval ( $ default = $ specs ) ; ## no critic 'eval'
}
}
2020-02-12 13:54:45 +00:00
}
if ( $ default && $ rdtype eq "image" ) {
2020-04-25 17:52:39 +00:00
$ default = FW_makeImage ( $ default ) ; # Icon aus "string" errechnen wenn "image" als Rückgabe erwartet wird und $default gesetzt
2020-02-12 13:54:45 +00:00
}
2020-02-11 09:11:52 +00:00
use warnings ;
return $ default ;
}
2020-01-19 14:21:41 +00:00
1 ;
= pod
2020-02-25 23:46:45 +00:00
= item summary Module to integrate Synology Calendar
2020-01-19 14:21:41 +00:00
= item summary_DE Modul zur Integration von Synology Calendar
= begin html
< a name = "SSCal" > </a>
<h3> SSCal </h3>
<ul>
2020-02-28 08:09:35 +00:00
This module is used to integrate Synology Calendar Server with FHEM .
2020-04-25 17:52:39 +00:00
The SSCal module is based on functions of Synology Calendar API . <br> <br>
The connection to the calendar server is established via a session ID after successful login . Requirements / queries of the server
are stored internally in a queue and processed sequentially . If the calendar server is temporarily unavailable
the saved queries are retrieved as soon as the connection to the server is working again . <br> <br>
2020-01-19 14:21:41 +00:00
2020-02-28 08:09:35 +00:00
Both appointment calendars ( Events ) and task lists ( ToDo ) can be processed . For these different calendar types
different device models can be defined , Model <b> Diary </b> for appointments and Model <b> Tasks </b> for
Task lists . <br> <br>
2020-02-27 20:23:56 +00:00
If you want discuss about or like to support the development of this module , there is a thread in the FHEM forum: <br>
< a href = "https://forum.fhem.de/index.php/topic,106963.0.html" > 57 _SSCal - Modul für den Synology Kalender </a> . <br> <br>
Further information about the module you can find in the ( german ) FHEM Wiki: <br>
2020-12-16 14:40:38 +00:00
< a href = "https://wiki.fhem.de/wiki/SSCal_-_Integration_des_Synology_Calendar_Servers" > SSCal - Integration des Synology Calendar Servers </a> .
2020-02-27 20:23:56 +00:00
<br> <br> <br>
<b> Preparation </b> <br> <br>
2020-04-25 17:52:39 +00:00
<ul>
As basic requirement the <b> Synology Calendar Package </b> must be installed on your Synology Disc Station . <br>
2020-02-27 20:23:56 +00:00
In Synology DSM a user as member of the administrator group <b> must </b> be defined for access use . This user must also have the rights
2020-04-25 17:52:39 +00:00
to read and / or write the relevant calendars . The entitlement for the calendars are set directly in the
2020-02-27 20:23:56 +00:00
< a href = "https://www.synology.com/en-global/knowledgebase/DSM/help/Calendar/calendar_desc" > Synology calendar application </a> .
2020-04-25 17:52:39 +00:00
The login credentials are assigned later by the set <b> credentials </b> command to the defined device .
2020-02-27 20:23:56 +00:00
<br> <br>
Furthermore some more Perl modules must be installed or available: <br> <br>
<table>
<colgroup> < col width = 35 % > < col width = 65 % > < / colgroup >
<tr> <td> JSON </td> <td> </td> </tr>
<tr> <td> Data:: Dumper </td> <td> </td> </tr>
<tr> <td> MIME:: Base64 </td> <td> </td> </tr>
<tr> <td> Time:: HiRes </td> <td> </td> </tr>
<tr> <td> Encode </td> <td> </td> </tr>
<tr> <td> POSIX </td> <td> </td> </tr>
<tr> <td> HttpUtils </td> <td> ( FHEM module ) </td> </tr>
<tr> <td> Blocking </td> <td> ( FHEM module ) </td> </tr>
<tr> <td> Meta </td> <td> ( FHEM module ) </td> </tr>
</table>
<br> <br>
</ul>
< a name = "SSCaldefine" > </a>
<b> Definition </b>
<ul>
<br>
The creation of SSCal devices is differed between the definition of diaries and task lists .
2020-04-25 17:52:39 +00:00
<br> <br>
2020-02-27 20:23:56 +00:00
The definition is done with: <br> <br>
2020-04-25 17:52:39 +00:00
<ul>
2020-02-27 20:23:56 +00:00
<b> <code> define & lt ; Name & gt ; SSCal & lt ; ServerAddr & gt ; [ & lt ; Port & gt ; ] [ & lt ; Protocol & gt ; ] [ Tasks ] </code> </b> <br> <br>
</ul>
The parameters are in detail:
<br>
<br>
<table>
<colgroup> < col width = 10 % > < col width = 90 % > < / colgroup >
<tr> <td> <b> Name </b> </td> <td> Name of the device in FHEM </td> </tr>
<tr> <td> <b> ServerAddr </b> </td> <td> IP address of Synology Disc Station . <b> Note: </b> If you use DNS name instead of IP address , don ' t forget to set the attribute dnsServer in global device ! </td> </tr>
<tr> <td> <b> Port </b> </td> <td> optional - Port of Synology Disc Station ( default: 5000 ) . </td> </tr>
<tr> <td> <b> Protocol </b> </td> <td> optional - Protocol used for communication with the calendar server , http or https ( default: http ) . </td> </tr>
<tr> <td> <b> Tasks </b> </td> <td> optional - to define a task list device add "Tasks" to the definition </td> </tr>
</table>
<br> <br>
<b> Examples: </b>
<pre>
<code> define Appointments SSCal 192.168 .2 .10 </code>
<code> define Appointments SSCal 192.168 .2 .10 5001 https </code>
# creates a diary device on default port (5000/http) respectively https on port 5001
<code> define Tasklist SSCal ds . myds . org 5001 https Tasks </code>
# creates a task list device with protocol https on port 5001
</pre>
After definition of a device only the command < a href = "#SSCalcredentials" > credentials </a> is available .
2020-04-25 17:52:39 +00:00
First of all you have to set the credentials for communication with the Synology calendar server by using this command . <br> <br>
2020-02-27 20:23:56 +00:00
If the login was successful , all for the user accessible calendars will be determined . The calendars to retrieve
2020-04-25 17:52:39 +00:00
are selectable by attribute < a href = "#usedCalendars" > usedCalendars </a> .
2020-02-27 20:23:56 +00:00
<br> <br> <br>
</ul>
< a name = "SSCalset" > </a>
<b> Set </b>
<ul>
<br>
The following set commands are valid for both device models Diary / Tasks or partly for one of these device models .
<br> <br>
<ul>
< a name = "SSCalcalUpdate" > </a>
<li> <b> calUpdate [ & lt ; list of calendars & gt ; ] </b> <br>
Fetch entries of the selected calendars ( see attribute < a href = "#usedCalendars" > usedCalendars </a> ) .
Alternatively you can enter a list of calendars to fetch separated by comma . The calendar names may contain spaces .
<br> <br>
<ul>
<b> Examples: </b> <br> <br>
set Appointments calUpdate <br>
# fetch the entries of calendars specified in attribute usedCalendars <br><br>
set Appointments calUpdate Heikos Kalender , Abfall <br>
# fetch the entries of both calendars "Heikos Kalender" and "Abfall". <br><br>
</ul>
2020-01-19 14:21:41 +00:00
2020-02-27 20:23:56 +00:00
</li> <br>
</ul>
<ul>
< a name = "SSCalcleanCompleteTasks" > </a>
<li> <b> cleanCompleteTasks </b> & nbsp ; & nbsp ; & nbsp ; & nbsp ; ( only model "Tasks" ) <br>
All completed tasks in the specified task lists ( see attribute < a href = "#usedCalendars" > usedCalendars </a> ) are deleted . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCaldeleteEventId" > </a>
<li> <b> deleteEventId & lt ; Id & gt ; </b> <br>
The specified Event Id ( see reading x_x_EventId ) will be delted from calendar or tas list . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCalcredentials" > </a>
<li> <b> credentials & lt ; User & gt ; & lt ; Passwort & gt ; </b> <br>
Store the credentials for calendar communication . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCaleraseReadings" > </a>
<li> <b> eraseReadings </b> <br>
Delete all calendar readings . It doesn ' t effect the calendar entries itself ! <br>
</li> <br>
</ul>
<ul>
< a name = "SSCallistSendqueue" > </a>
<li> <b> listSendqueue </b> <br>
Shows all entries in the sendqueue . Normally the queue is filled only for a short time , but may contain entries
permanently in case of problems . Thereby the occured failures can be identified and assigned . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCallogout" > </a>
<li> <b> logout </b> <br>
The user will be logged out and the session to the Synology Disc Station will be cleared . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCalpurgeSendqueue" > </a>
<li> <b> purgeSendqueue </b> <br>
Deletes entries from the sendqueue . Several options are usable dependend from situation: <br> <br>
<ul>
<table>
<colgroup> < col width = 15 % > < col width = 85 % > < / colgroup >
<tr> <td> - all - </td> <td> deletes all entries from sendqueue </td> </tr>
<tr> <td> - permError - </td> <td> deletes all entries which are suspended from further processing caused by a permanent error </td> </tr>
<tr> <td> & lt ; Index & gt ; </td> <td> deletes the specified entry from sendqueue </td> </tr>
</table>
</ul>
</li> <br>
</ul>
<ul>
< a name = "SSCalrestartSendqueue" > </a>
<li> <b> restartSendqueue </b> <br>
The processing of entries in sendqueue will be new started manually . Because of the sendqueue will be restarted automatically by
every new retrieval it is normally not necessary to execute this command . <br>
</li> <br>
</ul>
</ul>
< a name = "SSCalget" > </a>
<b> Get </b>
<ul>
<br>
<ul>
< a name = "SSCalapiInfo" > </a>
<li> <b> apiInfo </b> <br>
Retrieves the API informations of the Synology calendar server and open a popup window with its data .
<br>
</li> <br>
</ul>
<ul>
< a name = "SSCalcalAsHtml" > </a>
<li> <b> calAsHtml </b> <br>
Shows a popup with the time summary . In own perl routines and for integration in a weblink device this
overview can be used as follows: <br> <br>
<ul>
2020-05-16 15:35:34 +00:00
{ FHEM::SSCal:: calAsHtml ( "<SSCal-Device>" ) }
2020-02-27 20:23:56 +00:00
</ul>
</li> <br>
</ul>
<ul>
< a name = "SSCalgetCalendars" > </a>
<li> <b> getCalendars </b> <br>
Requests the existing calendars from your Synology Disc Station and open a popup window with informations about each available calendar .
</li> <br>
<br>
</ul>
<ul>
< a name = "SSCalstoredCredentials" > </a>
<li> <b> storedCredentials </b> <br>
Shows the stored User / Password combination .
</li> <br>
<br>
</ul>
<ul>
< a name = "SSCalversionNotes" > </a>
<li> <b> versionNotes </b> <br>
Shows important informations and hints about the module .
</li> <br>
<br>
</ul>
</ul>
< a name = "SSCamattr" > </a>
<b> Attribute </b>
<br> <br>
<ul>
<ul>
< a name = "asyncMode" > </a>
<li> <b> asyncMode </b> <br>
If set to "1" , the data parsing will be executed within a background process and avoid possible blocking situations . <br>
( default: 0 )
</li> <br>
</ul>
<ul>
< a name = "createATDevs" > </a>
<li> <b> createATDevs </b> <br>
If set to "1" , FHEM commands and Perl routines to be executed are recognised automatically in a calendar entry by SSCal .
In this case SSCal defines , changes or deletes at - devices to execute these commands independently . <br>
A FHEM command to be executed has to be included into <b> { } </b> in the field <b> Description </b> of Synology Calendar
2020-04-25 17:52:39 +00:00
application WebUI , Perl routines has to be included into double <b> { { } } </b> . <br>
2020-02-27 20:23:56 +00:00
For further detailed information please read the Wiki ( germnan ) section:
2020-05-16 15:35:34 +00:00
< a href = "https://wiki.fhem.de/wiki/-_Integration_des_Synology_Calendar_Servers#at-Devices_f.C3.BCr_Steuerungen_automatisch_erstellen_und_verwalten_lassen" > at - Devices für Steuerungen automatisch erstellen und verwalten lassen </a> .
2020-02-27 20:23:56 +00:00
<br>
( default: 0 )
</li> <br>
</ul>
<ul>
< a name = "cutOlderDays" > </a>
<li> <b> cutOlderDays </b> <br>
Entries in calendars are ignored if the due date is older than the number of specified days . <br>
( default: 5 ) <br> <br>
<ul>
<b> Example: </b> <br> <br>
attr & lt ; Name & gt ; cutOlderDays 30 <br>
</ul>
</li> <br>
</ul>
<ul>
< a name = "cutLaterDays" > </a>
<li> <b> cutLaterDays </b> <br>
Entries in calendars are ignored if the due date is later than the number of specified days . <br>
( default: 5 ) <br> <br>
<ul>
<b> Example: </b> <br> <br>
attr & lt ; Name & gt ; cutLaterDays 90 <br>
</ul>
</li> <br>
</ul>
<ul>
< a name = "filterCompleteTask" > </a>
<li> <b> filterCompleteTask </b> & nbsp ; & nbsp ; & nbsp ; & nbsp ; ( only model "Tasks" ) <br>
Entries of the calendar are filtered dependend from their completion: <br> <br>
<ul>
<table>
<colgroup> < col width = 10 % > < col width = 90 % > < / colgroup >
<tr> <td> 1 </td> <td> only completed tasks are shown </td> </tr>
<tr> <td> 2 </td> <td> only not completed tasks are shown </td> </tr>
<tr> <td> 3 </td> <td> completed and not completed tasks are shown ( default ) </td> </tr>
</table>
</ul>
</li> <br>
</ul>
<ul>
< a name = "filterDueTask" > </a>
<li> <b> filterDueTask </b> & nbsp ; & nbsp ; & nbsp ; & nbsp ; ( only model "Tasks" ) <br>
Entries in taks lists with / without due date are filtered: <br> <br>
<ul>
<table>
<colgroup> < col width = 10 % > < col width = 90 % > < / colgroup >
<tr> <td> 1 </td> <td> only tasks with due date are shown </td> </tr>
<tr> <td> 2 </td> <td> only tasks without due date are shown </td> </tr>
<tr> <td> 3 </td> <td> tasks with and without due date are shown ( default ) </td> </tr>
</table>
</ul>
</li> <br>
</ul>
<ul>
< a name = "interval" > </a>
<li> <b> interval & lt ; seconds & gt ; </b> <br>
Interval in seconds to fetch calendar entries automatically . If "0" is specified , no calendar fetch is
executed . ( default ) <br> <br>
<ul>
<b> Example: </b> <br> <br>
Set the attribute as follows if the calendar entries should retrieved every hour: <br>
attr & lt ; Name & gt ; interval 3600 <br>
</ul>
</li> <br>
</ul>
<ul>
< a name = "loginRetries" > </a>
<li> <b> loginRetries </b> <br>
Number of attempts for the initial user login . <br>
( default: 3 )
</li> <br>
</ul>
<ul>
< a name = "showRepeatEvent" > </a>
<li> <b> showRepeatEvent </b> & nbsp ; & nbsp ; & nbsp ; & nbsp ; ( only model "Diary" ) <br>
If "true" , one - time events as well as recurrent events are fetched . Otherwise only one - time events are retrieved . <br>
( default: true )
</li> <br>
</ul>
<ul>
< a name = "showPassInLog" > </a>
<li> <b> showPassInLog </b> <br>
If "1" , the password respectively the SID will be shown in the logfile . <br>
( default: 0 )
</li> <br>
</ul>
<ul>
< a name = "tableColumnMap" > </a>
<li> <b> tableColumnMap </b> <br>
Determines how the link to a map is shown in the table column "Map" : <br> <br>
<ul>
<table>
<colgroup> < col width = 10 % > < col width = 90 % > < / colgroup >
<tr> <td> <b> icon </b> </td> <td> shows a user customisable symbol ( default ) </td> </tr>
<tr> <td> <b> data </b> </td> <td> shows the GPS data </td> </tr>
<tr> <td> <b> text </b> </td> <td> shows a text adjustable by the user </td> </tr>
</table>
</ul>
<br>
To make further adjustments , there are some more possibilities to specify properties in the attribute tableSpecs .
For detailed informations about the possibilities to configure the overview table please consult the ( german ) Wiki
chapter
2020-05-16 15:35:34 +00:00
< a href = "https://wiki.fhem.de/wiki/-_Integration_des_Synology_Calendar_Servers#Darstellung_der_.C3.9Cbersichtstabelle_in_Raum-_und_Detailansicht_beeinflussen" > Darstellung der Übersichtstabelle in Raum - und Detailansicht beeinflussen </a> .
2020-02-27 20:23:56 +00:00
<br>
</li> <br>
</ul>
<ul>
< a name = "tableInDetail" > </a>
<li> <b> tableInDetail </b> <br>
An overview diary or taks table will be displayed in detail view . <br>
( default: 1 )
</li> <br>
</ul>
<ul>
< a name = "tableInRoom" > </a>
<li> <b> tableInRoom </b> <br>
An overview diary or taks table will be displayed in room view . <br>
( default: 1 )
</li> <br>
</ul>
<ul>
< a name = "tableFields" > </a>
<li> <b> tableFields </b> <br>
Selection of the fields to be displayed in the overview table in room or detail view . <br>
</li> <br>
</ul>
<ul>
< a name = "tableSpecs" > </a>
<li> <b> tableSpecs </b> <br>
By several key - value pair combinations the presentation of informations in the overview table can be adjusted .
The ( german ) Wiki chapter
2020-05-16 15:35:34 +00:00
< a href = "https://wiki.fhem.de/wiki/-_Integration_des_Synology_Calendar_Servers#Darstellung_der_.C3.9Cbersichtstabelle_in_Raum-_und_Detailansicht_beeinflussen" > Darstellung der Übersichtstabelle in Raum - und Detailansicht beeinflussen </a>
2020-02-27 20:23:56 +00:00
provides more detailed help for it .
</li> <br>
</ul>
<ul>
< a name = "timeout" > </a>
<li> <b> timeout & lt ; seconds & gt ; </b> <br>
Timeout for calendar fetch in seconds . <br>
( default: 20 )
</li> <br>
</ul>
<ul>
< a name = "usedCalendars" > </a>
<li> <b> usedCalendars </b> <br>
Selection of calendars to fetch from a popup window . The list of accessible calendars will initial be created during
FHEM startup . At all times it can also be done manually with command: <br> <br>
<ul>
get & lt ; Name & gt ; getCalendars <br>
</ul>
<br>
As long as the accessible calendars are not successfully fetched , this attribute only contains simply the entry: <br> <br>
<ul>
- - wait for Calendar list - - <br>
</ul>
</li> <br>
</ul>
</ul>
2020-04-25 17:52:39 +00:00
<br>
< a name = "SSCalEvents" > </a>
<b> Hints for event generation </b>
<ul>
<br>
Depending on the volume of the retrieved data , a large number of readings can be created .
To avoid too extensive event generation in FHEM , the attribute <b> event - on - update - reading </b> is preset after
definition of the calendar device to: <br> <br>
<ul>
attr <name> event - on - update - reading . * Summary . * , state
</ul>
<br>
If events are to be created for all readings , event - on - update - reading must be set to . * and mustn ' t be deleted .
<br> <br>
SSCal generates additional events for each event , which contains a start time , with each new read - in
of a calendar . These events provide the user with assistance in creating his own control logic in FHEM based on
calendar entries . <br> <br>
The event <b> composite </b> contains the information fields: <br> <br>
<ul>
<li> block number of the appointment </li>
<li> Event ID of the appointment </li>
<li> indicator for a serial appointment ( 0 = no serial appointment or 1 = serial appointment ) </li>
<li> start time in ISO 8601 format </li>
<li> Status of the event </li>
<li> the text in Description ( corresponds to the Description field in Synology Calendar WebUI ) or the text in Summary
if Description is not set </li>
</ul>
<br>
The event <b> compositeBlockNumbers </b> contains the block numbers of all events of the calendar .
If there are no appointments , this event has only the value <b> none </b> .
</ul>
2020-02-27 20:23:56 +00:00
</ul>
2020-01-19 14:21:41 +00:00
= end html
= begin html_DE
< a name = "SSCal" > </a>
<h3> SSCal </h3>
<ul>
2020-02-25 23:46:45 +00:00
Mit diesem Modul erfolgt die Integration des Synology Calendar Servers in FHEM .
2020-04-25 17:52:39 +00:00
Das Modul SSCal basiert auf Funktionen der Synology Calendar API . <br> <br>
Die Verbindung zum Kalenderserver erfolgt über eine Session ID nach erfolgreichem Login . Anforderungen / Abfragen des Servers
werden intern in einer Queue gespeichert und sequentiell abgearbeitet . Steht der Kalenderserver temporär nicht zur Verfügung ,
werden die gespeicherten Abfragen nachgeholt sobald die Verbindung zum Server wieder funktioniert . <br> <br>
2020-02-25 23:46:45 +00:00
Es können sowohl Terminkalender ( Events ) und Aufgabenlisten ( ToDo ) verarbeitet werden . Für diese verschiedenen Kalenderarten
können verschiedene Device - Models definiert werden , Model <b> Diary </b> für Terminkalender und Model <b> Tasks </b> für
Aufgabenlisten . <br> <br>
Wenn sie über dieses Modul diskutieren oder zur Verbesserung des Moduls beitragen möchten , ist im FHEM - Forum ein Sammelplatz unter: <br>
< a href = "https://forum.fhem.de/index.php/topic,106963.0.html" > 57 _SSCal - Modul für den Synology Kalender </a> . <br> <br>
Weitere Infomationen zum Modul sind im FHEM - Wiki zu finden: <br>
2020-12-16 14:40:38 +00:00
< a href = "https://wiki.fhem.de/wiki/SSCal_-_Integration_des_Synology_Calendar_Servers" > SSCal - Integration des Synology Calendar Servers </a> .
2020-02-25 23:46:45 +00:00
<br> <br> <br>
<b> Vorbereitung </b> <br> <br>
2020-04-25 17:52:39 +00:00
<ul>
Als Grundvoraussetzung muss das <b> Synology Calendar Package </b> auf der Diskstation installiert sein . <br>
2020-02-25 23:46:45 +00:00
Im Synology DSM wird ein User benutzt , der Mitglied der Administrator - Group sein <b> muß </b> und zusätzlich die benötigte Berechtigung
2020-04-25 17:52:39 +00:00
zum Lesen und / oder Schreiben der relevanten Kalender hat . Die Kalenderberechtigungen werden direkt in der
2020-02-27 20:23:56 +00:00
< a href = "https://www.synology.com/de-de/knowledgebase/DSM/help/Calendar/calendar_desc" > Synology Kalenderapplikation </a> eingestellt .
2020-04-25 17:52:39 +00:00
Die Zugangsdaten werden später über ein Set <b> credentials </b> Kommando dem angelegten Device zugewiesen .
2020-02-25 23:46:45 +00:00
<br> <br>
2020-02-26 19:53:52 +00:00
Weiterhin müssen diverse Perl - Module installiert sein: <br> <br>
2020-02-25 23:46:45 +00:00
<table>
<colgroup> < col width = 35 % > < col width = 65 % > < / colgroup >
<tr> <td> JSON </td> <td> </td> </tr>
<tr> <td> Data:: Dumper </td> <td> </td> </tr>
<tr> <td> MIME:: Base64 </td> <td> </td> </tr>
<tr> <td> Time:: HiRes </td> <td> </td> </tr>
<tr> <td> Encode </td> <td> </td> </tr>
<tr> <td> POSIX </td> <td> </td> </tr>
<tr> <td> HttpUtils </td> <td> ( FHEM - Modul ) </td> </tr>
<tr> <td> Blocking </td> <td> ( FHEM - Modul ) </td> </tr>
<tr> <td> Meta </td> <td> ( FHEM - Modul ) </td> </tr>
</table>
<br> <br>
</ul>
< a name = "SSCaldefine" > </a>
<b> Definition </b>
<ul>
<br>
2020-02-27 20:23:56 +00:00
Bei der Definition wird zwischen einem Kalenderdevice für Termine ( Events ) und Aufgaben ( Tasks ) unterschieden .
2020-04-25 17:52:39 +00:00
<br> <br>
2020-02-25 23:46:45 +00:00
Die Definition erfolgt mit: <br> <br>
2020-04-25 17:52:39 +00:00
<ul>
2020-02-25 23:46:45 +00:00
<b> <code> define & lt ; Name & gt ; SSCal & lt ; ServerAddr & gt ; [ & lt ; Port & gt ; ] [ & lt ; Protocol & gt ; ] [ Tasks ] </code> </b> <br> <br>
</ul>
Die Parameter beschreiben im Einzelnen:
<br>
<br>
<table>
2020-02-27 20:23:56 +00:00
<colgroup> < col width = 10 % > < col width = 90 % > < / colgroup >
2020-02-25 23:46:45 +00:00
<tr> <td> <b> Name </b> </td> <td> der Name des neuen Kalenderdevices in FHEM </td> </tr>
2020-02-27 20:23:56 +00:00
<tr> <td> <b> ServerAddr </b> </td> <td> die IP - Addresse der Synology DS . <b> Hinweis: </b> Wird der DNS - Name statt IP - Adresse verwendet , sollte das Attribut dnsServer im global Device gesetzt werden ! </td> </tr>
2020-02-25 23:46:45 +00:00
<tr> <td> <b> Port </b> </td> <td> optional - Port der Synology DS ( default: 5000 ) . </td> </tr>
<tr> <td> <b> Protocol </b> </td> <td> optional - Protokoll zur Kommunikation mit dem Kalender - Server , http oder https ( default: http ) . </td> </tr>
<tr> <td> <b> Tasks </b> </td> <td> optional - zur Definition einer Aufgabenliste wird "Tasks" hinzugefügt </td> </tr>
</table>
<br> <br>
2020-02-27 20:23:56 +00:00
<b> Beispiele: </b>
2020-02-25 23:46:45 +00:00
<pre>
<code> define Appointments SSCal 192.168 .2 .10 </code>
<code> define Appointments SSCal 192.168 .2 .10 5001 https </code>
# erstellt Terminkalenderdevice mit Standardport (5000/http) bzw. https auf Port 5001
<code> define Tasklist SSCal ds . myds . org 5001 https Tasks </code>
# erstellt Aufgabenlistendevice mit https auf Port 5001
</pre>
Nach der Definition eines Devices steht nur der set - Befehl < a href = "#SSCalcredentials" > credentials </a> zur Verfügung .
2020-04-25 17:52:39 +00:00
Mit diesem Befehl werden zunächst die Zugangsparameter dem Device bekannt gemacht . <br> <br>
2020-02-25 23:46:45 +00:00
War der Login erfolgreich , werden alle dem User zugänglichen Kalender ermittelt und im
2020-04-25 17:52:39 +00:00
Attribut < a href = "#usedCalendars" > usedCalendars </a> zur Auswahl bereitgestellt .
2020-02-25 23:46:45 +00:00
<br> <br> <br>
</ul>
< a name = "SSCalset" > </a>
<b> Set </b>
<ul>
<br>
Die aufgeführten set - Kommandos sind sowohl für die Devicemodels Diary / Tasks oder teilweise nur für einen dieser Devicemodels gültig .
<br> <br>
<ul>
< a name = "SSCalcalUpdate" > </a>
<li> <b> calUpdate [ & lt ; Kalenderliste & gt ; ] </b> <br>
2020-02-25 23:58:06 +00:00
Ruft die Einträge der selektierten Kalender ( siehe Attribut < a href = "#usedCalendars" > usedCalendars </a> ) ab .
2020-04-25 17:52:39 +00:00
Alternativ kann eine Komma getrennte Liste der abzurufenden Kalender dem Befehl übergeben werden . Die Kalendernamen können
Leerzeichen enthalten .
2020-02-25 23:46:45 +00:00
<br> <br>
<ul>
<b> Beispiel: </b> <br> <br>
set Appointments calUpdate <br>
# ruft die Einträge der im Attribut usedCalendars spezifizierten Kalender ab <br><br>
set Appointments calUpdate Heikos Kalender , Abfall <br>
# ruft die Einträge der Kalender "Heikos Kalender" und "Abfall" ab. <br><br>
</ul>
</li> <br>
</ul>
<ul>
< a name = "SSCalcleanCompleteTasks" > </a>
<li> <b> cleanCompleteTasks </b> & nbsp ; & nbsp ; & nbsp ; & nbsp ; ( nur Model "Tasks" ) <br>
2020-02-25 23:58:06 +00:00
In den selektierten Aufgabenlisten ( siehe Attribut < a href = "#usedCalendars" > usedCalendars </a> ) werden alle
2020-02-25 23:46:45 +00:00
abgeschlossenen Aufgaben gelöscht . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCaldeleteEventId" > </a>
<li> <b> deleteEventId & lt ; Id & gt ; </b> <br>
Die angegebene Event Id ( siehe Reading x_x_EventId ) wird aus dem Kalender bzw . der Aufgabenliste gelöscht . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCalcredentials" > </a>
<li> <b> credentials & lt ; User & gt ; & lt ; Passwort & gt ; </b> <br>
Speichert die Zugangsdaten . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCaleraseReadings" > </a>
<li> <b> eraseReadings </b> <br>
Löscht alle Kalenderreadings . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCallistSendqueue" > </a>
<li> <b> listSendqueue </b> <br>
Zeigt alle Einträge in der Sendequeue . Die Queue ist normalerweise nur kurz gefüllt , kann aber im Problemfall
dauerhaft Einträge enthalten . Dadurch kann ein bei einer Abrufaufgabe aufgetretener Fehler ermittelt und zugeordnet
werden . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCallogout" > </a>
<li> <b> logout </b> <br>
Der User wird ausgeloggt und die Session mit der Synology DS beendet . <br>
</li> <br>
</ul>
<ul>
< a name = "SSCalpurgeSendqueue" > </a>
<li> <b> purgeSendqueue </b> <br>
Löscht Einträge in der Sendequeue . Es stehen verschiedene Optionen je nach Situation zur Verfügung: <br> <br>
<ul>
<table>
<colgroup> < col width = 15 % > < col width = 85 % > < / colgroup >
<tr> <td> - all - </td> <td> löscht alle in der Sendequeue vorhandenen Einträge </td> </tr>
<tr> <td> - permError - </td> <td> löscht alle Einträge , die durch einen permanenten Fehler von der weiteren Verarbeitung ausgeschlossen sind </td> </tr>
<tr> <td> & lt ; Index & gt ; </td> <td> löscht einen eindeutigen Eintrag der Sendequeue </td> </tr>
</table>
</ul>
</li> <br>
</ul>
<ul>
< a name = "SSCalrestartSendqueue" > </a>
<li> <b> restartSendqueue </b> <br>
Die Abarbeitung der Einträge in der Sendequeue wird manuell neu angestoßen . Normalerweise nicht nötig , da die Sendequeue bei der
Initialisierung jedes neuen Abrufs impliziz neu gestartet wird . <br>
</li> <br>
</ul>
</ul>
< a name = "SSCalget" > </a>
<b> Get </b>
<ul>
<br>
<ul>
< a name = "SSCalapiInfo" > </a>
<li> <b> apiInfo </b> <br>
Ruft die API Informationen des Synology Calendar Servers ab und öffnet ein Popup mit diesen Informationen .
<br>
</li> <br>
</ul>
<ul>
< a name = "SSCalcalAsHtml" > </a>
<li> <b> calAsHtml </b> <br>
Zeigt ein Popup mit einer Terminübersicht . In eigenen perl - Routinen und für die Einbindung in weblink kann
diese Übersicht aufgerufen werden mit: <br> <br>
<ul>
2020-05-16 15:35:34 +00:00
{ FHEM::SSCal:: calAsHtml ( "<SSCal-Device>" ) }
2020-02-25 23:46:45 +00:00
</ul>
</li> <br>
</ul>
<ul>
< a name = "SSCalgetCalendars" > </a>
<li> <b> getCalendars </b> <br>
Ruft die auf der Synology vorhandenen Kalender ab und öffnet ein Popup mit Informationen über die jeweiligen Kalender .
</li> <br>
<br>
</ul>
<ul>
< a name = "SSCalstoredCredentials" > </a>
<li> <b> storedCredentials </b> <br>
Zeigt die gespeicherten User / Passwort Kombination .
</li> <br>
<br>
</ul>
<ul>
< a name = "SSCalversionNotes" > </a>
<li> <b> versionNotes </b> <br>
Zeigt Informationen und Hilfen zum Modul .
</li> <br>
<br>
</ul>
</ul>
< a name = "SSCamattr" > </a>
<b> Attribute </b>
<br> <br>
<ul>
<ul>
< a name = "asyncMode" > </a>
<li> <b> asyncMode </b> <br>
Wenn "1" wird das Datenparsing in einen Hintergrundprozess ausgelagert und vermeidet Blockierungssituationen . <br>
( default: 0 )
</li> <br>
</ul>
<ul>
< a name = "createATDevs" > </a>
<li> <b> createATDevs </b> <br>
Wenn "1" werden bei der Erkennung von FHEM - Kommandos bzw . auszuführenden Perl - Routinen im Kalendereintrag durch SSCal
automatisiert at - Devices zur termingerechten Ausführung dieser Kommandos erstellt , geändert und gelöscht . <br>
Auszuführende FHEM - Kommandos werden in <b> { } </b> eingeschlossen im Feld <b> Beschreibung </b> im Synology Kalender WebUI
hinterlegt , Perl Routinen werden in doppelte <b> { { } } </b> eingeschlossen . <br>
Lesen sie bitte dazu die detailliierte Beschreibung im Wiki Abschnitt
2020-05-16 15:35:34 +00:00
< a href = "https://wiki.fhem.de/wiki/-_Integration_des_Synology_Calendar_Servers#at-Devices_f.C3.BCr_Steuerungen_automatisch_erstellen_und_verwalten_lassen" > at - Devices für Steuerungen automatisch erstellen und verwalten lassen </a> .
2020-02-25 23:46:45 +00:00
<br>
( default: 0 )
</li> <br>
</ul>
<ul>
< a name = "cutOlderDays" > </a>
<li> <b> cutOlderDays </b> <br>
Terminkalendereinträge und Aufgabenkalendereinträge mit Fälligkeitstermin älter als die angegeben Tage werden von der
Verarbeitung ausgeschlossen . <br>
( default: 5 ) <br> <br>
<ul>
<b> Beispiel: </b> <br> <br>
attr & lt ; Name & gt ; cutOlderDays 30 <br>
</ul>
</li> <br>
</ul>
<ul>
< a name = "cutLaterDays" > </a>
<li> <b> cutLaterDays </b> <br>
Terminkalendereinträge und Aufgabenkalendereinträge mit Fälligkeitstermin später als die angegeben Tage werden von der
Verarbeitung ausgeschlossen . <br>
( default: 5 ) <br> <br>
<ul>
<b> Beispiel: </b> <br> <br>
attr & lt ; Name & gt ; cutLaterDays 90 <br>
</ul>
</li> <br>
</ul>
<ul>
< a name = "filterCompleteTask" > </a>
<li> <b> filterCompleteTask </b> & nbsp ; & nbsp ; & nbsp ; & nbsp ; ( nur Model "Tasks" ) <br>
Es werden Einträge in Aufgabenkalendern entsprechend der Fertigstellung gefiltert: <br> <br>
<ul>
<table>
<colgroup> < col width = 10 % > < col width = 90 % > < / colgroup >
<tr> <td> 1 </td> <td> nur fertig gestellte Aufgaben werden angezeigt </td> </tr>
<tr> <td> 2 </td> <td> nur nicht fertige Aufgaben werden angezeigt </td> </tr>
<tr> <td> 3 </td> <td> es werden fertige und nicht fertige Aufgaben angezeigt ( default ) </td> </tr>
</table>
</ul>
</li> <br>
</ul>
<ul>
< a name = "filterDueTask" > </a>
<li> <b> filterDueTask </b> & nbsp ; & nbsp ; & nbsp ; & nbsp ; ( nur Model "Tasks" ) <br>
Es werden Einträge in Aufgabenkalendern mit / ohne Fälligkeit gefiltert: <br> <br>
<ul>
<table>
<colgroup> < col width = 10 % > < col width = 90 % > < / colgroup >
<tr> <td> 1 </td> <td> nur Einträge mit Fälligkeitstermin werden angezeigt </td> </tr>
<tr> <td> 2 </td> <td> nur Einträge ohne Fälligkeitstermin werden angezeigt </td> </tr>
<tr> <td> 3 </td> <td> es werden Einträge mit und ohne Fälligkeitstermin angezeigt ( default ) </td> </tr>
</table>
</ul>
</li> <br>
</ul>
<ul>
< a name = "interval" > </a>
2020-02-27 20:23:56 +00:00
<li> <b> interval & lt ; Sekunden & gt ; </b> <br>
2020-02-25 23:46:45 +00:00
2020-02-27 20:23:56 +00:00
Automatisches Abrufintervall der Kalendereintträge in Sekunden . Ist "0" agegeben , wird kein automatischer Datenabruf
2020-02-25 23:46:45 +00:00
ausgeführt . ( default ) <br>
Sollen z . B . jede Stunde die Einträge der gewählten Kalender abgerufen werden , wird das Attribut wie
folgt gesetzt: <br> <br>
<ul>
attr & lt ; Name & gt ; interval 3600 <br>
</ul>
</li> <br>
</ul>
<ul>
< a name = "loginRetries" > </a>
<li> <b> loginRetries </b> <br>
Anzahl der Versuche für das inititiale User login . <br>
( default: 3 )
</li> <br>
</ul>
<ul>
< a name = "showRepeatEvent" > </a>
<li> <b> showRepeatEvent </b> & nbsp ; & nbsp ; & nbsp ; & nbsp ; ( nur Model "Diary" ) <br>
Wenn "true" werden neben einmaligen Terminen ebenfalls wiederkehrende Termine ausgewertet . <br>
( default: true )
</li> <br>
</ul>
<ul>
< a name = "showPassInLog" > </a>
<li> <b> showPassInLog </b> <br>
Wenn "1" wird das Passwort bzw . die SID im Log angezeigt . <br>
( default: 0 )
</li> <br>
</ul>
<ul>
< a name = "tableColumnMap" > </a>
<li> <b> tableColumnMap </b> <br>
Legt fest , wie der Link zur Karte in der Tabellspalte "Map" bzw . "Karte" gestaltet wird: <br> <br>
<ul>
<table>
<colgroup> < col width = 10 % > < col width = 90 % > < / colgroup >
<tr> <td> <b> icon </b> </td> <td> es wird ein durch den User anpassbares Symbol angezeigt ( default ) </td> </tr>
<tr> <td> <b> data </b> </td> <td> es werden die GPS - Daten angezeigt </td> </tr>
<tr> <td> <b> text </b> </td> <td> es wird ein durch den Nutzer einstellbarer Text verwendet </td> </tr>
</table>
</ul>
<br>
Der Nutzer kann weitere Anpassungen des verwendeten Icons oder Textes in den Eigenschaften des Attributs tableSpecs
vornehmen . Für detailliierte Informationen dazu siehe Wiki - Kapitel
2020-05-16 15:35:34 +00:00
< a href = "https://wiki.fhem.de/wiki/-_Integration_des_Synology_Calendar_Servers#Darstellung_der_.C3.9Cbersichtstabelle_in_Raum-_und_Detailansicht_beeinflussen" > Darstellung der Übersichtstabelle in Raum - und Detailansicht beeinflussen </a> .
2020-02-25 23:46:45 +00:00
<br>
</li> <br>
</ul>
<ul>
< a name = "tableInDetail" > </a>
<li> <b> tableInDetail </b> <br>
Eine Termin / Aufgabenübersicht wird in der Detailansicht erstellt bzw . ausgeschaltet . <br>
( default: 1 )
</li> <br>
</ul>
<ul>
< a name = "tableInRoom" > </a>
<li> <b> tableInRoom </b> <br>
Eine Termin / Aufgabenübersicht wird in der Raumansicht erstellt bzw . ausgeschaltet . <br>
( default: 1 )
</li> <br>
</ul>
<ul>
< a name = "tableFields" > </a>
<li> <b> tableFields </b> <br>
Auswahl der in der Termin / Aufgabenübersicht ( Raum - bzw . Detailansicht ) anzuzeigenden Felder über eine Drop - Down
Liste . <br>
</li> <br>
</ul>
<ul>
< a name = "tableSpecs" > </a>
<li> <b> tableSpecs </b> <br>
Über verschiedene Schlüssel - Wertpaar Kombinationen kann die Darstellung der Informationen in der Übersichtstabelle
angepasst werden . Das Wiki - Kapitel
2020-05-17 20:44:49 +00:00
< a href = "https://wiki.fhem.de/wiki/SSCal_-_Integration_des_Synology_Calendar_Servers#Darstellung_der_.C3.9Cbersichtstabelle_in_Raum-_und_Detailansicht_beeinflussen" > Darstellung der Übersichtstabelle in Raum - und Detailansicht beeinflussen </a>
2020-02-25 23:46:45 +00:00
liefert detailiierte Informationen dazu .
</li> <br>
</ul>
<ul>
< a name = "timeout" > </a>
2020-02-27 20:23:56 +00:00
<li> <b> timeout & lt ; Sekunden & gt ; </b> <br>
2020-02-25 23:46:45 +00:00
Timeout für den Datenabruf in Sekunden . <br>
( default: 20 )
</li> <br>
</ul>
<ul>
< a name = "usedCalendars" > </a>
<li> <b> usedCalendars </b> <br>
Auswahl der abzurufenden Kalender über ein Popup . Die Liste der Kalender wird beim Start des Moduls initial gefüllt ,
kann danach aber ebenfalls durch den Befehl: <br> <br>
<ul>
get & lt ; Name & gt ; getCalendars <br>
</ul>
<br>
manuell ausgeführt werden .
Wurde noch kein erfolgreicher Kalenderabruf ausgeführt , enthält dieses Attribut lediglich den Eintrag: <br> <br>
<ul>
- - wait for Calendar list - - <br>
</ul>
</li> <br>
</ul>
</ul>
2020-04-25 17:52:39 +00:00
<br>
< a name = "SSCalEvents" > </a>
<b> Hinweise zur Eventgenerierung </b>
<ul>
<br>
Je nach Umfang der abgerufenen Daten können sehr viele Readings erstellt werden . Um eine zu umfangreiche Eventgenerierung
in FHEM zu verhindern , ist nach der Definition des Kalenderdevices das Attribut <b> event - on - update - reading </b>
voreingestellt auf: <br> <br>
<ul>
attr <Name> event - on - update - reading . * Summary . * , state
</ul>
<br>
Sollen Events für alle Readings erstellt werden , muss event - on - update - reading auf . * eingestellt und nicht gelöscht
werden . <br> <br>
SSCal generiert für jedes Ereignis , welches einen Begin - Zeitpunkt enthält , zusätzliche Events bei jedem erneuten Einlesen
eines Kalenders . Diese Events bieten dem Anwender Hilfe zur Erstellung eigener Steuerungslogiken in FHEM auf Grundlage
von Kalendereinträgen . <br> <br>
Der Event <b> composite </b> enthält die Informationsfelder: <br> <br>
<ul>
<li> Blocknummer des Termins </li>
<li> Event - ID des Termins </li>
<li> Kennzeichen für ein Serientermin ( 0 = kein Serientermin oder 1 = Serientermin ) </li>
<li> Startzeitpunkt im ISO 8601 Format </li>
<li> Status des Events </li>
<li> den Text in Description ( entspricht dem Feld Beschreibung im Synology Kalender WebUI ) bzw . den Text in Summary
falls Description nicht gesetzt ist </li>
</ul>
<br>
Der Event <b> compositeBlockNumbers </b> enthält die Blocknummern aller Termine des Kalenders . Sind keine Termine vorhanden , enthält
dieser Event nur den Wert <b> none </b> .
</ul>
2020-01-19 14:21:41 +00:00
</ul>
= end html_DE
= for : application / json ; q = META . json 57 _SSCal . pm
{
"abstract" : "Integration of Synology Calendar." ,
"x_lang" : {
"de" : {
"abstract" : "Integration des Synology Calendars."
}
} ,
"keywords" : [
"Synology" ,
"Calendar" ,
"Appointments"
] ,
"version" : "v1.1.1" ,
2020-02-12 13:54:45 +00:00
"release_status" : "stable" ,
2020-01-19 14:21:41 +00:00
"author" : [
"Heiko Maaz <heiko.maaz@t-online.de>"
] ,
"x_fhem_maintainer" : [
"DS_Starter"
] ,
"x_fhem_maintainer_github" : [
"nasseeder1"
] ,
"prereqs" : {
"runtime" : {
"requires" : {
"FHEM" : 5.00918799 ,
"perl" : 5.014 ,
2020-02-25 23:46:45 +00:00
"POSIX" : 0 ,
2020-02-09 15:05:13 +00:00
"JSON" : 4.020 ,
2020-01-19 14:21:41 +00:00
"Data::Dumper" : 0 ,
"MIME::Base64" : 0 ,
"Time::HiRes" : 0 ,
"HttpUtils" : 0 ,
"Blocking" : 0 ,
2020-04-25 17:52:39 +00:00
"Encode" : 0
2020-01-19 14:21:41 +00:00
} ,
"recommends" : {
"FHEM::Meta" : 0
} ,
"suggests" : {
}
}
} ,
"resources" : {
"x_wiki" : {
"web" : "https://wiki.fhem.de/wiki/SSCal - Integration des Synology Calendar Servers" ,
"title" : "SSCal - Integration des Synology Calendar Servers"
} ,
"repository" : {
"x_dev" : {
"type" : "svn" ,
"url" : "https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter" ,
"web" : "https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter/57_SSCal.pm" ,
"x_branch" : "dev" ,
"x_filepath" : "fhem/contrib/" ,
"x_raw" : "https://svn.fhem.de/fhem/trunk/fhem/contrib/DS_Starter/57_SSCal.pm"
}
}
}
}
= end : application / json ; q = META . json
= cut