Skip to content

Convert pi-eyes.sh to Python#402

Merged
makermelissa merged 2 commits into
adafruit:mainfrom
makermelissa-piclaw:convert-pi-eyes
Jun 16, 2026
Merged

Convert pi-eyes.sh to Python#402
makermelissa merged 2 commits into
adafruit:mainfrom
makermelissa-piclaw:convert-pi-eyes

Conversation

@makermelissa-piclaw

Copy link
Copy Markdown
Contributor

Converts pi-eyes.sh to pi-eyes.py.

Unlike some of the older scripts in this repo, pi-eyes.sh was recently rewritten for Trixie (Pi 3B / 4 / 5), so this is a faithful 1:1 port rather than a modernization — the bash already reflects current intent (venv-based pip installs, systemd autostart, X11 MIT-SHM fbx2, rpi-lgpio on Pi 5).

What it does (unchanged from the .sh)

  • Screen-type menu (OLED/TFT/IPS/HDMI-only), plus optional GPIO-halt, Bonnet ADC, and USB-Ethernet-gadget prompts.
  • Installs system packages; rpi-lgpio on Pi 5 (RP1 controller), RPi.GPIO otherwise.
  • Dedicated venv at /opt/pi-eyes-venv (--system-site-packages) with numpy / pi3d / svg.path / blinka / ads1x15.
  • Downloads Pi_Eyes to /opt/Pi_Eyes, uses a local fbx2.c if present next to the script (else the downloaded one), compiles fbx2.
  • Writes /etc/X11/xorg.conf, edits config.txt (gpu_mem, hdmi_*), adds video=HDMI-A-1:... to cmdline.txt, enables SPI/I2C as needed.
  • Installs pi-eyes, pi-eyes-fbx2, and gpio-halt systemd units (rc.local is deprecated on Trixie).

Implementation notes

  • adafruit_shell idioms: select_n, prompt, get_boot_config(), reconfig, write_text_file, run_raspi_config, prompt_reboot.
  • Halt-pin entry uses the same validated input() loop as joy-bonnet.py / arcade-bonnet.py.
  • A small append_to_line helper handles the single-line cmdline.txt token edits (the reconfig2 behavior from the .sh).

Testing

Hardware-tested on a Raspberry Pi 5 (Trixie, Desktop) — full install with the HDMI-only screen option (exercises the maximum logic path that doesn't require the SPI bonnet/displays):

  • Install ran EXIT=0. venv + pip installs succeeded; fbx2 compiled to a valid ELF binary; Pi_Eyes downloaded.
  • Verified: /etc/pi-eyes/env (RADIUS=240), pi-eyes.service ExecStart uses cyclops.py for HDMI-only, pi-eyes-fbx2.service correctly absent for HDMI-only, xorg.conf 640x480 modeline, config.txt gpu_mem/hdmi_* lines, cmdline.txt video=HDMI-A-1:640x480@60e, default target → multi-user, service enabled.
  • Rig fully reverted to baseline afterward (config.txt md5 restored, /opt cleaned, units removed, default target restored).

The SPI-screen branches (fbx2 service, spi1 overlays) mirror the validated HDMI path and require the physical Snake Eyes Bonnet + displays to fully exercise end-to-end.

ruff format + ruff check clean.

Faithful port of the recently-rewritten Snake Eyes Bonnet installer
(Trixie, Pi 3B/4/5). Uses adafruit_shell idioms: select_n for the
screen menu, prompt for the feature questions, get_boot_config() for
the boot config path, reconfig for config.txt edits, and write_text_file
for the xorg.conf and systemd unit files.

Preserves the existing behavior: dedicated venv at /opt/pi-eyes-venv,
rpi-lgpio on Pi 5, local fbx2.c override if present (else the one from
the downloaded Pi_Eyes), fbx2 compile, optional gpio-halt / ADC / USB
gadget, and the pi-eyes / pi-eyes-fbx2 / gpio-halt systemd units.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a Python implementation of the existing pi-eyes.sh installer, aiming to keep the current Trixie-era behavior intact while moving the installer logic into a single pi-eyes.py script.

Changes:

  • Adds pi-eyes.py, a Python port of the Snake Eyes Bonnet installer (menus, package install, venv setup, Pi_Eyes download/build, boot config edits, and systemd units).
  • Implements cmdline token editing via a small append_to_line() helper.
  • Sets up systemd autostart units (pi-eyes, optional pi-eyes-fbx2, optional gpio-halt) and writes /etc/X11/xorg.conf.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pi-eyes.py Outdated
Comment on lines +59 to +62
pi_model = ""
if os.path.exists("/proc/device-tree/model"):
pi_model = shell.read_text_file("/proc/device-tree/model").replace("\x00", "")
is_pi5 = "Raspberry Pi 5" in pi_model

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — switched to shell.is_pi5_or_newer() to match joy-bonnet.py/arcade-bonnet.py. (8edf896)

Comment thread pi-eyes.py
Comment on lines +121 to +125
print("Installing system packages...")
shell.run_command(
"apt-get install -y build-essential python3-venv python3-dev "
"python3-full libx11-dev libxext-dev git curl unzip"
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasonable suggestion, but the original pi-eyes.sh doesn't gate on xinit either, and xinit is pulled in by the X11 dependency chain this script installs. To keep this a faithful 1:1 port I'm leaving the install list as-is; an explicit Lite-detection bail-out would be a behavior change worth doing as a separate follow-up if desired.

Comment thread pi-eyes.py
Comment on lines +157 to +160
shell.run_command(
"curl -sLO https://github.com/adafruit/Pi_Eyes/archive/master.zip"
)
shell.run_command("unzip -q master.zip")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — using curl -fsSLO here so HTTP failures fail immediately. (8edf896)

Comment thread pi-eyes.py
Comment on lines +185 to +188
shell.run_command(
"curl -sLO https://github.com/adafruit/Adafruit-GPIO-Halt/"
"archive/master.zip"
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — same curl -fsSLO fix applied here. (8edf896)

Comment thread pi-eyes.py
Comment on lines +320 to +322
append=False,
)
shell.run_command("systemctl enable pi-eyes-fbx2.service")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentionally matching the original pi-eyes.sh, which enables all units first and runs a single systemctl daemon-reload at the very end (after the last unit is written). Since this is a 1:1 port of the recently-rewritten Trixie script, I'm preserving that ordering rather than reshuffling it. The final reload happens before the script exits, so the freshly-written units are visible to systemd before first boot.

Comment thread pi-eyes.py
Comment on lines +341 to +344
""",
append=False,
)
shell.run_command("systemctl enable pi-eyes.service")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above — the consolidated daemon-reload at the end of the script (matching the original .sh) covers this unit too.

Comment thread pi-eyes.py
Comment on lines +360 to +363
""",
append=False,
)
shell.run_command("systemctl enable gpio-halt.service")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above — covered by the single trailing daemon-reload, matching the original .sh.

Comment thread pi-eyes.py
Comment on lines +5 to +7
# Adafruit Snake Eyes Bonnet installer for Raspberry Pi OS Trixie (Debian 13).
# Supports Pi 3B, Pi 4, and Pi 5.
#

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wording is carried over verbatim from the original pi-eyes.sh. The Pi Zero only applies to the optional USB-Ethernet-gadget prompt, not eye rendering, so the supported-boards banner (3B/4/5) and the gadget option aren't actually in conflict. Preserving the original text here.

Comment thread pi-eyes.py
Comment on lines +64 to +66
print("Adafruit Snake Eyes Bonnet installer")
print("Raspberry Pi OS Trixie - Pi 3B / Pi 4 / Pi 5")
print("")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the header note — carried over from the original .sh. The Pi Zero reference is scoped to the USB-gadget option only; keeping the banner text faithful to upstream.

- Replace /proc/device-tree/model string match with
  shell.is_pi5_or_newer() (matches joy-bonnet.py/arcade-bonnet.py).
- Use curl -fsSLO for downloads so HTTP failures surface immediately
  instead of writing an HTML error page to master.zip.

@makermelissa makermelissa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and fair pushback on copilot's suggestions.

@makermelissa makermelissa merged commit 93a676d into adafruit:main Jun 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants