Accessing custom I2C devices from Python

Hi,

[I originally posted this in Hardware because it has to do with I2C but after working on it pretty hard I’ve come to realization that this is probably a docker issue, so I’m reposting here]

I have a custom PCB with several I2C devices (ADS7830 a/d converters, TCA8534 IO expanders, etc.) that I’d like to integrate into my system. The hardware is known working (I’m converting from OpenHAB to HA after the whole Insteon debacle). I’m running on a Pi 3B+ with Home Assistant OS 8.0.

I followed the instructions here for enabling I2C and it appears to be working:

➜  ~ ll /dev/i2c*
crw-------    1 root     root       89,   0 Mar 11 02:33 /dev/i2c-0
crw-------    1 root     root       89,   1 Mar 11 02:33 /dev/i2c-1
crw-------    1 root     root       89,  10 Mar 11 02:33 /dev/i2c-10
crw-------    1 root     root       89,  11 Mar 11 02:33 /dev/i2c-11
➜  ~ lsmod | grep i2c
i2c_dev                20480  0
i2c_mux_pinctrl        16384  0
i2c_mux                16384  1 i2c_mux_pinctrl
i2c_bcm2835            16384  0

My plan is to write something in AppDaemon that periodically scans the I2C devices and connects the I2C endpoints to HA objects either directly or through MQTT (is this the best approach?). Currently though I’m having trouble accessing the I2C bus from within HA using Python. For example, when I try from the Python console I get the following:

➜ python3
Python 3.9.7 (default, Nov 24 2021, 21:15:59)
[GCC 10.3.1 20211027] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from smbus2 import SMBus
>>> bus = SMBus(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.9/site-packages/smbus2/smbus2.py", line 280, in __init__
    self.open(bus)
  File "/usr/lib/python3.9/site-packages/smbus2/smbus2.py", line 310, in open
    self.fd = os.open(filepath, os.O_RDWR)
PermissionError: [Errno 1] Operation not permitted: '/dev/i2c-1'
>>>

I get a similar error from within AppDaemon.

I thought that maybe HA had the I2C bus open but wouldn’t that increase the reference count on the i2c_dev module? Is this a protection mode issue?

Any help would be greatly appreciated.

Update 1:

  • Turning off Protection mode on the terminal has no effect
  • Tried it on a Pi4 with the same results
  • On Raspian I tried having two simultaneous open i2c devices and that worked OK so it’s not a problem of HA already having the device open.

Update 2:
I’m fairly convinced that the host device is not being added to the container resulting in the Permission Error. I’m guessing that this is supposed to happen as a result of putting the rpi-i2c.conf file in CONFIG/modules on the boot partition. I believe that I did this correctly. Is there any way to check that this is the issue? Why didn’t it work?

1 Like

I finally figured out how to make this work. The solution was to create a custom add-on. Creating a custom add-on is easy, you can get one up and running by adding just four files in your add-ons folder. The new add-on runs in its own docker container and by configuring this container correctly you can get the required permission to access the I2C device. Here’s an overview of what I did.

  1. Install HA on an uSD card.
  2. Follow instructions here for enabling the i2c device. Be sure to reboot twice. After doing this you should see /dev/i2c-1 in the file system.
  3. Follow instructions here for creating the add-on but use the file contents shown below to enable the i2c device in the add-on container.
  4. Install the add-on as described and then build and start it. In the log you should see “I2C test starting…” followed by “it worked!”.
  5. If you get this far you have a working i2c bus and the full power of Python (with imports) available to you. Be sure to modify the Dockerfile for each import you use.
  6. After making changes to the python script click Rebuild. HA doesn’t always immediately notice changes to config.yaml. To force it, uninstall the add-on, then do ‘Check for updates’ in the add-on store and re-install. Change the version number so that the update is more obvious.
File #1: Dockerfile
ARG BUILD_FROM
FROM $BUILD_FROM
RUN apk add --no-cache python3 py3-pip
RUN pip3 install smbus2
COPY test.py /
COPY run.sh /
RUN chmod a+x /run.sh
CMD [ "/run.sh" ]
File #2: test.py
import smbus2
bus = smbus2.SMBus(1)
print('it worked!')
File #3: config.yaml
name: "I2C Test"
description: "Opens the I2C device"
version: "1.0.0"
slug: "i2c_test"
init: false
arch:
  - aarch64
  - amd64
  - armhf
  - armv7
  - i386
log_level: info
startup: services
privileged:
  - SYS_ADMIN
devices:
  - /dev/i2c-1
device_tree: true
File #4: run.sh
#!/usr/bin/with-contenv bashio

echo "I2C test starting..."
python3 test.py
2 Likes

@412jim - many thanks for this.
I was running around in circles trying to find something like this.
Super helpful post

s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service legacy-services: starting
s6-rc: info: service legacy-services successfully started
I2C test starting...
it worked!
s6-rc: info: service legacy-services: stopping
s6-rc: info: service legacy-services successfully stopped
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped```

Thank you for the very helpful post! I thought that I had found the solution access the i2c bus on my pi4 running HA OS, sadly not…

Have enabled i2c and is visible, following instructions above I get below error when attempting to install the addon

ERROR (SyncWorker_0) [supervisor.docker.addon] Invalid build environment, can’t build this add-on!

I have also installed i2c-tools (apk get i2c-tools) but i2cget and i2cset both come back with Operation not permitted

Can i2c devices actually be used when running HA OS?

Hi, I recently installed HA OS on a Raspberry pi CM4 IO board. It has a PWM controller EMC2301 on board for a little fan. I enabled I2C following this guide. Is there any way to control the FAN from home assistant (2022.12)? Thanks

1 Like

Same problem here, I hope someone can help

hi, anyone know what this means when I try to install the addon?

The command '/bin/ash -o pipefail -c apk add --no-cache python3 py3-pip' returned a non-zero code: 2

It works well when I use the HA Docs example.

May 2024 update:

HA no longer allows python packages that aren’t apks to be loaded in a Docker container, so the old method of installing smbus2 no longer works. The new way to do this is to create a python virtual environment and load the smbus2 package into that. This only requires minor changes to Dockerfile and run.sh.

The Dockerfile actually gets a little simpler:

ARG BUILD_FROM
FROM $BUILD_FROM

RUN apk add --no-cache python3  

# Copy data for add-on
COPY alarm.py /
COPY run.sh /

# Start the script
RUN chmod a+x /run.sh

CMD [ "/run.sh" ]

The run.sh file creates the virtual python environment, loads all of the packages, and runs the addon, in this case alarm.py

#!/usr/bin/with-contenv bashio

echo "Create virtual environment"
mkdir /virtual_env
python3 -m venv /virtual_env
source /virtual_env/bin/activate

echo "Install packages"
python3 -m pip install smbus2
python3 -m pip install paho-mqtt

echo "Start scanner"
python3 alarm.py
1 Like

Hi, could you clarify the content of the run.sh and the alarm.py?


OK, nevermind, I’ve figured out that it’s actually a content of the test.py