Neue Serie: Hausgemacht. Auch wir haben im Alltag Probleme, für die es keine fertige Lösung von der Stange gibt. In dieser Serie zeigen wir, welche Herausforderungen bei uns auftauchen und wie wir sie lösen, mit denselben Prinzipien, nach denen wir auch Kundenprojekte angehen. Mit allem, was funktioniert hat, und allem, was nicht. Teil 1 beginnt bei einem Problem, das vermutlich jeder kennt, der Familie und Firma gleichzeitig organisiert. Es geht um den Kalender.
Wie bekommt man private und geschäftliche Termine unter einen Hut? Wer an dieser Stelle Tipps zur Work-Life-Balance erwartet, den muss ich enttäuschen. Ich habe weder eine Morgenroutine noch ein Achtsamkeitsritual anzubieten, und mein Kalender sah lange eher nach Work-Life-Chaos aus. Als Softwareentwickler habe ich aber einen anderen Vorteil: Wenn mich etwas nervt, kann ich ein Programm dagegen schreiben. Das löst nicht den Konflikt zwischen Job und Familie. Aber zumindest weiß jetzt der Kalender Bescheid.
Bei uns ist es normal, dass Kollegen mir Termine direkt in den Outlook-Kalender stellen. Ein Supportgespräch, eine Projektabstimmung, ein Kundentermin. Niemand fragt vorher per Mail „Hast Du Dienstag um zehn Zeit?“. Man schaut in meinen Kalender, sieht eine Lücke und bucht. Das spart allen Zeit, und genau so soll es sein.
Das funktioniert allerdings nur, wenn die Lücken stimmen. Und das taten sie nicht. Ein großer Teil meiner Termine steht gar nicht in Outlook: Arzttermine, Elternabende, Training der Kinder, Musikunterricht, Schulsport. Die liegen in unserem gemeinsamen Familienkalender auf einem eigenen Baïkal-Server und in iCloud auf dem iPhone. Und dann gibt es noch einen Kalender, in dem meine Geschwister und ich die Pflege eines Angehörigen organisieren. Für mein Team war mein Dienstagnachmittag also frei, für mich nicht. So landete ein Strategietermin genau auf dem Nachmittag, an dem ich meine Tochter zum Kieferorthopäden begleiten musste. Und weil ich zusätzlich in einem zweiten Unternehmen mit eigenem Outlook-Kalender mitarbeite, überschnitt sich auch mal ein Supportgespräch mit einem Meeting, das schlicht im anderen Kalender stand.
Die naheliegende Lösung wäre, die Familientermine einfach zusätzlich in den Geschäftskalender zu tippen. Nur pflege ich den Familienkalender nicht allein, meine Frau trägt dort genauso Termine ein und verschiebt sie. Jeder Termin müsste also doppelt gepflegt werden, und jede Änderung von ihr müsste ich von Hand nachziehen. Das geht genau so lange gut, bis ich es einmal vergesse. Und selbst wenn es klappt: Dann sieht das Team zwar, dass ich keine Zeit habe. Es sieht aber auch, warum. Ich habe vor meinem Team nichts zu verbergen. Trotzdem müssen Familientermine nicht für jeden lesbar im Geschäftskalender stehen. Für die Planung reicht es völlig zu wissen, dass ich an diesem Nachmittag nicht kann.
Der eigentliche Konflikt: Mein Team braucht Sichtbarkeit, um planen zu können. Meine Familie braucht Privatsphäre. Beides muss im selben Kalender funktionieren. Die Kollegen sollen sehen, dass ich belegt bin, aber nicht, womit. Ich selbst will alles sehen. Und meine Frau soll weiter einfach im Familienkalender planen können, ohne an Outlook denken zu müssen.
Bestandsaufnahme: Wo meine Termine überall stehen
Als ich das einmal ehrlich aufgeschrieben habe, kam diese Liste heraus:
| Kalender | System | Sieht mein Team das? |
|---|---|---|
| Appontes geschäftlich | Outlook / Microsoft 365 | Ja, hier wird geplant |
| Zweites Unternehmen | Outlook, eigener Tenant | Nein |
| Privat und Geburtstage | Google Kalender | Nein |
| Pflege eines Angehörigen, mit meinen Geschwistern geteilt | Google Kalender | Nein |
| Geteilte Familienkalender | Google, andere Domain, nur Frei/Gebucht | Nein |
| Gemeinsamer Familienkalender | Baïkal, selbst betrieben | Nein |
| Training, Musik, Schulsport | Apple iCloud | Nein |
Sieben Kalender auf vier Plattformen, davon zwei Microsoft-Tenants, und mein Team sieht genau einen. Jede App zeigt brav ihren eigenen Ausschnitt und kennt die anderen nicht. Eigentlich müsste ich vor jeder Zusage mehrere Apps durchklicken. Mache ich natürlich nicht, und genau dann kracht es.
Der Pflegekalender verdient einen zweiten Blick. Wenn ein Angehöriger Unterstützung braucht, wird die Organisation schnell zum zweiten Job. Wer fährt am Mittwoch zum Arzt mit? Wer ist da, wenn der Pflegedienst kommt? Wer übernimmt den Einkauf, wer den Anruf bei der Krankenkasse, und wer schaut am Wochenende vorbei? Wer so etwas schon einmal über eine Familiengruppe im Messenger organisiert hat, weiß, wie schnell wichtige Absprachen zwischen Urlaubsfotos und Geburtstagsgrüßen verschwinden. Bei uns steht es deshalb in einem gemeinsamen Google-Kalender. Jeder trägt ein, was er übernehmen kann, und alle sehen, wo noch eine Lücke ist.
Das funktioniert gut, solange jeder seine Zusagen auch im eigenen Alltag im Blick hat. Und genau da lag bei mir das Problem: Meine Zusage für Donnerstagnachmittag stand im Pflegekalender, mein Team plante im Outlook-Kalender. Die beiden wussten nichts voneinander.
Die Anforderung, sauber formuliert
Bevor eine Zeile Code entstand, habe ich aufgeschrieben, was die Lösung leisten muss. So, wie wir es bei jedem Kundenprojekt auch machen:
- Ein Kalender, in dem alles steht. Der Appontes-Kalender in Outlook, weil das Team dort ohnehin plant.
- Belegt heißt belegt. Jeder Termin aus jeder Quelle blockiert dort die Zeit, damit niemand mehr hineinbucht.
- Private Inhalte bleiben privat. Kollegen sehen nur „Privater Termin“, ohne Betreff, Ort oder Beschreibung.
- Kein Abtippen. Familientermine werden weiter dort gepflegt, wo die Familie sie pflegt. Der Rest passiert automatisch.
- Nichts kaputtmachen. Weder meine eigenen Termine noch die Originale in den Familienkalendern dürfen jemals verändert werden.
Die Architektur: Ein Kalender führt, alle anderen liefern zu
Alles mit allem zu synchronisieren, kam nicht in Frage. Wer schon einmal eine Zwei-Wege-Synchronisation gebaut hat, kennt das Ergebnis: Ein Termin wird in A geändert, wandert nach B, wird dort als Änderung erkannt, wandert zurück nach A, und nach ein paar Runden steht derselbe Termin viermal im Kalender. Außerdem würden Geschäftstermine im Familienkalender landen, wo sie genauso wenig verloren haben wie Familiendetails im Geschäftskalender.
Also gilt, was wir auch bei Schnittstellen rund um Sage 100 empfehlen: Pro Information gibt es genau ein führendes System.
- Der Appontes-Kalender ist die zentrale Sicht. Dort plant das Team, dort sehe ich alles.
- Alle anderen Kalender bleiben die Quelle für ihre Termine. Der Trainingstermin wird weiterhin im Familienkalender gepflegt, nicht in Outlook.
- Gespiegelt wird nur in eine Richtung. Von den Quellen nach Outlook, nie zurück. Eine Schleife kann so gar nicht erst entstehen.
Gebaut an einem Samstag
Gebaut habe ich das Ganze an einem Samstag als kleines Python-Script. Es läuft alle 15 Minuten und schaut jeweils acht Wochen nach vorn:
- Unser Familienkalender liegt auf Baïkal, einem schlanken Open-Source-Server für Kalender und Kontakte, der mit PHP auf fast jedem Webspace läuft. Unsere Familientermine liegen damit auf unserem eigenen Server und nicht bei einem großen Anbieter. iPhones und Android-Geräte synchronisieren direkt per CalDAV, und genau darüber liest auch das Script.
- iCloud wird ebenfalls über CalDAV gelesen. Dafür gibt es bei Apple ein app-spezifisches Passwort, das nur für diesen Zweck gilt und sich jederzeit einzeln widerrufen lässt. Das eigentliche Apple-Passwort bleibt, wo es hingehört.
- Google liefert seine Termine über die private iCal-Adresse des Kalenders. Ohne zusätzliches Konto und ohne Freigabe. Das gilt für meinen privaten Kalender genauso wie für den Pflegekalender mit meinen Geschwistern.
- Der zweite Outlook-Kalender wird über Microsoft Graph gelesen, mit reinem Leserecht. Keine Weiterleitungsregel, keine Kalenderfreigabe quer durch die Firmen.
- Ziel ist immer der Appontes-Kalender. Für jeden Quelltermin entsteht dort eine Kopie, die die Zeit blockiert.
Das Herzstück: Belegt für alle, lesbar nur für mich
Outlook kennt für jeden Termin eine Vertraulichkeitsstufe. Steht sie auf Privat, ist die Zeit für Kollegen belegt, und im Kalender erscheint nur „Privater Termin“. Kein Betreff, kein Ort, kein Inhalt. Ich selbst sehe den Termin vollständig, mit Uhrzeit, Ort und Notiz.
Das Team sieht also am Dienstag von 16 bis 17 Uhr einen belegten Block mit Schloss und plant drumherum. Ich sehe, dass meine Tochter zum Kieferorthopäden muss und wo die Praxis ist. Mehr braucht keine Seite.
Damit das Script die Kopien direkt mit dieser Vertraulichkeitsstufe anlegen kann, schreibt es über Microsoft Graph in meinen Kalender. Dafür habe ich einen eigenen, sehr schmalen Zugang eingerichtet:
- Eine eigene App-Registrierung in Entra ID, nur für diesen Zweck. Kein Universalschlüssel, der für alles Mögliche herhalten muss.
- Genau eine Berechtigung,
Calendars.ReadWrite, delegiert. Die App handelt also nur im Namen meines eigenen Kontos. Kein Zugriff auf Mail, Dateien oder die Kalender anderer. - Anmeldung per Gerätecode. Das Script zeigt einen Code an, ich melde mich im Browser an und bestätige. Mein Passwort kennt das Script nicht, es bekommt nur ein Token, das ausschließlich für Kalender gilt.
Wichtig dabei: Die Kopien werden schon privat angelegt und nicht erst nachträglich umgestellt. Es gibt also keinen Moment, in dem ein Familientermin kurz für alle lesbar im Kalender steht.
Privat ist kein Zugriffsschutz. Microsoft weist selbst darauf hin: Wer Leserechte auf Deinen Kalender hat, kommt technisch auch an private Elemente heran, und Stellvertretungen mit der Option „private Elemente anzeigen“ sehen sie ohnehin. Ich habe die Freigabe für meine Kollegen deshalb zusätzlich auf Frei/Gebucht gestellt. Die Freigabe regelt, was andere sehen dürfen. Das Privat-Kennzeichen sorgt dafür, dass in Outlook auch wirklich nur „Privater Termin“ steht.
Die Regeln, die das Ganze sicher machen
Termine von A nach B zu schreiben ist trivial. Der eigentliche Aufwand steckte in den Regeln, die verhindern, dass so ein Abgleich irgendwann etwas kaputt macht, das ihm nicht gehört, oder etwas verrät, das er nicht verraten soll.
Jede Kopie trägt ein Kennzeichen
Jeder kopierte Termin bekommt eine Markierung, aus welchem Kalender er stammt und zu welchem Originaltermin er gehört. Der Abgleich fasst ausschließlich Termine mit dieser Markierung an. Er legt Kopien an, aktualisiert sie und löscht Kopien, deren Original verschwunden ist. Die Termine, die mein Team bei mir einstellt, meine eigenen Einträge und die Originale in den Familienkalendern berührt er nie.
Das Kennzeichen steckt in einer erweiterten Eigenschaft des Termins, die Outlook gar nicht anzeigt. In meiner ersten Version stand es noch als letzte Zeile in der Notiz. Für Kollegen unsichtbar, weil privat, für mich aber jedes Mal mit im Bild.
Warum der Aufwand? Ein Abgleich, der löschen darf, muss zweifelsfrei wissen, was er selbst angelegt hat. Ohne Kennzeichen wäre ein Logikfehler genau einen Durchlauf davon entfernt, echte Kundentermine zu löschen. Mit Kennzeichen ist das Schlimmste, was passieren kann, eine überflüssige Kopie. Als zusätzliche Bremse bricht das Script ab, wenn ein Durchlauf ungewöhnlich viele Kopien löschen will, und eine Quelle, die gerade nicht erreichbar ist, löst gar keine Löschungen aus.
Keine Teilnehmer, keine Einladungen
Kopierte Termine werden grundsätzlich ohne Teilnehmer angelegt. Klingt nach Detail, ist aber entscheidend. Ein Termin mit Teilnehmern verschickt beim Anlegen, Ändern und Löschen Einladungen und Absagen. Ich wollte nicht, dass ein Trainer oder ein Kollege alle 15 Minuten Post aus meinem Kalender bekommt, schon gar nicht mit Details aus dem Familienkalender.
Ganztägige Termine blockieren nichts
Geburtstage, Ferien, Papiersammlung: Ganztägige Einträge werden gespiegelt, aber als frei eingetragen. Ich sehe sie, und das Team kann mir an einem Geburtstag trotzdem ein Meeting einstellen. Sonst wäre mein Kalender an jedem Tag mit einem Geburtstag komplett ausgebucht, und bei der Größe meines Bekanntenkreises wären das erstaunlich viele Tage.
Beim Pflegekalender nur das Nötigste
Beim Pflegekalender bin ich noch einen Schritt weiter gegangen. Dort stehen Arztbesuche, Termine mit dem Pflegedienst und manchmal eine Notiz, wie es gerade geht. Das sind Gesundheitsdaten eines Menschen, der selbst gar nicht gefragt wurde, ob sie in irgendeinem Firmenkalender landen sollen. Die gehören dort nicht hin, auch nicht als privater Termin.
Deshalb übernimmt der Abgleich aus diesem Kalender nur die Termine, in denen mein Name steht, also das, was ich selbst übernommen habe. Und auch die nur als Zeitblock mit dem neutralen Betreff „Familie“, ohne Ort und ohne Beschreibung. Mein Team sieht wie immer „Privater Termin“. Ich sehe „Familie“ und weiß, dass ich am Donnerstag ab 14 Uhr nicht kann. Was genau ansteht, schaue ich im Pflegekalender nach, dort, wo meine Geschwister und ich es gemeinsam pflegen. Die Termine meiner Geschwister tauchen bei mir gar nicht auf. Die blockieren schließlich nicht meine Zeit.
Was ich bewusst nicht spiegle
Die geteilten Familienkalender aus der anderen Google-Domain sehe ich nur als Frei/Gebucht. Ich weiß also, dass dort um 16 Uhr etwas ist, aber nicht was. Technisch ließen sich diese Blöcke abfragen und übernehmen. Nur hilft mir das nicht weiter: Ein belegter Block im Kalender eines anderen Familienmitglieds heißt noch lange nicht, dass ich keine Zeit habe. Ohne Inhalt kann ich das nicht unterscheiden, und ein Abgleich, der auf Verdacht meine Zeit blockiert, macht den Kalender wieder unzuverlässig.
Dazu kommt: Wer seinen Kalender nur als Frei/Gebucht teilt, hat sich bewusst dafür entschieden. Diese Zeiten in meinen Geschäftskalender zu kopieren, wäre nicht im Sinne der Person, die sie freigegeben hat. Für diese Kalender bleibt es beim Blick in die Google-App. Damit kann ich leben, die meisten Kollisionen kamen ohnehin aus den anderen Quellen.
Das Script zum Nachbauen
Wenn Du vor demselben Problem stehst, kannst Du mein Script als Ausgangspunkt nehmen. Es heißt privacal und liegt als Open-Source-Projekt auf GitHub. Es liest Baïkal, iCloud und andere CalDAV-Kalender, Google-Kalender über die private iCal-Adresse und weitere Microsoft-365-Kalender, auch aus einem anderen Tenant. Geschrieben wird nur in den Zielkalender, mit allen Regeln von oben. Passwörter und geheime Adressen kommen aus Umgebungsvariablen, nicht aus der Konfiguration.
Python 3.11 oder neuer, MIT-Lizenz. Mit Beispielkonfiguration und Anleitung zur Einrichtung.
Das Script ist bewusst schlank gehalten und als Basis zum Weiterbauen gedacht, nicht als fertiges Produkt. Verbesserungen und Ideen sind als Issue oder Pull Request willkommen. Probier es zuerst mit --dry-run aus. Dann zeigt es nur an, was es anlegen, ändern oder löschen würde.
Was ich daraus mitnehme
Ein privates Problem mit einer sehr geschäftlichen Lösung. Die Prinzipien dahinter sind dieselben wie in jedem Integrationsprojekt:
- Sichtbarkeit und Privatsphäre sind kein Widerspruch. Man muss nur trennen zwischen der Information „belegt“ und dem Inhalt. Das Team bekommt das eine, ich beides.
- Datenschutz gehört in die Anforderung. Private Termine landen nur deshalb im Geschäftskalender, weil sie dort privat markiert ankommen und die Freigabe passt. Und manchmal heißt Datenschutz auch, bewusst weniger zu übernehmen, als technisch ginge.
- Ein führendes System pro Information. Einseitige Synchronisation ist langweilig, und genau deshalb funktioniert sie.
- Automatik fasst nur an, was ihr gehört. Ein Prozess, der löschen darf, braucht eine eindeutige Zuständigkeit. Bei Kalendern genauso wie bei Belegen.
- So wenig Rechte wie möglich. Eine eigene App-Registrierung mit einer einzigen Berechtigung. App-spezifisches Passwort statt Hauptpasswort. Wenn ich einen Zugang nicht mehr brauche, widerrufe ich genau diesen einen.
Fazit
Private und geschäftliche Termine unter einem Hut? Zumindest im Kalender, ja. Mein Team plant wieder einfach in meinem Kalender, und die Lücken stimmen. Meine Frau plant weiter im Familienkalender, und ihre Termine tauchen spätestens eine Viertelstunde später bei mir auf. Wo ich nicht kann, steht „Privater Termin“, und niemand muss fragen, warum. Doppelbuchungen sind nicht ganz verschwunden, ich bin immer noch ein Mensch. Aber sie passieren nicht mehr, weil ein Termin unsichtbar war. Für die Work-Life-Balance bin ich weiterhin selbst zuständig. Dafür gibt es leider noch kein Script.
Den größten Teil des Samstags habe ich übrigens nicht mit Programmieren verbracht, sondern mit der Frage, was der Abgleich auf keinen Fall tun und auf keinen Fall zeigen darf. Das ist bei Kalendern nicht anders als bei einer Schnittstelle zwischen ERP und Shop.