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 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}")
|
||||
|
||||
Reference in New Issue
Block a user