Add travel time, geocoding, and reminders to create_event
- travel_minutes: shows as gray block before event on iPhone - location geocoding via Nominatim → X-APPLE-STRUCTURED-LOCATION with geo coords - reminder_minutes: VALARM trigger before event - Also externalized all credentials to config.json Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+46
-5
@@ -185,17 +185,33 @@ def get_tasks(
|
||||
return "\n\n".join(results) if results else "Keine Aufgaben"
|
||||
|
||||
|
||||
def _geocode(address):
|
||||
"""Geocode an address via Nominatim. Returns (lat, lon, display_name) or None."""
|
||||
try:
|
||||
r = httpx.get("https://nominatim.openstreetmap.org/search",
|
||||
params={"q": address, "format": "json", "limit": 1},
|
||||
headers={"User-Agent": "mcp-home/1.0"}, timeout=10)
|
||||
results = r.json()
|
||||
if results:
|
||||
return float(results[0]["lat"]), float(results[0]["lon"]), results[0].get("display_name", address)
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def create_event(
|
||||
calendar: Annotated[str, Field(description="Calendar name, e.g. 'Stefan', 'Arbeit', 'Familie'")],
|
||||
title: Annotated[str, Field(description="Event title")],
|
||||
start: Annotated[str, Field(description="Start: YYYY-MM-DD for all-day, or YYYY-MM-DDTHH:MM for timed events, e.g. '2026-06-15T14:00'")],
|
||||
end: Annotated[str, Field(description="End: YYYY-MM-DD for all-day, or YYYY-MM-DDTHH:MM, e.g. '2026-06-15T15:30'")],
|
||||
location: Annotated[str, Field(description="Location/address")] = "",
|
||||
location: Annotated[str, Field(description="Location or address, e.g. 'Marienplatz 1, Muenchen'. Gets geocoded for map display.")] = "",
|
||||
description: Annotated[str, Field(description="Event description or notes")] = "",
|
||||
allday: Annotated[bool, Field(description="True for all-day events (then use YYYY-MM-DD for start/end)")] = False,
|
||||
travel_minutes: Annotated[int, Field(description="Travel time in minutes shown before the event on iPhone (0=none, e.g. 30 for 30 min commute)")] = 0,
|
||||
reminder_minutes: Annotated[int, Field(description="Reminder alert X minutes before the event (0=none, e.g. 15, 30, 60)")] = 0,
|
||||
) -> str:
|
||||
"""Create a new calendar event. For all-day events, end date is exclusive (event on June 15 → start=2026-06-15, end=2026-06-16)."""
|
||||
"""Create a new calendar event. For all-day events, end date is exclusive (June 15 only → start=2026-06-15, end=2026-06-16). Location is geocoded for map pins. Travel time shows as block before the event on iPhone."""
|
||||
user = get_current_user()
|
||||
if not user: return "Error: not authenticated"
|
||||
cals = [c for c in _discover(user, "VEVENT") if calendar.lower() in c["name"].lower()]
|
||||
@@ -211,11 +227,36 @@ def create_event(
|
||||
else:
|
||||
ev.add("dtstart").value = datetime.fromisoformat(start)
|
||||
ev.add("dtend").value = datetime.fromisoformat(end)
|
||||
if location: ev.add("location").value = location
|
||||
if description: ev.add("description").value = description
|
||||
if location:
|
||||
ev.add("location").value = location
|
||||
geo = _geocode(location)
|
||||
if geo:
|
||||
lat, lon, display = geo
|
||||
loc_prop = ev.add("x-apple-structured-location")
|
||||
loc_prop.value = f"geo:{lat},{lon}"
|
||||
loc_prop.params["VALUE"] = ["URI"]
|
||||
loc_prop.params["X-APPLE-RADIUS"] = ["70"]
|
||||
loc_prop.params["X-APPLE-REFERENCEFRAME"] = ["1"]
|
||||
loc_prop.params["X-TITLE"] = [location]
|
||||
if description:
|
||||
ev.add("description").value = description
|
||||
if travel_minutes > 0:
|
||||
tp = ev.add("x-apple-travel-duration")
|
||||
tp.value = f"PT{travel_minutes}M"
|
||||
tp.params["VALUE"] = ["DURATION"]
|
||||
if reminder_minutes > 0:
|
||||
alarm = ev.add("valarm")
|
||||
alarm.add("action").value = "DISPLAY"
|
||||
alarm.add("description").value = title
|
||||
alarm.add("trigger").value = timedelta(minutes=-reminder_minutes)
|
||||
ev.add("dtstamp").value = datetime.utcnow()
|
||||
r = httpx.put(RADICALE + cals[0]["href"] + uid + ".ics", content=cal.serialize(), auth=_auth(user), headers={"Content-Type": "text/calendar"}, timeout=15)
|
||||
return f"Termin erstellt: {title} am {start}" if r.status_code in (201, 204) else f"Fehler: HTTP {r.status_code}"
|
||||
extras = []
|
||||
if location and _geocode(location): extras.append("mit Karte")
|
||||
if travel_minutes: extras.append(f"{travel_minutes} Min. Fahrzeit")
|
||||
if reminder_minutes: extras.append(f"Erinnerung {reminder_minutes} Min. vorher")
|
||||
extra_str = f" ({', '.join(extras)})" if extras else ""
|
||||
return f"Termin erstellt: {title} am {start}{extra_str}" if r.status_code in (201, 204) else f"Fehler: HTTP {r.status_code}"
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
|
||||
Reference in New Issue
Block a user