Last active
January 11, 2021 21:38
-
-
Save jbaiter/39d686325062f3058236267490a2785b to your computer and use it in GitHub Desktop.
Small script to inhibit charging when powering a USB-PD Thinkpad with a USB-C powerbank.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ACTION=="change", SUBSYSTEM=="power_supply", RUN+="/usr/local/sbin/powerbank" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
""" | |
Adjust the value of POWERBANK_WATTS to match your Powerbank's wattage. | |
If the wattage is identical to your actual charger, you're out of luck, | |
since the script uses this to differentiate a powerbank from an actual charger :-/ | |
Requirements: | |
- acpi_call kernel module | |
- python-notify2 | |
- tlp | |
Only tested on a T480s, the ACPI calls may differ for other models. | |
""" | |
import ctypes | |
import os | |
import subprocess | |
import time | |
import notify2 | |
DISPLAY = ":0" | |
XAUTHORITY = os.path.expanduser('~/.Xauthority') | |
POWERBANK_WATTS = 31 | |
DEFAULT_THRESHOLDS = (85, 90) | |
POWERBANK_THRESHOLDS = (20, 25) | |
TLP_MODES = { | |
'battery (manual)': 'bat', | |
'AC (manual)': 'ac', | |
'battery': 'bat', | |
'AC': 'ac', | |
} | |
def call_acpi(cmd, *args): | |
if args: | |
cmd += ' 0x' | |
cmd += ''.join(hex(arg)[2:] if isinstance(arg, int) else arg | |
for arg in args) | |
with open('/proc/acpi/call', 'wb') as fp: | |
fp.write(cmd.encode('utf8')) | |
with open('/proc/acpi/call', 'rb') as fp: | |
val = ctypes.create_string_buffer(fp.read()).value | |
try: | |
return int(val, 0) | |
except: | |
return val | |
def get_tlp_mode(): | |
tlp_stat_cmd = ['/usr/bin/tlp-stat', '-s'] | |
if not os.path.exists(tlp_stat_cmd[0]): | |
tlp_stat_cmd = ['/usr/sbin/tlp', 'stat', '-s'] | |
out = subprocess.check_output(tlp_stat_cmd) | |
modeline = next(l for l in out.split('\n') if n.startswith('Mode')) | |
mode = modeline.split('=')[1].strip().tolower() | |
return TLP_MODES.get(mode, mode) | |
def set_tlp_mode(mode=None): | |
if mode and mode not in ('ac', 'bat'): | |
raise ValueError("Invalid mode '{}', mut be 'ac' or 'bat'".format(mode)) | |
subprocess.check_output(['/usr/sbin/tlp', mode or 'auto']) | |
def get_power_supply_watts(): | |
return call_acpi('\_SB.PCI0.LPCB.EC.HWAT') | |
def set_charging_thresholds(start=0, stop=0): | |
call_acpi('\_SB.PCI0.LPCB.EC.HKEY.BCCS', 1, start) | |
call_acpi('\_SB.PCI0.LPCB.EC.HKEY.BCSS', 1, stop) | |
def get_charging_thresholds(): | |
start = call_acpi('\_SB.PCI0.LPCB.EC.HKEY.BCTG', 1) | |
stop = call_acpi('\_SB.PCI0.LPCB.EC.HKEY.BCSG', 1) | |
return start, stop | |
def main(): | |
time.sleep(3) | |
if not os.environ.get('DISPLAY'): | |
os.environ['DISPLAY'] = DISPLAY | |
if not os.environ.get('XAUTHORITY'): | |
os.environ['XAUTHORITY'] = XAUTHORITY | |
notify2.init('powerbank-notifier') | |
wattage = get_power_supply_watts() | |
thresholds = get_charging_thresholds() | |
if thresholds == POWERBANK_THRESHOLDS: | |
return | |
elif 0 < wattage <= POWERBANK_WATTS: | |
notify_header = "Detected Powerbank" | |
notify_msg = 'Inhibiting charging, switching TLP to battery mode' | |
set_tlp_mode('bat') | |
set_charging_thresholds(*POWERBANK_THRESHOLDS) | |
elif wattage > 0: | |
notify_header = "Detected {}W charger".format(wattage) | |
notify_msg = "Re-enabling charging, switching TLP to AC mode" | |
set_tlp_mode('ac') | |
set_charging_thresholds(*DEFAULT_THRESHOLDS) | |
else: | |
notify_header = "Running from internal battery" | |
notify_msg = "Re-enabling charging, switching TLP to auto mode" | |
set_tlp_mode(None) | |
set_charging_thresholds(*DEFAULT_THRESHOLDS) | |
if notify_msg is not None: | |
notification = notify2.Notification(notify_header, notify_msg) | |
notification.show() | |
if __name__ == '__main__': | |
try: | |
main() | |
except Exception as e: | |
print(e) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment