├── .github ├── dependabot.yml └── workflows │ ├── build-and-push-image.yml │ └── dependabot-merge.yml ├── .vscode └── tasks.json ├── Dockerfile ├── LICENSE.txt ├── README.md ├── build.sh ├── container ├── hassfest ├── requirements.txt ├── test.env └── test.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily -------------------------------------------------------------------------------- /.github/workflows/build-and-push-image.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Image 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | docker: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Login to docker hub 12 | uses: docker/login-action@v1 13 | with: 14 | username: ${{ secrets.DOCKERHUB_USERNAME }} 15 | password: ${{ secrets.DOCKERHUB_TOKEN }} 16 | - name: Build and push 17 | uses: docker/build-push-action@v2 18 | with: 19 | context: . 20 | push: true 21 | tags: thomasloven/hass-custom-devcontainer:latest 22 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-merge.yml: -------------------------------------------------------------------------------- 1 | name: auto-merge 2 | 3 | on: 4 | pull_request_target: 5 | 6 | jobs: 7 | auto-merge: 8 | if: github.actor == 'dependabot[bot]' 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 12 | with: 13 | target: minor 14 | github-token: ${{ secrets.GH_TOKEN }} 15 | command: squash and merge -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build", 8 | "type": "shell", 9 | "command": "docker build . -t thomasloven/hass-custom-devcontainer --force-rm", 10 | "problemMatcher": [] 11 | }, 12 | { 13 | "label": "Test", 14 | "type": "shell", 15 | "command": "./test.sh", 16 | "problemMatcher": [] 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #FROM homeassistant/home-assistant:dev 2 | FROM mcr.microsoft.com/devcontainers/python:1-3.13 3 | 4 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 5 | 6 | RUN \ 7 | curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ 8 | && apt-get update \ 9 | && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 10 | bluez \ 11 | libffi-dev \ 12 | libssl-dev \ 13 | libjpeg-dev \ 14 | zlib1g-dev \ 15 | autoconf \ 16 | build-essential \ 17 | libopenjp2-7 \ 18 | libtiff6 \ 19 | libturbojpeg0-dev \ 20 | tzdata \ 21 | ffmpeg \ 22 | liblapack3 \ 23 | liblapack-dev \ 24 | libatlas-base-dev \ 25 | \ 26 | git \ 27 | libpcap-dev \ 28 | && apt-get clean \ 29 | && rm -rf /var/lib/apt/lists/* \ 30 | && source /usr/local/share/nvm/nvm.sh \ 31 | && nvm install lts/iron \ 32 | && pip install --upgrade wheel pip 33 | 34 | COPY --from=ghcr.io/alexxit/go2rtc:latest /usr/local/bin/go2rtc /bin/go2rtc 35 | RUN pip3 install uv 36 | 37 | EXPOSE 8123 38 | 39 | VOLUME /config 40 | 41 | USER vscode 42 | ENV VIRTUAL_ENV="/home/vscode/.local/ha-venv" 43 | RUN uv venv $VIRTUAL_ENV 44 | ENV PATH="$VIRTUAL_ENV/bin:$PATH" 45 | 46 | COPY requirements.txt /tmp/requirements.txt 47 | RUN uv pip install -r /tmp/requirements.txt 48 | COPY container /usr/bin 49 | COPY hassfest /usr/bin 50 | 51 | CMD sudo -E container -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Thomas Lovén 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A docker container for developing and testing custom Home Assistant plugins and integrations 2 | 3 | ## Usage 4 | 5 | ``` 6 | docker run --rm -it \ 7 | -p 5000:8123 \ 8 | -v $(pwd):/workspaces/test \ 9 | -v $(pwd):/config/www/workspace \ 10 | -e LOVELACE_LOCAL_FILES="myplugin.js" 11 | -e LOVELACE_PLUGINS="thomasloven/lovelace-card-mod thomasloven/lovelace-auto-entities custom-cards/button-card" \ 12 | thomasloven/hass-custom-devcontainer 13 | ``` 14 | 15 | The default action of the image is to run `container`, which will 16 | - Make sure there's a basic Home Assistant configuration in `/config` 17 | - Add a default admin user to Home Assistant 18 | - Skip the onboarding procedure 19 | - Download and install [HACS](https://hacs.xyz) 20 | - Download lovelace plugins from github 21 | - Add plugins to lovelace configuration 22 | - Start Home Assistant with `-v` 23 | 24 | 25 | ### Environment Variables 26 | 27 | | Name | Description | Default | 28 | |---|---|---| 29 | | `HASS_USERNAME` | The username of the default user | `dev` | 30 | | `HASS_PASSWORD` | The password of the default user | `dev` | 31 | | `LOVELACE_PLUGINS` | List of lovelac plugins to download from github | Empty | 32 | | `LOVELACE_LOCAL_FILES` | List of filenames in `/config/www/workspace` to add as lovelace resources | Emtpy | 33 | 34 | ### About Lovelace Plugins 35 | The dowload and install of plugins is *very* basic. This is not HACS. 36 | 37 | `LOVELACE_PLUGINS` should be a space separated list of author/repo pairs, e.g. `"thomasloven/lovelace-card-mod kalkih/mini-media-player"` 38 | 39 | `LOVELACE_LOCAL_FILES` is for the currently worked on plugins and should be a list of file names which are mounted in `/config/www/workspace`. 40 | 41 | ### Container Script 42 | 43 | ```bash 44 | container 45 | ``` 46 | Set up and launch Home Assistant 47 | 48 | ```bash 49 | container setup 50 | ``` 51 | Perform download and setup parts but do not launch Home Assistant 52 | 53 | ```bash 54 | container launch 55 | ``` 56 | Launch Home Assistant with `hass -c /config -v` 57 | 58 | ## devcontainer.json example 59 | 60 | ```json 61 | { 62 | "image": "thomasloven/hass-custom-devcontainer", 63 | "postCreateCommand": "container setup && npm add", 64 | "forwardPorts": [8123], 65 | "mounts": [ 66 | "source=${localWorkspaceFolder},target=/config/www/workspace,type=bind", 67 | "source=${localWorkspaceFolder}/test,target=/config/test,type=bind", 68 | "source=${localWorkspaceFolder}/test/configuration.yaml,target=/config/configuration.yaml,type=bind" 69 | ], 70 | "runArgs": ["--env-file", "${localWorkspaceFolder}/test/.env"] 71 | } 72 | ``` -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | docker build -t thomasloven/hass-custom-devcontainer . -------------------------------------------------------------------------------- /container: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | plugins= 4 | 5 | function ensure_hass_config() { 6 | hass --script ensure_config -c /config 7 | } 8 | 9 | function create_hass_user() { 10 | local username=${HASS_USERNAME:-dev} 11 | local password=${HASS_PASSWORD:-dev} 12 | echo "Creating Home Assistant User ${username}:${password}" 13 | hass --script auth -c /config add ${username} ${password} 14 | } 15 | 16 | function bypass_onboarding() { 17 | cat > /config/.storage/onboarding << EOF 18 | { 19 | "data": { 20 | "done": [ 21 | "user", 22 | "core_config", 23 | "integration" 24 | ] 25 | }, 26 | "key": "onboarding", 27 | "version": 3 28 | } 29 | EOF 30 | } 31 | 32 | function fetch_lovelace_plugins() { 33 | mkdir -p /config/www/workspace 34 | for plugin in $LOVELACE_PLUGINS 35 | do 36 | local author=$(cut -d '/' -f1 <<<"$plugin") 37 | local repo=$(cut -d '/' -f2 <<<"$plugin") 38 | local prefix=$(cut -d '-' -f1 <<<"$repo") 39 | local file 40 | 41 | if [[ $prefix == "lovelace" ]]; then 42 | file=$(cut -c 10- <<<"$repo") 43 | else 44 | file=$repo 45 | fi 46 | 47 | echo "Downloading ${plugin} to ${file}.js" 48 | curl https://raw.githubusercontent.com/${author}/${repo}/master/dist/${file}.js --output /config/www/${file}.js --fail \ 49 | || curl https://raw.githubusercontent.com/${author}/${repo}/master/${file}.js --output /config/www/${file}.js --fail \ 50 | || curl -L https://github.com/${author}/${repo}/releases/latest/download/${file}.js --output /config/www/${file}.js --fail \ 51 | || curl -L https://github.com/${author}/${repo}/releases/latest/download/${file}-bundle.js --output /config/www/${file}.js --fail 52 | plugins="${plugins} /local/${file}.js" 53 | done 54 | } 55 | 56 | function install_lovelace_plugins() { 57 | 58 | for file in $LOVELACE_LOCAL_FILES 59 | do 60 | plugins="${plugins} /local/workspace/${file}" 61 | done 62 | 63 | fetch_lovelace_plugins 64 | 65 | cat > /config/.storage/lovelace_resources << EOF 66 | { 67 | "data": { 68 | "items": [ 69 | EOF 70 | 71 | local i="0" 72 | for plugin in $plugins 73 | do 74 | if [ $i -gt "0" ]; then 75 | echo "," >> /config/.storage/lovelace_resources 76 | fi 77 | 78 | i=$((i+1)) 79 | 80 | head -c -1 >> /config/.storage/lovelace_resources << EOF 81 | { 82 | "id": "${i}", 83 | "type": "module", 84 | "url": "${plugin}" 85 | } 86 | EOF 87 | done 88 | 89 | cat >> /config/.storage/lovelace_resources << EOF 90 | 91 | ] 92 | }, 93 | "key": "lovelace_resources", 94 | "version": 1 95 | } 96 | EOF 97 | } 98 | 99 | function install_hacs() { 100 | curl -sfSL https://get.hacs.xyz | bash - 101 | } 102 | 103 | function setup() { 104 | ensure_hass_config 105 | create_hass_user 106 | bypass_onboarding 107 | install_hacs 108 | install_lovelace_plugins 109 | } 110 | 111 | function get_dev() { 112 | rm -rf /usr/src/homeassistant 113 | git clone --branch dev --single-branch --depth 1 https://github.com/home-assistant/core.git /usr/src/homeassistant 114 | pip install --upgrade -e /usr/src/homeassistant 115 | } 116 | 117 | function run() { 118 | hass -c /config -v 119 | } 120 | 121 | if [[ -f "$ENV_FILE" ]]; then 122 | source "$ENV_FILE" 123 | fi 124 | 125 | case $1 in 126 | setup-dev) 127 | get_dev 128 | setup 129 | ;; 130 | setup) 131 | setup 132 | ;; 133 | launch) 134 | run 135 | ;; 136 | run-dev) 137 | get_dev 138 | setup 139 | run 140 | ;; 141 | *) 142 | setup 143 | run 144 | ;; 145 | esac -------------------------------------------------------------------------------- /hassfest: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /usr/src/homeassistant 4 | for integration in `find /config/custom_components -mindepth 1 -maxdepth 1 -type d` 5 | do 6 | python -m script.hassfest --action validate --integration-path $integration 7 | done -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | homeassistant==2025.3.4 2 | -------------------------------------------------------------------------------- /test.env: -------------------------------------------------------------------------------- 1 | HASS_USERNAME=test 2 | HASS_PASSWORD=test -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "================" 4 | echo "To test run:" 5 | echo "$ sudo container setup-dev" 6 | echo "$ sudo container launch" 7 | echo "" 8 | echo "You may need to run the seccond command two or more times for all packages to install" 9 | echo "================" 10 | 11 | docker run --rm -it \ 12 | -p 5002:8123 \ 13 | -v $(pwd):/workspaces/test \ 14 | -v $(pwd):/config/www/workspace \ 15 | -e LOVELACE_PLUGINS="thomasloven/lovelace-card-mod thomasloven/lovelace-auto-entities custom-cards/button-card kalkih/mini-media-player" \ 16 | -e ENV_FILE="/workspaces/test/test.env" \ 17 | thomasloven/hass-custom-devcontainer bash 18 | # sudo container setup-dev 19 | # sudo container launch --------------------------------------------------------------------------------