tl;dr; The symlinks to /dev/ttyUSBx devices mapped via compose.yaml to static USB ports in the docker container do not work after a reboot of the RasPi if the symlinks changed during that reboot and if the container image has not been newly composed after the symlinks changed.
Hi all,
I have a bit of a tricky issue and hope for the wisdom of the crowd to help me! I use three USB devices in my HA installation on a RasPi4 running on a docker container. One for Skyconnect (Zigbee), one to connect to my LG Chem batteries and my Studer Xtenders inverters via an XCom485i and then a RS485 interface to read values from some meters connected to a RS485 modus.
In the HA configuration, the three USB devices always need to appear on the same USB ports in the docker container since they are written into the configs.
Therefore I created “symlinks” via UDEV rules so the USB ports on the host device can be accessed via these static symlinks. here are my /etc/udev/rules.d/10-usb-serial.rules:
SUBSYSTEM=="tty", ATTRS{idProduct}=="ea60", ATTRS{idVendor}=="10c4", ATTRS{serial}=="ae94a629d960ed119dcdcde05720eef3", SYMLINK+="ttyUSB_SkyConnect"
SUBSYSTEM=="tty", ATTRS{idProduct}=="6001", ATTRS{idVendor}=="0403", ATTRS{serial}=="AQ00K6WL", SYMLINK+="ttyUSB_RS485"
SUBSYSTEM=="tty", ATTRS{idProduct}=="6001", ATTRS{idVendor}=="0403", ATTRS{serial}=="AB0KTQ80", SYMLINK+="ttyUSB_XCom485i"
The symlinks created after reboot are always correct (but do change from time to time):
0 crw-rw-rw- 1 root dialout 188, 0 Aug 25 20:39 /dev/ttyUSB0
0 crw-rw-rw- 1 root dialout 188, 1 Aug 25 20:39 /dev/ttyUSB1
0 crw-rw-rw- 1 root dialout 188, 2 Aug 25 13:53 /dev/ttyUSB2
0 lrwxrwxrwx 1 root root 7 Aug 25 13:53 /dev/ttyUSB_RS485 -> ttyUSB1
0 lrwxrwxrwx 1 root root 7 Aug 25 13:53 /dev/ttyUSB_SkyConnect -> ttyUSB2
0 lrwxrwxrwx 1 root root 7 Aug 25 13:53 /dev/ttyUSB_XCom485i -> ttyUSB0
Also the serial by-ID links are always created correctly after reboot (and again change from time to time):
0 lrwxrwxrwx 1 root root 13 Aug 25 13:53 /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0KTQ80-if00-port0 -> ../../ttyUSB0
0 lrwxrwxrwx 1 root root 13 Aug 25 13:53 /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AQ00K6WL-if00-port0 -> ../../ttyUSB1
0 lrwxrwxrwx 1 root root 13 Aug 25 13:53 /dev/serial/by-id/usb-Nabu_Casa_SkyConnect_v1.0_ae94a629d960ed119dcdcde05720eef3-if00-port0 -> ../../ttyUSB2
Here is where the tricky issue starts. In my compose.yaml file the devices section looks like this:
devices:
- "/dev/ttyUSB_XCom485i:/dev/ttyUSB0"
- "/dev/ttyUSB_RS485:/dev/ttyUSB1"
- "/dev/ttyUSB_SkyConnect:/dev/ttyUSB2"
so I basically use the symlinks to map them to the static ports in the container so that I always access the devices via the right ports. So far so good, and everything actually works fine in the HA docker container if and only if the mapping of the USB ports is exactly like it was when I last run: docker compose up -d
, i.e.:
/dev/ttyUSB_RS485 -> ttyUSB1
/dev/ttyUSB_SkyConnect -> ttyUSB2
/dev/ttyUSB_XCom485i -> ttyUSB0
However, if I reboot and the configuration ends up being e.g.:
/dev/ttyUSB_RS485 -> ttyUSB0
/dev/ttyUSB_SkyConnect -> ttyUSB1
/dev/ttyUSB_XCom485i -> ttyUSB2
then on the host system everything still works fine, i.e. if I access /dev/ttyUSB_XCom485i it is the correct XCom485i device that I expect, likewise if I access /dev/ttyUSB_RS485 it is the RS485 device that I expect.
However, within the HA docker container unfortunately, the ports seemed to be stuck with the configuration that was active during the last docker compose up -d
, i.e. if I want to access the XCom485i in the container, it is still mapped to /dev/ttyUSB0 on the host system (which was the symlink for XCom485i during the last docker compose up -d
) but that obviously does not work anymore since the symlink now points to /dev/ttyUSB2.
If I access the XCom485i on /dev/ttyUSB2 within the container, it does work, but that doesn’t help me since the configurations for the three devices are hardcoded in Home Assistant, i.e. in a modbus.yaml file, or via the Skyconnect configuration or in a pyscript for the XCom485i. But then the mapping in the compose.yaml file via the symlinks does not seem to do the trick that I really need? I.e. it does not actually newly map the ports from the symlinks to USB0, USB1 and USB2 ports as specified in the compose.yaml every time I restart the raspi only if I recreate the container.
Now I saw that @AseKarlsson and @mwav3 had a similar/related discussion almost 2 years ago here, but my particular issue described above wasn’t actually covered. So do you have any ideas or suggestions? Could it be that I would need to newly compose the HA docker container at every reboot to get the correct USB port mappings specified in the compose.yaml file after the current symlinks for the USB ports are created? If so how would I best do that?
Or would this be something that could be handled by the restart:
policy in compose.yaml? I currently have restart: unless-stopped
.
Any help would be greatly appreciated. It took my quite a while to get to this point to understand and verufy the root cause but I am stuck a bit, unless it is really that the containers will need to be recreated at every reboot? Is there any disadvantage to doing this? Obviously I would need to be careful with the image versions specified in compose.yaml and avoid ‘image: ghcr.io/home-assistant/home-assistant:latest’ so that it not automatically updates the HA version before I had a chance to test it.
Any help, idea, link, tip would be greatly appreciated. Thanks much!