Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
32 changes: 32 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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:
20 changes: 20 additions & 0 deletions script/docker-start
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading