Wait for HA to list a new event before reporting it created

Home Assistant confirms calendar/event/create before its entity's event list
reflects it. Both clients already force a refetch after saving and the server
caches nothing, so the refetch simply lost the race: the event was created but
invisible until the next manual reload. Users read that as a failure, retry,
and end up with a duplicate.

Fixed once here rather than having web, iOS and Android each guess a delay.
Bounded at two seconds and silent on failure — the event exists either way, so
a slow Home Assistant must not turn a successful create into an error.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-20 21:08:06 +02:00
co-authored by Claude Opus 5
parent 10686cf383
commit 5977ad0928
+33 -5
View File
@@ -3,7 +3,7 @@ import logging
import secrets
import ssl
import time
from datetime import datetime, timezone
from datetime import datetime, timedelta, timezone
from typing import Optional
from urllib.parse import urlencode
@@ -248,6 +248,35 @@ def _ha_create_event(url: str, token: str, entity_id: str, data: dict) -> dict:
return {}
def _ha_wait_until_listed(url: str, token: str, entity_id: str, data: dict) -> None:
"""Block until HA actually lists the event we just created.
HA confirms the create before its entity's event list reflects it, so a
client that refetches straight away misses the new event, the user assumes
it failed, retries — and ends up with a duplicate. Waiting here fixes every
client at once instead of each one guessing a delay.
Gives up quietly: the event exists either way, it may just need one more
refresh. Never turn a successful create into an error.
"""
try:
start = datetime.fromisoformat(_ha_format_dt(data.get("start", "")))
end = datetime.fromisoformat(_ha_format_dt(data.get("end", "")))
except Exception:
return
title = data.get("title", "")
# Widen the window so an all-day event or a timezone edge cannot hide it.
window_start, window_end = start - timedelta(days=1), end + timedelta(days=1)
for _ in range(8):
time.sleep(0.25)
try:
raw = _ha_get_events(url, token, entity_id, window_start, window_end)
except Exception:
return
if any(ev.get("summary") == title for ev in raw):
return
def _ha_update_event(url: str, token: str, entity_id: str, uid: str, data: dict):
"""Update an event. Tries calendar/event/update first; if the integration
doesn't support update (e.g. Google Calendar via HA), falls back to
@@ -697,10 +726,9 @@ def create_event(
account = cal.account
token = _get_valid_token(account, db)
try:
_ha_create_event(
account.url, token, cal.entity_id,
data.model_dump(exclude_none=True),
)
payload = data.model_dump(exclude_none=True)
_ha_create_event(account.url, token, cal.entity_id, payload)
_ha_wait_until_listed(account.url, token, cal.entity_id, payload)
return {"ok": True}
except Exception as exc:
raise HTTPException(500, f"HA event create failed: {exc}")