- populateCalendarSelect: filtert jetzt nach !sidebar_hidden statt
enabled. Unchecked (versteckte) Kalender bleiben so im
Termin-erstellen-Dropdown verfügbar
- buildWritableCalendars: HA-Kalender werden als Kopier-Ziele aufgeführt
- copyEventToCalendar: routet HA-Ziele über /homeassistant/events
Endpoint (vorher fielen sie in den CalDAV-Fallback)
CalDAV-Events hatten bisher kein source-Feld gesetzt. applyCalendarColor
filtert aber via ev.source !== 'caldav', sodass der Patch nie auf
CalDAV-Events angewendet wurde – die Farbe blieb sichtbar bis F5.
Jetzt wird source: 'caldav' beim Anreichern der Events gesetzt.
CalDAV-Events hatten bisher kein source-Feld gesetzt. applyCalendarColor
filtert aber via ev.source !== 'caldav', sodass der Patch nie auf
CalDAV-Events angewendet wurde – die Farbe blieb sichtbar bis F5.
Jetzt wird source: 'caldav' beim Anreichern der Events gesetzt.
Nach dem Speichern eines Termins wird das gecachte Event-Objekt
direkt in-place gepatcht und die View neu gerendert. Vorher war die
neue Farbe erst nach F5 sichtbar, weil zwar fetchAndRender(true)
aufgerufen wurde, aber der Render-Pfad das Update nicht zuverlässig
übernommen hat.
Nach dem Speichern eines Termins wird das gecachte Event-Objekt
direkt in-place gepatcht und die View neu gerendert. Vorher war die
neue Farbe erst nach F5 sichtbar, weil zwar fetchAndRender(true)
aufgerufen wurde, aber der Render-Pfad das Update nicht zuverlässig
übernommen hat.
Manche HA-Integrationen registrieren nur den WebSocket-Handler, keinen
Service-Call. Die HA-Web-UI nutzt deshalb den WebSocket-Pfad. Calendarr
macht das jetzt auch:
- _ha_ws_call: minimaler WebSocket-Client für eine einzelne Command
- create: erst WS, dann Service-Call als Fallback
- update: nur WS (Service-Call existiert oft nicht)
- delete: nur WS (Service-Call existiert oft nicht)
Neue Dependency: websocket-client==1.8.0
Manche HA-Integrationen registrieren nur den WebSocket-Handler, keinen
Service-Call. Die HA-Web-UI nutzt deshalb den WebSocket-Pfad. Calendarr
macht das jetzt auch:
- _ha_ws_call: minimaler WebSocket-Client für eine einzelne Command
- create: erst WS, dann Service-Call als Fallback
- update: nur WS (Service-Call existiert oft nicht)
- delete: nur WS (Service-Call existiert oft nicht)
Neue Dependency: websocket-client==1.8.0
HA's Service-Call-Schema akzeptiert je nach Version verschiedene
Body-Shapes für entity_id. Wir probieren jetzt der Reihe nach:
1. entity_id als String
2. entity_id als Liste
3. target-Wrapper
Wenn alle fehlschlagen, klare Anweisung zum HA-Developer-Tools-Test.
HA's Service-Call-Schema akzeptiert je nach Version verschiedene
Body-Shapes für entity_id. Wir probieren jetzt der Reihe nach:
1. entity_id als String
2. entity_id als Liste
3. target-Wrapper
Wenn alle fehlschlagen, klare Anweisung zum HA-Developer-Tools-Test.
calendar.delete_event schlägt mit 400 fehl, wenn die HA-Integration
das Feature nicht unterstützt (z.B. Google-Calendar via HA hat nur
CREATE_EVENT, kein DELETE/UPDATE).
- Versucht erst Service-Call, dann REST DELETE als Fallback
- Bei 400 wird der User aufgeklärt, dass die Integration vermutlich
kein Löschen unterstützt
calendar.delete_event schlägt mit 400 fehl, wenn die HA-Integration
das Feature nicht unterstützt (z.B. Google-Calendar via HA hat nur
CREATE_EVENT, kein DELETE/UPDATE).
- Versucht erst Service-Call, dann REST DELETE als Fallback
- Bei 400 wird der User aufgeklärt, dass die Integration vermutlich
kein Löschen unterstützt
- _ha_format_dt: Parst ISO-Datetime zu datetime-Objekt, emittiert
ohne Millisekunden, MIT Timezone-Offset. Vorher landeten Termine
am falschen Datum, weil das Frontend UTC schickt aber wir die
Timezone gestrippt haben → HA hat als lokale Zeit interpretiert
- Leere Strings werden nicht mehr in den Body aufgenommen (HA
Validator könnte diese ablehnen)
- Logging in create/delete/update für besseres Debugging der HA-Calls
- _ha_format_dt: Parst ISO-Datetime zu datetime-Objekt, emittiert
ohne Millisekunden, MIT Timezone-Offset. Vorher landeten Termine
am falschen Datum, weil das Frontend UTC schickt aber wir die
Timezone gestrippt haben → HA hat als lokale Zeit interpretiert
- Leere Strings werden nicht mehr in den Body aufgenommen (HA
Validator könnte diese ablehnen)
- Logging in create/delete/update für besseres Debugging der HA-Calls
- POST /api/homeassistant/events Endpoint mit calendar.create_event
- Frontend: HA-Termine erstellen statt 'nicht unterstützt' Toast
- Datetime-Format an HA-Konvention angepasst:
'YYYY-MM-DD HH:MM:SS' (Space-Separator, ohne Timezone)
- _ha_format_dt Helper für ISO → HA Datetime-Konvertierung
- POST /api/homeassistant/events Endpoint mit calendar.create_event
- Frontend: HA-Termine erstellen statt 'nicht unterstützt' Toast
- Datetime-Format an HA-Konvention angepasst:
'YYYY-MM-DD HH:MM:SS' (Space-Separator, ohne Timezone)
- _ha_format_dt Helper für ISO → HA Datetime-Konvertierung
calendar.update_event existiert erst ab HA 2024.6. Wenn der Service
nicht verfügbar ist (400), wird stattdessen delete_event + create_event
verwendet. Funktioniert mit HA 2022.5+.
calendar.update_event existiert erst ab HA 2024.6. Wenn der Service
nicht verfügbar ist (400), wird stattdessen delete_event + create_event
verwendet. Funktioniert mit HA 2022.5+.
PUT/DELETE /api/calendars/{entity_id}/{uid} existieren nicht in HA.
Stattdessen: POST /api/services/calendar/update_event und
POST /api/services/calendar/delete_event (HA 2023.x+)
PUT/DELETE /api/calendars/{entity_id}/{uid} existieren nicht in HA.
Stattdessen: POST /api/services/calendar/update_event und
POST /api/services/calendar/delete_event (HA 2023.x+)
- HA Update/Delete: UID wird URL-encoded (@ → %40), Delete mit
Fallback auf Service-Call API für ältere HA-Versionen
- Lösch-Dialog: Event-Popup wird geschlossen BEVOR der Bestätigungsdialog
erscheint, kein Überlappen mehr
- HA Update/Delete: UID wird URL-encoded (@ → %40), Delete mit
Fallback auf Service-Call API für ältere HA-Versionen
- Lösch-Dialog: Event-Popup wird geschlossen BEVOR der Bestätigungsdialog
erscheint, kein Überlappen mehr
- HA-Events: Update/Delete-Endpoints via HA REST API implementiert
- HA read-only Guard entfernt, stattdessen korrekte API-Anbindung
- Selected-Day: Outline-Ring statt gefüllter Kreis (Today bleibt gefüllt)
- Serien-Löschung: RECURRENCE-ID aus CalDAV-Events erkennen, damit
expandierte Serientermine als recurring markiert werden und der
Lösch-Dialog Einzel-/Serienlöschung anbietet
- HA-Events: Update/Delete-Endpoints via HA REST API implementiert
- HA read-only Guard entfernt, stattdessen korrekte API-Anbindung
- Selected-Day: Outline-Ring statt gefüllter Kreis (Today bleibt gefüllt)
- Serien-Löschung: RECURRENCE-ID aus CalDAV-Events erkennen, damit
expandierte Serientermine als recurring markiert werden und der
Lösch-Dialog Einzel-/Serienlöschung anbietet
- caldav_client: del+add statt direkter Zuweisung bei VEVENT-Properties
(behebt "DTSTART MUST appear exactly once" Validierungsfehler)
- HA-Events als read-only behandeln (kein Bearbeiten/Löschen im Popup)
- [object Object] Toast behoben: HA-Events fallen nicht mehr in CalDAV-Pfad
- caldav_client: del+add statt direkter Zuweisung bei VEVENT-Properties
(behebt "DTSTART MUST appear exactly once" Validierungsfehler)
- HA-Events als read-only behandeln (kein Bearbeiten/Löschen im Popup)
- [object Object] Toast behoben: HA-Events fallen nicht mehr in CalDAV-Pfad
- End-Datum passt sich automatisch an wenn Start geändert wird (Duration bleibt erhalten)
- Erstellen-Button nutzt den aktuell angesehenen Tag statt immer heute
- Monatsansicht: Einzelklick = Tag auswählen, Doppelklick = Tagesansicht, Rechtsklick = Kontextmenü
- CalDAV URL-Matching robuster (Normalisierung, Path-Fallback, calendar_id Parameter)
- iCal-Abo-Termine sind nicht mehr bearbeitbar (Read-Only-Schutz)
- Wiederkehrende Termine mit RRULE-Support (täglich/wöchentlich/monatlich/jährlich/benutzerdefiniert)
- End-Datum passt sich automatisch an wenn Start geändert wird (Duration bleibt erhalten)
- Erstellen-Button nutzt den aktuell angesehenen Tag statt immer heute
- Monatsansicht: Einzelklick = Tag auswählen, Doppelklick = Tagesansicht, Rechtsklick = Kontextmenü
- CalDAV URL-Matching robuster (Normalisierung, Path-Fallback, calendar_id Parameter)
- iCal-Abo-Termine sind nicht mehr bearbeitbar (Read-Only-Schutz)
- Wiederkehrende Termine mit RRULE-Support (täglich/wöchentlich/monatlich/jährlich/benutzerdefiniert)
Home Assistant unterstützt keinen Password-Grant — deshalb kam immer
"Ungültige Anmeldedaten", egal was eingegeben wurde. Jetzt wird der
Nutzer nach demselben Muster wie bei Google zur HA-Login-Seite
weitergeleitet, meldet sich dort an und kommt zurück zu Calendarr.
Änderungen:
- Neuer POST /api/homeassistant/auth-url und GET /callback Endpoint
- Account speichert client_id für spätere Token-Refreshes
- Modal: "Benutzername/Passwort" → "Mit Home Assistant anmelden"
- Frontend behandelt ?ha_connected=1 / ?ha_error=... nach Rückkehr
- Version v1 → v2
Home Assistant unterstützt keinen Password-Grant — deshalb kam immer
"Ungültige Anmeldedaten", egal was eingegeben wurde. Jetzt wird der
Nutzer nach demselben Muster wie bei Google zur HA-Login-Seite
weitergeleitet, meldet sich dort an und kommt zurück zu Calendarr.
Änderungen:
- Neuer POST /api/homeassistant/auth-url und GET /callback Endpoint
- Account speichert client_id für spätere Token-Refreshes
- Modal: "Benutzername/Passwort" → "Mit Home Assistant anmelden"
- Frontend behandelt ?ha_connected=1 / ?ha_error=... nach Rückkehr
- Version v1 → v2
Vorher wurde die Version erst in initCalendar() gesetzt – wenn JS
vorher fehlschlug, blieb der Text leer. Jetzt steht v1 direkt im
HTML (Titel, Login-Button, Sidebar-Button, Impressum-Modal).
Für künftige Releases: v1 → v2 in index.html + version.js ersetzen.
Vorher wurde die Version erst in initCalendar() gesetzt – wenn JS
vorher fehlschlug, blieb der Text leer. Jetzt steht v1 direkt im
HTML (Titel, Login-Button, Sidebar-Button, Impressum-Modal).
Für künftige Releases: v1 → v2 in index.html + version.js ersetzen.
Neue version.js als Single Point of Truth (APP_VERSION).
Sidebar, Login-Screen und Impressum-Modal zeigen die aktuelle
Version an — ab jetzt bei jeder Änderung v2, v3 ... hochzählen.
Startet bei v1.
Neue version.js als Single Point of Truth (APP_VERSION).
Sidebar, Login-Screen und Impressum-Modal zeigen die aktuelle
Version an — ab jetzt bei jeder Änderung v2, v3 ... hochzählen.
Startet bei v1.
- Backend gibt 400 statt 401 bei falschen HA-Credentials zurück, damit
der globale api.js-Logout-Handler nicht ausgelöst wird
- Null-Guard im JS nach api.post verhindert den "calendars of null"-Crash
- Radio-Buttons für Anmeldemethode nicht mehr in form-group, damit
input[type=radio] kein width:100% bekommt und sauber nebeneinander liegt
- Backend gibt 400 statt 401 bei falschen HA-Credentials zurück, damit
der globale api.js-Logout-Handler nicht ausgelöst wird
- Null-Guard im JS nach api.post verhindert den "calendars of null"-Crash
- Radio-Buttons für Anmeldemethode nicht mehr in form-group, damit
input[type=radio] kein width:100% bekommt und sauber nebeneinander liegt
Ergänzt die HA-Integration um Password-Grant OAuth2: Nutzer können sich
nun wahlweise mit einem Long-Lived Token oder mit Benutzername/Passwort
anmelden. Access Tokens werden automatisch per Refresh-Token erneuert.
Ergänzt die HA-Integration um Password-Grant OAuth2: Nutzer können sich
nun wahlweise mit einem Long-Lived Token oder mit Benutzername/Passwort
anmelden. Access Tokens werden automatisch per Refresh-Token erneuert.
Der Cursor war relativ zum .gcp-Container positioniert, aber ohne den
Offset des Canvas innerhalb des Containers (Padding). Jetzt wird die
Canvas-Position via getBoundingClientRect() eingerechnet, sodass der
Cursor exakt auf der Farbpalette bleibt.
Der Cursor war relativ zum .gcp-Container positioniert, aber ohne den
Offset des Canvas innerhalb des Containers (Padding). Jetzt wird die
Canvas-Position via getBoundingClientRect() eingerechnet, sodass der
Cursor exakt auf der Farbpalette bleibt.
updateUI verwendete svCanvas.width (HTML-Attribut, 220px) statt der
tatsächlich gerenderten Breite. Wenn CSS den Canvas größer rendert,
stoppte der Cursor vor dem rechten Rand. Jetzt wird getBoundingClientRect()
verwendet, konsistent mit handleSV.
updateUI verwendete svCanvas.width (HTML-Attribut, 220px) statt der
tatsächlich gerenderten Breite. Wenn CSS den Canvas größer rendert,
stoppte der Cursor vor dem rechten Rand. Jetzt wird getBoundingClientRect()
verwendet, konsistent mit handleSV.
Beim Ausblenden eines Kalenders (sidebar_hidden) wurde fetchAndRender()
ohne force=true aufgerufen, wodurch der Cache nie invalidiert wurde und
die Events weiterhin angezeigt wurden. Jetzt wird der Cache sofort
gefiltert (wie beim Checkbox-Deaktivieren), ohne einen neuen Netzwerkaufruf.
Beim Ausblenden eines Kalenders (sidebar_hidden) wurde fetchAndRender()
ohne force=true aufgerufen, wodurch der Cache nie invalidiert wurde und
die Events weiterhin angezeigt wurden. Jetzt wird der Cache sofort
gefiltert (wie beim Checkbox-Deaktivieren), ohne einen neuen Netzwerkaufruf.
- Neue Integration: Home Assistant als Kalenderquelle via REST-API
(GET /api/calendars + GET /api/calendars/{entity_id})
- Authentifizierung per Long-Lived Access Token
- Neues Modal zum Verbinden (Name, URL, Token) mit Fehlerbehandlung
- Kalender einzeln aktivierbar/deaktivierbar, Farbe änderbar
- Ausgeblendete HA-Kalender in Einstellungen wiederherstellbar
- Sync- und Trennen-Buttons in den Einstellungen
- Bugfix: CalDAV- und Google-Kalender mit sidebar_hidden=true
liefern nun keine Events mehr im Kalender