Repository navigation
Archive 1.1.3 Design Documentation
This page describes the Home Assistant 2025.12 era / integration 1.1.3 design. It is kept verbatim for historical reference and for users still running the
mainbranch (v1.1.3) or an older release.It was stored as
Developer-&-Technical-Reference-Guide.asciidocuntil the wiki restructure; the content was always Markdown, so it renders correctly again as a.mdpage. Formatting was left untouched except for this banner and the list of defects at the end.👉 For the current design see the Architecture Design Document and the Software Design Document (integration 2.0.x, branch
ha2026_09_dev, Home Assistant 2026.9+).Back to the Documentation home.
The TimeTree Calendar Integration is a custom component for Home Assistant. It bridges the gap between the TimeTree cloud service and the Home Assistant ecosystem, allowing users to visualize their schedules, monitor sync status, and create new events directly from their smart home dashboard.
The system operates on a hybrid Polling and Push architecture:
- Polling (Read): The system periodically (default: 60 min, configurable) connects to TimeTree servers to fetch the latest calendar data.
-
Push (Write): When a user triggers the
calendar.create_eventservice, the integration immediately pushes the data to TimeTree via a simulated web-client API call.
High-Level Data Flow:
TimeTree Cloud ⟷ TimeTreeApi (Client) ⟷ DataUpdateCoordinator ⟷ Calendar & Sensor Entities ⟷ Home Assistant Core
- Role: The abstraction layer for network communication.
- Responsibility:
- Authenticates users using Email/Password to obtain a session cookie (
_session_id). - Handles "Unauthorized" (401) errors by automatically refreshing the session.
- Fetches calendar metadata and event lists (handling pagination/chunking).
- Constructs and sends JSON payloads for creating new events.
- Role: User Interface for setup and settings.
- Responsibility:
- Initial Setup: Captures credentials, validates login, and presents a list of calendars for selection.
- Options: Provides a settings dialog to adjust the Scan Interval (5–120 minutes) dynamically without reinstalling.
- Role: State and Interval Manager.
- Responsibility:
- Manages the polling loop based on the user-configured scan interval.
- Stores the latest event data in memory.
- Broadcasts updates to all entities (Calendar and Sensor) simultaneously to ensure data consistency.
-
Calendar Entity: Implements
CalendarEntity. Displays events and handles thecreate_eventservice. -
Sensor Entity: Implements
SensorEntity(device_class: timestamp). Displays the exact time of the last successful synchronization for debugging purposes.
The integration simulates a web client to communicate with the TimeTree backend:
-
Base URL:
https://timetreeapp.com/api/v1 -
User Agent:
web/2.1.0/en(Mimics the official web application). - Endpoints Used:
-
PUT /auth/email/signin: Authentication. -
GET /calendars: List available calendars. -
GET /calendar/{id}/events/sync: Fetch events. -
POST /calendar/{id}/events: Create a new event.
| File | Description |
|---|---|
__init__.py |
Initializes the integration, loads platforms, and registers the Options Update Listener to reload config when settings change. |
api.py |
Contains the TimeTreeApi class. Handles all HTTP GET/PUT/POST requests and JSON parsing. |
calendar.py |
Defines TimeTreeCalendarEntity. Handles event filtering (All-Day vs Timed) and the async_create_event method. |
config_flow.py |
Defines TimeTreeConfigFlow and TimeTreeOptionsFlowHandler. Manages the setup wizard and the "Configure" slider. |
const.py |
Constants for DOMAIN, configuration keys, and default interval limits (5-120 min). |
coordinator.py |
Defines TimeTreeCoordinator. Fetches scan interval from entry.options or entry.data. |
sensor.py |
Defines TimeTreeLastUpdatedSensor. diagnostic entity for connection monitoring. |
-
Attributes:
_session(requests.Session),_session_id(cookie). - Key Methods:
-
_login(): Generates a random UUID and performs a login handshake. -
_create_event(payload): Sends a formatted JSON payload to TimeTree. Uses internal types (type: 0,category: 1) to mimic a standard event. -
parse_event(json): Static utility critical for normalizing timestamps. Converts TimeTree's millisecond timestamps into Pythondatetimeordateobjects.
-
Attributes:
_attr_supported_features = CalendarEntityFeature.CREATE_EVENT. - Key Logic:
-
Event Filtering: Separates logic for All-Day events (Date comparison) vs Timed events (Datetime comparison) to prevent Python
TypeError. -
Write Action: The
async_create_eventmethod captures HA arguments, converts them to TimeTree-compatible milliseconds, sends the API request, and immediately triggers a coordinator refresh.
TimeTree returns timestamps in milliseconds. The integration must decide whether to cast these to datetime or date.
-
Algorithm:
-
If
all_dayisTrue→ Convert timestamp todatetime→ Extract.date()→ Result isdateobject. -
If
all_dayisFalse→ Convert timestamp todatetime(with Timezone) → Result isdatetimeobject. -
Reasoning: Home Assistant's Calendar view strictly requires
dateobjects for all-day events to render them correctly as "whole day" bars.
- The
coordinatordoes not hardcode the interval. - Logic:
- Check
config_entry.options(User defined via "Configure"). - If missing, check
config_entry.data(Defined during initial setup). - If missing, use
DEFAULT_SCAN_INTERVAL(60 min).
-
Trigger: When a user changes the slider in the UI,
async_reload_entryin__init__.pytears down the integration and re-initializes the coordinator with the new timer.
Event Payload (Write Structure) When creating an event, the internal API expects this specific structure:
{
"type": 0,
"category": 1,
"title": "Summary",
"note": "Description",
"location": "Location",
"all_day": false,
"start_at": 1735664400000,
"end_at": 1735671600000,
"start_timezone": "UTC",
"end_timezone": "UTC",
"uuid": "random-uuid-v4"
}
-
Credential Storage: Email and Password are stored encrypted in Home Assistant's
.storage/core.config_entries. -
Session Management: The integration generates a unique
uuidfor every login session to distinguish itself as a unique client device. -
Debug Logging: Sensitive payloads (creation data) are only logged when
custom_components.timetreeis explicitly set todebuglevel in the user's configuration.
The design above worked, but the runtime behaviour contained five independent defects that made month/week views fail and event creation impossible. They are documented here so the archived page is not mistaken for a working specification:
| # | Defect in 1.1.3 | Fixed in 2.0.x by |
|---|---|---|
| 1 | Only the first page of GET /calendar/{id}/events/sync was imported; the chunk/since cursor was ignored, so large calendars were silently incomplete. |
Following the cursor until chunk == false in TimeTreeApi._get_events. |
| 2 |
recurrences[0] was handed to Home Assistant including the RRULE: prefix, which fails CalendarEvent validation (rrule did not contain FREQ) and breaks every view spanning such a date (issue #2). |
recurrence.py strips the prefix, validates FREQ, expands occurrences and keeps the bare rule in rrule. |
| 3 | All-day events were off by one day in both directions: TimeTree stores an inclusive end (a one day event has start_at == end_at), Home Assistant expects an exclusive end (issue #5). |
TimeTreeEvent.ha_end (+1 day) on read, build_event_payload (-1 day, UTC midnight) on write. |
| 4 |
async_create_event read start_date_time/start_date, but Home Assistant core normalises the service data into dtstart/dtend — the kwargs were always None, so every calendar.create_event call raised (issue #3). |
_resolve_times() accepts dtstart/dtend, the WebSocket spelling and legacy keys. |
| 5 | Writes went to the plural /calendar/{id}/events route with a partial payload (no label, attendees, attachment, alerts, CSRF token, browser TLS) and were rejected with 422 (issue #5). |
Singular /calendar/{id}/event resource, full browser payload, scraped X-CSRF-Token, curl_cffi impersonation, plural fallback. |
Additional 1.1.3 limitations carried into the archive: no update/delete support, one calendar
per config entry, no conflict handling, no export to another calendar, no recurrence
expansion, no label/attendee exposure (issue #6), hass.data[DOMAIN] storage and the
Home Assistant 2025.12 API surface.
Migration note: 2.0.x requires Home Assistant 2026.9 or newer. Existing 1.1.3 config
entries keep working — the legacy single calendar layout (calendar_id/calendar_name in
entry.data) is picked up by TimeTreeOptions.from_entry and can be extended to multiple
calendars in the options flow.
🟢 Current — 2.0.x
(branch ha2026_09_dev, HA 2026.9+)
🗄️ Archived — 1.1.3
(branch main)
🔗 Links