- TypeScript 41.2%
- Python 35.3%
- Rust 12.5%
- Astro 7.6%
- Dockerfile 1.7%
- Other 1.7%
| .forgejo/workflows | ||
| api | ||
| app | ||
| oci | ||
| ssh-cli | ||
| .dev.env | ||
| .envrc.dist | ||
| .gitignore | ||
| Containerfile | ||
| dev.Caddyfile | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
Dooris
Dooris setup using HomeMatic with Web UI, API and OIDC integration.
Based on prior work of hmdooris.
Project structure:
├── api # Python application interacting with HomeMatic and providing the API.
├── ssh-cli # CLI application that allows door management over SSH
└── app # Web UI
Architecture Explanation
The deployment of dooris is a little bit involved but I hope this paragraph can give some clarity about the required details.
This dooris implementation works together with an existing HomeMatic installation that does the actual work of turning locks. Dooris is in principal just the access layer as well as integration layer into other CCH services. For example, dooris enables us to bind guard access to the underlying HomeMatic to our Keycloak roles as well as enabling new access methods like dooris-over-ssh.
The appliction is implemented using the following different components:
-
An API (or backend). This python application is intended to be accessible over simple HTTP(S) connections and serves as the central point of managing everything. It interacts directly with HomeMatic by automatically querying its API for locks and connecting to its integrated MQTT server for state updates. All other components only every talk to this API and the API is in charge of managing all the different integrations.
-
An astro frontend served at the same HTTP(S) host as the API. This is the user facing part of dooris and what you see when you navigate to said url. In the background, this frontend simply talks to the API for available doors, state updates, lock interactions and whatelse. Since the frontend is also the user facing part of dooris, it is the intended place for catching common error conditions and displaying helpful hints.
-
The ssh-cli rust application whose setup is a bit contrived but which serves as an alternative access layer to the HTTP(S) frontend. Even though dooris is mainly packaged as a docker container, using this part of dooris requires configuration on the underlying linux host.
The way the integration works is as follows:
-
On the underlying docker host, a local
doorisuser is created with a home directory configured that is shared with the running dooris container. User ID, group ID and permissions must also match between the host user and the container user (10000for uid and gid by default). -
The container has an entrypoint script which copies the bundled
ssh-clibinary into said shared directory. Afterwards, the web service is started normally. -
The web service must be configured with a static API token (either via environment variables or cli parameters) to allow the ssh-cli access.
-
The keycloak instance backing dooris must have the attribute-endpoints-provider plugin installed and configured to expose an endpoint containing a list of ssh public keys and accessible by the dooris clients service account. That endpoint must be configured in the dooris backend instance.
-
The dooris backend regularly queries the configured keycloak endpoint for a list of ssh keys and writes them into the host-container shared directory as an authorized_keys file.
-
Finally, the ssh server on the host must have a specific configuration section for the local dooris user to use the automatically written authorized_keys file as well as force the command to be the placed
ssh-clibinary. The binary must also be configured via environment variables to connect to the running backend (including authorization).This can be done, for example, with the following config section:
Match User dooris AuthorizedKeysFile /my/shared/directory/dooris_authorized_keys ForceCommand /my/shared/directory/ssh-cli SetEnv DOORIS_SERVER_URL=https://dooris.ccchh.net/ DOORIS_API_TOKEN=foobar123 -
When a user logs in with the
doorisuser now, they are always dropped into thessh-clibinary. This works in cases where a command is specified on the users cli or where it isn't. In any case, thessh-clibinary interprets the given command (via theSSH_ORIGINAL_COMMANDenvironment variable), figures out what to do, and contacts the running API with the configured credentials to do it.
-
Configuration
The final application can be configured either via CLI arguments or via environment variables. If both are given, the CLI flag takes precedence.
THe following table lists all available configuration parameters:
| CLI Flag | Environment Variable | Required? | Default Value |
|---|---|---|---|
--openid-issuer |
DOORIS_OPENID_ISSUER |
Yes | None |
--openid-scope |
DOORIS_OPENID_SCOPE |
No | openid profile |
--openid-client-id |
DOORIS_OPENID_CLIENT_ID |
Yes | None |
--openid-client-secret |
DOORIS_OPENID_CLIENT_SECRET |
Yes | None |
--base-url |
DOORIS_BASE_URL |
Yes | None |
--serve-static |
DOORIS_SERVE_STATIC |
No | None |
--ccujack-url |
DOORIS_CCUJACK_URL |
No | https://hmdooris-ccu.ccchh.net:2122 |
--ccujack-mqtt |
DOORIS_CCUJACK_MQTT |
No | hmdooris-ccu.ccchh.net:1883 |
--ccujack-user |
DOORIS_CCUJACK_USER |
Yes | None |
--ccujack-password |
DOORIS_CCUJACK_PASSWORD |
Yes | None |
--static-api-tokens |
DOORIS_STATIC_API_TOKENS |
No | None |
API Development
Most things should automatically be set up with the included direnv script but if you don't use that, take the following steps:
- Go to the api/ directory
- Run
uv venvto create a python virtual environment - Install all dependencies of the dooris project into that virtual environment with
uv sync - Run a development server
watchexec -r -w src/ uv run dooris-api