Repository navigation
Replace gpiozero with a minimal /dev/gpiomem GPIO helper - #170
Open
zigpy-review-bot wants to merge 5 commits into
Open
zigpy-review-bot wants to merge 5 commits into
zigpy-review-bot wants to merge 5 commits into
Conversation
The PiZiGate code only switches GPIO 17 and 27 to outputs and drives them. On Home Assistant, gpiozero does this with its native backend, which maps /dev/gpiomem and writes the BCM2835 GPIO registers. Do the same directly with a small standard-library class, and drop gpiozero, which removes gpiozero, colorzero and setuptools from the runtime dependencies. Co-authored-by: puddly <32534428+puddly@users.noreply.github.com>
gpiozero's native backend refuses to start unless it finds a Raspberry Pi board revision code. Check the same code (from the device tree, or /proc/cpuinfo) before mapping /dev/gpiomem, and only continue for the SoCs whose GPIO block has the BCM2835 register layout (BCM2835, BCM2836, BCM2837, BCM2711). This keeps the BCM2835 register offsets from being written to a different GPIO block, e.g. on a Pi 5 or another board. Failures raise GPIOError with the reason, including when /dev/gpiomem cannot be opened.
Run GPIOMemory against a file standing in for /dev/gpiomem, and assert the exact pin sequence and delays of both PiZiGate mode switches.
This was referenced Oct 13, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #170 +/- ##
==========================================
+ Coverage 46.46% 49.08% +2.62%
==========================================
Files 8 9 +1
Lines 1300 1367 +67
==========================================
+ Hits 604 671 +67
Misses 696 696 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #168, as an alternative to #169:
gpiozerois replaced with a small helper that writes the Raspberry Pi's GPIO registers through/dev/gpiomem, following puddly's suggestion in #168, andgpiozerois dropped fromdependencies. That removesgpiozero→colorzero→setuptoolsfrom zigpy-zigate's runtime dependencies, and with them from zha's. (Home Assistant's image still installsgpiozerofor itsremote_rpi_gpiointegration, but ZHA no longer needs it or imports it.)On Home Assistant,
gpiozeroalready drives the PiZiGate this way. The image installsgpiozero1.6.2 (pinned byremote_rpi_gpio) and thepigpioclient, but none of the librariesgpiozerotries first (RPi.GPIO,lgpio,RPIO). Thepigpioclient only works with a running pigpio daemon, so unless the user runs one,gpiozerofalls back to its built-in native backend. That backend (the same in 1.6.2 and 2.0.1) maps/dev/gpiomemand writes the same BCM2835 GPIO registers. This PR does that directly, so for the existing Raspberry Pi 1–4 users the PiZiGate is driven the same way as before, with the same device access.Changes
zigpy_zigate/gpio.py, standard library only. The first commit is puddly'sGPIOMemoryclass from Avoid the setuptools runtime dependency pulled in through gpiozero #168 (co-authored): it maps/dev/gpiomem, switches a pin to output by replacing only that pin's 3-bit field in its function-select register, and drives it through the set/clear registers.set_pizigate_running_mode()/set_pizigate_flashing_mode()use it with the same pin sequence and 0.5 s delays as before.UnclosableOutputDeviceis gone: the registers keep their values after the mapping is closed, so the pins stay outputs and stay driven, including after the process exits, as withgpiozero's native backend.gpiozero(/proc/device-tree/system/linux,revision, then theRevisionline of/proc/cpuinfo), and only continues for the BCM2835, BCM2836, BCM2837 and BCM2711 (Pi 1–4, Zero, Compute Module 1–4), whose GPIO block has this register layout. Otherwise it raisesGPIOErrorinstead of writing these offsets into a different chip's registers: on a Pi 5, or on a non-Pi board whose serial port matchesis_pizigate()(it also matches/dev/ttyS*).gpiozero's native backend also refuses to start without a revision code, but does not reject the Pi 5./dev/gpiomemthat cannot be opened also raisesGPIOError, with the reason. Nothing catches these, so as before the connection attempt fails with the exception./dev/gpiomemaccess is needed.Compared with #169
#169 drives the pins through the GPIO character device (
/dev/gpiochipN) instead. That interface is the same on every Pi, so it may also work on a Pi 5, but it changes more for current users: it needs/dev/gpiochip*access instead of/dev/gpiomem(a Docker setup that only passes/dev/gpiomemthrough breaks), Linux 5.10 or later, and whether the pins stay driven after the process exits depends on the kernel. It is also more code (a 196-line helper that searches the GPIO chips for the right line). This PR keeps the current behaviour and leaves the Pi 5 for later: it has been out for three years without a PiZiGate request.Differences for current users
None expected on Home Assistant (Raspberry Pi 1–4). Corner cases compared with
gpiozero:/dev/memfallback. When/dev/gpiomemcannot be opened,gpiozerotries/dev/memat the GPIO address from the device tree, which needs root and a kernel that permits it. This PR doesn't, and fails withGPIOErrorinstead. Raspberry Pi kernels provide/dev/gpiomem.gpiozerouses another backend.gpiozeroprefersRPi.GPIOorlgpiowhen they can be imported (e.g. a Raspberry Pi OS install that uses the system's Python packages), andpigpiowhen a pigpio daemon is reachable. On a Pi 1–4 this PR drives the same pins through/dev/gpiomeminstead, so such a setup now needs access to/dev/gpiomem(lgpioused/dev/gpiochip*,pigpiowent through its daemon). On a Pi 5,lgpiocurrently makes the PiZiGate work there, and this PR raisesGPIOErrorinstead.gpiozero'sGPIOZERO_PIN_FACTORYandPIGPIO_ADDRenvironment variables no longer have an effect.gpiozerobriefly makes it an input and disables its pull resistor before switching it to an output. This PR switches it to an output directly. The pull setting has no effect while the pin is driven.Testing
tests/test_gpio.py: the board check (device tree and/proc/cpuinfocodes, old-style and new-style, Pi 5 and unknown codes, non-Pi), andGPIOMemoryagainst a file standing in for/dev/gpiomem(only the pin's own function-select field changes, the set/clear writes, the values persist after closing, re-opening works).tests/test_uart.pynow asserts the exact pin sequence and delays of both mode switches.gpiozeroandcolorzeronot importable. black, flake8, isort, mypy and pyupgrade pass via pre-commit; theautoflake8hook was left out because its repository no longer exists (also affectsdev, see Replace black, isort, flake8 and pyupgrade with ruff #167).--gpiomode still works.