Hallo Foren Gemeinde,
ich hoffe ich finde hier antworten. Ich nutze zur Haus Automatisierung die Software IP Symcon die auf PHP basiert und habe ein passendes Modul installiert um den Google Kalender auszulesen und in meine Visualisierung zu integrieren.
Es geht dabei um den FullCalendar:
https://fullcalendar.io/
Beschreibung der TimeZone:
https://fullcalendar.io/docs/timezone
und das passende IP Symcon Modul:
https://github.com/skyslasher/iCal-Calendar
Habe soweit bei allem durch geblickt aber bei der Timezone bzw. das Anzeigen der richtigen Uhrzeit funktioniert einfach nicht. Ich habe schon einige Parameter probiert weiß aber nicht so recht wo der hin gehört.
Hier ist der gesamte Code den ich angelegt habe:
Anbei noch die Module.php:
Jetzt habe ich heraus gefunden wenn ich in der module.php vom Reader folgende Zeile ändere, stimmt die Zeitangabe der täglich Wiederholten Termine, aber nicht mehr der einzel Termine. Praktisch genau umgekehrt und die einzel Termine sind 2 Stunden nach hinten verschoben. Ich denke mal das dort der php code falsch ist.
Original Zeile 174:
geänderte Zeile 174:
Was ich auch noch nicht heraus gefunden habe, das die vergangenen Termine angezeigt bleiben, wie das z.B. im Google Kalender der Fall ist.
Ich hoffe es kann mir hier im Forum jemand einen Tipp geben.
Viele Grüße
Stefan
ich hoffe ich finde hier antworten. Ich nutze zur Haus Automatisierung die Software IP Symcon die auf PHP basiert und habe ein passendes Modul installiert um den Google Kalender auszulesen und in meine Visualisierung zu integrieren.
Es geht dabei um den FullCalendar:
https://fullcalendar.io/
Beschreibung der TimeZone:
https://fullcalendar.io/docs/timezone
und das passende IP Symcon Modul:
https://github.com/skyslasher/iCal-Calendar
Habe soweit bei allem durch geblickt aber bei der Timezone bzw. das Anzeigen der richtigen Uhrzeit funktioniert einfach nicht. Ich habe schon einige Parameter probiert weiß aber nicht so recht wo der hin gehört.
Hier ist der gesamte Code den ich angelegt habe:
PHP-Code:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.9.0/fullcalendar.min.css' />
<link href='https://code.jquery.com/ui/1.12.1/themes/dark-hive/jquery-ui.css' rel='stylesheet' />
<script type="text/javascript" src='//cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js'></script>
<script type="text/javascript" src='//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>
<script type="text/javascript" src='//cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.9.0/fullcalendar.min.js'></script>
<script type="text/javascript" src='//cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.9.0/locale-all.js'></script>
<script>
$(document).ready(function() {
$('#calendar').fullCalendar({
// put your options and callbacks here
weekends: true, // Samstag und Sonntag anzeigen
themeSystem: 'jquery-ui', // Theme System ändern: bootstrap3,bootstrap4,jquery-ui,standard
defaultView: 'month', // legt fest welche Ansicht beim öffnen zuerst gezeigt wird
locale: 'de', // Sprache auswählen für die Wochen/Monatsnamen
weekNumbers: true, // Anzeige Wochennummer
weekNumbersWithinDays: false, // Anzeige Wochennummer in der Tagspalte
weekNumberTitle: 'Woche ', // Anzeigenamen der Woche Beispiel: Woche 47 oder W 47
eventLimitText: 'weitere', // Zeichenfolge für die verdeckten Events
firstDay: '1', // Der Tag an dem jede Woche beginnt: Sunday=0, Monday=1, Tuesday=2, usw.
displayEventTime: true, // Gibt an, ob der Text für das Datum / die Uhrzeit eines Ereignisses angezeigt werden soll oder nicht
displayEventEnd: true, // Gibt an, ob die Endzeit eines Ereignisses angezeigt werden soll oder nicht
navLinks: true, // Datum oder Wochentag klickbar bzw. auswählbar zur Tag Ansicht
fixedWeekCount: false, // legt fest das nur die Wochen angezeigt werden, die ein Monat hat -> true: immer 6 Wochen / false: unabhängig vom Monat
showNonCurrentDates: true, // legt fest ob Termine angezeigt werden bei den Tagen vorher oder nachher eines Monats. Beispiel: Wenn der erste eines Monats auf Mittwoch fällt, werden Mon + Di die Termine angezeigt
timezoneParam: false,
views: { // Seperate Einstellungen der einzelnen Ansichten
basic: {
// options apply to basicWeek and basicDay views
titleFormat: 'MMMM YYYY', // Titel Ansicht von Tag, Monat, Jahr
columnHeaderFormat: 'ddd D/M', // Ansicht von Tag, Monat in der Wochenübersicht
eventLimit: '6' // Anzahl der Events die angezeigt werden sollen -> false zeigt alle Events an
},
agenda: {
// options apply to agendaWeek and agendaDay views
},
week: {
// options apply to basicWeek and agendaWeek views
titleFormat: 'D. MMMM YYYY', // Ansicht von Tag, Monat, Jahr
columnHeaderFormat: 'ddd D/M' // Ansicht von Tag, Monat in der Wochenübersicht
},
day: {
// options apply to basicDay and agendaDay views
titleFormat: 'D. MMMM YYYY' // Ansicht von Tag, Monat, Jahr
}
},
header: { // Ansicht Kopfzeile
left: 'month,agendaWeek,agendaDay,listMonth,basicWeek', // Ansicht und Auswahl des Kalenders
center: 'title',
right: 'prevYear,prev,next,nextYear'
},
footer: { // Ansicht Fußzeile
left: 'month,agendaWeek,agendaDay,listMonth,basicWeek', // Ansicht und Auswahl des Kalenders
center: 'title',
right: 'custom1,custom2,next,nextYear'
},
buttonText: { // Button Text umbenennung
today: 'Heute',
month: 'Monat',
week: 'Woche',
day: 'Tag',
list: 'Liste',
basicDay: 'Basis Tag',
basicWeek: 'Basis Woche',
listDay: 'Liste Tag',
listWeek: 'Liste Woche',
listMonth: 'Liste Monat',
listYear: 'Liste Jahr',
agendaDay: 'Agenda Tag',
agendaWeek: 'Agenda Woche',
timelineDay: 'Timeline Tag',
timelineWeek: 'Timeline Woche',
timelineMonth: 'Timeline Monat',
timelineYear: 'Timeline Jahr'
},
buttonIcons: { // Ansicht Button Icons / Pfeile für Standard Ansicht wenn kein Thema gewählt ist
prev: 'left-single-arrow',
next: 'right-single-arrow',
prevYear: 'left-double-arrow',
nextYear: 'right-double-arrow'
},
themeButtonIcons: { // Ansicht Button Icons / Pfeile für jQuery-ui Theme
prev: 'circle-triangle-w',
next: 'circle-triangle-e',
prevYear: 'seek-prev',
nextYear: 'seek-next'
},
bootstrapGlyphicons: { // Ansicht Button Icons für Bootstrap3 Theme / Liste Glyphicons: https://getbootstrap.com/docs/3.3/components/#glyphicons
close: 'glyphicon-remove',
prev: 'glyphicon-chevron-left',
next: 'glyphicon-chevron-right',
prevYear: 'glyphicon-backward',
nextYear: 'glyphicon-forward'
},
bootstrapFontAwesome: { // Ansicht Button Icons für Bootstrap4 Theme / Liste Awesome Glyphicons: https://fontawesome.com/icons?d=gallery
close: 'fa-times',
prev: 'fa-chevron-left',
next: 'fa-chevron-right',
prevYear: 'fa-angle-double-left',
nextYear: 'fa-angle-double-right'
},
customButtons: { // Eigene Button Erstellung
custom1: {
text: 'custom 1',
click: function() {
alert('clicked custom button 1!');
}
},
custom2: {
text: 'custom 2',
click: function() {
alert('clicked custom button 2!');
}
}
},
eventSources: [
{
url: 'feed.php',
data: {
InstanceID: 17299 // Reader Frühschicht
},
color: '#ffd700', // <- frei konfigurierbar
textColor: 'white', // <- frei konfigurierbar
error: function() {
$('#script-warning').show();
}
}, // hier kann auch Schluss sein falls nur ein einziger Feed angezeigt werden soll
{
url: 'feed.php',
data: {
InstanceID: 59134 // Reader Mittagschicht
},
color: 'red', // <- frei konfigurierbar
textColor: 'white', // <- frei konfigurierbar
timezone: 'local',
error: function() {
$('#script-warning').show();
}
}, // hier können noch weitere Feeds angehängt werden
{
url: 'feed.php',
data: {
InstanceID: 48984 // Reader Nachtschicht
},
color: 'green', // <- frei konfigurierbar
textColor: 'black', // <- frei konfigurierbar
error: function() {
$('#script-warning').show();
}
},
{
url: 'feed.php',
data: {
InstanceID: 10453 // Reader Freie Tage
},
color: '#1874cd', // <- frei konfigurierbar
textColor: 'black', // <- frei konfigurierbar
error: function() {
$('#script-warning').show();
}
},
{
url: 'feed.php',
data: {
InstanceID: 41085 // Müllabfuhr Termine
},
color: '#008B8B', // <- frei konfigurierbar
textColor: 'black', // <- frei konfigurierbar
error: function() {
$('#script-warning').show();
}
},
{
url: 'feed.php',
data: {
InstanceID: 46733 // <- ändern!
},
color: 'green', // <- frei konfigurierbar
textColor: 'black', // <- frei konfigurierbar
error: function() {
$('#script-warning').show();
}
}
]
})
});
</script>
</head>
<body>
<div id='calendar'></div>
</body>
</html>
Anbei noch die Module.php:
PHP-Code:
<?
use RRule\RRule;
include_once __DIR__ . '/../libs/base.php';
include_once __DIR__ . '/../libs/includes.php';
include_once __DIR__ . '/../libs/iCalcreator-master/autoload.php';
include_once __DIR__ . '/../libs/php-rrule-master/src/RRuleInterface.php';
include_once __DIR__ . '/../libs/php-rrule-master/src/RfcParser.php';
include_once __DIR__ . '/../libs/php-rrule-master/src/RRule.php';
include_once __DIR__ . '/../libs/php-rrule-master/src/RSet.php';
define( 'ICCR_Debug', false );
define( 'ICCR_Property_CalendarURL', 'CalendarServerURL' );
define( 'ICCR_Property_Username', 'Username' );
define( 'ICCR_Property_Password', 'Password' );
define( 'ICCR_Property_DaysToCache', 'DaysToCache' );
define( 'ICCR_Property_UpdateFrequency', 'UpdateFrequency' );
define( 'ICCR_Default_DaysToCache', 366 );
define( 'ICCR_Default_UpdateFrequency', 15 );
/***********************************************************************
* iCal importer class
************************************************************************/
class ICCR_iCalImporter
{
private $Timezone;
private $NowDateTime;
private $NowTimestamp;
private $PostNotifySeconds = 0;
private $SecondsToCache;
private $CacheSizeDateTime;
private $CalendarTimezones;
/*
debug method, depending on defined constant
*/
private function LogDebug( $Debug )
{
if ( ICCR_Debug )
IPS_LogMessage( 'iCalImporter Debug', $Debug );
}
/*
convert the timezone RRULE to a datetime object in the given/current year
*/
private function TZRRuleToDateTime( $RRule, $Year = '' )
{
$result = false;
// always yearly, once a year
if ( array_key_exists( "BYDAY", $RRule ) )
{
if ( array_key_exists( "0", $RRule[ "BYDAY" ] ) )
{
$Occ = $RRule[ "BYDAY" ][ "0" ];
if ( array_key_exists( "DAY", $RRule[ "BYDAY" ] ) )
{
$Day = $RRule[ "BYDAY" ][ "DAY" ];
if ( array_key_exists( "BYMONTH", $RRule ) )
{
$Month = $RRule[ "BYMONTH" ];
$DateObj = DateTime::createFromFormat( '!m', $Month );
$MonthName = $DateObj->format( 'F' );
switch ( $Day ) // RFC5545
{
case "MO": $DayName = "Monday"; break;
case "TU": $DayName = "Tuesday"; break;
case "WE": $DayName = "Wednesday"; break;
case "TH": $DayName = "Thursday"; break;
case "FR": $DayName = "Friday"; break;
case "SA": $DayName = "Saturday"; break;
case "SU": $DayName = "Sunday"; break;
default: $DayName = "Sunday"; break;
}
return date_timestamp_set( new DateTime, strtotime( $Occ . " " . $DayName . " " . $MonthName . " " . $Year . "00:00:00" ) );
}
}
}
}
}
/*
apply the time offset from a timezone provided by the loaded calendar
*/
private function ApplyCustomTimezoneOffset( $EventDateTime, $CustomTimezoneName )
{
// is timezone in calendar provided timezone?
foreach ( $this->CalendarTimezones as $CalendarTimezone )
{
if ( $CalendarTimezone[ "TZID" ] == $CustomTimezoneName )
{
$DSTStartDateTime = $this->TZRRuleToDateTime( $CalendarTimezone[ "DSTSTART" ], $EventDateTime->format( "Y" ) );
$DSTEndDateTime = $this->TZRRuleToDateTime( $CalendarTimezone[ "DSTEND" ], $EventDateTime->format( "Y" ) );
// between these dates?
if ( ( $EventDateTime > $DSTStartDateTime ) && ( $EventDateTime < $DSTEndDateTime ) )
{
$EventDateTime->add( DateInterval::createFromDateString( strtotime( $CalendarTimezone[ "DSTOFFSET" ] ) ) );
}
else
{
$EventDateTime->add( DateInterval::createFromDateString( strtotime( $CalendarTimezone[ "OFFSET" ] ) ) );
}
break;
}
}
return $EventDateTime;
}
/*
convert iCal format to PHP DateTime respecting timezone information
every information will be transformed into the current timezone!
*/
private function iCalDateTimeArrayToDateTime( $DT )
{
$Year = $DT[ "value" ][ "year" ];
$Month = $DT[ "value" ][ "month" ];
$Day = $DT[ "value" ][ "day" ];
$WholeDay = false;
if ( array_key_exists( "params", $DT ) && array_key_exists( "VALUE", $DT[ "params" ] ) )
{
// whole-day, this is not timezone relevant!
if ( "DATE" == $DT[ "params" ][ "VALUE" ] )
$WholeDay = true;
}
if ( array_key_exists( "hour", $DT[ "value" ] ) )
$Hour = $DT[ "value" ][ "hour" ];
else
$Hour = 0;
if ( array_key_exists( "min", $DT[ "value" ] ) )
$Min = $DT[ "value" ][ "min" ];
else
$Min = 0;
if ( array_key_exists( "sec", $DT[ "value" ] ) )
$Sec = $DT[ "value" ][ "sec" ];
else
$Sec = 0;
// owncloud calendar
if ( array_key_exists( "params", $DT ) && array_key_exists( "TZID", $DT[ "params" ] ) )
$Timezone = $DT[ "params" ][ "TZID" ];
// google calendar
else if ( array_key_exists( "tz", $DT[ "value" ] ) )
$Timezone = "UTC";
else
$Timezone = $this->Timezone;
$DateTime = new DateTime();
if ( $WholeDay )
{
$DateTime->setTimezone( timezone_open( $this->Timezone ) );
$DateTime->setDate( $Year, $Month, $Day );
$DateTime->setTime( $Hour, $Min, $Sec );
}
else
{
$IsStandardTimezone = true;
$SetTZResult = @$DateTime->setTimezone( timezone_open( $Timezone ) );
if ( false < $SetTZResult )
{
// no standard timezone, set to UTC first
$DateTime->setTimezone( timezone_open ( 'UTC' ) );
$IsStandardTimezone = false;
}
$DateTime->setDate( $Year, $Month, $Day );
$DateTime->setTime( $Hour, $Min, $Sec );
if ( !$IsStandardTimezone )
{
// set UTC offset if provided in calendar data
$DateTime = $this->ApplyCustomTimezoneOffset( $DateTime, $Timezone );
}
// convert to local timezone
$DateTime->setTimezone( timezone_open( $this->Timezone ) );
}
return $DateTime;
}
/*
basic setup
*/
function __construct( $PostNotifyMinutes, $DaysToCache )
{
$this->Timezone = date_default_timezone_get();
$this->NowDateTime = date_create();
$this->NowTimestamp = date_timestamp_get( $this->NowDateTime );
$this->PostNotifySeconds = $PostNotifyMinutes * 60;
$this->SecondsToCache = $DaysToCache * 24 * 60 * 60;
$this->CacheSizeDateTime = date_timestamp_set( date_create(), $this->NowTimestamp + $this->SecondsToCache );
}
/*
main import method
*/
public function ImportCalendar( $iCalData )
{
$iCalCalendarArray = array();
$this->CalendarTimezones = array();
$Config = array(
"unique_id" => "ergomation.de",
"TZID" => $this->Timezone,
"X-WR-TIMEZONE" => $this->Timezone
);
$vCalendar = new kigkonsult\iCalcreator\vcalendar( $Config );
$vCalendar->parse( $iCalData );
// get calendar supplied timezones
while( $Comp = $vCalendar->getComponent( "vtimezone" ) )
{
$ProvidedTZ = array();
$Standard = $Comp->getComponent( "STANDARD" );
$Daylight = $Comp->getComponent( "DAYLIGHT" );
if ( ( false !== $Standard ) && ( false !== $Daylight ) )
{
$ProvidedTZ[ "TZID" ] = $Comp->getProperty( "TZID" );
$ProvidedTZ[ "DSTSTART" ] = $Daylight->getProperty( "rrule", false, false );
$ProvidedTZ[ "DSTEND" ] = $Standard->getProperty( "rrule", false, false );
$ProvidedTZ[ "OFFSET" ] = $Standard->getProperty( "TZOFFSETTO" );
$ProvidedTZ[ "DSTOFFSET" ] = $Standard->getProperty( "TZOFFSETFROM" );
$this->CalendarTimezones[] = $ProvidedTZ;
}
}
while( $Comp = $vCalendar->getComponent( "vevent" ) )
{
$ThisEventArray = array();
$ThisEvent = array();
$ThisEvent[ "UID" ] = $Comp->getProperty( "uid", false, false );
$ThisEvent[ "Name" ] = $Comp->getProperty( "summary", false, false );
$ThisEvent[ "Location" ] = $Comp->getProperty( "location", false, false );
$ThisEvent[ "Description" ] = $Comp->getProperty( "description", false, false );
$StartingTime = $this->iCalDateTimeArrayToDateTime( $Comp->getProperty( "dtstart", false, true ) );
$EndingTime = $this->iCalDateTimeArrayToDateTime( $Comp->getProperty( "dtend", false, true ) );
$StartingTimestamp = date_timestamp_get( $StartingTime );
$EndingTimestamp = date_timestamp_get( $EndingTime );
$Duration = $EndingTimestamp - $StartingTimestamp;
if ( $this->NowTimestamp < ( $StartingTimestamp - $this->SecondsToCache ) )
{
// event is too far in the future, ignore
$this->LogDebug( "Event " . $ThisEvent[ "Name" ] . "is too far in the future, ignoring" );
}
else
{
// check if recurring
$CalRRule = $Comp->getProperty( "rrule", false, false );
if ( is_array( $CalRRule ) )
{
// $this->LogDebug( "Recurring event" );
if ( array_key_exists( "UNTIL", $CalRRule ) )
{
$UntilDateTime = $this->iCalDateTimeArrayToDateTime( array( "value" => $CalRRule[ "UNTIL" ] ) );
// replace iCal date array with datetime object
$CalRRule[ "UNTIL" ] = $UntilDateTime;
}
// replace/set iCal date array with datetime object
$CalRRule[ "DTSTART" ] = $StartingTime;
$RRule = new RRule( $CalRRule );
foreach ( $RRule->getOccurrencesBetween( $this->NowDateTime, $this->CacheSizeDateTime ) as $Occurrence )
{
$ThisEvent[ "From" ] = date_timestamp_get( $Occurrence );
$ThisEvent[ "To" ] = $ThisEvent[ "From" ] + $Duration;
$ThisEvent[ "FromS" ] = date( "Y-m-d H:i:s", $ThisEvent[ "From" ] );
$ThisEvent[ "ToS" ] = date( "Y-m-d H:i:s", $ThisEvent[ "To" ] );
$ThisEventArray[] = $ThisEvent;
}
}
else
{
$ThisEvent[ "From" ] = $StartingTimestamp;
$ThisEvent[ "To" ] = $EndingTimestamp;
$ThisEvent[ "FromS" ] = date( "Y-m-d H:i:s", $ThisEvent[ "From" ] );
$ThisEvent[ "ToS" ] = date( "Y-m-d H:i:s", $ThisEvent[ "To" ] );
$ThisEventArray[] = $ThisEvent;
}
foreach ( $ThisEventArray as $ThisEvent )
{
if ( $this->NowTimestamp > ( $ThisEvent[ "To" ] + $this->PostNotifySeconds ) )
{
// event is past notification times, ignore
$this->LogDebug( "Event " . $ThisEvent[ "Name" ] . " is past the notification times, ignoring" );
}
else
{
// insert event(s)
$iCalCalendarArray[] = $ThisEvent;
}
}
}
}
// sort by start date/time to make the check on changes work
usort( $iCalCalendarArray, function ( $a, $b ) { return $a[ "From" ] - $b[ "From" ]; } );
return $iCalCalendarArray;
}
}
/***********************************************************************
* module class
************************************************************************/
class iCalCalendarReader extends ErgoIPSModule {
// buffer for ical calendar stream between function calls
private $curl_result = '';
/***********************************************************************
* customized debug methods
************************************************************************/
/*
debug on/off is a defined constant
*/
protected function IsDebug()
{
return ICCR_Debug;
}
/*
sender for debug messages is set
*/
protected function GetLogID()
{
return IPS_GetName( $this->InstanceID );
}
/***********************************************************************
* standard module methods
************************************************************************/
/*
basic setup
*/
public function Create()
{
parent::Create();
// create configuration properties
$this->RegisterPropertyString( ICCR_Property_CalendarURL, '' );
$this->RegisterPropertyString( ICCR_Property_Username, '' );
$this->RegisterPropertyString( ICCR_Property_Password, '' );
$this->RegisterPropertyInteger( ICCR_Property_DaysToCache, ICCR_Default_DaysToCache );
$this->RegisterPropertyInteger( ICCR_Property_UpdateFrequency, ICCR_Default_UpdateFrequency );
// initialize persistence
$this->SetBuffer( "CalendarBuffer", "" );
$this->SetBuffer( "Notifications", "" );
$this->SetBuffer( "MaxPreNotifySeconds", "" );
$this->SetBuffer( "MaxPostNotifySeconds", "" );
// create timer
$this->RegisterTimer( "Update", 0, 'ICCR_UpdateCalendar( $_IPS["TARGET"] );' ); // no update on init
$this->RegisterTimer( "Cron", 1000 * 60 , 'ICCR_TriggerNotifications( $_IPS["TARGET"] );' ); // cron runs every minute
}
/*
react on user configuration dialog
*/
public function ApplyChanges() {
parent::ApplyChanges();
$this->SetTimerInterval( "Update", $this->GetUpdateFrequency() * 1000 * 60 );
$Status = $this->CheckCalendarURLSyntax();
$this->SetStatus( $Status );
// ready to run an update?
if ( 102 == $Status )
$this->UpdateClientConfig();
}
/***********************************************************************
* access methods to persistence
************************************************************************/
// property persistence (lasts across restarts)
private function GetCalendarServerURL()
{
return $this->ReadPropertyString( ICCR_Property_CalendarURL );
}
private function GetUsername()
{
return $this->ReadPropertyString( ICCR_Property_Username );
}
private function GetPassword()
{
return $this->ReadPropertyString( ICCR_Property_Password );
}
private function GetDaysToCache()
{
return $this->ReadPropertyInteger( ICCR_Property_DaysToCache );
}
private function GetUpdateFrequency()
{
return $this->ReadPropertyInteger( ICCR_Property_UpdateFrequency );
}
// runtime persistence (does not lasts across restarts)
private function GetCalendar()
{
return $this->GetBuffer( "CalendarBuffer" );
}
private function SetCalendar( $Value )
{
$this->SetBuffer( "CalendarBuffer", $Value );
}
private function GetNotifications()
{
return json_decode( $this->GetBuffer( "Notifications" ), true );
}
/*
save notifications and find the extremum
*/
private function SetNotifications( $Value )
{
$this->SetBuffer( "Notifications", json_encode( $Value ) );
$MaxPreNS = 0;
$MaxPostNS = 0;
if ( is_array( $Value ) )
{
foreach ( $Value as $Entry )
{
if ( array_key_exists( "PreNS", $Entry ) )
if ( $Entry[ "PreNS" ] > $MaxPreNS )
$MaxPreNS = $Entry[ "PreNS" ];
if ( array_key_exists( "PostNS", $Entry ) )
if ( $Entry[ "PostNS" ] > $MaxPostNS )
$MaxPostNS = $Entry[ "PostNS" ];
}
}
$this->SetMaxPreNotifySeconds( $MaxPostNS );
$this->SetMaxPostNotifySeconds( $MaxPostNS );
}
private function GetMaxPreNotifySeconds()
{
$Value = json_decode( $this->GetBuffer( "MaxPreNotifySeconds" ), true );
if ( empty( $Value ) )
return 0;
else
return $Value;
}
private function SetMaxPreNotifySeconds( $Value )
{
$this->SetBuffer( "MaxPreNotifySeconds", json_encode( $Value ) );
}
private function GetMaxPostNotifySeconds()
{
$Value = json_decode( $this->GetBuffer( "MaxPostNotifySeconds" ), true );
if ( empty( $Value ) )
return 0;
else
return $Value;
}
private function SetMaxPostNotifySeconds( $Value )
{
$this->SetBuffer( "MaxPostNotifySeconds", json_encode( $Value ) );
}
/*
check if calendar URL syntax is valid
*/
public function CheckCalendarURLSyntax()
{
$Status = 102;
// validate saved properties
$NC_URL = $this->GetCalendarServerURL();
if ( '' == $NC_URL )
{
$Status = 104;
}
else
{
// check URL format
if ( false === filter_var( $NC_URL, FILTER_VALIDATE_URL ) )
{
$Status = 200;
}
}
return $Status;
}
/***********************************************************************
* configuration helper
************************************************************************/
/*
get all notifications information from connected child instances
*/
private function GetChildrenConfig()
{
// empty configuration buffer
$Notifications = array();
$ChildInstances = IPS_GetInstanceListByModuleID( ICCN_Instance_GUID );
if ( sizeof( $ChildInstances ) == 0 )
return;
// transfer configuration
foreach( $ChildInstances as $ChInstance )
if ( IPS_GetInstance( $ChInstance )[ "ConnectionID" ] == $this->InstanceID )
{
$ClientConfig = json_decode( IPS_GetConfiguration( $ChInstance ), true );
$ClientPreNotifyMinutes = $ClientConfig[ "PreNotifyMinutes" ];
$ClientPostNotifyMinutes = $ClientConfig[ "PostNotifyMinutes" ];
// new entry
$Notifications[ $ChInstance ] = array(
"PreNS" => $ClientPreNotifyMinutes * 60,
"PostNS" => $ClientPostNotifyMinutes * 60,
"Status" => 0,
"Reason" => array()
);
}
$this->SetNotifications( $Notifications );
}
/***********************************************************************
* calendar loading and conversion methods
************************************************************************/
/*
load calendar from URL into $this->curl_result, returns IPS status value
*/
private function LoadCalendarURL( $URL )
{
$result = 102;
$username = $this->GetUsername();
$password = $this->GetPassword();
$this->LogDebug( 'Entering LoadCalendarURL()' );
$curl = curl_init();
curl_setopt( $curl, CURLOPT_URL, $URL );
curl_setopt( $curl, CURLOPT_SSL_VERIFYPEER, false ); // yes, easy but lazy
curl_setopt( $curl, CURLOPT_CONNECTTIMEOUT, 25 ); // 30s maximum script execution time
curl_setopt( $curl, CURLOPT_TIMEOUT, 25 ); // 30s maximum script execution time
curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, 1 );
curl_setopt( $curl, CURLOPT_MAXREDIRS, 5 ); // educated guess
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1 );
if ( '' != $username )
curl_setopt( $curl, CURLOPT_USERPWD, $username . ':' . $password );
$this->LogDebug( 'Loading from URL...' );
$this->curl_result = curl_exec ( $curl );
$this->LogDebug( 'Loaded' );
$curl_error_nr = curl_errno( $curl );
$curl_error_str = curl_error( $curl );
curl_close( $curl );
// check on curl error
if ( $curl_error_nr )
{
$this->LogError( 'Error on connect - ' . $curl_error_str . ', for ' . $URL );
// only differentiate between invalid, connect, SSL and auth
switch ( $curl_error_nr )
{
case 1:
case 3:
case 4:
// invalid URL
$result = 201;
break;
case 35:
case 53:
case 54:
case 58:
case 59:
case 60:
case 64:
case 66:
case 77:
case 80:
case 82:
case 83:
// SSL error
$result = 202;
break;
case 35:
case 67:
// auth error
$result = 203;
break;
default:
// connect error
$result = 204;
break;
}
}
// no curl error, continue
else
{
if ( substr( $this->curl_result, 0 ,15 ) != "BEGIN:VCALENDAR" )
{
// handle error document
$result = 205;
// ownCloud sends XML error messages
libxml_use_internal_errors( true );
$XML = simplexml_load_string( $this->curl_result );
// owncloud error?
if ( $XML !== false )
{
$XML->registerXPathNamespace( 'd', 'DAV:' );
if (0 != count( $XML->xpath( '//d:error' ) ) )
{
// XML error document
$exception = $XML->children( 'http://sabredav.org/ns' )->exception;
$message = $XML->children( 'http://sabredav.org/ns' )->message;
if ( 'Sabre\DAV\Exception\NotAuthenticated' == $exception )
{
$result = 203;
}
$this->LogError( 'Error: ' . $exception . ' - ' . $message );
}
}
// synology sends plain text
else if ( 'Please log in' == substr( $this->curl_result, 0 ,13 ) )
{
$this->LogError( 'Error logging on - invalid user/password combination for ' . $URL );
$result = 203;
}
// everything else goes here
else
{
$this->LogError( 'Error on connect - this is not a valid calendar URL: ' . $URL );
}
}
}
return $result;
}
/*
load calendar, convert calendar, return event array of false
*/
private function ReadCalendar()
{
$result = $this->CheckCalendarURLSyntax();
if ( 102 != $result )
return false;
$result = $this->LoadCalendarURL( $this->GetCalendarServerURL() );
if ( 102 != $result )
return false;
$MyImporter = new ICCR_iCalImporter(
$this->GetMaxPostNotifySeconds(),
$this->GetDaysToCache()
);
$iCalCalendarArray = $MyImporter->ImportCalendar( $this->curl_result );
return json_encode( $iCalCalendarArray );
}
/*
entry point for the periodic calendar update timer
also used to trigger manual calendar updates after configuration changes
accessible for external scripts
*/
public function UpdateCalendar()
{
$this->LogDebug( 'Starting calendar update' );
$TheOldCalendar = $this->GetCalendar();
$TheNewCalendar = $this->ReadCalendar();
if ( false === $TheNewCalendar )
{
$this->LogDebug( 'Failed to load calendar' );
return;
}
if ( 0 !== strcmp( $TheOldCalendar, $TheNewCalendar ) )
{
$this->LogDebug( 'Updating internal calendar' );
$this->SetCalendar( $TheNewCalendar );
}
else
$this->LogDebug( 'Calendar still in sync' );
}
/***********************************************************************
* calendar notifications methods
************************************************************************/
/*
check if event is triggering a presence notification
*/
private function CheckPresence( $Start, $End, $Pre, $Post, $Timestamp )
{
if ( ( $Start - $Pre ) < $Timestamp )
if ( ( $End + $Post ) > $Timestamp )
return true;
return false;
}
/*
entry point for the periodic 1m notifications timer
also used to trigger manual updates after configuration changes
accessible for external scripts
*/
public function TriggerNotifications()
{
$this->LogDebug( 'Entering TriggerNotifications()' );
$NowTimestamp = date_timestamp_get( date_create() );
$MaxPreNotifySeconds = $this->GetMaxPreNotifySeconds();
$Notifications = $this->GetNotifications();
if ( empty( $Notifications ) )
return;
foreach ($Notifications as $Notification )
{
$Notification[ "Status" ] = false;
$Notification[ "Reason" ] = array();
}
$TheCalendar = $this->GetCalendar();
$iCalCalendarArray = json_decode( $TheCalendar, true );
if ( !empty( $iCalCalendarArray ) )
{
foreach ( $iCalCalendarArray as $iCalItem )
{
foreach ($Notifications as $ChInstanceID => $Notification )
{
if ( $this->CheckPresence( $iCalItem[ "From" ], $iCalItem[ "To" ], $Notification[ "PreNS" ], $Notification[ "PostNS" ], $NowTimestamp ) )
{
// append status and reason to the corresponding notification
$Notifications[ $ChInstanceID ][ "Status" ] = true;
$Notifications[ $ChInstanceID ][ "Reason" ][] = $iCalItem;
}
}
}
}
// set status back to children
foreach ($Notifications as $ChInstanceID => $Notification )
{
$this->SendDataToChildren( json_encode(
array(
"DataID" => ICCR_TX,
"InstanceID" => $ChInstanceID,
"Notify" => array(
"Status" => $Notification[ "Status" ],
"Reason" => $Notification[ "Reason" ]
)
)
) );
}
}
/*
entry point for a child to inform the parent to update its children configuration
accessible for external scripts
*/
public function UpdateClientConfig()
{
$this->GetChildrenConfig();
$this->UpdateCalendar();
$this->TriggerNotifications();
}
/***********************************************************************
* methods for script access
************************************************************************/
/*
returns the registered notifications structure
*/
public function GetClientConfig()
{
return $this->GetNotifications();
}
/*
returns the internal calendar structure
*/
public function GetCachedCalendar()
{
return $this->GetCalendar();
}
}
?>
Original Zeile 174:
PHP-Code:
// no standard timezone, set to UTC first
$DateTime->setTimezone( timezone_open ( 'UTC' ) );
$IsStandardTimezone = false;
PHP-Code:
// no standard timezone, set to UTC first
$DateTime->setTimezone( timezone_open ( 'Europe/Berlin' ) );
$IsStandardTimezone = false;
Was ich auch noch nicht heraus gefunden habe, das die vergangenen Termine angezeigt bleiben, wie das z.B. im Google Kalender der Fall ist.
Ich hoffe es kann mir hier im Forum jemand einen Tipp geben.
Viele Grüße
Stefan
Kommentar