Skip to main content
Glama
resace3
by resace3

android-timeline-home-assistant

CI Integration Cross-repository E2E Build app Security License: MIT

Status: experimental. The server side is exercised end to end in CI against the real collector, but no physical Android phone has ever fed it. See Current limitations.

A Home Assistant app (formerly "add-on") that receives raw timestamped events from the android-timeline-termux collector, stores them immutably, derives versioned hourly and daily features, measures how much of each day was actually observed, publishes a handful of summary entities, and exposes a read-only MCP server so Claude can ask questions about the data.

What it does

  • Authenticated ingestion. Device-specific bearer tokens, stored only as keyed hashes. Idempotent on event_id, so a replay is always a no-op.

  • Immutable raw events. Features are derived and versioned; recomputing them never rewrites history.

  • Explicit missingness. Every hour is a row, including the empty ones. A flat line and a dead collector are different things and are reported differently.

  • Read-only MCP. No SQL, no shell, no filesystem, no mutation, no phone control. Bounded ranges, pagination, and redaction that cannot be turned off.

  • Minimal privileges. Ingress only, no published port, no Supervisor role, no Home Assistant directory mounted.

Related MCP server: airspeed-mcp

Architecture

flowchart TD
    phone["Android phone<br/><i>android-timeline-termux</i>"]

    subgraph app["Home Assistant app (this repository)"]
        direction TB
        api["FastAPI ingestion<br/>POST /api/v1/events/batch"]
        auth["Device token auth<br/>+ rate limiting"]
        store[("SQLite event store<br/>append-only")]
        features["Feature pipeline<br/>hourly + daily, versioned"]
        cov["Coverage and gap detection"]
        tl["Timeline API<br/>GET /api/v1/timeline/yesterday"]
        mcp["Read-only MCP server<br/>/mcp"]
        ent["Summary entity publisher"]

        api --> auth --> store
        store --> features --> cov
        cov --> tl
        cov --> mcp
        features --> ent
    end

    ha["Home Assistant Core<br/>sensor.* / binary_sensor.*"]
    claude["Claude / MCP client"]

    phone -->|"HTTPS, Bearer token,<br/>X-Batch-ID"| api
    api -->|"per-event acknowledgement"| phone
    ent -->|"Supervisor API proxy"| ha
    mcp --> claude

Installation

  1. In Home Assistant: Settings -> Apps -> App store -> ⋮ -> Repositories

  2. Add https://github.com/resace3/android-timeline-home-assistant

  3. Install Android Timeline from the store, then Start it.

  4. Open the app's web UI (ingress) to reach the diagnostic page.

  5. Enroll your phone:

    curl -X POST http://<app>/api/v1/admin/devices \
      -H 'Content-Type: application/json' \
      -d '{"device_id": "device-my-pixel-001"}'

    The response contains the device token once. Copy it into the collector's ~/.config/android-timeline/token. Only a keyed hash is stored here; there is no way to recover it later, only to rotate.

Full details, including running behind ingress and the optional custom integration: docs/installation.md.

Configuration

Option

Default

Notes

timezone

UTC

Where a "day" starts. Events are always stored in UTC.

features_enabled

true

Turning it off stops the pipeline; nothing is deleted.

publish_entities

true

Summary entities only, never one per event.

mcp_enabled

true

Read-only MCP endpoint, ingress-protected.

trust_ingress_admin

true

Safe because no port is published.

admin_token

(empty)

Only needed outside ingress.

retention_enabled

false

Raw events are kept forever by default.

API

Endpoint

Auth

Purpose

GET /api/v1/health

none

liveness; no user data

POST /api/v1/events/batch

device token

ingestion

GET /api/v1/devices/{id}/status

device token (own device)

queue and sync state

GET /api/v1/timeline/yesterday

admin

the day timeline

GET /api/v1/timeline/{date}

admin

a specific local date

GET /api/v1/features/hourly

admin

stored hourly features

GET /api/v1/coverage

admin

coverage and gaps

POST /api/v1/admin/devices

admin

enroll, returns a token once

POST /api/v1/admin/devices/{id}/rotate

admin

rotate, revoking the old token

DELETE /api/v1/admin/tokens/{id}

admin

revoke

MCP

Twelve read-only tools: list_devices, list_phone_sources, get_phone_latest, query_phone_events, get_day_timeline, get_hourly_features, get_daily_features, get_data_coverage, find_data_gaps, get_collector_status, export_phone_window and describe_server.

Guaranteed absent: arbitrary SQL, shell execution, filesystem access, mutation of any kind, and phone control. Date ranges are capped at 31 days, pages at 1000 rows, and precise coordinates, message bodies and contact names are stripped with no parameter to re-enable them.

Client configuration (placeholders only): docs/mcp.md.

Testing

Layer

Status

What it proves

Unit

required

database, auth, ingestion, features, coverage, timeline

API + MCP

required

the HTTP surface and the MCP protocol, in-process

Contract

required

Pydantic models and published JSON Schemas agree

E2E (in-process)

required

synthetic day -> ingest -> features -> timeline -> MCP, across a restart

Container (Layer A)

required

the real image: build, boot, drive, restart

Mock Supervisor (Layer B)

required

the one Supervisor endpoint this app calls

App image build (Layer D)

required

current official HA builder actions, amd64 + aarch64

Cross-repository E2E

required

the real collector uploading to the real image

Home Assistant Core (Layer C)

non-blocking

the custom integration loads in a real HA container

Supervised HA (Layer E)

not attempted

see below

The cross-repository workflow installs the actual collector from android-timeline-termux at a pinned revision, runs its own outbox and uploader against a container built from this repository, then asserts on storage, idempotency, features, coverage and MCP. Details: docs/testing.md.

Current limitations

Stated plainly:

  • No physical phone has ever fed this. Every event CI has ever ingested is synthetic.

  • Supervisor is not tested in CI. A real Supervisor needs privileged Docker-in-Docker or a full OS image; a flaky green result would be worse than an honest gap. Layer B tests the one endpoint this app actually calls, and nothing more.

  • Ingress trust is a design assumption. Treating an ingress request as an authenticated admin is only safe because no port is published. If you publish one, set admin_token and turn trust_ingress_admin off.

  • SQLite, single writer. Fine for a personal deployment; not a fleet.

  • Features are approximations. charging_minutes and wifi_connected_minutes are inferred from periodic samples, so a state change between samples is invisible. Every value ships with a coverage proportion for exactly this reason.

  • The timeline has no polished UI. The / page is a diagnostic table.

Privacy considerations

  • Raw events are stored as the phone sent them; the phone is where redaction happens, and it defaults to metadata only.

  • The MCP server redacts precise coordinates, message bodies and contact names again on the way out, with no override.

  • Home Assistant entities carry aggregates only, so the recorder database never accumulates behavioural detail.

  • No fixture, test or CI artifact contains real personal data, and CI fails if a credential-shaped string appears anywhere in history.

Relationship to the other repository

android-timeline-termux

runs on the phone: collection, the outbox, the upload client

This repository

runs in Home Assistant: ingestion, storage, features, coverage, MCP

This repository is the source of truth for the protocol schemas in schemas/. The collector vendors a byte-identical copy; the cross-repository workflow diffs them directly and fails on any difference. Both speak protocol_version 1. Compatibility policy: docs/data-model.md.

Development

python -m pip install -e ".[dev]"
ruff format . && ruff check . && mypy
pytest

Run the whole pipeline locally without Home Assistant:

docker compose -f test-harness/docker-compose.yml up --build

Roadmap

  • Ingest from a real phone (blocking for 1.0)

  • QR-code device enrollment

  • A real timeline UI

  • Optional Postgres backend for larger deployments

  • Intervention annotations feeding the causal-inference use case

  • Feature definitions v2 with dwell-time and place transitions

License

MIT

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    MCP server to read daily activity, sleep, heart rate, and body metrics from Google Health API, allowing AI assistants like Claude to access your health data. Optionally syncs health metrics to an Obsidian vault.
    Last updated
    5
    MIT
  • F
    license
    -
    quality
    C
    maintenance
    Remote MCP server that exposes Airspeed/Glyphic call data to Claude web, enabling tools to list, retrieve, and query calls, transcripts, snippets, and playbooks.
    Last updated
  • A
    license
    A
    quality
    C
    maintenance
    Read-only MCP server enabling natural language querying of personal health and cultural activity scores from the health.ojimpo.com dashboard via Claude.
    Last updated
    5
    20
    MIT

View all related MCP servers

Related MCP Connectors

  • Read-only MCP server for ClassQuill, a tutoring-business-management platform.

  • Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.

  • A paid remote MCP for AI SDK data query MCP, built to return verdicts, receipts, usage logs, and aud

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/resace3/android-timeline-home-assistant'

If you have feedback or need assistance with the MCP directory API, please join our Discord server