Created
October 25, 2025 23:47
-
-
Save ds17f/546bcc98a480d3ace5254d61b57011b9 to your computer and use it in GitHub Desktop.
SafeShutdown.py Script for Retroflag Nespi4 on Debian Bookworm using gpiod instead of the legacy RPi.GPIO
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
| ## This script was written by Claude Code when I was trying to set up Safe Shutdown on Bookworm | |
| ## Claude told me not to do this but I told it "Make It So!" | |
| ## The script worked on the first run, but I don't make any guarantees about it | |
| ## Hopefully if you're reading this it will help you too. | |
| ## Please link this gist to any threads out there that need it. | |
| import gpiod | |
| import os | |
| import time | |
| import signal | |
| import sys | |
| from multiprocessing import Process | |
| # GPIO pin definitions | |
| POWER_PIN = 3 # pin 5 | |
| LED_PIN = 14 # TXD | |
| RESET_PIN = 2 # pin 13 | |
| POWEREN_PIN = 4 # pin 5 | |
| # Open GPIO chip | |
| chip = gpiod.Chip('gpiochip4') # RPi4 uses gpiochip4 | |
| # Define lines | |
| power_line = chip.get_line(POWER_PIN) | |
| reset_line = chip.get_line(RESET_PIN) | |
| led_line = chip.get_line(LED_PIN) | |
| poweren_line = chip.get_line(POWEREN_PIN) | |
| def init(): | |
| """Initialize GPIO pins""" | |
| try: | |
| # Configure power and reset as inputs with pull-up | |
| power_line.request( | |
| consumer="power", | |
| type=gpiod.LINE_REQ_DIR_IN, | |
| flags=gpiod.LINE_REQ_FLAG_BIAS_PULL_UP | |
| ) | |
| reset_line.request( | |
| consumer="reset", | |
| type=gpiod.LINE_REQ_DIR_IN, | |
| flags=gpiod.LINE_REQ_FLAG_BIAS_PULL_UP | |
| ) | |
| # Configure LED and power enable as outputs | |
| led_line.request( | |
| consumer="led", | |
| type=gpiod.LINE_REQ_DIR_OUT, | |
| default_val=1 # HIGH | |
| ) | |
| poweren_line.request( | |
| consumer="poweren", | |
| type=gpiod.LINE_REQ_DIR_OUT, | |
| default_val=1 # HIGH | |
| ) | |
| except Exception as e: | |
| print(f"GPIO initialization error: {e}") | |
| sys.exit(1) | |
| def listen(): | |
| """Listen for button presses""" | |
| print("SafeShutdown listening for power button...") | |
| power_pressed = False | |
| reset_pressed = False | |
| try: | |
| while True: | |
| # Read power button | |
| power_state = power_line.get_value() | |
| if power_state == 0 and not power_pressed: # Button pressed (LOW) | |
| power_pressed = True | |
| print("Power button pressed, initiating shutdown...") | |
| os.system("sudo shutdown -h now") | |
| elif power_state == 1: # Button released (HIGH) | |
| power_pressed = False | |
| # Read reset button | |
| reset_state = reset_line.get_value() | |
| if reset_state == 0 and not reset_pressed: # Button pressed (LOW) | |
| reset_pressed = True | |
| print("Reset button pressed, initiating reboot...") | |
| os.system("sudo shutdown -r now") | |
| elif reset_state == 1: # Button released (HIGH) | |
| reset_pressed = False | |
| time.sleep(0.1) # Poll every 100ms | |
| except KeyboardInterrupt: | |
| print("\nShutdown listener stopped") | |
| finally: | |
| # Release GPIO | |
| power_line.release() | |
| reset_line.release() | |
| led_line.release() | |
| poweren_line.release() | |
| def signal_handler(sig, frame): | |
| """Handle termination signals gracefully""" | |
| print("Received signal, cleaning up...") | |
| try: | |
| power_line.release() | |
| reset_line.release() | |
| led_line.release() | |
| poweren_line.release() | |
| except: | |
| pass | |
| sys.exit(0) | |
| if __name__ == "__main__": | |
| signal.signal(signal.SIGTERM, signal_handler) | |
| signal.signal(signal.SIGINT, signal_handler) | |
| init() | |
| listen() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment