From 3351263c85cb836a9eb8bb8cbc982adcdb72eb2d Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Wed, 29 Apr 2026 18:38:43 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20HA-Event-Update=20URL-Encoding=20und=20P?= =?UTF-8?q?opup-=C3=9Cberlappung=20bei=20L=C3=B6sch-Dialog=20-=20HA=20Upda?= =?UTF-8?q?te/Delete:=20UID=20wird=20URL-encoded=20(@=20=E2=86=92=20%40),?= =?UTF-8?q?=20Delete=20mit=20=20=20Fallback=20auf=20Service-Call=20API=20f?= =?UTF-8?q?=C3=BCr=20=C3=A4ltere=20HA-Versionen=20-=20L=C3=B6sch-Dialog:?= =?UTF-8?q?=20Event-Popup=20wird=20geschlossen=20BEVOR=20der=20Best=C3=A4t?= =?UTF-8?q?igungsdialog=20=20=20erscheint,=20kein=20=C3=9Cberlappen=20mehr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/routers/homeassistant_router.py | 39 ++++++++++++++++--------- frontend/js/calendar.js | 2 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/backend/routers/homeassistant_router.py b/backend/routers/homeassistant_router.py index 4a0bb88..d24b872 100644 --- a/backend/routers/homeassistant_router.py +++ b/backend/routers/homeassistant_router.py @@ -110,7 +110,11 @@ def _ha_get_events(url: str, token: str, entity_id: str, start_dt: datetime, end def _ha_update_event(url: str, token: str, entity_id: str, uid: str, data: dict): - """Update an event via HA REST API.""" + """Update an event via HA REST API (HA 2023.11+).""" + from urllib.parse import quote + base = url.rstrip("/") + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + encoded_uid = quote(uid, safe="") body = {} if "title" in data: body["summary"] = data["title"] @@ -126,26 +130,35 @@ def _ha_update_event(url: str, token: str, entity_id: str, uid: str, data: dict) body["start"] = {"dateTime": data["start"]} body["end"] = {"dateTime": data["end"]} resp = http_requests.put( - f"{url.rstrip('/')}/api/calendars/{entity_id}/{uid}", - headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, - json=body, - timeout=15, - verify=False, + f"{base}/api/calendars/{entity_id}/{encoded_uid}", + headers=headers, json=body, timeout=15, verify=False, ) resp.raise_for_status() return resp def _ha_delete_event(url: str, token: str, entity_id: str, uid: str): - """Delete an event via HA REST API.""" + """Delete an event via HA REST API with fallback to service call.""" + base = url.rstrip("/") + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + # Try REST API first (HA 2023.11+) + from urllib.parse import quote + encoded_uid = quote(uid, safe="") resp = http_requests.delete( - f"{url.rstrip('/')}/api/calendars/{entity_id}/{uid}", - headers={"Authorization": f"Bearer {token}"}, - timeout=15, - verify=False, + f"{base}/api/calendars/{entity_id}/{encoded_uid}", + headers=headers, timeout=15, verify=False, ) - resp.raise_for_status() - return resp + if resp.status_code < 400: + return resp + # Fallback: service call + resp2 = http_requests.post( + f"{base}/api/services/calendar/delete_event", + headers=headers, + json={"entity_id": entity_id, "uid": uid}, + timeout=15, verify=False, + ) + resp2.raise_for_status() + return resp2 def _parse_ha_event(ev: dict, cal_db_id: int, cal_name: str, cal_color: str) -> dict: diff --git a/frontend/js/calendar.js b/frontend/js/calendar.js index f7bef0b..dbf1bba 100644 --- a/frontend/js/calendar.js +++ b/frontend/js/calendar.js @@ -1034,9 +1034,9 @@ function showEventPopup(ev, anchor) { }; document.getElementById('popup-delete').onclick = async () => { + popup.classList.add('hidden'); const scope = await showDeleteConfirm(ev); if (!scope) return; - popup.classList.add('hidden'); try { await deleteEventByScope(ev, scope); showToast(t('event_deleted'));