Thanks for the interest. This is a hobby project, so the bar for "contributing" is intentionally low — bug reports, feature ideas, and PRs are all welcome.
- Report a bug
- Request a feature
- Report a security issue (private channel — don't open a public issue)
- Code of Conduct
The app is a FastAPI + HTMX dashboard packaged as a Home Assistant app. You can develop and test it entirely on your laptop — no real UniFi or HA install needed for most changes.
cd access_control/rootfs/opt
python -m venv .venv
.venv/bin/pip install -r access_control/requirements-dev.txt
.venv/bin/pytest access_control/tests -qThe suite takes ~1 second and covers the auth engine, circuit breaker, ingress middleware, re-lock manager, and core route flows.
cd access_control
docker build \
--build-arg BUILD_FROM=ghcr.io/home-assistant/amd64-base-python:3.12-alpine3.21 \
--build-arg BUILD_ARCH=amd64 \
--build-arg BUILD_VERSION=dev \
-t access-control:dev .mkdir -p /tmp/ac-data
echo '{"log_level":"info","use_supervisor_api":false}' > /tmp/ac-opts.json
docker run --rm -p 8080:8080 \
-v /tmp/ac-data:/data \
-v /tmp/ac-opts.json:/data/options.json:ro \
access-control:devThen open http://localhost:8080/setup in your browser.
To simulate the HA Ingress + SSO path, pass the headers manually with
curl:
curl -H 'X-Ingress-Path: /api/hassio_ingress/dummytoken' \
-H 'X-Remote-User-Id: u1' \
-H 'X-Remote-User-Name: Test' \
-H 'X-Remote-User-Is-Admin: true' \
http://localhost:8080/Every push to main and every PR triggers:
yamllint— YAML files (configs, workflows, GitHub forms)hadolint— Dockerfile lintingshellcheck—run.shand other bashpytest— full test suite- Multi-arch build (
amd64,aarch64) using the home-assistant/builder action; PRs build with--test, main builds publish toghcr.io
All checks must pass before a PR can merge.
- Python: roughly PEP 8. No formatter is enforced yet; new code should look like neighboring code.
- Templates: HTML + Jinja2. Use relative URLs so they resolve
against
<base href>under both ingress and direct-port access. The README's "Anti-pattern" callout intemplates/base.htmlexplains why. - Tests: prefer
unittest.IsolatedAsyncioTestCasefor async tests (matches the existing pattern; works out of the box withpytest).
Conventional Commits are encouraged but not required:
feat: ...— new featurefix: ...— bug fixdocs: ...— documentation onlyrefactor: ...— internal restructuring with no behavior changetest: ...— test changes onlychore: ...— build, CI, dependencies
For PRs:
- Link the issue you're addressing if there is one (
Fixes #N). - Update
access_control/CHANGELOG.mdunder an## [Unreleased]heading if the change is user-visible. - Add tests for new behavior. Run
pytestbefore pushing. - If you change anything in
access_control/config.yaml'sversion, also updateCHANGELOG.mdwith the corresponding release section.
.
├── access_control/ # the app
│ ├── config.yaml # app manifest
│ ├── Dockerfile # multi-arch image
│ ├── build.yaml # base images per arch
│ ├── apparmor.txt # security profile
│ ├── README.md / DOCS.md / CHANGELOG.md
│ └── rootfs/
│ ├── run.sh # bashio entrypoint
│ └── opt/access_control/ # FastAPI app + tests
├── .github/workflows/ci.yaml # lint + build + publish
├── docs/specs/ # design notes
├── docs/screenshots/ # README screenshots
└── README.md # main README