From 00e1779f463f7d84a73974fd5cf15d4afa29165e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 25 May 2026 11:58:38 +0000 Subject: [PATCH] Support running as a standalone Docker container Adds a docker-compose.yml and README docs for running Timeframe outside the Home Assistant add-on system, configured entirely via environment variables (including a remote HA URL). The bundled Postgres and add-on/Supervisor mode are unchanged, so HA installs keep working. docker-start now persists SECRET_KEY_BASE to the data volume when no env var is set, generating a fresh key for new installs while preserving the legacy default for pre-existing databases so already-encrypted data stays readable. https://claude.ai/code/session_01DV6NWdvHQzMbBSp6zeCo5G --- README.md | 46 +++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 32 +++++++++++++++++++++++++++++++ script/docker-start | 20 ++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 docker-compose.yml diff --git a/README.md b/README.md index e4878cee..b0d327de 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,52 @@ An e-paper calendar, weather, and smart home family dashboard 5. Click **Start** 6. Access the app at port 8099 (e.g. `http://homeassistant.local:8099`) +## Run as a standalone Docker container + +Timeframe can also run as a regular Docker container, independent of the Home Assistant add-on system. All configuration comes from environment variables, and it can point at any reachable Home Assistant instance — local or remote. + +You'll need a Home Assistant **long-lived access token**: in HA, open your profile → **Security** → **Long-Lived Access Tokens** → **Create Token**. + +### Using Docker Compose + +A ready-to-edit [`docker-compose.yml`](docker-compose.yml) is included. Set `TIMEFRAME_HOME_ASSISTANT_URL` and `TIMEFRAME_HOME_ASSISTANT_TOKEN`, then: + +``` +docker compose up -d --build +``` + +Open the dashboard at `http://localhost:8099`. + +### Using docker run + +``` +docker build -t timeframe . + +docker run -d \ + --name timeframe \ + -p 8099:8099 \ + -v timeframe-data:/data \ + -e TIMEFRAME_HOME_ASSISTANT_URL="http://192.168.1.50:8123" \ + -e TIMEFRAME_HOME_ASSISTANT_TOKEN="your_long_lived_access_token" \ + timeframe +``` + +### Environment variables + +| Variable | Default | Description | +|---|---|---| +| `TIMEFRAME_HOME_ASSISTANT_URL` | `http://homeassistant.local:8123` | Base URL of your Home Assistant instance. Use any reachable address, e.g. `http://192.168.1.50:8123`. | +| `TIMEFRAME_HOME_ASSISTANT_TOKEN` | _(required)_ | Home Assistant long-lived access token. Required when running standalone. | +| `TIMEFRAME_TEMPERATURE_UNIT` | `F` | `F` or `C`. | +| `TIMEFRAME_SPEED_UNIT` | `mph` | `mph` or `kph`. | +| `TIMEFRAME_PRECIPITATION_UNIT` | `in` | `in`, `mm`, or `cm`. | +| `SECRET_KEY_BASE` | _(auto-generated)_ | Secret used to encrypt sessions and stored data. If unset, a random key is generated and persisted to the `/data` volume on first run. | +| `PORT` | `8099` | Port the web interface listens on inside the container. | + +Keep the `/data` volume across restarts — it holds the bundled Postgres database and the generated secret key. + +> When installed as a Home Assistant add-on, Timeframe instead authenticates automatically via the Supervisor and reads unit options from the add-on configuration UI, so no environment variables are required in that mode. + ## Configuration The following entities can be created in Home Assistant to customize behavior. Icon names are from [Material Design Icons](https://pictogrammers.com/library/mdi/) (without the `mdi-` prefix). diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..8c704927 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,32 @@ +# Standalone Docker deployment for Timeframe, independent of the Home Assistant +# add-on system. All configuration is supplied via environment variables. +# +# docker compose up -d --build +# +# Then open http://localhost:8099 +services: + timeframe: + build: . + # Alternatively, run an image you've built/pushed yourself: + # image: timeframe:latest + restart: unless-stopped + ports: + - "8099:8099" + volumes: + # Holds the Postgres database and the generated secret key. + - timeframe-data:/data + environment: + # Base URL of your Home Assistant instance (any reachable address). + TIMEFRAME_HOME_ASSISTANT_URL: "http://homeassistant.local:8123" + # Long-lived access token (HA: Profile -> Long-Lived Access Tokens). + TIMEFRAME_HOME_ASSISTANT_TOKEN: "your_long_lived_access_token" + # Optional unit overrides (defaults shown). + TIMEFRAME_TEMPERATURE_UNIT: "F" # F or C + TIMEFRAME_SPEED_UNIT: "mph" # mph or kph + TIMEFRAME_PRECIPITATION_UNIT: "in" # in, mm, or cm + # Optional: pin the secret used to encrypt sessions and stored data. + # If omitted, a key is generated and persisted to the data volume. + # SECRET_KEY_BASE: "change_me_to_a_long_random_string" + +volumes: + timeframe-data: diff --git a/script/docker-start b/script/docker-start index a60fef95..0792702a 100755 --- a/script/docker-start +++ b/script/docker-start @@ -5,6 +5,26 @@ PG_VERSION=17 PG_CLUSTER=main PG_DATA="/data/pgdata" +# Ensure a stable SECRET_KEY_BASE so encrypted columns and sessions survive +# restarts. An explicit env var always wins. Otherwise persist a key to the +# data volume: generate a fresh secure key for new installs, but keep the +# legacy default for databases created before this existed so their already +# encrypted data stays decryptable. Must run before the cluster is created +# below, since it checks whether the data dir predates this version. +if [ -z "${SECRET_KEY_BASE:-}" ]; then + SECRET_FILE="/data/.secret_key_base" + mkdir -p /data + if [ ! -f "$SECRET_FILE" ]; then + if [ -f "$PG_DATA/PG_VERSION" ]; then + printf 'foo' > "$SECRET_FILE" + else + ruby -rsecurerandom -e 'print SecureRandom.hex(64)' > "$SECRET_FILE" + fi + fi + SECRET_KEY_BASE="$(cat "$SECRET_FILE")" + export SECRET_KEY_BASE +fi + # Move Postgres data to persistent storage on first run if [ ! -f "$PG_DATA/PG_VERSION" ]; then mkdir -p "$PG_DATA"