Fan Speed Mirror plugin fails on OctoPi 1.1.0 (bookworm), works on OctoPi 0.18.0 (buster) and OctoPi 1.0.0 (bullseye)

What is the problem?

The Fan Speed Mirror plugin monitors the .gcode for M106 and M107 commands. For each command, a script is executed via:

self._logger.debug("Executing (" + cmd_line + ")")
try:
    r = subprocess.call([self.M106command, str(fanPwm)])
    if r < 0:
        self._logger.error("Error executing command %s: %s" % (cmd_line, r))
except OSError as e:
    self._logger.exception("Exception executing command %s: %s" (cmd_line, e))

The hardware I am using, Adafruit DC and Stepper Motor HAT for Raspberry Pi now requires that the libraries be installed in a venv.

When the command passed to the subprocess.call attempts to use the venv, it fails on OctoPi 1.1.0 (bookworm) but succeeds on OctoPi 1.0.0 (bullseye). The failure is that the command is not executed but there is no error returned.

The command works when executed from the (SSH) command line (see below).

What did you already try to solve it?

With significant assistance from @jneilliii, numerous options were tried including changes to the plugin code.

After many iterations, I was able to create test scripts that demonstrate the problem without any hardware or physical printer (i.e. it fails using the virtual printer).

Have you tried running in safe mode?

The plugin is required to initiate the problem so safe mode is not an option.

Did running in safe mode solve the problem?

See above.

Systeminfo Bundle

octoprint-systeminfo-20250618075910.zip (351.2 KB)

Additional information about your setup

OctoPrint 1.11.2, OctoPi 1.1.0 (bookworm), RPi 3B, Python 3.11.2, printer: LulzBot TAZ 6, FIRMWARE_NAME:Marlin FIRMWARE_VERSION:2.0.9.0.13, browser: Chrome 137.0.7151.104 (Official Build) (64-bit), operating system: Windows 11

The bullseye testing was done with:
OctoPrint 1.11.2, OctoPi 1.0.0 (bullseye), RPi 4B, Python 3.9.2, printer: Virtual, firmware: N/A, browser: Chrome, operating system: Windows 11

The files necessary to reproduce this issue are included in the .zip file below.

The assumption is that the files are in /home/pi. After extracting, execute chmod +x cp*.py. The *2.py files require the hardware and the venv built with the libraries so they are just included for reference.

The *4.py files use the system Python and the *5.py files use a venv built with python3 -m venv stock (the difference is in the #! first line).

Logging for the Fan Speed Mirror plugin is set to DEBUG. If successful, the scripts will output the time, the fan value received, and the venv used. The time can be used to correlate with the debug output in octoprint.log.

Note that these scripts can be executed from an SSH command line. For example:
./cpfan4.py 254 or cpfan5.py 255.

fanspeedmirror.zip (3.2 KB)

I would have gone straight to a github issue but since it can't be reproduced in safe mode, I've entered it here.

Only proceed here if you are 100% sure the issue you are seeing is actually a bug in OctoPrint that also happens in safe mode. If in doubt, check in on the forums first, we can always file a bug from a support request later.

One thing of note, the versions of python are different between the two OctoPi images. Wondering if that is why you are seeing this behavior in OctoPi 1.1.0 vs 1.0.0.

I agree that I can't tell if this is an OS issue, a Python issue, or an OctoPrint issue.

I'm not sure how to isolate the OS version from the Python version. Suggestions welcome.

I can test that fairly easy in windows on my dev machine, I have multiple python versions installed for plugin testing.

Great (and not so great) minds think alike... I'm messing with WSL on Windows 11...

I can test that fairly easy in windows on my dev machine, I have multiple python versions installed for plugin testing.

actually that won't work because of the shebang stuff.

I was able to get the cpfan4.py and cpfan5.py files to execute on WSL (using the shebang stuff):

Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 5.15.167.4-microsoft-standard-WSL2 x86_64)
brad@brad-morgan8:~$ python3 --version
Python 3.12.3
brad@brad-morgan8:/home/pi$ cat cpfan*.log
2025-06-18 10:02:44 set speed to 254
Not running in a virtual environment.
2025-06-18 10:04:45 set speed to 255
Currently running in a virtual environment.
Virtual environment path: /home/pi/stock

Now I just need to create a Python file that replicates the code in the plugin...

Here's the test.py program that replicates the (failing piece of) plugin code:

#!/usr/bin/python3
import datetime
import time
import sys
import subprocess

# get the command to execute and the speed from 0 (off) to 255 (max speed)
if len(sys.argv) >= 3:
	cmd_line = sys.argv[1]
	s = int(float(sys.argv[2])+0.5)
else:
	print("Need some data to get going")
	exit()

s = min(s,255)
t = float(s)/255.0
print("Executing (" + cmd_line + ")")
try:
	r = subprocess.call([cmd_line, str(s)])
	if r != 0:
		print("Error executing command %s: %s" % (cmd_line, r))
except OSError as e:
	print("Exception executing command %s: %s" % (cmd_line, e))

now = datetime.datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S") + " set speed to " + str(s))

It works on Python 3.12.3 on WSL and Python 3.11.2 on OctoPi 1.1.0.

Issue seems to be related to bookworm and calling GPIO related stuff used by the Adafruit Blinka library from an application started from service file.

based on a hint in this thread, adding Environment="LG_WD=/tmp" to the service file /etc/systemd/system/octoprint.service resolved the issue I think.

@jneilliii mentioned the "final nail".

I changed the plugin __init__.py from subprocess.call to subprocess.Popen which got us further along the path to the solution. I'll be updating the plugin github soon.