Skip to content

Commit 7a910ab

Browse files
committed
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.
1 parent 5df1fe0 commit 7a910ab

File tree

1 file changed

+52
-57
lines changed

1 file changed

+52
-57
lines changed

Pi_Hole_Ad_Blocker/mini_pitft_stats.py

Lines changed: 52 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@
1919
from PIL import Image, ImageDraw, ImageFont
2020
import adafruit_rgb_display.st7789 as st7789
2121

22-
API_TOKEN = "YOUR_API_TOKEN_HERE"
23-
api_url = "http://localhost/admin/api.php?summaryRaw&auth="+API_TOKEN
22+
API_URL = "http://pi.hole/api/stats/summary"
2423

2524
# Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4):
26-
cs_pin = digitalio.DigitalInOut(board.CE0)
25+
cs_pin = digitalio.DigitalInOut(board.D17)
2726
dc_pin = digitalio.DigitalInOut(board.D25)
2827
reset_pin = None
2928

@@ -34,77 +33,67 @@
3433
spi = board.SPI()
3534

3635
# Create the ST7789 display:
37-
disp = st7789.ST7789(spi, cs=cs_pin, dc=dc_pin, rst=reset_pin, baudrate=BAUDRATE,
38-
width=135, height=240, x_offset=53, y_offset=40)
36+
disp = st7789.ST7789(
37+
spi,
38+
dc_pin,
39+
cs_pin,
40+
reset_pin,
41+
135,
42+
240,
43+
baudrate=BAUDRATE,
44+
x_offset=53,
45+
y_offset=40,
46+
rotation=90
47+
)
3948

4049
# Create blank image for drawing.
4150
# Make sure to create image with mode 'RGB' for full color.
42-
height = disp.width # we swap height/width to rotate it to landscape!
43-
width = disp.height
44-
image = Image.new('RGB', (width, height))
45-
rotation = 90
46-
47-
# Get drawing object to draw on image.
51+
CANVAS_WIDTH = disp.height
52+
CANVAS_HEIGHT = disp.width
53+
image = Image.new('RGB', (CANVAS_WIDTH, CANVAS_HEIGHT))
4854
draw = ImageDraw.Draw(image)
4955

50-
# Draw a black filled box to clear the image.
51-
draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0))
52-
disp.image(image, rotation)
53-
# Draw some shapes.
54-
# First define some constants to allow easy resizing of shapes.
55-
padding = -2
56-
top = padding
57-
bottom = height-padding
58-
# Move left to right keeping track of the current x position for drawing shapes.
59-
x = 0
60-
61-
62-
# Alternatively load a TTF font. Make sure the .ttf font file is in the
63-
# same directory as the python script!
64-
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
65-
font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 24)
66-
67-
# Turn on the backlight
68-
backlight = digitalio.DigitalInOut(board.D22)
69-
backlight.switch_to_output()
70-
backlight.value = True
71-
72-
# Add buttons as inputs
56+
# Load default font (or replace with a TTF if desired)
57+
FONT_PATH = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
58+
font = ImageFont.truetype(FONT_PATH, 20)
59+
7360
buttonA = digitalio.DigitalInOut(board.D23)
7461
buttonA.switch_to_input()
7562

7663
while True:
7764
# Draw a black filled box to clear the image.
78-
draw.rectangle((0, 0, width, height), outline=0, fill=0)
65+
draw.rectangle((0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), outline=0, fill=(0, 0, 0))
7966

8067
# Shell scripts for system monitoring from here:
8168
# https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load
82-
cmd = "hostname -I | cut -d\' \' -f1"
83-
IP = "IP: "+subprocess.check_output(cmd, shell=True).decode("utf-8")
84-
cmd = "hostname | tr -d \'\\n\'"
69+
cmd = "hostname -I | cut -d' ' -f1"
70+
IP = "IP: " + subprocess.check_output(cmd, shell=True).decode("utf-8").strip()
71+
cmd = "hostname | tr -d '\\n'"
8572
HOST = subprocess.check_output(cmd, shell=True).decode("utf-8")
8673
cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'"
8774
CPU = subprocess.check_output(cmd, shell=True).decode("utf-8")
8875
cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%s MB %.2f%%\", $3,$2,$3*100/$2 }'"
8976
MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8")
9077
cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%d GB %s\", $3,$2,$5}'"
9178
Disk = subprocess.check_output(cmd, shell=True).decode("utf-8")
92-
cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk \'{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}\'" # pylint: disable=line-too-long
79+
cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'"
9380
Temp = subprocess.check_output(cmd, shell=True).decode("utf-8")
9481

95-
9682
# Pi Hole data!
9783
try:
98-
r = requests.get(api_url)
99-
data = json.loads(r.text)
100-
DNSQUERIES = data['dns_queries_today']
101-
ADSBLOCKED = data['ads_blocked_today']
102-
CLIENTS = data['unique_clients']
103-
except KeyError:
104-
time.sleep(1)
105-
continue
106-
107-
y = top
84+
r = requests.get(API_URL, timeout=5)
85+
r.raise_for_status()
86+
data = r.json()
87+
DNSQUERIES = data["queries"]["total"]
88+
ADSBLOCKED = data["queries"]["blocked"]
89+
CLIENTS = data["clients"]["total"]
90+
except (KeyError, requests.RequestException, json.JSONDecodeError):
91+
DNSQUERIES = None
92+
ADSBLOCKED = None
93+
CLIENTS = None
94+
95+
y = top = 5
96+
x = 5
10897
if not buttonA.value: # just button A pressed
10998
draw.text((x, y), IP, font=font, fill="#FFFF00")
11099
y += font.getbbox(IP)[3]
@@ -121,13 +110,19 @@
121110
y += font.getbbox(IP)[3]
122111
draw.text((x, y), HOST, font=font, fill="#FFFF00")
123112
y += font.getbbox(HOST)[3]
124-
draw.text((x, y), "Ads Blocked: {}".format(str(ADSBLOCKED)), font=font, fill="#00FF00")
125-
y += font.getbbox(str(ADSBLOCKED))[3]
126-
draw.text((x, y), "Clients: {}".format(str(CLIENTS)), font=font, fill="#0000FF")
127-
y += font.getbbox(str(CLIENTS))[3]
128-
draw.text((x, y), "DNS Queries: {}".format(str(DNSQUERIES)), font=font, fill="#FF00FF")
129-
y += font.getbbox(str(DNSQUERIES))[3]
113+
if ADSBLOCKED is not None:
114+
txt = f"Ads Blocked: {ADSBLOCKED}"
115+
draw.text((x, y), txt, font=font, fill="#00FF00")
116+
y += font.getbbox(txt)[3]
117+
if CLIENTS is not None:
118+
txt = f"Clients: {CLIENTS}"
119+
draw.text((x, y), txt, font=font, fill="#0000FF")
120+
y += font.getbbox(txt)[3]
121+
if DNSQUERIES is not None:
122+
txt = f"DNS Queries: {DNSQUERIES}"
123+
draw.text((x, y), txt, font=font, fill="#FF00FF")
124+
y += font.getbbox(txt)[3]
130125

131126
# Display image.
132-
disp.image(image, rotation)
127+
disp.image(image)
133128
time.sleep(.1)

0 commit comments

Comments
 (0)