Creates a schedule to simulate 'at home' lighting when away on vacation.
  • HTML 63.5%
  • Python 35.8%
  • Dockerfile 0.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-03-02 09:47:32 -08:00
.idea First commit of working code 2026-02-22 10:12:13 -08:00
docs Auto tagging steps for future stuff 2026-03-02 09:47:32 -08:00
templates feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
.dockerignore feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
.env.example feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
.gitignore feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
.python-version First commit of working code 2026-02-22 10:12:13 -08:00
.woodpecker.yml Auto tagging steps for future stuff 2026-03-02 09:47:32 -08:00
CLAUDE.md Auto tagging steps for future stuff 2026-03-02 09:47:32 -08:00
config.py feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
database.py feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
docker-compose.example.yml feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
docker-compose.yml feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
Dockerfile feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
ha_client.py Rewrite: FastAPI + APScheduler server replacing CalDAV pipeline 2026-02-22 16:04:49 -08:00
LICENSE Initial commit 2026-02-22 04:29:31 +01:00
main.py Rewrite: FastAPI + APScheduler server replacing CalDAV pipeline 2026-02-22 16:04:49 -08:00
pyproject.toml Rewrite: FastAPI + APScheduler server replacing CalDAV pipeline 2026-02-22 16:04:49 -08:00
README.md feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
sample-config.yaml feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
scheduler.py Rewrite: FastAPI + APScheduler server replacing CalDAV pipeline 2026-02-22 16:04:49 -08:00
server.py feat: Dockerize with config/data volume separation (#1) 2026-03-02 17:38:19 +01:00
uv.lock Rewrite: FastAPI + APScheduler server replacing CalDAV pipeline 2026-02-22 16:04:49 -08:00

ha-presence-simulation

A FastAPI service that generates randomized light schedules for home presence simulation while on vacation. Schedules are stored in SQLite and fired directly against the Home Assistant REST API via APScheduler — no calendar middleware required.

How It Works

Each light group has one or more time windows per day. For each window, a single on/off event is generated per day:

  • Start time: start_time + random 0..fidget_minutes offset
  • Duration: duration_minutes ±20%, randomized each run

Events are stored in a local SQLite database and scheduled with APScheduler, which fires light.turn_on and light.turn_off calls directly against the HA REST API at the right times. On server restart, all future jobs are re-registered from the database automatically.

Setup

Prerequisites

  • A running Home Assistant instance with a Long-Lived Access Token
  • To create one: Profile → Security → Long-Lived Access Tokens → Create Token

Configuration

Copy .env.example to .env and fill in your values:

cp .env.example .env
HA_URL=http://homeassistant.local:8123
HA_TOKEN=your-long-lived-access-token
TZ=America/Los_Angeles

Create a config/ directory and copy sample-config.yaml into it as config.yaml:

mkdir config
cp sample-config.yaml config/config.yaml

Edit config/config.yaml to define your light groups, brightness levels, and time windows:

light_groups:
  - name: Family Room
    brightness: 255          # 0-255
    entities:
      - light.family_room_lamps
    windows:
      - start_time: '18:30'  # earliest on-time
        fidget_minutes: 30   # random 0-30 min added to start_time
        duration_minutes: 90 # on-duration base; actual is ±20% of this

config/ and data/ are gitignored — they hold your personal configuration and runtime database and should not be committed.

cp docker-compose.example.yml docker-compose.yml
docker compose up -d

docker-compose.example.yml:

services:
  presence-simulation:
    image: codeberg.org/johnsturgeon/ha-presence-simulation:latest
    ports:
      - "8000:8000"
    volumes:
      - ./config:/config
      - ./data:/data
    env_file: .env
    restart: unless-stopped

./config (containing config.yaml) is mounted at /config inside the container. The SQLite database is written to ./data/ on the host, mounted at /data. Both directories survive container restarts and image updates.

Bare-metal

Requires Python 3.14+ and uv.

uv sync
uv run python main.py

The server starts on http://localhost:8000.

To keep it running persistently, use a process manager. Example systemd unit:

[Unit]
Description=HA Presence Simulation
After=network.target

[Service]
WorkingDirectory=/path/to/ha-presence-simulation
ExecStart=/path/to/ha-presence-simulation/.venv/bin/python main.py
Restart=always

[Install]
WantedBy=multi-user.target

Web UI

Page Path Description
Schedule / View upcoming events grouped by day; generate new schedules; delete individual events
Config /config Edit light groups, entities, windows, and brightness; refresh HA entity cache
iCal feed /calendar.ics Subscribe in any calendar app (Apple Calendar, Google Calendar, etc.)

Docs

Notes

  • Schedule generation is additive — generating a new range adds to existing events; use Clear All or delete individual rows if you want to replace.
  • The entity list on the Config page is cached from HA (refreshed every 30 minutes). Use the ↻ Refresh Entities button to pull immediately.
  • .env is gitignored — see .env.example for required variables.