This project involves adding two buttons to your Raspberry Pi, one to disable
n seconds and the other to enable it back. I run many services on
one Raspberry Pi and a dummy synchronous
while True loop will lock one core,
that isn’t going to fly.
For this project we need three LEDs, two buttons, and one 330 Ω resistance. We set the green, red and blue LEDs to pins 7, 15, and 12 respectively. In this project Raspbian and a RPi 3B+ were used.
Check the pinout diagram of the Raspberry Pi at pinout.
Let us start by listing the dependencies
#!/usr/bin/env python3 import RPi.GPIO as GPIO # Import Raspberry Pi GPIO library import pihole as ph import sys import asyncio
Controlling the pi-hole
We control the Pi-hole with PiHole-Api, for this we need the Pi-hole IP address and the WebUI password.
pihole_addr = "192.168.0.21" time_limit = 30 # Disable for at most 30 seconds pihole_password = "PIHOLE_PASSWORD" # Set the password for first use # Get password password = pihole_password # Pihole pihole = ph.PiHole(pihole_addr) pihole.authenticate(password) version = pihole.getVersion()['core_current'] status = pihole.status print("Runing pihole", version) print("Status:", pihole.status)
Using python-keyring to store passwords
Alternatively, using the keyring package we can securely store the password.
import keyring from keyrings.alt.file import PlaintextKeyring keyring.set_keyring(PlaintextKeyring()) password = keyring.get_password("pihole", pihole_addr) if password is None: keyring.set_password("pihole", pihole_addr, pihole_password) password = pihole_password
On the first run set
pihole_password to its real value and then set it to
None. The line
keyring.set_keyring(PlaintextKeyring()) should be only be
used if you do not want to be prompted for the keyring password every time, this
requires to add
Setting the GPIOs
This project uses a blue LED to signal that the Pi-hole is disabled. First we create a timer class to re-enable the Pi-hole.
## GPIO setup red = 7 # Red using pin 7 green = 15 # Green using pin 15 blue = 12 # Blue using pin 12 ## Async loop = None timer = None # Timer bouncetime = 500 # in ms class Timer: def __init__(self, timeout, callback): self._timeout = timeout self._callback = callback self._task = asyncio.ensure_future(self._job()) async def _job(self): await asyncio.sleep(self._timeout) await self._callback() def cancel(self): self._task.cancel() async def timeout_callback(): pihole.enable() GPIO.output(blue, GPIO.LOW)
We also add callbacks for the buttons
def button_pushed(_): if loop is None: print("Loop ended") return # should not come to this if GPIO.input(green): loop.call_soon_threadsafe(green_action) if GPIO.input(red): loop.call_soon_threadsafe(red_action) def red_action(): print("Red button pushed!") GPIO.output(blue, GPIO.HIGH) pihole.disable(time_limit) timer = Timer(time_limit, timeout_callback) def green_action(): print("Green button pushed!") if timer is not None: timer.cancel() GPIO.output(blue, GPIO.LOW) pihole.enable()
It is important to leave everything in a clean state when closing the program, this is done using the following function.
def exit_handler(): print('py-hole-ui closed') GPIO.cleanup() pihole.enable() loop.close()
Now we can define the main loop
try: GPIO.setwarnings(True) # Set warnings GPIO.setmode(GPIO.BOARD) # Use physical pin numbering # Set pins to input/output GPIO.setup(red, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(green, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(blue, GPIO.OUT) GPIO.add_event_detect( green, GPIO.RISING, callback=button_pushed, bouncetime=bouncetime, ) GPIO.add_event_detect( red, GPIO.RISING, callback=button_pushed, bouncetime=bouncetime, ) loop = asyncio.get_event_loop() loop.run_forever() finally: exit_handler()
Now python3 does not even appear in the process list, compared to the 100% CPU usage one could get using a more naive approach.
Code at github.