After Width: | Height: | Size: 689 B |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 71 KiB |
@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
trap ctrl_c INT
|
||||||
|
|
||||||
|
delay=0.05
|
||||||
|
|
||||||
|
function ctrl_c() {
|
||||||
|
echo "0" > /sys/class/gpio/gpio17/value
|
||||||
|
echo "0" > /sys/class/gpio/gpio27/value
|
||||||
|
echo "0" > /sys/class/gpio/gpio23/value
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# RED
|
||||||
|
echo "17" > /sys/class/gpio/export 2>/dev/null
|
||||||
|
echo "out" > /sys/class/gpio/gpio17/direction
|
||||||
|
# GREEN
|
||||||
|
echo "27" > /sys/class/gpio/export 2>/dev/null
|
||||||
|
echo "out" > /sys/class/gpio/gpio27/direction
|
||||||
|
# BLUE
|
||||||
|
echo "23" > /sys/class/gpio/export 2>/dev/null
|
||||||
|
echo "out" > /sys/class/gpio/gpio23/direction
|
||||||
|
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
echo "1" > /sys/class/gpio/gpio17/value
|
||||||
|
sleep $delay
|
||||||
|
echo "0" > /sys/class/gpio/gpio17/value
|
||||||
|
echo "1" > /sys/class/gpio/gpio27/value
|
||||||
|
sleep $delay
|
||||||
|
echo "0" > /sys/class/gpio/gpio27/value
|
||||||
|
echo "1" > /sys/class/gpio/gpio23/value
|
||||||
|
sleep $delay
|
||||||
|
echo "0" > /sys/class/gpio/gpio23/value
|
||||||
|
done
|
@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
sock=/var/run/pibox/framebuffer.sock
|
||||||
|
imgdir=~/img
|
||||||
|
blackimg='black-x240.png'
|
||||||
|
imglist=()
|
||||||
|
sleeptime=10 # seconds
|
||||||
|
hidecurloutput=1
|
||||||
|
debug=0
|
||||||
|
|
||||||
|
# -S FILE - True if the FILE exists and is a socket.
|
||||||
|
# https://linuxize.com/post/bash-check-if-file-exists/
|
||||||
|
if [ ! -S ${sock} ]; then echo Unable to find unix socket. Pls fix.; exit 1; fi
|
||||||
|
[ "$hidecurloutput" == "1" ] && curlout='-o /dev/null' || curlout=''
|
||||||
|
|
||||||
|
trap ctrl_c INT
|
||||||
|
|
||||||
|
function ctrl_c() {
|
||||||
|
sudo curl -sS --unix-socket ${sock} -X POST --data-binary @${imgdir}/${blackimg} http://localhost/image $curlout
|
||||||
|
tput cub 5
|
||||||
|
echo exiting
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function debuglog() {
|
||||||
|
[ $debug -eq 0 ] && return
|
||||||
|
echo $1
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadimgs() {
|
||||||
|
for file in `ls $imgdir`; do
|
||||||
|
debuglog "Found image file $file"
|
||||||
|
[ ${file} == ${blackimg} ] && continue
|
||||||
|
imglist[${#imglist[@]}]=${file}
|
||||||
|
debuglog "Loaded image file $file"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayimg() {
|
||||||
|
newimg="${1}"
|
||||||
|
debuglog "Displaying image ${1}"
|
||||||
|
sudo curl -sS --unix-socket ${sock} -X POST --data-binary @${imgdir}/${newimg} http://localhost/image $curlout
|
||||||
|
}
|
||||||
|
|
||||||
|
loadimgs
|
||||||
|
debuglog "Image list: ${imglist[@]}"
|
||||||
|
while true; do
|
||||||
|
for thisimg in ${imglist[@]}; do
|
||||||
|
displayimg "${thisimg}"
|
||||||
|
sleep ${sleeptime}
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
@ -0,0 +1,347 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
## -=Notebook=-
|
||||||
|
#
|
||||||
|
# - Exception hierarchy: https://docs.python.org/3/library/exceptions.html#exception-hierarchy
|
||||||
|
# Non-default Prerequisites:
|
||||||
|
# - Font: Cascadia Code
|
||||||
|
# - APT packages: python3-dev
|
||||||
|
# - PIP packages: psutil, pillow
|
||||||
|
|
||||||
|
import socket, io, psutil, datetime
|
||||||
|
from os.path import exists as file_exists
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
# Socket constants
|
||||||
|
KUBESOCK="/var/run/pibox/framebuffer.sock"
|
||||||
|
HEADER1=b"POST /image HTTP/1.1\x0d\x0aHost: localhost\x0d\x0aUser-Agent: python-socket\x0d\x0aAccept: */*\x0d\x0a"
|
||||||
|
HEADER2=b"\x0d\x0aContent-Type: application/x-www-form-urlencoded\x0d\x0a\x0d\x0a"
|
||||||
|
|
||||||
|
# Drawing constants
|
||||||
|
SCREENWIDTH=240
|
||||||
|
SCREENHEIGHT=240
|
||||||
|
CASCADIAFONTPATH="/usr/share/fonts/truetype/CascadiaCode/static/CascadiaMono-Regular.ttf"
|
||||||
|
FONTSIZE={"CHONKY": 35,"THICC": 25, "BIG":15, "MED":12,"SMOL":10}
|
||||||
|
#DISPLAYWIDTHCHARS={"CHONKY": 11, "THICC": 16, "BIG":26, "MED":34, "SMOL":40}
|
||||||
|
FONT={"CHONKY":ImageFont.truetype(CASCADIAFONTPATH,FONTSIZE["CHONKY"]),
|
||||||
|
"THICC":ImageFont.truetype(CASCADIAFONTPATH,FONTSIZE["THICC"]),
|
||||||
|
"BIG":ImageFont.truetype(CASCADIAFONTPATH,FONTSIZE["BIG"]),
|
||||||
|
"MED":ImageFont.truetype(CASCADIAFONTPATH,FONTSIZE["MED"]),
|
||||||
|
"SMOL":ImageFont.truetype(CASCADIAFONTPATH,FONTSIZE["SMOL"])}
|
||||||
|
BLACK,GRAY,WHITE=(0,0,0,255),(127,127,127,255),(255,255,255,255)
|
||||||
|
RED,ORANGE,YELLOW=(200,0,0,255),(200,125,0,255),(200,200,0,255)
|
||||||
|
DARK_GREEN, DARK_YELLOW, DARK_RED=(0,80,0,255),(150,150,0,255),(100,0,0,255)
|
||||||
|
GREEN,BLUE,PURPLE=(0,230,0,255),(80,190,255,255),(180,100,255,255)
|
||||||
|
AQUA,PINK=(55, 255, 255, 255),(255,150,230,255)
|
||||||
|
|
||||||
|
|
||||||
|
# Metrics constants
|
||||||
|
NET_ADAPTER="eth0"
|
||||||
|
CPULOADLABELS=["1min", "5min", "15min"]
|
||||||
|
# only added to as-anticipated
|
||||||
|
SUBNET_CIDR_MAP={"255.0.0.0": "8", "255.255.0.0": "16", "255.255.255.0": "24", "255.255.255.128":"25"}
|
||||||
|
|
||||||
|
mystats=Image.new("RGBA", (240, 240), BLACK)
|
||||||
|
drawer=ImageDraw.Draw(mystats)
|
||||||
|
|
||||||
|
def precheck():
|
||||||
|
if not file_exists(KUBESOCK):
|
||||||
|
raise FileNotFoundError(f"Cannot find socket {KUBESOCK} - ensure socket server is running")
|
||||||
|
if not file_exists(CASCADIAFONTPATH):
|
||||||
|
raise FileNotFoundError(f"Cannot find font file {CASCADIAFONTPATH} - please ensure it is installed on your system")
|
||||||
|
return
|
||||||
|
|
||||||
|
def sendimage(imgdata):
|
||||||
|
if type(imgdata) is Image.Image:
|
||||||
|
# https://stackoverflow.com/a/33117447
|
||||||
|
newbytes=io.BytesIO()
|
||||||
|
imgdata.save(newbytes, format="PNG")
|
||||||
|
imgbytes=newbytes.getvalue()
|
||||||
|
elif type(imgdata) is io.BytesIO:
|
||||||
|
imgbytes=imgdata.getvalue()
|
||||||
|
imglength=f"{len(imgbytes)}"
|
||||||
|
request_data=HEADER1+b"Content-Length: "+bytes(imglength,"UTF-8")+HEADER2+imgbytes
|
||||||
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
|
||||||
|
client.connect(KUBESOCK)
|
||||||
|
client.send(request_data)
|
||||||
|
#print(request_data)
|
||||||
|
print(client.recv(4096))
|
||||||
|
client.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
def writewords(text, line=5, spacing=3, indent=0, right=SCREENWIDTH, font="BIG", color=WHITE, alignment="LEFT", outline=None):
|
||||||
|
alignment=alignment.upper()
|
||||||
|
font=font.upper()
|
||||||
|
pixelwidth=round(drawer.textlength(text, font=FONT[font]),0)
|
||||||
|
#print(f"text \"{text}\" has width {pixelwidth} pixels.")
|
||||||
|
strokewidth=0 if outline == None else 1
|
||||||
|
if alignment == "LEFT":
|
||||||
|
drawer.text((indent, line), text, font=FONT[font], fill=color, stroke_fill=outline, stroke_width=strokewidth)
|
||||||
|
elif alignment == "RIGHT":
|
||||||
|
drawer.text((right-pixelwidth-indent, line), text, font=FONT[font], fill=color, stroke_fill=outline, stroke_width=strokewidth)
|
||||||
|
elif alignment == "CENTER":
|
||||||
|
drawer.text(((((right-indent)/2)-(pixelwidth/2))+indent, line), text, font=FONT[font], fill=color, stroke_fill=outline, stroke_width=strokewidth)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unsupported alignment provided to writewords function: {alignment}")
|
||||||
|
return line+FONTSIZE[font]+spacing
|
||||||
|
|
||||||
|
def drawbar(top=50, indent=20, right=220, height=30, color=WHITE, corners=(True, True, True, True), radius=10, percent=.5):
|
||||||
|
# Minimum 11% for easy draw (radius*2+1 pixels)
|
||||||
|
# Maximum 95% for easy draw
|
||||||
|
radius = radius if radius*2+2 < height else height/2-2
|
||||||
|
outlinecorners=corners
|
||||||
|
fillcorners=corners
|
||||||
|
#print(f"Top left: ({indent}, {top}) / Bottom right: ({right}, {top+height})")
|
||||||
|
# 0 1 2 3
|
||||||
|
# corners=(top_left, top_right, bottom_right, bottom_left)
|
||||||
|
if percent < .9: fillcorners=(corners[0], False, False, corners[3])
|
||||||
|
if percent >= .15:
|
||||||
|
drawer.rounded_rectangle(((indent, top), (((right-indent)*percent)+indent, top+height)), radius=radius, outline=None, fill=color, corners=fillcorners)
|
||||||
|
else:
|
||||||
|
# Due to how the rounded_rectangle function draws the shape we have to draw at least 11% (20% is just a nice number)
|
||||||
|
# and then "subtract" the leftover percents from the bar to show only what we want
|
||||||
|
# (also the top 5% isn't neatly rounded but that's a problem for a later time)
|
||||||
|
drawer.rounded_rectangle(((indent, top), (((right-indent)*.2)+indent, top+height)), radius=radius, outline=None, fill=color, corners=fillcorners)
|
||||||
|
drawer.rectangle(((((right-indent)*percent)+indent, top), (((right-indent)*.2)+indent, top+height)), fill=BLACK, outline=None)
|
||||||
|
# Then we draw the outline afterwards so it goes over our blacked-out region (if present)
|
||||||
|
drawer.rounded_rectangle(((indent, top), (right, top+height)), radius=radius, outline=color, fill=None, corners=outlinecorners)
|
||||||
|
return top+height+1
|
||||||
|
|
||||||
|
def substring_indicies(string, character):
|
||||||
|
# https://stackoverflow.com/a/13009866
|
||||||
|
return [i for i, letter in enumerate(string) if letter == character]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def textstats():
|
||||||
|
line=FONTSIZE['BIG']
|
||||||
|
column=(5,120)
|
||||||
|
# CPU PERCENTS
|
||||||
|
cpu_use=psutil.cpu_percent(interval=1,percpu=True)
|
||||||
|
core=0
|
||||||
|
while core < len(cpu_use):
|
||||||
|
drawer.text((column[core%2],line), f"CPU {core}: {cpu_use[core]}%", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+= 0 if core%2==0 else FONTSIZE['BIG']+5
|
||||||
|
core+=1
|
||||||
|
# CPU LOAD
|
||||||
|
cpu_loadsss=psutil.getloadavg()
|
||||||
|
load=0
|
||||||
|
line+=10
|
||||||
|
while load < len(cpu_loadsss):
|
||||||
|
drawer.text((column[0], line), f"{CPULOADLABELS[load]} load: {round(cpu_loadsss[load],4)}", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+5
|
||||||
|
load+=1
|
||||||
|
line+=10
|
||||||
|
# MEMORY USAGE
|
||||||
|
memories=psutil.virtual_memory()
|
||||||
|
memtotal=round(memories.total/1024/1024/1024, 2)
|
||||||
|
memavailable=round(memories.available/1024/1024/1024, 2)
|
||||||
|
drawer.text((column[0], line), f"Total: {memtotal}G", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+5
|
||||||
|
drawer.text((column[0], line), f"Avail: {memavailable}G", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+15
|
||||||
|
temp=round(psutil.sensors_temperatures()['cpu_thermal'][0].current, 1)
|
||||||
|
drawer.text((column[0], line),f"Temp: {temp}°C", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+15
|
||||||
|
# Hello, world!
|
||||||
|
drawer.text((column[0],line), "Hello, world!", font=FONT['BIG'], fill=WHITE)
|
||||||
|
|
||||||
|
def texttest():
|
||||||
|
line=5
|
||||||
|
spacing=3
|
||||||
|
smolspacing=3
|
||||||
|
indent=0
|
||||||
|
linebreak=15
|
||||||
|
drawer.text((indent, line), "{:^26}".format("--Letter spacing test--"), font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+linebreak
|
||||||
|
drawer.text((indent, line), f"font: {FONT['BIG'].getname()[0]} {FONTSIZE['BIG']}px", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+spacing
|
||||||
|
drawer.text((indent, line), "123456789012345678901234567890", font=FONT['MED'], fill=RED)
|
||||||
|
line+=FONTSIZE['BIG']+spacing
|
||||||
|
drawer.text((indent, line), "abcdefghijklmnopqrstuvwxyz", font=FONT['BIG'], fill=ORANGE)
|
||||||
|
line+=FONTSIZE['BIG']+spacing
|
||||||
|
drawer.text((indent, line), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", font=FONT['BIG'], fill=YELLOW)
|
||||||
|
line+=FONTSIZE['BIG']+spacing
|
||||||
|
drawer.text((indent, line), f"line spacing: {spacing}", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+linebreak
|
||||||
|
|
||||||
|
drawer.text((indent, line), f"smol font: {FONT['SMOL'].getname()[0]} {FONTSIZE['SMOL']}px", font=FONT["SMOL"], fill=WHITE)
|
||||||
|
line+=FONTSIZE["SMOL"]+smolspacing
|
||||||
|
drawer.text((indent, line), "1234567890123456789012345678901234567890", font=FONT["SMOL"], fill=GREEN)
|
||||||
|
line+=FONTSIZE["SMOL"]+smolspacing
|
||||||
|
drawer.text((indent, line), "abcdefghijklmnopqrstuvwxyz", font=FONT["SMOL"], fill=BLUE)
|
||||||
|
line+=FONTSIZE["SMOL"]+smolspacing
|
||||||
|
drawer.text((indent, line), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", font=FONT["SMOL"], fill=PURPLE)
|
||||||
|
line+=FONTSIZE["SMOL"]+smolspacing
|
||||||
|
drawer.text((indent, line), f"smol line spacing: {smolspacing}", font=FONT["SMOL"], fill=WHITE)
|
||||||
|
line+=FONTSIZE["SMOL"]+linebreak
|
||||||
|
|
||||||
|
drawer.text((indent, line), f"left indent: {indent}", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+spacing
|
||||||
|
|
||||||
|
def testguistats():
|
||||||
|
line=5
|
||||||
|
footerline=240-FONTSIZE['BIG']-5
|
||||||
|
barheight=10
|
||||||
|
barwidth=220
|
||||||
|
column=(5,120)
|
||||||
|
# CPU PERCENTS
|
||||||
|
line = writewords("CPU Usage", indent=5, line=line, font="big")
|
||||||
|
cpu_use=psutil.cpu_percent(interval=1,percpu=True)
|
||||||
|
core=0
|
||||||
|
while core < len(cpu_use):
|
||||||
|
rect_start=(column[0], line)
|
||||||
|
rect_end=(5+round(barwidth/100*cpu_use[core],0), line+barheight)
|
||||||
|
rect_outline=(5+barwidth,line+barheight)
|
||||||
|
drawer.rectangle([rect_start, rect_outline],fill=None,outline=GREEN)
|
||||||
|
drawer.rectangle([rect_start, rect_end],fill=GREEN,outline=None)
|
||||||
|
line+=barheight
|
||||||
|
core+=1
|
||||||
|
line+=FONTSIZE['BIG']
|
||||||
|
# CPU LOAD
|
||||||
|
# cpu_loadsss=psutil.getloadavg()
|
||||||
|
# load=0
|
||||||
|
# line+=10
|
||||||
|
# while load < len(cpu_loadsss):
|
||||||
|
# drawer.text((column[0], line), f"{CPULOADLABELS[load]} load: {round(cpu_loadsss[load],4)}", font=FONT['BIG'], fill=WHITE)
|
||||||
|
# line+=FONTSIZE['BIG']+5
|
||||||
|
# load+=1
|
||||||
|
# line+=10
|
||||||
|
# MEMORY USAGE
|
||||||
|
line = writewords("Memory Usage", line=line, indent=5)
|
||||||
|
memories=psutil.virtual_memory()
|
||||||
|
memtotal=round(memories.total/1024/1024/1024, 2)
|
||||||
|
memavailable=round(memories.available/1024/1024/1024, 2)
|
||||||
|
rect_start=(column[0], line)
|
||||||
|
rect_end=(5+round(barwidth/100*memories.percent,0), line+barheight)
|
||||||
|
rect_outline=(5+barwidth,line+barheight)
|
||||||
|
drawer.rectangle([rect_start, rect_outline],fill=None,outline=GREEN)
|
||||||
|
drawer.rectangle([rect_start, rect_end],fill=GREEN,outline=None)
|
||||||
|
line+=barheight+FONTSIZE['BIG']
|
||||||
|
# From https://github.com/kubesail/pibox-os/blob/main/pwm-fan/pi_fan_hwpwm.c
|
||||||
|
# Fan off = 60 / on = 65 / high = 80
|
||||||
|
temp=round(psutil.sensors_temperatures()['cpu_thermal'][0].current, 1)
|
||||||
|
tempcolor=GREEN if temp <= 66 else ORANGE if temp <= 80 else RED
|
||||||
|
writewords(f"Temp: {temp}°C", line=3, color=tempcolor, alignment='RIGHT')
|
||||||
|
|
||||||
|
# FOOTER
|
||||||
|
hostname=socket.gethostname()
|
||||||
|
# Trusts that IPv4 is in the address list before IPv6
|
||||||
|
eth=psutil.net_if_addrs()[NET_ADAPTER][0]
|
||||||
|
ip=eth.address
|
||||||
|
cidr=SUBNET_CIDR_MAP[eth.netmask]
|
||||||
|
writewords(f"{ip}/{cidr}", line=footerline, indent=5)
|
||||||
|
writewords(f"{hostname}", line=footerline, alignment="RIGHT")
|
||||||
|
return
|
||||||
|
line+=barheight
|
||||||
|
drawer.text((column[0], line), f"Total: {memtotal}G", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+5
|
||||||
|
drawer.text((column[0], line), f"Avail: {memavailable}G", font=FONT['BIG'], fill=WHITE)
|
||||||
|
line+=FONTSIZE['BIG']+5
|
||||||
|
# Hello, world!
|
||||||
|
drawer.text((column[0],line+24), "Hello, world!", font=FONT['BIG'], fill=WHITE)
|
||||||
|
return
|
||||||
|
|
||||||
|
def guistats():
|
||||||
|
headerline=round(FONTSIZE['BIG']*1.7,0)
|
||||||
|
line=headerline+20
|
||||||
|
padding=20
|
||||||
|
barindent=75
|
||||||
|
linepadding=10
|
||||||
|
diskheight=15
|
||||||
|
#nowstr=datetime.datetime.now().strftime("%Y-%m-%d %I:%M%p").lower()
|
||||||
|
nowstr=datetime.datetime.now().strftime("%b%d %I:%M%p")
|
||||||
|
timestr=datetime.datetime.now().strftime("%H:%M")
|
||||||
|
datestr=datetime.datetime.now().strftime("%b %d ")
|
||||||
|
|
||||||
|
## Header
|
||||||
|
# From https://github.com/kubesail/pibox-os/blob/main/pwm-fan/pi_fan_hwpwm.c
|
||||||
|
# Fan off = 60 / on = 65 / high = 80
|
||||||
|
temp=round(psutil.sensors_temperatures()['cpu_thermal'][0].current, 1)
|
||||||
|
tempcolor=GREEN if temp <= 66 else ORANGE if temp <= 80 else RED
|
||||||
|
writewords(f"{temp}°C", line=4, color=tempcolor, alignment='LEFT', indent=10)
|
||||||
|
writewords(f"{timestr}", line=4, indent=5, alignment='CENTER')
|
||||||
|
writewords(f"{datestr}", line=4, indent=5, alignment='RIGHT')
|
||||||
|
drawer.line(((linepadding, headerline), (SCREENWIDTH-linepadding, headerline)), fill=WHITE)
|
||||||
|
|
||||||
|
## Body
|
||||||
|
# CPU USAGE
|
||||||
|
writewords("CPU", line=line,font="THICC", spacing=5, indent=padding)
|
||||||
|
cpupercent=psutil.cpu_percent(interval=1)/100
|
||||||
|
cpucolor=GREEN if cpupercent <= .70 else ORANGE if cpupercent <= .85 else RED
|
||||||
|
corecount=psutil.cpu_count()
|
||||||
|
fiveminload=psutil.getloadavg()[1]
|
||||||
|
fiveminperc=round(fiveminload/corecount,2)
|
||||||
|
fiveminperc=.5
|
||||||
|
fivemincolor=DARK_GREEN if fiveminperc <= .70 else DARK_YELLOW if fiveminperc <= .85 else DARK_RED
|
||||||
|
drawbar(top=line, indent=barindent, right=SCREENWIDTH-padding, corners=(True, True, False, False), color=fivemincolor, percent=fiveminperc)
|
||||||
|
line=drawbar(top=line, indent=barindent, right=SCREENWIDTH-padding, corners=(True, True, False, False), color=cpucolor, percent=cpupercent)
|
||||||
|
writewords(f"{int(cpupercent*100)}%", indent=barindent, right=SCREENWIDTH-padding,line=line-FONTSIZE['BIG']-8,alignment='CENTER',outline=BLACK)
|
||||||
|
|
||||||
|
# MEMORY USAGE
|
||||||
|
memories=psutil.virtual_memory()
|
||||||
|
mempercent=round(memories.used/memories.total,2)
|
||||||
|
memcolor=AQUA if mempercent <= .70 else ORANGE if mempercent <= .85 else RED
|
||||||
|
memtotal=round(memories.total/1024/1024/1024, 2)
|
||||||
|
memused=round(memories.used/1024/1024/1024, 2)
|
||||||
|
memavailable=round(memories.available/1024/1024/1024, 2)
|
||||||
|
writewords("RAM", line=line,font="THICC", spacing=5, indent=padding)
|
||||||
|
line=drawbar(top=line, indent=barindent, right=SCREENWIDTH-padding, corners=(False, False, True, True), color=memcolor, percent=mempercent)
|
||||||
|
writewords(f"{memused}G/{memtotal}G", indent=barindent, right=SCREENWIDTH-padding, line=line-23,alignment='CENTER',outline=BLACK, font='big')
|
||||||
|
# line=drawbar(top=line, right=240-padding, corners=(False, False, True, True), color=memcolor, percent=mempercent)
|
||||||
|
# writewords(f"{memused}G/{memtotal}G", line=line-27,alignment='CENTER',outline=BLACK, font='big')
|
||||||
|
# line=writewords("Memory", line=line,font="THICC", spacing=5, indent=padding)
|
||||||
|
line+=25
|
||||||
|
|
||||||
|
# DISK USAGE
|
||||||
|
rawdisklist=psutil.disk_partitions(all=False)
|
||||||
|
disklist=[]
|
||||||
|
diskcolors=[BLUE,PINK,WHITE]
|
||||||
|
for disk in rawdisklist:
|
||||||
|
#if 'boot' in disk.mountpoint:
|
||||||
|
# continue
|
||||||
|
disklist.append(disk)
|
||||||
|
#print(f'found disk {disk.device}')
|
||||||
|
if len(disklist) == 1:
|
||||||
|
#diskname=disklist[0].device[substring_indicies(disklist[0].device, '/')[-1]+1:]
|
||||||
|
diskname=disklist[0].mountpoint
|
||||||
|
writewords(f"{diskname}", line, indent=padding, font='BIG')
|
||||||
|
drawbar(top=line,indent=SCREENWIDTH/2,height=diskheight, right=SCREENWIDTH-padding, color=diskcolors[0],percent=psutil.disk_usage(disklist[0].mountpoint).percent/100)
|
||||||
|
else:
|
||||||
|
diskindex=0
|
||||||
|
for disk in disklist:
|
||||||
|
# e.g. /dev/mmcblk0p2 or /dev/mapper/pibox--group-k3s
|
||||||
|
#diskname=disk.device[substring_indicies(disk.device, '/')[-1]+1:]
|
||||||
|
# e.g. / or /mnt/pibox
|
||||||
|
diskname=disk.mountpoint
|
||||||
|
diskusage=psutil.disk_usage(disk.mountpoint).percent/100
|
||||||
|
writewords(f"{diskname}", line=line, indent=padding, font='MED')
|
||||||
|
if disk.device == disklist[0].device:
|
||||||
|
corners=(True, True, False, False)
|
||||||
|
elif disk.device == disklist[-1].device:
|
||||||
|
corners=(False, False, True, True)
|
||||||
|
else:
|
||||||
|
corners=(False, False, False, False)
|
||||||
|
line=drawbar(top=line,indent=SCREENWIDTH/2,height=diskheight, right=SCREENWIDTH-padding, color=diskcolors[diskindex%len(diskcolors)],percent=diskusage, corners=corners)
|
||||||
|
diskindex+=1
|
||||||
|
hostname=socket.gethostname()
|
||||||
|
# Trusts that IPv4 is in the address list before IPv6
|
||||||
|
eth=psutil.net_if_addrs()[NET_ADAPTER][0]
|
||||||
|
ip=eth.address
|
||||||
|
cidr=SUBNET_CIDR_MAP[eth.netmask]
|
||||||
|
footerfont="BIG" if drawer.textlength(f"{ip}/{cidr} {hostname}", FONT['BIG']) <= SCREENWIDTH else "MED"
|
||||||
|
footerline=240-FONTSIZE[footerfont]-5
|
||||||
|
writewords(f"{ip}/{cidr}", line=footerline, indent=5, font=footerfont)
|
||||||
|
writewords(f"{hostname}", line=footerline, alignment="RIGHT", font=footerfont, indent=5)
|
||||||
|
footerlineline=SCREENHEIGHT-round(FONTSIZE[footerfont]*1.8,0)
|
||||||
|
drawer.line(((linepadding, footerlineline), (SCREENWIDTH-linepadding, footerlineline)), fill=WHITE)
|
||||||
|
return
|
||||||
|
|
||||||
|
precheck()
|
||||||
|
#textstats()
|
||||||
|
#testguistats()
|
||||||
|
#texttest()
|
||||||
|
guistats()
|
||||||
|
#sendimage(mystats)
|
||||||
|
mystats.save("stats.png")
|
||||||
|
|
@ -0,0 +1,123 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure img directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
with_items:
|
||||||
|
- "~/bin"
|
||||||
|
- "~/img"
|
||||||
|
- name: Copy image directory
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "img"
|
||||||
|
dest: "~/img"
|
||||||
|
- name: Copy screensaver script
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: screensaver.sh
|
||||||
|
dest: "~/bin/screensaver.sh"
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
# BCM & Fan PWM drivers
|
||||||
|
- name: Clone pibox-os repository
|
||||||
|
ansible.builtin.git:
|
||||||
|
repo: https://github.com/kubesail/pibox-os.git
|
||||||
|
dest: /usr/src/pibox-os
|
||||||
|
clone: true
|
||||||
|
force: true
|
||||||
|
update: true
|
||||||
|
register: piboxos_repo_updated
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Extract bcm2835-1.68.tar.gz
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
creates: /usr/src/pibox-os/pwm-fan/bcm2835-1.68/
|
||||||
|
dest: /usr/src/pibox-os/pwm-fan/
|
||||||
|
remote_src: true
|
||||||
|
src: /usr/src/pibox-os/pwm-fan/bcm2835-1.68.tar.gz
|
||||||
|
when: piboxos_repo_updated.changed
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Configure build env for bcm2835-1.68 fan pwm driver
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: /usr/src/pibox-os/pwm-fan/bcm2835-1.68/
|
||||||
|
cmd: bash ./configure
|
||||||
|
when: piboxos_repo_updated.changed
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Build bcm2835-1.68 driver
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: /usr/src/pibox-os/pwm-fan/bcm2835-1.68/
|
||||||
|
cmd: make
|
||||||
|
when: piboxos_repo_updated.changed
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Install bcm2835-1.68 driver
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: /usr/src/pibox-os/pwm-fan/bcm2835-1.68/
|
||||||
|
cmd: make install
|
||||||
|
when: piboxos_repo_updated.changed
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Build pwm fan driver
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: /usr/src/pibox-os/pwm-fan/
|
||||||
|
cmd: make
|
||||||
|
when: piboxos_repo_updated.changed
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Install pwm fan driver
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: /usr/src/pibox-os/pwm-fan/
|
||||||
|
cmd: make install
|
||||||
|
when: piboxos_repo_updated.changed
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
|
||||||
|
# Pibox framebuffer binary & service
|
||||||
|
- name: Create kubesail directory in /opt
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /opt/kubesail
|
||||||
|
state: directory
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Download pibox-framebuffer binary
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
dest: /opt/kubesail/pibox-framebuffer
|
||||||
|
mode: '755'
|
||||||
|
url: https://github.com/kubesail/pibox-framebuffer/releases/download/v22/pibox-framebuffer
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Download and install pibox-framebuffer service
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
dest: /etc/systemd/system/pibox-framebuffer.service
|
||||||
|
url: https://raw.githubusercontent.com/kubesail/pibox-framebuffer/main/pibox-framebuffer.service
|
||||||
|
register: pibox_framebuffer_downloaded
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Reload systemctl daemon if necessary
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: systemctl daemon-reload
|
||||||
|
when: pibox_framebuffer_downloaded.changed
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Enable SPI via /boot/config.txt edit
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /boot/firmware/config.txt
|
||||||
|
line: "dtparam=spi=on"
|
||||||
|
insertafter: "#dtparam=spi=on"
|
||||||
|
state: present
|
||||||
|
register: boot_config_updated
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Start pibox-framebuffer service if no boot config made
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: pibox-framebuffer
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|
||||||
|
- name: Reboot if SPI updated
|
||||||
|
ansible.builtin.reboot:
|
||||||
|
post_reboot_delay: 10
|
||||||
|
when: boot_config_updated.changed
|
||||||
|
become: true
|
||||||
|
become_method: sudo
|