I have an RFXcom device which allows me to send and receive signals on 433MHz to interact with for example KlikAanKlikUit devices. I’m running Home Assistant in a Docker Swarm, but that has the limitation that you can’t attach devices to the container. At least, it’s not documented…

The following allows you to add a device to any Docker container (swarm or not). The example is geared towards those deploying Docker Swarm services using a GitLab runner, but can be adjusted to any environment.

First we need the name of your device on your host. Use one that doesn’t change between reboots, for example:

  variables:
    DEVICE: "/dev/serial/by-id/usb-RFXCOM_RFXtrx433_A12BCDE-if00-port0"

Then we need its assigned major and minor identifiers. These may vary per device and change after a reboot, so we’ll retrieve them dynamically.

  script:
    - MAJOR=$(docker run --rm --device=${DEVICE}:${DEVICE} registry.gitlab.com/gereedschap/udevadm udevadm info -q property -n ${DEVICE} | grep MAJOR | awk -F= '{print $2}')
    - MINOR=$(docker run --rm --device=${DEVICE}:${DEVICE} registry.gitlab.com/gereedschap/udevadm udevadm info -q property -n ${DEVICE} | grep MINOR | awk -F= '{print $2}')

The container we use for this needs udevadm installed. You can do that for each run, or setup a image somewhere which has it installed from the udev package. You can also use the image I’ve created in the example above.

Next we need the new container in which we are going to make the device available. It is quite convoluted to get that going with Docker Swarm.

    - "OLD_CONTAINER=${CI_PROJECT_NAME}_homeassistant.1.$(docker service ps -f name=${CI_PROJECT_NAME}_homeassistant.1 ${CI_PROJECT_NAME}_homeassistant -q --no-trunc | head -n1)"
    - docker stack deploy -c docker-compose.yml --prune ${CI_PROJECT_NAME}
    - |
      printf "Waiting for old container to be replaced"
      while :; do
        printf "."
        CONTAINER=${CI_PROJECT_NAME}_homeassistant.1.$(docker service ps -f name=${CI_PROJECT_NAME}_homeassistant.1 ${CI_PROJECT_NAME}_homeassistant -q --no-trunc | head -n1)
        if [ "${CONTAINER}" != "${OLD_CONTAINER}" ]; then
          CONTAINER_ID=$(docker ps --no-trunc -aqf "name=${CONTAINER}")
          if [ ! -z "$CONTAINER_ID" ]; then
            if docker ps -q --no-trunc | grep ${CONTAINER_ID}; then
              echo
              break
            fi
          fi
        fi
        sleep 1
      done      

You can replace the above with the following to assume it will have replaced the container within a minute. This is because the above may fail at times.

    - docker stack deploy -c docker-compose.yml --prune ${CI_PROJECT_NAME}
    - sleep 60  # Sleep for 1 minute and assume our new container will be up

Finally we allow the device to be used by the new home assistant container, and create the device node in there as well.

    - docker run --rm --privileged -v /sys/fs/cgroup/devices/docker/${CONTAINER_ID}:/c_dev alpine sh -c "echo c ${MAJOR}:${MINOR} rwm > /c_dev/devices.allow"
    - docker exec ${CONTAINER} mknod /dev/rfxcom c ${MAJOR} ${MINOR}

Combining the above code blocks into a .gitlab-ci.yml job will have your device available for action in your container.

The above doesn’t take device detachments into account. So if you detach and re-attach the device, you probably need to run this job again.