Category: How-to

  • 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.

  • My wordpress setup

    I currently have wordpress running in docker, which I’ve been told is self-updating (dubious, but maybe). And then am using nginx to reverse proxy it.

    This is my docker-compose.yaml for anyone interested:

    services:
    
      wordpress:
        image: wordpress
        restart: always
        ports:
          - 8083:80
        environment:
          WORDPRESS_DB_HOST: db
          WORDPRESS_DB_USER: wordie
          WORDPRESS_DB_PASSWORD: NotMyRealPassword
          WORDPRESS_DB_NAME: wordpress
        volumes:
          - wordpress:/var/www/html
    
      db:
        image: mysql:8.0
        restart: always
        environment:
          MYSQL_DATABASE: wordpress
          MYSQL_USER: wordie
          MYSQL_PASSWORD: NotMyRealPassword
          MYSQL_RANDOM_ROOT_PASSWORD: '1'
        volumes:
          - db:/var/lib/mysql
    
    volumes:
      wordpress:
      db:

    And here is the relevant part of my nginx configuration:

    server {
        if ($host = localhost:8080/) {
    	return 301 https://$host$request_uri;
        }
    
        server_name localhost:8080/;
        listen 80;
        listen [::]:80;
        return 404;
    }
    server {
        server_name localhost:8080/;
        listen 443 ssl;
        listen [::]:443 ssl;
    
        ssl_certificate /etc/letsencrypt/livelocalhost:8080/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/livelocalhost:8080/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
        location / {
    	proxy_set_header Host $host;
    	proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Proto $scheme;
    	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:8083;
    	proxy_redirect off;
        }
    }

    Truth be told, some of those headers might not be needed. I just copied them from some other subdomains.

    The more important thing to do is change your wordpress settings in Settings>General to point to the right domain. I wasted a few minutes figuring that out.

    Anyway, good luck and have fun!