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:
parent
5df1fe038b
commit
7a910ab962
1 changed files with 50 additions and 55 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue