pi-hole / bookworm / st7789 changes

A lot has changed since this guide was release.

The Pi Hole 6.x release in early 2025 dramatically changed the API.

Bookworm changes how some of the pins are used.

Even the ST7789 MiniPiTFT chip has some different options.

This code was tested and work with a MiniPiTFt on a Pi 4. I'd like to update the guide to support accommidate the software updates.
This commit is contained in:
Mikey Sklar 2025-06-02 12:43:59 -07:00
parent 5df1fe038b
commit 7a910ab962

View file

@ -19,11 +19,10 @@ import board
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
import adafruit_rgb_display.st7789 as st7789 import adafruit_rgb_display.st7789 as st7789
API_TOKEN = "YOUR_API_TOKEN_HERE" API_URL = "http://pi.hole/api/stats/summary"
api_url = "http://localhost/admin/api.php?summaryRaw&auth="+API_TOKEN
# Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4): # Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4):
cs_pin = digitalio.DigitalInOut(board.CE0) cs_pin = digitalio.DigitalInOut(board.D17)
dc_pin = digitalio.DigitalInOut(board.D25) dc_pin = digitalio.DigitalInOut(board.D25)
reset_pin = None reset_pin = None
@ -34,54 +33,42 @@ BAUDRATE = 64000000
spi = board.SPI() spi = board.SPI()
# Create the ST7789 display: # Create the ST7789 display:
disp = st7789.ST7789(spi, cs=cs_pin, dc=dc_pin, rst=reset_pin, baudrate=BAUDRATE, disp = st7789.ST7789(
width=135, height=240, x_offset=53, y_offset=40) spi,
dc_pin,
cs_pin,
reset_pin,
135,
240,
baudrate=BAUDRATE,
x_offset=53,
y_offset=40,
rotation=90
)
# Create blank image for drawing. # Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color. # Make sure to create image with mode 'RGB' for full color.
height = disp.width # we swap height/width to rotate it to landscape! CANVAS_WIDTH = disp.height
width = disp.height CANVAS_HEIGHT = disp.width
image = Image.new('RGB', (width, height)) image = Image.new('RGB', (CANVAS_WIDTH, CANVAS_HEIGHT))
rotation = 90
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image) draw = ImageDraw.Draw(image)
# Draw a black filled box to clear the image. # Load default font (or replace with a TTF if desired)
draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0)) FONT_PATH = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
disp.image(image, rotation) font = ImageFont.truetype(FONT_PATH, 20)
# Draw some shapes.
# First define some constants to allow easy resizing of shapes.
padding = -2
top = padding
bottom = height-padding
# Move left to right keeping track of the current x position for drawing shapes.
x = 0
# Alternatively load a TTF font. Make sure the .ttf font file is in the
# same directory as the python script!
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 24)
# Turn on the backlight
backlight = digitalio.DigitalInOut(board.D22)
backlight.switch_to_output()
backlight.value = True
# Add buttons as inputs
buttonA = digitalio.DigitalInOut(board.D23) buttonA = digitalio.DigitalInOut(board.D23)
buttonA.switch_to_input() buttonA.switch_to_input()
while True: while True:
# Draw a black filled box to clear the image. # Draw a black filled box to clear the image.
draw.rectangle((0, 0, width, height), outline=0, fill=0) draw.rectangle((0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), outline=0, fill=(0, 0, 0))
# Shell scripts for system monitoring from here: # Shell scripts for system monitoring from here:
# https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load # https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load
cmd = "hostname -I | cut -d\' \' -f1" cmd = "hostname -I | cut -d' ' -f1"
IP = "IP: "+subprocess.check_output(cmd, shell=True).decode("utf-8") IP = "IP: " + subprocess.check_output(cmd, shell=True).decode("utf-8").strip()
cmd = "hostname | tr -d \'\\n\'" cmd = "hostname | tr -d '\\n'"
HOST = subprocess.check_output(cmd, shell=True).decode("utf-8") HOST = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'" cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'"
CPU = subprocess.check_output(cmd, shell=True).decode("utf-8") CPU = subprocess.check_output(cmd, shell=True).decode("utf-8")
@ -89,22 +76,24 @@ while True:
MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8") MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%d GB %s\", $3,$2,$5}'" cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%d GB %s\", $3,$2,$5}'"
Disk = subprocess.check_output(cmd, shell=True).decode("utf-8") Disk = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk \'{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}\'" # pylint: disable=line-too-long cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'"
Temp = subprocess.check_output(cmd, shell=True).decode("utf-8") Temp = subprocess.check_output(cmd, shell=True).decode("utf-8")
# Pi Hole data! # Pi Hole data!
try: try:
r = requests.get(api_url) r = requests.get(API_URL, timeout=5)
data = json.loads(r.text) r.raise_for_status()
DNSQUERIES = data['dns_queries_today'] data = r.json()
ADSBLOCKED = data['ads_blocked_today'] DNSQUERIES = data["queries"]["total"]
CLIENTS = data['unique_clients'] ADSBLOCKED = data["queries"]["blocked"]
except KeyError: CLIENTS = data["clients"]["total"]
time.sleep(1) except (KeyError, requests.RequestException, json.JSONDecodeError):
continue DNSQUERIES = None
ADSBLOCKED = None
CLIENTS = None
y = top y = top = 5
x = 5
if not buttonA.value: # just button A pressed if not buttonA.value: # just button A pressed
draw.text((x, y), IP, font=font, fill="#FFFF00") draw.text((x, y), IP, font=font, fill="#FFFF00")
y += font.getbbox(IP)[3] y += font.getbbox(IP)[3]
@ -121,13 +110,19 @@ while True:
y += font.getbbox(IP)[3] y += font.getbbox(IP)[3]
draw.text((x, y), HOST, font=font, fill="#FFFF00") draw.text((x, y), HOST, font=font, fill="#FFFF00")
y += font.getbbox(HOST)[3] y += font.getbbox(HOST)[3]
draw.text((x, y), "Ads Blocked: {}".format(str(ADSBLOCKED)), font=font, fill="#00FF00") if ADSBLOCKED is not None:
y += font.getbbox(str(ADSBLOCKED))[3] txt = f"Ads Blocked: {ADSBLOCKED}"
draw.text((x, y), "Clients: {}".format(str(CLIENTS)), font=font, fill="#0000FF") draw.text((x, y), txt, font=font, fill="#00FF00")
y += font.getbbox(str(CLIENTS))[3] y += font.getbbox(txt)[3]
draw.text((x, y), "DNS Queries: {}".format(str(DNSQUERIES)), font=font, fill="#FF00FF") if CLIENTS is not None:
y += font.getbbox(str(DNSQUERIES))[3] txt = f"Clients: {CLIENTS}"
draw.text((x, y), txt, font=font, fill="#0000FF")
y += font.getbbox(txt)[3]
if DNSQUERIES is not None:
txt = f"DNS Queries: {DNSQUERIES}"
draw.text((x, y), txt, font=font, fill="#FF00FF")
y += font.getbbox(txt)[3]
# Display image. # Display image.
disp.image(image, rotation) disp.image(image)
time.sleep(.1) time.sleep(.1)