Added script
This commit is contained in:
parent
1152fd9647
commit
3732e457d2
1 changed files with 156 additions and 0 deletions
156
autodischarger.py
Executable file
156
autodischarger.py
Executable file
|
@ -0,0 +1,156 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# vim: set ft=python:
|
||||||
|
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import os
|
||||||
|
from enum import Enum
|
||||||
|
import time
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
from datetime import date as D
|
||||||
|
|
||||||
|
MIN_CPU=8
|
||||||
|
MAX_CPU=85
|
||||||
|
|
||||||
|
BOINC_XML="/Library/Application Support/BOINC Data/global_prefs_override.xml"
|
||||||
|
BOINC_PASS=os.popen("cat '/Library/Application Support/BOINC Data/gui_rpc_auth.cfg'").read()
|
||||||
|
BOINC_CMD="env boinccmd --host localhost --passwd '" + BOINC_PASS + "'"
|
||||||
|
|
||||||
|
def send_notification(message):
|
||||||
|
os.system("terminal-notifier -title 'BOINC Auto discharger' -message '%s'" % (message))
|
||||||
|
|
||||||
|
class State(Enum):
|
||||||
|
CHARGING = 1
|
||||||
|
MUST_DISCHARGE = 2
|
||||||
|
DISCHARGING = 3
|
||||||
|
MUST_CHARGE = 4
|
||||||
|
|
||||||
|
def next(self, percent, charging):
|
||||||
|
state = None
|
||||||
|
if (self is State.DISCHARGING and percent <= 20) or (self is State.CHARGING and not charging):
|
||||||
|
state = State.MUST_CHARGE
|
||||||
|
elif (self is State.CHARGING and percent >= 90) or (self is State.DISCHARGING and charging):
|
||||||
|
state = State.MUST_DISCHARGE
|
||||||
|
elif (self is State.MUST_CHARGE and charging):
|
||||||
|
state = State.CHARGING
|
||||||
|
elif (self is State.MUST_DISCHARGE and not charging):
|
||||||
|
state = State.DISCHARGING
|
||||||
|
else:
|
||||||
|
return self
|
||||||
|
|
||||||
|
state.action()
|
||||||
|
return state
|
||||||
|
|
||||||
|
def action(self):
|
||||||
|
if self is State.MUST_CHARGE:
|
||||||
|
set_params(cpu=MIN_CPU, time_range=TimeRange(12, 30, 21, 00))
|
||||||
|
send_notification("Connect the charger! BOINC slowed down")
|
||||||
|
elif self is State.MUST_DISCHARGE:
|
||||||
|
send_notification("Disconnect the charger to speed up BOINC")
|
||||||
|
elif self is State.DISCHARGING:
|
||||||
|
set_params(cpu=MAX_CPU, time_range=TimeRange(00, 00, 24, 00))
|
||||||
|
else:
|
||||||
|
set_params(cpu=MIN_CPU, time_range=TimeRange(12, 30, 21, 00))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self is State.DISCHARGING:
|
||||||
|
return "Discharging, BOINC at full speed"
|
||||||
|
elif self is State.CHARGING:
|
||||||
|
return "Charging, BOINC at slow speed"
|
||||||
|
elif self is State.MUST_CHARGE:
|
||||||
|
return "Waiting for charger, BOINC at slow speed"
|
||||||
|
else:
|
||||||
|
return "Waiting for charger disconnect, BOINC at slow speed"
|
||||||
|
|
||||||
|
|
||||||
|
class TimeRange:
|
||||||
|
def __init__(self, start_h, start_m, end_h, end_m):
|
||||||
|
self.start_h = start_h
|
||||||
|
self.start_m = start_m
|
||||||
|
self.end_h = end_h
|
||||||
|
self.end_m = end_m
|
||||||
|
|
||||||
|
def valid(self):
|
||||||
|
if self.start_h is None or self.end_h is None or self.start_m is None or self.end_m is None:
|
||||||
|
return False
|
||||||
|
if not (0 <= self.start_h < 24 and 0 < self.end_h <= 24 and 0 <= self.start_m < 60 \
|
||||||
|
and 0 <= self.end_m < 60):
|
||||||
|
return False
|
||||||
|
return self.start_h * 60 + self.start_m < self.end_h * 60 + self.end_m
|
||||||
|
|
||||||
|
def get_start(self):
|
||||||
|
return self.start_h + self.start_m / 60
|
||||||
|
|
||||||
|
def get_end(self):
|
||||||
|
return self.end_h + self.end_m / 60
|
||||||
|
|
||||||
|
|
||||||
|
def set_param(tree, name, value):
|
||||||
|
dom = tree.find(name)
|
||||||
|
dom.text = "%2.6f" % (value)
|
||||||
|
|
||||||
|
|
||||||
|
def set_params(cpu=None, time_range=None, disable=True):
|
||||||
|
tree = ET.parse(BOINC_XML)
|
||||||
|
|
||||||
|
if cpu is not None and 0 < cpu <= 100:
|
||||||
|
set_param(tree, 'cpu_usage_limit', cpu)
|
||||||
|
|
||||||
|
if time_range is not None and time_range.valid():
|
||||||
|
set_param(tree, 'start_hour', time_range.get_start())
|
||||||
|
set_param(tree, 'end_hour', time_range.get_end())
|
||||||
|
|
||||||
|
tree.write(BOINC_XML)
|
||||||
|
os.system(BOINC_CMD + ' --read_global_prefs_override')
|
||||||
|
|
||||||
|
def charge_status():
|
||||||
|
percent = int(os.popen("pmset -g batt | awk '/Internal/ {print $3}'").read()[:-3])
|
||||||
|
charging = not os.system("pmset -g batt | grep discharging 2>&1 >/dev/null") == 0
|
||||||
|
return (percent, charging)
|
||||||
|
|
||||||
|
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
print('')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
status = None
|
||||||
|
percent, charging = charge_status()
|
||||||
|
|
||||||
|
if charging:
|
||||||
|
status = State.CHARGING
|
||||||
|
else:
|
||||||
|
status = State.DISCHARGING
|
||||||
|
|
||||||
|
os.system('clear')
|
||||||
|
status = status.next(percent, charging)
|
||||||
|
status.action()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
percent, charging = charge_status()
|
||||||
|
count = int(os.popen("system_profiler SPPowerDataType | grep " +
|
||||||
|
"'Cycle Count' | awk '{print $3}'").read())
|
||||||
|
cycles_remaining = 1000 - count
|
||||||
|
care_days = (D(2021, 6, 30) - D.today()).days
|
||||||
|
|
||||||
|
stats = None
|
||||||
|
if len(sys.argv) > 2 and sys.argv[2] == "stats":
|
||||||
|
stats = os.popen('istats').read()
|
||||||
|
|
||||||
|
os.system('clear')
|
||||||
|
print("Battery: %d%% - %s\nCharge count: %d" % (percent, status, count))
|
||||||
|
print("Remaining: %d days, %d cycles\nRate needed: %.5f cycles a day" %
|
||||||
|
(care_days, cycles_remaining, cycles_remaining / care_days))
|
||||||
|
|
||||||
|
if stats is not None:
|
||||||
|
print('\n' + stats[:stats.rfind('\n\n')])
|
||||||
|
|
||||||
|
status = status.next(percent, charging)
|
||||||
|
time.sleep(10 if len(sys.argv) <= 1 or sys.argv[1] is None else
|
||||||
|
int(sys.argv[1]))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Reference in a new issue