Add some of of the argon40 scripts
Needed to port...
This commit is contained in:
1
pythonscript/.gitignore
vendored
Normal file
1
pythonscript/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
__pycache__
|
||||||
57
pythonscript/argon-config
Executable file
57
pythonscript/argon-config
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "--------------------------"
|
||||||
|
echo "Argon Configuration Tool"
|
||||||
|
/etc/argon/argon-versioninfo.sh simple
|
||||||
|
echo "--------------------------"
|
||||||
|
get_number () {
|
||||||
|
read curnumber
|
||||||
|
if [ -z "$curnumber" ]
|
||||||
|
then
|
||||||
|
echo "-2"
|
||||||
|
return
|
||||||
|
elif [[ $curnumber =~ ^[+-]?[0-9]+$ ]]
|
||||||
|
then
|
||||||
|
if [ $curnumber -lt 0 ]
|
||||||
|
then
|
||||||
|
echo "-1"
|
||||||
|
return
|
||||||
|
elif [ $curnumber -gt 100 ]
|
||||||
|
then
|
||||||
|
echo "-1"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo $curnumber
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "-1"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mainloopflag=1
|
||||||
|
while [ $mainloopflag -eq 1 ]
|
||||||
|
do
|
||||||
|
echo
|
||||||
|
echo "Choose Option:"
|
||||||
|
echo " 1. Get Battery Status"
|
||||||
|
echo " 2. Dashboard"
|
||||||
|
echo " 3. Uninstall"
|
||||||
|
echo ""
|
||||||
|
echo " 0. Exit"
|
||||||
|
echo -n "Enter Number (0-3):"
|
||||||
|
newmode=$( get_number )
|
||||||
|
if [ $newmode -eq 0 ]
|
||||||
|
then
|
||||||
|
echo "Thank you."
|
||||||
|
mainloopflag=0
|
||||||
|
elif [ $newmode -eq 1 ]
|
||||||
|
then
|
||||||
|
sudo /usr/bin/python3 /etc/argon/argononeupd.py GETBATTERY
|
||||||
|
elif [ $newmode -eq 2 ]
|
||||||
|
then
|
||||||
|
sudo /usr/bin/python3 /etc/argon/argondashboard.py
|
||||||
|
elif [ $newmode -eq 3 ]
|
||||||
|
then
|
||||||
|
/etc/argon/argon-uninstall.sh
|
||||||
|
mainloopflag=0
|
||||||
|
fi
|
||||||
|
done
|
||||||
150
pythonscript/argon-uninstall.sh
Executable file
150
pythonscript/argon-uninstall.sh
Executable file
@@ -0,0 +1,150 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "----------------------"
|
||||||
|
echo " Argon Uninstall Tool"
|
||||||
|
echo "----------------------"
|
||||||
|
echo -n "Press Y to continue:"
|
||||||
|
read -n 1 confirm
|
||||||
|
echo
|
||||||
|
if [ "$confirm" = "y" ]
|
||||||
|
then
|
||||||
|
confirm="Y"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$confirm" != "Y" ]
|
||||||
|
then
|
||||||
|
echo "Cancelled"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
destfoldername=$USERNAME
|
||||||
|
if [ -z "$destfoldername" ]
|
||||||
|
then
|
||||||
|
destfoldername=$USER
|
||||||
|
fi
|
||||||
|
if [ "$destfoldername" = "root" ]
|
||||||
|
then
|
||||||
|
destfoldername=""
|
||||||
|
fi
|
||||||
|
if [ -z "$destfoldername" ]
|
||||||
|
then
|
||||||
|
destfoldername="pi"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
shortcutfile="/home/$destfoldername/Desktop/argonone-config.desktop"
|
||||||
|
if [ -f "$shortcutfile" ]; then
|
||||||
|
sudo rm $shortcutfile
|
||||||
|
if [ -f "/usr/share/pixmaps/ar1config.png" ]; then
|
||||||
|
sudo rm /usr/share/pixmaps/ar1config.png
|
||||||
|
fi
|
||||||
|
if [ -f "/usr/share/pixmaps/argoneon.png" ]; then
|
||||||
|
sudo rm /usr/share/pixmaps/argoneon.png
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
shortcutfile="/home/$destfoldername/Desktop/argononeup.desktop"
|
||||||
|
if [ -f "$shortcutfile" ]; then
|
||||||
|
sudo rm $shortcutfile
|
||||||
|
fi
|
||||||
|
|
||||||
|
INSTALLATIONFOLDER=/etc/argon
|
||||||
|
|
||||||
|
argononefanscript=$INSTALLATIONFOLDER/argononed.py
|
||||||
|
|
||||||
|
if [ -f $argononefanscript ]; then
|
||||||
|
sudo systemctl stop argononed.service
|
||||||
|
sudo systemctl disable argononed.service
|
||||||
|
|
||||||
|
# Turn off the fan
|
||||||
|
/usr/bin/python3 $argononefanscript FANOFF
|
||||||
|
|
||||||
|
# Remove files
|
||||||
|
sudo rm /lib/systemd/system/argononed.service
|
||||||
|
fi
|
||||||
|
|
||||||
|
argononeupscript=$INSTALLATIONFOLDER/argononeupd.py
|
||||||
|
if [ -f $argononeupscript ]; then
|
||||||
|
sudo systemctl stop argononeupd.service
|
||||||
|
sudo systemctl disable argononeupd.service
|
||||||
|
|
||||||
|
# Remove files
|
||||||
|
sudo rm /lib/systemd/system/argononeupd.service
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove RTC if any
|
||||||
|
argoneonrtcscript=$INSTALLATIONFOLDER/argoneond.py
|
||||||
|
if [ -f "$argoneonrtcscript" ]
|
||||||
|
then
|
||||||
|
# Disable Services
|
||||||
|
sudo systemctl stop argoneond.service
|
||||||
|
sudo systemctl disable argoneond.service
|
||||||
|
|
||||||
|
# No need for sudo
|
||||||
|
/usr/bin/python3 $argoneonrtcscript CLEAN
|
||||||
|
/usr/bin/python3 $argoneonrtcscript SHUTDOWN
|
||||||
|
|
||||||
|
# Remove files
|
||||||
|
sudo rm /lib/systemd/system/argoneond.service
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove UPS daemon if any
|
||||||
|
argononeupsscript=$INSTALLATIONFOLDER/argononeupsd.py
|
||||||
|
if [ -f "$argononeupsscript" ]
|
||||||
|
then
|
||||||
|
#sudo rmmod argonbatteryicon
|
||||||
|
# Disable Services
|
||||||
|
sudo systemctl stop argononeupsd.service
|
||||||
|
sudo systemctl disable argononeupsd.service
|
||||||
|
|
||||||
|
sudo systemctl stop argonupsrtcd.service
|
||||||
|
sudo systemctl disable argonupsrtcd.service
|
||||||
|
|
||||||
|
sudo systemctl --global stop argononeupsduser.service
|
||||||
|
sudo systemctl --global disable argononeupsduser.service
|
||||||
|
|
||||||
|
# Remove files
|
||||||
|
sudo rm /lib/systemd/system/argononeupsd.service
|
||||||
|
sudo rm /lib/systemd/system/argonupsrtcd.service
|
||||||
|
sudo rm /etc/systemd/user/argononeupsduser.service
|
||||||
|
|
||||||
|
find "/home" -maxdepth 1 -type d | while read line; do
|
||||||
|
shortcutfile="$line/Desktop/argonone-ups.desktop"
|
||||||
|
if [ -f "$shortcutfile" ]; then
|
||||||
|
sudo rm $shortcutfile
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "/usr/bin/argon-config" ]
|
||||||
|
then
|
||||||
|
sudo rm /usr/bin/argon-config
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "/usr/bin/argonone-config" ]
|
||||||
|
then
|
||||||
|
sudo rm /usr/bin/argonone-config
|
||||||
|
sudo rm /usr/bin/argonone-uninstall
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ -f "/usr/bin/argonone-ir" ]
|
||||||
|
then
|
||||||
|
sudo rm /usr/bin/argonone-ir
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delete config files
|
||||||
|
for configfile in argonunits argononed argononed-hdd argoneonrtc argoneonoled argonupsrtc argononeupd
|
||||||
|
do
|
||||||
|
if [ -f "/etc/${configfile}.conf" ]
|
||||||
|
then
|
||||||
|
sudo rm "/etc/${configfile}.conf"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sudo rm /lib/systemd/system-shutdown/argon-shutdown.sh
|
||||||
|
|
||||||
|
sudo rm -R -f $INSTALLATIONFOLDER
|
||||||
|
|
||||||
|
echo "Removed Argon Services."
|
||||||
|
echo "Cleanup will complete after restarting the device."
|
||||||
14
pythonscript/argon-versioninfo.sh
Executable file
14
pythonscript/argon-versioninfo.sh
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
VERSIONINFO="2509004"
|
||||||
|
|
||||||
|
echo "Version $VERSIONINFO"
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
echo
|
||||||
|
echo "We acknowledge the valuable feedback of the following:"
|
||||||
|
echo "ghalfacree, NHHiker"
|
||||||
|
echo
|
||||||
|
echo "Feel free to join the discussions at https://forum.argon40.com"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
0
pythonscript/argon40.png
Normal file
0
pythonscript/argon40.png
Normal file
371
pythonscript/argondashboard.py
Executable file
371
pythonscript/argondashboard.py
Executable file
@@ -0,0 +1,371 @@
|
|||||||
|
#!/bin/python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import signal
|
||||||
|
import curses
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append("/etc/argon/")
|
||||||
|
from argonsysinfo import *
|
||||||
|
from argonregister import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Constants
|
||||||
|
############
|
||||||
|
COLORPAIRID_DEFAULT=1
|
||||||
|
COLORPAIRID_LOGO=2
|
||||||
|
COLORPAIRID_DEFAULTINVERSE=3
|
||||||
|
COLORPAIRID_ALERT=4
|
||||||
|
COLORPAIRID_WARNING=5
|
||||||
|
COLORPAIRID_GOOD=6
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INPUTREFRESHMS=100
|
||||||
|
DISPLAYREFRESHMS=5000
|
||||||
|
UPS_LOGFILE="/dev/shm/upslog.txt"
|
||||||
|
|
||||||
|
|
||||||
|
###################
|
||||||
|
# Display Elements
|
||||||
|
###################
|
||||||
|
|
||||||
|
def displaydatetime(stdscr):
|
||||||
|
try:
|
||||||
|
curtimenow = time.localtime()
|
||||||
|
|
||||||
|
stdscr.addstr(1, 1, time.strftime("%A", curtimenow), curses.color_pair(COLORPAIRID_DEFAULT))
|
||||||
|
stdscr.addstr(2, 1, time.strftime("%b %d,%Y", curtimenow), curses.color_pair(COLORPAIRID_DEFAULT))
|
||||||
|
stdscr.addstr(3, 1, time.strftime("%I:%M%p", curtimenow), curses.color_pair(COLORPAIRID_DEFAULT))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def displayipbattery(stdscr):
|
||||||
|
try:
|
||||||
|
displaytextright(stdscr,1, argonsysinfo_getip()+" ", COLORPAIRID_DEFAULT)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
status = ""
|
||||||
|
level = ""
|
||||||
|
outobj = {}
|
||||||
|
# Load status
|
||||||
|
fp = open(UPS_LOGFILE, "r")
|
||||||
|
logdata = fp.read()
|
||||||
|
alllines = logdata.split("\n")
|
||||||
|
ctr = 0
|
||||||
|
while ctr < len(alllines):
|
||||||
|
tmpval = alllines[ctr].strip()
|
||||||
|
curinfo = tmpval.split(":")
|
||||||
|
if len(curinfo) > 1:
|
||||||
|
tmpattrib = curinfo[0].lower().split(" ")
|
||||||
|
# The rest are assumed to be value
|
||||||
|
outobj[tmpattrib[0]] = tmpval[(len(curinfo[0])+1):].strip()
|
||||||
|
ctr = ctr + 1
|
||||||
|
|
||||||
|
# Map to data
|
||||||
|
try:
|
||||||
|
statuslist = outobj["power"].lower().split(" ")
|
||||||
|
if statuslist[0] == "battery":
|
||||||
|
tmp_charging = 0
|
||||||
|
else:
|
||||||
|
tmp_charging = 1
|
||||||
|
tmp_battery = int(statuslist[1].replace("%",""))
|
||||||
|
|
||||||
|
colorpairidx = COLORPAIRID_DEFAULT
|
||||||
|
if tmp_charging:
|
||||||
|
if tmp_battery > 99:
|
||||||
|
status="Plugged"
|
||||||
|
level=""
|
||||||
|
else:
|
||||||
|
status="Charging"
|
||||||
|
level=str(tmp_battery)+"%"
|
||||||
|
else:
|
||||||
|
status="Battery"
|
||||||
|
level=str(tmp_battery)+"%"
|
||||||
|
if tmp_battery <= 20:
|
||||||
|
colorpairidx = COLORPAIRID_ALERT
|
||||||
|
elif tmp_battery <= 50:
|
||||||
|
colorpairidx = COLORPAIRID_WARNING
|
||||||
|
else:
|
||||||
|
colorpairidx = COLORPAIRID_GOOD
|
||||||
|
|
||||||
|
displaytextright(stdscr,2, status+" ", colorpairidx)
|
||||||
|
displaytextright(stdscr,3, level+" ", colorpairidx)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def displayramcpu(stdscr, refcpu, rowstart, colstart):
|
||||||
|
curusage_b = argonsysinfo_getcpuusagesnapshot()
|
||||||
|
try:
|
||||||
|
outputlist = []
|
||||||
|
tmpraminfo = argonsysinfo_getram()
|
||||||
|
outputlist.append({"title": "ram ", "value": tmpraminfo[1]+" "+tmpraminfo[0]+" Free"})
|
||||||
|
|
||||||
|
for cpuname in refcpu:
|
||||||
|
if cpuname == "cpu":
|
||||||
|
continue
|
||||||
|
if refcpu[cpuname]["total"] == curusage_b[cpuname]["total"]:
|
||||||
|
outputlist.append({"title": cpuname, "value": "Loading"})
|
||||||
|
else:
|
||||||
|
total = curusage_b[cpuname]["total"]-refcpu[cpuname]["total"]
|
||||||
|
idle = curusage_b[cpuname]["idle"]-refcpu[cpuname]["idle"]
|
||||||
|
outputlist.append({"title": cpuname, "value": str(int(100*(total-idle)/(total)))+"% Used"})
|
||||||
|
displaytitlevaluelist(stdscr, rowstart, colstart, outputlist)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return curusage_b
|
||||||
|
|
||||||
|
|
||||||
|
def displaytempfan(stdscr, rowstart, colstart):
|
||||||
|
try:
|
||||||
|
outputlist = []
|
||||||
|
try:
|
||||||
|
if busobj is not None:
|
||||||
|
fanspeed = argonregister_getfanspeed(busobj)
|
||||||
|
fanspeedstr = "Off"
|
||||||
|
if fanspeed > 0:
|
||||||
|
fanspeedstr = str(fanspeed)+"%"
|
||||||
|
outputlist.append({"title": "Fan ", "value": fanspeedstr})
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
# Todo load from config
|
||||||
|
temperature = "C"
|
||||||
|
hddtempctr = 0
|
||||||
|
maxcval = 0
|
||||||
|
mincval = 200
|
||||||
|
|
||||||
|
|
||||||
|
# Get min/max of hdd temp
|
||||||
|
hddtempobj = argonsysinfo_gethddtemp()
|
||||||
|
for curdev in hddtempobj:
|
||||||
|
if hddtempobj[curdev] < mincval:
|
||||||
|
mincval = hddtempobj[curdev]
|
||||||
|
if hddtempobj[curdev] > maxcval:
|
||||||
|
maxcval = hddtempobj[curdev]
|
||||||
|
hddtempctr = hddtempctr + 1
|
||||||
|
|
||||||
|
cpucval = argonsysinfo_getcputemp()
|
||||||
|
if hddtempctr > 0:
|
||||||
|
alltempobj = {"cpu": cpucval,"hdd min": mincval, "hdd max": maxcval}
|
||||||
|
# Update max C val to CPU Temp if necessary
|
||||||
|
if maxcval < cpucval:
|
||||||
|
maxcval = cpucval
|
||||||
|
|
||||||
|
displayrowht = 8
|
||||||
|
displayrow = 8
|
||||||
|
for curdev in alltempobj:
|
||||||
|
if temperature == "C":
|
||||||
|
# Celsius
|
||||||
|
tmpstr = str(alltempobj[curdev])
|
||||||
|
if len(tmpstr) > 4:
|
||||||
|
tmpstr = tmpstr[0:4]
|
||||||
|
else:
|
||||||
|
# Fahrenheit
|
||||||
|
tmpstr = str(32+9*(alltempobj[curdev])/5)
|
||||||
|
if len(tmpstr) > 5:
|
||||||
|
tmpstr = tmpstr[0:5]
|
||||||
|
if len(curdev) <= 3:
|
||||||
|
outputlist.append({"title": curdev.upper(), "value": tmpstr +temperature})
|
||||||
|
else:
|
||||||
|
outputlist.append({"title": curdev.upper(), "value": tmpstr +temperature})
|
||||||
|
else:
|
||||||
|
maxcval = cpucval
|
||||||
|
if temperature == "C":
|
||||||
|
# Celsius
|
||||||
|
tmpstr = str(cpucval)
|
||||||
|
if len(tmpstr) > 4:
|
||||||
|
tmpstr = tmpstr[0:4]
|
||||||
|
else:
|
||||||
|
# Fahrenheit
|
||||||
|
tmpstr = str(32+9*(cpucval)/5)
|
||||||
|
if len(tmpstr) > 5:
|
||||||
|
tmpstr = tmpstr[0:5]
|
||||||
|
|
||||||
|
outputlist.append({"title": "Temp", "value": tmpstr +temperature})
|
||||||
|
displaytitlevaluelist(stdscr, rowstart, colstart, outputlist)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def displaystorage(stdscr, rowstart, colstart):
|
||||||
|
try:
|
||||||
|
outputlist = []
|
||||||
|
tmpobj = argonsysinfo_listhddusage()
|
||||||
|
for curdev in tmpobj:
|
||||||
|
outputlist.append({"title": curdev, "value": argonsysinfo_kbstr(tmpobj[curdev]['total'])+ " "+ str(int(100*tmpobj[curdev]['used']/tmpobj[curdev]['total']))+"% Used" })
|
||||||
|
displaytitlevaluelist(stdscr, rowstart, colstart, outputlist)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Helpers
|
||||||
|
##################
|
||||||
|
|
||||||
|
# Initialize I2C Bus
|
||||||
|
bus = argonregister_initializebusobj()
|
||||||
|
|
||||||
|
def handle_resize(signum, frame):
|
||||||
|
# TODO: Not working?
|
||||||
|
curses.update_lines_cols()
|
||||||
|
# Ideally redraw here
|
||||||
|
|
||||||
|
def displaytitlevaluelist(stdscr, rowstart, leftoffset, curlist):
|
||||||
|
rowidx = rowstart
|
||||||
|
while rowidx < curses.LINES and len(curlist) > 0:
|
||||||
|
curline = ""
|
||||||
|
tmpitem = curlist.pop(0)
|
||||||
|
curline = tmpitem["title"]+": "+str(tmpitem["value"])
|
||||||
|
|
||||||
|
stdscr.addstr(rowidx, leftoffset, curline)
|
||||||
|
rowidx = rowidx + 1
|
||||||
|
|
||||||
|
|
||||||
|
def displaytextcentered(stdscr, rownum, strval, colorpairidx = COLORPAIRID_DEFAULT):
|
||||||
|
leftoffset = 0
|
||||||
|
numchars = len(strval)
|
||||||
|
if numchars < 1:
|
||||||
|
return
|
||||||
|
elif (numchars > curses.COLS):
|
||||||
|
leftoffset = 0
|
||||||
|
strval = strval[0:curses.COLS]
|
||||||
|
else:
|
||||||
|
leftoffset = (curses.COLS - numchars)>>1
|
||||||
|
|
||||||
|
stdscr.addstr(rownum, leftoffset, strval, curses.color_pair(colorpairidx))
|
||||||
|
|
||||||
|
|
||||||
|
def displaytextright(stdscr, rownum, strval, colorpairidx = COLORPAIRID_DEFAULT):
|
||||||
|
leftoffset = 0
|
||||||
|
numchars = len(strval)
|
||||||
|
if numchars < 1:
|
||||||
|
return
|
||||||
|
elif (numchars > curses.COLS):
|
||||||
|
leftoffset = 0
|
||||||
|
strval = strval[0:curses.COLS]
|
||||||
|
else:
|
||||||
|
leftoffset = curses.COLS - numchars
|
||||||
|
|
||||||
|
stdscr.addstr(rownum, leftoffset, strval, curses.color_pair(colorpairidx))
|
||||||
|
|
||||||
|
|
||||||
|
def displaylinebreak(stdscr, rownum, colorpairidx = COLORPAIRID_DEFAULTINVERSE):
|
||||||
|
strval = " "
|
||||||
|
while len(strval) < curses.COLS:
|
||||||
|
strval = strval + " "
|
||||||
|
stdscr.addstr(rownum, 0, strval, curses.color_pair(colorpairidx))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Main Loop
|
||||||
|
##################
|
||||||
|
|
||||||
|
def mainloop(stdscr):
|
||||||
|
try:
|
||||||
|
# Set up signal handler
|
||||||
|
signal.signal(signal.SIGWINCH, handle_resize)
|
||||||
|
|
||||||
|
maxloopctr = int(DISPLAYREFRESHMS/INPUTREFRESHMS)
|
||||||
|
sleepsecs = INPUTREFRESHMS/1000
|
||||||
|
|
||||||
|
loopctr = maxloopctr
|
||||||
|
loopmode = True
|
||||||
|
|
||||||
|
stdscr = curses.initscr()
|
||||||
|
|
||||||
|
# Turn off echoing of keys, and enter cbreak mode,
|
||||||
|
# where no buffering is performed on keyboard input
|
||||||
|
curses.noecho()
|
||||||
|
curses.cbreak()
|
||||||
|
curses.curs_set(0)
|
||||||
|
curses.start_color()
|
||||||
|
|
||||||
|
#curses.COLOR_BLACK
|
||||||
|
#curses.COLOR_BLUE
|
||||||
|
#curses.COLOR_CYAN
|
||||||
|
#curses.COLOR_GREEN
|
||||||
|
#curses.COLOR_MAGENTA
|
||||||
|
#curses.COLOR_RED
|
||||||
|
#curses.COLOR_WHITE
|
||||||
|
#curses.COLOR_YELLOW
|
||||||
|
|
||||||
|
curses.init_pair(COLORPAIRID_DEFAULT, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
||||||
|
curses.init_pair(COLORPAIRID_LOGO, curses.COLOR_WHITE, curses.COLOR_RED)
|
||||||
|
curses.init_pair(COLORPAIRID_DEFAULTINVERSE, curses.COLOR_BLACK, curses.COLOR_WHITE)
|
||||||
|
curses.init_pair(COLORPAIRID_ALERT, curses.COLOR_RED, curses.COLOR_BLACK)
|
||||||
|
curses.init_pair(COLORPAIRID_WARNING, curses.COLOR_YELLOW, curses.COLOR_BLACK)
|
||||||
|
curses.init_pair(COLORPAIRID_GOOD, curses.COLOR_GREEN, curses.COLOR_BLACK)
|
||||||
|
|
||||||
|
stdscr.nodelay(True)
|
||||||
|
|
||||||
|
refcpu = argonsysinfo_getcpuusagesnapshot()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
key = stdscr.getch()
|
||||||
|
# if key == ord('x') or key == ord('X'):
|
||||||
|
# Any key
|
||||||
|
if key > 0:
|
||||||
|
break
|
||||||
|
except curses.error:
|
||||||
|
# No key was pressed
|
||||||
|
pass
|
||||||
|
|
||||||
|
loopctr = loopctr + 1
|
||||||
|
if loopctr >= maxloopctr:
|
||||||
|
loopctr = 0
|
||||||
|
# Screen refresh loop
|
||||||
|
# Clear screen
|
||||||
|
stdscr.clear()
|
||||||
|
|
||||||
|
displaytextcentered(stdscr, 0, " ", COLORPAIRID_LOGO)
|
||||||
|
displaytextcentered(stdscr, 1, " Argon40 Dashboard ", COLORPAIRID_LOGO)
|
||||||
|
displaytextcentered(stdscr, 2, " ", COLORPAIRID_LOGO)
|
||||||
|
displaytextcentered(stdscr, 3, "Press any key to close")
|
||||||
|
displaylinebreak(stdscr, 5)
|
||||||
|
|
||||||
|
# Display Elements
|
||||||
|
displaydatetime(stdscr)
|
||||||
|
displayipbattery(stdscr)
|
||||||
|
|
||||||
|
# Data Columns
|
||||||
|
rowstart = 7
|
||||||
|
colstart = 20
|
||||||
|
refcpu = displayramcpu(stdscr, refcpu, rowstart, colstart)
|
||||||
|
displaystorage(stdscr, rowstart, colstart+30)
|
||||||
|
displaytempfan(stdscr, rowstart, colstart+60)
|
||||||
|
|
||||||
|
# Main refresh even
|
||||||
|
stdscr.refresh()
|
||||||
|
|
||||||
|
time.sleep(sleepsecs)
|
||||||
|
|
||||||
|
except Exception as initerr:
|
||||||
|
pass
|
||||||
|
|
||||||
|
##########
|
||||||
|
# Cleanup
|
||||||
|
##########
|
||||||
|
|
||||||
|
try:
|
||||||
|
curses.curs_set(1)
|
||||||
|
curses.echo()
|
||||||
|
curses.nocbreak()
|
||||||
|
curses.endwin()
|
||||||
|
except Exception as closeerr:
|
||||||
|
pass
|
||||||
|
|
||||||
|
curses.wrapper(mainloop)
|
||||||
568
pythonscript/argononeup-eepromconfig.py
Executable file
568
pythonscript/argononeup-eepromconfig.py
Executable file
@@ -0,0 +1,568 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Based on /usr/bin/rpi-eeprom-config of bookworm
|
||||||
|
"""
|
||||||
|
rpi-eeprom-config
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import atexit
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import string
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
VALID_IMAGE_SIZES = [512 * 1024, 2 * 1024 * 1024]
|
||||||
|
|
||||||
|
BOOTCONF_TXT = 'bootconf.txt'
|
||||||
|
BOOTCONF_SIG = 'bootconf.sig'
|
||||||
|
PUBKEY_BIN = 'pubkey.bin'
|
||||||
|
|
||||||
|
# Each section starts with a magic number followed by a 32 bit offset to the
|
||||||
|
# next section (big-endian).
|
||||||
|
# The number, order and size of the sections depends on the bootloader version
|
||||||
|
# but the following mask can be used to test for section headers and skip
|
||||||
|
# unknown data.
|
||||||
|
#
|
||||||
|
# The last 4KB of the EEPROM image is reserved for internal use by the
|
||||||
|
# bootloader and may be overwritten during the update process.
|
||||||
|
MAGIC = 0x55aaf00f
|
||||||
|
PAD_MAGIC = 0x55aafeef
|
||||||
|
MAGIC_MASK = 0xfffff00f
|
||||||
|
FILE_MAGIC = 0x55aaf11f # id for modifiable files
|
||||||
|
FILE_HDR_LEN = 20
|
||||||
|
FILENAME_LEN = 12
|
||||||
|
TEMP_DIR = None
|
||||||
|
|
||||||
|
# Modifiable files are stored in a single 4K erasable sector.
|
||||||
|
# The max content 4076 bytes because of the file header.
|
||||||
|
ERASE_ALIGN_SIZE = 4096
|
||||||
|
MAX_FILE_SIZE = ERASE_ALIGN_SIZE - FILE_HDR_LEN
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
# BEGIN: Argon40 added methods
|
||||||
|
def argon_rpisupported():
|
||||||
|
# bcm2711 = pi4, bcm2712 = pi5
|
||||||
|
return rpi5()
|
||||||
|
|
||||||
|
def argon_edit_config():
|
||||||
|
# modified/stripped version of edit_config
|
||||||
|
|
||||||
|
config_src = ''
|
||||||
|
# If there is a pending update then use the configuration from
|
||||||
|
# that in order to support incremental updates. Otherwise,
|
||||||
|
# use the current EEPROM configuration.
|
||||||
|
bootfs = shell_cmd(['rpi-eeprom-update', '-b']).rstrip()
|
||||||
|
pending = os.path.join(bootfs, 'pieeprom.upd')
|
||||||
|
if os.path.exists(pending):
|
||||||
|
config_src = pending
|
||||||
|
image = BootloaderImage(pending)
|
||||||
|
current_config = image.get_file(BOOTCONF_TXT).decode('utf-8')
|
||||||
|
else:
|
||||||
|
current_config, config_src = read_current_config()
|
||||||
|
|
||||||
|
# Add PSU Mas Current etc if not yet set
|
||||||
|
foundnewsetting = 0
|
||||||
|
addsetting="\nPSU_MAX_CURRENT=5000"
|
||||||
|
current_config_lines = current_config.splitlines()
|
||||||
|
new_config = current_config
|
||||||
|
lineidx = 0
|
||||||
|
while lineidx < len(current_config_lines):
|
||||||
|
current_config_pair = current_config_lines[lineidx].split("=")
|
||||||
|
newsetting = ""
|
||||||
|
if current_config_pair[0] == "PSU_MAX_CURRENT":
|
||||||
|
newsetting = "PSU_MAX_CURRENT=5000"
|
||||||
|
|
||||||
|
if newsetting != "":
|
||||||
|
addsetting = addsetting.replace("\n"+newsetting,"",1)
|
||||||
|
if current_config_lines[lineidx] != newsetting:
|
||||||
|
foundnewsetting = foundnewsetting + 1
|
||||||
|
new_config = new_config.replace(current_config_lines[lineidx], newsetting, 1)
|
||||||
|
|
||||||
|
lineidx = lineidx + 1
|
||||||
|
|
||||||
|
if addsetting != "":
|
||||||
|
# Append additional settings after [all]
|
||||||
|
new_config = new_config.replace("[all]", "[all]"+addsetting, 1)
|
||||||
|
foundnewsetting = foundnewsetting + 1
|
||||||
|
|
||||||
|
if foundnewsetting == 0:
|
||||||
|
# Already configured
|
||||||
|
print("EEPROM settings up to date")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Skipped editor and write new config to temp file
|
||||||
|
create_tempdir()
|
||||||
|
tmp_conf = os.path.join(TEMP_DIR, 'boot.conf')
|
||||||
|
out = open(tmp_conf, 'w')
|
||||||
|
out.write(new_config)
|
||||||
|
out.close()
|
||||||
|
|
||||||
|
# Apply updates
|
||||||
|
|
||||||
|
apply_update(tmp_conf, None, config_src)
|
||||||
|
|
||||||
|
# END: Argon40 added methods
|
||||||
|
|
||||||
|
|
||||||
|
def debug(s):
|
||||||
|
if DEBUG:
|
||||||
|
sys.stderr.write(s + '\n')
|
||||||
|
|
||||||
|
|
||||||
|
def rpi4():
|
||||||
|
compatible_path = "/sys/firmware/devicetree/base/compatible"
|
||||||
|
if os.path.exists(compatible_path):
|
||||||
|
with open(compatible_path, "rb") as f:
|
||||||
|
compatible = f.read().decode('utf-8')
|
||||||
|
if "bcm2711" in compatible:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def rpi5():
|
||||||
|
compatible_path = "/sys/firmware/devicetree/base/compatible"
|
||||||
|
if os.path.exists(compatible_path):
|
||||||
|
with open(compatible_path, "rb") as f:
|
||||||
|
compatible = f.read().decode('utf-8')
|
||||||
|
if "bcm2712" in compatible:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def exit_handler():
|
||||||
|
"""
|
||||||
|
Delete any temporary files.
|
||||||
|
"""
|
||||||
|
if TEMP_DIR is not None and os.path.exists(TEMP_DIR):
|
||||||
|
tmp_image = os.path.join(TEMP_DIR, 'pieeprom.upd')
|
||||||
|
if os.path.exists(tmp_image):
|
||||||
|
os.remove(tmp_image)
|
||||||
|
tmp_conf = os.path.join(TEMP_DIR, 'boot.conf')
|
||||||
|
if os.path.exists(tmp_conf):
|
||||||
|
os.remove(tmp_conf)
|
||||||
|
os.rmdir(TEMP_DIR)
|
||||||
|
|
||||||
|
def create_tempdir():
|
||||||
|
global TEMP_DIR
|
||||||
|
if TEMP_DIR is None:
|
||||||
|
TEMP_DIR = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
def pemtobin(infile):
|
||||||
|
"""
|
||||||
|
Converts an RSA public key into the format expected by the bootloader.
|
||||||
|
"""
|
||||||
|
# Import the package here to make this a weak dependency.
|
||||||
|
from Cryptodome.PublicKey import RSA
|
||||||
|
|
||||||
|
arr = bytearray()
|
||||||
|
f = open(infile,'r')
|
||||||
|
key = RSA.importKey(f.read())
|
||||||
|
|
||||||
|
if key.size_in_bits() != 2048:
|
||||||
|
raise Exception("RSA key size must be 2048")
|
||||||
|
|
||||||
|
# Export N and E in little endian format
|
||||||
|
arr.extend(key.n.to_bytes(256, byteorder='little'))
|
||||||
|
arr.extend(key.e.to_bytes(8, byteorder='little'))
|
||||||
|
return arr
|
||||||
|
|
||||||
|
def exit_error(msg):
|
||||||
|
"""
|
||||||
|
Trapped a fatal error, output message to stderr and exit with non-zero
|
||||||
|
return code.
|
||||||
|
"""
|
||||||
|
sys.stderr.write("ERROR: %s\n" % msg)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def shell_cmd(args):
|
||||||
|
"""
|
||||||
|
Executes a shell command waits for completion returning STDOUT. If an
|
||||||
|
error occurs then exit and output the subprocess stdout, stderr messages
|
||||||
|
for debug.
|
||||||
|
"""
|
||||||
|
start = time.time()
|
||||||
|
arg_str = ' '.join(args)
|
||||||
|
result = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
|
||||||
|
while time.time() - start < 5:
|
||||||
|
if result.poll() is not None:
|
||||||
|
break
|
||||||
|
|
||||||
|
if result.poll() is None:
|
||||||
|
exit_error("%s timeout" % arg_str)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
exit_error("%s failed: %d\n %s\n %s\n" %
|
||||||
|
(arg_str, result.returncode, result.stdout.read(), result.stderr.read()))
|
||||||
|
else:
|
||||||
|
return result.stdout.read().decode('utf-8')
|
||||||
|
|
||||||
|
def get_latest_eeprom():
|
||||||
|
"""
|
||||||
|
Returns the path of the latest EEPROM image file if it exists.
|
||||||
|
"""
|
||||||
|
latest = shell_cmd(['rpi-eeprom-update', '-l']).rstrip()
|
||||||
|
if not os.path.exists(latest):
|
||||||
|
exit_error("EEPROM image '%s' not found" % latest)
|
||||||
|
return latest
|
||||||
|
|
||||||
|
def apply_update(config, eeprom=None, config_src=None):
|
||||||
|
"""
|
||||||
|
Applies the config file to the latest available EEPROM image and spawns
|
||||||
|
rpi-eeprom-update to schedule the update at the next reboot.
|
||||||
|
"""
|
||||||
|
if eeprom is not None:
|
||||||
|
eeprom_image = eeprom
|
||||||
|
else:
|
||||||
|
eeprom_image = get_latest_eeprom()
|
||||||
|
create_tempdir()
|
||||||
|
|
||||||
|
# Replace the contents of bootconf.txt with the contents of the config file
|
||||||
|
tmp_update = os.path.join(TEMP_DIR, 'pieeprom.upd')
|
||||||
|
image = BootloaderImage(eeprom_image, tmp_update)
|
||||||
|
image.update_file(config, BOOTCONF_TXT)
|
||||||
|
image.write()
|
||||||
|
|
||||||
|
config_str = open(config).read()
|
||||||
|
if config_src is None:
|
||||||
|
config_src = ''
|
||||||
|
sys.stdout.write("Updating bootloader EEPROM\n image: %s\nconfig_src: %s\nconfig: %s\n%s\n%s\n%s\n" %
|
||||||
|
(eeprom_image, config_src, config, '#' * 80, config_str, '#' * 80))
|
||||||
|
|
||||||
|
sys.stdout.write("\n*** To cancel this update run 'sudo rpi-eeprom-update -r' ***\n\n")
|
||||||
|
|
||||||
|
# Ignore APT package checksums so that this doesn't fail when used
|
||||||
|
# with EEPROMs with configs delivered outside of APT.
|
||||||
|
# The checksums are really just a safety check for automatic updates.
|
||||||
|
args = ['rpi-eeprom-update', '-d', '-i', '-f', tmp_update]
|
||||||
|
resp = shell_cmd(args)
|
||||||
|
sys.stdout.write(resp)
|
||||||
|
|
||||||
|
def edit_config(eeprom=None):
|
||||||
|
"""
|
||||||
|
Implements something like 'git commit' for editing EEPROM configs.
|
||||||
|
"""
|
||||||
|
# Default to nano if $EDITOR is not defined.
|
||||||
|
editor = 'nano'
|
||||||
|
if 'EDITOR' in os.environ:
|
||||||
|
editor = os.environ['EDITOR']
|
||||||
|
|
||||||
|
config_src = ''
|
||||||
|
# If there is a pending update then use the configuration from
|
||||||
|
# that in order to support incremental updates. Otherwise,
|
||||||
|
# use the current EEPROM configuration.
|
||||||
|
bootfs = shell_cmd(['rpi-eeprom-update', '-b']).rstrip()
|
||||||
|
pending = os.path.join(bootfs, 'pieeprom.upd')
|
||||||
|
if os.path.exists(pending):
|
||||||
|
config_src = pending
|
||||||
|
image = BootloaderImage(pending)
|
||||||
|
current_config = image.get_file(BOOTCONF_TXT).decode('utf-8')
|
||||||
|
else:
|
||||||
|
current_config, config_src = read_current_config()
|
||||||
|
|
||||||
|
create_tempdir()
|
||||||
|
tmp_conf = os.path.join(TEMP_DIR, 'boot.conf')
|
||||||
|
out = open(tmp_conf, 'w')
|
||||||
|
out.write(current_config)
|
||||||
|
out.close()
|
||||||
|
cmd = "\'%s\' \'%s\'" % (editor, tmp_conf)
|
||||||
|
result = os.system(cmd)
|
||||||
|
if result != 0:
|
||||||
|
exit_error("Aborting update because \'%s\' exited with code %d." % (cmd, result))
|
||||||
|
|
||||||
|
new_config = open(tmp_conf, 'r').read()
|
||||||
|
if len(new_config.splitlines()) < 2:
|
||||||
|
exit_error("Aborting update because \'%s\' appears to be empty." % tmp_conf)
|
||||||
|
apply_update(tmp_conf, eeprom, config_src)
|
||||||
|
|
||||||
|
def read_current_config():
|
||||||
|
"""
|
||||||
|
Reads the configuration used by the current bootloader.
|
||||||
|
"""
|
||||||
|
fw_base = "/sys/firmware/devicetree/base/"
|
||||||
|
nvmem_base = "/sys/bus/nvmem/devices/"
|
||||||
|
|
||||||
|
if os.path.exists(fw_base + "/aliases/blconfig"):
|
||||||
|
with open(fw_base + "/aliases/blconfig", "rb") as f:
|
||||||
|
nvmem_ofnode_path = fw_base + f.read().decode('utf-8')
|
||||||
|
for d in os.listdir(nvmem_base):
|
||||||
|
if os.path.realpath(nvmem_base + d + "/of_node") in os.path.normpath(nvmem_ofnode_path):
|
||||||
|
return (open(nvmem_base + d + "/nvmem", "rb").read().decode('utf-8'), "blconfig device")
|
||||||
|
|
||||||
|
return (shell_cmd(['vcgencmd', 'bootloader_config']), "vcgencmd bootloader_config")
|
||||||
|
|
||||||
|
class ImageSection:
|
||||||
|
def __init__(self, magic, offset, length, filename=''):
|
||||||
|
self.magic = magic
|
||||||
|
self.offset = offset
|
||||||
|
self.length = length
|
||||||
|
self.filename = filename
|
||||||
|
debug("ImageSection %x offset %d length %d %s" % (magic, offset, length, filename))
|
||||||
|
|
||||||
|
class BootloaderImage(object):
|
||||||
|
def __init__(self, filename, output=None):
|
||||||
|
"""
|
||||||
|
Instantiates a Bootloader image writer with a source eeprom (filename)
|
||||||
|
and optionally an output filename.
|
||||||
|
"""
|
||||||
|
self._filename = filename
|
||||||
|
self._sections = []
|
||||||
|
self._image_size = 0
|
||||||
|
try:
|
||||||
|
self._bytes = bytearray(open(filename, 'rb').read())
|
||||||
|
except IOError as err:
|
||||||
|
exit_error("Failed to read \'%s\'\n%s\n" % (filename, str(err)))
|
||||||
|
self._out = None
|
||||||
|
if output is not None:
|
||||||
|
self._out = open(output, 'wb')
|
||||||
|
|
||||||
|
self._image_size = len(self._bytes)
|
||||||
|
if self._image_size not in VALID_IMAGE_SIZES:
|
||||||
|
exit_error("%s: Expected size %d bytes actual size %d bytes" %
|
||||||
|
(filename, self._image_size, len(self._bytes)))
|
||||||
|
self.parse()
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
"""
|
||||||
|
Builds a table of offsets to the different sections in the EEPROM.
|
||||||
|
"""
|
||||||
|
offset = 0
|
||||||
|
magic = 0
|
||||||
|
while offset < self._image_size:
|
||||||
|
magic, length = struct.unpack_from('>LL', self._bytes, offset)
|
||||||
|
if magic == 0x0 or magic == 0xffffffff:
|
||||||
|
break # EOF
|
||||||
|
elif (magic & MAGIC_MASK) != MAGIC:
|
||||||
|
raise Exception('EEPROM is corrupted %x %x %x' % (magic, magic & MAGIC_MASK, MAGIC))
|
||||||
|
|
||||||
|
filename = ''
|
||||||
|
if magic == FILE_MAGIC: # Found a file
|
||||||
|
# Discard trailing null characters used to pad filename
|
||||||
|
filename = self._bytes[offset + 8: offset + FILE_HDR_LEN].decode('utf-8').replace('\0', '')
|
||||||
|
debug("section at %d length %d magic %08x %s" % (offset, length, magic, filename))
|
||||||
|
self._sections.append(ImageSection(magic, offset, length, filename))
|
||||||
|
|
||||||
|
offset += 8 + length # length + type
|
||||||
|
offset = (offset + 7) & ~7
|
||||||
|
|
||||||
|
def find_file(self, filename):
|
||||||
|
"""
|
||||||
|
Returns the offset, length and whether this is the last section in the
|
||||||
|
EEPROM for a modifiable file within the image.
|
||||||
|
"""
|
||||||
|
offset = -1
|
||||||
|
length = -1
|
||||||
|
is_last = False
|
||||||
|
|
||||||
|
next_offset = self._image_size - ERASE_ALIGN_SIZE # Don't create padding inside the bootloader scratch page
|
||||||
|
for i in range(0, len(self._sections)):
|
||||||
|
s = self._sections[i]
|
||||||
|
if s.magic == FILE_MAGIC and s.filename == filename:
|
||||||
|
is_last = (i == len(self._sections) - 1)
|
||||||
|
offset = s.offset
|
||||||
|
length = s.length
|
||||||
|
break
|
||||||
|
|
||||||
|
# Find the start of the next non padding section
|
||||||
|
i += 1
|
||||||
|
while i < len(self._sections):
|
||||||
|
if self._sections[i].magic == PAD_MAGIC:
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
next_offset = self._sections[i].offset
|
||||||
|
break
|
||||||
|
ret = (offset, length, is_last, next_offset)
|
||||||
|
debug('%s offset %d length %d is-last %d next %d' % (filename, ret[0], ret[1], ret[2], ret[3]))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def update(self, src_bytes, dst_filename):
|
||||||
|
"""
|
||||||
|
Replaces a modifiable file with specified byte array.
|
||||||
|
"""
|
||||||
|
hdr_offset, length, is_last, next_offset = self.find_file(dst_filename)
|
||||||
|
update_len = len(src_bytes) + FILE_HDR_LEN
|
||||||
|
|
||||||
|
if hdr_offset + update_len > self._image_size - ERASE_ALIGN_SIZE:
|
||||||
|
raise Exception('No space available - image past EOF.')
|
||||||
|
|
||||||
|
if hdr_offset < 0:
|
||||||
|
raise Exception('Update target %s not found' % dst_filename)
|
||||||
|
|
||||||
|
if hdr_offset + update_len > next_offset:
|
||||||
|
raise Exception('Update %d bytes is larger than section size %d' % (update_len, next_offset - hdr_offset))
|
||||||
|
|
||||||
|
new_len = len(src_bytes) + FILENAME_LEN + 4
|
||||||
|
struct.pack_into('>L', self._bytes, hdr_offset + 4, new_len)
|
||||||
|
struct.pack_into(("%ds" % len(src_bytes)), self._bytes,
|
||||||
|
hdr_offset + 4 + FILE_HDR_LEN, src_bytes)
|
||||||
|
|
||||||
|
# If the new file is smaller than the old file then set any old
|
||||||
|
# data which is now unused to all ones (erase value)
|
||||||
|
pad_start = hdr_offset + 4 + FILE_HDR_LEN + len(src_bytes)
|
||||||
|
|
||||||
|
# Add padding up to 8-byte boundary
|
||||||
|
while pad_start % 8 != 0:
|
||||||
|
struct.pack_into('B', self._bytes, pad_start, 0xff)
|
||||||
|
pad_start += 1
|
||||||
|
|
||||||
|
# Create a padding section unless the padding size is smaller than the
|
||||||
|
# size of a section head. Padding is allowed in the last section but
|
||||||
|
# by convention bootconf.txt is the last section and there's no need to
|
||||||
|
# pad to the end of the sector. This also ensures that the loopback
|
||||||
|
# config read/write tests produce identical binaries.
|
||||||
|
pad_bytes = next_offset - pad_start
|
||||||
|
if pad_bytes > 8 and not is_last:
|
||||||
|
pad_bytes -= 8
|
||||||
|
struct.pack_into('>i', self._bytes, pad_start, PAD_MAGIC)
|
||||||
|
pad_start += 4
|
||||||
|
struct.pack_into('>i', self._bytes, pad_start, pad_bytes)
|
||||||
|
pad_start += 4
|
||||||
|
|
||||||
|
debug("pad %d" % pad_bytes)
|
||||||
|
pad = 0
|
||||||
|
while pad < pad_bytes:
|
||||||
|
struct.pack_into('B', self._bytes, pad_start + pad, 0xff)
|
||||||
|
pad = pad + 1
|
||||||
|
|
||||||
|
def update_key(self, src_pem, dst_filename):
|
||||||
|
"""
|
||||||
|
Replaces the specified public key entry with the public key values extracted
|
||||||
|
from the source PEM file.
|
||||||
|
"""
|
||||||
|
pubkey_bytes = pemtobin(src_pem)
|
||||||
|
self.update(pubkey_bytes, dst_filename)
|
||||||
|
|
||||||
|
def update_file(self, src_filename, dst_filename):
|
||||||
|
"""
|
||||||
|
Replaces the contents of dst_filename in the EEPROM with the contents of src_file.
|
||||||
|
"""
|
||||||
|
src_bytes = open(src_filename, 'rb').read()
|
||||||
|
if len(src_bytes) > MAX_FILE_SIZE:
|
||||||
|
raise Exception("src file %s is too large (%d bytes). The maximum size is %d bytes."
|
||||||
|
% (src_filename, len(src_bytes), MAX_FILE_SIZE))
|
||||||
|
self.update(src_bytes, dst_filename)
|
||||||
|
|
||||||
|
def write(self):
|
||||||
|
"""
|
||||||
|
Writes the updated EEPROM image to stdout or the specified output file.
|
||||||
|
"""
|
||||||
|
if self._out is not None:
|
||||||
|
self._out.write(self._bytes)
|
||||||
|
self._out.close()
|
||||||
|
else:
|
||||||
|
if hasattr(sys.stdout, 'buffer'):
|
||||||
|
sys.stdout.buffer.write(self._bytes)
|
||||||
|
else:
|
||||||
|
sys.stdout.write(self._bytes)
|
||||||
|
|
||||||
|
def get_file(self, filename):
|
||||||
|
hdr_offset, length, is_last, next_offset = self.find_file(filename)
|
||||||
|
offset = hdr_offset + 4 + FILE_HDR_LEN
|
||||||
|
file_bytes = self._bytes[offset:offset+length-FILENAME_LEN-4]
|
||||||
|
return file_bytes
|
||||||
|
|
||||||
|
def extract_files(self):
|
||||||
|
for i in range(0, len(self._sections)):
|
||||||
|
s = self._sections[i]
|
||||||
|
if s.magic == FILE_MAGIC:
|
||||||
|
file_bytes = self.get_file(s.filename)
|
||||||
|
open(s.filename, 'wb').write(file_bytes)
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
config_bytes = self.get_file('bootconf.txt')
|
||||||
|
if self._out is not None:
|
||||||
|
self._out.write(config_bytes)
|
||||||
|
self._out.close()
|
||||||
|
else:
|
||||||
|
if hasattr(sys.stdout, 'buffer'):
|
||||||
|
sys.stdout.buffer.write(config_bytes)
|
||||||
|
else:
|
||||||
|
sys.stdout.write(config_bytes)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
Utility for reading and writing the configuration file in the
|
||||||
|
Raspberry Pi bootloader EEPROM image.
|
||||||
|
"""
|
||||||
|
description = """\
|
||||||
|
Bootloader EEPROM configuration tool for the Raspberry Pi 4 and Raspberry Pi 5.
|
||||||
|
Operating modes:
|
||||||
|
|
||||||
|
1. Outputs the current bootloader configuration to STDOUT if no arguments are
|
||||||
|
specified OR the given output file if --out is specified.
|
||||||
|
|
||||||
|
rpi-eeprom-config [--out boot.conf]
|
||||||
|
|
||||||
|
2. Extracts the configuration file from the given 'eeprom' file and outputs
|
||||||
|
the result to STDOUT or the output file if --output is specified.
|
||||||
|
|
||||||
|
rpi-eeprom-config pieeprom.bin [--out boot.conf]
|
||||||
|
|
||||||
|
3. Writes a new EEPROM image replacing the configuration file with the contents
|
||||||
|
of the file specified by --config.
|
||||||
|
|
||||||
|
rpi-eeprom-config --config boot.conf --out newimage.bin pieeprom.bin
|
||||||
|
|
||||||
|
The new image file can be installed via rpi-eeprom-update
|
||||||
|
rpi-eeprom-update -d -f newimage.bin
|
||||||
|
|
||||||
|
4. Applies a given config file to an EEPROM image and invokes rpi-eeprom-update
|
||||||
|
to schedule an update of the bootloader when the system is rebooted.
|
||||||
|
|
||||||
|
Since this command launches rpi-eeprom-update to schedule the EEPROM update
|
||||||
|
it must be run as root.
|
||||||
|
|
||||||
|
sudo rpi-eeprom-config --apply boot.conf [pieeprom.bin]
|
||||||
|
|
||||||
|
If the 'eeprom' argument is not specified then the latest available image
|
||||||
|
is selected by calling 'rpi-eeprom-update -l'.
|
||||||
|
|
||||||
|
5. The '--edit' parameter behaves the same as '--apply' except that instead of
|
||||||
|
applying a predefined configuration file a text editor is launched with the
|
||||||
|
contents of the current EEPROM configuration.
|
||||||
|
|
||||||
|
Since this command launches rpi-eeprom-update to schedule the EEPROM update
|
||||||
|
it must be run as root.
|
||||||
|
|
||||||
|
The configuration file will be taken from:
|
||||||
|
* The blconfig reserved memory nvmem device
|
||||||
|
* The cached bootloader configuration 'vcgencmd bootloader_config'
|
||||||
|
* The current pending update - typically /boot/pieeprom.upd
|
||||||
|
|
||||||
|
sudo -E rpi-eeprom-config --edit [pieeprom.bin]
|
||||||
|
|
||||||
|
To cancel the pending update run 'sudo rpi-eeprom-update -r'
|
||||||
|
|
||||||
|
The default text editor is nano and may be overridden by setting the 'EDITOR'
|
||||||
|
environment variable and passing '-E' to 'sudo' to preserve the environment.
|
||||||
|
|
||||||
|
6. Signing the bootloader config file.
|
||||||
|
Updates an EEPROM binary with a signed config file (created by rpi-eeprom-digest) plus
|
||||||
|
the corresponding RSA public key.
|
||||||
|
|
||||||
|
Requires Python Cryptodomex libraries and OpenSSL. To install on Raspberry Pi OS run:-
|
||||||
|
sudo apt install openssl python-pip
|
||||||
|
sudo python3 -m pip install cryptodomex
|
||||||
|
|
||||||
|
rpi-eeprom-digest -k private.pem -i bootconf.txt -o bootconf.sig
|
||||||
|
rpi-eeprom-config --config bootconf.txt --digest bootconf.sig --pubkey public.pem --out pieeprom-signed.bin pieeprom.bin
|
||||||
|
|
||||||
|
Currently, the signing process is a separate step so can't be used with the --edit or --apply modes.
|
||||||
|
|
||||||
|
|
||||||
|
See 'rpi-eeprom-update -h' for more information about the available EEPROM images.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if os.getuid() != 0:
|
||||||
|
exit_error("Please run as root")
|
||||||
|
elif not argon_rpisupported():
|
||||||
|
# Skip
|
||||||
|
sys.exit(0)
|
||||||
|
argon_edit_config()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
atexit.register(exit_handler)
|
||||||
|
main()
|
||||||
1
pythonscript/argononeup.sh
Normal file → Executable file
1
pythonscript/argononeup.sh
Normal file → Executable file
@@ -201,6 +201,7 @@ sudo rm -Rf $INSTALLATIONFOLDER/ups/upsimg.tar.gz
|
|||||||
|
|
||||||
# Other utility scripts
|
# Other utility scripts
|
||||||
sudo wget $ARGONDOWNLOADSERVER/scripts/argondashboard.py -O $INSTALLATIONFOLDER/argondashboard.py --quiet
|
sudo wget $ARGONDOWNLOADSERVER/scripts/argondashboard.py -O $INSTALLATIONFOLDER/argondashboard.py --quiet
|
||||||
|
sudo wget $ARGONDOWNLOADSERVER/scripts/argonpowerbutton.py -O $INSTALLATIONFOLDER/argonpowerbutton.py --quiet
|
||||||
|
|
||||||
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-versioninfo.sh -O $versioninfoscript --quiet
|
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-versioninfo.sh -O $versioninfoscript --quiet
|
||||||
sudo chmod 755 $versioninfoscript
|
sudo chmod 755 $versioninfoscript
|
||||||
|
|||||||
453
pythonscript/argononeupd.py
Executable file
453
pythonscript/argononeupd.py
Executable file
@@ -0,0 +1,453 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
#
|
||||||
|
# This script monitor battery via ic2 and keyboard events.
|
||||||
|
#
|
||||||
|
# Additional comments are found in each function below
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
from queue import Queue
|
||||||
|
|
||||||
|
sys.path.append("/etc/argon/")
|
||||||
|
from argonregister import *
|
||||||
|
from argonpowerbutton import *
|
||||||
|
|
||||||
|
# Initialize I2C Bus
|
||||||
|
bus = argonregister_initializebusobj()
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
ADDR_BATTERY = 0x64
|
||||||
|
|
||||||
|
UPS_LOGFILE="/dev/shm/upslog.txt"
|
||||||
|
|
||||||
|
|
||||||
|
###################
|
||||||
|
# Utilty Functions
|
||||||
|
###################
|
||||||
|
|
||||||
|
# Debug Logger
|
||||||
|
def debuglog(typestr, logstr):
|
||||||
|
try:
|
||||||
|
DEBUGFILE="/dev/shm/argononeupdebuglog.txt"
|
||||||
|
tmpstrpadding = " "
|
||||||
|
|
||||||
|
with open(DEBUGFILE, "a") as txt_file:
|
||||||
|
txt_file.write("["+time.asctime(time.localtime(time.time()))+"] "+typestr.upper()+" "+logstr.strip().replace("\n","\n"+tmpstrpadding)+"\n")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# System Notifcation
|
||||||
|
def notifymessage(message, iscritical):
|
||||||
|
if not isinstance(message, str) or len(message.strip()) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
wftype="notify"
|
||||||
|
if iscritical:
|
||||||
|
wftype="critical"
|
||||||
|
os.system("export SUDO_UID=1000; wfpanelctl "+wftype+" \""+message+"\"")
|
||||||
|
os.system("export DISPLAY=:0.0; lxpanelctl notify \""+message+"\"")
|
||||||
|
|
||||||
|
|
||||||
|
#############
|
||||||
|
# Battery
|
||||||
|
#############
|
||||||
|
REG_CONTROL = 0x08
|
||||||
|
REG_SOCALERT = 0x0b
|
||||||
|
REG_PROFILE = 0x10
|
||||||
|
REG_ICSTATE = 0xA7
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def battery_restart():
|
||||||
|
# Set to active mode
|
||||||
|
try:
|
||||||
|
maxretry = 3
|
||||||
|
while maxretry > 0:
|
||||||
|
maxretry = maxretry - 1
|
||||||
|
|
||||||
|
# Restart
|
||||||
|
bus.write_byte_data(ADDR_BATTERY, REG_CONTROL, 0x30)
|
||||||
|
time.sleep(0.5)
|
||||||
|
# Activate
|
||||||
|
bus.write_byte_data(ADDR_BATTERY, REG_CONTROL, 0x00)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Wait for Ready Status
|
||||||
|
maxwaitsecs = 5
|
||||||
|
while maxwaitsecs > 0:
|
||||||
|
tmpval = bus.read_byte_data(ADDR_BATTERY, REG_ICSTATE)
|
||||||
|
if (tmpval&0x0C) != 0:
|
||||||
|
debuglog("battery-activate", "Activated Successfully")
|
||||||
|
return 0
|
||||||
|
time.sleep(1)
|
||||||
|
maxwaitsecs = maxwaitsecs - 1
|
||||||
|
|
||||||
|
|
||||||
|
debuglog("battery-activate", "Failed to activate")
|
||||||
|
return 2
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
debuglog("battery-activateerror", str(e))
|
||||||
|
except:
|
||||||
|
debuglog("battery-activateerror", "Activation Failed")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def battery_getstatus(restartifnotactive):
|
||||||
|
try:
|
||||||
|
tmpval = bus.read_byte_data(ADDR_BATTERY, REG_CONTROL)
|
||||||
|
if tmpval != 0:
|
||||||
|
if restartifnotactive == True:
|
||||||
|
tmpval = battery_restart()
|
||||||
|
|
||||||
|
if tmpval != 0:
|
||||||
|
debuglog("battery-status", "Inactive "+str(tmpval))
|
||||||
|
return 2
|
||||||
|
|
||||||
|
tmpval = bus.read_byte_data(ADDR_BATTERY, REG_SOCALERT)
|
||||||
|
if (tmpval&0x80) == 0:
|
||||||
|
debuglog("battery-status", "Profile not ready "+str(tmpval))
|
||||||
|
return 3
|
||||||
|
|
||||||
|
# OK
|
||||||
|
#debuglog("battery-status", "OK")
|
||||||
|
return 0
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
debuglog("battery-status-error", str(e))
|
||||||
|
except:
|
||||||
|
debuglog("battery-status-error", "Battery Status Failed")
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def battery_checkupdateprofile():
|
||||||
|
try:
|
||||||
|
REG_GPIOCONFIG = 0x0A
|
||||||
|
|
||||||
|
PROFILE_DATALIST = [0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xA8,0xAA,0xBE,0xC6,0xB8,0xAE,0xC2,0x98,0x82,0xFF,0xFF,0xCA,0x98,0x75,0x63,0x55,0x4E,0x4C,0x49,0x98,0x88,0xDC,0x34,0xDB,0xD3,0xD4,0xD3,0xD0,0xCE,0xCB,0xBB,0xE7,0xA2,0xC2,0xC4,0xAE,0x96,0x89,0x80,0x74,0x67,0x63,0x71,0x8E,0x9F,0x85,0x6F,0x3B,0x20,0x00,0xAB,0x10,0x00,0xB0,0x73,0x00,0x00,0x00,0x64,0x08,0xD3,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAC]
|
||||||
|
|
||||||
|
PROFILE_LEN = len(PROFILE_DATALIST)
|
||||||
|
|
||||||
|
# Try to compare profile if battery is active
|
||||||
|
tmpidx = 0
|
||||||
|
|
||||||
|
tmpval = battery_getstatus(True)
|
||||||
|
if tmpval == 0:
|
||||||
|
# Status OK, check profile
|
||||||
|
tmpidx = 0
|
||||||
|
while tmpidx < PROFILE_LEN:
|
||||||
|
tmpval = bus.read_byte_data(ADDR_BATTERY, REG_PROFILE+tmpidx)
|
||||||
|
if tmpval != PROFILE_DATALIST[tmpidx]:
|
||||||
|
debuglog("battery-profile-error", "Mismatch")
|
||||||
|
break
|
||||||
|
tmpidx = tmpidx + 1
|
||||||
|
|
||||||
|
if tmpidx == PROFILE_LEN:
|
||||||
|
# Matched
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
debuglog("battery-profile", "Status Error "+str(tmpval)+", will attempt to update")
|
||||||
|
|
||||||
|
# needs update
|
||||||
|
debuglog("battery-profile", "Updating...")
|
||||||
|
|
||||||
|
# Device Sleep state
|
||||||
|
|
||||||
|
# Restart
|
||||||
|
bus.write_byte_data(ADDR_BATTERY, REG_CONTROL, 0x30)
|
||||||
|
time.sleep(0.5)
|
||||||
|
# Sleep
|
||||||
|
bus.write_byte_data(ADDR_BATTERY, REG_CONTROL, 0xF0)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Write Profile
|
||||||
|
tmpidx = 0
|
||||||
|
while tmpidx < PROFILE_LEN:
|
||||||
|
bus.write_byte_data(ADDR_BATTERY, REG_PROFILE+tmpidx, PROFILE_DATALIST[tmpidx])
|
||||||
|
tmpidx = tmpidx + 1
|
||||||
|
|
||||||
|
debuglog("battery-profile", "Profile Updated,Restarting...")
|
||||||
|
|
||||||
|
# Set Update Flag
|
||||||
|
bus.write_byte_data(ADDR_BATTERY, REG_SOCALERT, 0x80)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Close Interrupts
|
||||||
|
bus.write_byte_data(ADDR_BATTERY, REG_GPIOCONFIG, 0)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Restart Battery
|
||||||
|
tmpval = battery_restart()
|
||||||
|
if tmpval == 0:
|
||||||
|
debuglog("battery-profile", "Update Completed")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
debuglog("battery-profile", "Unable to restart")
|
||||||
|
return 3
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
debuglog("battery-profile-error", str(e))
|
||||||
|
except:
|
||||||
|
debuglog("battery-profile-error", "Battery Profile Check/Update Failed")
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def battery_getpercent():
|
||||||
|
# State of Charge (SOC)
|
||||||
|
try:
|
||||||
|
SOC_HIGH_REG = 0x04
|
||||||
|
|
||||||
|
socpercent = bus.read_byte_data(ADDR_BATTERY, SOC_HIGH_REG)
|
||||||
|
if socpercent > 100:
|
||||||
|
return 100
|
||||||
|
elif socpercent > 0:
|
||||||
|
return socpercent
|
||||||
|
|
||||||
|
# Support Fraction percent
|
||||||
|
#SOC_LOW_REG = 0x05
|
||||||
|
#soc_low = bus.read_byte_data(ADDR_BATTERY, SOC_LOW_REG)
|
||||||
|
#socpercentfloat = socpercent + (soc_low / 256.0)
|
||||||
|
#if socpercentfloat > 100.0:
|
||||||
|
# return 100.0
|
||||||
|
#elif socpercentfloat > 0.0:
|
||||||
|
# return socpercentfloat
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
debuglog("battery-percenterror", str(e))
|
||||||
|
except:
|
||||||
|
debuglog("battery-percenterror", "Read Battery Failed")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def battery_isplugged():
|
||||||
|
# State of Charge (SOC)
|
||||||
|
try:
|
||||||
|
CURRENT_HIGH_REG = 0x0E
|
||||||
|
|
||||||
|
current_high = bus.read_byte_data(ADDR_BATTERY, CURRENT_HIGH_REG)
|
||||||
|
|
||||||
|
if (current_high & 0x80) > 0:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
#CURRENT_LOW_REG = 0x0F
|
||||||
|
#R_SENSE = 10.0
|
||||||
|
#current_low = bus.read_byte_data(ADDR_BATTERY, CURRENT_LOW_REG)
|
||||||
|
#raw_current = int.from_bytes([current_high, current_low], byteorder='big', signed=True)
|
||||||
|
#current = (52.4 * raw_current) / (32768 * R_SENSE)
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
debuglog("battery-chargingerror", str(e))
|
||||||
|
except:
|
||||||
|
debuglog("battery-chargingerror", "Read Charging Failed")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def battery_loadlogdata():
|
||||||
|
# status, version, time, schedule
|
||||||
|
outobj = {}
|
||||||
|
try:
|
||||||
|
fp = open(UPS_LOGFILE, "r")
|
||||||
|
logdata = fp.read()
|
||||||
|
alllines = logdata.split("\n")
|
||||||
|
ctr = 0
|
||||||
|
while ctr < len(alllines):
|
||||||
|
tmpval = alllines[ctr].strip()
|
||||||
|
curinfo = tmpval.split(":")
|
||||||
|
if len(curinfo) > 1:
|
||||||
|
tmpattrib = curinfo[0].lower().split(" ")
|
||||||
|
# The rest are assumed to be value
|
||||||
|
outobj[tmpattrib[0]] = tmpval[(len(curinfo[0])+1):].strip()
|
||||||
|
ctr = ctr + 1
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return outobj
|
||||||
|
|
||||||
|
def battery_check(readq):
|
||||||
|
CMDSTARTBYTE=0xfe
|
||||||
|
CMDCONTROLBYTECOUNT=3
|
||||||
|
CHECKSTATUSLOOPFREQ=50
|
||||||
|
|
||||||
|
CMDsendrequest = [ 0xfe, 0, 0, 0xfe, 0xfe, 0, 0, 0xfe, 0, 0, 0]
|
||||||
|
|
||||||
|
lastcmdtime=""
|
||||||
|
loopCtr = CHECKSTATUSLOOPFREQ
|
||||||
|
sendcmdid = -1
|
||||||
|
|
||||||
|
debuglog("battery", "Starting")
|
||||||
|
|
||||||
|
updatedesktopicon("Argon ONE UP", "/etc/argon/argon40.png")
|
||||||
|
|
||||||
|
maxretry = 5
|
||||||
|
while maxretry > 0:
|
||||||
|
try:
|
||||||
|
if battery_checkupdateprofile() == 0:
|
||||||
|
break
|
||||||
|
except Exception as mainerr:
|
||||||
|
try:
|
||||||
|
debuglog("battery-mainerror", str(mainerr))
|
||||||
|
except:
|
||||||
|
debuglog("battery-mainerror", "Error")
|
||||||
|
# Give time before retry
|
||||||
|
time.sleep(10)
|
||||||
|
maxretry = maxretry - 1
|
||||||
|
|
||||||
|
while maxretry > 0: # Outer loop; maxretry never decrements so infinite
|
||||||
|
qdata = ""
|
||||||
|
if readq.empty() == False:
|
||||||
|
qdata = readq.get()
|
||||||
|
|
||||||
|
if battery_getstatus(True) != 0:
|
||||||
|
# Give time before retry
|
||||||
|
time.sleep(3)
|
||||||
|
continue
|
||||||
|
|
||||||
|
prevnotifymsg = ""
|
||||||
|
previconfile = ""
|
||||||
|
statusstr = ""
|
||||||
|
|
||||||
|
needsupdate=False
|
||||||
|
device_battery=0
|
||||||
|
device_charging=0
|
||||||
|
|
||||||
|
while True: # Command loop
|
||||||
|
try:
|
||||||
|
if sendcmdid < 0:
|
||||||
|
cmddatastr = ""
|
||||||
|
|
||||||
|
if cmddatastr == "":
|
||||||
|
if loopCtr >= CHECKSTATUSLOOPFREQ:
|
||||||
|
# Check Battery Status
|
||||||
|
sendcmdid = 0
|
||||||
|
loopCtr = 0
|
||||||
|
else:
|
||||||
|
loopCtr = loopCtr + 1
|
||||||
|
if (loopCtr&1) == 0:
|
||||||
|
sendcmdid = 0 # Check Battery Status
|
||||||
|
|
||||||
|
if sendcmdid == 0:
|
||||||
|
tmp_battery = battery_getpercent()
|
||||||
|
tmp_charging = battery_isplugged()
|
||||||
|
|
||||||
|
if tmp_charging != device_charging or tmp_battery!=device_battery:
|
||||||
|
device_battery=tmp_battery
|
||||||
|
device_charging=tmp_charging
|
||||||
|
tmpiconfile = "/etc/argon/ups/"
|
||||||
|
needsupdate=True
|
||||||
|
curnotifymsg = ""
|
||||||
|
curnotifycritical = False
|
||||||
|
|
||||||
|
if device_charging == 0:
|
||||||
|
if device_battery>99:
|
||||||
|
statusstr = "Charged"
|
||||||
|
else:
|
||||||
|
statusstr = "Charging"
|
||||||
|
curnotifymsg = statusstr
|
||||||
|
tmpiconfile = tmpiconfile+"charge_"+str(device_battery)
|
||||||
|
else:
|
||||||
|
statusstr = "Battery"
|
||||||
|
tmpiconfile = tmpiconfile+"discharge_"+str(device_battery)
|
||||||
|
|
||||||
|
if device_battery > 50:
|
||||||
|
curnotifymsg="Battery Mode"
|
||||||
|
elif device_battery > 20:
|
||||||
|
curnotifymsg="50%% Battery"
|
||||||
|
elif device_battery > 10:
|
||||||
|
curnotifymsg="20%% Battery"
|
||||||
|
else:
|
||||||
|
curnotifymsg="Low Battery"
|
||||||
|
curnotifycritical=True
|
||||||
|
|
||||||
|
tmpiconfile = tmpiconfile + ".png"
|
||||||
|
statusstr = statusstr + " " + str(device_battery)+"%"
|
||||||
|
|
||||||
|
# Add/update desktop icons too; add check to minimize write
|
||||||
|
if previconfile != tmpiconfile:
|
||||||
|
updatedesktopicon(statusstr, tmpiconfile)
|
||||||
|
previconfile = tmpiconfile
|
||||||
|
|
||||||
|
# Send notification if necessary
|
||||||
|
if prevnotifymsg != curnotifymsg:
|
||||||
|
notifymessage(curnotifymsg, curnotifycritical)
|
||||||
|
|
||||||
|
prevnotifymsg = curnotifymsg
|
||||||
|
|
||||||
|
|
||||||
|
sendcmdid=-1
|
||||||
|
|
||||||
|
if needsupdate==True:
|
||||||
|
# Log File
|
||||||
|
otherstr = ""
|
||||||
|
with open(UPS_LOGFILE, "w") as txt_file:
|
||||||
|
txt_file.write("Status as of: "+time.asctime(time.localtime(time.time()))+"\n Power:"+statusstr+"\n"+otherstr)
|
||||||
|
|
||||||
|
needsupdate=False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
debuglog("battery-error", str(e))
|
||||||
|
except:
|
||||||
|
debuglog("battery-error", "Error")
|
||||||
|
break
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
def updatedesktopicon(statusstr, tmpiconfile):
|
||||||
|
try:
|
||||||
|
icontitle = "Argon ONE UP"
|
||||||
|
tmp = os.popen("find /home -maxdepth 1 -type d").read()
|
||||||
|
alllines = tmp.split("\n")
|
||||||
|
for curfolder in alllines:
|
||||||
|
if curfolder == "/home" or curfolder == "":
|
||||||
|
continue
|
||||||
|
#debuglog("desktop-update-path", curfolder)
|
||||||
|
#debuglog("desktop-update-text", statusstr)
|
||||||
|
#debuglog("desktop-update-icon", tmpiconfile)
|
||||||
|
with open(curfolder+"/Desktop/argononeup.desktop", "w") as txt_file:
|
||||||
|
txt_file.write("[Desktop Entry]\nName="+icontitle+"\nComment="+statusstr+"\nIcon="+tmpiconfile+"\nExec=lxterminal --working-directory="+curfolder+"/ -t \"Argon ONE UP\" -e \"/etc/argon/argon-config\"\nType=Application\nEncoding=UTF-8\nTerminal=false\nCategories=None;\n")
|
||||||
|
except Exception as desktope:
|
||||||
|
#pass
|
||||||
|
try:
|
||||||
|
debuglog("desktop-update-error", str(desktope))
|
||||||
|
except:
|
||||||
|
debuglog("desktop-update-error", "Error")
|
||||||
|
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
cmd = sys.argv[1].upper()
|
||||||
|
if cmd == "GETBATTERY":
|
||||||
|
outobj = battery_loadlogdata()
|
||||||
|
try:
|
||||||
|
print(outobj["power"])
|
||||||
|
except:
|
||||||
|
print("Error retrieving battery status")
|
||||||
|
elif cmd == "RESETBATTERY":
|
||||||
|
battery_checkupdateprofile()
|
||||||
|
|
||||||
|
elif cmd == "SERVICE":
|
||||||
|
# Starts sudo level services
|
||||||
|
try:
|
||||||
|
ipcq = Queue()
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
cmd = sys.argv[2].upper()
|
||||||
|
t1 = Thread(target = battery_check, args =(ipcq, ))
|
||||||
|
#t2 = Thread(target = argonpowerbutton_monitorlid, args =(ipcq, ))
|
||||||
|
|
||||||
|
t1.start()
|
||||||
|
#t2.start()
|
||||||
|
|
||||||
|
ipcq.join()
|
||||||
|
except Exception:
|
||||||
|
sys.exit(1)
|
||||||
0
pythonscript/argonpowerbutton.py
Executable file
0
pythonscript/argonpowerbutton.py
Executable file
74
pythonscript/argonregister.py
Executable file
74
pythonscript/argonregister.py
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
#
|
||||||
|
# Argon Register Helper methods
|
||||||
|
# Same as argonregister, but no support for new register commands
|
||||||
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
|
import smbus
|
||||||
|
|
||||||
|
# I2C Addresses
|
||||||
|
ADDR_ARGONONEFAN=0x1a
|
||||||
|
ADDR_ARGONONEREG=ADDR_ARGONONEFAN
|
||||||
|
|
||||||
|
# ARGONONEREG Addresses
|
||||||
|
ADDR_ARGONONEREG_DUTYCYCLE=0x80
|
||||||
|
ADDR_ARGONONEREG_FW=0x81
|
||||||
|
ADDR_ARGONONEREG_IR=0x82
|
||||||
|
ADDR_ARGONONEREG_CTRL=0x86
|
||||||
|
|
||||||
|
# Initialize bus
|
||||||
|
def argonregister_initializebusobj():
|
||||||
|
try:
|
||||||
|
return smbus.SMBus(1)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
# Older version
|
||||||
|
return smbus.SMBus(0)
|
||||||
|
except Exception:
|
||||||
|
print("Unable to detect i2c")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Checks if the FW supports control registers
|
||||||
|
def argonregister_checksupport(busobj):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def argonregister_getbyte(busobj, address):
|
||||||
|
if busobj is None:
|
||||||
|
return 0
|
||||||
|
return busobj.read_byte_data(ADDR_ARGONONEREG, address)
|
||||||
|
|
||||||
|
def argonregister_setbyte(busobj, address, bytevalue):
|
||||||
|
if busobj is None:
|
||||||
|
return
|
||||||
|
busobj.write_byte_data(ADDR_ARGONONEREG,address,bytevalue)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def argonregister_getfanspeed(busobj, regsupport=None):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def argonregister_setfanspeed(busobj, newspeed, regsupport=None):
|
||||||
|
if busobj is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if newspeed > 100:
|
||||||
|
newspeed = 100
|
||||||
|
elif newspeed < 0:
|
||||||
|
newspeed = 0
|
||||||
|
|
||||||
|
busobj.write_byte(ADDR_ARGONONEFAN,newspeed)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def argonregister_signalpoweroff(busobj):
|
||||||
|
if busobj is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
busobj.write_byte(ADDR_ARGONONEFAN,0xFF)
|
||||||
|
|
||||||
|
def argonregister_setircode(busobj, vallist):
|
||||||
|
if busobj is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
busobj.write_i2c_block_data(ADDR_ARGONONEREG, ADDR_ARGONONEREG_IR, vallist)
|
||||||
394
pythonscript/argonsysinfo.py
Executable file
394
pythonscript/argonsysinfo.py
Executable file
@@ -0,0 +1,394 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
#
|
||||||
|
# Misc methods to retrieve system information.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import socket
|
||||||
|
|
||||||
|
def argonsysinfo_listcpuusage(sleepsec = 1):
|
||||||
|
outputlist = []
|
||||||
|
curusage_a = argonsysinfo_getcpuusagesnapshot()
|
||||||
|
time.sleep(sleepsec)
|
||||||
|
curusage_b = argonsysinfo_getcpuusagesnapshot()
|
||||||
|
|
||||||
|
for cpuname in curusage_a:
|
||||||
|
if cpuname == "cpu":
|
||||||
|
continue
|
||||||
|
if curusage_a[cpuname]["total"] == curusage_b[cpuname]["total"]:
|
||||||
|
outputlist.append({"title": cpuname, "value": "0%"})
|
||||||
|
else:
|
||||||
|
total = curusage_b[cpuname]["total"]-curusage_a[cpuname]["total"]
|
||||||
|
idle = curusage_b[cpuname]["idle"]-curusage_a[cpuname]["idle"]
|
||||||
|
outputlist.append({"title": cpuname, "value": int(100*(total-idle)/(total))})
|
||||||
|
return outputlist
|
||||||
|
|
||||||
|
def argonsysinfo_getcpuusagesnapshot():
|
||||||
|
cpupercent = {}
|
||||||
|
errorflag = False
|
||||||
|
try:
|
||||||
|
cpuctr = 0
|
||||||
|
# user, nice, system, idle, iowait, irc, softirq, steal, guest, guest nice
|
||||||
|
tempfp = open("/proc/stat", "r")
|
||||||
|
alllines = tempfp.readlines()
|
||||||
|
for temp in alllines:
|
||||||
|
temp = temp.replace('\t', ' ')
|
||||||
|
temp = temp.strip()
|
||||||
|
while temp.find(" ") >= 0:
|
||||||
|
temp = temp.replace(" ", " ")
|
||||||
|
if len(temp) < 3:
|
||||||
|
cpuctr = cpuctr +1
|
||||||
|
continue
|
||||||
|
|
||||||
|
checkname = temp[0:3]
|
||||||
|
if checkname == "cpu":
|
||||||
|
infolist = temp.split(" ")
|
||||||
|
idle = 0
|
||||||
|
total = 0
|
||||||
|
colctr = 1
|
||||||
|
while colctr < len(infolist):
|
||||||
|
curval = int(infolist[colctr])
|
||||||
|
if colctr == 4 or colctr == 5:
|
||||||
|
idle = idle + curval
|
||||||
|
total = total + curval
|
||||||
|
colctr = colctr + 1
|
||||||
|
if total > 0:
|
||||||
|
cpupercent[infolist[0]] = {"total": total, "idle": idle}
|
||||||
|
cpuctr = cpuctr +1
|
||||||
|
|
||||||
|
tempfp.close()
|
||||||
|
except IOError:
|
||||||
|
errorflag = True
|
||||||
|
return cpupercent
|
||||||
|
|
||||||
|
|
||||||
|
def argonsysinfo_liststoragetotal():
|
||||||
|
outputlist = []
|
||||||
|
ramtotal = 0
|
||||||
|
errorflag = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
hddctr = 0
|
||||||
|
tempfp = open("/proc/partitions", "r")
|
||||||
|
alllines = tempfp.readlines()
|
||||||
|
|
||||||
|
for temp in alllines:
|
||||||
|
temp = temp.replace('\t', ' ')
|
||||||
|
temp = temp.strip()
|
||||||
|
while temp.find(" ") >= 0:
|
||||||
|
temp = temp.replace(" ", " ")
|
||||||
|
infolist = temp.split(" ")
|
||||||
|
if len(infolist) >= 4:
|
||||||
|
# Check if header
|
||||||
|
if infolist[3] != "name":
|
||||||
|
parttype = infolist[3][0:3]
|
||||||
|
if parttype == "ram":
|
||||||
|
ramtotal = ramtotal + int(infolist[2])
|
||||||
|
elif parttype[0:2] == "sd" or parttype[0:2] == "hd":
|
||||||
|
lastchar = infolist[3][-1]
|
||||||
|
if lastchar.isdigit() == False:
|
||||||
|
outputlist.append({"title": infolist[3], "value": argonsysinfo_kbstr(int(infolist[2]))})
|
||||||
|
else:
|
||||||
|
# SD Cards
|
||||||
|
lastchar = infolist[3][-2]
|
||||||
|
if lastchar[0] != "p":
|
||||||
|
outputlist.append({"title": infolist[3], "value": argonsysinfo_kbstr(int(infolist[2]))})
|
||||||
|
|
||||||
|
tempfp.close()
|
||||||
|
#outputlist.append({"title": "ram", "value": argonsysinfo_kbstr(ramtotal)})
|
||||||
|
except IOError:
|
||||||
|
errorflag = True
|
||||||
|
return outputlist
|
||||||
|
|
||||||
|
def argonsysinfo_getram():
|
||||||
|
totalram = 0
|
||||||
|
totalfree = 0
|
||||||
|
tempfp = open("/proc/meminfo", "r")
|
||||||
|
alllines = tempfp.readlines()
|
||||||
|
|
||||||
|
for temp in alllines:
|
||||||
|
temp = temp.replace('\t', ' ')
|
||||||
|
temp = temp.strip()
|
||||||
|
while temp.find(" ") >= 0:
|
||||||
|
temp = temp.replace(" ", " ")
|
||||||
|
infolist = temp.split(" ")
|
||||||
|
if len(infolist) >= 2:
|
||||||
|
if infolist[0] == "MemTotal:":
|
||||||
|
totalram = int(infolist[1])
|
||||||
|
elif infolist[0] == "MemFree:":
|
||||||
|
totalfree = totalfree + int(infolist[1])
|
||||||
|
elif infolist[0] == "Buffers:":
|
||||||
|
totalfree = totalfree + int(infolist[1])
|
||||||
|
elif infolist[0] == "Cached:":
|
||||||
|
totalfree = totalfree + int(infolist[1])
|
||||||
|
if totalram == 0:
|
||||||
|
return "0%"
|
||||||
|
return [str(int(100*totalfree/totalram))+"%", str((totalram+512*1024)>>20)+"GB"]
|
||||||
|
|
||||||
|
def argonsysinfo_getcputemp():
|
||||||
|
try:
|
||||||
|
tempfp = open("/sys/class/thermal/thermal_zone0/temp", "r")
|
||||||
|
temp = tempfp.readline()
|
||||||
|
tempfp.close()
|
||||||
|
#cval = temp/1000
|
||||||
|
#fval = 32+9*temp/5000
|
||||||
|
return float(int(temp)/1000)
|
||||||
|
except IOError:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def argonsysinfo_getmaxhddtemp():
|
||||||
|
maxtempval = 0
|
||||||
|
try:
|
||||||
|
hddtempobj = argonsysinfo_gethddtemp()
|
||||||
|
for curdev in hddtempobj:
|
||||||
|
if hddtempobj[curdev] > maxtempval:
|
||||||
|
maxtempval = hddtempobj[curdev]
|
||||||
|
return maxtempval
|
||||||
|
except:
|
||||||
|
return maxtempval
|
||||||
|
|
||||||
|
def argonsysinfo_gethddtemp():
|
||||||
|
# May 2022: Used smartctl, hddtemp is not available on some platforms
|
||||||
|
hddtempcmd = "/usr/sbin/smartctl"
|
||||||
|
if os.path.exists(hddtempcmd) == False:
|
||||||
|
# Fallback for now
|
||||||
|
hddtempcmd = "/usr/sbin/hddtemp"
|
||||||
|
|
||||||
|
outputobj = {}
|
||||||
|
if os.path.exists(hddtempcmd):
|
||||||
|
try:
|
||||||
|
tmp = os.popen("lsblk | grep -e '0 disk' | awk '{print $1}'").read()
|
||||||
|
alllines = tmp.split("\n")
|
||||||
|
for curdev in alllines:
|
||||||
|
if curdev[0:2] == "sd" or curdev[0:2] == "hd":
|
||||||
|
tempval = argonsysinfo_getdevhddtemp(hddtempcmd,curdev)
|
||||||
|
if tempval > 0:
|
||||||
|
outputobj[curdev] = tempval
|
||||||
|
return outputobj
|
||||||
|
except:
|
||||||
|
return outputobj
|
||||||
|
return outputobj
|
||||||
|
|
||||||
|
def argonsysinfo_getdevhddtemp(hddtempcmd, curdev):
|
||||||
|
cmdstr = ""
|
||||||
|
if hddtempcmd == "/usr/sbin/hddtemp":
|
||||||
|
cmdstr = "/usr/sbin/hddtemp -n sata:/dev/"+curdev
|
||||||
|
elif hddtempcmd == "/usr/sbin/smartctl":
|
||||||
|
cmdstr = "/usr/sbin/smartctl -d sat -A /dev/"+curdev+" | grep Temperature_Celsius | awk '{print $10}'"
|
||||||
|
|
||||||
|
tempval = 0
|
||||||
|
if len(cmdstr) > 0:
|
||||||
|
try:
|
||||||
|
temperaturestr = os.popen(cmdstr+" 2>&1").read()
|
||||||
|
tempval = float(temperaturestr)
|
||||||
|
except:
|
||||||
|
tempval = -1
|
||||||
|
|
||||||
|
return tempval
|
||||||
|
|
||||||
|
def argonsysinfo_getip():
|
||||||
|
ipaddr = ""
|
||||||
|
st = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
try:
|
||||||
|
# Connect to nonexistent device
|
||||||
|
st.connect(('254.255.255.255', 1))
|
||||||
|
ipaddr = st.getsockname()[0]
|
||||||
|
except Exception:
|
||||||
|
ipaddr = 'N/A'
|
||||||
|
finally:
|
||||||
|
st.close()
|
||||||
|
return ipaddr
|
||||||
|
|
||||||
|
|
||||||
|
def argonsysinfo_getrootdev():
|
||||||
|
tmp = os.popen('mount').read()
|
||||||
|
alllines = tmp.split("\n")
|
||||||
|
|
||||||
|
for temp in alllines:
|
||||||
|
temp = temp.replace('\t', ' ')
|
||||||
|
temp = temp.strip()
|
||||||
|
while temp.find(" ") >= 0:
|
||||||
|
temp = temp.replace(" ", " ")
|
||||||
|
infolist = temp.split(" ")
|
||||||
|
if len(infolist) >= 3:
|
||||||
|
|
||||||
|
if infolist[2] == "/":
|
||||||
|
return infolist[0]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def argonsysinfo_listhddusage():
|
||||||
|
outputobj = {}
|
||||||
|
raidlist = argonsysinfo_listraid()
|
||||||
|
raiddevlist = []
|
||||||
|
raidctr = 0
|
||||||
|
while raidctr < len(raidlist['raidlist']):
|
||||||
|
raiddevlist.append(raidlist['raidlist'][raidctr]['title'])
|
||||||
|
# TODO: May need to use different method for each raid type (i.e. check raidlist['raidlist'][raidctr]['value'])
|
||||||
|
#outputobj[raidlist['raidlist'][raidctr]['title']] = {"used":int(raidlist['raidlist'][raidctr]['info']['used']), "total":int(raidlist['raidlist'][raidctr]['info']['size'])}
|
||||||
|
raidctr = raidctr + 1
|
||||||
|
|
||||||
|
rootdev = argonsysinfo_getrootdev()
|
||||||
|
|
||||||
|
tmp = os.popen('df').read()
|
||||||
|
alllines = tmp.split("\n")
|
||||||
|
|
||||||
|
for temp in alllines:
|
||||||
|
temp = temp.replace('\t', ' ')
|
||||||
|
temp = temp.strip()
|
||||||
|
while temp.find(" ") >= 0:
|
||||||
|
temp = temp.replace(" ", " ")
|
||||||
|
infolist = temp.split(" ")
|
||||||
|
if len(infolist) >= 6:
|
||||||
|
if infolist[1] == "Size":
|
||||||
|
continue
|
||||||
|
if len(infolist[0]) < 5:
|
||||||
|
continue
|
||||||
|
elif infolist[0][0:5] != "/dev/":
|
||||||
|
continue
|
||||||
|
curdev = infolist[0]
|
||||||
|
if curdev == "/dev/root" and rootdev != "":
|
||||||
|
curdev = rootdev
|
||||||
|
tmpidx = curdev.rfind("/")
|
||||||
|
if tmpidx >= 0:
|
||||||
|
curdev = curdev[tmpidx+1:]
|
||||||
|
|
||||||
|
if curdev in raidlist['hddlist']:
|
||||||
|
# Skip devices that are part of a RAID setup
|
||||||
|
continue
|
||||||
|
elif curdev in raiddevlist:
|
||||||
|
# Skip RAID ID that already have size data
|
||||||
|
# (use df information otherwise)
|
||||||
|
if curdev in outputobj:
|
||||||
|
continue
|
||||||
|
elif curdev[0:2] == "sd" or curdev[0:2] == "hd":
|
||||||
|
curdev = curdev[0:-1]
|
||||||
|
else:
|
||||||
|
curdev = curdev[0:-2]
|
||||||
|
|
||||||
|
# Aggregate values (i.e. sda1, sda2 to sda)
|
||||||
|
if curdev in outputobj:
|
||||||
|
outputobj[curdev] = {"used":outputobj[curdev]['used']+int(infolist[2]), "total":outputobj[curdev]['total']+int(infolist[1])}
|
||||||
|
else:
|
||||||
|
outputobj[curdev] = {"used":int(infolist[2]), "total":int(infolist[1])}
|
||||||
|
|
||||||
|
return outputobj
|
||||||
|
|
||||||
|
def argonsysinfo_kbstr(kbval, wholenumbers = True):
|
||||||
|
remainder = 0
|
||||||
|
suffixidx = 0
|
||||||
|
suffixlist = ["KB", "MB", "GB", "TB"]
|
||||||
|
while kbval > 1023 and suffixidx < len(suffixlist):
|
||||||
|
remainder = kbval & 1023
|
||||||
|
kbval = kbval >> 10
|
||||||
|
suffixidx = suffixidx + 1
|
||||||
|
|
||||||
|
#return str(kbval)+"."+str(remainder) + suffixlist[suffixidx]
|
||||||
|
remainderstr = ""
|
||||||
|
if kbval < 100 and wholenumbers == False:
|
||||||
|
remainder = int((remainder+50)/100)
|
||||||
|
if remainder > 0:
|
||||||
|
remainderstr = "."+str(remainder)
|
||||||
|
elif remainder >= 500:
|
||||||
|
kbval = kbval + 1
|
||||||
|
return str(kbval)+remainderstr + suffixlist[suffixidx]
|
||||||
|
|
||||||
|
def argonsysinfo_listraid():
|
||||||
|
hddlist = []
|
||||||
|
outputlist = []
|
||||||
|
# cat /proc/mdstat
|
||||||
|
# multiple mdxx from mdstat
|
||||||
|
# mdadm -D /dev/md1
|
||||||
|
|
||||||
|
ramtotal = 0
|
||||||
|
errorflag = False
|
||||||
|
try:
|
||||||
|
hddctr = 0
|
||||||
|
tempfp = open("/proc/mdstat", "r")
|
||||||
|
alllines = tempfp.readlines()
|
||||||
|
for temp in alllines:
|
||||||
|
temp = temp.replace('\t', ' ')
|
||||||
|
temp = temp.strip()
|
||||||
|
while temp.find(" ") >= 0:
|
||||||
|
temp = temp.replace(" ", " ")
|
||||||
|
infolist = temp.split(" ")
|
||||||
|
if len(infolist) >= 4:
|
||||||
|
|
||||||
|
# Check if raid info
|
||||||
|
if infolist[0] != "Personalities" and infolist[1] == ":":
|
||||||
|
devname = infolist[0]
|
||||||
|
raidtype = infolist[3]
|
||||||
|
#raidstatus = infolist[2]
|
||||||
|
hddctr = 4
|
||||||
|
while hddctr < len(infolist):
|
||||||
|
tmpdevname = infolist[hddctr]
|
||||||
|
tmpidx = tmpdevname.find("[")
|
||||||
|
if tmpidx >= 0:
|
||||||
|
tmpdevname = tmpdevname[0:tmpidx]
|
||||||
|
hddlist.append(tmpdevname)
|
||||||
|
hddctr = hddctr + 1
|
||||||
|
devdetail = argonsysinfo_getraiddetail(devname)
|
||||||
|
outputlist.append({"title": devname, "value": raidtype, "info": devdetail})
|
||||||
|
|
||||||
|
tempfp.close()
|
||||||
|
except IOError:
|
||||||
|
# No raid
|
||||||
|
errorflag = True
|
||||||
|
|
||||||
|
return {"raidlist": outputlist, "hddlist": hddlist}
|
||||||
|
|
||||||
|
|
||||||
|
def argonsysinfo_getraiddetail(devname):
|
||||||
|
state = ""
|
||||||
|
raidtype = ""
|
||||||
|
size = 0
|
||||||
|
used = 0
|
||||||
|
total = 0
|
||||||
|
working = 0
|
||||||
|
active = 0
|
||||||
|
failed = 0
|
||||||
|
spare = 0
|
||||||
|
rebuildstat = ""
|
||||||
|
tmp = os.popen('mdadm -D /dev/'+devname).read()
|
||||||
|
alllines = tmp.split("\n")
|
||||||
|
|
||||||
|
for temp in alllines:
|
||||||
|
temp = temp.replace('\t', ' ')
|
||||||
|
temp = temp.strip()
|
||||||
|
while temp.find(" ") >= 0:
|
||||||
|
temp = temp.replace(" ", " ")
|
||||||
|
infolist = temp.split(" : ")
|
||||||
|
if len(infolist) == 2:
|
||||||
|
if infolist[0].lower() == "raid level":
|
||||||
|
raidtype = infolist[1]
|
||||||
|
elif infolist[0].lower() == "array size":
|
||||||
|
tmpidx = infolist[1].find(" ")
|
||||||
|
if tmpidx > 0:
|
||||||
|
size = (infolist[1][0:tmpidx])
|
||||||
|
elif infolist[0].lower() == "used dev size":
|
||||||
|
tmpidx = infolist[1].find(" ")
|
||||||
|
if tmpidx > 0:
|
||||||
|
used = (infolist[1][0:tmpidx])
|
||||||
|
elif infolist[0].lower() == "state":
|
||||||
|
tmpidx = infolist[1].rfind(" ")
|
||||||
|
if tmpidx > 0:
|
||||||
|
state = (infolist[1][tmpidx+1:])
|
||||||
|
else:
|
||||||
|
state = infolist[1]
|
||||||
|
elif infolist[0].lower() == "total devices":
|
||||||
|
total = infolist[1]
|
||||||
|
elif infolist[0].lower() == "active devices":
|
||||||
|
active = infolist[1]
|
||||||
|
elif infolist[0].lower() == "working devices":
|
||||||
|
working = infolist[1]
|
||||||
|
elif infolist[0].lower() == "failed devices":
|
||||||
|
failed = infolist[1]
|
||||||
|
elif infolist[0].lower() == "spare devices":
|
||||||
|
spare = infolist[1]
|
||||||
|
elif infolist[0].lower() == "rebuild status":
|
||||||
|
tmpidx = infolist[1].find("%")
|
||||||
|
if tmpidx > 0:
|
||||||
|
rebuildstat = (infolist[1][0:tmpidx])+"%"
|
||||||
|
return {"state": state, "raidtype": raidtype, "size": int(size), "used": int(used), "devices": int(total), "active": int(active), "working": int(working), "failed": int(failed), "spare": int(spare), "rebuildstat": rebuildstat}
|
||||||
Reference in New Issue
Block a user