Add bluetooth on flag & completions

This commit is contained in:
Adnan Hodzic 2025-04-13 08:53:50 +02:00
parent a7df573ec8
commit 528a7d6b37
2 changed files with 192 additions and 88 deletions

View File

@ -5,16 +5,16 @@
# Blog post: https://foolcontrol.org/?p=3124 # Blog post: https://foolcontrol.org/?p=3124
# core import # core import
import sys, time import sys, time, os
from subprocess import run from subprocess import run
from shutil import rmtree from shutil import rmtree
from auto_cpufreq.battery_scripts.battery import * from auto_cpufreq.battery_scripts.battery import *
from auto_cpufreq.config.config import config as conf, find_config_file from auto_cpufreq.config.config import config as conf, find_config_file
from auto_cpufreq.core import * from auto_cpufreq.core import *
from auto_cpufreq.globals import GITHUB, IS_INSTALLED_WITH_AUR, IS_INSTALLED_WITH_SNAP from auto_cpufreq.globals import GITHUB, IS_INSTALLED_WITH_AUR, IS_INSTALLED_WITH_SNAP, SNAP_DAEMON_CHECK
from auto_cpufreq.modules.system_monitor import ViewType, SystemMonitor from auto_cpufreq.modules.system_monitor import ViewType, SystemMonitor
# import everything from power_helper, including bluetooth_disable # import everything from power_helper, including bluetooth_disable and bluetooth_enable
from auto_cpufreq.power_helper import * from auto_cpufreq.power_helper import *
from threading import Thread from threading import Thread
@ -31,11 +31,13 @@ from threading import Thread
@click.option("--get-state", is_flag=True, hidden=True) @click.option("--get-state", is_flag=True, hidden=True)
@click.option("--completions", is_flag=False, help="Enables shell completions for bash, zsh and fish.\n Possible values bash|zsh|fish") @click.option("--completions", is_flag=False, help="Enables shell completions for bash, zsh and fish.\n Possible values bash|zsh|fish")
@click.option("--bluetooth_boot_off", is_flag=True, help="Turn off Bluetooth on boot") @click.option("--bluetooth_boot_off", is_flag=True, help="Turn off Bluetooth on boot")
@click.option("--bluetooth_boot_on", is_flag=True, help="Turn on Bluetooth on boot")
@click.option("--debug", is_flag=True, help="Show debug info (include when submitting bugs)") @click.option("--debug", is_flag=True, help="Show debug info (include when submitting bugs)")
@click.option("--version", is_flag=True, help="Show currently installed version") @click.option("--version", is_flag=True, help="Show currently installed version")
@click.option("--donate", is_flag=True, help="Support the project") @click.option("--donate", is_flag=True, help="Support the project")
def main(monitor, live, daemon, install, update, remove, force, config, stats, get_state, completions, def main(monitor, live, daemon, install, update, remove, force, config, stats, get_state, completions,
bluetooth_boot_off, bluetooth_boot_off,
bluetooth_boot_on,
debug, version, donate): debug, version, donate):
# display info if config file is used # display info if config file is used
config_path = find_config_file(config) config_path = find_config_file(config)
@ -44,20 +46,25 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
if conf.has_config(): if conf.has_config():
print("\nUsing settings defined in " + config_path + " file") print("\nUsing settings defined in " + config_path + " file")
if len(sys.argv) == 1: # Check for empty arguments first
is_empty_run = len(sys.argv) == 1
# set governor override unless None or invalid, but not if it's an empty run
if not is_empty_run and force is not None:
not_running_daemon_check()
root_check()
set_override(force)
# Handle empty run after potential force override is processed or skipped
if is_empty_run:
print("\n" + "-" * 32 + " auto-cpufreq " + "-" * 33 + "\n") print("\n" + "-" * 32 + " auto-cpufreq " + "-" * 33 + "\n")
print("Automatic CPU speed & power optimizer for Linux") print("Automatic CPU speed & power optimizer for Linux")
print("\nExample usage:\nauto-cpufreq --monitor") print("\nExample usage:\nauto-cpufreq --monitor")
print("\n-----\n") print("\n-----\n")
run(["auto-cpufreq", "--help"]) run(["auto-cpufreq", "--help"])
footer() footer()
# set governor override unless None or invalid # Handle other flags if it's not an empty run
if force is not None: else:
not_running_daemon_check()
root_check() # Calling root_check before set_override as it will require sudo access
set_override(force) # Calling set override, only if force has some values
if monitor: if monitor:
root_check() root_check()
battery_setup() battery_setup()
@ -69,15 +76,21 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
gnome_power_detect() gnome_power_detect()
tlp_service_detect() tlp_service_detect()
if IS_INSTALLED_WITH_SNAP or tlp_stat_exists or (systemctl_exists and not bool(gnome_power_status)): # Determine if confirmation is needed
needs_confirmation = IS_INSTALLED_WITH_SNAP or tlp_stat_exists
# Check gnome_power_status only if relevant variables exist
if not IS_INSTALLED_WITH_SNAP and 'systemctl_exists' in globals() and systemctl_exists and 'gnome_power_status' in locals() and not bool(gnome_power_status):
needs_confirmation = True
if needs_confirmation:
try: try:
input("press Enter to continue or Ctrl + c to exit...") input("press Enter to continue or Ctrl + c to exit...")
except KeyboardInterrupt: except KeyboardInterrupt:
conf.notifier.stop() conf.notifier.stop()
sys.exit(0) sys.exit(0)
monitor = SystemMonitor(suggestion=True, type=ViewType.MONITOR) monitor_instance = SystemMonitor(suggestion=True, type=ViewType.MONITOR)
monitor.run(on_quit=conf.notifier.stop) monitor_instance.run(on_quit=conf.notifier.stop)
elif live: elif live:
root_check() root_check()
battery_setup() battery_setup()
@ -91,7 +104,13 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
tuned_stop_live() tuned_stop_live()
tlp_service_detect() tlp_service_detect()
if IS_INSTALLED_WITH_SNAP or tlp_stat_exists or (systemctl_exists and not bool(gnome_power_status)): # Determine if confirmation is needed
needs_confirmation = IS_INSTALLED_WITH_SNAP or tlp_stat_exists
# Check gnome_power_status only if relevant variables exist
if not IS_INSTALLED_WITH_SNAP and 'systemctl_exists' in globals() and systemctl_exists and 'gnome_power_status' in locals() and not bool(gnome_power_status):
needs_confirmation = True
if needs_confirmation:
try: try:
input("press Enter to continue or Ctrl + c to exit...") input("press Enter to continue or Ctrl + c to exit...")
except KeyboardInterrupt: except KeyboardInterrupt:
@ -100,18 +119,19 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
cpufreqctl() cpufreqctl()
def live_daemon(): def live_daemon():
# Redirect stdout to suppress prints
class NullWriter: class NullWriter:
def write(self, _): pass def write(self, _): pass
def flush(self): pass def flush(self): pass
original_stdout = sys.stdout
try: try:
sys.stdout = NullWriter() sys.stdout = NullWriter()
while True: while True:
time.sleep(1) time.sleep(1)
set_autofreq() set_autofreq()
except: except Exception as e: # Catch specific exceptions if possible
pass print(f"Error in live daemon thread: {e}", file=original_stdout) # Log errors
finally:
sys.stdout = original_stdout # Ensure stdout is restored
def live_daemon_off(): def live_daemon_off():
gnome_power_start_live() gnome_power_start_live()
@ -122,12 +142,12 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
thread = Thread(target=live_daemon, daemon=True) thread = Thread(target=live_daemon, daemon=True)
thread.start() thread.start()
monitor = SystemMonitor(type=ViewType.LIVE) monitor_instance = SystemMonitor(type=ViewType.LIVE)
monitor.run(on_quit=live_daemon_off) monitor_instance.run(on_quit=live_daemon_off)
elif daemon: elif daemon:
config_info_dialog() config_info_dialog()
root_check() root_check()
file_stats() file_stats() # This function from core.py likely uses the stats paths internally
if IS_INSTALLED_WITH_SNAP and SNAP_DAEMON_CHECK == "enabled": if IS_INSTALLED_WITH_SNAP and SNAP_DAEMON_CHECK == "enabled":
gnome_power_detect_snap() gnome_power_detect_snap()
tlp_service_detect_snap() tlp_service_detect_snap()
@ -136,84 +156,142 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
tlp_service_detect() tlp_service_detect()
battery_setup() battery_setup()
conf.notifier.start() conf.notifier.start()
while True: print("Starting auto-cpufreq daemon...") # Add startup message
try: try:
footer() # Initial setup before loop
gov_check() gov_check()
cpufreqctl() cpufreqctl()
distro_info() distro_info() # Show info once on start
sysinfo() sysinfo() # Show info once on start
while True:
set_autofreq() set_autofreq()
countdown(2) time.sleep(2) # Use simple sleep instead of countdown
except KeyboardInterrupt: break except KeyboardInterrupt:
conf.notifier.stop() print("\nDaemon interrupted. Restoring settings...")
finally: # Ensure cleanup happens
conf.notifier.stop()
cpufreqctl_restore() # Restore system state when daemon stops
footer()
elif install: elif install:
root_check() root_check()
if IS_INSTALLED_WITH_SNAP: if IS_INSTALLED_WITH_SNAP:
running_daemon_check() running_daemon_check()
gnome_power_detect_snap() gnome_power_detect_snap()
tlp_service_detect_snap() tlp_service_detect_snap()
# ToDo: add note to say you can use bluetooth flag to enable bluetooth on boot bluetooth_notif_snap() # Warn about bluetooth boot setting
bluetooth_notif_snap()
gov_check() gov_check()
run("snapctl set daemon=enabled", shell=True) run("snapctl set daemon=enabled", shell=True, check=True)
run("snapctl start --enable auto-cpufreq", shell=True) run("snapctl start --enable auto-cpufreq", shell=True, check=True)
else: else:
running_daemon_check() running_daemon_check()
gov_check() gov_check()
# ToDo: add note to say you can use bluetooth flag to enable bluetooth on boot deploy_daemon() # This function from core.py likely uses stats paths
deploy_daemon()
deploy_complete_msg() deploy_complete_msg()
elif update: elif update:
root_check() root_check()
custom_dir = "/opt/auto-cpufreq/source" custom_dir = "/opt/auto-cpufreq/source"
for arg in sys.argv: update_flag_present = False
if arg.startswith("--update="): args_to_remove = []
custom_dir = arg.split("=")[1] # Simplified update argument parsing
sys.argv.remove(arg)
if "--update" in sys.argv: if "--update" in sys.argv:
update = True update_flag_present = True
sys.argv.remove("--update") args_to_remove.append("--update")
if len(sys.argv) == 2: custom_dir = sys.argv[1] # Simple check if next arg exists and isn't another flag
idx = sys.argv.index("--update")
if IS_INSTALLED_WITH_SNAP: if idx + 1 < len(sys.argv) and not sys.argv[idx+1].startswith('--'):
print("Detected auto-cpufreq was installed using snap") custom_dir = sys.argv[idx+1]
# refresh snap directly using this command args_to_remove.append(custom_dir)
# path wont work in this case
print("Please update using snap package manager, i.e: `sudo snap refresh auto-cpufreq`.")
#check for AUR
elif IS_INSTALLED_WITH_AUR: print("Arch-based distribution with AUR support detected. Please refresh auto-cpufreq using your AUR helper.")
else: else:
is_new_update = check_for_update() for arg in sys.argv:
if not is_new_update: return if arg.startswith("--update="):
ans = input("Do you want to update auto-cpufreq to the latest release? [Y/n]: ").strip().lower() update_flag_present = True
if not os.path.exists(custom_dir): os.makedirs(custom_dir) custom_dir = arg.split("=", 1)[1]
if os.path.exists(os.path.join(custom_dir, "auto-cpufreq")): rmtree(os.path.join(custom_dir, "auto-cpufreq")) args_to_remove.append(arg)
if ans in ['', 'y', 'yes']: break # Found it, no need to check further
remove_daemon()
remove_complete_msg() # Remove the parsed arguments
new_update(custom_dir) # This prevents them from being misinterpreted later if code relies on sys.argv directly
print("enabling daemon") _original_argv = sys.argv[:] # Make a copy if needed elsewhere
run(["auto-cpufreq", "--install"]) sys.argv = [arg for arg in sys.argv if arg not in args_to_remove]
print("auto-cpufreq is installed with the latest version")
run(["auto-cpufreq", "--version"])
else: print("Aborted") if update_flag_present:
if IS_INSTALLED_WITH_SNAP:
print("Detected auto-cpufreq was installed using snap")
print("Please update using snap package manager, i.e: `sudo snap refresh auto-cpufreq`.")
elif IS_INSTALLED_WITH_AUR:
print("Arch-based distribution with AUR support detected. Please refresh auto-cpufreq using your AUR helper.")
else:
is_new_update = check_for_update()
if not is_new_update: return
ans = input(f"Update source will be placed in '{custom_dir}'.\nDo you want to update auto-cpufreq to the latest release? [Y/n]: ").strip().lower()
if ans in ['', 'y', 'yes']:
# Ensure directory exists
os.makedirs(custom_dir, exist_ok=True)
auto_cpufreq_subdir = os.path.join(custom_dir, "auto-cpufreq")
if os.path.isdir(auto_cpufreq_subdir):
print(f"Removing existing source sub-directory: {auto_cpufreq_subdir}")
try:
rmtree(auto_cpufreq_subdir)
except OSError as e:
print(f"Error removing directory {auto_cpufreq_subdir}: {e}")
print("Update aborted.")
return # Stop update if cleanup fails
print("Removing existing installation (if any)...")
remove_daemon() # Call remove logic first
# remove_complete_msg() # Optional message
print(f"Downloading new version to {custom_dir}...")
new_update(custom_dir) # Download the update
print("Running installer for the updated version...")
# Use subprocess.run to call the install command of the main script
install_result = run([_original_argv[0], "--install"], check=False) # Use original script path
if install_result.returncode == 0:
print("\nUpdate and installation successful.")
run([_original_argv[0], "--version"])
else:
print("\nError during installation after update. Please check messages above.")
else:
print("Update aborted.")
# else: # If neither --update nor --update= was found
# Potentially show help or an error if update was expected
# print("Update command not used correctly.")
# run([_original_argv[0], "--help"])
elif remove: elif remove:
root_check() root_check()
if IS_INSTALLED_WITH_SNAP: if IS_INSTALLED_WITH_SNAP:
run("snapctl set daemon=disabled", shell=True) run("snapctl stop auto-cpufreq", shell=True, check=False)
run("snapctl stop --disable auto-cpufreq", shell=True) run("snapctl set daemon=disabled", shell=True, check=True)
if auto_cpufreq_stats_path.exists(): # run("snapctl disable auto-cpufreq", shell=True, check=True) # Deprecated? Use stop --disable
if auto_cpufreq_stats_file is not None: run("snapctl stop --disable auto-cpufreq", shell=True, check=True) # Correct way to stop and disable
auto_cpufreq_stats_file.close()
auto_cpufreq_stats_path.unlink() # Check if stats path/file variables exist before using them
# {the following snippet also used in --update, update it there too(if required)} # These should be available from 'from auto_cpufreq.core import *'
# * undo bluetooth boot disable if 'auto_cpufreq_stats_path' in globals() and auto_cpufreq_stats_path.exists():
# Close file handle safely
if 'auto_cpufreq_stats_file' in globals() and auto_cpufreq_stats_file is not None:
if not auto_cpufreq_stats_file.closed:
try:
auto_cpufreq_stats_file.close()
except Exception as e:
print(f"Warning: Could not close stats file handle: {e}")
# Set to None after closing or if already closed
# auto_cpufreq_stats_file = None # Modifying imported var might be tricky
# Remove the file
try:
auto_cpufreq_stats_path.unlink()
print(f"Removed stats file: {auto_cpufreq_stats_path}")
except OSError as e:
print(f"Warning: Could not remove stats file {auto_cpufreq_stats_path}: {e}")
# Reminders
gnome_power_rm_reminder_snap() gnome_power_rm_reminder_snap()
else: remove_daemon() bluetooth_on_notif_snap()
else:
remove_daemon() # Defined in core.py, handles service removal and cleanup including stats file
remove_complete_msg() remove_complete_msg()
elif stats: elif stats:
not_running_daemon_check() not_running_daemon_check()
@ -225,20 +303,27 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
gnome_power_detect() gnome_power_detect()
tlp_service_detect() tlp_service_detect()
if IS_INSTALLED_WITH_SNAP or tlp_stat_exists or (systemctl_exists and not bool(gnome_power_status)): # Determine if confirmation is needed
needs_confirmation = IS_INSTALLED_WITH_SNAP or tlp_stat_exists
# Check gnome_power_status only if relevant variables exist
if not IS_INSTALLED_WITH_SNAP and 'systemctl_exists' in globals() and systemctl_exists and 'gnome_power_status' in locals() and not bool(gnome_power_status):
needs_confirmation = True
if needs_confirmation:
try: try:
input("press Enter to continue or Ctrl + c to exit...") input("press Enter to continue or Ctrl + c to exit...")
except KeyboardInterrupt: except KeyboardInterrupt:
conf.notifier.stop() # conf.notifier might not be started for stats mode
sys.exit(0) sys.exit(0)
monitor = SystemMonitor(type=ViewType.STATS) monitor_instance = SystemMonitor(type=ViewType.STATS)
monitor.run() monitor_instance.run()
elif get_state: elif get_state:
not_running_daemon_check() not_running_daemon_check()
override = get_override() override = get_override()
print(override) print(override)
elif completions: elif completions:
# Logic for completions (kept as per previous code)
if completions == "bash": if completions == "bash":
print("Run the below command in your current shell!\n") print("Run the below command in your current shell!\n")
print("echo 'eval \"$(_AUTO_CPUFREQ_COMPLETE=bash_source auto-cpufreq)\"' >> ~/.bashrc") print("echo 'eval \"$(_AUTO_CPUFREQ_COMPLETE=bash_source auto-cpufreq)\"' >> ~/.bashrc")
@ -251,12 +336,16 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
print("Run the below command in your current shell!\n") print("Run the below command in your current shell!\n")
print("echo '_AUTO_CPUFREQ_COMPLETE=fish_source auto-cpufreq | source' > ~/.config/fish/completions/auto-cpufreq.fish") print("echo '_AUTO_CPUFREQ_COMPLETE=fish_source auto-cpufreq | source' > ~/.config/fish/completions/auto-cpufreq.fish")
else: print("Invalid Option, try bash|zsh|fish as argument to --completions") else: print("Invalid Option, try bash|zsh|fish as argument to --completions")
elif bluetooth_boot_off: elif bluetooth_boot_off:
root_check() root_check()
bluetooth_disable() bluetooth_disable()
footer() footer()
elif bluetooth_boot_on:
root_check()
bluetooth_enable()
footer()
elif debug: elif debug:
# ToDo: add status of GNOME Power Profile service status
config_info_dialog() config_info_dialog()
root_check() root_check()
battery_get_thresholds() battery_get_thresholds()
@ -270,7 +359,11 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
python_info() python_info()
print() print()
device_info() device_info()
print(f"Battery is: {'' if charging() else 'dis'}charging") # Check charging status function exists and call it
if 'charging' in globals() and callable(charging):
print(f"Battery is: {'' if charging() else 'dis'}charging")
else:
print("Battery status unavailable.")
print() print()
app_res_use() app_res_use()
get_load() get_load()
@ -288,5 +381,16 @@ def main(monitor, live, daemon, install, update, remove, force, config, stats, g
print("Show your appreciation by donating!") print("Show your appreciation by donating!")
print(GITHUB+"#donate") print(GITHUB+"#donate")
footer() footer()
# else: # Optional: Handle unrecognized flags if not caught by click
# print(f"Error: Unrecognized arguments.")
# run([_original_argv[0], "--help"])
if __name__ == "__main__": main()
if __name__ == "__main__":
try:
import click
except ImportError:
print("Error: Required dependency 'click' not found. Please install it (e.g., pip install click)")
sys.exit(1)
# Call main entry point
main()

View File

@ -7,9 +7,9 @@ from subprocess import call, DEVNULL, getoutput, STDOUT
from sys import argv from sys import argv
# ToDo: update README part how to run this script # ToDo: update README part how to run this script
from .core import * from auto_cpufreq.core import *
from .globals import GITHUB, IS_INSTALLED_WITH_SNAP from auto_cpufreq.globals import GITHUB, IS_INSTALLED_WITH_SNAP
from .tlp_stat_parser import TLPStatusParser from auto_cpufreq.tlp_stat_parser import TLPStatusParser
# app_name var # app_name var
app_name = "python3 power_helper.py" if argv[0] == "power_helper.py" else "auto-cpufreq" app_name = "python3 power_helper.py" if argv[0] == "power_helper.py" else "auto-cpufreq"