Home Assistant w/ docker-compose, on Linux

Everything on this page should be taken with a grain of salt, because I haven’t been using Home Assistant for that long. Trust nothing. But also if this helps you, then that’s cool too. Most of the things here have been stolen from other guides, and tweaked slightly by me. This guide is probably more for myself than for anyone else.

A friend of mine introduced me to this app, and honestly it’s the sickest thing I’ve touched in a bit. You’re basically limited only by your imagination and wallet (sadly). Anyway, I’m not here to sell you on it, but I will share my setup and a couple of my automations as examples. From here on, if you see the acronym “HA” just assume it means “Home Assistant”.

I would definitely not recommend this setup for beginners. Both because I’m not great at explaining things, and because it is mildly complex.

The majority of this information was stolen from here: https://iotechonline.com/home-assistant-install-with-docker-compose/ so basically, if this guide is too hard to read, try reading that guide which is most likely made by someone who knows what they’re doing.

The main differences between this and that one are that they have node-red and hass-configurator, and I’ve dropped those but added zwave-js-ui and zigbee2mqtt.

To start off, make a new folder for your HA data. (I put mine in ~/Documents/Docker/Home-Assistant). Now, in that folder you’re gonna create two files, called “docker-compose.yaml” and “.env”. Also you’re going to want to create a folder in that one called “hass-config“, and create a file name “configuration.yaml” in it.

Create this folder and file structure in the folder for your HA data as a non-root user:

folder structure

├── docker-compose.yaml       (file)
├── .env                      (file)
├── hass-config               (folder)
│   └── configuration.yaml    (file)
├── mariadb                   (folder)
├── mosquitto                 (folder)
│   ├── config                (folder)
│   │   └── mosquitto.conf    (file)
│   ├── data                  (folder)
│   └── log                   (folder)
└── zigbee2mqtt               (folder)
    └── configuration.yaml    (file)

Here’s my docker-compose.yaml:

docker-compose.yaml
version: '3'
services:
  homeassistant:
    container_name: hass
    image: homeassistant/home-assistant
    volumes:
      - ./hass-config:/config
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
    restart: unless-stopped
    network_mode: host
    depends_on:
      - mariadb
      - mosquitto

  mariadb:
    image: linuxserver/mariadb
    container_name: mariadb
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
      MYSQL_DATABASE: ha_db
      MYSQL_USER: homeassistant
      MYSQL_PASSWORD: "${HA_MYSQL_PASSWORD}"
      PUID: 1000
      PGID: 1000
    volumes:
      - ./mariadb:/config
    ports:
      - "3307:3306"

  zwave-js-ui:
    container_name: zwave-js-ui
    image: zwavejs/zwave-js-ui:latest
    restart: always
    stop_signal: SIGINT
    tty: true
    environment:
            - SESSION_SECRET=mysupersecretkey
            - ZWAVEJS_EXTERNAL_CONFIG=/usr/src/app/store/.config-db
            # Uncomment if you want log times and dates to match your timezone instead of UTC
            # Available at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
            #- TZ=America/New_York
    networks:
      - zwave
    devices:
      - "/dev/serial/by-id/usb-Silicon_Labs_Zooz_ZST10 700_Z-Wave_Stick_0001-if00-port0:/dev/zwave"
    volumes: 
      - ./zwavejs/store:/usr/src/app/store
    ports:
      - "8091:8091"
      - "3000:3000"

  zigbee2mqtt:
    container_name: zigbee2mqtt
    image: koenkk/zigbee2mqtt
    restart: unless-stopped
    volumes:
      - ./zigbee2mqtt:/app/data
      - /run/udev:/run/udev:ro
    ports:
      # Frontend port
      - 8087:8081
    environment:
      - TZ=America/Edmonton
    devices:
      - "/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_6ed36468addbed118cf1e72d62c613ac-if00-port0:/dev/ttyACM0"

  mosquitto:
    image: eclipse-mosquitto
    container_name: mosquitto
    restart: unless-stopped
    ports:
      - "1883:1883"
      - "9001:9001"
    volumes:
      - "./mosquitto:/mosquitto"
    environment:
      - TZ=America/Edmonton
    user: "${PUID}:${PGID}"
    command: "mosquitto -c /mosquitto-no-auth.conf"

  portainer:
    ports:
      - "9000:9000"
    container_name: portainer
    restart: unless-stopped
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "./portainer/portainer_data:/data"
    image: "portainer/portainer-ce"

  esphome:
    container_name: esphome
    image: esphome/esphome
    volumes:
      - ./esphome:/config
      - /etc/localtime:/etc/localtime:ro
    restart: always
    privileged: true
    network_mode: host

networks:
    zwave:
volumes:
    zwave-config:
        name: zwave-config

You’ll probably have to change the “devices” sections in both “zigbee2mqtt” and “zwave-js-ui” to reflect whatever dongles you have, unless you happen to have the exact same ones I do.

Here’s my .env file:

.env
MYSQL_ROOT_PASSWORD=NotMyRealPassword2
HA_MYSQL_PASSWORD=NotMyRealPassword3
PUID=1000
PGID=1000

Here’s my hass-config/configuration.yaml:

configuration.yaml
# Loads default set of integrations. Do not remove.
default_config:

mqtt:
powercalc:  
recorder:
  db_url: mysql://homeassistant:[email protected]:3307/ha_db?charset=utf8mb4
  purge_keep_days: 60

# Load frontend themes from the themes folder
frontend:
  themes: !include_dir_merge_named themes
  extra_module_url:
    - /local/community/custom-brand-icons/custom

automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

Here’s my “mosquitto/config/mosquitto.conf“:

mosquitto.conf
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
listener 1883
allow_anonymous true

Here’s my “zigbee2mqtt/configuration.yaml“:

configuration.yaml
homeassistant: true
permit_join: false
mqtt:
  base_topic: zigbee2mqtt
  server: mqtt://mosquitto
frontend:
  port: 8081
serial:
  port: /dev/ttyACM0

(I’ve replaced my passwords with placeholders, if it wasn’t obvious)

For an explanation of what the different things do:

  • the homeassistant block is the only one that’s actually required, afaik.
  • the mariadb block is for using a better database system with homeassistant, to improve performance.
  • the zwave-js-ui block is used with a zwave dongle to use zwave devices with HA, and the ui is accessible at port 8091.
  • the zigbee2mqtt block is used with a dongle to make it so you can use zigbee devices with HA, and the ui is accessible on port 8087.
  • the mosquitto block is needed for zigbee2mqtt.
  • the portainer block adds portainer on port 9000, it’s a useful tool for managing your docker stuff.
  • the esphome block adds the esphome service, which is fantastic. More about this later.

To set this up using docker-compose, you’d just download both files, move them to a folder, make any changes you want (probably should change the passwords in the .env file) and run “sudo docker-compose up -d".

(Myself, I’d run “sudo docker-compose up” and look at the logs for errors, before you run it in the background using “sudo docker-compose up -d“.)

After that, go to http://<your server’s ip address>:8123 and it should ask you to make an admin account. Congrats! You should be in.

Later on, to update the docker images, navigate to the folder you have the files in. Then, run these four commands:

sudo docker-compose down   # Stop HA
sudo docker-compose pull   # Download new images of HA
sudo docker-compose up -d  # Start HA again (using new images)
sudo docker image prune -f # Remove old images to free up space

Anyway, that’s the basic installation stuff. Here’s some more info on a couple of things:

portainer

This is a pretty nice web ui for looking at your docker containers/stacks. I enjoy it. Homepage can be found here: https://www.portainer.io/

esphome

This may not be as useful to some people, but basically there is a custom firmware for esp-based wifi smart devices, and this addon makes deploying your custom firmware to them easier, and lets you change their configuration using a nice web ui. I have a few cheap smart plugs and this makes it so much nicer to deal with them. The project’s homepage is here: https://esphome.io/

Examples of useful automations

My hardware

Currently, my server is a ROG GL552VW with an intel i5-6300hq, 4 core 2.3ghz base/3.2ghz boost. It works well for me.

As far as smart devices go, I have six Globe Electric 50347 smart plugs, because they were on sale at Canadian Tire for $6.93 each, and can be flashed with esphome with relative ease using tuya-cloudcutter.

Here’s an example esphome config for one of them:

misc.yaml
substitutions:
  number: "1"
  device_name: Misc${number}
  device_description: Globe Electric 50347 Smart plug
  friendly_name: Misc Plug ${number}

esphome:
  name: misc

bk72xx:
  board: wb2s

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "<generate a key here https://esphome.io/components/api.html>"

web_server:
  port: 80
  auth:
    username: admin
    password: "NotMyPassword5"

ota:
  - platform: esphome
    password: "NotMyPassword4"
    
wifi:
  ssid: "NachoBusiness"
  password: "12345678"
  reboot_timeout: "3min"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  #ap:
  #  ssid: "Misc${number} Fallback Hotspot"
  #  password: "NotMyPassword6"

text_sensor:
  - platform: wifi_info
    ip_address:
      name: ESP IP Address
    ssid:
      name: ESP Connected SSID
    bssid:
      name: ESP Connected BSSID
    mac_address:
      name: ESP Mac Wifi Address
      
sensor:

  - platform: wifi_signal
    name: ${friendly_name} Signal
    update_interval: 60s

  - platform: hlw8012
    model: BL0937     # note that the model must be specified to use special calculation parameters
    sel_pin:          # I believe that cf_pin reports either Voltage or Current depending on this select pin
      inverted: true  # determine whether true reports Voltage
      number: P24
    cf_pin:           # current or voltage (ele_pin: 7)
      inverted: true  # the logic of BL0937 is opposite from HLW8012
      number: P7
    cf1_pin:          #  Power (vi_pin: 8)
      inverted: true  # the logic of BL0937 is opposite from HLW8012
      number: P8

    update_interval: 15s      # How often to measure and report values
    
    # PC191HA measures and returns Voltage OR Current according to the value of sel_pin,
    #   but it can change the value of sel_pin periodically  
    initial_mode: "VOLTAGE"       # reports VOLTAGE or CURRENT
    change_mode_every: 4          # how many times to report before swapping between
        #   reporting Voltage or Current. Note that the first value reported should be ignored as inaccurate

    # Adjust according to the actual resistor values on board to calibrate the specific unit
    voltage_divider:  872.357   # LOWER VALUE GIVES LOWER VOLTAGE - Was 775 -wb
    current_resistor: 0.0009  # HIGHER VALUE GIVES LOWER WATTAGE

    #
    # how the power monitoring values are returned to ESPHome
    #

    voltage:
      name: ${device_name} Voltage
      id:   ${device_name}_voltage
      unit_of_measurement: V
      accuracy_decimals: 1
      filters:
        - skip_initial: 2
    power:
      name: ${device_name} Power
      id:   ${device_name}_power
      unit_of_measurement: W
      accuracy_decimals: 2
      filters:
        - skip_initial: 2

    # power should simply be current x voltage -- except that the pc191ha doesn't follow that formula.  
    # Setting current_resistor to give an accurate Amperage does NOT also give the correct Wattage
    # so here I calculate current from power and voltage
    
  - platform: template  
    name: ${device_name} Current
    id:   ${device_name}_current
    unit_of_measurement: A
    accuracy_decimals: 2
    update_interval: "20s"
    lambda: |-
      return (id(${device_name}_power).state / id(${device_name}_voltage).state);
    filters:  
      - skip_initial: 2

  - platform: uptime
    name: ${device_name} Uptime
    id:   ${device_name}_uptime
    update_interval: "30s"

light:
  - platform: status_led
    name: "led"
    internal: true
    id: led
    pin:
      number: P6
      inverted: true

binary_sensor:
  - platform: gpio
    pin:
      number: P10
      inverted: true
    id: button1
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms
    on_click:
      - switch.toggle: outlet

  - platform: status
    name: ${friendly_name} status

switch:
  - platform: gpio
    name: ${friendly_name} Outlet
    id: outlet
    pin: P26
    icon: mdi:power-socket-us
    on_turn_on:
      - light.turn_on: led
    on_turn_off:
      - light.turn_off: led

captive_portal:

I’ll eventually upload some version of this to http://devices.esphome.io, but I want to calibrate the voltage and power on them first, which I haven’t done yet. The basic config was taken from a couple of smart plugs, namely these two:

https://devices.esphome.io/devices/Globe-Electric-Wi-Fi-Smart-Plug-50329

https://devices.esphome.io/devices/Arlec-PC191HA-Plug

If you’re wanting to use this config, you’ll have to replace the api key and all the passwords, and the wifi information.

Besides all of those, I also have two Aqara USC-01 wall switches, and an Aqara T2 Relay (up above a fan with a light). You can use the Aqara wall switches in decoupled relay mode to provide constant power to the relay, and set the button presses to do things.

For instance, one press will toggle the first half of the relay, toggling my lights, and a double press will toggle the second half, toggling my fan. I’m pretty happy with it except for the delay.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *