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:
co-authored by
Claude Opus 5
parent
10686cf383
commit
5977ad0928
@@ -3,7 +3,7 @@ import logging
|
|||||||
import secrets
|
import secrets
|
||||||
import ssl
|
import ssl
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
@@ -248,6 +248,35 @@ def _ha_create_event(url: str, token: str, entity_id: str, data: dict) -> dict:
|
|||||||
return {}
|
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):
|
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
|
"""Update an event. Tries calendar/event/update first; if the integration
|
||||||
doesn't support update (e.g. Google Calendar via HA), falls back to
|
doesn't support update (e.g. Google Calendar via HA), falls back to
|
||||||
@@ -697,10 +726,9 @@ def create_event(
|
|||||||
account = cal.account
|
account = cal.account
|
||||||
token = _get_valid_token(account, db)
|
token = _get_valid_token(account, db)
|
||||||
try:
|
try:
|
||||||
_ha_create_event(
|
payload = data.model_dump(exclude_none=True)
|
||||||
account.url, token, cal.entity_id,
|
_ha_create_event(account.url, token, cal.entity_id, payload)
|
||||||
data.model_dump(exclude_none=True),
|
_ha_wait_until_listed(account.url, token, cal.entity_id, payload)
|
||||||
)
|
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise HTTPException(500, f"HA event create failed: {exc}")
|
raise HTTPException(500, f"HA event create failed: {exc}")
|
||||||
|
|||||||
Reference in New Issue
Block a user