import json import logging import re import subprocess from mflux.callbacks.callback import BeforeLoopCallback from mflux.utils.exceptions import StopImageGenerationException logger = logging.getLogger(__name__) class BatterySaver(BeforeLoopCallback): PMSET_AC_POWER_STATUS = "Now drawing from 'AC Power'" PMSET_BATT_STATUS_PATTERN = r"InternalBattery-.+?(\d+)%" _machine_model: str | None = None _is_battery_powered: bool | None = None def __init__(self, battery_percentage_stop_limit: int = 10): self.limit = battery_percentage_stop_limit def call_before_loop(self, **kwargs) -> None: # type: ignore current_pct = self._get_battery_percentage() if current_pct is not None and current_pct <= self.limit: raise StopImageGenerationException(f"Battery below {self.limit}% threshold: {current_pct}%") def _get_battery_percentage(self) -> int | None: if not self._is_machine_battery_powered(): return None percentage = None try: result = subprocess.run( ["pmset", "-g", "batt"], capture_output=True, text=True, check=True, ) if self.PMSET_AC_POWER_STATUS not in result.stdout: if match := re.search(self.PMSET_BATT_STATUS_PATTERN, result.stdout): percentage = int(match.group(1)) except (subprocess.CalledProcessError, TypeError) as e: logger.warning( f"Cannot read battery percentage via 'pmset -g batt': {e}. " f"Battery saver functionality is disabled and the program will continue running." ) return percentage @classmethod def _is_machine_battery_powered(cls) -> bool: if cls._is_battery_powered is None: machine_model = cls._get_machine_model() cls._is_battery_powered = "MacBook" in machine_model return cls._is_battery_powered @classmethod def _get_machine_model(cls) -> str: if cls._machine_model is None: try: result = subprocess.run( ["system_profiler", "-json", "SPHardwareDataType"], capture_output=True, text=True, check=True, ) data = json.loads(result.stdout) cls._machine_model = data["SPHardwareDataType"][0]["machine_model"] except (subprocess.CalledProcessError, json.JSONDecodeError, IndexError, KeyError) as e: logger.warning(f"Cannot determine machine model via 'system_profiler -json SPHardwareDataType': {e}") cls._machine_model = "Unknown" return cls._machine_model