[Modul] iCal Calender in IP Symcon lesen und verarbeiten

Endlich!:slight_smile:

Jetzt habe ich den Fehler gefunden. Die Dateien calendar.html und feed.php hatte ich von git-Server genommen. Diese funktionieren nicht! Man muß die Dateien aus dem Modul /docs/Exempels nehmen, dann klappt es auf Anhieb.
Danke nochmal für die Hilfe.

Gruß qinshi

Wenn ich im Google Kalender im Chrome Browser einen neuen Termin eingebe, wird der im Webfront nicht übernommen. Was mache ich falsch?

Der Google Kalender übernimmt den Termin? Ist der Effekt nur bei Chrome?

Gruß q

Wenn ich einen Termin anlege übernimmt der Google Kalender ihn in meinem Account, aber im Webfront wird er nicht angezeigt. Beim Edge Browser das gleiche.

Ist das so richtig, wenn man die Termine anzeigen möchte, das man den IPS Dienst neustarten muss?

Hallo Stefan71,

ich habe das mit Chrome und dem Google Kalender getestet, bei mir funktioniert es. Da muß bei Dir ein anderes Problem vorliegen. Allerdings kann es dauern bis die Termine im Kalender erscheinen. Wenn der IP Symcon Dienst gestartet ist, und der Kalender richtig installiert ist, holt er sich die Termine automatisch. Ggf muß du die Anzeige im Browser aktualisieren.

Gruß qinshi

Hallo,
nur den Browser aktualisieren reicht bei mir nicht. Was bei mir auch nicht funktioniert mit den Themes. Wenn ich im Skript ein anderes eingebe, wird das nicht übernommen.

Wo hast du denn den zip Ordner abgelegt?

Ich habe den zip Ordner überhaupt nicht abgelegt sondern nur die Datein wie oben beschrieben. Um die Themes zu wechseln muß man sicher tiefer in die Materie einsteigen. Dafür gibt es hier sicher experten. Meine Kentnisse reichen dafür noch nicht. Ggf muß Du alles nochmal neu installieren. Irgendwo hat sich sicher ein Fehler eingeschlichen.

Viel Erfolg

q

Wenn ich das richtig lese, braucht man ja in der calender.html nur in Zeile 6 den Theme Namen zu ändern.

<link rel="stylesheet" type="text/css" href="https://bootswatch.com/paper/bootstrap.min.css">

Hallo Stefan71,

das sehe ich genauso. Da mußt Du sicher den Pfad anpassen. Vielleicht liegt da der Fehler.

Gruß q

Was mir auch aufgefallen ist das bei mir die Pfeile fehlen.

iCal Pfeile.PNG

Edit: Wenn man das Theme wechseln möchte ist der richtige Pfad:

https://bootswatch.com/3/united/bootstrap.min.css

Bei mir fehlen die Pfeile auch. Danke für den Link zum Pfad.

q

Hallo,
habe immer noch so meine Schwierigkeiten mit der Anzeige des iCal Kalenders.

Punkt 1: Obwohl mir im Google Kalender alles richtig angezeigt wird, wird im iCal Kalender einfach eine Schicht zweimal aufgeführt:

Punkt 2: Wie oben auf dem Bild auch zu sehen ist, ist die falsche Uhrzeit hinterlegt. Im Google Kalender ist die richtige Zeit abgebildet.

Punkt 3: Wo kann ich einstellen das die vergangenen Termine trotzdem angezeigt werden?

Punkt 4: Wenn ich das Theme System wechsle auf Bootstrap 4, werden mir keine Pfeile mehr angezeigt.

Hat jemand einen Tip für mich?

Hallo,
gibt es nicht noch mehr User die das Modul hier erfolgreich am laufen haben?

Jetzt habe ich ein weiteren Fehler:

Alle Termine verschwinden sofort, sobald der folgende Tag anbricht, aber wieso bleibt der Termin des 4 Oktober / Blaue Tonne bestehen? Wieso ist der Samstag jetzt schon weg, obwohl ich dort einen ganztägigen Termin eingegeben habe. Ich weiß nicht wo die Fehler liegen auch das mit der Uhrzeit.

Gibt es nicht noch sowas ähnliches wie dieses Modul?

Hallo Stefan71

bei mir läuft es, aber die Anzeige der Termine ist nicht zufriedenstellnd. Ich habe im Augenblick andere Baustellen

Gruß q

Hi,
ich bin bald am verzweifeln. Komme einfach nicht darauf wie ich die richtige Uhrzeit anzeigen kann für die Termine. Habe schon einiges probiert, bringt das Ganze aber nicht zum Erfolg.

Das steht in der Doku:

https://fullcalendar.io/docs/timezoneParam

https://fullcalendar.io/docs/timezone

Meine calendar.html sieht zurzeit so aus, bin aber noch nicht ganz fertig:

<!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
        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>

Hallo,
habe meine Probleme mit der Zeitanzeige im iCal Modul in Verbindung mit dem Google Kalender. Wenn ich einen einzel Termin anlege stimmt die Zeitangabe, wenn ich einen wiederholten Termin anlege ist die Zeitangabe um 2 Stunden nach vorne versetzt.
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.

Original: (Zeile 174)

// no standard timezone, set to UTC first
				$DateTime->setTimezone( timezone_open ( 'UTC' ) );
				$IsStandardTimezone = false;

Geändert:

// no standard timezone, set to UTC first
				$DateTime->setTimezone( timezone_open ( 'Europe/Berlin' ) );
				$IsStandardTimezone = false;

Module.php:

<?

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();
    }

}

?>

Meine PHP Kenntnisse reichen leider nicht um selbst das Skript zu ändern. Aber irgendwie muss es doch zu schaffen sein die richtige Uhrzeit anzeigen zu lassen.

Gruß
Stefan

@Slash, ich habe das Modul bei mir eingebunden, funktioniert super, danke dafür!
Aber könntest du die form.json anpassen, dass das Modul auch mit der Webkonsole funktioniert?

Ich kann dir auch gerne dafür einen PR schicken.

Grüße,
Kai

Mir ist aufgefallen, dass das Modul Probleme bei Serienterminen hat. Wenn einzelne Termine eines Serientermins geändert werden, dann erscheinen Termine doppelt.

Kann leider keinen PR dafür anbieten. Die Ermittlung scheint mir etwas komplexer zu sein.

Gruß

Burkhard

Hallo zusammen,

unter V5 auf einem raspberry pi bekomme ich folgenden Fehler bei übernahme der Einstallungen:

Kalender URL habe ich wie auf github beschrieben von meiner Synology kopiert. Hatte das schon jemand?

Gruß Kay