diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51d6313 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +**/* + +!.gitignore +!docker-compose.yml +!README.md + +!hass +!hass/* diff --git a/README.md b/README.md new file mode 100644 index 0000000..db0ae04 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# Home Assistant docker compose + +This compose sets up Home Assistant and services used by it + +## hass +Home assistant container. + +The image is specially built with the following features +- Home Assistant doesn't run as root +- /config is a volume +- /home/hass/.local/lib is a volume to make pip cache persistent between rebuilds +- Port 8123 is exposed by default +- Added a healthcheck +- If the env variable WAITDB_HOST is set Home Assistant doesn't start until the mysql database at that host with username WAITDB_USER and password WAITDB_PASS is available. + +Available on all networks. +Make sure to select all network adapters as active under Configuration->General->Network so `discovery` works. + +To update: + +```bash +> docker-compose build --pull hass +> docker-compose down hass +> docker-compose up -d hass +``` + +## db +Mariadb for Home Assistant use. + +## dbadmin +PHPMyAdmin container for tweaking the database if necessary. +Start when needed. + +## mqtt +Mosquitto server. + +Exposed to IOT network (which is also accessible from LAN thanks to its netmask). + +## influx +Storage for Home Assistant sensor data + +## grafana +For pretty plotting of data from `influx` + +## deconz +Deconz container + +`/dev/conbee` is created on the host by a udev rule in `/etc/udev/rules.d/10-conbee-local.rules`: +``` + ACTION="add", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", SYMLINK+="conbee" +``` + +VNC is activated with password `secret`. This is not exposed outside of the docker internal network, though (can be connected to with e.g. a guacamole container). + +A simple healthcheck is added for stability. + +## plejd +plejd2mqtt container + +https://github.com/thomasloven/plejd2mqtt - a standalone version of a Home Assistant add-on which connects plejd via bluetooth to MQTT. + +Access to the bluetooth bus is required through `/var/run/dbus`. + +## Mobile +pajikos/sms-gammu-gateway exposes GSM modem functionality via REST. + +`/dev/gsmmodem` is created on the host by a udev rule in `/etc/udev/rules.d/999-gsm-modem.rules`: +``` + ACTION=="add", ATTRS{idVendor}=="2001", ATTRS{idProduct}=="ac01", RUN+="/usr/sbin/usb_modeswitch -K -v 2001 -p ac01 -V 2001 -P 7e3d" + +SUBSYSTEM=="tty", ACTION=="add", ENV{ID_VENDOR_ID}=="2001", ENV{ID_MODEL_ID}=="7e3d", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="gsmmodem" +``` + +This requires [usb-modeswitch](https://packages.debian.org/bullseye/usb-modeswitch) to be installed on the host, and the exact commands vary greatly from device to device. The above works for my D-Link DWM222, but may or may not work with your D-Link DWM222. + +Health check added for stability \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 6b4546b..a7e2658 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -8,10 +8,31 @@ networks: lan: external: true +volumes: + hass: + driver: local + driver_opts: + o: bind + type: none + device: /data/files/hass + video: + driver: local + driver_opts: + o: bind + type: none + device: /data/video + music: + driver: local + driver_opts: + o: bind + type: none + device: /data/music + services: hass: container_name: hass - image: homeassistant/home-assistant + build: ./hass + image: thomasloven/home-assistant restart: always networks: default: @@ -19,20 +40,24 @@ services: iot: lan: volumes: - - /data/files/hass:/config + - hass:/config + - video:/media/video + - music:/media/music + - /etc/localtime:/etc/localtime:ro + environment: + WAITDB_HOST: db + WAITDB_USER: hass + WAITDB_PASS: hass depends_on: - db - working_dir: /config labels: traefik.enable: true traefik.docker.network: web - traefik.http.services.hass.loadbalancer.server.port: 8123 traefik.http.routers.hass.rule: HOST(`${HASS_ROOT}`) traefik.http.routers.hass.tls.certResolver: le - environment: - TZ: Europe/Stockholm + db: - image: mariadb + image: mariadb:10.4.12 restart: always volumes: - ./db:/var/lib/mysql @@ -44,9 +69,22 @@ services: MYSQL_PASSWORD: hass MYSQL_ROOT_PASSWORD: hass + dbadmin: + image: phpmyadmin + networks: + default: + web: + environment: + PMA_ARBITRARY: 1 + labels: + traefik.enable: true + traefik.docker.network: web + traefik.http.routers.dbadmin.rule: HOST(`db.${HASS_ROOT}`) + traefik.http.routers.dbadmin.middlewares: auth@file + traefik.http.routers.dbadmin.tls.certResolver: le mqtt: - image: eclipse-mosquitto:latest + image: eclipse-mosquitto:1.6 restart: always networks: default: @@ -55,6 +93,7 @@ services: volumes: - ./mqtt/data:/mosquitto/data - ./mqtt/log:/mosquitto/log + #- ./mqtt/config:/mosquitto/config grafana: image: grafana/grafana @@ -70,10 +109,11 @@ services: traefik.enable: true traefik.docker.network: web traefik.http.routers.grafana.rule: HOST(`grafana.${HASS_ROOT}`) + traefik.http.routers.grafana.middlewares: auth@file traefik.http.routers.grafana.tls.certResolver: le influx: - image: influxdb + image: influxdb:1.8 restart: always volumes: - ./influx:/var/lib/influxdb @@ -87,26 +127,52 @@ services: # Devices deconz: - image: marthoc/deconz:amd64 + image: marthoc/deconz:amd64-2.05.77 restart: always devices: - /dev/conbee:/dev/ttyUSB0 volumes: - ./deconz:/root/.local/share/dresden-elektronik/deCONZ + - ./deconz-otau:/root/otau - /etc/timezone:/etc/timezone networks: web: default: -# vnc: environment: DECONZ_VNC_MODE: 1 DECONZ_VNC_PASSWORD: secret + DEBUG_OTAU: 1 + DECONZ_WEB_PORT: 8081 + ports: + - 8081:8081 labels: traefik.enable: true traefik.docker.network: web - traefik.http.services.deconz.loadbalancer.server.port: 80 - traefik.http.routers.deconz.rule: HOST(`deconz.avagen.${PRIVATE_DOMAIN}`) + traefik.http.services.deconz.loadbalancer.server.port: 8081 + traefik.http.routers.deconz.rule: HOST(`deconz.${HASS_ROOT}`) + traefik.http.routers.deconz.middlewares: auth@file traefik.http.routers.deconz.tls.certResolver: le + healthcheck: + test: curl http://localhost:5900 || exit 1 # mysensors: # rflink: + + plejd: + image: thomasloven/plejd2mqtt + restart: always + environment: + PLEJD_SITE: + PLEJD_USERNAME: + PLEJD_PASSWORD: + MQTT_BROKER: mqtt://mqtt + volumes: + - /var/run/dbus:/var/run/dbus + + mobile: + image: pajikos/sms-gammu-gateway + restart: always + devices: + - /dev/gsmmodem:/dev/mobile + healthcheck: + test: ["CMD", "wget", "-O", "-", "localhost:5000/signal"] diff --git a/hass/Dockerfile b/hass/Dockerfile new file mode 100644 index 0000000..e876fbe --- /dev/null +++ b/hass/Dockerfile @@ -0,0 +1,41 @@ +ARG HASS_VERSION=latest + +FROM homeassistant/home-assistant:$HASS_VERSION + +RUN apk add --update-cache zbar mysql-client \ + && rm -rf /var/cache/apk/* + +# Check if root +# If running as root, remove setuid and setgid flags of everything +# Then add a hass user and make that the owner of /config +# Finally, make pip install to user folders +RUN python3 -c 'import os; assert os.geteuid() == 0, "Already non-root! Skip changing user"' \ + && find / -xdev -type f -perm /u+s -exec chmod -c u-s {} \; \ + && find / -xdev -type f -perm /g+s -exec chmod -c g-s {} \; \ + && adduser -D hass \ + && addgroup hass dialout \ + && chown hass /config \ + && mkdir -p ~hass/.config/pip \ + && chown hass ~hass/.config \ + && chown hass ~hass/.config/pip \ + && echo -e '[install]\nuser = yes' > ~hass/.config/pip/pip.conf \ + && mkdir -p ~hass/.local/lib \ + && chown hass ~hass/.local \ + && chown hass ~hass/.local/lib + +COPY wait-for-db.sh /home/hass/wait-for-db.sh +RUN chmod +x /home/hass/wait-for-db.sh + +USER hass + +# Make /config persistent even if not mounted +VOLUME /config +# Make pip cache persistent +VOLUME /home/hass/.local/lib +WORKDIR /config +# Export default port for use with routers like traefik +EXPOSE 8123/tcp + +CMD ["/home/hass/wait-for-db.sh", "python3", "-m", "homeassistant", "-v", "--config", "/config"] + +HEALTHCHECK CMD curl http://localhost:8123/ || exit 1 diff --git a/hass/wait-for-db.sh b/hass/wait-for-db.sh new file mode 100644 index 0000000..e5abf6c --- /dev/null +++ b/hass/wait-for-db.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# wait-for-db.sh + +set -e + +host=${WAITDB_HOST} +user=${WAITDB_USER} +pass=${WAITDB_PASS} + +echo "$host $user $pass" + +if [[ -v host ]]; then + until mysql --host="$host" --user="$user" --password="$pass" -e '\q'; do + >&2 echo "Database unavailable - sleeping" + sleep 1 + done + >&2 echo "Database available!" +fi + +>&2 echo "Executing command" +exec "$@" \ No newline at end of file