Compare commits

...

20 Commits

Author SHA1 Message Date
okunze a7203e0777 Automated Change by GitHub Action 2026-03-27 21:24:09 +00:00
okunze 57498e55cd Automated Change by GitHub Action 2026-03-20 21:19:07 +00:00
Oliver Kunze d018932082 Merge pull request #8 from ManuHry/original-upstream
Add Argon ONE V5 and Argon ONE UP CM5
2026-03-17 11:49:05 +01:00
Emmanuel 4726434b51 Add Argon ONE V5 and Argon ONE UP CM5
Add argon1v5.sh and argononeup.sh
Recheck all dependencies in scripts and manuals to append missing files (like argon-eeprom.sh and firmware/ArgonOne.uf2 for Argon ONE V3)
Sort downloads by path to facilitate reading
2026-03-17 11:07:34 +01:00
okunze 9fcd379747 Automated Change by GitHub Action 2026-02-20 21:12:59 +00:00
okunze 093deb83cd Automated Change by GitHub Action 2026-02-13 21:23:51 +00:00
okunze ba8a9c4664 Automated Change by GitHub Action 2026-02-06 21:19:03 +00:00
okunze c05a4ebbfb Automated Change by GitHub Action 2026-01-16 21:10:08 +00:00
okunze 73215692ce Automated Change by GitHub Action 2026-01-09 21:10:05 +00:00
okunze f0aeadea8b Automated Change by GitHub Action 2026-01-02 21:07:33 +00:00
okunze c26498ae91 Automated Change by GitHub Action 2025-12-12 21:09:02 +00:00
okunze f3386d5f01 Automated Change by GitHub Action 2025-11-28 21:08:17 +00:00
okunze b58a168f89 Automated Change by GitHub Action 2025-07-11 21:08:59 +00:00
okunze bd6f98d5ef Automated Change by GitHub Action 2025-07-04 21:09:12 +00:00
okunze 377d676348 Automated Change by GitHub Action 2025-06-27 21:08:43 +00:00
Oliver Kunze 9b506fd5f0 Update README.md 2025-06-11 14:30:11 +02:00
okunze 755a2ca262 Automated Change by GitHub Action 2025-06-11 12:21:48 +00:00
Oliver Kunze 1a7ab2e005 Update Check for updates.yml 2025-06-11 14:20:52 +02:00
Oliver Kunze d3055fdb9c Update Check for updates.yml 2025-06-11 14:17:48 +02:00
Oliver Kunze 59472bd224 Update Check for updates.yml
- Update actions
- Download all mentioned files
2025-06-11 14:14:00 +02:00
77 changed files with 12186 additions and 37 deletions
+302 -8
View File
@@ -15,32 +15,326 @@ jobs:
steps:
# https://github.com/marketplace/actions/checkout
- name: Checkout
uses: actions/checkout@v3.5.3
- name: Create download folder
uses: actions/checkout@v4.2.2
- name: Create ./download folder
run: mkdir -p ./download
- name: Create ./download/firmware folder
run: mkdir -p ./download/firmware
- name: Create ./download/oled folder
run: mkdir -p ./download/oled
- name: Create ./download/scripts folder
run: mkdir -p ./download/scripts
- name: Create ./download/tools folder
run: mkdir -p ./download/tools
- name: Create ./download/ups folder
run: mkdir -p ./download/ups
# https://github.com/marketplace/actions/github-action-for-wget
- name: Github Action for wget (ar1config.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ar1config.png -P ./download
- name: Github Action for wget (argon1.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/argon1.sh -P ./download
- name: Github Action for wget (ar1config.png)
- name: Github Action for wget (argon1v5.sh)
uses: wei/wget@v1.1.1
with:
args: http://download.argon40.com/ar1config.png -P ./download
- name: Github Action for wget (ar1uninstall.png)
args: https://download.argon40.com/argon1v5.sh -P ./download
- name: Github Action for wget (argon40.png)
uses: wei/wget@v1.1.1
with:
args: http://download.argon40.com/ar1uninstall.png -P ./download
args: https://download.argon40.com/argon40.png -P ./download
- name: Github Action for wget (argon-eeprom.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/argon-eeprom.sh -P ./download
- name: Github Action for wget (argoneon.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/argoneon.png -P ./download
- name: Github Action for wget (argonone-irdecoder.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/argonone-irdecoder.py -P ./download
- name: Github Action for wget (argononeup.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/argononeup.sh -P ./download
- name: Github Action for wget (ArgonOne.uf2)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/firmware/ArgonOne.uf2 -P ./download/firmware
- name: Github Action for wget (bgcpu.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/bgcpu.bin -P ./download/oled
- name: Github Action for wget (bgdefault.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/bgdefault.bin -P ./download/oled
- name: Github Action for wget (bgip.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/bgip.bin -P ./download/oled
- name: Github Action for wget (bgraid.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/bgraid.bin -P ./download/oled
- name: Github Action for wget (bgram.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/bgram.bin -P ./download/oled
- name: Github Action for wget (bgstorage.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/bgstorage.bin -P ./download/oled
- name: Github Action for wget (bgtemp.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/bgtemp.bin -P ./download/oled
- name: Github Action for wget (bgtime.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/bgtime.bin -P ./download/oled
- name: Github Action for wget (font16x12.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/font16x12.bin -P ./download/oled
- name: Github Action for wget (font16x8.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/font16x8.bin -P ./download/oled
- name: Github Action for wget (font24x16.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/font24x16.bin -P ./download/oled
- name: Github Action for wget (font32x24.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/font32x24.bin -P ./download/oled
- name: Github Action for wget (font48x32.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/font48x32.bin -P ./download/oled
- name: Github Action for wget (font64x48.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/font64x48.bin -P ./download/oled
- name: Github Action for wget (font8x6.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/font8x6.bin -P ./download/oled
- name: Github Action for wget (logo1v5.bin)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/oled/logo1v5.bin -P ./download/oled
- name: Github Action for wget (argon-blstrdac.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argon-blstrdac.sh -P ./download/scripts
- name: Github Action for wget (argondashboard.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argondashboard.py -P ./download/scripts
- name: Github Action for wget (argoneond.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argoneond.py -P ./download/scripts
- name: Github Action for wget (argoneond.service)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argoneond.service -P ./download/scripts
- name: Github Action for wget (argoneonoled.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argoneonoled.py -P ./download/scripts
- name: Github Action for wget (argoneon-oledconfig.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argoneon-oledconfig.sh -P ./download/scripts
- name: Github Action for wget (argoneon-rtcconfig.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argoneon-rtcconfig.sh -P ./download/scripts
- name: Github Action for wget (argonkeyboard.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonkeyboard.py -P ./download/scripts
- name: Github Action for wget (argononed.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononed.py -P ./download/scripts
- name: Github Action for wget (argononed.service)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononed.service -P ./download/scripts
- name: Github Action for wget (argonone-fanconfig.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonone-fanconfig.sh -P ./download/scripts
- name: Github Action for wget (argonone-irconfig.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/argonone-irconfig.sh -P ./download
args: https://download.argon40.com/scripts/argonone-irconfig.sh -P ./download/scripts
- name: Github Action for wget (argonone-irdecoder-libgpiod.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonone-irdecoder-libgpiod.py -P ./download/scripts
- name: Github Action for wget (argononeoled.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononeoled.py -P ./download/scripts
- name: Github Action for wget (argonone-oledconfig.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonone-oledconfig.sh -P ./download/scripts
- name: Github Action for wget (argononeoledd.service)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononeoledd.service -P ./download/scripts
- name: Github Action for wget (argononeupd.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononeupd.py -P ./download/scripts
- name: Github Action for wget (argononeupd.service)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononeupd.service -P ./download/scripts
- name: Github Action for wget (argononeupduser.service)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononeupduser.service -P ./download/scripts
- name: Github Action for wget (argononeup-lidconfig.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononeup-lidconfig.sh -P ./download/scripts
- name: Github Action for wget (argonone-upsconfig.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonone-upsconfig.sh -P ./download/scripts
- name: Github Action for wget (argononeupsd.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononeupsd.py -P ./download/scripts
- name: Github Action for wget (argononeupsd.service)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argononeupsd.service -P ./download/scripts
- name: Github Action for wget (argonpowerbutton-libgpiod.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonpowerbutton-libgpiod.py -P ./download/scripts
- name: Github Action for wget (argonpowerbutton-rpigpio.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonpowerbutton-rpigpio.py -P ./download/scripts
- name: Github Action for wget (argonregister.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonregister.py -P ./download/scripts
- name: Github Action for wget (argonregister-v1.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonregister-v1.py -P ./download/scripts
- name: Github Action for wget (argon-rpi-eeprom-config-default.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argon-rpi-eeprom-config-default.py -P ./download/scripts
- name: Github Action for wget (argon-rpi-eeprom-config-psu.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argon-rpi-eeprom-config-psu.py -P ./download/scripts
- name: Github Action for wget (argonrtc.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonrtc.py -P ./download/scripts
- name: Github Action for wget (argon-shutdown.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argon-shutdown.sh -P ./download/scripts
- name: Github Action for wget (argonstatus.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonstatus.py -P ./download/scripts
- name: Github Action for wget (argon-status.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argon-status.sh -P ./download/scripts
- name: Github Action for wget (argonsysinfo.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonsysinfo.py -P ./download/scripts
- name: Github Action for wget (argon-uninstall.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argon-uninstall.sh -P ./download/scripts
- name: Github Action for wget (argon-unitconfig.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argon-unitconfig.sh -P ./download/scripts
- name: Github Action for wget (argonupsrtcd.py)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonupsrtcd.py -P ./download/scripts
- name: Github Action for wget (argonupsrtcd.service)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argonupsrtcd.service -P ./download/scripts
- name: Github Action for wget (argon-versioninfo.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/scripts/argon-versioninfo.sh -P ./download/scripts
- name: Github Action for wget (setntpserver.sh)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/tools/setntpserver.sh -P ./download/tools
- name: Github Action for wget (battery_0.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/battery_0.png -P ./download/ups
- name: Github Action for wget (battery_1.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/battery_1.png -P ./download/ups
- name: Github Action for wget (battery_2.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/battery_2.png -P ./download/ups
- name: Github Action for wget (battery_3.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/battery_3.png -P ./download/ups
- name: Github Action for wget (battery_4.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/battery_4.png -P ./download/ups
- name: Github Action for wget (battery_alert.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/battery_alert.png -P ./download/ups
- name: Github Action for wget (battery_charging.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/battery_charging.png -P ./download/ups
- name: Github Action for wget (battery_plug.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/battery_plug.png -P ./download/ups
- name: Github Action for wget (battery_unknown.png)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/battery_unknown.png -P ./download/ups
- name: Github Action for wget (upsimg.tar.gz)
uses: wei/wget@v1.1.1
with:
args: https://download.argon40.com/ups/upsimg.tar.gz -P ./download/ups
- name: Delete ./source
run: rm -rf ./source
- name: Rename ./download to ./source
run: mv ./download ./source
# https://github.com/marketplace/actions/git-auto-commit
- name: Git Auto Commit
uses: stefanzweifel/git-auto-commit-action@v4.16.0
uses: stefanzweifel/git-auto-commit-action@v6.0.1
with:
commit_message: Automated Change by GitHub Action
+14 -14
View File
@@ -1,8 +1,8 @@
# Argon ONE (V2) Pi 4 Script
# Argon ONE Script
I have been using the [Argon ONE (V2) Case for Raspberry Pi 4](https://www.argon40.com/collections/raspberry-pi-cases "Argon ONE (V2) Case for Raspberry Pi 4") for a long time and I am very happy with it.
I have been using the [Argon ONE Cases for Raspberry Pi](https://www.argon40.com/collections/raspberry-pi-cases "Argon ONE Cases for Raspberry Pi") for a long time and I am very happy with them.
To be able to use the case to the full extent, it is recommended to install the [script](https://download.argon40.com/argon1.sh "https://download.argon40.com/argon1.sh") offered by [Argon 40](https://www.argon40.com "https://www.argon40.com").
To be able to use the cases to the full extent, it is recommended to install the [script](https://download.argon40.com/argon1.sh "https://download.argon40.com/argon1.sh") offered by [Argon 40](https://www.argon40.com "https://www.argon40.com").
I have saved a copy here along with the instructions to have a copy in case [Argon 40](https://www.argon40.com "https://www.argon40.com") no longer offers it themselves.
@@ -13,19 +13,19 @@ You can find them here:
* Argon 40 Website: [https://www.argon40.com](https://www.argon40.com "https://www.argon40.com")
* Argon 40 Github: [https://github.com/Argon40Tech](https://github.com/Argon40Tech "https://github.com/Argon40Tech")
## How to install Argon ONE (V2) Pi 4 Power Button & Fan Control
## How to install Argon ONE Pi Power Button & Fan Control
### Prerequisites
* [Raspberry Pi 4 Model B (2GB, 4GB or 8GB version)](https://www.raspberrypi.org/products/raspberry-pi-4-model-b/ "Raspberry Pi 4 Model B")
* [Raspberry Pi](https://www.raspberrypi.org/products/ "Raspberry Pi")
* [Raspberry Pi OS (previously called Raspbian)](https://www.raspberrypi.org/downloads/ "Raspberry Pi OS") installed on microSD card
* [Argon ONE (V2) Case for Raspberry Pi 4](https://www.argon40.com/collections/raspberry-pi-cases "Argon ONE (V2) Case for Raspberry Pi 4")
* [Argon ONE Cases for Raspberry Pi](https://www.argon40.com/collections/raspberry-pi-cases "Argon ONE Cases for Raspberry Pi")
### Installing
1. Connect to the internet.
2. Open "Terminal" in Raspbian.
3. Type the text below in the "Terminal" to initiate installation of Argon ONE (V2) Pi 4 script.
3. Type the text below in the "Terminal" to initiate installation of Argon ONE Pi script.
```
curl https://download.argon40.com/argon1.sh | bash
@@ -35,9 +35,9 @@ You can find them here:
## Usage Instructions
### Argon ONE (V2) Pi 4 Power Button Functions
### Argon ONE Pi Power Button Functions
ARGON ONE (V2) PI 4 STATE | ACTION | FUNCTION
ARGON ONE PI STATE | ACTION | FUNCTION
:------------------: | :----: | :------:
OFF | Short Press | Turn ON
ON | Long Press (>= 3 s) | Soft Shutdown and Power Cut
@@ -45,8 +45,8 @@ ON | Short Press (< 3 s) | Nothing
ON | Double Tap | Reboot
ON | Long Press (>= 5 s) | Forced Shutdown
### Argon ONE (V2) Pi 4 Fan Speed
Upon installation of the Argon ONE (V2) Pi 4 script by default, the settings of the Argon ONE (V2) Pi 4 cooling system are as follows:
### Argon ONE Pi Fan Speed
Upon installation of the Argon ONE Pi script by default, the settings of the Argon ONE Pi cooling system are as follows:
CPU TEMP | FAN POWER
:------: | :-------:
@@ -54,7 +54,7 @@ CPU TEMP | FAN POWER
60 C | 55%
65 C | 100%
However, you may change or configure the FAN to your desired settings by clicking the Argon ONE (V2) Pi 4 Config icon on your Desktop.
However, you may change or configure the FAN to your desired settings by clicking the Argon ONE Pi Config icon on your Desktop.
Or via "Terminal" by typing and following the specified format:
@@ -62,9 +62,9 @@ Or via "Terminal" by typing and following the specified format:
argonone-config
```
## Uninstalling Argon ONE (V2) Pi 4 Script
## Uninstalling Argon ONE Pi Script
To uninstall the Argon ONE (V2) Pi 4 script you may do so by clicking the Argon One (V2) Pi 4 Uninstall icon on your Desktop.
To uninstall the Argon ONE Pi script you may do so by clicking the Argon One Pi Uninstall icon on your Desktop.
You may also remove the script via "Terminal" by typing.
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
echo "*************"
echo " Argon Setup "
echo "*************"
# Helper variables
ARGONDOWNLOADSERVER=https://download.argon40.com
eepromrpiscript="/usr/bin/rpi-eeprom-config"
eepromconfigscript=/dev/shm/argon-eeprom.py
# Check if Raspbian, Ubuntu, others
CHECKPLATFORM="Others"
if [ -f "/etc/os-release" ]
then
source /etc/os-release
if [ "$ID" = "raspbian" ]
then
CHECKPLATFORM="Raspbian"
elif [ "$ID" = "debian" ]
then
# For backwards compatibility, continue using raspbian
CHECKPLATFORM="Raspbian"
elif [ "$ID" = "ubuntu" ]
then
CHECKPLATFORM="Ubuntu"
fi
fi
# Check if original eeprom script exists before running
if [ "$CHECKPLATFORM" = "Raspbian" ]
then
if [ -f "$eepromrpiscript" ]
then
sudo apt-get update && sudo apt-get upgrade -y
sudo rpi-eeprom-update
# EEPROM Config Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-rpi-eeprom-config-default.py -O $eepromconfigscript --quiet
sudo chmod 755 $eepromconfigscript
sudo $eepromconfigscript
fi
else
echo "Please run this under Raspberry Pi OS"
fi
+20 -3
View File
@@ -228,6 +228,8 @@ else
fi
fi
echo "Installing/updating dependencies..."
for curpkg in ${pkglist[@]}; do
sudo apt-get install -y $curpkg
RESULT=$(argon_check_pkg "$curpkg")
@@ -240,6 +242,8 @@ for curpkg in ${pkglist[@]}; do
fi
done
echo "Updating configuration ..."
# Ubuntu Mate for RPi has raspi-config too
command -v raspi-config &> /dev/null
if [ $? -eq 0 ]
@@ -271,8 +275,9 @@ then
rm $TMPCONFIGFILE
fi
# Added to enabled NVMe for pi5
# Additional config for pi5
set_nvme_default
set_maxusbcurrent
# Fan Setup
basename="argonone"
@@ -289,6 +294,7 @@ daemonfanservice=/lib/systemd/system/$daemonname.service
daemonhddconfigfile=/etc/${daemonname}-hdd.conf
echo "Installing/Updating scripts and services ..."
if [ -f "$eepromrpiscript" ]
then
@@ -304,6 +310,13 @@ then
sudo chmod 755 $upsconfigscript
fi
for TMPDIRECTORY in "/lib/systemd/system" "/lib/systemd/system-shutdown"
do
sudo mkdir -p "$TMPDIRECTORY"
sudo chmod 755 $TMPDIRECTORY
sudo chown root:root "$TMPDIRECTORY"
done
# Fan Config Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argonone-fanconfig.sh -O $fanconfigscript --quiet
sudo chmod 755 $fanconfigscript
@@ -705,6 +718,8 @@ fi
shortcutfile="/home/$destfoldername/Desktop/argonone-config.desktop"
if [ -d "/home/$destfoldername/Desktop" ]
then
echo "Creating/Updating Desktop Elements ..."
terminalcmd="lxterminal --working-directory=/home/$destfoldername/ -t"
if [ -f "/home/$destfoldername/.twisteros.twid" ]
then
@@ -735,6 +750,8 @@ fi
configcmd="$(basename -- $configscript)"
echo "Initializing Services ..."
if [ "$setupmode" = "Setup" ]
then
if [ -f "/usr/bin/$configcmd" ]
@@ -772,10 +789,12 @@ else
fi
fi
if [ "$CHECKPLATFORM" = "Raspbian" ]
then
if [ -f "$eepromrpiscript" ]
then
echo "Checking EEPROM ..."
sudo apt-get update && sudo apt-get upgrade -y
sudo rpi-eeprom-update
# EEPROM Config Script
@@ -785,8 +804,6 @@ else
echo "WARNING: EEPROM not updated. Please run this under Raspberry Pi OS"
fi
set_maxusbcurrent
echo "*********************"
echo " $setupmode Completed "
+822
View File
@@ -0,0 +1,822 @@
#!/bin/bash
echo "*************"
echo " Argon Setup "
echo "*************"
# Check time if need to 'fix'
NEEDSTIMESYNC=0
LOCALTIME=$(date -u +%s%N | cut -b1-10)
GLOBALTIME=$(curl -s 'http://worldtimeapi.org/api/ip.txt' | grep unixtime | cut -b11-20)
TIMEDIFF=$((GLOBALTIME-LOCALTIME))
# about 26hrs, max timezone difference
if [ $TIMEDIFF -gt 100000 ]
then
NEEDSTIMESYNC=1
fi
argon_time_error() {
echo "**********************************************"
echo "* WARNING: Device time seems to be incorrect *"
echo "* This may cause problems during setup. *"
echo "**********************************************"
echo "Possible Network Time Protocol Server issue"
echo "Try running the following to correct:"
echo " curl -k https://download.argon40.com/tools/setntpserver.sh | bash"
}
if [ $NEEDSTIMESYNC -eq 1 ]
then
argon_time_error
fi
# Helper variables
ARGONDOWNLOADSERVER=https://download.argon40.com
INSTALLATIONFOLDER=/etc/argon
FLAGFILEV1=$INSTALLATIONFOLDER/flag_v1
versioninfoscript=$INSTALLATIONFOLDER/argon-versioninfo.sh
uninstallscript=$INSTALLATIONFOLDER/argon-uninstall.sh
shutdownscript=/lib/systemd/system-shutdown/argon-shutdown.sh
configscript=$INSTALLATIONFOLDER/argon-config
unitconfigscript=$INSTALLATIONFOLDER/argon-unitconfig.sh
blstrdacconfigscript=$INSTALLATIONFOLDER/argon-blstrdac.sh
statusdisplayscript=$INSTALLATIONFOLDER/argon-status.sh
setupmode="Setup"
if [ -f $configscript ]
then
setupmode="Update"
echo "Updating files"
else
sudo mkdir $INSTALLATIONFOLDER
sudo chmod 755 $INSTALLATIONFOLDER
fi
##########
# Start code lifted from raspi-config
# is_pifive, get_serial_hw and do_serial_hw based on raspi-config
if [ -e /boot/firmware/config.txt ] ; then
FIRMWARE=/firmware
else
FIRMWARE=
fi
CONFIG=/boot${FIRMWARE}/config.txt
set_config_var() {
if ! grep -q -E "$1=$2" $3 ; then
echo "$1=$2" | sudo tee -a $3 > /dev/null
fi
}
is_pifive() {
grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F]4[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo
return $?
}
get_serial_hw() {
if is_pifive ; then
if grep -q -E "dtparam=uart0=off" $CONFIG ; then
echo 1
elif grep -q -E "dtparam=uart0" $CONFIG ; then
echo 0
else
echo 1
fi
else
if grep -q -E "^enable_uart=1" $CONFIG ; then
echo 0
elif grep -q -E "^enable_uart=0" $CONFIG ; then
echo 1
elif [ -e /dev/serial0 ] ; then
echo 0
else
echo 1
fi
fi
}
do_serial_hw() {
if [ $1 -eq 0 ] ; then
if is_pifive ; then
set_config_var dtparam=uart0 on $CONFIG
else
set_config_var enable_uart 1 $CONFIG
fi
else
if is_pifive ; then
sudo sed $CONFIG -i -e "/dtparam=uart0.*/d"
else
set_config_var enable_uart 0 $CONFIG
fi
fi
}
# End code lifted from raspi-config
##########
# Reuse is_pifive, set_config_var
set_nvme_default() {
if is_pifive ; then
set_config_var dtparam nvme $CONFIG
set_config_var dtparam=pciex1_gen 3 $CONFIG
fi
}
set_maxusbcurrent() {
if is_pifive ; then
#set_config_var max_usb_current 1 $CONFIG
set_config_var usb_max_current_enable 1 $CONFIG
fi
}
argon_check_pkg() {
RESULT=$(dpkg-query -W -f='${Status}\n' "$1" 2> /dev/null | grep "installed")
if [ "" == "$RESULT" ]; then
echo "NG"
else
echo "OK"
fi
}
CHECKDEVICE="oneoled" # Hardcoded for argon1oled
# Check if has RTC
# Todo for multiple OS
#i2cdetect -y 1 | grep -q ' 51 '
#if [ $? -eq 0 ]
#then
# CHECKDEVICE="eon"
#fi
CHECKGPIOMODE="libgpiod" # libgpiod or rpigpio
# Check if Raspbian, Ubuntu, others
CHECKPLATFORM="Others"
CHECKPLATFORMVERSION=""
CHECKPLATFORMVERSIONNUM=""
if [ -f "/etc/os-release" ]
then
source /etc/os-release
if [ "$ID" = "raspbian" ]
then
CHECKPLATFORM="Raspbian"
CHECKPLATFORMVERSION=$VERSION_ID
elif [ "$ID" = "debian" ]
then
# For backwards compatibility, continue using raspbian
CHECKPLATFORM="Raspbian"
CHECKPLATFORMVERSION=$VERSION_ID
elif [ "$ID" = "ubuntu" ]
then
CHECKPLATFORM="Ubuntu"
CHECKPLATFORMVERSION=$VERSION_ID
fi
echo ${CHECKPLATFORMVERSION} | grep -e "\." > /dev/null
if [ $? -eq 0 ]
then
CHECKPLATFORMVERSIONNUM=`cut -d "." -f2 <<< $CHECKPLATFORMVERSION `
CHECKPLATFORMVERSION=`cut -d "." -f1 <<< $CHECKPLATFORMVERSION `
fi
fi
gpiopkg="python3-libgpiod"
if [ "$CHECKGPIOMODE" = "rpigpio" ]
then
if [ "$CHECKPLATFORM" = "Raspbian" ]
then
gpiopkg="raspi-gpio python3-rpi.gpio"
else
gpiopkg="python3-rpi.gpio"
fi
fi
if [ "$CHECKPLATFORM" = "Raspbian" ]
then
if [ "$CHECKDEVICE" = "eon" ]
then
pkglist=($gpiopkg python3-smbus i2c-tools smartmontools)
elif [ "$CHECKDEVICE" = "oneoled" ]
then
pkglist=($gpiopkg python3-smbus i2c-tools python3-luma.oled)
else
pkglist=($gpiopkg python3-smbus i2c-tools)
fi
else
# Todo handle lgpio
# Ubuntu has serial and i2c enabled
if [ "$CHECKDEVICE" = "eon" ]
then
pkglist=($gpiopkg python3-smbus i2c-tools smartmontools)
elif [ "$CHECKDEVICE" = "oneoled" ]
then
pkglist=($gpiopkg python3-smbus i2c-tools python3-luma.oled)
else
pkglist=($gpiopkg python3-smbus i2c-tools)
fi
fi
echo "Installing/updating dependencies..."
for curpkg in ${pkglist[@]}; do
sudo apt-get install -y $curpkg
RESULT=$(argon_check_pkg "$curpkg")
if [ "NG" == "$RESULT" ]
then
echo "********************************************************************"
echo "Please also connect device to the internet and restart installation."
echo "********************************************************************"
exit
fi
done
echo "Updating configuration ..."
# Ubuntu Mate for RPi has raspi-config too
command -v raspi-config &> /dev/null
if [ $? -eq 0 ]
then
# Enable i2c and serial
sudo raspi-config nonint do_i2c 0
if [ ! "$CHECKDEVICE" = "fanhat" ]
then
if [ "$CHECKPLATFORM" = "Raspbian" ]
then
# bookworm raspi-config prompts user when configuring serial
if [ $(get_serial_hw) -eq 1 ]; then
do_serial_hw 0
fi
else
sudo raspi-config nonint do_serial 2
fi
fi
fi
if [ "$CHECKDEVICE" = "oneoled" ]
then
TMPCONFIGFILE="/dev/shm/tmpconfig.txt"
cat $CONFIG | grep -v 'dtoverlay=dwc2' > $TMPCONFIGFILE
chmod 755 $TMPCONFIGFILE
sudo cp $TMPCONFIGFILE $CONFIG
set_config_var dtoverlay=dwc2,dr_mode host $CONFIG
rm $TMPCONFIGFILE
fi
# Additional config for pi5
set_nvme_default
set_maxusbcurrent
# Fan Setup
basename="argonone"
daemonname=$basename"d"
irconfigscript=$INSTALLATIONFOLDER/${basename}-ir
upsconfigscript=$INSTALLATIONFOLDER/${basename}-upsconfig.sh
fanconfigscript=$INSTALLATIONFOLDER/${basename}-fanconfig.sh
eepromrpiscript="/usr/bin/rpi-eeprom-config"
eepromconfigscript=$INSTALLATIONFOLDER/${basename}-eepromconfig.py
powerbuttonscript=$INSTALLATIONFOLDER/$daemonname.py
unitconfigfile=/etc/argonunits.conf
daemonconfigfile=/etc/$daemonname.conf
daemonfanservice=/lib/systemd/system/$daemonname.service
daemonhddconfigfile=/etc/${daemonname}-hdd.conf
echo "Installing/Updating scripts and services ..."
if [ -f "$eepromrpiscript" ]
then
# EEPROM Config Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-rpi-eeprom-config-psu.py -O $eepromconfigscript --quiet
sudo chmod 755 $eepromconfigscript
fi
if is_pifive
then
# UPS Config Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argonone-upsconfig.sh -O $upsconfigscript --quiet
sudo chmod 755 $upsconfigscript
fi
for TMPDIRECTORY in "/lib/systemd/system" "/lib/systemd/system-shutdown"
do
sudo mkdir -p "$TMPDIRECTORY"
sudo chmod 755 $TMPDIRECTORY
sudo chown root:root "$TMPDIRECTORY"
done
# Fan Config Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argonone-fanconfig.sh -O $fanconfigscript --quiet
sudo chmod 755 $fanconfigscript
# Fan Daemon/Service Files
sudo wget $ARGONDOWNLOADSERVER/scripts/argononed.py -O $powerbuttonscript --quiet
if [ "$CHECKDEVICE" = "oneoled" ]
then
sudo wget $ARGONDOWNLOADSERVER/scripts/argononeoledd.service -O $daemonfanservice --quiet
else
sudo wget $ARGONDOWNLOADSERVER/scripts/argononed.service -O $daemonfanservice --quiet
fi
sudo chmod 644 $daemonfanservice
if [ ! "$CHECKDEVICE" = "fanhat" ]
then
# IR Files
sudo wget $ARGONDOWNLOADSERVER/scripts/argonone-irconfig.sh -O $irconfigscript --quiet
sudo chmod 755 $irconfigscript
if [ ! "$CHECKDEVICE" = "eon" ]
then
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-blstrdac.sh -O $blstrdacconfigscript --quiet
sudo chmod 755 $blstrdacconfigscript
fi
fi
# Other utility scripts
sudo wget $ARGONDOWNLOADSERVER/scripts/argonstatus.py -O $INSTALLATIONFOLDER/argonstatus.py --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argondashboard.py -O $INSTALLATIONFOLDER/argondashboard.py --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-status.sh -O $statusdisplayscript --quiet
sudo chmod 755 $statusdisplayscript
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-versioninfo.sh -O $versioninfoscript --quiet
sudo chmod 755 $versioninfoscript
sudo wget $ARGONDOWNLOADSERVER/scripts/argonsysinfo.py -O $INSTALLATIONFOLDER/argonsysinfo.py --quiet
if [ -f "$FLAGFILEV1" ]
then
sudo wget $ARGONDOWNLOADSERVER/scripts/argonregister-v1.py -O $INSTALLATIONFOLDER/argonregister.py --quiet
else
sudo wget $ARGONDOWNLOADSERVER/scripts/argonregister.py -O $INSTALLATIONFOLDER/argonregister.py --quiet
fi
sudo wget "$ARGONDOWNLOADSERVER/scripts/argonpowerbutton-${CHECKGPIOMODE}.py" -O $INSTALLATIONFOLDER/argonpowerbutton.py --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argononed.py -O $powerbuttonscript --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-unitconfig.sh -O $unitconfigscript --quiet
sudo chmod 755 $unitconfigscript
# Generate default Fan config file if non-existent
if [ ! -f $daemonconfigfile ]; then
sudo touch $daemonconfigfile
sudo chmod 666 $daemonconfigfile
echo '#' >> $daemonconfigfile
echo '# Argon Fan Speed Configuration (CPU)' >> $daemonconfigfile
echo '#' >> $daemonconfigfile
echo '55=30' >> $daemonconfigfile
echo '60=55' >> $daemonconfigfile
echo '65=100' >> $daemonconfigfile
fi
if [ "$CHECKDEVICE" = "eon" ]
then
if [ ! -f $daemonhddconfigfile ]; then
sudo touch $daemonhddconfigfile
sudo chmod 666 $daemonhddconfigfile
echo '#' >> $daemonhddconfigfile
echo '# Argon Fan Speed Configuration (HDD)' >> $daemonhddconfigfile
echo '#' >> $daemonhddconfigfile
echo '35=30' >> $daemonhddconfigfile
echo '40=55' >> $daemonhddconfigfile
echo '45=100' >> $daemonhddconfigfile
fi
fi
# Generate default Unit config file if non-existent
if [ ! -f $unitconfigfile ]; then
sudo touch $unitconfigfile
sudo chmod 666 $unitconfigfile
echo '#' >> $unitconfigfile
fi
if [ "$CHECKDEVICE" = "eon" ]
then
# RTC Setup
basename="argoneon"
daemonname=$basename"d"
rtcconfigfile=/etc/argoneonrtc.conf
rtcconfigscript=$INSTALLATIONFOLDER/${basename}-rtcconfig.sh
daemonrtcservice=/lib/systemd/system/$daemonname.service
rtcdaemonscript=$INSTALLATIONFOLDER/$daemonname.py
# Generate default RTC config file if non-existent
if [ ! -f $rtcconfigfile ]; then
sudo touch $rtcconfigfile
sudo chmod 666 $rtcconfigfile
echo '#' >> $rtcconfigfile
echo '# Argon RTC Configuration' >> $rtcconfigfile
echo '#' >> $rtcconfigfile
fi
# RTC Config Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argoneon-rtcconfig.sh -O $rtcconfigscript --quiet
sudo chmod 755 $rtcconfigscript
# RTC Daemon/Service Files
sudo wget $ARGONDOWNLOADSERVER/scripts/argonrtc.py -O $INSTALLATIONFOLDER/argonrtc.py --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argoneond.py -O $rtcdaemonscript --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argoneond.service -O $daemonrtcservice --quiet
sudo chmod 644 $daemonrtcservice
fi
if [ "$CHECKDEVICE" = "eon" ] || [ "$CHECKDEVICE" = "oneoled" ]
then
# OLED Setup
basename="argoneon"
daemonname=$basename"d"
oledconfigscript=$INSTALLATIONFOLDER/${basename}-oledconfig.sh
oledlibscript=$INSTALLATIONFOLDER/${basename}oled.py
oledconfigfile=/etc/argoneonoled.conf
# Generate default OLED config file if non-existent
if [ ! -f $oledconfigfile ]; then
sudo touch $oledconfigfile
sudo chmod 666 $oledconfigfile
echo '#' >> $oledconfigfile
echo '# Argon OLED Configuration' >> $oledconfigfile
echo '#' >> $oledconfigfile
echo 'switchduration=30' >> $oledconfigfile
if [ "$CHECKDEVICE" = "eon" ]
then
echo 'screenlist="clock cpu storage raid ram temp ip"' >> $oledconfigfile
else
echo 'screenlist="logo1v5 clock cpu storage ram temp ip"' >> $oledconfigfile
fi
fi
# OLED Config Script
if [ "$CHECKDEVICE" = "eon" ]
then
sudo wget $ARGONDOWNLOADSERVER/scripts/argoneonoled.py -O $oledlibscript --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argoneon-oledconfig.sh -O $oledconfigscript --quiet
else
sudo wget $ARGONDOWNLOADSERVER/scripts/argononeoled.py -O $oledlibscript --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argonone-oledconfig.sh -O $oledconfigscript --quiet
fi
sudo chmod 755 $oledconfigscript
if [ ! -d $INSTALLATIONFOLDER/oled ]
then
sudo mkdir $INSTALLATIONFOLDER/oled
fi
for binfile in font8x6 font16x12 font32x24 font64x48 font16x8 font24x16 font48x32 bgdefault bgram bgip bgtemp bgcpu bgraid bgstorage bgtime
do
sudo wget $ARGONDOWNLOADSERVER/oled/${binfile}.bin -O $INSTALLATIONFOLDER/oled/${binfile}.bin --quiet
done
if [ "$CHECKDEVICE" = "oneoled" ]
then
for binfile in logo1v5
do
sudo wget $ARGONDOWNLOADSERVER/oled/${binfile}.bin -O $INSTALLATIONFOLDER/oled/${binfile}.bin --quiet
done
fi
fi
# Argon Uninstall Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-uninstall.sh -O $uninstallscript --quiet
sudo chmod 755 $uninstallscript
# Argon Shutdown script
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-shutdown.sh -O $shutdownscript --quiet
sudo chmod 755 $shutdownscript
# Argon Config Script
if [ -f $configscript ]; then
sudo rm $configscript
fi
sudo touch $configscript
# To ensure we can write the following lines
sudo chmod 666 $configscript
echo '#!/bin/bash' >> $configscript
echo 'echo "--------------------------"' >> $configscript
echo 'echo "Argon Configuration Tool"' >> $configscript
echo "$versioninfoscript simple" >> $configscript
echo 'echo "--------------------------"' >> $configscript
echo 'get_number () {' >> $configscript
echo ' read curnumber' >> $configscript
echo ' if [ -z "$curnumber" ]' >> $configscript
echo ' then' >> $configscript
echo ' echo "-2"' >> $configscript
echo ' return' >> $configscript
echo ' elif [[ $curnumber =~ ^[+-]?[0-9]+$ ]]' >> $configscript
echo ' then' >> $configscript
echo ' if [ $curnumber -lt 0 ]' >> $configscript
echo ' then' >> $configscript
echo ' echo "-1"' >> $configscript
echo ' return' >> $configscript
echo ' elif [ $curnumber -gt 100 ]' >> $configscript
echo ' then' >> $configscript
echo ' echo "-1"' >> $configscript
echo ' return' >> $configscript
echo ' fi ' >> $configscript
echo ' echo $curnumber' >> $configscript
echo ' return' >> $configscript
echo ' fi' >> $configscript
echo ' echo "-1"' >> $configscript
echo ' return' >> $configscript
echo '}' >> $configscript
echo '' >> $configscript
echo 'mainloopflag=1' >> $configscript
echo 'while [ $mainloopflag -eq 1 ]' >> $configscript
echo 'do' >> $configscript
echo ' echo' >> $configscript
echo ' echo "Choose Option:"' >> $configscript
if [ ! "$CHECKDEVICE" = "oneoled" ]
then
echo ' echo " 1. Configure Fan"' >> $configscript
fi
blstrdacoption=0
if [ "$CHECKDEVICE" = "fanhat" ]
then
uninstalloption="4"
else
if [ ! "$CHECKDEVICE" = "oneoled" ]
then
echo ' echo " 2. Configure IR"' >> $configscript
fi
if [ "$CHECKDEVICE" = "eon" ]
then
# ArgonEON Has RTC
echo ' echo " 3. Configure RTC and/or Schedule"' >> $configscript
echo ' echo " 4. Configure OLED"' >> $configscript
uninstalloption="7"
elif [ "$CHECKDEVICE" = "oneoled" ]
then
echo ' echo " 1. Configure OLED"' >> $configscript
echo ' echo " 2. Argon Industria UPS"' >> $configscript
uninstalloption="5"
elif is_pifive
then
echo ' echo " 3. Argon Industria UPS"' >> $configscript
uninstalloption="7"
blstrdacoption=$(($uninstalloption-3))
echo " echo \" $blstrdacoption. Configure BLSTR DAC (v3/v5 only)\"" >> $configscript
else
uninstalloption="6"
blstrdacoption=$(($uninstalloption-3))
echo " echo \" $blstrdacoption. Configure BLSTR DAC (v3 only)\"" >> $configscript
fi
fi
unitsoption=$(($uninstalloption-2))
echo " echo \" $unitsoption. Configure Units\"" >> $configscript
statusoption=$(($uninstalloption-1))
echo " echo \" $statusoption. System Information\"" >> $configscript
echo " echo \" $uninstalloption. Uninstall\"" >> $configscript
echo ' echo ""' >> $configscript
echo ' echo " 0. Exit"' >> $configscript
echo " echo -n \"Enter Number (0-$uninstalloption):\"" >> $configscript
echo ' newmode=$( get_number )' >> $configscript
echo ' if [ $newmode -eq 0 ]' >> $configscript
echo ' then' >> $configscript
echo ' echo "Thank you."' >> $configscript
echo ' mainloopflag=0' >> $configscript
echo ' elif [ $newmode -eq 1 ]' >> $configscript
echo ' then' >> $configscript
# Option 1
if [ "$CHECKDEVICE" = "eon" ]
then
echo ' echo "Choose Triggers:"' >> $configscript
echo ' echo " 1. CPU Temperature"' >> $configscript
echo ' echo " 2. HDD Temperature"' >> $configscript
echo ' echo ""' >> $configscript
echo ' echo " 0. Cancel"' >> $configscript
echo " echo -n \"Enter Number (0-2):\"" >> $configscript
echo ' submode=$( get_number )' >> $configscript
echo ' if [ $submode -eq 1 ]' >> $configscript
echo ' then' >> $configscript
echo " $fanconfigscript" >> $configscript
echo ' mainloopflag=0' >> $configscript
echo ' elif [ $submode -eq 2 ]' >> $configscript
echo ' then' >> $configscript
echo " $fanconfigscript hdd" >> $configscript
echo ' mainloopflag=0' >> $configscript
echo ' fi' >> $configscript
elif [ "$CHECKDEVICE" = "oneoled" ]
then
echo " $oledconfigscript" >> $configscript
echo ' mainloopflag=0' >> $configscript
else
echo " $fanconfigscript" >> $configscript
echo ' mainloopflag=0' >> $configscript
fi
# Options 2 onwards
if [ ! "$CHECKDEVICE" = "fanhat" ]
then
if [ "$CHECKDEVICE" = "oneoled" ]
then
echo ' elif [ $newmode -eq 2 ]' >> $configscript
echo ' then' >> $configscript
echo " $upsconfigscript" >> $configscript
#echo ' mainloopflag=0' >> $configscript
else
echo ' elif [ $newmode -eq 2 ]' >> $configscript
echo ' then' >> $configscript
echo " $irconfigscript" >> $configscript
#echo ' mainloopflag=0' >> $configscript
if [ "$CHECKDEVICE" = "eon" ]
then
echo ' elif [ $newmode -eq 3 ]' >> $configscript
echo ' then' >> $configscript
echo " $rtcconfigscript" >> $configscript
#echo ' mainloopflag=0' >> $configscript
echo ' elif [ $newmode -eq 4 ]' >> $configscript
echo ' then' >> $configscript
echo " $oledconfigscript" >> $configscript
#echo ' mainloopflag=0' >> $configscript
elif is_pifive
then
echo ' elif [ $newmode -eq 3 ]' >> $configscript
echo ' then' >> $configscript
echo " $upsconfigscript" >> $configscript
#echo ' mainloopflag=0' >> $configscript
fi
if [ $blstrdacoption -gt 0 ]
then
echo " elif [ \$newmode -eq $blstrdacoption ]" >> $configscript
echo ' then' >> $configscript
echo " $blstrdacconfigscript" >> $configscript
#echo ' mainloopflag=0' >> $configscript
fi
fi
fi
# Standard options
echo " elif [ \$newmode -eq $unitsoption ]" >> $configscript
echo ' then' >> $configscript
echo " $unitconfigscript" >> $configscript
#echo ' mainloopflag=0' >> $configscript
echo " elif [ \$newmode -eq $statusoption ]" >> $configscript
echo ' then' >> $configscript
echo " $statusdisplayscript" >> $configscript
echo " elif [ \$newmode -eq $uninstalloption ]" >> $configscript
echo ' then' >> $configscript
echo " $uninstallscript" >> $configscript
echo ' mainloopflag=0' >> $configscript
echo ' fi' >> $configscript
echo 'done' >> $configscript
sudo chmod 755 $configscript
# Desktop Icon
destfoldername=$USERNAME
if [ -z "$destfoldername" ]
then
destfoldername=$USER
fi
if [ -z "$destfoldername" ]
then
destfoldername="pi"
fi
shortcutfile="/home/$destfoldername/Desktop/argonone-config.desktop"
if [ -d "/home/$destfoldername/Desktop" ]
then
echo "Creating/Updating Desktop Elements ..."
terminalcmd="lxterminal --working-directory=/home/$destfoldername/ -t"
if [ -f "/home/$destfoldername/.twisteros.twid" ]
then
terminalcmd="xfce4-terminal --default-working-directory=/home/$destfoldername/ -T"
fi
imagefile=ar1config.png
if [ "$CHECKDEVICE" = "eon" ]
then
imagefile=argoneon.png
fi
sudo wget https://download.argon40.com/$imagefile -O /usr/share/pixmaps/$imagefile --quiet
if [ -f $shortcutfile ]; then
sudo rm $shortcutfile
fi
# Create Shortcuts
echo "[Desktop Entry]" > $shortcutfile
echo "Name=Argon Configuration" >> $shortcutfile
echo "Comment=Argon Configuration" >> $shortcutfile
echo "Icon=/usr/share/pixmaps/$imagefile" >> $shortcutfile
echo 'Exec='$terminalcmd' "Argon Configuration" -e '$configscript >> $shortcutfile
echo "Type=Application" >> $shortcutfile
echo "Encoding=UTF-8" >> $shortcutfile
echo "Terminal=false" >> $shortcutfile
echo "Categories=None;" >> $shortcutfile
chmod 755 $shortcutfile
fi
configcmd="$(basename -- $configscript)"
echo "Initializing Services ..."
if [ "$setupmode" = "Setup" ]
then
if [ -f "/usr/bin/$configcmd" ]
then
sudo rm /usr/bin/$configcmd
fi
sudo ln -s $configscript /usr/bin/$configcmd
if [ "$CHECKDEVICE" = "one" ] || [ "$CHECKDEVICE" = "oneoled" ]
then
sudo ln -s $configscript /usr/bin/argonone-config
sudo ln -s $uninstallscript /usr/bin/argonone-uninstall
sudo ln -s $irconfigscript /usr/bin/argonone-ir
elif [ "$CHECKDEVICE" = "fanhat" ]
then
sudo ln -s $configscript /usr/bin/argonone-config
sudo ln -s $uninstallscript /usr/bin/argonone-uninstall
fi
# Enable and Start Service(s)
sudo systemctl daemon-reload
sudo systemctl enable argononed.service
sudo systemctl start argononed.service
if [ "$CHECKDEVICE" = "eon" ]
then
sudo systemctl enable argoneond.service
sudo systemctl start argoneond.service
fi
else
sudo systemctl daemon-reload
sudo systemctl restart argononed.service
if [ "$CHECKDEVICE" = "eon" ]
then
sudo systemctl restart argoneond.service
fi
fi
if [ "$CHECKPLATFORM" = "Raspbian" ]
then
if [ -f "$eepromrpiscript" ]
then
echo "Checking EEPROM ..."
sudo apt-get update && sudo apt-get upgrade -y
sudo rpi-eeprom-update
# EEPROM Config Script
sudo $eepromconfigscript
fi
else
echo "WARNING: EEPROM not updated. Please run this under Raspberry Pi OS"
fi
echo "*********************"
echo " $setupmode Completed "
echo "*********************"
$versioninfoscript
echo
echo "Use '$configcmd' to configure device"
echo
if [ $NEEDSTIMESYNC -eq 1 ]
then
argon_time_error
fi
Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+514
View File
@@ -0,0 +1,514 @@
#!/usr/bin/python3
# Standard Headers
import sys
import smbus
# For GPIO
import RPi.GPIO as GPIO
from datetime import datetime
import os
import time
# Check if Lirc Lib is installed
haslirclib = os.path.isfile("/usr/bin/mode2")
if haslirclib == True:
from multiprocessing import Process
#########################
# Use GPIO
def getGPIOPulseData():
irreceiver_pin = 23 # IR Receiver Pin
GPIO.setmode(GPIO.BCM)
GPIO.setup(irreceiver_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
# Counter
ctr = 0
value = GPIO.input(irreceiver_pin)
# mark time
startTime = datetime.now()
pulseTime = startTime
# Pulse Data
pulsedata = []
aborted = False
while aborted == False:
# Wait for transition
try:
if value:
channel = GPIO.wait_for_edge(irreceiver_pin, GPIO.FALLING, timeout=PULSETIMEOUTMS)
else:
channel = GPIO.wait_for_edge(irreceiver_pin, GPIO.RISING, timeout=PULSETIMEOUTMS)
except Exception as e:
# GPIO Error
GPIO.cleanup()
return [(-2, -2)]
if channel is None:
if ctr == 0:
continue
else:
aborted = True
if len(pulsedata) == 0:
# CTRL+C
return [(-1, -1)]
break
# high/low Length
now = datetime.now()
pulseLength = now - pulseTime
pulseTime = now
# Update value (changed triggered), this also inverts value before saving
if value:
value = 0
else:
value = 1
if pulseLength.microseconds > PULSETAIL_MAXMICROS_NEC and ctr == 0:
continue
pulsedata.append((value, pulseLength.microseconds))
ctr = ctr + 1
if pulseLength.microseconds > PULSETAIL_MAXMICROS_NEC:
break
elif ctr > PULSEDATA_MAXCOUNT:
break
GPIO.cleanup()
# Data is most likely incomplete
if aborted == True:
return []
elif ctr >= PULSEDATA_MAXCOUNT:
print (" * Unable to decode. Please try again *")
return []
return pulsedata
#########################
# Use LIRC
def lircMode2Task(irlogfile):
os.system("mode2 > "+irlogfile+" 2>&1")
def startLIRCMode2Logging(irlogfile):
# create a new process
loggerprocess = Process(target=lircMode2Task,args=(irlogfile,))
loggerprocess.start()
# mode2 will start new process, terminate current
time.sleep(0.1)
loggerprocess.kill()
return True
def endLIRCMode2Logging(irlogfile):
tmplogfile = irlogfile+".tmp"
os.system("ps | grep ode2 > "+tmplogfile+"")
if os.path.exists(tmplogfile) == True:
ctr = 0
fp = open(tmplogfile, "r")
for curline in fp:
if len(curline) > 0:
rowdata = curline.split(" ")
pid = ""
processname = ""
colidx = 0
while colidx < len(rowdata):
if len(rowdata[colidx]) > 0:
if pid == "":
pid = rowdata[colidx]
else:
processname = rowdata[colidx]
colidx = colidx + 1
if processname=="mode2\n":
os.system("kill -9 "+pid)
fp.close()
os.remove(tmplogfile)
return True
def getLIRCPulseData():
if haslirclib == False:
print (" * LIRC Module not found, please reboot and try again *")
return []
irlogfile = "/dev/shm/lircdecoder.log"
loggerresult = startLIRCMode2Logging(irlogfile)
if loggerresult == False:
return [(-1, -1)]
# Wait for log file
logsize = 0
while logsize == 0:
if os.path.exists(irlogfile) == True:
logsize = os.path.getsize(irlogfile)
if logsize == 0:
time.sleep(0.1)
# Wait for data to start
newlogsize = logsize
while logsize == newlogsize:
time.sleep(0.1)
newlogsize = os.path.getsize(irlogfile)
print(" Thank you")
# Wait for data to stop
while logsize != newlogsize:
logsize = newlogsize
time.sleep(0.1)
newlogsize = os.path.getsize(irlogfile)
# Finalize File
loggerresult = endLIRCMode2Logging(irlogfile)
if loggerresult == False:
return [(-1, -1)]
# Decode logfile into Pulse Data
pulsedata = []
terminated = False
if os.path.exists(irlogfile) == True:
ctr = 0
fp = open(irlogfile, "r")
for curline in fp:
if len(curline) > 0:
rowdata = curline.split(" ")
if len(rowdata) == 2:
duration = int(rowdata[1])
value = 0
if rowdata[0] == "pulse":
value = 1
ctr = ctr + 1
if value == 1 or ctr > 1:
if len(pulsedata) > 0 and duration > PULSELEADER_MINMICROS_NEC:
terminated = True
break
else:
pulsedata.append((value, duration))
fp.close()
os.remove(irlogfile)
# Check if terminating pulse detected
if terminated == False:
print (" * Unable to read signal. Please try again *")
return []
return pulsedata
#########################
# Common
irconffile = "/etc/lirc/lircd.conf.d/argon.lircd.conf"
# I2C
address = 0x1a # I2C Address
addressregister = 0xaa # I2C Address Register
# Constants
PULSETIMEOUTMS = 1000
VERIFYTARGET = 3
PULSEDATA_MAXCOUNT = 200 # Fail safe
# NEC Protocol Constants
PULSEBIT_MAXMICROS_NEC = 2500
PULSEBIT_ZEROMICROS_NEC = 1000
PULSELEADER_MINMICROS_NEC = 8000
PULSELEADER_MAXMICROS_NEC = 10000
PULSETAIL_MAXMICROS_NEC = 12000
# Flags
FLAGV1ONLY = False
try:
if os.path.isfile("/etc/argon/flag_v1"):
FLAGV1ONLY = True
except Exception:
FLAGV1ONLY = False
# Standard Methods
def getbytestring(pulsedata):
outstring = ""
for curbyte in pulsedata:
tmpstr = hex(curbyte)[2:]
while len(tmpstr) < 2:
tmpstr = "0" + tmpstr
outstring = outstring+tmpstr
return outstring
def displaybyte(pulsedata):
print (getbytestring(pulsedata))
def pulse2byteNEC(pulsedata):
outdata = []
bitdata = 1
curbyte = 0
bitcount = 0
for (mode, duration) in pulsedata:
if mode == 1:
continue
elif duration > PULSEBIT_MAXMICROS_NEC:
continue
elif duration > PULSEBIT_ZEROMICROS_NEC:
curbyte = curbyte*2 + 1
else:
curbyte = curbyte*2
bitcount = bitcount + 1
if bitcount == 8:
outdata.append(curbyte)
curbyte = 0
bitcount = 0
# Shouldn't happen, but just in case
if bitcount > 0:
outdata.append(curbyte)
return outdata
def bytecompare(a, b):
idx = 0
maxidx = len(a)
if maxidx != len(b):
return 1
while idx < maxidx:
if a[idx] != b[idx]:
return 1
idx = idx + 1
return 0
# Main Flow
mode = "custom"
if len(sys.argv) > 1:
mode = sys.argv[1]
powerdata = []
buttonlist = ['POWER', 'UP', 'DOWN', 'LEFT', 'RIGHT',
'VOLUMEUP', 'VOLUMEDOWN', 'OK', 'HOME', 'MENU'
'BACK']
ircodelist = ['00ff39c6', '00ff53ac', '00ff4bb4', '00ff9966', '00ff837c',
'00ff01fe', '00ff817e', '00ff738c', '00ffd32c', '00ffb946',
'00ff09f6']
buttonidx = 0
if mode == "power":
buttonlist = ['POWER']
ircodelist = ['']
elif mode == "resetpower":
# Just Set the power so it won't create/update the conf file
buttonlist = ['POWER']
mode = "default"
elif mode == "custom":
buttonlist = ['POWER', 'UP', 'DOWN', 'LEFT', 'RIGHT',
'VOLUMEUP', 'VOLUMEDOWN', 'OK', 'HOME', 'MENU'
'BACK']
ircodelist = ['', '', '', '', '',
'', '', '', '', '',
'']
#buttonlist = ['POWER', 'VOLUMEUP', 'VOLUMEDOWN']
#ircodelist = ['', '', '']
if mode == "default":
# To skip the decoding loop
buttonidx = len(buttonlist)
# Set MCU IR code
powerdata = [0x00, 0xff, 0x39, 0xc6]
else:
print ("************************************************")
print ("* WARNING: Current buttons are still active. *")
print ("* Please temporarily assign to a *")
print ("* different button if you plan to *")
print ("* reuse buttons. *")
print ("* e.g. Power Button triggers shutdown *")
print ("* *")
print ("* PROCEED AT YOUR OWN RISK *")
print ("* (Press CTRL+C to abort at any time) *")
print ("************************************************")
readaborted = False
# decoding loop
while buttonidx < len(buttonlist):
print ("Press your button for "+buttonlist[buttonidx]+" (CTRL+C to abort)")
irprotocol = ""
outdata = []
verifycount = 0
readongoing = True
# Handles NEC protocol Only
while readongoing == True:
# Try GPIO-based reading, if it fails, fallback to LIRC
pulsedata = getGPIOPulseData()
if len(pulsedata) == 1:
if pulsedata[0][0] == -2:
pulsedata = getLIRCPulseData()
# Aborted
if len(pulsedata) == 1:
if pulsedata[0][0] == -1:
readongoing = False
readaborted = True
buttonidx = len(buttonlist)
break
# Ignore repeat code (NEC)
if len(pulsedata) <= 4:
continue
# Get leading signal
(mode, duration) = pulsedata[0]
# Decode IR Protocols
# https://www.sbprojects.net/knowledge/ir/index.php
if duration >= PULSELEADER_MINMICROS_NEC and duration <= PULSELEADER_MAXMICROS_NEC:
irprotocol = "NEC"
# NEC has 9ms head, +/- 1ms
curdata = pulse2byteNEC(pulsedata)
if len(curdata) > 0:
if verifycount > 0:
if bytecompare(outdata, curdata) == 0:
verifycount = verifycount + 1
else:
verifycount = 0
else:
outdata = curdata
verifycount = 1
if verifycount >= VERIFYTARGET:
readongoing = False
print ("")
elif verifycount == 0:
print (" * IR code mismatch, please try again *")
elif VERIFYTARGET - verifycount > 1:
print (" Press the button "+ str(VERIFYTARGET - verifycount)+ " more times")
else:
print (" Press the button 1 more time")
else:
print (" * Decoding error. Please try again *")
else:
print (" * Unrecognized signal. Please try again *")
#curdata = pulse2byteLSB(pulsedata)
#displaybyte(curdata)
# Check for duplicates
newircode = getbytestring(outdata)
if verifycount > 0:
checkidx = 0
while checkidx < buttonidx and checkidx < len(buttonlist):
if ircodelist[checkidx] == newircode:
print (" Button already assigned. Please try again")
verifycount = 0
break
checkidx = checkidx + 1
# Store code, and power button code if applicable
if verifycount > 0:
if buttonidx == 0:
powerdata = outdata
if buttonidx < len(buttonlist):
# Abort will cause out of bounds
ircodelist[buttonidx] = newircode
#print (buttonlist[buttonidx]+": "+ newircode)
buttonidx = buttonidx + 1
if len(powerdata) > 0 and readaborted == False:
# Send to device if completed or reset mode
#print("Writing " + getbytestring(powerdata))
print("Updating Device...")
try:
bus=smbus.SMBus(1)
except Exception:
try:
# Older version
bus=smbus.SMBus(0)
except Exception:
bus=None
if bus is None:
print("Device Update Failed: Unable to detect i2c")
else:
# Check for Argon Control Register Support
checkircodewrite = False
argoncyclereg = 0x80
if FLAGV1ONLY == False:
oldval = bus.read_byte_data(address, argoncyclereg)
newval = oldval + 1
if newval >= 100:
newval = 98
bus.write_byte_data(address,argoncyclereg, newval)
time.sleep(1)
newval = bus.read_byte_data(address, argoncyclereg)
if newval != oldval:
addressregister = 0x82
checkircodewrite = True
bus.write_byte_data(address,argoncyclereg, oldval)
bus.write_i2c_block_data(address, addressregister, powerdata)
if checkircodewrite == True:
# Check if data was written for devices that support it
print("Verifying ...")
time.sleep(2)
checkircodedata = bus.read_i2c_block_data(address, addressregister, 4)
checkircodecounter = 0
while checkircodecounter < 4:
# Reuse readaborted flag as indicator if IR code was successfully updated
if checkircodedata[checkircodecounter] != powerdata[checkircodecounter]:
readaborted = True
checkircodecounter = checkircodecounter + 1
if readaborted == False:
print("Device Update Successful")
else:
print("Verification Failed")
bus.close()
# Update IR Conf if there are other button
if buttonidx > 1 and readaborted == False:
print("Updating Remote Control Codes...")
fp = open(irconffile, "w")
# Standard NEC conf header
fp.write("#\n")
fp.write("# Based on NEC templates at http://lirc.sourceforge.net/remotes/nec/\n")
fp.write("# Configured codes based on data gathered\n")
fp.write("#\n")
fp.write("\n")
fp.write("begin remote\n")
fp.write(" name argon\n")
fp.write(" bits 32\n")
fp.write(" flags SPACE_ENC\n")
fp.write(" eps 20\n")
fp.write(" aeps 200\n")
fp.write("\n")
fp.write(" header 8800 4400\n")
fp.write(" one 550 1650\n")
fp.write(" zero 550 550\n")
fp.write(" ptrail 550\n")
fp.write(" repeat 8800 2200\n")
fp.write(" gap 38500\n")
fp.write(" toggle_bit 0\n")
fp.write("\n")
fp.write(" frequency 38000\n")
fp.write("\n")
fp.write(" begin codes\n")
# Write Key Codes
buttonidx = 1
while buttonidx < len(buttonlist):
fp.write(" KEY_"+buttonlist[buttonidx]+" 0x"+ircodelist[buttonidx]+"\n")
buttonidx = buttonidx + 1
fp.write(" end codes\n")
fp.write("end remote\n")
fp.close()
+471
View File
@@ -0,0 +1,471 @@
#!/bin/bash
echo "*************"
echo " Argon Setup "
echo "*************"
# Check time if need to 'fix'
NEEDSTIMESYNC=0
LOCALTIME=$(date -u +%s%N | cut -b1-10)
GLOBALTIME=$(curl -s 'http://worldtimeapi.org/api/ip.txt' | grep unixtime | cut -b11-20)
TIMEDIFF=$((GLOBALTIME-LOCALTIME))
# about 26hrs, max timezone difference
if [ $TIMEDIFF -gt 100000 ]
then
NEEDSTIMESYNC=1
fi
argon_time_error() {
echo "**********************************************"
echo "* WARNING: Device time seems to be incorrect *"
echo "* This may cause problems during setup. *"
echo "**********************************************"
echo "Possible Network Time Protocol Server issue"
echo "Try running the following to correct:"
echo " curl -k https://download.argon40.com/tools/setntpserver.sh | bash"
}
if [ $NEEDSTIMESYNC -eq 1 ]
then
argon_time_error
fi
# Helper variables
ARGONDOWNLOADSERVER=https://download.argon40.com
INSTALLATIONFOLDER=/etc/argon
pythonbin="sudo /usr/bin/python3"
versioninfoscript=$INSTALLATIONFOLDER/argon-versioninfo.sh
uninstallscript=$INSTALLATIONFOLDER/argon-uninstall.sh
configscript=$INSTALLATIONFOLDER/argon-config
argondashboardscript=$INSTALLATIONFOLDER/argondashboard.py
setupmode="Setup"
if [ -f $configscript ]
then
setupmode="Update"
echo "Updating files"
else
sudo mkdir $INSTALLATIONFOLDER
sudo chmod 755 $INSTALLATIONFOLDER
fi
##########
# Start code lifted from raspi-config
# set_config_var based on raspi-config
if [ -e /boot/firmware/config.txt ] ; then
FIRMWARE=/firmware
else
FIRMWARE=
fi
CONFIG=/boot${FIRMWARE}/config.txt
set_config_var() {
if ! grep -q -E "$1=$2" $3 ; then
echo "$1=$2" | sudo tee -a $3 > /dev/null
fi
}
# End code lifted from raspi-config
##########
# Reuse set_config_var
set_nvme_default() {
set_config_var dtparam nvme $CONFIG
set_config_var dtparam=pciex1_gen 3 $CONFIG
}
set_external_antenna() {
set_config_var dtparam ant2 $CONFIG
}
argon_check_pkg() {
RESULT=$(dpkg-query -W -f='${Status}\n' "$1" 2> /dev/null | grep "installed")
if [ "" == "$RESULT" ]; then
echo "NG"
else
echo "OK"
fi
}
CHECKDEVICE="oneup" # Hardcoded for argononeup
CHECKGPIOMODE="libgpiod" # libgpiod or rpigpio
# Check if Raspbian, Ubuntu, others
CHECKPLATFORM="Others"
CHECKPLATFORMVERSION=""
CHECKPLATFORMVERSIONNUM=""
if [ -f "/etc/os-release" ]
then
source /etc/os-release
if [ "$ID" = "raspbian" ]
then
CHECKPLATFORM="Raspbian"
CHECKPLATFORMVERSION=$VERSION_ID
elif [ "$ID" = "debian" ]
then
# For backwards compatibility, continue using raspbian
CHECKPLATFORM="Raspbian"
CHECKPLATFORMVERSION=$VERSION_ID
elif [ "$ID" = "ubuntu" ]
then
CHECKPLATFORM="Ubuntu"
CHECKPLATFORMVERSION=$VERSION_ID
fi
echo ${CHECKPLATFORMVERSION} | grep -e "\." > /dev/null
if [ $? -eq 0 ]
then
CHECKPLATFORMVERSIONNUM=`cut -d "." -f2 <<< $CHECKPLATFORMVERSION `
CHECKPLATFORMVERSION=`cut -d "." -f1 <<< $CHECKPLATFORMVERSION `
fi
fi
gpiopkg="python3-libgpiod"
if [ "$CHECKGPIOMODE" = "rpigpio" ]
then
if [ "$CHECKPLATFORM" = "Raspbian" ]
then
gpiopkg="raspi-gpio python3-rpi.gpio"
else
gpiopkg="python3-rpi.gpio"
fi
fi
pkglist=($gpiopkg python3-smbus i2c-tools python3-evdev ddcutil)
echo "Installing/updating dependencies..."
for curpkg in ${pkglist[@]}; do
sudo apt-get install -y $curpkg
RESULT=$(argon_check_pkg "$curpkg")
if [ "NG" == "$RESULT" ]
then
echo "********************************************************************"
echo "Please also connect device to the internet and restart installation."
echo "********************************************************************"
exit
fi
done
echo "Updating configuration ..."
# Ubuntu Mate for RPi has raspi-config too
command -v raspi-config &> /dev/null
if [ $? -eq 0 ]
then
# Enable i2c
sudo raspi-config nonint do_i2c 0
fi
# Added to enabled NVMe for pi5
set_nvme_default
# Fan Setup
basename="argononeup"
daemonname=$basename"d"
eepromrpiscript="/usr/bin/rpi-eeprom-config"
eepromconfigscript=$INSTALLATIONFOLDER/${basename}-eepromconfig.py
daemonscript=$INSTALLATIONFOLDER/$daemonname.py
daemonservice=/lib/systemd/system/$daemonname.service
userdaemonservice=/etc/systemd/user/${daemonname}user.service
daemonconfigfile=/etc/$daemonname.conf
lidconfigscript=$INSTALLATIONFOLDER/${basename}-lidconfig.sh
for TMPDIRECTORY in "/lib/systemd/system"
do
sudo mkdir -p "$TMPDIRECTORY"
sudo chmod 755 $TMPDIRECTORY
sudo chown root:root "$TMPDIRECTORY"
done
echo "Installing/Updating scripts and services ..."
if [ ! -f $daemonconfigfile ]; then
# Generate config file for fan speed
sudo touch $daemonconfigfile
sudo chmod 666 $daemonconfigfile
echo '#' >> $daemonconfigfile
echo '# Argon One Up Configuration' >> $daemonconfigfile
echo '#' >> $daemonconfigfile
echo '# lidshutdownsecs number of seconds till shutdown when lid is closed 0 if do nothing' >> $daemonconfigfile
echo 'lidshutdownsecs=300' >> $daemonconfigfile
fi
# Lid Config Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argononeup-lidconfig.sh -O $lidconfigscript --quiet
sudo chmod 755 $lidconfigscript
if [ -f "$eepromrpiscript" ]
then
# EEPROM Config Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-rpi-eeprom-config-psu.py -O $eepromconfigscript --quiet
sudo chmod 755 $eepromconfigscript
fi
# Daemon/Service Files
sudo wget $ARGONDOWNLOADSERVER/scripts/${daemonname}.py -O $daemonscript --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/${daemonname}.service -O $daemonservice --quiet
sudo chmod 644 $daemonservice
sudo wget $ARGONDOWNLOADSERVER/scripts/${daemonname}user.service -O $userdaemonservice --quiet
sudo chmod 644 $userdaemonservice
# Battery Images
if [ ! -d "$INSTALLATIONFOLDER/ups" ]
then
sudo mkdir $INSTALLATIONFOLDER/ups
fi
sudo wget $ARGONDOWNLOADSERVER/ups/upsimg.tar.gz -O $INSTALLATIONFOLDER/ups/upsimg.tar.gz --quiet
sudo tar xfz $INSTALLATIONFOLDER/ups/upsimg.tar.gz -C $INSTALLATIONFOLDER/ups/
sudo rm -Rf $INSTALLATIONFOLDER/ups/upsimg.tar.gz
sudo wget "$ARGONDOWNLOADSERVER/scripts/argonpowerbutton-${CHECKGPIOMODE}.py" -O $INSTALLATIONFOLDER/argonpowerbutton.py --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argonkeyboard.py -O $INSTALLATIONFOLDER/argonkeyboard.py --quiet
# Other utility scripts
sudo wget $ARGONDOWNLOADSERVER/scripts/argondashboard.py -O $INSTALLATIONFOLDER/argondashboard.py --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-versioninfo.sh -O $versioninfoscript --quiet
sudo chmod 755 $versioninfoscript
sudo wget $ARGONDOWNLOADSERVER/scripts/argonsysinfo.py -O $INSTALLATIONFOLDER/argonsysinfo.py --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argonregister-v1.py -O $INSTALLATIONFOLDER/argonregister.py --quiet
# Argon Uninstall Script
sudo wget $ARGONDOWNLOADSERVER/scripts/argon-uninstall.sh -O $uninstallscript --quiet
sudo chmod 755 $uninstallscript
# Argon Config Script
if [ -f $configscript ]; then
sudo rm $configscript
fi
sudo touch $configscript
# To ensure we can write the following lines
sudo chmod 666 $configscript
echo '#!/bin/bash' >> $configscript
echo 'echo "--------------------------"' >> $configscript
echo 'echo "Argon Configuration Tool"' >> $configscript
echo "$versioninfoscript simple" >> $configscript
echo 'echo "--------------------------"' >> $configscript
echo 'get_number () {' >> $configscript
echo ' read curnumber' >> $configscript
echo ' if [ -z "$curnumber" ]' >> $configscript
echo ' then' >> $configscript
echo ' echo "-2"' >> $configscript
echo ' return' >> $configscript
echo ' elif [[ $curnumber =~ ^[+-]?[0-9]+$ ]]' >> $configscript
echo ' then' >> $configscript
echo ' if [ $curnumber -lt 0 ]' >> $configscript
echo ' then' >> $configscript
echo ' echo "-1"' >> $configscript
echo ' return' >> $configscript
echo ' elif [ $curnumber -gt 100 ]' >> $configscript
echo ' then' >> $configscript
echo ' echo "-1"' >> $configscript
echo ' return' >> $configscript
echo ' fi ' >> $configscript
echo ' echo $curnumber' >> $configscript
echo ' return' >> $configscript
echo ' fi' >> $configscript
echo ' echo "-1"' >> $configscript
echo ' return' >> $configscript
echo '}' >> $configscript
echo '' >> $configscript
echo 'mainloopflag=1' >> $configscript
echo 'while [ $mainloopflag -eq 1 ]' >> $configscript
echo 'do' >> $configscript
echo ' echo' >> $configscript
echo ' echo "Choose Option:"' >> $configscript
echo ' echo " 1. Get Battery Status"' >> $configscript
echo ' echo " 2. Configure Lid Behavior"' >> $configscript
uninstalloption="4"
statusoption=$(($uninstalloption-1))
echo " echo \" $statusoption. Dashboard\"" >> $configscript
echo " echo \" $uninstalloption. Uninstall\"" >> $configscript
echo ' echo ""' >> $configscript
echo ' echo " 0. Exit"' >> $configscript
echo " echo -n \"Enter Number (0-$uninstalloption):\"" >> $configscript
echo ' newmode=$( get_number )' >> $configscript
echo ' if [ $newmode -eq 0 ]' >> $configscript
echo ' then' >> $configscript
echo ' echo "Thank you."' >> $configscript
echo ' mainloopflag=0' >> $configscript
echo ' elif [ $newmode -eq 1 ]' >> $configscript
echo ' then' >> $configscript
# Option 1
echo " $pythonbin $daemonscript GETBATTERY" >> $configscript
echo ' elif [ $newmode -eq 2 ]' >> $configscript
echo ' then' >> $configscript
# Option 2
echo " $lidconfigscript" >> $configscript
# Standard options
echo " elif [ \$newmode -eq $statusoption ]" >> $configscript
echo ' then' >> $configscript
echo " $pythonbin $argondashboardscript" >> $configscript
echo " elif [ \$newmode -eq $uninstalloption ]" >> $configscript
echo ' then' >> $configscript
echo " $uninstallscript" >> $configscript
echo ' mainloopflag=0' >> $configscript
echo ' fi' >> $configscript
echo 'done' >> $configscript
sudo chmod 755 $configscript
# Desktop Icon
destfoldername=$USERNAME
if [ -z "$destfoldername" ]
then
destfoldername=$USER
fi
if [ -z "$destfoldername" ]
then
destfoldername="pi"
fi
shortcutfile="/home/$destfoldername/Desktop/argononeup.desktop"
if [ -d "/home/$destfoldername/Desktop" ]
then
echo "Creating/Updating Desktop Elements ..."
terminalcmd="lxterminal --working-directory=/home/$destfoldername/ -t"
if [ -f "/home/$destfoldername/.twisteros.twid" ]
then
terminalcmd="xfce4-terminal --default-working-directory=/home/$destfoldername/ -T"
fi
imagefile=argon40.png
sudo wget https://download.argon40.com/$imagefile -O /etc/argon/$imagefile --quiet
if [ -f $shortcutfile ]; then
sudo rm $shortcutfile
fi
# Create Shortcuts
echo "[Desktop Entry]" > $shortcutfile
echo "Name=Argon Configuration" >> $shortcutfile
echo "Comment=Argon Configuration" >> $shortcutfile
echo "Icon=/etc/argon/$imagefile" >> $shortcutfile
echo 'Exec='$terminalcmd' "Argon Configuration" -e '$configscript >> $shortcutfile
echo "Type=Application" >> $shortcutfile
echo "Encoding=UTF-8" >> $shortcutfile
echo "Terminal=false" >> $shortcutfile
echo "Categories=None;" >> $shortcutfile
chmod 755 $shortcutfile
fi
configcmd="$(basename -- $configscript)"
echo "Initializing Services ..."
# Force remove lock files
sudo rm -f /dev/shm/argononeupkeyboardlock.txt
sudo rm -f /dev/shm/argononeupkeyboardlock.txt.a
if [ "$setupmode" = "Setup" ]
then
if [ -f "/usr/bin/$configcmd" ]
then
sudo rm /usr/bin/$configcmd
fi
sudo ln -s $configscript /usr/bin/$configcmd
# Enable and Start Service(s)
sudo systemctl daemon-reload
sudo systemctl enable argononeupd.service
sudo systemctl start argononeupd.service
else
sudo systemctl daemon-reload
sudo systemctl restart argononeupd.service
fi
# Enable and Start User Service(s)
for tmpuser in `awk -F: '{ if ($3 >= 1000) print $1 }' /etc/passwd`
do
if [ "$tmpuser" != "nobody" ]
then
if [ "$setupmode" = "Setup" ]
then
sudo -u "$tmpuser" systemctl --user enable argononeupduser.service
sudo -u "$tmpuser" systemctl --user start argononeupduser.service
else
sudo -u "$tmpuser" systemctl --user restart argononeupduser.service
fi
fi
done
# Current user / fallback
if [ "$setupmode" = "Setup" ]
then
systemctl --user enable argononeupduser.service
systemctl --user start argononeupduser.service
else
systemctl --user restart argononeupduser.service
fi
if [ "$CHECKPLATFORM" = "Raspbian" ]
then
if [ -f "$eepromrpiscript" ]
then
echo "Checking EEPROM ..."
sudo apt-get update && sudo apt-get upgrade -y
sudo rpi-eeprom-update
# EEPROM Config Script
sudo $eepromconfigscript
fi
else
echo "WARNING: EEPROM not updated. Please run this under Raspberry Pi OS"
fi
echo "*********************"
echo " $setupmode Completed "
echo "*********************"
$versioninfoscript
echo
echo "Use '$configcmd' to configure device"
echo
if [ $NEEDSTIMESYNC -eq 1 ]
then
argon_time_error
fi
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+194
View File
@@ -0,0 +1,194 @@
#!/bin/bash
if [ -e /boot/firmware/config.txt ] ; then
FIRMWARE=/firmware
else
FIRMWARE=
fi
CONFIG=/boot${FIRMWARE}/config.txt
# Check if Raspbian
CHECKPLATFORM="Others"
if [ -f "/etc/os-release" ]
then
source /etc/os-release
if [ "$ID" = "raspbian" ]
then
CHECKPLATFORM="Raspbian"
elif [ "$ID" = "debian" ]
then
# For backwards compatibility, continue using raspbian
CHECKPLATFORM="Raspbian"
fi
fi
echo "------------------------------------"
echo " Argon BLSTR DAC Configuration Tool"
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
}
irexecrcfile=/etc/lirc/irexec.lircrc
irexecshfile=/etc/argon/argonirexec
irdecodefile=/etc/argon/argonirdecoder
kodiuserdatafolder="$HOME/.kodi/userdata"
kodilircmapfile="$kodiuserdatafolder/Lircmap.xml"
remotemode=""
needinstallation=1
CONFIGSETTING="dtoverlay=hifiberry-dacplus,slave"
if grep -q -E "$CONFIGSETTING" $CONFIG
then
# Already installed
needinstallation=0
fi
loopflag=1
while [ $loopflag -eq 1 ]
do
echo
echo "Select option:"
if [ $needinstallation -eq 1 ]
then
echo " 1. Enable BLSTR DAC"
echo " 2. Cancel"
echo -n "Enter Number (1-2):"
else
echo " 1. Select audio configuration"
echo " 2. Disable BLSTR DAC"
echo " 3. Cancel"
echo -n "Enter Number (1-3):"
fi
newmode=$( get_number )
if [[ $newmode -ge 1 && $newmode -le 3 ]]
then
if [[ $needinstallation -eq 1 && $newmode -ge 3 ]]
then
# Invalid option
loopflag=1
# Uninstall
else
loopflag=0
if [ $needinstallation -eq 1 ]
then
if [ $newmode -eq 2 ]
then
# Cancel
newmode=4
fi
else
if [ $newmode -eq 1 ]
then
# Audio Conf
newmode=3
fi
fi
fi
fi
done
needrestart=0
echo
if [ $newmode -eq 2 ]
then
# Uninstall
blstrdactmpconfigfile=/dev/shm/argonblstrdacconfig.txt
cat $CONFIG | grep -v "$CONFIGSETTING" > $blstrdactmpconfigfile
cat $blstrdactmpconfigfile | sudo tee $CONFIG 1> /dev/null
sudo rm $blstrdactmpconfigfile
echo "Uninstall Completed"
echo
needrestart=1
elif [ $newmode -eq 3 ]
then
# Audio Conf
loopflag=1
while [ $loopflag -eq 1 ]
do
echo
echo "Select audio configuration:"
echo " 1. PulseAudio"
echo " 2. Pipewire"
echo " 3. Cancel"
echo -n "Enter Number (1-3):"
newmode=$( get_number )
if [[ $newmode -ge 1 && $newmode -le 3 ]]
then
loopflag=0
fi
done
if [[ $newmode -ge 1 && $newmode -le 2 ]]
then
sudo raspi-config nonint do_audioconf $newmode
else
echo "Cancelled"
fi
elif [ $newmode -eq 1 ]
then
# Install
echo "$CONFIGSETTING" | sudo tee -a $CONFIG 1> /dev/null
#sudo raspi-config nonint do_audioconf 1
#systemctl --global -q disable pipewire-pulse
#systemctl --global -q disable wireplumber
#systemctl --global -q enable pulseaudio
#if [ -e /etc/alsa/conf.d/99-pipewire-default.conf ] ; then
# rm /etc/alsa/conf.d/99-pipewire-default.conf
#fi
echo "Please run configuration and choose different audio configuration if there are problems"
needrestart=1
else
echo "Cancelled"
#exit
fi
echo
#echo "Thank you."
if [ $needrestart -eq 1 ]
then
echo "Changes should take after reboot."
fi
@@ -0,0 +1,576 @@
#!/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 NVMe boot priority etc if not yet set
foundnewsetting = 0
addsetting="\nBOOT_UART=1\nWAKE_ON_GPIO=0\nPOWER_OFF_ON_HALT=1\nBOOT_ORDER=0xf416\nPCIE_PROBE=1"
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] == "BOOT_UART":
newsetting = "BOOT_UART=1"
elif current_config_pair[0] == "WAKE_ON_GPIO":
newsetting = "WAKE_ON_GPIO=0"
elif current_config_pair[0] == "POWER_OFF_ON_HALT":
newsetting = "POWER_OFF_ON_HALT=1"
elif current_config_pair[0] == "BOOT_ORDER":
newsetting = "BOOT_ORDER=0xf416"
elif current_config_pair[0] == "PCIE_PROBE":
newsetting = "PCIE_PROBE=1"
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()
@@ -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()
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
pythonbin=/usr/bin/python3
argononefanscript=/etc/argon/argononed.py
argoneonrtcscript=/etc/argon/argoneond.py
argonirconfigscript=/etc/argon/argonone-ir
if [ ! -z "$1" ]
then
if [ -f "$argononefanscript" ]
then
$pythonbin $argononefanscript FANOFF
fi
if [ "$1" = "poweroff" ] || [ "$1" = "halt" ]
then
if [ -f $argonirconfigscript ]
then
if [ -f $argoneonrtcscript ]
then
$pythonbin $argoneonrtcscript SHUTDOWN
fi
$pythonbin $argononefanscript SHUTDOWN
fi
fi
fi
+106
View File
@@ -0,0 +1,106 @@
#!/bin/bash
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
}
INSTALLATIONFOLDER=/etc/argon
pythonbin="sudo /usr/bin/python3"
argonstatusscript=$INSTALLATIONFOLDER/argonstatus.py
argondashboardscript=$INSTALLATIONFOLDER/argondashboard.py
argononefanscript=$INSTALLATIONFOLDER/argononed.py
argoneonrtcscript=$INSTALLATIONFOLDER/argoneond.py
echo "--------------------------"
echo " Argon System Information"
echo "--------------------------"
loopflag=1
while [ $loopflag -eq 1 ]
do
echo
echo " 1. Temperatures"
echo " 2. CPU Utilization"
echo " 3. Storage Capacity"
echo " 4. RAM"
echo " 5. IP Address"
lastoption=5
if [ -f $argononefanscript ]
then
echo " 6. Fan Speed"
lastoption=6
fi
if [ -f "$argoneonrtcscript" ]
then
echo " 7. RTC Schedules"
echo " 8. RAID"
lastoption=8
fi
lastoption=$((lastoption + 1))
echo " ${lastoption}. Dashboard"
echo
echo " 0. Back"
echo -n "Enter Number (0-${lastoption}):"
newmode=$( get_number )
if [ $newmode -eq 0 ]
then
loopflag=0
elif [ $newmode -gt 0 ] && [ $newmode -le $lastoption ]
then
echo "--------------------------"
if [ $newmode -eq $lastoption ]
then
$pythonbin $argondashboardscript
elif [ $newmode -eq 1 ]
then
$pythonbin $argonstatusscript "temperature"
elif [ $newmode -eq 2 ]
then
$pythonbin $argonstatusscript "cpu usage"
elif [ $newmode -eq 3 ]
then
$pythonbin $argonstatusscript "storage"
elif [ $newmode -eq 4 ]
then
$pythonbin $argonstatusscript "ram"
elif [ $newmode -eq 5 ]
then
$pythonbin $argonstatusscript "ip"
elif [ $newmode -eq 6 ]
then
$pythonbin $argonstatusscript "temperature" "fan configuration" "fan speed"
elif [ $newmode -eq 7 ]
then
$pythonbin $argoneonrtcscript GETSCHEDULELIST
elif [ $newmode -eq 8 ]
then
$pythonbin $argonstatusscript "raid"
fi
echo "--------------------------"
fi
done
+180
View File
@@ -0,0 +1,180 @@
#!/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
# Remove files
sudo rm /lib/systemd/system/argononeupsd.service
sudo rm /lib/systemd/system/argonupsrtcd.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
# Remove UPS daemon if any
argononeupsscript=$INSTALLATIONFOLDER/argononeupd.py
if [ -f "$argononeupsscript" ]
then
#sudo rmmod argonbatteryicon
# Disable Services
sudo systemctl stop argononeupd.service
sudo systemctl disable argononeupd.service
for tmpuser in `awk -F: '{ if ($3 >= 1000) print $1 }' /etc/passwd`
do
if [ "$tmpuser" != "nobody" ]
then
sudo -u "$tmpuser" systemctl --user stop argononeupduser.service
sudo -u "$tmpuser" systemctl --user disable argononeupduser.service
fi
done
systemctl --user stop argononeupduser.service
systemctl --user disable argononeupduser.service
# Remove files
sudo rm /lib/systemd/system/argononeupd.service
sudo rm /etc/systemd/user/argononeupduser.service
find "/home" -maxdepth 1 -type d | while read line; do
shortcutfile="$line/Desktop/argononeup.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."
+105
View File
@@ -0,0 +1,105 @@
#!/bin/bash
unitconfigfile=/etc/argonunits.conf
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
}
saveconfig () {
echo "#" > $unitconfigfile
echo "# Argon Unit Configuration" >> $unitconfigfile
echo "#" >> $unitconfigfile
echo "temperature=$1" >> $unitconfigfile
}
updateconfig=1
unitloopflag=1
while [ $unitloopflag -eq 1 ]
do
if [ $updateconfig -eq 1 ]
then
. $unitconfigfile
fi
updateconfig=0
if [ -z "$temperature" ]
then
temperature="C"
updateconfig=1
fi
# Write default values to config file, daemon already uses default so no need to restart service
if [ $updateconfig -eq 1 ]
then
saveconfig $temperature
updateconfig=0
fi
echo "-----------------------------"
echo "Argon Display Units"
echo "-----------------------------"
echo "Choose from the list:"
echo " 1. Temperature: $temperature"
echo
echo " 0. Back"
echo -n "Enter Number (0-1):"
newmode=$( get_number )
if [ $newmode -eq 0 ]
then
unitloopflag=0
elif [ $newmode -eq 1 ]
then
echo
echo "-----------------------------"
echo "Temperature Display"
echo "-----------------------------"
echo "Choose from the list:"
echo " 1. Celsius"
echo " 2. Fahrenheit"
echo
echo " 0. Cancel"
echo -n "Enter Number (0-2):"
cmdmode=$( get_number )
if [ $cmdmode -eq 1 ]
then
temperature="C"
updateconfig=1
elif [ $cmdmode -eq 2 ]
then
temperature="F"
updateconfig=1
fi
fi
if [ $updateconfig -eq 1 ]
then
saveconfig $temperature
sudo systemctl restart argononed.service
fi
done
echo
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
VERSIONINFO="2603002"
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
+374
View File
@@ -0,0 +1,374 @@
#!/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
try:
curses.wrapper(mainloop)
except Exception as wrapperr:
pass
+294
View File
@@ -0,0 +1,294 @@
#!/bin/bash
oledconfigfile=/etc/argoneonoled.conf
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
}
get_pagename() {
if [ "$1" == "clock" ]
then
pagename="Current Date/Time"
elif [ "$1" == "cpu" ]
then
pagename="CPU Utilization"
elif [ "$1" == "storage" ]
then
pagename="Storage Utilization"
elif [ "$1" == "raid" ]
then
pagename="RAID Information"
elif [ "$1" == "ram" ]
then
pagename="Available RAM"
elif [ "$1" == "temp" ]
then
pagename="CPU/HDD Temperature"
elif [ "$1" == "ip" ]
then
pagename="IP Address"
else
pagename="Invalid"
fi
}
configure_pagelist () {
pagemasterlist="clock cpu storage raid ram temp ip"
newscreenlist="$1"
pageloopflag=1
while [ $pageloopflag -eq 1 ]
do
echo "--------------------------------"
echo " OLED Pages "
echo "--------------------------------"
i=1
for curpage in $newscreenlist
do
get_pagename $curpage
echo " $i. Remove $pagename"
i=$((i+1))
done
if [ $i -eq 1 ]
then
echo " No page configured"
fi
echo
echo " $i. Add Page"
echo
echo " 0. Done"
echo -n "Enter Number (0-$i):"
cmdmode=$( get_number )
if [ $cmdmode -eq 0 ]
then
pageloopflag=0
elif [[ $cmdmode -eq $i ]]
then
echo "--------------------------------"
echo " Choose Page to Add"
echo "--------------------------------"
echo
i=1
for curpage in $pagemasterlist
do
get_pagename $curpage
echo " $i. $pagename"
i=$((i+1))
done
echo
echo " 0. Cancel"
echo -n "Enter Number (0-$i):"
pagenum=$( get_number )
if [[ $pagenum -ge 1 && $pagenum -le $i ]]
then
i=1
for curpage in $pagemasterlist
do
if [ $i -eq $pagenum ]
then
if [ "$newscreenlist" == "" ]
then
newscreenlist="$curpage"
else
newscreenlist="$newscreenlist $curpage"
fi
fi
i=$((i+1))
done
fi
elif [[ $cmdmode -ge 1 && $cmdmode -lt $i ]]
then
tmpscreenlist=""
i=1
for curpage in $newscreenlist
do
if [ ! $i -eq $cmdmode ]
then
tmpscreenlist="$tmpscreenlist $curpage"
fi
i=$((i+1))
done
if [ "$tmpscreenlist" == "" ]
then
newscreenlist="$tmpscreenlist"
else
# Remove leading space
newscreenlist="${tmpscreenlist:1}"
fi
fi
done
}
saveconfig () {
echo "#" > $oledconfigfile
echo "# Argon OLED Configuration" >> $oledconfigfile
echo "#" >> $oledconfigfile
echo "enabled=$1" >> $oledconfigfile
echo "switchduration=$2" >> $oledconfigfile
echo "screensaver=$3" >> $oledconfigfile
echo "screenlist=\"$4\"" >> $oledconfigfile
}
updateconfig=1
oledloopflag=1
while [ $oledloopflag -eq 1 ]
do
if [ $updateconfig -eq 1 ]
then
. $oledconfigfile
fi
updateconfig=0
if [ -z "$enabled" ]
then
enabled="Y"
updateconfig=1
fi
if [ -z "$screenlist" ]
then
screenlist="clock ip"
updateconfig=1
fi
if [ -z "$screensaver" ]
then
screensaver=120
updateconfig=1
fi
if [ -z "$switchduration" ]
then
switchduration=0
updateconfig=1
fi
# Write default values to config file, daemon already uses default so no need to restart service
if [ $updateconfig -eq 1 ]
then
saveconfig $enabled $switchduration $screensaver "$screenlist"
updateconfig=0
fi
displaystring=": Manually"
if [ $switchduration -gt 1 ]
then
displaystring="Every $switchduration secs"
fi
echo "-----------------------------"
echo "Argon OLED Configuration Tool"
echo "-----------------------------"
echo "Choose from the list:"
echo " 1. Switch Page $displaystring"
echo " 2. Configure Pages"
echo " 3. Turn OFF OLED Screen when unchanged after $screensaver secs"
echo " 4. Enable OLED Pages: $enabled"
echo
echo " 0. Back"
echo -n "Enter Number (0-3):"
newmode=$( get_number )
if [ $newmode -eq 0 ]
then
oledloopflag=0
elif [ $newmode -eq 1 ]
then
echo
echo -n "Enter # of Seconds (10-60, Manual if 0):"
cmdmode=$( get_number )
if [ $cmdmode -eq 0 ]
then
switchduration=0
updateconfig=1
elif [[ $cmdmode -ge 10 && $cmdmode -le 60 ]]
then
updateconfig=1
switchduration=$cmdmode
else
echo
echo "Invalid duration"
echo
fi
elif [ $newmode -eq 3 ]
then
echo
echo -n "Enter # of Seconds (60 or above, Manual if 0):"
cmdmode=$( get_number )
if [ $cmdmode -eq 0 ]
then
screensaver=0
updateconfig=1
elif [ $cmdmode -ge 60 ]
then
updateconfig=1
screensaver=$cmdmode
else
echo
echo "Invalid duration"
echo
fi
elif [ $newmode -eq 2 ]
then
configure_pagelist "$screenlist"
if [ ! "$screenlist" == "$newscreenlist" ]
then
screenlist="$newscreenlist"
updateconfig=1
fi
elif [ $newmode -eq 4 ]
then
echo
echo -n "Enable OLED Pages (Y/n)?:"
read -n 1 confirm
tmpenabled="$enabled"
if [[ "$confirm" == "n" || "$confirm" == "N" ]]
then
tmpenabled="N"
elif [[ "$confirm" == "y" || "$confirm" == "Y" ]]
then
tmpenabled="Y"
else
echo "Invalid response"
fi
if [ ! "$enabled" == "$tmpenabled" ]
then
enabled="$tmpenabled"
updateconfig=1
fi
fi
if [ $updateconfig -eq 1 ]
then
saveconfig $enabled $switchduration $screensaver "$screenlist"
sudo systemctl restart argononed.service
fi
done
echo
+421
View File
@@ -0,0 +1,421 @@
#!/bin/bash
if [ -z "$1" ]
then
rtcdaemonname=argoneond
rtcconfigfile=/etc/argoneonrtc.conf
else
rtcdaemonname=${1}d
rtcconfigfile=/etc/${1}.conf
fi
pythonbin=/usr/bin/python3
argonrtcscript=/etc/argon/$rtcdaemonname.py
CHECKPLATFORM="Others"
# Check if Raspbian
grep -q -F 'Raspbian' /etc/os-release &> /dev/null
if [ $? -eq 0 ]
then
CHECKPLATFORM="Raspbian"
else
# Ubuntu needs elevated access for SMBus
pythonbin="sudo /usr/bin/python3"
fi
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
}
configure_schedule () {
scheduleloopflag=1
while [ $scheduleloopflag -eq 1 ]
do
echo "--------------------------------"
echo " Configure Schedule "
echo "--------------------------------"
echo " 1. Add Schedule"
echo " or"
echo " Remove Schedule"
$pythonbin $argonrtcscript GETSCHEDULELIST
echo
echo " 99. Main Menu"
echo " 0. Back"
#echo "NOTE: You can also edit $rtcconfigfile directly"
echo -n "Enter Number:"
newmode=$( get_number )
if [ $newmode -eq 0 ]
then
scheduleloopflag=0
elif [ $newmode -eq 99 ]
then
scheduleloopflag=0
rtcloopflag=2
elif [ $newmode -eq 1 ]
then
configure_newschedule
elif [ $newmode -gt 1 ]
then
echo "CONFIRM SCHEDULE REMOVAL"
$pythonbin $argonrtcscript SHOWSCHEDULE $newmode
echo -n "Press Y to remove schedule #$newmode:"
read -n 1 confirm
if [ "$confirm" = "y" ]
then
confirm="Y"
fi
if [ "$confirm" = "Y" ]
then
$pythonbin $argonrtcscript REMOVESCHEDULE $newmode
sudo systemctl restart $rtcdaemonname.service
fi
echo ""
fi
done
}
configure_newschedule () {
cmdmode=1
hour=8
minute=0
minuteprefix=":0"
dayidx=0
repeat=1
subloopflag=1
while [ $subloopflag -eq 1 ]
do
minuteprefix=":0"
if [ $minute -ge 10 ]
then
minuteprefix=":"
fi
typestr="Shutdown"
if [ $cmdmode -eq 1 ]
then
typestr="Startup"
fi
daystr="Daily"
if [ $dayidx -eq 1 ]
then
daystr="Mon"
elif [ $dayidx -eq 2 ]
then
daystr="Tue"
elif [ $dayidx -eq 3 ]
then
daystr="Wed"
elif [ $dayidx -eq 4 ]
then
daystr="Thu"
elif [ $dayidx -eq 5 ]
then
daystr="Fri"
elif [ $dayidx -eq 6 ]
then
daystr="Sat"
elif [ $dayidx -eq 7 ]
then
daystr="Sun"
fi
repeatstr="Yes"
if [ $repeat -eq 0 ]
then
repeatstr="Once"
if [ $dayidx -eq 0 ]
then
daystr="Next Occurence"
fi
fi
echo "--------------------------------"
echo " Configure Schedule"
echo "--------------------------------"
echo " 1. Type: $typestr"
echo " 2. Set Time: $hour$minuteprefix$minute"
echo " 3. Repeating: $repeatstr"
echo " 4. Day: $daystr"
echo
echo " 5. Add Schedule"
echo
echo " 0. Cancel"
echo -n "Enter Number (0-5):"
setmode=$( get_number )
if [ $setmode -eq 0 ]
then
subloopflag=0
elif [ $setmode -eq 1 ]
then
echo "--------------------------------"
echo " Schedule Type "
echo "--------------------------------"
echo " 1. Startup"
echo " 2. Shutdown"
echo
echo -n "Enter Number (1-2):"
tmpval=$( get_number )
if [ $tmpval -eq 1 ]
then
cmdmode=1
elif [ $tmpval -eq 2 ]
then
cmdmode=0
else
echo "Invalid Option"
fi
elif [ $setmode -eq 2 ]
then
echo -n "Enter Hour (0-23):"
tmphour=$( get_number )
echo -n "Enter Minute (0-59):"
tmpminute=$( get_number )
if [[ $tmpminute -ge 0 && $tmpminute -le 59 && $tmphour -ge 0 && $tmphour -le 23 ]]
then
minute=$tmpminute
hour=$tmphour
else
echo "Invalid value(s)"
fi
elif [ $setmode -eq 3 ]
then
echo -n "Repeat schedule (Y/n)?:"
read -n 1 confirm
if [ "$confirm" = "y" ]
then
repeat=1
else
repeat=0
fi
elif [ $setmode -eq 4 ]
then
echo "Select Day of the Week:"
echo " 0. Daily"
echo " 1. Monday"
echo " 2. Tuesday"
echo " 3. Wednesday"
echo " 4. Thursday"
echo " 5. Friday"
echo " 6. Saturday"
echo " 7. Sunday"
echo -n "Enter Number (0-7):"
tmpval=$( get_number )
if [[ $tmpval -ge 0 && $tmpval -le 7 ]]
then
dayidx=$tmpval
else
echo "Invalid Option"
fi
elif [ $setmode -eq 5 ]
then
if [ $dayidx -eq 0 ]
then
cronweekday="*"
elif [ $dayidx -eq 7 ]
then
cronweekday="7"
else
cronweekday=$dayidx
fi
cmdcode="off"
if [ $cmdmode -eq 1 ]
then
cmdcode="on"
fi
echo "$minute $hour * * $cronweekday $cmdcode" >> $rtcconfigfile
sudo systemctl restart $rtcdaemonname.service
subloopflag=0
fi
done
}
configure_newcron () {
subloopflag=1
while [ $subloopflag -eq 1 ]
do
echo "--------------------------------"
echo " Schedule Type "
echo "--------------------------------"
echo " 1. Startup"
echo " 2. Shutdown"
echo
echo " 0. Cancel"
echo -n "Enter Number (0-2):"
cmdmode=$( get_number )
if [ $cmdmode -eq 0 ]
then
subloopflag=0
elif [[ $cmdmode -ge 1 && $cmdmode -le 2 ]]
then
cmdcode="on"
echo "--------------------------------"
if [ $cmdmode -eq 1 ]
then
echo " Schedule Startup"
else
echo " Schedule Shutdown"
cmdcode="off"
fi
echo "--------------------------------"
echo "Select Schedule:"
echo " 1. Hourly"
echo " 2. Daily"
echo " 3. Weekly"
echo " 4. Monthly"
echo
echo " 0. Back"
echo -n "Enter Number (0-4):"
newmode=$( get_number )
if [[ $newmode -ge 1 && $newmode -le 4 ]]
then
echo ""
if [ $cmdmode -eq 1 ]
then
echo "New Startup Schedule"
else
echo "New Shutdown Schedule"
fi
if [ $newmode -eq 1 ]
then
echo -n "Enter Minute (0-59):"
minute=$( get_number )
if [[ $minute -ge 0 && $minute -le 59 ]]
then
echo "$minute * * * * $cmdcode" >> $rtcconfigfile
sudo systemctl restart $rtcdaemonname.service
subloopflag=0
else
echo "Invalid value"
fi
elif [ $newmode -eq 2 ]
then
echo -n "Enter Hour (0-23):"
hour=$( get_number )
echo -n "Enter Minute (0-59):"
minute=$( get_number )
if [[ $minute -ge 0 && $minute -le 59 && $hour -ge 0 && $hour -le 23 ]]
then
echo "$minute $hour * * * $cmdcode" >> $rtcconfigfile
sudo systemctl restart $rtcdaemonname.service
subloopflag=0
else
echo "Invalid value(s)"
fi
elif [ $newmode -eq 3 ]
then
echo "Select Day of the Week:"
echo " 0. Sunday"
echo " 1. Monday"
echo " 2. Tuesday"
echo " 3. Wednesday"
echo " 4. Thursday"
echo " 5. Friday"
echo " 6. Saturday"
echo -n "Enter Number (0-6):"
weekday=$( get_number )
echo -n "Enter Hour (0-23):"
hour=$( get_number )
echo -n "Enter Minute (0-59):"
minute=$( get_number )
if [[ $minute -ge 0 && $minute -le 59 && $hour -ge 0 && $hour -le 23 && $weekday -ge 0 && $weekday -le 6 ]]
then
echo "$minute $hour * * $weekday $cmdcode" >> $rtcconfigfile
sudo systemctl restart $rtcdaemonname.service
subloopflag=0
else
echo "Invalid value(s)"
fi
elif [ $newmode -eq 4 ]
then
echo -n "Enter Date (1-31):"
monthday=$( get_number )
if [[ $monthday -ge 29 ]]
then
echo "WARNING: This schedule will not trigger for certain months"
fi
echo -n "Enter Hour (0-23):"
hour=$( get_number )
echo -n "Enter Minute (0-59):"
minute=$( get_number )
if [[ $minute -ge 0 && $minute -le 59 && $hour -ge 0 && $hour -le 23 && $monthday -ge 1 && $monthday -le 31 ]]
then
echo "$minute $hour $monthday * * $cmdcode" >> $rtcconfigfile
sudo systemctl restart $rtcdaemonname.service
subloopflag=0
else
echo "Invalid value(s)"
fi
fi
fi
fi
done
}
rtcloopflag=1
while [ $rtcloopflag -eq 1 ]
do
echo "----------------------------"
echo "Argon RTC Configuration Tool"
echo "----------------------------"
$pythonbin $argonrtcscript GETRTCTIME
echo "Choose from the list:"
echo " 1. Update RTC Time"
echo " 2. Configure Startup/Shutdown Schedules"
echo
echo " 0. Exit"
echo -n "Enter Number (0-2):"
newmode=$( get_number )
if [ $newmode -eq 0 ]
then
rtcloopflag=0
elif [[ $newmode -ge 1 && $newmode -le 2 ]]
then
if [ $newmode -eq 1 ]
then
echo "Matching RTC Time to System Time..."
$pythonbin $argonrtcscript UPDATERTCTIME
elif [ $newmode -eq 2 ]
then
configure_schedule
fi
fi
done
echo
+487
View File
@@ -0,0 +1,487 @@
#!/usr/bin/python3
import sys
import datetime
import math
import os
import time
sys.path.append("/etc/argon/")
from argonregister import argonregister_initializebusobj
import argonrtc
# Initialize I2C Bus
bus = argonregister_initializebusobj()
ADDR_RTC=0x51
#################
# Common/Helpers
#################
RTC_CONFIGFILE = "/etc/argoneonrtc.conf"
RTC_ALARM_BIT = 0x8
RTC_TIMER_BIT = 0x4
# PCF8563 number system Binary Coded Decimal (BCD)
# BCD to Decimal
def numBCDtoDEC(val):
return (val & 0xf)+(((val >> 4) & 0xf)*10)
# Decimal to BCD
def numDECtoBCD(val):
return (math.floor(val/10)<<4) + (val % 10)
# Check if Event Bit is raised
def hasRTCEventFlag(flagbit):
if bus is None:
return False
bus.write_byte(ADDR_RTC,1)
out = bus.read_byte_data(ADDR_RTC, 1)
return (out & flagbit) != 0
# Clear Event Bit if raised
def clearRTCEventFlag(flagbit):
if bus is None:
return False
out = bus.read_byte_data(ADDR_RTC, 1)
if (out & flagbit) != 0:
# Unset only if fired
bus.write_byte_data(ADDR_RTC, 1, out&(0xff-flagbit))
return True
return False
# Enable Event Flag
def setRTCEventFlag(flagbit, enabled):
if bus is None:
return
# 0x10 = TI_TP flag, 0 by default
ti_tp_flag = 0x10
# flagbit=0x4 for timer flag, 0x1 for enable timer flag
# flagbit=0x8 for alarm flag, 0x2 for enable alarm flag
enableflagbit = flagbit>>2
disableflagbit = 0
if enabled == False:
disableflagbit = enableflagbit
enableflagbit = 0
out = bus.read_byte_data(ADDR_RTC, 1)
bus.write_byte_data(ADDR_RTC, 1, (out&(0xff-flagbit-disableflagbit - ti_tp_flag))|enableflagbit)
#########
# Describe Methods
#########
# Describe Timer Setting
def describeTimer(showsetting):
if bus is None:
return "Error"
out = bus.read_byte_data(ADDR_RTC, 14)
tmp = out & 3
if tmp == 3:
outstr = " Minute(s)"
elif tmp == 2:
outstr = " Second(s)"
elif tmp == 1:
outstr = "/64th Second"
elif tmp == 0:
outstr = "/4096th Second"
if (out & 0x80) != 0:
out = bus.read_byte_data(ADDR_RTC, 15)
return "Every "+(numBCDtoDEC(out)+1)+outstr
elif showsetting == True:
return "Disabled (Interval every 1"+outstr+")"
# Setting might matter to save resources
return "None"
# Describe Alarm Setting
def describeAlarm():
if bus is None:
return "Error"
minute = -1
hour = -1
caldate = -1
weekday = -1
out = bus.read_byte_data(ADDR_RTC, 9)
if (out & 0x80) == 0:
minute = numBCDtoDEC(out & 0x7f)
out = bus.read_byte_data(ADDR_RTC, 10)
if (out & 0x80) == 0:
hour = numBCDtoDEC(out & 0x3f)
out = bus.read_byte_data(ADDR_RTC, 11)
if (out & 0x80) == 0:
caldate = numBCDtoDEC(out & 0x3f)
out = bus.read_byte_data(ADDR_RTC, 12)
if (out & 0x80) == 0:
weekday = numBCDtoDEC(out & 0x7)
if weekday < 0 and caldate < 0 and hour < 0 and minute < 0:
return "None"
# Convert from UTC
utcschedule = argonrtc.describeSchedule([-1], [weekday], [caldate], [hour], [minute])
weekday, caldate, hour, minute = argonrtc.convertAlarmTimezone(weekday, caldate, hour, minute, False)
return argonrtc.describeSchedule([-1], [weekday], [caldate], [hour], [minute]) + " Local (RTC Schedule: "+utcschedule+" UTC)"
# Describe Control Flags
def describeControlRegisters():
if bus is None:
print("Error")
return
out = bus.read_byte_data(ADDR_RTC, 1)
print("\n***************")
print("Control Status 2")
print("\tTI_TP Flag:", ((out & 0x10) != 0))
print("\tAlarm Flag:", ((out & RTC_ALARM_BIT) != 0),"( Enabled =", (out & (RTC_ALARM_BIT>>2)) != 0, ")")
print("\tTimer Flag:", ((out & RTC_TIMER_BIT) != 0),"( Enabled =", (out & (RTC_TIMER_BIT>>2)) != 0, ")")
print("Alarm Setting:")
print("\t"+describeAlarm())
print("Timer Setting:")
print("\t"+describeTimer(True))
print("***************\n")
#########
# Alarm
#########
# Check if RTC Alarm Flag is ON
def hasRTCAlarmFlag():
return hasRTCEventFlag(RTC_ALARM_BIT)
# Clear RTC Alarm Flag
def clearRTCAlarmFlag():
return clearRTCEventFlag(RTC_ALARM_BIT)
# Enables RTC Alarm Register
def enableAlarm(registeraddr, value, mask):
if bus is None:
return
# 0x00 is Enabled
bus.write_byte_data(ADDR_RTC, registeraddr, (numDECtoBCD(value)&mask))
# Disables RTC Alarm Register
def disableAlarm(registeraddr):
if bus is None:
return
# 0x80 is disabled
bus.write_byte_data(ADDR_RTC, registeraddr, 0x80)
# Removes all alarm settings
def removeRTCAlarm():
setRTCEventFlag(RTC_ALARM_BIT, False)
disableAlarm(9)
disableAlarm(10)
disableAlarm(11)
disableAlarm(12)
# Set RTC Alarm (Negative values ignored)
def setRTCAlarm(enableflag, weekday, caldate, hour, minute):
weekday, caldate, hour, minute = argonrtc.getRTCAlarm(weekday, caldate, hour, minute)
if caldate < 1 and weekday < 0 and hour < 0 and minute < 0:
return -1
clearRTCAlarmFlag()
setRTCEventFlag(RTC_ALARM_BIT, enableflag)
if minute >= 0:
enableAlarm(9, minute, 0x7f)
else:
disableAlarm(9)
if hour >= 0:
enableAlarm(10, hour, 0x7f)
else:
disableAlarm(10)
if caldate >= 0:
enableAlarm(11, caldate, 0x7f)
else:
disableAlarm(11)
if weekday >= 0:
# 0 - Sun (datetime 0 - Mon)
if weekday > 5:
weekday = 0
else:
weekday = weekday + 1
enableAlarm(12, weekday, 0x7f)
else:
disableAlarm(12)
return 0
# Set RTC Hourly Alarm
def setRTCAlarmHourly(enableflag, minute):
return setRTCAlarm(enableflag, -1, -1, -1, minute)
# Set RTC Daily Alarm
def setRTCAlarmDaily(enableflag, hour, minute):
return setRTCAlarm(enableflag, -1, -1, hour, minute)
# Set RTC Weekly Alarm
def setRTCAlarmWeekly(enableflag, dayofweek, hour, minute):
return setRTCAlarm(enableflag, dayofweek, -1, hour, minute)
# Set RTC Monthly Alarm
def setRTCAlarmMonthly(enableflag, caldate, hour, minute):
return setRTCAlarm(enableflag, -1, caldate, hour, minute)
#########
# Timer
#########
# Check if RTC Timer Flag is ON
def hasRTCTimerFlag():
return hasRTCEventFlag(RTC_TIMER_BIT)
# Clear RTC Timer Flag
def clearRTCTimerFlag():
return clearRTCEventFlag(RTC_TIMER_BIT)
# Remove RTC Timer Setting
def removeRTCTimer():
if bus is None:
return
setRTCEventFlag(RTC_TIMER_BIT, False)
# Timer disable and Set Timer frequency to lowest (0x3=1 per minute)
bus.write_byte_data(ADDR_RTC, 14, 3)
bus.write_byte_data(ADDR_RTC, 15, 0)
# Set RTC Timer Interval
def setRTCTimerInterval(enableflag, value, inSeconds = False):
if bus is None:
return -1
if value > 255 or value < 1:
return -1
clearRTCTimerFlag()
setRTCEventFlag(RTC_TIMER_BIT, enableflag)
# 0x80 Timer Enabled, mode: 0x3=1/Min, 0x2=1/Sec, 0x1=Per 64th Sec, 0=Per 4096th Sec
timerconfigFlag = 0x83
if inSeconds == True:
timerconfigFlag = 0x82
bus.write_byte_data(ADDR_RTC, 14, timerconfigFlag)
bus.write_byte_data(ADDR_RTC, 15, numDECtoBCD(value&0xff))
return 0
#############
# Date/Time
#############
# Returns RTC timestamp as datetime object
def getRTCdatetime():
if bus is None:
return datetime.datetime(2000, 1, 1, 0, 0, 0)
# Data Sheet Recommends to read this manner (instead of from registers)
bus.write_byte(ADDR_RTC,2)
out = bus.read_byte(ADDR_RTC)
out = numBCDtoDEC(out & 0x7f)
second = out
#warningflag = (out & 0x80)>>7
out = bus.read_byte(ADDR_RTC)
minute = numBCDtoDEC(out & 0x7f)
out = bus.read_byte(ADDR_RTC)
hour = numBCDtoDEC(out & 0x3f)
out = bus.read_byte(ADDR_RTC)
caldate = numBCDtoDEC(out & 0x3f)
out = bus.read_byte(ADDR_RTC)
#weekDay = numBCDtoDEC(out & 7)
out = bus.read_byte(ADDR_RTC)
month = numBCDtoDEC(out & 0x1f)
out = bus.read_byte(ADDR_RTC)
year = numBCDtoDEC(out)
#print({"year":year, "month": month, "date": caldate, "hour": hour, "minute": minute, "second": second})
if month == 0:
# Reset, uninitialized RTC
month = 1
# Timezone is GMT/UTC +0
# Year is from 2000
try:
return datetime.datetime(year+2000, month, caldate, hour, minute, second)+argonrtc.getLocaltimeOffset()
except:
return datetime.datetime(2000, 1, 1, 0, 0, 0)
# set RTC time using datetime object (Local time)
def setRTCdatetime(localdatetime):
if bus is None:
return
# Set local time to UTC
localdatetime = localdatetime - argonrtc.getLocaltimeOffset()
# python Sunday = 6, RTC Sunday = 0
weekDay = localdatetime.weekday()
if weekDay == 6:
weekDay = 0
else:
weekDay = weekDay + 1
# Write to respective registers
bus.write_byte_data(ADDR_RTC,2,numDECtoBCD(localdatetime.second))
bus.write_byte_data(ADDR_RTC,3,numDECtoBCD(localdatetime.minute))
bus.write_byte_data(ADDR_RTC,4,numDECtoBCD(localdatetime.hour))
bus.write_byte_data(ADDR_RTC,5,numDECtoBCD(localdatetime.day))
bus.write_byte_data(ADDR_RTC,6,numDECtoBCD(weekDay))
bus.write_byte_data(ADDR_RTC,7,numDECtoBCD(localdatetime.month))
# Year is from 2000
bus.write_byte_data(ADDR_RTC,8,numDECtoBCD(localdatetime.year-2000))
#########
# Config
#########
# Set Next Alarm on RTC
def setNextAlarm(commandschedulelist, prevdatetime):
nextcommandtime, weekday, caldate, hour, minute = argonrtc.getNextAlarm(commandschedulelist, prevdatetime)
if prevdatetime >= nextcommandtime:
return prevdatetime
if weekday < 0 and caldate < 0 and hour < 0 and minute < 0:
# No schedule
# nextcommandtime is current time, which will be replaced/checked next iteration
removeRTCAlarm()
return nextcommandtime
setRTCAlarm(True, nextcommandtime.weekday(), nextcommandtime.day, nextcommandtime.hour, nextcommandtime.minute)
return nextcommandtime
def allowshutdown():
uptime = 0.0
errorflag = False
try:
cpuctr = 0
tempfp = open("/proc/uptime", "r")
alllines = tempfp.readlines()
for temp in alllines:
infolist = temp.split(" ")
if len(infolist) > 1:
uptime = float(infolist[0])
break
tempfp.close()
except IOError:
errorflag = True
# 120=2mins minimum up time
return uptime > 120
######
if len(sys.argv) > 1:
cmd = sys.argv[1].upper()
# Enable Alarm/Timer Flags
enableflag = True
if cmd == "CLEAN":
removeRTCAlarm()
removeRTCTimer()
elif cmd == "SHUTDOWN":
clearRTCAlarmFlag()
clearRTCTimerFlag()
elif cmd == "GETRTCSCHEDULE":
print("Alarm Setting:")
print("\t"+describeAlarm())
#print("Timer Setting:")
#print("\t"+describeTimer(True))
elif cmd == "GETRTCTIME":
print("RTC Time:", getRTCdatetime())
elif cmd == "UPDATERTCTIME":
setRTCdatetime(datetime.datetime.now())
print("RTC Time:", getRTCdatetime())
elif cmd == "GETSCHEDULELIST":
argonrtc.describeConfigList(RTC_CONFIGFILE)
elif cmd == "SHOWSCHEDULE":
if len(sys.argv) > 2:
if sys.argv[2].isdigit():
# Display starts at 2, maps to 0-based index
configidx = int(sys.argv[2])-2
configlist = argonrtc.loadConfigList(RTC_CONFIGFILE)
if len(configlist) > configidx:
print (" ",argonrtc.describeConfigListEntry(configlist[configidx]))
else:
print(" Invalid Schedule")
elif cmd == "REMOVESCHEDULE":
if len(sys.argv) > 2:
if sys.argv[2].isdigit():
# Display starts at 2, maps to 0-based index
configidx = int(sys.argv[2])-2
argonrtc.removeConfigEntry(RTC_CONFIGFILE, configidx)
elif cmd == "SERVICE":
argonrtc.updateSystemTime(getRTCdatetime())
commandschedulelist = argonrtc.formCommandScheduleList(argonrtc.loadConfigList(RTC_CONFIGFILE))
nextrtcalarmtime = setNextAlarm(commandschedulelist, datetime.datetime.now())
serviceloop = True
while serviceloop==True:
clearRTCAlarmFlag()
clearRTCTimerFlag()
tmpcurrenttime = datetime.datetime.now()
if nextrtcalarmtime <= tmpcurrenttime:
# Update RTC Alarm to next iteration
nextrtcalarmtime = setNextAlarm(commandschedulelist, nextrtcalarmtime)
if len(argonrtc.getCommandForTime(commandschedulelist, tmpcurrenttime, "off")) > 0:
# Shutdown detected, issue command then end service loop
if allowshutdown():
os.system("shutdown now -h")
serviceloop = False
# Don't break to sleep while command executes (prevents service to restart)
time.sleep(60)
elif False:
print("System Time: ", datetime.datetime.now())
print("RTC Time: ", getRTCdatetime())
describeControlRegisters()
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Argon EON RTC Service
After=multi-user.target
[Service]
Type=simple
Restart=always
RemainAfterExit=true
ExecStart=/usr/bin/python3 /etc/argon/argoneond.py SERVICE
[Install]
WantedBy=multi-user.target
+345
View File
@@ -0,0 +1,345 @@
#!/usr/bin/python3
import sys
import datetime
import math
import os
import time
# Initialize I2C Bus
import smbus
try:
bus=smbus.SMBus(1)
except Exception:
try:
# Older version
bus=smbus.SMBus(0)
except Exception:
print("Unable to detect i2c")
bus=None
OLED_WD=128
OLED_HT=64
OLED_SLAVEADDRESS=0x6a
ADDR_OLED=0x3c
OLED_NUMFONTCHAR=256
OLED_BUFFERIZE = ((OLED_WD*OLED_HT)>>3)
oled_imagebuffer = [0] * OLED_BUFFERIZE
def oled_getmaxY():
return OLED_HT
def oled_getmaxX():
return OLED_WD
def oled_loadbg(bgname):
if bgname == "bgblack":
oled_clearbuffer()
return
elif bgname == "bgwhite":
oled_clearbuffer(1)
return
try:
file = open("/etc/argon/oled/"+bgname+".bin", "rb")
bgbytes = list(file.read())
file.close()
ctr = len(bgbytes)
if ctr == OLED_BUFFERIZE:
oled_imagebuffer[:] = bgbytes
elif ctr > OLED_BUFFERIZE:
oled_imagebuffer[:] = bgbytes[0:OLED_BUFFERIZE]
else:
oled_imagebuffer[0:ctr] = bgbytes
# Clear the rest of the buffer
while ctr < OLED_BUFFERIZE:
oled_imagebuffer[ctr] = 0
ctr=ctr+1
except FileNotFoundError:
oled_clearbuffer()
def oled_clearbuffer(value = 0):
if value != 0:
value = 0xff
ctr = 0
while ctr < OLED_BUFFERIZE:
oled_imagebuffer[ctr] = value
ctr=ctr+1
def oled_writebyterow(x,y,bytevalue, mode = 0):
bufferoffset = OLED_WD*(y>>3) + x
if mode == 0:
oled_imagebuffer[bufferoffset] = bytevalue
elif mode == 1:
oled_imagebuffer[bufferoffset] = bytevalue^oled_imagebuffer[bufferoffset]
else:
oled_imagebuffer[bufferoffset] = bytevalue|oled_imagebuffer[bufferoffset]
def oled_writebuffer(x,y,value, mode = 0):
yoffset = y>>3
yshift = y&0x7
ybit = (1<<yshift)
ymask = 0xFF^ybit
if value != 0:
value = ybit
bufferoffset = OLED_WD*yoffset + x
curval = oled_imagebuffer[bufferoffset]
if mode & 1:
oled_imagebuffer[bufferoffset] = curval^value
else:
oled_imagebuffer[bufferoffset] = curval&ymask|value
def oled_fill(value):
oled_clearbuffer(value)
oled_flushimage()
def oled_flushimage(hidescreen = True):
if hidescreen == True:
# Reset/Hide screen
oled_power(False)
xctr = 0
while xctr < OLED_WD:
yctr = 0
while yctr < OLED_HT:
oled_flushblock(xctr, yctr)
yctr = yctr + 8
xctr = xctr + 32
if hidescreen == True:
# Display
oled_power(True)
def oled_flushblock(xoffset, yoffset):
yoffset = yoffset>>3
blocksize = 32
if bus is None:
return
try:
# Set COM-H Addressing
bus.write_byte_data(ADDR_OLED, 0, 0x20)
bus.write_byte_data(ADDR_OLED, 0, 0x1)
# Set Column range
bus.write_byte_data(ADDR_OLED, 0, 0x21)
bus.write_byte_data(ADDR_OLED, 0, xoffset)
bus.write_byte_data(ADDR_OLED, 0, xoffset+blocksize-1)
# Set Row Range
bus.write_byte_data(ADDR_OLED, 0, 0x22)
bus.write_byte_data(ADDR_OLED, 0, yoffset)
bus.write_byte_data(ADDR_OLED, 0, yoffset)
# Set Display Start Line
bus.write_byte_data(ADDR_OLED, 0, 0x40)
bufferoffset = OLED_WD*yoffset + xoffset
# Write Out Buffer
bus.write_i2c_block_data(ADDR_OLED, OLED_SLAVEADDRESS, oled_imagebuffer[bufferoffset:(bufferoffset+blocksize)])
except:
return
def oled_drawfilledrectangle(x, y, wd, ht, mode = 0):
ymax = y + ht
cury = y&0xF8
xmax = x + wd
curx = x
if ((y & 0x7)) != 0:
yshift = y&0x7
bytevalue = (0xFF<<yshift)&0xFF
# If 8 no additional masking needed
if ymax-cury < 8:
yshift = 8-((ymax-cury)&0x7)
bytevalue = bytevalue & (0xFF>>yshift)
while curx < xmax:
oled_writebyterow(curx,cury,bytevalue, mode)
curx = curx + 1
cury = cury + 8
# Draw 8 rows at a time when possible
while cury + 8 < ymax:
curx = x
while curx < xmax:
oled_writebyterow(curx,cury,0xFF, mode)
curx = curx + 1
cury = cury + 8
if cury < ymax:
yshift = 8-((ymax-cury)&0x7)
bytevalue = (0xFF>>yshift)
curx = x
while curx < xmax:
oled_writebyterow(curx,cury,bytevalue, mode)
curx = curx + 1
def oled_writetextaligned(textdata, x, y, boxwidth, alignmode, charwd = 6, mode = 0):
leftoffset = 0
if alignmode == 1:
# Centered
leftoffset = (boxwidth-len(textdata)*charwd)>>1
elif alignmode == 2:
# Right aligned
leftoffset = (boxwidth-len(textdata)*charwd)
oled_writetext(textdata, x+leftoffset, y, charwd, mode)
def oled_writetext(textdata, x, y, charwd = 6, mode = 0):
if charwd < 6:
charwd = 6
charht = int((charwd<<3)/6)
if charht & 0x7:
charht = (charht&0xF8) + 8
try:
file = open("/etc/argon/oled/font"+str(charht)+"x"+str(charwd)+".bin", "rb")
fontbytes = list(file.read())
file.close()
except FileNotFoundError:
try:
# Default to smallest
file = open("/etc/argon/oled/font8x6.bin", "rb")
fontbytes = list(file.read())
file.close()
except FileNotFoundError:
return
if ((y & 0x7)) == 0:
# Use optimized loading
oled_fastwritetext(textdata, x, y, charht, charwd, fontbytes, mode)
return
numfontrow = charht>>3
ctr = 0
while ctr < len(textdata):
fontoffset = ord(textdata[ctr])*charwd
fontcol = 0
while fontcol < charwd and x < OLED_WD:
fontrow = 0
row = y
while fontrow < numfontrow and row < OLED_HT and x >= 0:
curbit = 0x80
curbyte = (fontbytes[fontoffset + fontcol + (OLED_NUMFONTCHAR*charwd*fontrow)])
subrow = 0
while subrow < 8 and row < OLED_HT:
value = 0
if (curbyte&curbit) != 0:
value = 1
oled_writebuffer(x,row,value, mode)
curbit = curbit >> 1
row = row + 1
subrow = subrow + 1
fontrow = fontrow + 1
fontcol = fontcol + 1
x = x + 1
ctr = ctr + 1
def oled_fastwritetext(textdata, x, y, charht, charwd, fontbytes, mode = 0):
numfontrow = charht>>3
ctr = 0
while ctr < len(textdata):
fontoffset = ord(textdata[ctr])*charwd
fontcol = 0
while fontcol < charwd and x < OLED_WD:
fontrow = 0
row = y&0xF8
while fontrow < numfontrow and row < OLED_HT and x >= 0:
curbyte = (fontbytes[fontoffset + fontcol + (OLED_NUMFONTCHAR*charwd*fontrow)])
oled_writebyterow(x,row,curbyte, mode)
fontrow = fontrow + 1
row = row + 8
fontcol = fontcol + 1
x = x + 1
ctr = ctr + 1
return
def oled_power(turnon = True):
cmd = 0xAE
if turnon == True:
cmd = cmd|1
if bus is None:
return
try:
bus.write_byte_data(ADDR_OLED, 0, cmd)
except:
return
def oled_inverse(enable = True):
cmd = 0xA6
if enable == True:
cmd = cmd|1
if bus is None:
return
try:
bus.write_byte_data(ADDR_OLED, 0, cmd)
except:
return
def oled_fullwhite(enable = True):
cmd = 0xA4
if enable == True:
cmd = cmd|1
if bus is None:
return
try:
bus.write_byte_data(ADDR_OLED, 0, cmd)
except:
return
def oled_reset():
if bus is None:
return
try:
# Set COM-H Addressing
bus.write_byte_data(ADDR_OLED, 0, 0x20)
bus.write_byte_data(ADDR_OLED, 0, 0x1)
# Set Column range
bus.write_byte_data(ADDR_OLED, 0, 0x21)
bus.write_byte_data(ADDR_OLED, 0, 0)
bus.write_byte_data(ADDR_OLED, 0, OLED_WD-1)
# Set Row Range
bus.write_byte_data(ADDR_OLED, 0, 0x22)
bus.write_byte_data(ADDR_OLED, 0, 0)
bus.write_byte_data(ADDR_OLED, 0, (OLED_HT>>3)-1)
# Set Page Addressing
bus.write_byte_data(ADDR_OLED, 0, 0x20)
bus.write_byte_data(ADDR_OLED, 0, 0x2)
# Set GDDRAM Address
bus.write_byte_data(ADDR_OLED, 0, 0xB0)
# Set Display Start Line
bus.write_byte_data(ADDR_OLED, 0, 0x40)
except:
return
+824
View File
@@ -0,0 +1,824 @@
#!/usr/bin/python3
#
# This script monitor battery via ic2 and keyboard events.
#
# Additional comments are found in each function below
#
#
from evdev import InputDevice, categorize, ecodes, list_devices
from select import select
import subprocess
import sys
import os
import time
from threading import Thread
from queue import Queue
UPS_LOGFILE="/dev/shm/upslog.txt"
KEYBOARD_LOCKFILE="/dev/shm/argononeupkeyboardlock.txt"
KEYCODE_BRIGHTNESSUP = "KEY_BRIGHTNESSUP"
KEYCODE_BRIGHTNESSDOWN = "KEY_BRIGHTNESSDOWN"
KEYCODE_VOLUMEUP = "KEY_VOLUMEUP"
KEYCODE_VOLUMEDOWN = "KEY_VOLUMEDOWN"
KEYCODE_PAUSE = "KEY_PAUSE"
KEYCODE_MUTE = "KEY_MUTE"
###################
# Utilty Functions
###################
# Debug Logger
def debuglog(typestr, logstr):
return
# try:
# DEBUGFILE="/dev/shm/argononeupkeyboarddebuglog.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
def runcmdlist(key, cmdlist):
try:
cmdresult = subprocess.run(cmdlist,
capture_output=True,
text=True,
check=True
)
#debuglog(key+"-result-output",str(cmdresult.stdout))
if cmdresult.stderr:
debuglog(key+"-result-error",str(cmdresult.stderr))
#debuglog(key+"-result-code",str(cmdresult.returncode))
except subprocess.CalledProcessError as e:
debuglog(key+"-error-output",str(e.stdout))
if e.stderr:
debuglog(key+"-error-error",str(e.stderr))
debuglog(key+"-error-code",str(e.returncode))
except FileNotFoundError:
debuglog(key+"-error-filenotfound","Command Not Found")
except Exception as othererr:
try:
debuglog(key+"-error-other", str(othererr))
except:
debuglog(key+"-error-other", "Other Error")
def createlockfile(fname):
# try:
# if os.path.isfile(fname):
# return True
# except Exception as checklockerror:
# try:
# debuglog("keyboard-lock-error", str(checklockerror))
# except:
# debuglog("keyboard-lock-error", "Error Checking Lock File")
# try:
# with open(fname, "w") as txt_file:
# txt_file.write(time.asctime(time.localtime(time.time()))+"\n")
# except Exception as lockerror:
# try:
# debuglog("keyboard-lock-error", str(lockerror))
# except:
# debuglog("keyboard-lock-error", "Error Creating Lock File")
return False
def deletelockfile(fname):
# try:
# os.remove(fname)
# except Exception as lockerror:
# try:
# debuglog("keyboard-lock-error", str(lockerror))
# except:
# debuglog("keyboard-lock-error", "Error Removing Lock File")
return True
# 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 (copied)
#############
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 Exception as einit:
try:
debuglog("keyboard-battery-error", str(einit))
except:
debuglog("keyboard-battery-error", "Error getting battery status")
#pass
return outobj
def keyboardevent_getdevicepaths():
outlist = []
try:
for path in list_devices():
try:
tmpdevice = InputDevice(path)
keyeventlist = tmpdevice.capabilities().get(ecodes.EV_KEY, [])
# Keyboard has EV_KEY (key) and EV_REP (autorepeat)
if ecodes.KEY_BRIGHTNESSDOWN in keyeventlist and ecodes.KEY_BRIGHTNESSDOWN in keyeventlist:
outlist.append(path)
#debuglog("keyboard-device-keys", path)
#debuglog("keyboard-device-keys", str(keyeventlist))
elif ecodes.KEY_F2 in keyeventlist and ecodes.KEY_F3 in keyeventlist:
# Keyboards with FN key sometimes do not include KEY_BRIGHTNESS in declaration
outlist.append(path)
#debuglog("keyboard-device-keys", path)
#debuglog("keyboard-device-keys", str(keyeventlist))
tmpdevice.close()
except:
pass
except:
pass
return outlist
def keyboardevent_devicechanged(curlist, newlist):
try:
for curpath in curlist:
if curpath not in newlist:
return True
for newpath in newlist:
if newpath not in curlist:
return True
except:
pass
return False
def keyboardevent_getbrigthnesstoolid():
toolid = 0
try:
output = subprocess.check_output(["ddcutil", "--version"], text=True, stderr=subprocess.DEVNULL)
lines = output.splitlines()
if len(lines) > 0:
tmpline = lines[0].strip()
toolid = int(tmpline.split(" ")[1].split(".")[0])
except Exception as einit:
try:
debuglog("keyboard-brightness-tool-error", str(einit))
except:
debuglog("keyboard-brightness-tool-error", "Error getting tool id value")
debuglog("keyboard-brightness-tool", toolid)
return toolid
def keyboardevent_getbrigthnessinfo(toolid, defaultlevel=50):
level = defaultlevel
try:
# VCP code x10(Brightness ): current value = 90, max value = 100
if toolid > 1:
# Disabled dynamic sleep "--disable-dynamic-sleep", "--sleep-multiplier", "0.1"
output = subprocess.check_output(["ddcutil", "--skip-ddc-checks", "--disable-dynamic-sleep", "--sleep-multiplier", "0.1", "getvcp", "10"], text=True, stderr=subprocess.DEVNULL)
else:
output = subprocess.check_output(["ddcutil", "--sleep-multiplier", "0.1", "getvcp", "10"], text=True, stderr=subprocess.DEVNULL)
debuglog("keyboard-brightness-info", output)
level = int(output.split(":")[-1].split(",")[0].split("=")[-1].strip())
except Exception as einit:
try:
debuglog("keyboard-brightness-error", str(einit))
except:
debuglog("keyboard-brightness-error", "Error getting base value")
return {
"level": level
}
def keyboardevent_adjustbrigthness(toolid, baselevel, adjustval=5):
curlevel = baselevel
if adjustval == 0:
return {
"level": baselevel
}
# Moved reading because ddcutil has delay
# try:
# tmpobj = keyboardevent_getbrigthnessinfo(toolid, curlevel)
# curlevel = tmpobj["level"]
# except Exception:
# pass
tmpval = max(10, min(100, curlevel + adjustval))
if tmpval != curlevel:
try:
debuglog("keyboard-brightness", str(curlevel)+"% to "+str(tmpval)+"%")
if toolid > 1:
# Disabled dynamic sleep "--disable-dynamic-sleep", "--sleep-multiplier", "0.1"
runcmdlist("brightness", ["ddcutil", "--skip-ddc-checks", "--disable-dynamic-sleep", "--sleep-multiplier", "0.1", "setvcp", "10", str(tmpval)])
else:
runcmdlist("brightness", ["ddcutil", "--sleep-multiplier", "0.1", "setvcp", "10", str(tmpval)])
notifymessage("Brightness: "+str(tmpval)+"%", False)
except Exception as adjusterr:
try:
debuglog("keyboard-brightness-error", str(adjusterr))
except:
debuglog("keyboard-brightness-error", "Error adjusting value")
return {
"level": curlevel
}
# DEBUG: Checking
#keyboardevent_getbrigthnessinfo(toolid, tmpval)
return {
"level": tmpval
}
def keyboardevent_getvolumesinkid(usedefault=True):
if usedefault == True:
return "@DEFAULT_SINK@"
cursinkid = 0
try:
output = subprocess.check_output(["wpctl", "status"], text=True, encoding='utf-8', stderr=subprocess.DEVNULL)
# Find Audio section
tmpline = ""
foundidx = 0
lines = output.splitlines()
lineidx = 0
while lineidx < len(lines):
tmpline = lines[lineidx].strip()
if tmpline == "Audio":
foundidx = lineidx
break
lineidx = lineidx + 1
if foundidx < 1:
return 0
# Find Sinks section
foundidx = 0
lineidx = lineidx + 1
while lineidx < len(lines):
if "Sinks:" in lines[lineidx]:
foundidx = lineidx
break
elif len(lines[lineidx]) < 1:
break
lineidx = lineidx + 1
if foundidx < 1:
return 0
# Get find default id, or first id
lineidx = lineidx + 1
while lineidx < len(lines):
if "vol:" in lines[lineidx] and "." in lines[lineidx]:
tmpstr = lines[lineidx].split(".")[0]
tmplist = tmpstr.split()
if len(tmplist) > 1:
if tmplist[len(tmplist)-2] == "*":
return int(tmplist[len(tmplist)-1])
if len(tmplist) > 0 and cursinkid < 1:
cursinkid = int(tmplist[len(tmplist)-1])
elif len(lines[lineidx]) < 3:
break
lineidx = lineidx + 1
except Exception as einit:
try:
debuglog("keyboard-volume-error", str(einit))
except:
debuglog("keyboard-volume-error", "Error getting device ID")
return cursinkid
def keyboardevent_getvolumeinfo(deviceidstr="", defaultlevel=50, defaultmuted=0):
muted = defaultmuted
level = defaultlevel
try:
if deviceidstr == "":
audioidstr = str(keyboardevent_getvolumesinkid())
if audioidstr == "0":
debuglog("keyboard-volume-error", "Error getting device id")
return {
"level": defaultmuted,
"muted": defaultlevel
}
deviceidstr = audioidstr
output = subprocess.check_output(["wpctl", "get-volume", deviceidstr], text=True, stderr=subprocess.DEVNULL)
debuglog("keyboard-volume-info", output)
muted = 0
level = 0
# Parse output, examples
# Volume: 0.65
# Volume: 0.55 [MUTED]
outlist = output.split()
if len(outlist) > 0:
# Get last element
tmpstr = outlist[len(outlist)-1]
# Check if muted
if "MUTE" in tmpstr:
muted = 1
if len(outlist) > 1:
tmpstr = outlist[len(outlist)-2]
if tmpstr.endswith("%"):
# Level 100% to 0%
level = int(float(tmpstr[:-1]))
elif tmpstr.replace('.', '').isdigit():
# Level 1.00 to 0.00
level = int(float(tmpstr) * 100.0)
except Exception as einit:
try:
debuglog("keyboard-volume-error", str(einit))
except:
debuglog("keyboard-volume-error", "Error getting base value")
return {
"level": defaultmuted,
"muted": defaultlevel
}
#debuglog("keyboard-volume-get", str(level)+"% Mute:"+str(muted))
return {
"level": level,
"muted": muted
}
def keyboardevent_adjustvolume(baselevel, basemuted, adjustval=5):
curlevel = baselevel
curmuted = basemuted
needsnotification = False
deviceidstr = str(keyboardevent_getvolumesinkid())
if deviceidstr == "0":
debuglog("keyboard-volume-error", "Error getting device id")
return {
"level": baselevel,
"muted": basemuted
}
# try:
# tmpobj = keyboardevent_getvolumeinfo(deviceidstr, curlevel, curmuted)
# curlevel = tmpobj["level"]
# curmuted = tmpobj["muted"]
# except Exception:
# pass
tmpmuted = curmuted
if adjustval == 0:
# Toggle Mute
if curmuted == 0:
tmpmuted = 1
else:
tmpmuted = 0
tmpval = max(10, min(100, curlevel + adjustval))
if tmpval != curlevel:
try:
debuglog("keyboard-volume", str(curlevel)+"% to "+str(tmpval)+"%")
runcmdlist("volume", ["wpctl", "set-volume", deviceidstr, f"{tmpval}%"])
needsnotification = True
tmpmuted = 0
except Exception as adjusterr:
try:
debuglog("keyboard-volume-error", str(adjusterr))
except:
debuglog("keyboard-volume-error", "Error adjusting value")
return {
"level": curlevel,
"muted": curmuted
}
elif adjustval != 0:
# To unmute even if no volume level change
tmpmuted = 0
if tmpmuted != curmuted:
try:
debuglog("keyboard-mute", str(tmpmuted))
runcmdlist("mute", ["wpctl", "set-mute", deviceidstr, str(tmpmuted)])
needsnotification = True
except Exception as adjusterr:
try:
debuglog("keyboard-mute-error", str(adjusterr))
except:
debuglog("keyboard-mute-error", "Error adjusting value")
return {
"level": tmpval,
"muted": curmuted
}
#if tmpmuted == 1:
# notifymessage("Volume: Muted", False)
#else:
# notifymessage("Volume: "+str(tmpval)+"%", False)
# DEBUG: Checking
#keyboardevent_getvolumeinfo(deviceidstr, tmpval, tmpmuted)
return {
"level": tmpval,
"muted": tmpmuted
}
def keyboard_getlayoutfieldvalue(tmpval):
debuglog("keyboard-layout-lang", tmpval)
if "us" in tmpval:
debuglog("keyboard-layout-langout", "us")
return "us"
debuglog("keyboard-layout-langout", "gb")
return "gb" # uk, gb, etc
#return tmpval
def keyboard_getdevicefw(kbdevice):
# info: vendor 0x6080=24704, product 0x8062=32866
try:
if kbdevice.info.vendor == 24704 and kbdevice.info.product == 32866:
# Special HID
return "314"
except Exception as infoerr:
pass
return ""
def keyboardevemt_keyhandler(readq):
ADJUSTTYPE_NONE=0
ADJUSTTYPE_BRIGHTNESS=1
ADJUSTTYPE_VOLUME=2
ADJUSTTYPE_MUTE=3
ADJUSTTYPE_BATTERYINFO=4
DATAREFRESHINTERVALSEC = 10
PRESSWAITINTERVALSEC = 0.5
FIRSTHOLDINTERVALSEC = 0.5
HOLDWAITINTERVALSEC = 0.5
# Get current levels
volumetime = time.time()
curvolumemuted = 0
curvolume = 50
brightnesstime = volumetime
curbrightness = 50
brightnesstoolid = 0
try:
brightnesstoolid = keyboardevent_getbrigthnesstoolid()
except Exception:
brightnesstoolid = 0
pass
try:
tmpobj = keyboardevent_getbrigthnessinfo(brightnesstoolid)
curbrightness = tmpobj["level"]
except Exception:
pass
try:
tmpobj = keyboardevent_getvolumeinfo()
curvolumemuted = tmpobj["muted"]
curvolume = tmpobj["level"]
except Exception:
pass
while True:
try:
tmpkeymode = 0
tmpkeycode = ""
adjustval = 0
adjusttype = ADJUSTTYPE_NONE
tmpcode = readq.get() # Blocking
try:
codeelements = tmpcode.split("+")
if len(codeelements) == 2:
if codeelements[0] == "PRESS":
tmpkeymode = 1
else:
tmpkeymode = 2
tmpkeycode = codeelements[1]
elif tmpcode == "EXIT":
readq.task_done()
return
except Exception:
tmpkeycode = ""
tmpkeymode = 0
pass
tmptime = time.time()
if tmpkeycode in [KEYCODE_BRIGHTNESSDOWN, KEYCODE_BRIGHTNESSUP]:
if tmpkeymode == 1 and tmptime - brightnesstime > DATAREFRESHINTERVALSEC:
# Do not update value during hold
try:
tmpobj = keyboardevent_getbrigthnessinfo(brightnesstoolid)
curbrightness = tmpobj["level"]
except Exception:
pass
adjusttype = ADJUSTTYPE_BRIGHTNESS
if tmpkeycode == KEYCODE_BRIGHTNESSDOWN:
adjustval = -5*tmpkeymode
else:
adjustval = 5*tmpkeymode
brightnesstime = tmptime
elif tmpkeycode in [KEYCODE_MUTE, KEYCODE_VOLUMEDOWN, KEYCODE_VOLUMEUP]:
if tmpkeymode == 1 and tmptime - volumetime > DATAREFRESHINTERVALSEC and tmpkeymode == 1:
# Do not update value during hold
try:
tmpobj = keyboardevent_getvolumeinfo()
curvolumemuted = tmpobj["muted"]
curvolume = tmpobj["level"]
except Exception:
pass
if tmpkeycode == KEYCODE_MUTE:
adjusttype = ADJUSTTYPE_MUTE
adjustval = 0
else:
adjusttype = ADJUSTTYPE_VOLUME
if tmpkeycode == KEYCODE_VOLUMEDOWN:
adjustval = -5*tmpkeymode
else:
adjustval = 5*tmpkeymode
volumetime = tmptime
elif tmpkeycode == KEYCODE_PAUSE:
adjusttype = ADJUSTTYPE_BATTERYINFO
else:
readq.task_done()
continue
try:
tmplockfilea = KEYBOARD_LOCKFILE+".a"
if createlockfile(tmplockfilea) == False:
# Debug ONLY
# if tmpkeymode == 1:
# debuglog("keyboard-event", "Press Key Code: "+str(tmpkeycode))
# else:
# debuglog("keyboard-event", "Hold Key Code: "+str(tmpkeycode))
if adjusttype == ADJUSTTYPE_BRIGHTNESS:
try:
tmpobj = keyboardevent_adjustbrigthness(brightnesstoolid, curbrightness, adjustval)
curbrightness = tmpobj["level"]
except Exception as brightnesserr:
try:
debuglog("keyboard-brightnessother-error", str(brightnesserr))
except:
debuglog("keyboard-brightnessother-error", "Error adjusting value")
pass
elif adjusttype == ADJUSTTYPE_VOLUME or adjusttype == ADJUSTTYPE_MUTE:
try:
tmpobj = keyboardevent_adjustvolume(curvolume, curvolumemuted, adjustval)
curvolumemuted = tmpobj["muted"]
curvolume = tmpobj["level"]
except Exception as volumeerr:
try:
debuglog("keyboard-volumeother-error", str(volumeerr))
except:
debuglog("keyboard-volumeother-error", "Error adjusting value")
pass
elif adjusttype == ADJUSTTYPE_BATTERYINFO:
outobj = battery_loadlogdata()
try:
notifymessage(outobj["power"], False)
except:
pass
deletelockfile(tmplockfilea)
except Exception as keyhandlererr:
try:
debuglog("keyboard-handlererror", str(keyhandleerr))
except:
debuglog("keyboard-handlererror", "Error")
readq.task_done()
except Exception as mainerr:
time.sleep(10)
# While True
def keyboardevent_monitor(writeq):
READTIMEOUTSECS = 1.0
FIRSTHOLDINTERVALSEC = 0.5
HOLDWAITINTERVALSEC = 0.5
while True:
try:
keypresstimestamp = {}
keyholdtimestamp = {}
# Get Devices
devicelist = []
devicefdlist = []
devicepathlist = keyboardevent_getdevicepaths()
devicefwlist = []
deviceidx = 0
while deviceidx < len(devicepathlist):
try:
tmpdevice = InputDevice(devicepathlist[deviceidx])
devicelist.append(tmpdevice)
devicefdlist.append(tmpdevice.fd)
devicefwlist.append(keyboard_getdevicefw(tmpdevice))
#debuglog("keyboard-device-info", devicepathlist[deviceidx])
#debuglog("keyboard-device-info", str(tmpdevice.info))
except Exception as deverr:
try:
debuglog("keyboard-deviceerror", str(deverr)+ " "+ devicepathlist[deviceidx])
except:
debuglog("keyboard-deviceerror", "Error "+devicepathlist[deviceidx])
deviceidx = deviceidx + 1
try:
debuglog("keyboard-update", str(len(devicefdlist))+" Devices")
while len(devicefdlist) > 0:
# Exception when one of the devices gets removed
# Wait for events on any registered device
r, w, x = select(devicefdlist, [], [], READTIMEOUTSECS)
for fd in r:
found = False
curdevicefw = ""
deviceidx = 0
while deviceidx < len(devicefdlist):
if devicefdlist[deviceidx] == fd:
curdevicefw = devicefwlist[deviceidx]
found = True
break
deviceidx = deviceidx + 1
if found:
for event in devicelist[deviceidx].read():
try:
# Process the event
#print("Device: "+devicelist[deviceidx].path+", Event: ", event)
#debuglog("keyboard-event", "Device: "+devicelist[deviceidx].path+", Event: "+str(event))
if event.type == ecodes.EV_KEY:
key_event = categorize(event)
keycodelist = []
# 2 hold, 0 release, 1 press
if event.value == 2 or event.value == 1:
#debuglog("keyboard-event", "Mode:"+str(event.value)+" Key Code: "+str(key_event.keycode))
if isinstance(key_event.keycode, str):
keycodelist = [key_event.keycode]
else:
keycodelist = key_event.keycode
else:
continue
keycodelistidx = 0
while keycodelistidx < len(keycodelist):
tmpkeycode = keycodelist[keycodelistidx]
if curdevicefw == "314":
# Remap printscreen event as pause and vice versa for special handling
if tmpkeycode == "KEY_PRINTSCREEN":
tmpkeycode = KEYCODE_PAUSE
elif tmpkeycode == "KEY_SYSRQ":
# This gets fired for some devices
tmpkeycode = KEYCODE_PAUSE
elif tmpkeycode == KEYCODE_PAUSE:
# Some other key so it will not fire
tmpkeycode = "KEY_PRINTSCREEN"
#debuglog("keyboard-event", "FW:" + curdevicefw+ " Key Code: "+tmpkeycode + " Press:"+keycodelist[keycodelistidx])
keycodelistidx = keycodelistidx + 1
# if tmpkeycode not in [KEYCODE_BRIGHTNESSDOWN, KEYCODE_BRIGHTNESSUP, KEYCODE_VOLUMEDOWN, KEYCODE_VOLUMEUP]:
# if event.value == 2:
# # Skip hold for unhandled keys
# continue
# elif tmpkeycode not in [KEYCODE_PAUSE, KEYCODE_MUTE]:
# # Skip press for unhandled keys
# continue
if tmpkeycode not in [KEYCODE_BRIGHTNESSDOWN, KEYCODE_BRIGHTNESSUP]:
if event.value == 2:
# Skip hold for unhandled keys
continue
elif tmpkeycode not in [KEYCODE_PAUSE]:
# Skip press for unhandled keys
continue
tmptime = time.time()
finalmode = event.value
if event.value == 2:
# Hold needs checking
if tmpkeycode in keypresstimestamp:
# Guard time before first for hold
if (tmptime - keypresstimestamp[tmpkeycode]) >= FIRSTHOLDINTERVALSEC:
# Guard time for hold
if tmpkeycode in keyholdtimestamp:
if (tmptime - keyholdtimestamp[tmpkeycode]) < HOLDWAITINTERVALSEC:
#debuglog("keyboard-event", "Hold Key Code: "+str(tmpkeycode)+" - Skip")
continue
else:
#debuglog("keyboard-event", "Hold Key Code: "+str(tmpkeycode)+" - Skip")
continue
else:
# Should not happen, but treat as if first press
finalmode = 1
#debuglog("keyboard-event", "Mode:"+str(event.value) + " Final:"+str(finalmode)+" " +str(tmpkeycode))
if finalmode == 1:
keypresstimestamp[tmpkeycode] = tmptime
writeq.put("PRESS+"+tmpkeycode)
else:
keyholdtimestamp[tmpkeycode] = tmptime
writeq.put("HOLD+"+tmpkeycode)
except Exception as keyhandleerr:
try:
debuglog("keyboard-keyerror", str(keyhandleerr))
except:
debuglog("keyboard-keyerror", "Error")
newpathlist = keyboardevent_getdevicepaths()
if keyboardevent_devicechanged(devicepathlist, newpathlist):
debuglog("keyboard-update", "Device list changed")
break
except Exception as e:
try:
debuglog("keyboard-mainerror", str(e))
except:
debuglog("keyboard-mainerror", "Error")
# Close devices
while len(devicelist) > 0:
tmpdevice = devicelist.pop(0)
try:
tmpdevice.close()
except:
pass
except Exception as mainerr:
time.sleep(10)
# While True
try:
writeq.put("EXIT")
except Exception:
pass
if len(sys.argv) > 1:
cmd = sys.argv[1].upper()
if cmd == "SERVICE":
if createlockfile(KEYBOARD_LOCKFILE) == True:
debuglog("keyboard-service", "Already running")
else:
try:
debuglog("keyboard-service", "Service Starting")
ipcq = Queue()
t1 = Thread(target = keyboardevemt_keyhandler, args =(ipcq, ))
t2 = Thread(target = keyboardevent_monitor, args =(ipcq, ))
t1.start()
t2.start()
ipcq.join()
except Exception as einit:
try:
debuglog("keyboard-service-error", str(einit))
except:
debuglog("keyboard-service-error", "Error")
debuglog("keyboard-service", "Service Stopped")
deletelockfile(KEYBOARD_LOCKFILE)
+254
View File
@@ -0,0 +1,254 @@
#!/bin/bash
daemonconfigfile=/etc/argononed.conf
unitconfigfile=/etc/argonunits.conf
fanmode="CPU"
if [ "$1" == "hdd" ]
then
daemonconfigfile=/etc/argononed-hdd.conf
fanmode="HDD"
fi
if [ -f "$unitconfigfile" ]
then
. $unitconfigfile
fi
if [ -z "$temperature" ]
then
temperature="C"
fi
echo "------------------------------------------"
echo " Argon Fan Speed Configuration Tool ($fanmode)"
echo "------------------------------------------"
echo "WARNING: This will remove existing configuration."
echo -n "Press Y to continue:"
read -n 1 confirm
echo
fanloopflag=1
newmode=0
if [ "$confirm" = "y" ]
then
confirm="Y"
fi
if [ "$confirm" != "Y" ]
then
fanloopflag=0
echo "Cancelled."
else
echo "Thank you."
fi
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 212 ]
then
# 212F = 100C
echo "-1"
return
fi
echo $curnumber
return
fi
echo "-1"
return
}
while [ $fanloopflag -eq 1 ]
do
echo
echo "Select fan mode:"
echo " 1. Always on"
if [ "$fanmode" == "HDD" ]
then
if [ "$temperature" == "C" ]
then
echo " 2. Adjust to temperatures (35C, 40C, and 45C)"
else
echo " 2. Adjust to temperatures (95F, 104F, and 113F)"
fi
else
if [ "$temperature" == "C" ]
then
echo " 2. Adjust to temperatures (55C, 60C, and 65C)"
else
echo " 2. Adjust to temperatures (130F, 140F, and 150F)"
fi
fi
echo " 3. Customize temperature cut-offs"
echo
echo " 0. Exit"
echo "NOTE: You can also edit $daemonconfigfile directly"
echo -n "Enter Number (0-3):"
newmode=$( get_number )
if [[ $newmode -eq 0 ]]
then
fanloopflag=0
elif [ $newmode -eq 1 ]
then
echo "#" > $daemonconfigfile
echo "# Argon Fan Speed Configuration $fanmode" >> $daemonconfigfile
echo "#" >> $daemonconfigfile
echo "# Min Temp=Fan Speed" >> $daemonconfigfile
errorfanflag=1
while [ $errorfanflag -eq 1 ]
do
echo -n "Please provide fan speed (30-100 only):"
curfan=$( get_number )
if [ $curfan -ge 30 ]
then
errorfanflag=0
elif [ $curfan -gt 100 ]
then
errorfanflag=0
fi
done
echo "1="$curfan >> $daemonconfigfile
sudo systemctl restart argononed.service
echo "Fan always on."
elif [ $newmode -eq 2 ]
then
echo "#" > $daemonconfigfile
echo "# Argon Fan Speed Configuration $fanmode" >> $daemonconfigfile
echo "#" >> $daemonconfigfile
echo "# Min Temp=Fan Speed" >> $daemonconfigfile
echo "Please provide fan speeds for the following temperatures:"
curtemp=55
maxtemp=70
if [ "$fanmode" == "HDD" ]
then
curtemp=30
maxtemp=60
fi
while [ $curtemp -lt $maxtemp ]
do
errorfanflag=1
while [ $errorfanflag -eq 1 ]
do
displaytemp=$curtemp
if [ "$temperature" == "F" ]
then
# Convert C to F
displaytemp=$((($curtemp*9/5)+32))
fi
echo -n ""$displaytemp"$temperature (30-100 only):"
curfan=$( get_number )
if [ $curfan -ge 30 ]
then
errorfanflag=0
elif [ $curfan -gt 100 ]
then
errorfanflag=0
fi
done
echo $curtemp"="$curfan >> $daemonconfigfile
curtemp=$((curtemp+5))
done
sudo systemctl restart argononed.service
echo "Configuration updated."
elif [ $newmode -eq 3 ]
then
echo "Please provide fan speeds and temperature pairs"
echo
subloopflag=1
paircounter=0
while [ $subloopflag -eq 1 ]
do
errortempflag=1
errorfanflag=1
echo "(You may set a blank value to end configuration)"
while [ $errortempflag -eq 1 ]
do
echo -n "Provide minimum temperature of $fanmode (in $temperature) then [ENTER]:"
curtemp=$( get_number )
if [ $curtemp -ge 0 ]
then
errortempflag=0
elif [ $curtemp -eq -2 ]
then
# Blank
errortempflag=0
errorfanflag=0
subloopflag=0
fi
done
while [ $errorfanflag -eq 1 ]
do
echo -n "Provide fan speed for "$curtemp"$temperature (30-100) then [ENTER]:"
curfan=$( get_number )
if [ $curfan -ge 30 ]
then
errorfanflag=0
elif [ $curfan -gt 100 ]
then
errorfanflag=0
elif [ $curfan -eq -2 ]
then
# Blank
errortempflag=0
errorfanflag=0
subloopflag=0
fi
done
if [ $subloopflag -eq 1 ]
then
if [ $paircounter -eq 0 ]
then
echo "#" > $daemonconfigfile
echo "# Argon Fan Configuration" >> $daemonconfigfile
echo "#" >> $daemonconfigfile
echo "# Min Temp=Fan Speed" >> $daemonconfigfile
fi
displaytemp=$curtemp
paircounter=$((paircounter+1))
if [ "$temperature" == "F" ]
then
# Convert to F to C
curtemp=$((($curtemp-32)*5/9))
fi
echo $curtemp"="$curfan >> $daemonconfigfile
echo "* Fan speed will be set to "$curfan" once $fanmode temperature reaches "$displaytemp"$temperature"
echo
fi
done
echo
if [ $paircounter -gt 0 ]
then
echo "Thank you! We saved "$paircounter" pairs."
sudo systemctl restart argononed.service
echo "Changes should take effect now."
else
echo "Cancelled, no data saved."
fi
fi
done
echo
@@ -1,5 +1,6 @@
#!/bin/bash
if [ -e /boot/firmware/config.txt ] ; then
FIRMWARE=/firmware
else
@@ -24,9 +25,10 @@ then
fi
fi
echo "--------------------------------"
echo "Argon One IR Configuration Tool"
echo "--------------------------------"
echo "-----------------------------"
echo " Argon IR Configuration Tool"
echo "------------------------------"
echo "WARNING: This only supports NEC"
echo " protocol only."
echo -n "Press Y to continue:"
@@ -70,8 +72,8 @@ get_number () {
}
irexecrcfile=/etc/lirc/irexec.lircrc
irexecshfile=/usr/bin/argonirexec
irdecodefile=/usr/bin/argonirdecoder
irexecshfile=/etc/argon/argonirexec
irdecodefile=/etc/argon/argonirdecoder
kodiuserdatafolder="$HOME/.kodi/userdata"
kodilircmapfile="$kodiuserdatafolder/Lircmap.xml"
remotemode=""
@@ -176,9 +178,9 @@ then
fi
elif [ $newmode -eq 2 ]
then
echo "--------------------------------"
echo "Argon One IR Configuration Tool"
echo "--------------------------------"
echo "-----------------------------"
echo " Argon IR Configuration Tool"
echo "-----------------------------"
echo "WARNING: This will install LIRC"
echo " and related libraries."
echo -n "Press Y to agree:"
@@ -320,7 +322,10 @@ then
echo ' <down>KEY_DOWN</down>' | tee -a $kodilircmapfile 1> /dev/null
echo ' <select>KEY_OK</select>' | tee -a $kodilircmapfile 1> /dev/null
echo ' <start>KEY_HOME</start>' | tee -a $kodilircmapfile 1> /dev/null
echo ' <rootmenu>KEY_MENUBACK</rootmenu>' | tee -a $kodilircmapfile 1> /dev/null
# 20240611: User reported mapping is incorrect
#echo ' <rootmenu>KEY_MENUBACK</rootmenu>' | tee -a $kodilircmapfile 1> /dev/null
echo ' <rootmenu>KEY_MENU</rootmenu>' | tee -a $kodilircmapfile 1> /dev/null
echo ' <back>KEY_BACK</back>' | tee -a $kodilircmapfile 1> /dev/null
echo ' <volumeplus>KEY_VOLUMEUP</volumeplus>' | tee -a $kodilircmapfile 1> /dev/null
echo ' <volumeminus>KEY_VOLUMEDOWN</volumeminus>' | tee -a $kodilircmapfile 1> /dev/null
echo ' </remote>' | tee -a $kodilircmapfile 1> /dev/null
@@ -0,0 +1,525 @@
#!/usr/bin/python3
# Standard Headers
import sys
import smbus
# For GPIO
import gpiod
from datetime import datetime
import os
import time
# Check if Lirc Lib is installed
haslirclib = os.path.isfile("/usr/bin/mode2")
if haslirclib == True:
from multiprocessing import Process
#########################
# Use GPIO
def getGPIOPulseData():
# Counter
ctr = 0
# Pin Assignments
LINE_IRRECEIVER=23
try:
try:
# Pi5 mapping
chip = gpiod.Chip('4')
except Exception as gpioerr:
# Old mapping
chip = gpiod.Chip('0')
lineobj = chip.get_line(LINE_IRRECEIVER)
lineobj.request(consumer="argon", type=gpiod.LINE_REQ_EV_BOTH_EDGES)
except Exception as e:
# GPIO Error
return [(-2, -2)]
# Start reading
value = lineobj.get_value()
# mark time
startTime = datetime.now()
pulseTime = startTime
# Pulse Data
pulsedata = []
aborted = False
while aborted == False:
# Wait for transition
try:
while True:
hasevent = lineobj.event_wait(10)
if hasevent:
# Event data needs to be read
eventdata = lineobj.event_read()
break
except Exception as e:
# GPIO Error
lineobj.release()
chip.close()
return [(-2, -2)]
# high/low Length
now = datetime.now()
pulseLength = now - pulseTime
pulseTime = now
# Update value (changed triggered), this also inverts value before saving
if value:
value = 0
else:
value = 1
if pulseLength.microseconds > PULSETAIL_MAXMICROS_NEC and ctr == 0:
continue
pulsedata.append((value, pulseLength.microseconds))
ctr = ctr + 1
if pulseLength.microseconds > PULSETAIL_MAXMICROS_NEC:
break
elif ctr > PULSEDATA_MAXCOUNT:
break
lineobj.release()
chip.close()
# Data is most likely incomplete
if aborted == True:
return []
elif ctr >= PULSEDATA_MAXCOUNT:
print (" * Unable to decode. Please try again *")
return []
return pulsedata
#########################
# Use LIRC
def lircMode2Task(irlogfile):
os.system("mode2 > "+irlogfile+" 2>&1")
def startLIRCMode2Logging(irlogfile):
# create a new process
loggerprocess = Process(target=lircMode2Task,args=(irlogfile,))
loggerprocess.start()
# mode2 will start new process, terminate current
time.sleep(0.1)
loggerprocess.kill()
return True
def endLIRCMode2Logging(irlogfile):
tmplogfile = irlogfile+".tmp"
os.system("ps | grep ode2 > "+tmplogfile+"")
if os.path.exists(tmplogfile) == True:
ctr = 0
fp = open(tmplogfile, "r")
for curline in fp:
if len(curline) > 0:
rowdata = curline.split(" ")
pid = ""
processname = ""
colidx = 0
while colidx < len(rowdata):
if len(rowdata[colidx]) > 0:
if pid == "":
pid = rowdata[colidx]
else:
processname = rowdata[colidx]
colidx = colidx + 1
if processname=="mode2\n":
os.system("kill -9 "+pid)
fp.close()
os.remove(tmplogfile)
return True
def getLIRCPulseData():
if haslirclib == False:
print (" * LIRC Module not found, please reboot and try again *")
return []
irlogfile = "/dev/shm/lircdecoder.log"
loggerresult = startLIRCMode2Logging(irlogfile)
if loggerresult == False:
return [(-1, -1)]
# Wait for log file
logsize = 0
while logsize == 0:
if os.path.exists(irlogfile) == True:
logsize = os.path.getsize(irlogfile)
if logsize == 0:
time.sleep(0.1)
# Wait for data to start
newlogsize = logsize
while logsize == newlogsize:
time.sleep(0.1)
newlogsize = os.path.getsize(irlogfile)
print(" Thank you")
# Wait for data to stop
while logsize != newlogsize:
logsize = newlogsize
time.sleep(0.1)
newlogsize = os.path.getsize(irlogfile)
# Finalize File
loggerresult = endLIRCMode2Logging(irlogfile)
if loggerresult == False:
return [(-1, -1)]
# Decode logfile into Pulse Data
pulsedata = []
terminated = False
if os.path.exists(irlogfile) == True:
ctr = 0
fp = open(irlogfile, "r")
for curline in fp:
if len(curline) > 0:
rowdata = curline.split(" ")
if len(rowdata) == 2:
duration = int(rowdata[1])
value = 0
if rowdata[0] == "pulse":
value = 1
ctr = ctr + 1
if value == 1 or ctr > 1:
if len(pulsedata) > 0 and duration > PULSELEADER_MINMICROS_NEC:
terminated = True
break
else:
pulsedata.append((value, duration))
fp.close()
os.remove(irlogfile)
# Check if terminating pulse detected
if terminated == False:
print (" * Unable to read signal. Please try again *")
return []
return pulsedata
#########################
# Common
irconffile = "/etc/lirc/lircd.conf.d/argon.lircd.conf"
# I2C
address = 0x1a # I2C Address
addressregister = 0xaa # I2C Address Register
# Constants
PULSETIMEOUTMS = 1000
VERIFYTARGET = 3
PULSEDATA_MAXCOUNT = 200 # Fail safe
# NEC Protocol Constants
PULSEBIT_MAXMICROS_NEC = 2500
PULSEBIT_ZEROMICROS_NEC = 1000
PULSELEADER_MINMICROS_NEC = 8000
PULSELEADER_MAXMICROS_NEC = 10000
PULSETAIL_MAXMICROS_NEC = 12000
# Flags
FLAGV1ONLY = False
try:
if os.path.isfile("/etc/argon/flag_v1"):
FLAGV1ONLY = True
except Exception:
FLAGV1ONLY = False
# Standard Methods
def getbytestring(pulsedata):
outstring = ""
for curbyte in pulsedata:
tmpstr = hex(curbyte)[2:]
while len(tmpstr) < 2:
tmpstr = "0" + tmpstr
outstring = outstring+tmpstr
return outstring
def displaybyte(pulsedata):
print (getbytestring(pulsedata))
def pulse2byteNEC(pulsedata):
outdata = []
bitdata = 1
curbyte = 0
bitcount = 0
for (mode, duration) in pulsedata:
if mode == 1:
continue
elif duration > PULSEBIT_MAXMICROS_NEC:
continue
elif duration > PULSEBIT_ZEROMICROS_NEC:
curbyte = curbyte*2 + 1
else:
curbyte = curbyte*2
bitcount = bitcount + 1
if bitcount == 8:
outdata.append(curbyte)
curbyte = 0
bitcount = 0
# Shouldn't happen, but just in case
if bitcount > 0:
outdata.append(curbyte)
return outdata
def bytecompare(a, b):
idx = 0
maxidx = len(a)
if maxidx != len(b):
return 1
while idx < maxidx:
if a[idx] != b[idx]:
return 1
idx = idx + 1
return 0
# Main Flow
mode = "custom"
if len(sys.argv) > 1:
mode = sys.argv[1]
powerdata = []
buttonlist = ['POWER', 'UP', 'DOWN', 'LEFT', 'RIGHT',
'VOLUMEUP', 'VOLUMEDOWN', 'OK', 'HOME', 'MENU'
'BACK']
ircodelist = ['00ff39c6', '00ff53ac', '00ff4bb4', '00ff9966', '00ff837c',
'00ff01fe', '00ff817e', '00ff738c', '00ffd32c', '00ffb946',
'00ff09f6']
buttonidx = 0
if mode == "power":
buttonlist = ['POWER']
ircodelist = ['']
elif mode == "resetpower":
# Just Set the power so it won't create/update the conf file
buttonlist = ['POWER']
mode = "default"
elif mode == "custom":
buttonlist = ['POWER', 'UP', 'DOWN', 'LEFT', 'RIGHT',
'VOLUMEUP', 'VOLUMEDOWN', 'OK', 'HOME', 'MENU'
'BACK']
ircodelist = ['', '', '', '', '',
'', '', '', '', '',
'']
#buttonlist = ['POWER', 'VOLUMEUP', 'VOLUMEDOWN']
#ircodelist = ['', '', '']
if mode == "default":
# To skip the decoding loop
buttonidx = len(buttonlist)
# Set MCU IR code
powerdata = [0x00, 0xff, 0x39, 0xc6]
else:
print ("************************************************")
print ("* WARNING: Current buttons are still active. *")
print ("* Please temporarily assign to a *")
print ("* different button if you plan to *")
print ("* reuse buttons. *")
print ("* e.g. Power Button triggers shutdown *")
print ("* *")
print ("* PROCEED AT YOUR OWN RISK *")
print ("* (Press CTRL+C to abort at any time) *")
print ("************************************************")
readaborted = False
# decoding loop
while buttonidx < len(buttonlist):
print ("Press your button for "+buttonlist[buttonidx]+" (CTRL+C to abort)")
irprotocol = ""
outdata = []
verifycount = 0
readongoing = True
# Handles NEC protocol Only
while readongoing == True:
# Try GPIO-based reading, if it fails, fallback to LIRC
pulsedata = getGPIOPulseData()
if len(pulsedata) == 1:
if pulsedata[0][0] == -2:
pulsedata = getLIRCPulseData()
# Aborted
if len(pulsedata) == 1:
if pulsedata[0][0] == -1:
readongoing = False
readaborted = True
buttonidx = len(buttonlist)
break
# Ignore repeat code (NEC)
if len(pulsedata) <= 4:
continue
# Get leading signal
(mode, duration) = pulsedata[0]
# Decode IR Protocols
# https://www.sbprojects.net/knowledge/ir/index.php
if duration >= PULSELEADER_MINMICROS_NEC and duration <= PULSELEADER_MAXMICROS_NEC:
irprotocol = "NEC"
# NEC has 9ms head, +/- 1ms
curdata = pulse2byteNEC(pulsedata)
if len(curdata) > 0:
if verifycount > 0:
if bytecompare(outdata, curdata) == 0:
verifycount = verifycount + 1
else:
verifycount = 0
else:
outdata = curdata
verifycount = 1
if verifycount >= VERIFYTARGET:
readongoing = False
print ("")
elif verifycount == 0:
print (" * IR code mismatch, please try again *")
elif VERIFYTARGET - verifycount > 1:
print (" Press the button "+ str(VERIFYTARGET - verifycount)+ " more times")
else:
print (" Press the button 1 more time")
else:
print (" * Decoding error. Please try again *")
else:
print (" * Unrecognized signal. Please try again *")
#curdata = pulse2byteLSB(pulsedata)
#displaybyte(curdata)
# Check for duplicates
newircode = getbytestring(outdata)
if verifycount > 0:
checkidx = 0
while checkidx < buttonidx and checkidx < len(buttonlist):
if ircodelist[checkidx] == newircode:
print (" Button already assigned. Please try again")
verifycount = 0
break
checkidx = checkidx + 1
# Store code, and power button code if applicable
if verifycount > 0:
if buttonidx == 0:
powerdata = outdata
if buttonidx < len(buttonlist):
# Abort will cause out of bounds
ircodelist[buttonidx] = newircode
#print (buttonlist[buttonidx]+": "+ newircode)
buttonidx = buttonidx + 1
if len(powerdata) > 0 and readaborted == False:
# Send to device if completed or reset mode
#print("Writing " + getbytestring(powerdata))
print("Updating Device...")
try:
bus=smbus.SMBus(1)
except Exception:
try:
# Older version
bus=smbus.SMBus(0)
except Exception:
bus=None
if bus is None:
print("Device Update Failed: Unable to detect i2c")
else:
# Check for Argon Control Register Support
checkircodewrite = False
argoncyclereg = 0x80
if FLAGV1ONLY == False:
oldval = bus.read_byte_data(address, argoncyclereg)
newval = oldval + 1
if newval >= 100:
newval = 98
bus.write_byte_data(address,argoncyclereg, newval)
time.sleep(1)
newval = bus.read_byte_data(address, argoncyclereg)
if newval != oldval:
addressregister = 0x82
checkircodewrite = True
bus.write_byte_data(address,argoncyclereg, oldval)
bus.write_i2c_block_data(address, addressregister, powerdata)
if checkircodewrite == True:
# Check if data was written for devices that support it
print("Verifying ...")
time.sleep(2)
checkircodedata = bus.read_i2c_block_data(address, addressregister, 4)
checkircodecounter = 0
while checkircodecounter < 4:
# Reuse readaborted flag as indicator if IR code was successfully updated
if checkircodedata[checkircodecounter] != powerdata[checkircodecounter]:
readaborted = True
checkircodecounter = checkircodecounter + 1
if readaborted == False:
print("Device Update Successful")
else:
print("Verification Failed")
bus.close()
# Update IR Conf if there are other button
if buttonidx > 1 and readaborted == False:
print("Updating Remote Control Codes...")
fp = open(irconffile, "w")
# Standard NEC conf header
fp.write("#\n")
fp.write("# Based on NEC templates at http://lirc.sourceforge.net/remotes/nec/\n")
fp.write("# Configured codes based on data gathered\n")
fp.write("#\n")
fp.write("\n")
fp.write("begin remote\n")
fp.write(" name argon\n")
fp.write(" bits 32\n")
fp.write(" flags SPACE_ENC\n")
fp.write(" eps 20\n")
fp.write(" aeps 200\n")
fp.write("\n")
fp.write(" header 8800 4400\n")
fp.write(" one 550 1650\n")
fp.write(" zero 550 550\n")
fp.write(" ptrail 550\n")
fp.write(" repeat 8800 2200\n")
fp.write(" gap 38500\n")
fp.write(" toggle_bit 0\n")
fp.write("\n")
fp.write(" frequency 38000\n")
fp.write("\n")
fp.write(" begin codes\n")
# Write Key Codes
buttonidx = 1
while buttonidx < len(buttonlist):
fp.write(" KEY_"+buttonlist[buttonidx]+" 0x"+ircodelist[buttonidx]+"\n")
buttonidx = buttonidx + 1
fp.write(" end codes\n")
fp.write("end remote\n")
fp.close()
+294
View File
@@ -0,0 +1,294 @@
#!/bin/bash
oledconfigfile=/etc/argoneonoled.conf
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
}
get_pagename() {
if [ "$1" == "clock" ]
then
pagename="Current Date/Time"
elif [ "$1" == "cpu" ]
then
pagename="CPU Utilization"
elif [ "$1" == "storage" ]
then
pagename="Storage Utilization"
elif [ "$1" == "ram" ]
then
pagename="Available RAM"
elif [ "$1" == "temp" ]
then
pagename="CPU Temperature"
elif [ "$1" == "ip" ]
then
pagename="IP Address"
elif [ "$1" == "logo1v5" ]
then
pagename="Logo:One v5"
else
pagename="Invalid"
fi
}
configure_pagelist () {
pagemasterlist="logo1v5 clock cpu storage ram temp ip"
newscreenlist="$1"
pageloopflag=1
while [ $pageloopflag -eq 1 ]
do
echo "--------------------------------"
echo " OLED Pages "
echo "--------------------------------"
i=1
for curpage in $newscreenlist
do
get_pagename $curpage
echo " $i. Remove $pagename"
i=$((i+1))
done
if [ $i -eq 1 ]
then
echo " No page configured"
fi
echo
echo " $i. Add Page"
echo
echo " 0. Done"
echo -n "Enter Number (0-$i):"
cmdmode=$( get_number )
if [ $cmdmode -eq 0 ]
then
pageloopflag=0
elif [[ $cmdmode -eq $i ]]
then
echo "--------------------------------"
echo " Choose Page to Add"
echo "--------------------------------"
echo
i=1
for curpage in $pagemasterlist
do
get_pagename $curpage
echo " $i. $pagename"
i=$((i+1))
done
echo
echo " 0. Cancel"
echo -n "Enter Number (0-$i):"
pagenum=$( get_number )
if [[ $pagenum -ge 1 && $pagenum -le $i ]]
then
i=1
for curpage in $pagemasterlist
do
if [ $i -eq $pagenum ]
then
if [ "$newscreenlist" == "" ]
then
newscreenlist="$curpage"
else
newscreenlist="$newscreenlist $curpage"
fi
fi
i=$((i+1))
done
fi
elif [[ $cmdmode -ge 1 && $cmdmode -lt $i ]]
then
tmpscreenlist=""
i=1
for curpage in $newscreenlist
do
if [ ! $i -eq $cmdmode ]
then
tmpscreenlist="$tmpscreenlist $curpage"
fi
i=$((i+1))
done
if [ "$tmpscreenlist" == "" ]
then
newscreenlist="$tmpscreenlist"
else
# Remove leading space
newscreenlist="${tmpscreenlist:1}"
fi
fi
done
}
saveconfig () {
echo "#" > $oledconfigfile
echo "# Argon OLED Configuration" >> $oledconfigfile
echo "#" >> $oledconfigfile
echo "enabled=$1" >> $oledconfigfile
echo "switchduration=$2" >> $oledconfigfile
echo "screensaver=$3" >> $oledconfigfile
echo "screenlist=\"$4\"" >> $oledconfigfile
}
updateconfig=1
oledloopflag=1
while [ $oledloopflag -eq 1 ]
do
if [ $updateconfig -eq 1 ]
then
. $oledconfigfile
fi
updateconfig=0
if [ -z "$enabled" ]
then
enabled="Y"
updateconfig=1
fi
if [ -z "$screenlist" ]
then
screenlist="ip cpu ram"
updateconfig=1
fi
if [ -z "$screensaver" ]
then
screensaver=120
updateconfig=1
fi
if [ -z "$switchduration" ]
then
switchduration=0
updateconfig=1
fi
# Write default values to config file, daemon already uses default so no need to restart service
if [ $updateconfig -eq 1 ]
then
saveconfig $enabled $switchduration $screensaver "$screenlist"
updateconfig=0
fi
displaystring=": Manually"
if [ $switchduration -gt 1 ]
then
displaystring="Every $switchduration secs"
fi
echo "-----------------------------"
echo "Argon OLED Configuration Tool"
echo "-----------------------------"
echo "Choose from the list:"
echo " 1. Switch Page $displaystring"
echo " 2. Configure Pages"
echo " 3. Turn OFF OLED Screen when unchanged after $screensaver secs"
echo " 4. Enable OLED Pages: $enabled"
echo
echo " 0. Back"
echo -n "Enter Number (0-3):"
newmode=$( get_number )
if [ $newmode -eq 0 ]
then
oledloopflag=0
elif [ $newmode -eq 1 ]
then
echo
echo -n "Enter # of Seconds (10-60, Manual if 0):"
cmdmode=$( get_number )
if [ $cmdmode -eq 0 ]
then
switchduration=0
updateconfig=1
elif [[ $cmdmode -ge 10 && $cmdmode -le 60 ]]
then
updateconfig=1
switchduration=$cmdmode
else
echo
echo "Invalid duration"
echo
fi
elif [ $newmode -eq 3 ]
then
echo
echo -n "Enter # of Seconds (60 or above, Manual if 0):"
cmdmode=$( get_number )
if [ $cmdmode -eq 0 ]
then
screensaver=0
updateconfig=1
elif [ $cmdmode -ge 60 ]
then
updateconfig=1
screensaver=$cmdmode
else
echo
echo "Invalid duration"
echo
fi
elif [ $newmode -eq 2 ]
then
configure_pagelist "$screenlist"
if [ ! "$screenlist" == "$newscreenlist" ]
then
screenlist="$newscreenlist"
updateconfig=1
fi
elif [ $newmode -eq 4 ]
then
echo
echo -n "Enable OLED Pages (Y/n)?:"
read -n 1 confirm
tmpenabled="$enabled"
if [[ "$confirm" == "n" || "$confirm" == "N" ]]
then
tmpenabled="N"
elif [[ "$confirm" == "y" || "$confirm" == "Y" ]]
then
tmpenabled="Y"
else
echo "Invalid response"
fi
if [ ! "$enabled" == "$tmpenabled" ]
then
enabled="$tmpenabled"
updateconfig=1
fi
fi
if [ $updateconfig -eq 1 ]
then
saveconfig $enabled $switchduration $screensaver "$screenlist"
sudo systemctl restart argononed.service
fi
done
echo
+316
View File
@@ -0,0 +1,316 @@
#!/bin/bash
if [ -e /boot/firmware/config.txt ] ; then
FIRMWARE=/firmware
else
FIRMWARE=
fi
CONFIG=/boot${FIRMWARE}/config.txt
CHECKGPIOMODE="libgpiod" # gpiod or rpigpio
# Check if Raspbian, Ubuntu, others
CHECKPLATFORM="Others"
CHECKPLATFORMVERSION=""
CHECKPLATFORMVERSIONNUM=""
if [ -f "/etc/os-release" ]
then
source /etc/os-release
if [ "$ID" = "raspbian" ]
then
CHECKPLATFORM="Raspbian"
CHECKPLATFORMVERSION=$VERSION_ID
elif [ "$ID" = "debian" ]
then
# For backwards compatibility, continue using raspbian
CHECKPLATFORM="Raspbian"
CHECKPLATFORMVERSION=$VERSION_ID
elif [ "$ID" = "ubuntu" ]
then
CHECKPLATFORM="Ubuntu"
CHECKPLATFORMVERSION=$VERSION_ID
fi
echo ${CHECKPLATFORMVERSION} | grep -e "\." > /dev/null
if [ $? -eq 0 ]
then
CHECKPLATFORMVERSIONNUM=`cut -d "." -f2 <<< $CHECKPLATFORMVERSION `
CHECKPLATFORMVERSION=`cut -d "." -f1 <<< $CHECKPLATFORMVERSION `
fi
fi
pythonbin=/usr/bin/python3
# Files
ARGONDOWNLOADSERVER=https://download.argon40.com
INSTALLATIONFOLDER=/etc/argon
basename="argononeups"
daemonname=$basename"d"
daemonupsservice=/lib/systemd/system/$daemonname.service
upsdaemonscript=$INSTALLATIONFOLDER/$daemonname.py
rtcdaemonname="argonupsrtcd"
daemonrtcservice=/lib/systemd/system/$rtcdaemonname.service
rtcdaemonscript=$INSTALLATIONFOLDER/$rtcdaemonname.py
requireinstall=0
newmode=0
if [ ! -z "$1" ]
then
requireinstall=1
newmode=3 # installation
fi
echo "-----------------------------------"
echo " Argon Industria UPS Configuration"
echo "-----------------------------------"
if [ ! -f "$upsdaemonscript" ]
then
echo "Install Argon Industria UPS Tools"
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
requireinstall=1
newmode=3 # Reinstall
fi
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
}
UPSCMDFILE="/dev/shm/upscmd.txt"
UPSSTATUSFILE="/dev/shm/upslog.txt"
rtcconfigscript=$INSTALLATIONFOLDER/argonups-rtcconfig.sh
if [ -f "$UPSSTATUSFILE" ] && [ -f "$rtcconfigscript" ]
then
# cat $UPSSTATUSFILE
sudo $pythonbin $rtcdaemonscript GETBATTERY
fi
loopflag=1
while [ $loopflag -eq 1 ]
do
if [ $requireinstall -eq 0 ]
then
echo
echo "Select option:"
echo " 1. UPS Battery Status"
echo " 2. Configure RTC and/or Schedule"
echo " 3. Reinstall UPS Tools"
echo " 4. Uninstall UPS Tools"
echo ""
echo " 0. Back"
echo -n "Enter Number (0-4):"
newmode=$( get_number )
fi
if [[ $newmode -ge 0 && $newmode -le 4 ]]
then
if [ $newmode -eq 1 ]
then
sudo $pythonbin $rtcdaemonscript GETBATTERY
#if [ -f "$UPSSTATUSFILE" ]
#then
# cat $UPSSTATUSFILE
#else
# echo "Unable to retrieve status"
#fi
elif [ $newmode -eq 2 ]
then
$rtcconfigscript "argonupsrtc"
#TMPTIMESTR=`date +"%Y%d%m%H%M%S"`
#TMPDATASTR=`date +"%Y %m %d %H %M %S"`
#echo "$TMPTIMESTR" > $UPSCMDFILE
#echo "3 $TMPDATASTR" >> $UPSCMDFILE
elif [ $newmode -eq 3 ]
then
# Start installation
if [ ! -d "$INSTALLATIONFOLDER/ups" ]
then
sudo mkdir $INSTALLATIONFOLDER/ups
fi
rtcconfigfile=/etc/argonupsrtc.conf
# Generate default RTC config file if non-existent
if [ ! -f $rtcconfigfile ]; then
sudo touch $rtcconfigfile
sudo chmod 666 $rtcconfigfile
echo '#' >> $rtcconfigfile
echo '# Argon RTC Configuration' >> $rtcconfigfile
echo '#' >> $rtcconfigfile
fi
for iconfile in battery_0 battery_2 battery_4 battery_charging battery_unknown battery_1 battery_3 battery_alert battery_plug
do
sudo wget $ARGONDOWNLOADSERVER/ups/${iconfile}.png -O $INSTALLATIONFOLDER/ups/${iconfile}.png --quiet
done
sudo wget $ARGONDOWNLOADSERVER/ups/upsimg.tar.gz -O $INSTALLATIONFOLDER/ups/upsimg.tar.gz --quiet
sudo tar xfz $INSTALLATIONFOLDER/ups/upsimg.tar.gz -C $INSTALLATIONFOLDER/ups/
sudo rm -Rf $INSTALLATIONFOLDER/ups/upsimg.tar.gz
# Desktop Icon
destfoldername=$USERNAME
if [ -z "$destfoldername" ]
then
destfoldername=$USER
fi
if [ -z "$destfoldername" ]
then
destfoldername="pi"
fi
shortcutfile="/home/$destfoldername/Desktop/argonone-ups.desktop"
if [ -d "/home/$destfoldername/Desktop" ]
then
terminalcmd="lxterminal --working-directory=/home/$destfoldername/ -t"
if [ -f "/home/$destfoldername/.twisteros.twid" ]
then
terminalcmd="xfce4-terminal --default-working-directory=/home/$destfoldername/ -T"
fi
echo "[Desktop Entry]" > $shortcutfile
echo "Name=Argon UPS" >> $shortcutfile
echo "Comment=Argon UPS" >> $shortcutfile
echo "Icon=/etc/argon/ups/loading_0.png" >> $shortcutfile
echo 'Exec='$terminalcmd' "Argon UPS" -e "'$rtcconfigscript' argonupsrtc"' >> $shortcutfile
echo "Type=Application" >> $shortcutfile
echo "Encoding=UTF-8" >> $shortcutfile
echo "Terminal=false" >> $shortcutfile
echo "Categories=None;" >> $shortcutfile
chmod 755 $shortcutfile
fi
# Stopped using default battery indicator
## Build Kernel Module
#sourcecodefolder=$INSTALLATIONFOLDER/tmp
#buildfolder=$sourcecodefolder/build
#if [ -d $sourcecodefolder ]
#then
# sudo rm -rf $sourcecodefolder
#fi
#if [ "$CHECKPLATFORM" = "Ubuntu" ]
#then
# sudo apt-get install build-essential
#fi
#sudo mkdir -p $buildfolder
#sudo chmod -R 755 $buildfolder
#FILELIST="COPYING Makefile argonbatteryicon.c"
#for fname in $FILELIST
#do
# sudo wget $ARGONDOWNLOADSERVER/modules/argonbatteryicon/$fname -O $buildfolder/#$fname --quiet
#done
## Start Build
#cd $buildfolder/
#sudo make
#sudo cp "$buildfolder/argonbatteryicon.ko" "$INSTALLATIONFOLDER/ups/"
## Cleanup
#cd $INSTALLATIONFOLDER/
#sudo rm -Rf "$sourcecodefolder"
sudo wget $ARGONDOWNLOADSERVER/scripts/argononeupsd.py -O "$upsdaemonscript" --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argononeupsd.service -O "$daemonupsservice" --quiet
sudo chmod 666 $daemonupsservice
#echo "User=$destfoldername" >> "$daemonupsservice"
#echo "Group=$destfoldername" >> "$daemonupsservice"
sudo chmod 644 $daemonupsservice
sudo wget $ARGONDOWNLOADSERVER/scripts/argoneon-rtcconfig.sh -O $rtcconfigscript --quiet
sudo chmod 755 $rtcconfigscript
sudo wget $ARGONDOWNLOADSERVER/scripts/argonrtc.py -O $INSTALLATIONFOLDER/argonrtc.py --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argonupsrtcd.py -O "$rtcdaemonscript" --quiet
sudo wget $ARGONDOWNLOADSERVER/scripts/argonupsrtcd.service -O "$daemonrtcservice" --quiet
sudo chmod 644 $daemonrtcservice
if [ $requireinstall -eq 1 ]
then
requireinstall=0
sudo systemctl enable "$daemonname.service"
sudo systemctl start "$daemonname.service"
sudo systemctl enable "$rtcdaemonname.service"
sudo systemctl start "$rtcdaemonname.service"
else
sudo systemctl restart "$daemonname.service"
sudo systemctl restart "$rtcdaemonname.service"
loopflag=0
fi
if [ ! -z "$1" ]
then
# Called from setup script
loopflag=0
fi
elif [ $newmode -eq 4 ]
then
sudo systemctl stop "$daemonname.service"
sudo systemctl disable "$daemonname.service"
sudo rm $daemonupsservice
sudo rm $upsdaemonscript
sudo systemctl stop "$rtcdaemonname.service"
sudo systemctl disable "$rtcdaemonname.service"
sudo rm $daemonrtcservice
sudo rm $rtcdaemonscript
sudo rm -R -f $INSTALLATIONFOLDER/ups
echo "Uninstall Completed"
loopflag=0
else
echo "Cancelled"
loopflag=0
fi
fi
done
+619
View File
@@ -0,0 +1,619 @@
#!/usr/bin/python3
#
# This script set fan speed and monitor power button events.
#
# Fan Speed is set by sending 0 to 100 to the MCU (Micro Controller Unit)
# The values will be interpreted as the percentage of fan speed, 100% being maximum
#
# Power button events are sent as a pulse signal to BCM Pin 4 (BOARD P7)
# A pulse width of 20-30ms indicates reboot request (double-tap)
# A pulse width of 40-50ms indicates shutdown request (hold and release after 3 secs)
#
# Additional comments are found in each function below
#
# Standard Deployment/Triggers:
# * Raspbian, OSMC: Runs as service via /lib/systemd/system/argononed.service
# * lakka, libreelec: Runs as service via /storage/.config/system.d/argononed.service
# * recalbox: Runs as service via /etc/init.d/
#
import sys
import os
import time
from threading import Thread
from queue import Queue
sys.path.append("/etc/argon/")
from argonsysinfo import *
from argonregister import *
from argonpowerbutton import *
# Initialize I2C Bus
bus = argonregister_initializebusobj()
OLED_ENABLED=False
if os.path.exists("/etc/argon/argoneonoled.py"):
import datetime
from argoneonoled import *
OLED_ENABLED=True
OLED_CONFIGFILE = "/etc/argoneonoled.conf"
UNIT_CONFIGFILE = "/etc/argonunits.conf"
SHUTDOWN_FLAGFILE = "/dev/shm/argonshutdownflag.txt"
# This function converts the corresponding fanspeed for the given temperature
# The configuration data is a list of strings in the form "<temperature>=<speed>"
def get_fanspeed(tempval, configlist):
for curconfig in configlist:
curpair = curconfig.split("=")
tempcfg = float(curpair[0])
fancfg = int(float(curpair[1]))
if tempval >= tempcfg:
if fancfg < 1:
return 0
elif fancfg < 25:
return 25
return fancfg
return 0
# This function retrieves the fanspeed configuration list from a file, arranged by temperature
# It ignores lines beginning with "#" and checks if the line is a valid temperature-speed pair
# The temperature values are formatted to uniform length, so the lines can be sorted properly
def load_config(fname):
newconfig = []
try:
with open(fname, "r") as fp:
for curline in fp:
if not curline:
continue
tmpline = curline.strip()
if not tmpline:
continue
if tmpline[0] == "#":
continue
tmppair = tmpline.split("=")
if len(tmppair) != 2:
continue
tempval = 0
fanval = 0
try:
tempval = float(tmppair[0])
if tempval < 0 or tempval > 100:
continue
except:
continue
try:
fanval = int(float(tmppair[1]))
if fanval < 0 or fanval > 100:
continue
except:
continue
newconfig.append( "{:5.1f}={}".format(tempval,fanval))
if len(newconfig) > 0:
newconfig.sort(reverse=True)
except:
return []
return newconfig
# Load OLED Config file
def load_oledconfig(fname):
output={}
screenduration=-1
screenlist=[]
try:
with open(fname, "r") as fp:
for curline in fp:
if not curline:
continue
tmpline = curline.strip()
if not tmpline:
continue
if tmpline[0] == "#":
continue
tmppair = tmpline.split("=")
if len(tmppair) != 2:
continue
if tmppair[0] == "switchduration":
output['screenduration']=int(tmppair[1])
elif tmppair[0] == "screensaver":
output['screensaver']=int(tmppair[1])
elif tmppair[0] == "screenlist":
output['screenlist']=tmppair[1].replace("\"", "").split(" ")
elif tmppair[0] == "enabled":
output['enabled']=tmppair[1].replace("\"", "")
except:
return {}
return output
# Load Unit Config file
def load_unitconfig(fname):
output={"temperature": "C"}
try:
with open(fname, "r") as fp:
for curline in fp:
if not curline:
continue
tmpline = curline.strip()
if not tmpline:
continue
if tmpline[0] == "#":
continue
tmppair = tmpline.split("=")
if len(tmppair) != 2:
continue
if tmppair[0] == "temperature":
output['temperature']=tmppair[1].replace("\"", "")
except:
return {}
return output
def load_fancpuconfig():
fanconfig = ["65=100", "60=55", "55=30"]
tmpconfig = load_config("/etc/argononed.conf")
if len(tmpconfig) > 0:
fanconfig = tmpconfig
return fanconfig
def load_fanhddconfig():
fanhddconfig = ["50=100", "40=55", "30=30"]
fanhddconfigfile = "/etc/argononed-hdd.conf"
if os.path.isfile(fanhddconfigfile):
tmpconfig = load_config(fanhddconfigfile)
if len(tmpconfig) > 0:
fanhddconfig = tmpconfig
else:
fanhddconfig = []
return fanhddconfig
# This function is the thread that monitors temperature and sets the fan speed
# The value is fed to get_fanspeed to get the new fan speed
# To prevent unnecessary fluctuations, lowering fan speed is delayed by 30 seconds
#
# Location of config file varies based on OS
#
def temp_check():
INITIALSPEEDVAL = 200 # ensures fan speed gets set during initialization (e.g. change settings)
argonregsupport = argonregister_checksupport(bus)
fanconfig = load_fancpuconfig()
fanhddconfig = load_fanhddconfig()
prevspeed=INITIALSPEEDVAL
while True:
# Speed based on CPU Temp
val = argonsysinfo_getcputemp()
newspeed = get_fanspeed(val, fanconfig)
# Speed based on HDD Temp
val = argonsysinfo_getmaxhddtemp()
tmpspeed = get_fanspeed(val, fanhddconfig)
# Use faster fan speed
if tmpspeed > newspeed:
newspeed = tmpspeed
if prevspeed == newspeed:
time.sleep(30)
continue
elif newspeed < prevspeed and prevspeed != INITIALSPEEDVAL:
# Pause 30s before speed reduction to prevent fluctuations
time.sleep(30)
prevspeed = newspeed
try:
if newspeed > 0:
# Spin up to prevent issues on older units
argonregister_setfanspeed(bus, 100, argonregsupport)
# Set fan speed has sleep
argonregister_setfanspeed(bus, newspeed, argonregsupport)
time.sleep(30)
except IOError:
time.sleep(60)
#
# This function is the thread that updates OLED
#
def display_loop(readq):
weekdaynamelist = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
monthlist = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]
oledscreenwidth = oled_getmaxX()
fontwdSml = 6 # Maps to 6x8
fontwdReg = 8 # Maps to 8x16
stdleftoffset = 54
temperature="C"
tmpconfig=load_unitconfig(UNIT_CONFIGFILE)
if "temperature" in tmpconfig:
temperature = tmpconfig["temperature"]
screensavermode = False
screensaversec = 120
screensaverctr = 0
screenenabled = ["clock", "ip"]
prevscreen = ""
curscreen = ""
screenid = 0
screenjogtime = 0
screenjogflag = 0 # start with screenid 0
cpuusagelist = []
curlist = []
tmpconfig=load_oledconfig(OLED_CONFIGFILE)
if "screensaver" in tmpconfig:
screensaversec = tmpconfig["screensaver"]
if "screenduration" in tmpconfig:
screenjogtime = tmpconfig["screenduration"]
if "screenlist" in tmpconfig:
screenenabled = tmpconfig["screenlist"]
if "enabled" in tmpconfig:
if tmpconfig["enabled"] == "N":
screenenabled = []
while len(screenenabled) > 0:
try:
if os.path.isfile(SHUTDOWN_FLAGFILE):
display_defaultimg()
return
except:
pass
if len(curlist) == 0 and screenjogflag == 1:
# Reset Screen Saver
screensavermode = False
screensaverctr = 0
# Update screen info
screenid = screenid + screenjogflag
if screenid >= len(screenenabled):
screenid = 0
prevscreen = curscreen
curscreen = screenenabled[screenid]
if screenjogtime == 0:
# Resets jogflag (if switched manually)
screenjogflag = 0
else:
screenjogflag = 1
needsUpdate = False
if curscreen == "cpu":
# CPU Usage
if len(curlist) == 0:
try:
if len(cpuusagelist) == 0:
cpuusagelist = argonsysinfo_listcpuusage()
curlist = cpuusagelist
except:
curlist = []
if len(curlist) > 0:
oled_loadbg("bgcpu")
# Display List
yoffset = 0
tmpmax = 4
while tmpmax > 0 and len(curlist) > 0:
curline = ""
tmpitem = curlist.pop(0)
curline = tmpitem["title"]+": "+str(tmpitem["value"])+"%"
oled_writetext(curline, stdleftoffset, yoffset, fontwdSml)
oled_drawfilledrectangle(stdleftoffset, yoffset+12, int((oledscreenwidth-stdleftoffset-4)*tmpitem["value"]/100), 2)
tmpmax = tmpmax - 1
yoffset = yoffset + 16
needsUpdate = True
else:
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "storage":
# Storage Info
if len(curlist) == 0:
try:
tmpobj = argonsysinfo_listhddusage()
for curdev in tmpobj:
curlist.append({"title": curdev, "value": argonsysinfo_kbstr(tmpobj[curdev]['total']), "usage": int(100*tmpobj[curdev]['used']/tmpobj[curdev]['total']) })
#curlist = argonsysinfo_liststoragetotal()
except:
curlist = []
if len(curlist) > 0:
oled_loadbg("bgstorage")
yoffset = 16
tmpmax = 3
while tmpmax > 0 and len(curlist) > 0:
tmpitem = curlist.pop(0)
# Right column first, safer to overwrite white space
oled_writetextaligned(tmpitem["value"], 77, yoffset, oledscreenwidth-77, 2, fontwdSml)
oled_writetextaligned(str(tmpitem["usage"])+"%", 50, yoffset, 74-50, 2, fontwdSml)
tmpname = tmpitem["title"]
if len(tmpname) > 8:
tmpname = tmpname[0:8]
oled_writetext(tmpname, 0, yoffset, fontwdSml)
tmpmax = tmpmax - 1
yoffset = yoffset + 16
needsUpdate = True
else:
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "raid":
# Raid Info
if len(curlist) == 0:
try:
tmpobj = argonsysinfo_listraid()
curlist = tmpobj['raidlist']
except:
curlist = []
if len(curlist) > 0:
oled_loadbg("bgraid")
tmpitem = curlist.pop(0)
oled_writetextaligned(tmpitem["title"], 0, 0, stdleftoffset, 1, fontwdSml)
oled_writetextaligned(tmpitem["value"], 0, 8, stdleftoffset, 1, fontwdSml)
oled_writetextaligned(argonsysinfo_kbstr(tmpitem["info"]["size"]), 0, 56, stdleftoffset, 1, fontwdSml)
if len(tmpitem['info']['state']) > 0:
oled_writetext( tmpitem['info']['state'], stdleftoffset, 8, fontwdSml )
if len(tmpitem['info']['rebuildstat']) > 0:
oled_writetext("Rebuild:" + tmpitem['info']['rebuildstat'], stdleftoffset, 16, fontwdSml)
# TODO: May need to use different method for each raid type (i.e. check raidlist['raidlist'][raidctr]['value'])
#oled_writetext("Used:"+str(int(100*tmpitem["info"]["used"]/tmpitem["info"]["size"]))+"%", stdleftoffset, 24, fontwdSml)
oled_writetext("Active:"+str(int(tmpitem["info"]["active"]))+"/"+str(int(tmpitem["info"]["devices"])), stdleftoffset, 32, fontwdSml)
oled_writetext("Working:"+str(int(tmpitem["info"]["working"]))+"/"+str(int(tmpitem["info"]["devices"])), stdleftoffset, 40, fontwdSml)
oled_writetext("Failed:"+str(int(tmpitem["info"]["failed"]))+"/"+str(int(tmpitem["info"]["devices"])), stdleftoffset, 48, fontwdSml)
needsUpdate = True
else:
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "ram":
# RAM
try:
oled_loadbg("bgram")
tmpraminfo = argonsysinfo_getram()
oled_writetextaligned(tmpraminfo[0], stdleftoffset, 8, oledscreenwidth-stdleftoffset, 1, fontwdReg)
oled_writetextaligned("of", stdleftoffset, 24, oledscreenwidth-stdleftoffset, 1, fontwdReg)
oled_writetextaligned(tmpraminfo[1], stdleftoffset, 40, oledscreenwidth-stdleftoffset, 1, fontwdReg)
needsUpdate = True
except:
needsUpdate = False
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "temp":
# Temp
try:
oled_loadbg("bgtemp")
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:
oled_writetext(curdev.upper()+": "+ tmpstr+ chr(167) +temperature, stdleftoffset, displayrow, fontwdSml)
else:
oled_writetext(curdev.upper()+":", stdleftoffset, displayrow, fontwdSml)
oled_writetext(" "+ tmpstr+ chr(167) +temperature, stdleftoffset, displayrow+displayrowht, fontwdSml)
displayrow = displayrow + displayrowht*2
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]
oled_writetextaligned(tmpstr+ chr(167) +temperature, stdleftoffset, 24, oledscreenwidth-stdleftoffset, 1, fontwdReg)
# Temperature Bar: 40C is min, 80C is max
maxht = 21
barht = int(maxht*(maxcval-40)/40)
if barht > maxht:
barht = maxht
elif barht < 1:
barht = 1
oled_drawfilledrectangle(24, 20+(maxht-barht), 3, barht, 2)
needsUpdate = True
except:
needsUpdate = False
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "ip":
# IP Address
try:
oled_loadbg("bgip")
oled_writetextaligned(argonsysinfo_getip(), 0, 8, oledscreenwidth, 1, fontwdReg)
needsUpdate = True
except:
needsUpdate = False
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "logo1v5":
# Logo
try:
oled_loadbg("logo1v5")
needsUpdate = True
except:
needsUpdate = False
# Next page due to error/no data
screenjogflag = 1
else:
try:
oled_loadbg("bgtime")
# Date and Time HH:MM
curtime = datetime.datetime.now()
# Month/Day
outstr = str(curtime.day).strip()
if len(outstr) < 2:
outstr = " "+outstr
outstr = monthlist[curtime.month-1]+outstr
oled_writetextaligned(outstr, stdleftoffset, 8, oledscreenwidth-stdleftoffset, 1, fontwdReg)
# Day of Week
oled_writetextaligned(weekdaynamelist[curtime.weekday()], stdleftoffset, 24, oledscreenwidth-stdleftoffset, 1, fontwdReg)
# Time
outstr = str(curtime.minute).strip()
if len(outstr) < 2:
outstr = "0"+outstr
outstr = str(curtime.hour)+":"+outstr
if len(outstr) < 5:
outstr = "0"+outstr
oled_writetextaligned(outstr, stdleftoffset, 40, oledscreenwidth-stdleftoffset, 1, fontwdReg)
needsUpdate = True
except:
needsUpdate = False
# Next page due to error/no data
screenjogflag = 1
if needsUpdate == True:
if screensavermode == False:
# Update screen if not screen saver mode
oled_power(True)
oled_flushimage(prevscreen != curscreen)
oled_reset()
timeoutcounter = 0
while timeoutcounter<screenjogtime or screenjogtime == 0:
qdata = ""
if readq.empty() == False:
qdata = readq.get()
if qdata == "OLEDSWITCH":
# Trigger screen switch
screenjogflag = 1
# Reset Screen Saver
screensavermode = False
screensaverctr = 0
readq.task_done()
break
elif qdata == "OLEDSTOP":
# End OLED Thread
display_defaultimg()
readq.task_done()
return
else:
screensaverctr = screensaverctr + 1
if screensaversec <= screensaverctr and screensavermode == False:
screensavermode = True
oled_fill(0)
oled_reset()
oled_power(False)
if timeoutcounter == 0:
# Use 1 sec sleep get CPU usage
cpuusagelist = argonsysinfo_listcpuusage(1)
else:
time.sleep(1)
timeoutcounter = timeoutcounter + 1
if timeoutcounter >= 60 and screensavermode == False:
# Refresh data every minute, unless screensaver got triggered
screenjogflag = 0
break
display_defaultimg()
def display_defaultimg():
# Load default image
#oled_power(True)
#oled_loadbg("bgdefault")
#oled_flushimage()
oled_fill(0)
oled_reset()
if len(sys.argv) > 1:
cmd = sys.argv[1].upper()
if cmd == "SHUTDOWN":
try:
with open(SHUTDOWN_FLAGFILE, "w") as f:
f.write("signalled")
except:
pass
# Signal poweroff
argonregister_signalpoweroff(bus)
if OLED_ENABLED == True:
display_defaultimg()
elif cmd == "FANOFF":
# Turn off fan
argonregister_setfanspeed(bus,0)
if OLED_ENABLED == True:
display_defaultimg()
elif cmd == "SERVICE":
# Starts the power button and temperature monitor threads
try:
ipcq = Queue()
if len(sys.argv) > 2:
cmd = sys.argv[2].upper()
if cmd == "OLEDSWITCH":
t1 = Thread(target = argonpowerbutton_monitorswitch, args =(ipcq, ))
else:
t1 = Thread(target = argonpowerbutton_monitor, args =(ipcq, ))
t2 = Thread(target = temp_check)
if OLED_ENABLED == True:
t3 = Thread(target = display_loop, args =(ipcq, ))
t1.start()
t2.start()
if OLED_ENABLED == True:
t3.start()
ipcq.join()
except Exception:
sys.exit(1)
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Argon One Fan and Button Service
After=multi-user.target
[Service]
Type=simple
Restart=always
RemainAfterExit=true
ExecStart=/usr/bin/python3 /etc/argon/argononed.py SERVICE
[Install]
WantedBy=multi-user.target
+333
View File
@@ -0,0 +1,333 @@
#!/usr/bin/python3
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
from PIL import Image
import sys
import datetime
import math
import os
import time
# Initialize I2C Bus
import smbus
oledport=1
try:
bus=smbus.SMBus(1)
except Exception:
try:
oledport=0
# Older version
bus=smbus.SMBus(0)
except Exception:
print("Unable to detect i2c")
bus=None
ADDR_OLED=0x3c
OLED_WD=1
OLED_HT=1
oled_device=None
try:
oled_device=ssd1306(i2c(port=oledport, address=ADDR_OLED))
OLED_WD=oled_device.bounding_box[2]+1
OLED_HT=oled_device.bounding_box[3]+1
except Exception:
print("Unable to initialize OLED")
bus=None
OLED_NUMFONTCHAR=256
OLED_BUFFERIZE = ((OLED_WD*OLED_HT)>>3)
oled_imagebuffer = [0] * OLED_BUFFERIZE
def oled_getmaxY():
return OLED_HT
def oled_getmaxX():
return OLED_WD
def oled_loadbg(bgname):
if bgname == "bgblack":
oled_clearbuffer()
return
elif bgname == "bgwhite":
oled_clearbuffer(1)
return
try:
file = open("/etc/argon/oled/"+bgname+".bin", "rb")
bgbytes = list(file.read())
file.close()
ctr = len(bgbytes)
if ctr == OLED_BUFFERIZE:
oled_imagebuffer[:] = bgbytes
elif ctr > OLED_BUFFERIZE:
oled_imagebuffer[:] = bgbytes[0:OLED_BUFFERIZE]
else:
oled_imagebuffer[0:ctr] = bgbytes
# Clear the rest of the buffer
while ctr < OLED_BUFFERIZE:
oled_imagebuffer[ctr] = 0
ctr=ctr+1
except FileNotFoundError:
oled_clearbuffer()
def oled_clearbuffer(value = 0):
if value != 0:
value = 0xff
ctr = 0
while ctr < OLED_BUFFERIZE:
oled_imagebuffer[ctr] = value
ctr=ctr+1
def oled_writebyterow(x,y,bytevalue, mode = 0):
bufferoffset = OLED_WD*(y>>3) + x
if mode == 0:
oled_imagebuffer[bufferoffset] = bytevalue
elif mode == 1:
oled_imagebuffer[bufferoffset] = bytevalue^oled_imagebuffer[bufferoffset]
else:
oled_imagebuffer[bufferoffset] = bytevalue|oled_imagebuffer[bufferoffset]
def oled_writebuffer(x,y,value, mode = 0):
yoffset = y>>3
yshift = y&0x7
ybit = (1<<yshift)
ymask = 0xFF^ybit
if value != 0:
value = ybit
bufferoffset = OLED_WD*yoffset + x
curval = oled_imagebuffer[bufferoffset]
if mode & 1:
oled_imagebuffer[bufferoffset] = curval^value
else:
oled_imagebuffer[bufferoffset] = curval&ymask|value
def oled_fill(value):
oled_clearbuffer(value)
oled_flushimage()
def oled_flushimage(hidescreen = True):
if bus is None:
return
if hidescreen == True:
# Reset/Hide screen
oled_power(False)
tmplist = [0]*OLED_BUFFERIZE
# Each byte = 1 col x 8 rows
ymask = 0
yidx = 0
xmask = 0
xidx = 0
outbyte = 0
srcidx = 0
outoffsetidx = 0
outyoffset = 0
xoffset = 0
while srcidx < OLED_BUFFERIZE:
# OLED_WDx8 pixels at a time, y in reverse bit order
outyoffset = 0
yidx = 0
ymask = 1
while yidx < 8:
outoffsetidx = 0
outbyte = 0
xmask = 0x80
xidx = 0
xoffset = 0
while xoffset < OLED_WD:
if oled_imagebuffer[srcidx+xoffset] & ymask:
outbyte = outbyte | xmask
xmask = xmask >> 1
xidx = xidx + 1
if xidx >= 8:
tmplist[srcidx+outoffsetidx + outyoffset] = outbyte
xmask = 0x80
xidx = 0
outbyte = 0
outoffsetidx = outoffsetidx + 1
xoffset = xoffset + 1
outyoffset = outyoffset + (OLED_WD>>3)
yidx = yidx + 1
ymask = ymask << 1
srcidx = srcidx + OLED_WD
oled_device.display(Image.frombytes("1", [OLED_WD, OLED_HT], bytes(tmplist)))
if hidescreen == True:
# Display
oled_power(True)
def oled_drawfilledrectangle(x, y, wd, ht, mode = 0):
ymax = y + ht
cury = y&0xF8
xmax = x + wd
curx = x
if ((y & 0x7)) != 0:
yshift = y&0x7
bytevalue = (0xFF<<yshift)&0xFF
# If 8 no additional masking needed
if ymax-cury < 8:
yshift = 8-((ymax-cury)&0x7)
bytevalue = bytevalue & (0xFF>>yshift)
while curx < xmax:
oled_writebyterow(curx,cury,bytevalue, mode)
curx = curx + 1
cury = cury + 8
# Draw 8 rows at a time when possible
while cury + 8 < ymax:
curx = x
while curx < xmax:
oled_writebyterow(curx,cury,0xFF, mode)
curx = curx + 1
cury = cury + 8
if cury < ymax:
yshift = 8-((ymax-cury)&0x7)
bytevalue = (0xFF>>yshift)
curx = x
while curx < xmax:
oled_writebyterow(curx,cury,bytevalue, mode)
curx = curx + 1
def oled_writetextaligned(textdata, x, y, boxwidth, alignmode, charwd = 6, mode = 0):
leftoffset = 0
if alignmode == 1:
# Centered
leftoffset = (boxwidth-len(textdata)*charwd)>>1
elif alignmode == 2:
# Right aligned
leftoffset = (boxwidth-len(textdata)*charwd)
oled_writetext(textdata, x+leftoffset, y, charwd, mode)
def oled_writetext(textdata, x, y, charwd = 6, mode = 0):
if charwd < 6:
charwd = 6
charht = int((charwd<<3)/6)
if charht & 0x7:
charht = (charht&0xF8) + 8
try:
file = open("/etc/argon/oled/font"+str(charht)+"x"+str(charwd)+".bin", "rb")
fontbytes = list(file.read())
file.close()
except FileNotFoundError:
try:
# Default to smallest
file = open("/etc/argon/oled/font8x6.bin", "rb")
fontbytes = list(file.read())
file.close()
except FileNotFoundError:
return
if ((y & 0x7)) == 0:
# Use optimized loading
oled_fastwritetext(textdata, x, y, charht, charwd, fontbytes, mode)
return
numfontrow = charht>>3
ctr = 0
while ctr < len(textdata):
fontoffset = ord(textdata[ctr])*charwd
fontcol = 0
while fontcol < charwd and x < OLED_WD:
fontrow = 0
row = y
while fontrow < numfontrow and row < OLED_HT and x >= 0:
curbit = 0x80
curbyte = (fontbytes[fontoffset + fontcol + (OLED_NUMFONTCHAR*charwd*fontrow)])
subrow = 0
while subrow < 8 and row < OLED_HT:
value = 0
if (curbyte&curbit) != 0:
value = 1
oled_writebuffer(x,row,value, mode)
curbit = curbit >> 1
row = row + 1
subrow = subrow + 1
fontrow = fontrow + 1
fontcol = fontcol + 1
x = x + 1
ctr = ctr + 1
def oled_fastwritetext(textdata, x, y, charht, charwd, fontbytes, mode = 0):
numfontrow = charht>>3
ctr = 0
while ctr < len(textdata):
fontoffset = ord(textdata[ctr])*charwd
fontcol = 0
while fontcol < charwd and x < OLED_WD:
fontrow = 0
row = y&0xF8
while fontrow < numfontrow and row < OLED_HT and x >= 0:
curbyte = (fontbytes[fontoffset + fontcol + (OLED_NUMFONTCHAR*charwd*fontrow)])
oled_writebyterow(x,row,curbyte, mode)
fontrow = fontrow + 1
row = row + 8
fontcol = fontcol + 1
x = x + 1
ctr = ctr + 1
return
def oled_power(turnon = True):
if bus is None:
return
try:
if turnon == True:
oled_device.show()
else:
oled_device.hide()
except:
return
def oled_inverse(enable = True):
# Not supported?
return
def oled_fullwhite(enable = True):
# Not supported?
return
def oled_reset():
return
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Argon One Fan and Button Service
After=multi-user.target
[Service]
Type=simple
Restart=always
RemainAfterExit=true
ExecStart=/usr/bin/python3 /etc/argon/argononed.py SERVICE OLEDSWITCH
[Install]
WantedBy=multi-user.target
+114
View File
@@ -0,0 +1,114 @@
#!/bin/bash
tmpfile="/dev/shm/argontmpconf.txt"
daemonconfigfile="/etc/argononeupd.conf"
if [ -f "$daemonconfigfile" ]
then
. $daemonconfigfile
fi
if [ -z "$lidshutdownsecs" ]
then
lidshutdownsecs=0
fi
mainloopflag=1
newmode=0
get_number () {
read curnumber
if [ -z "$curnumber" ]
then
echo "-2"
return
elif [[ $curnumber =~ ^[+-]?[0-9]+$ ]]
then
if [ $curnumber -lt 0 ]
then
echo "-1"
return
fi
echo $curnumber
return
fi
echo "-1"
return
}
while [ $mainloopflag -eq 1 ]
do
lidshutdownmins=$((lidshutdownsecs / 60))
echo "------------------------------------------"
echo " Argon One Up Lid Configuration Tool"
echo "------------------------------------------"
echo
echo "Lid Close Behavior:"
if [ $lidshutdownsecs -lt 1 ]
then
echo "(Do Nothing)"
else
echo "(Shut down after $lidshutdownmins minute(s))"
fi
echo " 1. Do Nothing"
echo " 2. Shutdown"
echo
echo " 0. Exit"
echo "NOTE: You can also edit $daemonconfigfile directly"
echo -n "Enter Number (0-2):"
newmode=$( get_number )
if [[ $newmode -eq 0 ]]
then
mainloopflag=0
elif [ $newmode -eq 1 ]
then
lidshutdownsecs=0
elif [ $newmode -eq 2 ]
then
maxmins=120
echo "Please provide number of minutes until shutdown:"
echo -n "Enter Number (1-$maxmins):"
curval=$( get_number )
if [ $curval -gt $maxmins ]
then
newmode=0
echo "Invalid input"
elif [ $curval -lt 1 ]
then
newmode=0
echo "Invalid input"
else
lidshutdownsecs=$((curval * 60))
fi
fi
if [ $newmode -eq 1 ] || [ $newmode -eq 2 ]
then
if [ -f "$daemonconfigfile" ]
then
grep -v 'lidshutdownsecs' "$daemonconfigfile" > $tmpfile
else
echo '#' > $tmpfile
echo '# Argon One Up Configuration' >> $tmpfile
echo '#' >> $tmpfile
fi
echo '# lidshutdownsecs number of seconds till shutdown when lid is closed 0 if do nothing' >> $tmpfile
echo "lidshutdownsecs=$lidshutdownsecs" >> $tmpfile
sudo cp $tmpfile $daemonconfigfile
sudo chmod 666 $daemonconfigfile
echo "Configuration updated."
fi
done
echo
+474
View File
@@ -0,0 +1,474 @@
#!/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,0xFF,0xB0,0x73,0x00,0x00,0x00,0x64,0x08,0xD3,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFA]
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 -1
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)
return 0
except Exception as e:
try:
debuglog("battery-chargingerror", str(e))
except:
debuglog("battery-chargingerror", "Read Charging Failed")
return -1
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 = ""
shutdowntriggered=False
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 < 0 or tmp_battery < 0:
# communication error, retain old value
tmp_charging = device_charging
tmp_battery = device_battery
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_battery>99:
# Prevents switching issue
statusstr = "Charged"
curnotifymsg = statusstr
tmpiconfile = tmpiconfile+"charge_"+str(device_battery)
elif device_charging == 0:
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"
elif device_battery > 5:
#curnotifymsg="Low Battery"
curnotifymsg="Low Battery: The device may power off automatically soon."
curnotifycritical=True
else:
curnotifymsg="CRITICAL BATTERY: Shutting Down in 1 minute"
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)
if shutdowntriggered==False and device_battery <= 5 and device_charging != 0:
shutdowntriggered=True
os.system("shutdown +1 """+curnotifymsg+".""")
debuglog("battery-shutdown", "Shutdown in 1 minute")
if shutdowntriggered==True and (device_charging == 0 or device_battery>=10):
shutdowntriggered=False
os.system("shutdown -c ""Charging, shutdown cancelled.""")
debuglog("battery-shutdown", "Abort")
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)
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Argon ONE UP Service
After=multi-user.target
[Service]
Type=simple
Restart=always
RemainAfterExit=true
ExecStart=/usr/bin/python3 /etc/argon/argononeupd.py SERVICE
[Install]
WantedBy=multi-user.target
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Argon ONE UP Service
After=multi-user.target
[Service]
Type=simple
Restart=always
RemainAfterExit=true
ExecStart=/usr/bin/python3 /etc/argon/argonkeyboard.py SERVICE
[Install]
WantedBy=default.target
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/python3
import time
import os
UPS_LOGFILE="/dev/shm/upslog.txt"
#UPS_DEVFILE="/dev/argonbatteryicon"
def notifymessage(message, iscritical):
wftype="notify"
if iscritical:
wftype="critical"
os.system("export SUDO_UID=1000; wfpanelctl "+wftype+" \""+message+"\"")
os.system("export DISPLAY=:0.0; lxpanelctl notify \""+message+"\"")
try:
outobj = {}
#os.system("insmod /etc/argon/ups/argonbatteryicon.ko")
prevnotifymsg=""
tmp_battery = 100
tmp_charging = 1
device_battery = -1
device_charging = -1
while True:
try:
# 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("%",""))
except:
tmp_charging = device_charging
tmp_battery = device_battery
# Update module data if changed
if tmp_charging != device_charging or tmp_battery!=device_battery:
device_charging = tmp_charging
device_battery = tmp_battery
# No longer using default battery indicator
#try:
# with open(UPS_DEVFILE, 'w') as f:
# f.write("capacity = "+str(device_battery)+"\ncharging = "+str(device_charging)+"\n")
#except Exception as e:
# pass
curnotifymsg = ""
curnotifycritical=False
if tmp_charging:
if "Shutting Down" in prevnotifymsg:
os.system("shutdown -c ""Charging, shutdown cancelled.""")
if tmp_battery > 99:
curnotifymsg="Fully Charged"
elif tmp_charging:
curnotifymsg="Charging"
else:
if tmp_battery > 50:
curnotifymsg="Battery Mode"
elif tmp_battery > 20:
curnotifymsg="50%% Battery"
elif tmp_battery > 10:
curnotifymsg="20%% Battery"
elif tmp_battery > 5:
#curnotifymsg="Low Battery"
curnotifymsg="Low Battery: The device may power off automatically soon."
curnotifycritical=True
else:
curnotifymsg="CRITICAL BATTERY: Shutting Down in 1 minute"
curnotifycritical=True
if prevnotifymsg != curnotifymsg:
notifymessage(curnotifymsg, curnotifycritical)
if tmp_battery <= 5 and tmp_charging == 0:
os.system("shutdown +1 """+curnotifymsg+".""")
prevnotifymsg = curnotifymsg
except OSError:
pass
time.sleep(60)
except:
pass
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=Argon Industria UPS Battery Service
DefaultDependencies=no
[Install]
WantedBy=sysinit.target
[Service]
Type=simple
KillSignal=SIGINT
TimeoutStopSec=8
Restart=on-failure
WorkingDirectory=/etc/argon/ups/
ExecStart=/usr/bin/python3 /etc/argon/argononeupsd.py
+206
View File
@@ -0,0 +1,206 @@
# For Libreelec/Lakka, note that we need to add system paths
# import sys
# sys.path.append('/storage/.kodi/addons/virtual.rpi-tools/lib')
import gpiod
import os
import time
# Debug Logger
def argonpowerbutton_debuglog(typestr, logstr):
try:
DEBUGFILE="/dev/shm/argononegpiodebuglog.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
def argonpowerbutton_getvalue(lineobj,lineid):
if lineid is not None:
tmpval = lineobj.get_value(lineid) != gpiod.line.Value.INACTIVE
if tmpval == False:
return 0
return 1
return lineobj.get_value()
def argonpowerbutton_watchline(debugname, dataq, lineid, callback):
monitormode = True
argonpowerbutton_debuglog(debugname, "Starting")
# Pi5 mapping, 0 for older
chippath = '/dev/gpiochip4'
try:
chip = gpiod.Chip(chippath)
except Exception as gpioerr:
try:
# Old mapping
chippath = '/dev/gpiochip0'
chip = gpiod.Chip(chippath)
except Exception as gpioolderr:
chippath = ""
if len(chippath) == 0:
argonpowerbutton_debuglog(debugname+"-error", "Unable to initialize GPIO")
try:
dataq.put("ERROR")
except:
pass
return
# Monitoring starts
try:
try:
# Reference https://github.com/brgl/libgpiod/blob/master/bindings/python/examples/gpiomon.py
lineobj = chip.get_line(lineid)
if lineid == 27:
lineobj.request(consumer="argon", type=gpiod.LINE_REQ_EV_BOTH_EDGES, flags=gpiod.LINE_REQ_FLAG_BIAS_PULL_UP)
else:
lineobj.request(consumer="argon", type=gpiod.LINE_REQ_EV_BOTH_EDGES)
while monitormode == True:
hasevent = lineobj.event_wait(10)
if hasevent:
eventdata = lineobj.event_read()
monitormode = callback(eventdata.type == gpiod.LineEvent.RISING_EDGE, lineobj, dataq, None)
lineobj.release()
chip.close()
except Exception:
# https://github.com/brgl/libgpiod/blob/master/bindings/python/examples/watch_line_rising.py
configobj = {lineid: gpiod.LineSettings(direction=gpiod.line.Direction.INPUT, edge_detection=gpiod.line.Edge.BOTH)}
if lineid == 27:
configobj = {lineid: gpiod.LineSettings(direction=gpiod.line.Direction.INPUT, edge_detection=gpiod.line.Edge.BOTH, bias=gpiod.line.Bias.PULL_UP )}
with gpiod.request_lines(
chippath,
consumer="argon",
config=configobj,
) as request:
while monitormode:
# Blocks until at least one event is available
for event in request.read_edge_events():
monitormode = callback(event.event_type == event.Type.RISING_EDGE, request, dataq, event.line_offset)
except Exception as monitorerror:
try:
argonpowerbutton_debuglog(debugname+"-error", str(monitorerror))
except:
argonpowerbutton_debuglog(debugname+"-error", "Error aborting")
try:
dataq.put("ERROR")
except:
pass
# This function is the thread that monitors activity in our shutdown pin
# The pulse width is measured, and the corresponding shell command will be issued
def argonpowerbutton_getconfigval(keyname, datatype="int"):
keyname = keyname.lower()
fname = "/etc/argononeupd.conf"
try:
with open(fname, "r") as fp:
for curline in fp:
if not curline:
continue
tmpline = curline.replace(" ", "").replace("\t", "")
if not tmpline:
continue
if tmpline[0] == "#":
continue
tmppair = tmpline.split("=")
if len(tmppair) != 2:
continue
tmpvar = tmppair[0].lower()
if tmpvar != keyname:
continue
try:
if datatype == "int":
return int(tmppair[1])
elif datatype == "float":
return float(tmppair[1])
return tmppair[1]
except:
continue
except:
pass
if datatype == "int":
return -1
elif datatype == "float":
return -1
return ""
def argonpowerbutton_monitorlidevent(isrising, lineobj, writeq, lineid):
if isrising == False:
targetsecs = argonpowerbutton_getconfigval("lidshutdownsecs")
if targetsecs > 0:
argonpowerbutton_debuglog("lid-monitor", "Close Detect; Wait for :"+str(targetsecs))
else:
argonpowerbutton_debuglog("lid-monitor", "Close Detected; Do nothing")
# Time pulse data
time.sleep(1)
pulsetimesec = 1
# 0 - Lid is closed, 1 - Lid is open
while argonpowerbutton_getvalue(lineobj, lineid) == 0:
if targetsecs > 0:
if pulsetimesec >= targetsecs:
argonpowerbutton_debuglog("lid-monitor", "Target Reached, shutting down")
monitormode = False
os.system("shutdown now -h")
return False
time.sleep(1)
pulsetimesec += 1
argonpowerbutton_debuglog("lid-monitor", "Open Detected")
return True
def argonpowerbutton_monitorlid(writeq):
LINE_LIDMONITOR=27
argonpowerbutton_watchline("lid-monitor", writeq, LINE_LIDMONITOR, argonpowerbutton_monitorlidevent)
def argonpowerbutton_monitorevent(isrising, lineobj, writeq, lineid):
pulsetime = 0
if isrising == True:
# Time pulse data
while argonpowerbutton_getvalue(lineobj, lineid) == 1:
time.sleep(0.01)
pulsetime += 1
if pulsetime >=2 and pulsetime <=3:
# Testing
#writeq.put("OLEDSWITCH")
writeq.put("OLEDSTOP")
os.system("reboot")
return False
elif pulsetime >=4 and pulsetime <=5:
writeq.put("OLEDSTOP")
os.system("shutdown now -h")
return False
elif pulsetime >=6 and pulsetime <=7:
writeq.put("OLEDSWITCH")
return True
def argonpowerbutton_monitor(writeq):
LINE_SHUTDOWN=4
argonpowerbutton_watchline("button", writeq, LINE_SHUTDOWN, argonpowerbutton_monitorevent)
def argonpowerbutton_monitorswitchevent(isrising, lineobj, writeq, lineid):
pulsetime = 0
if isrising == True:
# Time pulse data
while argonpowerbutton_getvalue(lineobj, lineid) == 1:
time.sleep(0.01)
pulsetime += 1
if pulsetime >= 10:
writeq.put("OLEDSWITCH")
return True
def argonpowerbutton_monitorswitch(writeq):
LINE_SHUTDOWN=4
argonpowerbutton_watchline("button-switch", writeq, LINE_SHUTDOWN, argonpowerbutton_monitorswitchevent)
# Testing
#argonpowerbutton_monitor(None)
+156
View File
@@ -0,0 +1,156 @@
# For Libreelec/Lakka, note that we need to add system paths
# import sys
# sys.path.append('/storage/.kodi/addons/virtual.rpi-tools/lib')
import RPi.GPIO as GPIO
import os
import time
# Debug Logger
def argonpowerbutton_debuglog(typestr, logstr):
try:
DEBUGFILE="/dev/shm/argononegpiodebuglog.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
# This function is the thread that monitors activity in our shutdown pin
# The pulse width is measured, and the corresponding shell command will be issued
def argonpowerbutton_getconfigval(keyname, datatype="int"):
keyname = keyname.lower()
fname = "/etc/argononeupd.conf"
try:
with open(fname, "r") as fp:
for curline in fp:
if not curline:
continue
tmpline = curline.replace(" ", "").replace("\t", "")
if not tmpline:
continue
if tmpline[0] == "#":
continue
tmppair = tmpline.split("=")
if len(tmppair) != 2:
continue
tmpvar = tmppair[0].lower()
if tmpvar != keyname:
continue
try:
if datatype == "int":
return int(tmppair[1])
elif datatype == "float":
return float(tmppair[1])
return tmppair[1]
except:
continue
except:
pass
if datatype == "int":
return -1
elif datatype == "float":
return -1
return ""
def argonpowerbutton_monitorlid(writeq):
try:
argonpowerbutton_debuglog("lid-monitor", "Starting")
monitormode = True
# 0 - Lid is closed, 1 - Lid is open
# Pin Assignments
PIN_LIDMONITOR=27
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN_LIDMONITOR, GPIO.IN, pull_up_down=GPIO.PUD_UP)
while monitormode == True:
pulsetimesec = 1
GPIO.wait_for_edge(PIN_LIDMONITOR, GPIO.FALLING)
targetsecs = argonpowerbutton_getconfigval("lidshutdownsecs")
if targetsecs > 0:
argonpowerbutton_debuglog("lid-monitor", "Close Detect; Wait for :"+str(targetsecs))
else:
argonpowerbutton_debuglog("lid-monitor", "Close Detected; Do nothing")
# Time pulse data
time.sleep(1)
while GPIO.input(PIN_LIDMONITOR) == GPIO.LOW:
if targetsecs > 0:
if pulsetimesec >= targetsecs:
argonpowerbutton_debuglog("lid-monitor", "Target Reached, shutting down")
monitormode = False
os.system("shutdown now -h")
break
time.sleep(1)
pulsetimesec += 1
argonpowerbutton_debuglog("lid-monitor", "Open Detected")
except Exception as liderror:
try:
argonpowerbutton_debuglog("lid-monitor-error", str(liderror))
except:
argonpowerbutton_debuglog("lid-monitor-error", "Error aborting")
#pass
GPIO.cleanup()
def argonpowerbutton_monitor(writeq):
try:
# Pin Assignments
PIN_SHUTDOWN=4
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN_SHUTDOWN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
while True:
pulsetime = 1
GPIO.wait_for_edge(PIN_SHUTDOWN, GPIO.RISING)
time.sleep(0.01)
while GPIO.input(PIN_SHUTDOWN) == GPIO.HIGH:
time.sleep(0.01)
pulsetime += 1
if pulsetime >=2 and pulsetime <=3:
# Testing
#writeq.put("OLEDSWITCH")
writeq.put("OLEDSTOP")
os.system("reboot")
break
elif pulsetime >=4 and pulsetime <=5:
writeq.put("OLEDSTOP")
os.system("shutdown now -h")
break
elif pulsetime >=6 and pulsetime <=7:
writeq.put("OLEDSWITCH")
except Exception:
writeq.put("ERROR")
GPIO.cleanup()
def argonpowerbutton_monitorswitch(writeq):
try:
# Pin Assignments
PIN_SHUTDOWN=4
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN_SHUTDOWN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
while True:
pulsetime = 1
GPIO.wait_for_edge(PIN_SHUTDOWN, GPIO.RISING)
time.sleep(0.01)
while GPIO.input(PIN_SHUTDOWN) == GPIO.HIGH:
time.sleep(0.01)
pulsetime += 1
if pulsetime >= 10:
writeq.put("OLEDSWITCH")
except Exception:
writeq.put("ERROR")
GPIO.cleanup()
+74
View 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)
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/python3
#
# Argon Register Helper methods
#
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):
if busobj is None:
return False
try:
oldval = argonregister_getbyte(busobj, ADDR_ARGONONEREG_DUTYCYCLE)
newval = oldval + 1
if newval >= 100:
newval = 98
argonregister_setbyte(busobj, ADDR_ARGONONEREG_DUTYCYCLE, newval)
newval = argonregister_getbyte(busobj, ADDR_ARGONONEREG_DUTYCYCLE)
if newval != oldval:
argonregister_setbyte(busobj, ADDR_ARGONONEREG_DUTYCYCLE, oldval)
return True
return False
except:
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):
if busobj is None:
return 0
usereg=False
if regsupport is None:
usereg=argonregister_checksupport(busobj)
else:
usereg=regsupport
if usereg == True:
return argonregister_getbyte(busobj, ADDR_ARGONONEREG_DUTYCYCLE)
else:
return 0
def argonregister_setfanspeed(busobj, newspeed, regsupport=None):
if busobj is None:
return
if newspeed > 100:
newspeed = 100
elif newspeed < 0:
newspeed = 0
usereg=False
if regsupport is None:
usereg=argonregister_checksupport(busobj)
else:
usereg=regsupport
if usereg == True:
argonregister_setbyte(busobj, ADDR_ARGONONEREG_DUTYCYCLE, newspeed)
else:
busobj.write_byte(ADDR_ARGONONEFAN,newspeed)
time.sleep(1)
def argonregister_signalpoweroff(busobj):
if busobj is None:
return
if argonregister_checksupport(busobj):
argonregister_setbyte(busobj, ADDR_ARGONONEREG_CTRL, 1)
else:
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)
+642
View File
@@ -0,0 +1,642 @@
#!/usr/bin/python3
import os
import datetime
#########
# Describe Methods
#########
# Helper method to add proper suffix to numbers
def getNumberSuffix(numval):
onesvalue = numval % 10
if onesvalue == 1:
return "st"
elif onesvalue == 2:
return "nd"
elif onesvalue == 3:
return "rd"
return "th"
def describeHourMinute(hour, minute):
if hour < 0:
return ""
outstr = ""
ampmstr = ""
if hour <= 0:
hour = 0
outstr = outstr + "12"
ampmstr = "am"
elif hour <= 12:
outstr = outstr + str(hour)
if hour == 12:
ampmstr = "pm"
else:
ampmstr = "am"
else:
outstr = outstr + str(hour-12)
ampmstr = "pm"
if minute >= 10:
outstr = outstr+":"
elif minute > 0:
outstr = outstr+":0"
else:
if hour == 0:
ampmstr = "mn"
elif hour == 12:
ampmstr = "nn"
return outstr+ampmstr
if minute <= 0:
minute = 0
outstr = outstr+str(minute)
return outstr+ampmstr
# Describe Schedule Parameter Values
def describeSchedule(monthlist, weekdaylist, datelist, hourlist, minutelist):
weekdaynamelist = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
monthnamelist = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
curprefix = ""
hasDate = False
hasMonth = False
foundvalue = False
monthdatestr = ""
for curmonth in monthlist:
for curdate in datelist:
if curdate >= 0:
hasDate = True
if curmonth >= 0:
hasMonth = True
monthdatestr = monthdatestr + "," + monthnamelist[curmonth-1]+" "+str(curdate) + getNumberSuffix(curdate)
else:
monthdatestr = monthdatestr + ","+str(curdate) + getNumberSuffix(curdate)
else:
if curmonth >= 0:
monthdatestr = monthdatestr + "," + monthnamelist[curmonth-1]
if len(monthdatestr) > 0:
foundvalue = True
# Remove Leading Comma
monthdatestr = monthdatestr[1:]
if hasMonth == True:
curprefix = "Annually:"
else:
curprefix = "Monthly:"
monthdatestr = monthdatestr + " of the Month"
monthdatestr = " Every "+monthdatestr
weekdaystr = ""
for curweekday in weekdaylist:
if curweekday >= 0:
hasDate = True
weekdaystr = weekdaystr + "," + weekdaynamelist[curweekday]
if len(weekdaystr) > 0:
foundvalue = True
# Remove Leading Comma
weekdaystr = weekdaystr[1:]
if len(curprefix) == 0:
curprefix = "Weekly:"
weekdaystr = " on " + weekdaystr
else:
weekdaystr = ",on " + weekdaystr
hasHour = False
hasMinute = False
hourminstr = ""
for curhour in hourlist:
for curminute in minutelist:
if curhour >= 0:
hasHour = True
if curminute >= 0:
hasMinute = True
hourminstr = hourminstr + "," + describeHourMinute(curhour, curminute)
elif curminute >= 0:
hasMinute = True
hourminstr = hourminstr + "," + str(curminute) + getNumberSuffix(curminute)
if len(hourminstr) > 0:
foundvalue = True
# Remove Leading Comma
hourminstr = hourminstr[1:]
if hasHour == True:
if hasDate == True:
hourminstr = "at " + hourminstr
else:
hourminstr = "Daily: " + hourminstr
if hasMinute == False:
hourminstr = hourminstr + " every minute"
else:
if hourminstr == "0":
hourminstr = "At the start of every hour"
else:
hourminstr = "Hourly: At " + hourminstr + " minute"
else:
hourminstr = "Every minute"
if len(curprefix) > 0:
hourminstr = ","+hourminstr
return (curprefix + monthdatestr + weekdaystr + hourminstr).strip()
#########
# Alarm
#########
# Alarm to UTC/Local time
def convertAlarmTimezone(weekday, caldate, hour, minute, toutc):
utcdiffsec = getLocaltimeOffset().seconds
if toutc == False:
utcdiffsec = utcdiffsec*(-1)
utcdiffsec = utcdiffsec - (utcdiffsec%60)
utcdiffmin = utcdiffsec % 3600
utcdiffhour = int((utcdiffsec - utcdiffmin)/3600)
utcdiffmin = int(utcdiffmin/60)
addhour = 0
if minute >= 0:
minute = minute - utcdiffmin
if minute < 0:
addhour = -1
minute = minute + 60
elif minute > 59:
addhour = 1
minute = minute - 60
addday = 0
if hour >= 0:
hour = hour - utcdiffhour
tmphour = hour + addhour
if hour < 0:
hour = hour + 24
elif hour > 23:
hour = hour - 24
if tmphour < 0:
addday = -1
elif tmphour > 23:
addday = 1
if addday != 0:
if weekday >= 0:
weekday = weekday + addday
if weekday < 0:
weekday = weekday + 7
elif weekday > 6:
weekday = weekday - 7
if caldate > 0:
# Edge cases might not be handled properly though
curtime = datetime.datetime.now()
maxmonthdate = getLastMonthDate(curtime.year, curtime.month)
caldate = caldate + addday
if caldate == 0:
# move to end of the month
caldate = maxmonthdate
elif caldate > maxmonthdate:
# move to next month
caldate = 1
return [weekday, caldate, hour, minute]
# Get RTC Alarm Setting (Negative values ignored)
def getRTCAlarm(weekday, caldate, hour, minute):
hasError = False
if caldate < 1 and weekday < 0 and hour < 0 and minute < 0:
hasError = True
elif minute > 59:
hasError = True
elif hour > 23:
hasError = True
elif weekday > 6:
hasError = True
elif caldate > 31:
hasError = True
if hasError == True:
return [-1, -1, -1, -1]
# Convert to UTC
return convertAlarmTimezone(weekday, caldate, hour, minute, True)
#########
# Date/Time tools
#########
# Get local time vs UTC
def getLocaltimeOffset():
localdatetime = datetime.datetime.now()
utcdatetime = datetime.datetime.fromtimestamp(localdatetime.timestamp(), datetime.timezone.utc)
# Remove TZ info to allow subtraction
utcdatetime = utcdatetime.replace(tzinfo = None)
return localdatetime - utcdatetime
# Sync Time to RTC Time (for Daemon use)
def updateSystemTime(rtctime):
os.system("date -s '"+rtctime.isoformat()+"' >/dev/null 2>&1")
#########
# Config
#########
# Load config value as array of integers
def getConfigValue(valuestr):
try:
if valuestr == "*":
return [-1]
tmplist = valuestr.split(",")
map_object = map(int, tmplist)
return list(map_object)
except:
return [-1]
# Load config line data as array of Command schedule
def newCommandSchedule(curline):
result = []
linedata = curline.split(" ")
if len(linedata) < 6:
return result
minutelist = getConfigValue(linedata[0])
hourlist = getConfigValue(linedata[1])
datelist = getConfigValue(linedata[2])
#monthlist = getConfigValue(linedata[3])
monthlist = [-1] # Certain edge cases will not be handled properly
weekdaylist = getConfigValue(linedata[4])
cmd = ""
ctr = 5
while ctr < len(linedata):
cmd = cmd + " " + linedata[ctr]
ctr = ctr + 1
cmd = cmd.strip()
for curmin in minutelist:
for curhour in hourlist:
for curdate in datelist:
for curmonth in monthlist:
for curweekday in weekdaylist:
result.append({ "minute": curmin, "hour": curhour, "date": curdate, "month":curmonth, "weekday": curweekday, "cmd":cmd })
return result
# Save updated config file
def saveConfigList(fname, configlist):
f = open(fname, "w")
f.write("#\n")
f.write("# Argon RTC Configuration\n")
f.write("# - Follows cron general format, but with only * and csv support\n")
f.write("# - Each row follows the following format:\n")
f.write("# min hour date month dayOfWeek Command\n")
f.write("# e.g. Shutdown daily at 1am\n")
f.write("# 0 1 * * * off\n")
f.write("# Shutdown daily at 1am and 1pm\n")
f.write("# 0 1,13 * * * off\n")
f.write("# - Commands are currently on or off only\n")
f.write("# - Limititations\n")
f.write("# Requires MINUTE value\n")
f.write("# Month values are ignored (edge cases not supported)\n")
f.write("#\n")
for config in configlist:
f.write(config+"\n")
f.close()
# Remove config line
def removeConfigEntry(fname, entryidx):
configlist = loadConfigList(fname)
if len(configlist) > entryidx:
configlist.pop(entryidx)
saveConfigList(fname, configlist)
# Load config list (removes invalid data)
def loadConfigList(fname):
try:
result = []
with open(fname, "r") as fp:
for curline in fp:
if not curline:
continue
curline = curline.strip().replace('\t', ' ')
# Handle special characters that get encoded
tmpline = "".join([c if 0x20<=ord(c) and ord(c)<=0x7e else "" for c in curline])
if not tmpline:
continue
if tmpline[0] == "#":
continue
checkdata = tmpline.split(" ")
if len(checkdata) > 5:
# Don't include every minute type of schedule
if checkdata[0] != "*":
result.append(tmpline)
return result
except:
return []
# Form Command Schedule list from config list
def formCommandScheduleList(configlist):
try:
result = []
for config in configlist:
result = result + newCommandSchedule(config)
return result
except:
return []
# Describe config list entry
def describeConfigListEntry(configlistitem):
linedata = configlistitem.split(" ")
if len(linedata) < 6:
return ""
minutelist = getConfigValue(linedata[0])
hourlist = getConfigValue(linedata[1])
datelist = getConfigValue(linedata[2])
#monthlist = getConfigValue(linedata[3])
monthlist = [-1] # Certain edge cases will not be handled properly
weekdaylist = getConfigValue(linedata[4])
cmd = ""
ctr = 5
while ctr < len(linedata):
cmd = cmd + " " + linedata[ctr]
ctr = ctr + 1
cmd = cmd.strip().lower()
if cmd == "on":
cmd = "Startup"
else:
cmd = "Shutdown"
return cmd+" | "+describeSchedule(monthlist, weekdaylist, datelist, hourlist, minutelist)
# Describe config list and show indices
def describeConfigList(fname):
# 1 is reserved for New schedule
ctr = 2
configlist = loadConfigList(fname)
for config in configlist:
tmpline = describeConfigListEntry(config)
if len(tmpline) > 0:
print(" "+str(ctr)+". ", tmpline)
ctr = ctr + 1
if ctr == 2:
print(" No Existing Schedules")
# Check Command schedule if it should fire for the give time
def checkDateForCommandSchedule(commandschedule, datetimeobj):
testminute = commandschedule.get("minute", -1)
testhour = commandschedule.get("hour", -1)
testdate = commandschedule.get("date", -1)
testmonth = commandschedule.get("month", -1)
testweekday = commandschedule.get("weekday", -1)
if testminute < 0 or testminute == datetimeobj.minute:
if testhour < 0 or testhour == datetimeobj.hour:
if testdate < 0 or testdate == datetimeobj.day:
if testmonth < 0 or testmonth == datetimeobj.month:
if testweekday < 0:
return True
else:
# python Sunday = 6, RTC Sunday = 0
weekDay = datetimeobj.weekday()
if weekDay == 6:
weekDay = 0
else:
weekDay = weekDay + 1
if testweekday == weekDay:
return True
return False
# Get current command
def getCommandForTime(commandschedulelist, datetimeobj, checkcmd):
ctr = 0
while ctr < len(commandschedulelist):
testcmd = commandschedulelist[ctr].get("cmd", "")
if (testcmd.lower() == checkcmd or len(checkcmd) == 0) and len(testcmd) > 0:
if checkDateForCommandSchedule(commandschedulelist[ctr], datetimeobj) == True:
return testcmd
ctr = ctr + 1
return ""
# Get Last Date of Month
def getLastMonthDate(year, month):
if month < 12:
testtime = datetime.datetime(year, month+1, 1)
else:
testtime = datetime.datetime(year+1, 1, 1)
testtime = testtime - datetime.timedelta(days=1)
return testtime.day
# Increment to the next iteration of command schedule
def incrementCommandScheduleTime(commandschedule, testtime, addmode):
testminute = commandschedule.get("minute", -1)
testhour = commandschedule.get("hour", -1)
testdate = commandschedule.get("date", -1)
testmonth = commandschedule.get("month", -1)
testweekday = commandschedule.get("weekday", -1)
if addmode == "minute":
testfield = commandschedule.get(addmode, -1)
if testfield < 0:
if testtime.minute < 59:
return testtime + datetime.timedelta(minutes=1)
else:
return incrementCommandScheduleTime(commandschedule, testtime.replace(minute=0), "hour")
else:
return incrementCommandScheduleTime(commandschedule, testtime, "hour")
elif addmode == "hour":
testfield = commandschedule.get(addmode, -1)
if testfield < 0:
if testtime.hour < 23:
return testtime + datetime.timedelta(hours=1)
else:
return incrementCommandScheduleTime(commandschedule, testtime.replace(hour=0), "date")
else:
return incrementCommandScheduleTime(commandschedule, testtime, "date")
elif addmode == "date":
testfield = commandschedule.get(addmode, -1)
if testfield < 0:
maxmonthdate = getLastMonthDate(testtime.year, testtime.month)
if testtime.day < maxmonthdate:
return testtime + datetime.timedelta(days=1)
else:
return incrementCommandScheduleTime(commandschedule, testtime.replace(day=1), "month")
else:
return incrementCommandScheduleTime(commandschedule, testtime, "month")
elif addmode == "month":
testfield = commandschedule.get(addmode, -1)
if testfield < 0:
nextmonth = testtime.month
nextyear = testtime.year
while True:
if nextmonth < 12:
nextmonth = nextmonth + 1
else:
nextmonth = 1
nextyear = nextyear + 1
maxmonthdate = getLastMonthDate(nextyear, nextmonth)
if testtime.day <= maxmonthdate:
return testtime.replace(month=nextmonth, year=nextyear)
else:
return incrementCommandScheduleTime(commandschedule, testtime, "year")
else:
# Year
if testtime.month == 2 and testtime.day == 29:
# Leap day handling
nextyear = testtime.year
while True:
nextyear = nextyear + 1
maxmonthdate = getLastMonthDate(nextyear, testtime.month)
if testtime.day <= maxmonthdate:
return testtime.replace(year=nextyear)
else:
return testtime.replace(year=(testtime.year+1))
# Set Next Alarm on RTC
def getNextAlarm(commandschedulelist, prevdatetime):
curtime = datetime.datetime.now()
if prevdatetime > curtime:
return [prevdatetime, -1, -1, -1, -1]
# Divisible by 4 for leap day
checklimityears = 12
foundnextcmd = False
nextcommandschedule = {}
# To be sure it's later than any schedule
nextcommandtime = curtime.replace(year=(curtime.year+checklimityears))
ctr = 0
while ctr < len(commandschedulelist):
testcmd = commandschedulelist[ctr].get("cmd", "").lower()
if testcmd == "on":
invaliddata = False
testminute = commandschedulelist[ctr].get("minute", -1)
testhour = commandschedulelist[ctr].get("hour", -1)
testdate = commandschedulelist[ctr].get("date", -1)
testmonth = commandschedulelist[ctr].get("month", -1)
testweekday = commandschedulelist[ctr].get("weekday", -1)
tmpminute = testminute
tmphour = testhour
tmpdate = testdate
tmpmonth = testmonth
tmpyear = curtime.year
if tmpminute < 0:
tmpminute = curtime.minute
if tmphour < 0:
tmphour = curtime.hour
if tmpdate < 0:
tmpdate = curtime.day
if tmpmonth < 0:
tmpmonth = curtime.month
maxmonthdate = getLastMonthDate(tmpyear, tmpmonth)
if tmpdate > maxmonthdate:
# Invalid month date
if testdate < 0:
tmpdate = maxmonthdate
else:
# Date is fixed
if testminute < 0:
tmpminute = 0
if testhour < 0:
tmphour = 0
if testmonth < 0 and testdate <= 31:
# Look for next valid month
while tmpdate > maxmonthdate:
if tmpmonth < 12:
tmpmonth = tmpmonth + 1
else:
tmpmonth = 1
tmpyear = tmpyear + 1
maxmonthdate = getLastMonthDate(tmpyear, tmpmonth)
elif tmpdate == 29 and tmpmonth == 2:
# Fixed to leap day
while tmpdate > maxmonthdate:
tmpyear = tmpyear + 1
maxmonthdate = getLastMonthDate(tmpyear, tmpmonth)
else:
invaliddata = True
if invaliddata == False:
try:
testtime = datetime.datetime(tmpyear, tmpmonth, tmpdate, tmphour, tmpminute)
except:
# Force time diff
testtime = curtime - datetime.timedelta(hours=1)
tmptimediff = (curtime - testtime).total_seconds()
else:
tmptimediff = 0
if testweekday >= 0:
# Day of Week check
# python Sunday = 6, RTC Sunday = 0
weekDay = testtime.weekday()
if weekDay == 6:
weekDay = 0
else:
weekDay = weekDay + 1
if weekDay != testweekday or tmptimediff > 0:
# Resulting 0-ed time will be <= the testtime
if testminute < 0:
testtime = testtime.replace(minute=0)
if testhour < 0:
testtime = testtime.replace(hour=0)
dayoffset = testweekday-weekDay
if dayoffset < 0:
dayoffset = dayoffset + 7
elif dayoffset == 0:
dayoffset = 7
testtime = testtime + datetime.timedelta(days=dayoffset)
# Just look for the next valid weekday; Can be optimized
while checkDateForCommandSchedule(commandschedulelist[ctr], testtime) == False and (testtime.year - curtime.year) < checklimityears:
testtime = testtime + datetime.timedelta(days=7)
if (testtime.year - curtime.year) >= checklimityears:
# Too many iterations, abort/ignore
tmptimediff = 0
else:
tmptimediff = (curtime - testtime).total_seconds()
if tmptimediff > 0:
# Find next iteration that's greater than the current time (Day of Week check already handled)
while tmptimediff >= 0:
testtime = incrementCommandScheduleTime(commandschedulelist[ctr], testtime, "minute")
tmptimediff = (curtime - testtime).total_seconds()
if nextcommandtime > testtime and tmptimediff < 0:
nextcommandschedule = commandschedulelist[ctr]
nextcommandtime = testtime
foundnextcmd = True
ctr = ctr + 1
if foundnextcmd == True:
# Schedule Alarm
# Assume no date,weekday involved just shift the hour and minute accordingly
paramminute = nextcommandschedule.get("minute", -1)
paramhour = nextcommandschedule.get("hour", -1)
if nextcommandschedule.get("weekday", -1) >=0 or nextcommandschedule.get("date", -1) > 0:
# Set alarm based on hour/minute of next occurrence to factor in timezone changes if any
paramminute = nextcommandtime.minute
paramhour = nextcommandtime.hour
weekday, caldate, hour, minute = getRTCAlarm(nextcommandschedule.get("weekday", -1), nextcommandschedule.get("date", -1), paramhour, paramminute)
return [nextcommandtime, weekday, caldate, hour, minute]
# This will ensure that this will be replaced next iteration
return [curtime, -1, -1, -1, -1]
+172
View File
@@ -0,0 +1,172 @@
#!/usr/bin/python3
import sys
import os
sys.path.append("/etc/argon/")
from argonsysinfo import *
from argonregister import *
from argononed import *
def getFahrenheit(celsiustemp):
try:
return (32+9*(celsiustemp)/5)
except:
return 0
temperature="C"
tmpconfig=load_unitconfig(UNIT_CONFIGFILE)
if "temperature" in tmpconfig:
temperature = tmpconfig["temperature"]
baseleftoffset = ""
stdleftoffset = " "
#if len(sys.argv) > 2:
# baseleftoffset = stdleftoffset
baseleftoffset = stdleftoffset
argctr = 1
while argctr < len(sys.argv):
cmd = sys.argv[argctr].lower()
argctr = argctr + 1
if baseleftoffset != "":
print(cmd.upper(),"INFORMATION:")
if cmd == "cpu usage":
# CPU Usage
curlist = argonsysinfo_listcpuusage()
while len(curlist) > 0:
curline = ""
tmpitem = curlist.pop(0)
curline = tmpitem["title"]+": "+str(tmpitem["value"])+"%"
print(baseleftoffset+curline)
elif cmd == "storage":
# Storage Info
curlist = []
try:
tmpobj = argonsysinfo_listhddusage()
for curdev in tmpobj:
curlist.append({"title": curdev, "value": argonsysinfo_kbstr(tmpobj[curdev]['total']), "usage": int(100*tmpobj[curdev]['used']/tmpobj[curdev]['total']) })
#curlist = argonsysinfo_liststoragetotal()
except Exception:
curlist = []
while len(curlist) > 0:
tmpitem = curlist.pop(0)
# Right column first, safer to overwrite white space
print(baseleftoffset+tmpitem["title"], str(tmpitem["usage"])+"%","used of", tmpitem["value"])
elif cmd == "raid":
# Raid Info
curlist = []
try:
tmpobj = argonsysinfo_listraid()
curlist = tmpobj['raidlist']
except Exception:
curlist = []
if len(curlist) > 0:
tmpitem = curlist.pop(0)
print(baseleftoffset+tmpitem["title"], tmpitem["value"], argonsysinfo_kbstr(tmpitem["info"]["size"]))
if len(tmpitem['info']['state']) > 0:
print(baseleftoffset+stdleftoffset,tmpitem['info']['state'])
if len(tmpitem['info']['rebuildstat']) > 0:
print(baseleftoffset+stdleftoffset,"Rebuild:" + tmpitem['info']['rebuildstat'])
print(baseleftoffset+stdleftoffset,"Active:"+str(int(tmpitem["info"]["active"]))+"/"+str(int(tmpitem["info"]["devices"])))
print(baseleftoffset+stdleftoffset,"Working:"+str(int(tmpitem["info"]["working"]))+"/"+str(int(tmpitem["info"]["devices"])))
print(baseleftoffset+stdleftoffset,"Failed:"+str(int(tmpitem["info"]["failed"]))+"/"+str(int(tmpitem["info"]["devices"])))
else:
print(baseleftoffset+stdleftoffset,"N/A")
elif cmd == "ram":
# RAM
try:
tmpraminfo = argonsysinfo_getram()
print(baseleftoffset+tmpraminfo[0],"of", tmpraminfo[1])
except Exception:
pass
elif cmd == "temperature":
# Temp
try:
hddtempctr = 0
maxcval = 0
mincval = 200
alltempobj = {"cpu": argonsysinfo_getcputemp()}
# Get min/max of hdd temp
hddtempobj = argonsysinfo_gethddtemp()
for curdev in hddtempobj:
alltempobj[curdev] = hddtempobj[curdev]
if hddtempobj[curdev] < mincval:
mincval = hddtempobj[curdev]
if hddtempobj[curdev] > maxcval:
maxcval = hddtempobj[curdev]
hddtempctr = hddtempctr + 1
if hddtempctr > 0:
alltempobj["hdd min"]=mincval
alltempobj["hdd max"]=maxcval
for curdev in alltempobj:
if temperature == "C":
# Celsius
tmpstr = str(alltempobj[curdev])
if len(tmpstr) > 4:
tmpstr = tmpstr[0:4]
else:
# Fahrenheit
tmpstr = str(getFahrenheit(alltempobj[curdev]))
if len(tmpstr) > 5:
tmpstr = tmpstr[0:5]
print(baseleftoffset+curdev.upper()+": "+ tmpstr+ chr(176) +temperature)
except Exception:
pass
elif cmd == "ip":
# IP Address
try:
print(baseleftoffset+argonsysinfo_getip())
except Exception:
pass
elif cmd == "fan speed":
# Fan Speed
try:
newspeed = argonregister_getfanspeed(argonregister_initializebusobj())
if newspeed <= 0:
fanconfig = load_fancpuconfig()
fanhddconfig = load_fanhddconfig()
# Speed based on CPU Temp
val = argonsysinfo_getcputemp()
newspeed = get_fanspeed(val, fanconfig)
val = argonsysinfo_getmaxhddtemp()
tmpspeed = get_fanspeed(val, fanhddconfig)
if tmpspeed > newspeed:
newspeed = tmpspeed
print(baseleftoffset+"Fan Speed",str(newspeed))
except Exception:
pass
elif cmd == "fan configuration":
fanconfig = load_fancpuconfig()
fanhddconfig = load_fanhddconfig()
if len(fanhddconfig) > 0:
print(baseleftoffset+"Fan Temp-Speed cut-offs")
for curconfig in fanconfig:
print(baseleftoffset+stdleftoffset,curconfig)
if len(fanhddconfig) > 0:
print(baseleftoffset+"HDD Temp-Speed cut-offs")
for curconfig in fanhddconfig:
print(baseleftoffset+stdleftoffset,curconfig)
+394
View 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}
+564
View File
@@ -0,0 +1,564 @@
#!/usr/bin/python3
import json
import sys
import datetime
import math
import os
import time
import serial
from threading import Thread
from queue import Queue
sys.path.append("/etc/argon/")
import argonrtc
#################
# Common/Helpers
#################
#UPS_SERIALPORT="/dev/ttyUSB0"
UPS_SERIALPORT="/dev/ttyACM0"
UPS_LOGFILE="/dev/shm/upslog.txt"
UPS_CMDFILE="/dev/shm/upscmd.txt"
RTC_CONFIGFILE = "/etc/argonupsrtc.conf"
#############
# RTC
#############
def hexAsDec(hexval):
return (hexval&0xF) + 10*((hexval>>4)&0xf)
def decAsHex(decval):
return (decval%10) + (math.floor(decval/10)<<4)
# Returns RTC timestamp as datetime object
def getDatetimeObj(dataobj, datakey):
try:
datetimearray = dataobj[datakey].split(" ")
if len(datetimearray)>1:
datearray = datetimearray[0].split("/")
timearray = datetimearray[1].split(":")
if len(datearray) == 3 and len(timearray) > 1:
year = int(datearray[2])
month = int(datearray[0])
caldate = int(datearray[1])
hour = int(timearray[0])
minute = int(timearray[1])
second = 0
if len(timearray) > 2:
second = int(timearray[2])
return datetime.datetime(year, month, caldate, hour, minute, second)+argonrtc.getLocaltimeOffset()
except:
pass
return datetime.datetime(1999, 1, 1, 0, 0, 0)
def getRTCpoweronschedule():
outobj = ups_sendcmd("7")
return getDatetimeObj(outobj, "schedule")
def getRTCdatetime():
outobj = ups_sendcmd("5")
return getDatetimeObj(outobj, "time")
# set RTC time using datetime object (Local time)
def setRTCdatetime():
# Set local time to UTC
outobj = ups_sendcmd("3")
return getDatetimeObj(outobj, "time")
# Set Next Alarm on RTC
def setNextAlarm(commandschedulelist, prevdatetime):
nextcommandtime, weekday, caldate, hour, minute = argonrtc.getNextAlarm(commandschedulelist, prevdatetime)
if prevdatetime >= nextcommandtime:
return prevdatetime
if weekday < 0 and caldate < 0 and hour < 0 and minute < 0:
# No schedule
# nextcommandtime is current time, which will be replaced/checked next iteration
return nextcommandtime
# Convert to RTC timezone
alarmtime = nextcommandtime - argonrtc.getLocaltimeOffset()
outobj = ups_sendcmd("6 "+alarmtime.strftime("%Y %m %d %H %M"))
return getDatetimeObj(outobj, "schedule")
#############
# Status
#############
def ups_debuglog(typestr, logstr):
try:
UPS_DEBUGFILE="/dev/shm/upsdebuglog.txt"
tmpstrpadding = " "
with open(UPS_DEBUGFILE, "a") as txt_file:
txt_file.write("["+datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")+"] "+typestr.upper()+" "+logstr.strip().replace("\n","\n"+tmpstrpadding)+"\n")
except:
pass
def ups_sendcmd(cmdstr):
# status, version, time, schedule
ups_debuglog("sendcmd", cmdstr)
try:
outstr = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
with open(UPS_CMDFILE, "w") as txt_file:
txt_file.write(datetime.datetime.now().strftime("%Y%m%d%H%M%S")+"\n"+cmdstr+"\n")
time.sleep(3)
except:
pass
outobj = ups_loadlogdata()
try:
ups_debuglog("sendcmd-response", json.dumps(outobj))
except:
pass
return outobj
def ups_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 ups_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
ups_debuglog("serial", "Starting "+UPS_SERIALPORT)
updatedesktopicon("Argon UPS", "Argon UPS", "/etc/argon/ups/loading_0.png")
while True: # Outer loop to reconnect to device
qdata = ""
if readq.empty() == False:
qdata = readq.get()
try:
ser = serial.Serial(UPS_SERIALPORT, 115200, timeout = 1)
ser.close()
ser.open()
except Exception as mainerr:
try:
ups_debuglog("serial-mainerror", str(mainerr))
except:
ups_debuglog("serial-mainerror", "Error")
# Give time before retry
time.sleep(10)
continue
previconfile = ""
statusstr = ""
device_battery=0
device_charging=0
device_chargecurrent=-1
device_version=-1
device_rtctime= [-1, -1, -1, -1, -1, -1]
device_powerontime= [-1, -1, -1, -1, -1]
while True: # Command loop
try:
if sendcmdid < 0:
cmddatastr = ""
try:
fp = open(UPS_CMDFILE, "r")
cmdlog = fp.read()
alllines = cmdlog.split("\n")
if len(alllines) > 1:
if lastcmdtime != alllines[0]:
lastcmdtime=alllines[0]
cmddatastr=alllines[1]
tmpcmdarray = cmddatastr.split(" ")
sendcmdid = int(tmpcmdarray[0])
if sendcmdid == 3:
# Get/rebuild time here to minimize delay/time gap
newrtcdatetime = datetime.datetime.now() - argonrtc.getLocaltimeOffset()
cmddatastr = ("3 "+newrtcdatetime.strftime("%Y %m %d %H %M %S"))
tmpcmdarray = cmddatastr.split(" ")
if len(tmpcmdarray) != 7:
cmddatastr = ""
sendcmdid = 0
elif sendcmdid == 6:
if len(tmpcmdarray) != 6:
cmddatastr = ""
sendcmdid = 0
except OSError:
cmddatastr = ""
if cmddatastr == "":
if loopCtr >= CHECKSTATUSLOOPFREQ:
# Check Battery Status
sendcmdid = 0
loopCtr = 0
else:
loopCtr = loopCtr + 1
if loopCtr == 2:
sendcmdid = 5 # Get RTC Time
elif loopCtr == 3:
sendcmdid = 7 # Get Power on Time
elif loopCtr == 4:
sendcmdid = 4 # Get Version
elif loopCtr == 5:
sendcmdid = 2 # Get Charge Current
elif (loopCtr&1) == 0:
sendcmdid = 0 # Check Battery Status
if sendcmdid >= 0:
sendSize = 0
cmdSize = 0
if len(cmddatastr) > 0:
# set RTC Time (3, 6 bytes)
# set Power of Time (6, 5 bytes)
tmpcmdarray = cmddatastr.split(" ")
CMDsendrequest[1] = len(tmpcmdarray) - 1 # Length
CMDsendrequest[2] = sendcmdid
cmdSize = CMDsendrequest[1] + 4
# Copy payload
tmpdataidx = cmdSize - 1 # Start at end
while tmpdataidx > 3:
tmpdataidx = tmpdataidx - 1
if tmpdataidx == 3 and (sendcmdid == 3 or sendcmdid == 6):
tmpval = int(tmpcmdarray[tmpdataidx-2])
if tmpval >= 2000:
tmpval = tmpval - 2000
else:
tmpval = 0
CMDsendrequest[tmpdataidx] = decAsHex(tmpval)
else:
CMDsendrequest[tmpdataidx] = decAsHex(int(tmpcmdarray[tmpdataidx-2]))
datasum = 0
tmpdataidx = cmdSize - 1
while tmpdataidx > 0:
tmpdataidx = tmpdataidx - 1
datasum = (datasum+CMDsendrequest[tmpdataidx]) & 0xff
CMDsendrequest[cmdSize-1] = datasum
sendSize = ser.write(serial.to_bytes(CMDsendrequest[0:cmdSize]))
ups_debuglog("serial-out-cmd", serial.to_bytes(CMDsendrequest[0:cmdSize]).hex(" "))
else:
# Default Get/Read command
CMDsendrequest[1] = 0 # Length
CMDsendrequest[2] = sendcmdid
CMDsendrequest[3] = (sendcmdid+CMDsendrequest[0]) & 0xff
sendSize = ser.write(serial.to_bytes(CMDsendrequest[0:4]))
cmdSize = CMDsendrequest[1] + 4
#ups_debuglog("serial-out-def", serial.to_bytes(CMDsendrequest[0:4]).hex(" "))
if cmdSize > 0:
sendcmdid=-1
if sendSize == cmdSize:
# Give time to respond
time.sleep(1)
else:
break
# read incoming data
readOut = ser.read()
if len(readOut) == 0:
continue
readdatalen = 1
while True:
tmpreadlen = ser.inWaiting() # Check remaining byte size
if tmpreadlen < 1:
break
readOut += ser.read(tmpreadlen)
readdatalen += tmpreadlen
readintarray = [tmpint for tmpint in readOut]
if len(cmddatastr) > 0:
ups_debuglog("serial-in ", readOut.hex(" "))
cmddatastr = ""
# Parse command stream
tmpidx = 0
while tmpidx < readdatalen:
if readintarray[tmpidx] == CMDSTARTBYTE and tmpidx + CMDCONTROLBYTECOUNT < readdatalen:
# Cmd format: Min 4 bytes
# tmpidx tmpidx+1 tmpidx+2
# 0xfe (byte count) (cmd ID) (payload; byte count) (datasum)
tmpdatalen = readintarray[tmpidx+1]
tmpcmd = readintarray[tmpidx+2]
if tmpidx + CMDCONTROLBYTECOUNT + tmpdatalen < readdatalen:
# Validate datasum
datasum = 0
tmpdataidx = tmpidx + tmpdatalen + CMDCONTROLBYTECOUNT
while tmpdataidx > tmpidx:
tmpdataidx = tmpdataidx - 1
datasum = (datasum+readintarray[tmpdataidx]) & 0xff
if datasum != readintarray[tmpidx + tmpdatalen + CMDCONTROLBYTECOUNT]:
# Invalid sum
pass
else:
needsupdate=False
if tmpcmd == 0:
# Check State
if tmpdatalen >= 2:
needsupdate=True
tmp_battery = readintarray[tmpidx+CMDCONTROLBYTECOUNT]
if tmp_battery>100:
tmp_battery=100
elif tmp_battery<1:
tmp_battery=0
tmp_charging = readintarray[tmpidx+CMDCONTROLBYTECOUNT+1]
#ups_debuglog("battery-data", str(tmp_charging)+" "+str(tmp_battery))
if tmp_charging != device_charging or tmp_battery!=device_battery:
device_battery=tmp_battery
device_charging=tmp_charging
tmpiconfile = "/etc/argon/ups/"
icontitle = "Argon UPS"
if device_charging == 0:
if device_battery==100:
statusstr = "Charged"
#tmpiconfile = tmpiconfile+"battery_plug"
else:
#icontitle = str(device_battery)+"%"+" Full"
statusstr = "Charging"
#tmpiconfile = tmpiconfile+"battery_charging"
tmpiconfile = tmpiconfile+"charge_"+str(device_battery)
else:
#icontitle = str(device_battery)+"%"+" Left"
statusstr = "Battery"
tmpiconfile = tmpiconfile+"discharge_"+str(device_battery)
tmpiconfile = tmpiconfile + ".png"
statusstr = statusstr + " " + str(device_battery)+"%"
#ups_debuglog("battery-info", statusstr)
# Add/update desktop icons too; add check to minimize write
if previconfile != tmpiconfile:
updatedesktopicon(icontitle, statusstr, tmpiconfile)
previconfile = tmpiconfile
elif tmpcmd == 2:
# Charge Current
if tmpdatalen >= 2:
device_chargecurrent = ((readintarray[tmpidx+CMDCONTROLBYTECOUNT])<<8) | readintarray[tmpidx+CMDCONTROLBYTECOUNT+1]
elif tmpcmd == 4:
# Version
if tmpdatalen >= 1:
needsupdate=True
device_version = readintarray[tmpidx+CMDCONTROLBYTECOUNT]
elif tmpcmd == 5:
# RTC Time
if tmpdatalen >= 6:
needsupdate=True
tmpdataidx = 0
while tmpdataidx < 6:
device_rtctime[tmpdataidx] = hexAsDec(readintarray[tmpidx+CMDCONTROLBYTECOUNT+tmpdataidx])
tmpdataidx = tmpdataidx + 1
elif tmpcmd == 7:
# Power On Time
if tmpdatalen >= 5:
needsupdate=True
tmpdataidx = 0
while tmpdataidx < 5:
device_powerontime[tmpdataidx] = hexAsDec(readintarray[tmpidx+CMDCONTROLBYTECOUNT+tmpdataidx])
tmpdataidx = tmpdataidx + 1
elif tmpcmd == 8:
# Send Acknowledge
sendcmdid = tmpcmd
elif tmpcmd == 3:
# New RTC Time set
sendcmdid = 5
elif tmpcmd == 6:
# New Power On Time set
sendcmdid = 7
if needsupdate==True:
# Log File
otherstr = ""
if device_version >= 0:
otherstr = otherstr + " Version:"+str(device_version)+"\n"
if device_rtctime[0] >= 0:
otherstr = otherstr + " Time:"+str(device_rtctime[1])+"/"+str(device_rtctime[2])+"/"+str(device_rtctime[0]+2000)+" "+str(device_rtctime[3])+":"+str(device_rtctime[4])+":"+str(device_rtctime[5])+"\n"
if device_powerontime[1] > 0:
otherstr = otherstr + " Schedule:"+str(device_powerontime[1])+"/"+str(device_powerontime[2])+"/"+str(device_powerontime[0]+2000)+" "+str(device_powerontime[3])+":"+str(device_powerontime[4])+"\n"
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)
#ups_debuglog("status-update", "\n Power:"+statusstr+"\n"+otherstr)
# Point to datasum, so next loop iteration will be correct
tmpidx = tmpidx + tmpdatalen + CMDCONTROLBYTECOUNT
tmpidx = tmpidx + 1
except Exception as e:
try:
ups_debuglog("serial-error", str(e))
except:
ups_debuglog("serial-error", "Error")
break
def updatedesktopicon(icontitle, statusstr, tmpiconfile):
try:
tmp = os.popen("find /home -maxdepth 1 -type d").read()
alllines = tmp.split("\n")
for curfolder in alllines:
if curfolder == "/home" or curfolder == "":
continue
#ups_debuglog("desktop-update-path", curfolder)
#ups_debuglog("desktop-update-text", statusstr)
#ups_debuglog("desktop-update-icon", tmpiconfile)
with open(curfolder+"/Desktop/argonone-ups.desktop", "w") as txt_file:
txt_file.write("[Desktop Entry]\nName="+icontitle+"\nComment="+statusstr+"\nIcon="+tmpiconfile+"\nExec=lxterminal --working-directory="+curfolder+"/ -t \"Argon UPS\" -e \"/etc/argon/argonone-upsconfig.sh argonupsrtc\"\nType=Application\nEncoding=UTF-8\nTerminal=false\nCategories=None;\n")
except Exception as desktope:
#pass
try:
ups_debuglog("desktop-update-error", str(desktope))
except:
ups_debuglog("desktop-update-error", "Error")
def allowshutdown():
uptime = 0.0
errorflag = False
try:
cpuctr = 0
tempfp = open("/proc/uptime", "r")
alllines = tempfp.readlines()
for temp in alllines:
infolist = temp.split(" ")
if len(infolist) > 1:
uptime = float(infolist[0])
break
tempfp.close()
except IOError:
errorflag = True
# 120=2mins minimum up time
return uptime > 120
######
if len(sys.argv) > 1:
cmd = sys.argv[1].upper()
if cmd == "GETBATTERY":
#outobj = ups_sendcmd("0")
outobj = ups_loadlogdata()
try:
print(outobj["power"])
except:
print("Error retrieving battery status")
elif cmd == "GETRTCSCHEDULE":
tmptime = getRTCpoweronschedule()
if tmptime.year > 1999:
print("Alarm Setting:", tmptime)
else:
print("Alarm Setting: None")
elif cmd == "GETRTCTIME":
tmptime = getRTCdatetime()
if tmptime.year > 1999:
print("RTC Time:", tmptime)
else:
print("Error reading RTC Time")
elif cmd == "UPDATERTCTIME":
tmptime = setRTCdatetime()
if tmptime.year > 1999:
print("RTC Time:", tmptime)
else:
print("Error reading RTC Time")
elif cmd == "GETSCHEDULELIST":
argonrtc.describeConfigList(RTC_CONFIGFILE)
elif cmd == "SHOWSCHEDULE":
if len(sys.argv) > 2:
if sys.argv[2].isdigit():
# Display starts at 2, maps to 0-based index
configidx = int(sys.argv[2])-2
configlist = argonrtc.loadConfigList(RTC_CONFIGFILE)
if len(configlist) > configidx:
print (" ",argonrtc.describeConfigListEntry(configlist[configidx]))
else:
print(" Invalid Schedule")
elif cmd == "REMOVESCHEDULE":
if len(sys.argv) > 2:
if sys.argv[2].isdigit():
# Display starts at 2, maps to 0-based index
configidx = int(sys.argv[2])-2
argonrtc.removeConfigEntry(RTC_CONFIGFILE, configidx)
elif cmd == "SERVICE":
ipcq = Queue()
tmprtctime = getRTCdatetime()
if tmprtctime.year >= 2000:
argonrtc.updateSystemTime(tmprtctime)
commandschedulelist = argonrtc.formCommandScheduleList(argonrtc.loadConfigList(RTC_CONFIGFILE))
nextrtcalarmtime = setNextAlarm(commandschedulelist, datetime.datetime.now())
t1 = Thread(target = ups_check, args =(ipcq, ))
t1.start()
serviceloop = True
while serviceloop==True:
tmpcurrenttime = datetime.datetime.now()
if nextrtcalarmtime <= tmpcurrenttime:
# Update RTC Alarm to next iteration
nextrtcalarmtime = setNextAlarm(commandschedulelist, nextrtcalarmtime)
if len(argonrtc.getCommandForTime(commandschedulelist, tmpcurrenttime, "off")) > 0:
# Shutdown detected, issue command then end service loop
if allowshutdown():
os.system("shutdown now -h")
serviceloop = False
# Don't break to sleep while command executes (prevents service to restart)
time.sleep(60)
ipcq.join()
elif False:
print("System Time: ", datetime.datetime.now())
print("RTC Time: ", getRTCdatetime())
+10
View File
@@ -0,0 +1,10 @@
[Unit]
Description=Argon UPS RTC Service
After=multi-user.target
[Service]
Type=simple
Restart=always
RemainAfterExit=true
ExecStart=/usr/bin/python3 /etc/argon/argonupsrtcd.py SERVICE
[Install]
WantedBy=multi-user.target
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
NTPSERVER="time.google.com"
TMPCONFIG=/dev/shm/tmpconfig.conf
# timesyncd
CONFIG=/etc/systemd/timesyncd.conf
if [ -f "$CONFIG" ]
then
cat "$CONFIG" | grep -v -e 'NTP=' > "$TMPCONFIG"
echo "NTP=$NTPSERVER" >> "$TMPCONFIG"
sudo chown root:root "$TMPCONFIG"
sudo chmod 644 "$TMPCONFIG"
sudo mv "$TMPCONFIG" "$CONFIG"
# /usr/sbin/ntpd
sudo service systemd-timesyncd restart > /dev/null 2>&1
fi
for CURSERVICECONFIG in ntp chrony
do
CONFIG=/etc/${CURSERVICECONFIG}.conf
if [ -f "$CONFIG" ]
then
cat "$CONFIG" | grep -v -e 'pool ' > "$TMPCONFIG"
#echo "server $NTPSERVER" >> "$TMPCONFIG"
echo "pool time1.google.com iburst" >> "$TMPCONFIG"
echo "pool time2.google.com iburst" >> "$TMPCONFIG"
echo "pool time3.google.com iburst" >> "$TMPCONFIG"
echo "pool time4.google.com iburst" >> "$TMPCONFIG"
sudo chown root:root "$TMPCONFIG"
sudo chmod 644 "$TMPCONFIG"
sudo mv "$TMPCONFIG" "$CONFIG"
sudo service ${CURSERVICECONFIG} restart > /dev/null 2>&1
fi
done
Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.