|
1 | 1 | # SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries |
2 | 2 | # SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries |
3 | 3 | # SPDX-FileCopyrightText: 2017 James DeVito for Adafruit Industries |
| 4 | +# SPDX-FileCopyrightText: 2025 Mikey Sklar for Adafruit Industries |
4 | 5 | # |
5 | 6 | # SPDX-License-Identifier: MIT |
6 | 7 |
|
|
29 | 30 | # Adafruit Blinka to support CircuitPython libraries. CircuitPython does |
30 | 31 | # not support PIL/pillow (python imaging library)! |
31 | 32 |
|
32 | | -# Import Python System Libraries |
| 33 | + |
33 | 34 | import json |
34 | 35 | import subprocess |
35 | 36 | import time |
36 | 37 |
|
37 | | -# Import Requests Library |
38 | 38 | import requests |
39 | | - |
40 | | -# Import Blinka |
41 | 39 | from board import SCL, SDA |
42 | 40 | import busio |
43 | 41 | import adafruit_ssd1306 |
44 | | - |
45 | | -# Import Python Imaging Library |
46 | 42 | from PIL import Image, ImageDraw, ImageFont |
47 | 43 |
|
48 | | -API_TOKEN = "YOUR_API_TOKEN_HERE" |
49 | | -api_url = "http://localhost/admin/api.php?summaryRaw&auth="+API_TOKEN |
| 44 | +api_url = "http://localhost/api/stats/summary" |
50 | 45 |
|
51 | 46 | # Create the I2C interface. |
52 | 47 | i2c = busio.I2C(SCL, SDA) |
53 | 48 |
|
54 | 49 | # Create the SSD1306 OLED class. |
55 | | -# The first two parameters are the pixel width and pixel height. Change these |
56 | | -# to the right size for your display! |
57 | 50 | disp = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c) |
58 | 51 |
|
59 | 52 | # Leaving the OLED on for a long period of time can damage it |
60 | | -# Set these to prevent OLED burn in |
61 | | -DISPLAY_ON = 10 # on time in seconds |
62 | | -DISPLAY_OFF = 50 # off time in seconds |
| 53 | +DISPLAY_ON = 10 # on time in seconds |
| 54 | +DISPLAY_OFF = 50 # off time in seconds |
63 | 55 |
|
64 | 56 | # Clear display. |
65 | 57 | disp.fill(0) |
66 | 58 | disp.show() |
67 | 59 |
|
68 | 60 | # Create blank image for drawing. |
69 | | -# Make sure to create image with mode '1' for 1-bit color. |
70 | 61 | width = disp.width |
71 | 62 | height = disp.height |
72 | 63 | image = Image.new('1', (width, height)) |
|
77 | 68 | # Draw a black filled box to clear the image. |
78 | 69 | draw.rectangle((0, 0, width, height), outline=0, fill=0) |
79 | 70 |
|
80 | | -# Draw some shapes. |
81 | | -# First define some constants to allow easy resizing of shapes. |
82 | 71 | padding = -2 |
83 | 72 | top = padding |
84 | | -bottom = height - padding |
85 | | -# Move left to right keeping track of the current x position |
86 | | -# for drawing shapes. |
87 | 73 | x = 0 |
88 | 74 |
|
89 | 75 | # Load nice silkscreen font |
90 | 76 | font = ImageFont.truetype('/home/pi/slkscr.ttf', 8) |
91 | 77 |
|
92 | 78 | while True: |
93 | | - # Draw a black filled box to clear the image. |
| 79 | + # Clear the image buffer |
94 | 80 | draw.rectangle((0, 0, width, height), outline=0, fill=0) |
95 | 81 |
|
96 | | - # Shell scripts for system monitoring from here : |
97 | | - # https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load |
98 | | - cmd = "hostname -I | cut -d\' \' -f1 | tr -d \'\\n\'" |
| 82 | + # Shell scripts for system monitoring |
| 83 | + cmd = "hostname -I | cut -d' ' -f1 | tr -d '\\n'" |
99 | 84 | IP = subprocess.check_output(cmd, shell=True).decode("utf-8") |
100 | | - cmd = "hostname | tr -d \'\\n\'" |
| 85 | + cmd = "hostname | tr -d '\\n'" |
101 | 86 | HOST = subprocess.check_output(cmd, shell=True).decode("utf-8") |
102 | 87 | cmd = "top -bn1 | grep load | awk " \ |
103 | 88 | "'{printf \"CPU Load: %.2f\", $(NF-2)}'" |
|
109 | 94 | "\"Disk: %d/%dGB %s\", $3,$2,$5}'" |
110 | 95 | Disk = subprocess.check_output(cmd, shell=True).decode("utf-8") |
111 | 96 |
|
112 | | - # Pi Hole data! |
| 97 | + # Pi-Hole data! |
113 | 98 | try: |
114 | | - r = requests.get(api_url) |
115 | | - data = json.loads(r.text) |
116 | | - DNSQUERIES = data['dns_queries_today'] |
117 | | - ADSBLOCKED = data['ads_blocked_today'] |
118 | | - CLIENTS = data['unique_clients'] |
119 | | - except KeyError: |
120 | | - time.sleep(1) |
121 | | - continue |
122 | | - |
123 | | - draw.text((x, top), "IP: " + str(IP) + |
124 | | - " (" + HOST + ")", font=font, fill=255) |
125 | | - draw.text((x, top + 8), "Ads Blocked: " + |
126 | | - str(ADSBLOCKED), font=font, fill=255) |
127 | | - draw.text((x, top + 16), "Clients: " + |
128 | | - str(CLIENTS), font=font, fill=255) |
129 | | - draw.text((x, top + 24), "DNS Queries: " + |
130 | | - str(DNSQUERIES), font=font, fill=255) |
131 | | - |
132 | | - # skip over original stats |
133 | | - # draw.text((x, top+8), str(CPU), font=font, fill=255) |
134 | | - # draw.text((x, top+16), str(MemUsage), font=font, fill=255) |
135 | | - # draw.text((x, top+25), str(Disk), font=font, fill=255) |
| 99 | + r = requests.get(api_url, timeout=2) |
| 100 | + r.raise_for_status() |
| 101 | + data = r.json() |
| 102 | + DNSQUERIES = data["queries"]["total"] |
| 103 | + ADSBLOCKED = data["queries"]["blocked"] |
| 104 | + CLIENTS = data["clients"]["total"] |
| 105 | + except Exception: |
| 106 | + DNSQUERIES = 0 |
| 107 | + ADSBLOCKED = 0 |
| 108 | + CLIENTS = 0 |
| 109 | + |
| 110 | + draw.text((x, top), "IP: " + IP + " (" + HOST + ")", font=font, fill=255) |
| 111 | + draw.text((x, top + 8), "Ads Blocked: " + str(ADSBLOCKED), font=font, fill=255) |
| 112 | + draw.text((x, top + 16), "Clients: " + str(CLIENTS), font=font, fill=255) |
| 113 | + draw.text((x, top + 24), "DNS Queries: " + str(DNSQUERIES), font=font, fill=255) |
136 | 114 |
|
137 | 115 | # Display image. |
138 | 116 | disp.image(image) |
139 | 117 | disp.show() |
140 | 118 | time.sleep(DISPLAY_ON) |
| 119 | + |
| 120 | + # Blank screen to prevent burn-in |
141 | 121 | disp.fill(0) |
142 | 122 | disp.show() |
143 | 123 | time.sleep(DISPLAY_OFF) |
0 commit comments