Skip to content
DotfilesDotfiles
Esc
↑↓navigate↵open⌘Jpreview
On this page

Home Assistant

Running Home Assistant Core and frontend development servers through dot, pitchfork and Herdr.

I work on Home Assistant Core and the frontend most days. dot homeassistant (dot ha) sets up each repository, then runs its dev server as a pitchfork daemon so it gets a stable HTTPS URL from pitchfork’s proxy. Under Herdr, each command opens its own tab in the [HA] Core or [HA] Frontend workspace.

The short hacdev, hafdev* and hadev* functions in .zshrc just call dot ha. Repository paths, workspaces, daemon names, URLs and serve targets live in the private ~/.config/dot/homeassistant.yml, and the pitchfork config lives in the private overlay too, so it is copied below with real URLs replaced.

Commands

Command Alias
dot homeassistant dot ha
dot homeassistant core dot ha c
dot homeassistant frontend dot ha f
Case Command Shortcut
What is running, and where dot ha status [target...] hadevstatus, hacdevstatus, hafdevstatus, hafdevservestatus, hafdevsuitesstatus
Logs dot ha logs <target> [--follow] [--lines <n>] hacdevlogs, hafdevlogs, hafdevservelogs, hafdevdesignlogs, hafdevdemologs, hafdeve2eapplogs
Core with the local frontend build dot ha core dev [--background] hacdev, hacdevbg
Core, rebased onto upstream/dev first dot ha core dev --latest [--background] hacdevlatest, hacdevlatestbg
Core setup (Git, venv, bootstrap) dot ha core setup [--latest] Run by hacdev*
Frontend watch build alone dot ha frontend dev [--background] [--attach] hafdev, hafdevbg
Frontend against another Core dot ha frontend serve [<target or URL>] [--background] hafdevserve
Frontend against prod dot ha frontend serve prod [--background] hafdevserveprod, hafdevserveprodbg
Frontend through Home Assistant Link dot ha frontend serve link [--background] hafdevserveprodnc, hafdevserveprodncbg
Gallery dot ha frontend gallery [--background] hafdevdesign, hafdevdesignbg
Demo dot ha frontend demo [--background] hafdevdemo, hafdevdemobg
E2E test app dot ha frontend e2e [--background] hafdeve2eapp, hafdeve2eappbg
Production build dot ha frontend build hafbuild
E2E tests dot ha frontend test-e2e [app|demo|gallery] None
Core and frontend in Herdr tabs dot ha dev [--background] hadev, hadevbg
Stop servers (all by default) dot ha stop [target...] hadevstop, hacdevstop, hafdevstop, hafdevservestop, hafdevdesignstop, hafdevdemostop, hafdeve2eappstop, hafdevsuitesstop
Target Daemon URL
core ha-core/dev https://dev.ha-core.localhost
build ha-frontend/build Through Core’s URL
serve ha-frontend/serve https://serve.ha-frontend.localhost
prod ha-core/prod https://prod.ha-core.localhost
gallery, demo, e2e The frontend’s own background mode Printed when started

Port 443 already reaches pitchfork’s proxy (listening on 8443), so the URLs need no port. The proxy builds each hostname from the daemon name and namespace. Its auto-start is off, so a stopped daemon’s URL shows pitchfork’s “No daemon found” page rather than starting it; only the commands start daemons.

How the commands behave

  • Interactive runs go through dot status-run, which pins a header with the state, URL and elapsed time, and keeps the terminal title in step.
  • Each runs its setup first: dot ha core setup for Core (Git, virtual environment, script/bootstrap), script/bootstrap for the frontend.
  • If the daemon is already running, it asks before restarting it. dot ha dev’s Frontend tab follows a running build instead.
  • --background starts the daemon and returns once it’s ready; otherwise it follows the logs, and Ctrl+C stops it.
  • Every dot ha frontend command takes the frontend’s build lock, so it asks to stop the build and serve daemons first. Lint, format, type checks and unit tests don’t take the lock and stay plain pnpm.

Core behind the proxy

The proxy sets Host to localhost:<port> and adds X-Forwarded-* headers. Core rejects those unless it trusts the proxy. In the dev Core, turn on Trust X-Forwarded-For and add 127.0.0.1/32 and ::1/128 to Trusted proxies, under Settings > System > Network > Reverse proxy.

Serving against a remote Core also needs https://serve.ha-frontend.localhost in that instance’s CORS allowed origins, on the same page.

Open your Home Assistant instance and show your network configuration.

The browser blocks an HTTPS page from calling a plain HTTP Core, so the login works but the frontend can’t connect afterwards. ha-core/prod fronts the prod instance on an HTTPS URL with dot http-forward, which drops the proxy’s X-Forwarded-* headers so the remote Core accepts the requests without trusting this machine as a proxy. dot ha f serve prod starts it before serve.

Core only registers the frontend routes if hass_frontend exists when it starts, and a fresh build briefly removes it. ha-core/dev depends on ha-frontend/build, so starting Core starts the build too and waits for it to be ready.

Agents

Agents use the same dot ha commands. Under an agent, dot ha skips setup, Herdr tabs and prompts: it reuses a running daemon, runs suites in the background, and fails with a message instead of stopping a conflicting daemon. dot ha core setup and dot ha logs --follow refuse to run. The Home Assistant workspace AGENTS.md tells agents not to run hass, pnpm dev, pnpm dev:serve or pnpm build directly, and to ask before stopping or swapping a running daemon.

Pitchfork config

~/.config/pitchfork/config.toml enables the proxy and registers both repositories as namespaces:

[daemons]

[settings.proxy]
auto_start = false
enable = true
https = true
port = 8443

[namespaces.ha-core]
dir = "/home/<user>/repos/home-assistant/core"
config = ["/home/<user>/.config/dotfiles-private/pitchfork/.config/pitchfork/home-assistant/core.toml"]

[namespaces.ha-frontend]
dir = "/home/<user>/repos/home-assistant/frontend"
config = ["/home/<user>/.config/dotfiles-private/pitchfork/.config/pitchfork/home-assistant/frontend.toml"]

home-assistant/core.toml:

[daemons.dev]
run = "exec .venv/bin/hass -c config"
depends = ["ha-frontend/build"]
port = 8123
ready_port = { port = 8123, timeout = "5m" }
retry = true
stop_signal = { signal = "SIGTERM", timeout = "60s" }

[daemons.prod]
run = "exec dot http-forward --port 8126 --target http://homeassistant.local:8123"
port = 8126
ready_port = 8126

home-assistant/frontend.toml:

[daemons.build]
run = "exec pnpm dev --fetch-translations"
ready_output = { pattern = "Build done", timeout = "10m" }

[daemons.serve]
run = """
args="${XDG_STATE_HOME:-$HOME/.local/state}/dot/homeassistant/serve.args"
eval "set -- $(cat "$args" 2>/dev/null)"
exec pnpm dev:serve --fetch-translations "$@"
"""
port = 8124
ready_port = { port = 8124, timeout = "10m" }

dot config

~/.config/dot/homeassistant.yml:

core:
  directory: ~/repos/home-assistant/core
  workspace: "[HA] Core"
  daemon: ha-core/dev
  url: https://dev.ha-core.localhost
frontend:
  directory: ~/repos/home-assistant/frontend
  workspace: "[HA] Frontend"
  build: ha-frontend/build
  serve:
    daemon: ha-frontend/serve
    url: https://serve.ha-frontend.localhost
    targets:
      prod:
        title: Prod
        url: https://prod.ha-core.localhost
        daemon: ha-core/prod
      link:
        title: Link
        url: https://<instance>.ui.nabu.casa

Last updated on