1#!/usr/bin/env python3

2#

3# This file is part of Checkbox.

4#

5# Copyright 2024 Canonical Ltd.

6# Authors: Bin Li <bin.li@canonical.com>

7#

8# Checkbox is free software: you can redistribute it and/or modify

9# it under the terms of the GNU General Public License version 3,

10# as published by the Free Software Foundation.

11#

12# Checkbox is distributed in the hope that it will be useful,

13# but WITHOUT ANY WARRANTY; without even the implied warranty of

14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

15# GNU General Public License for more details.

16#

17# You should have received a copy of the GNU General Public License

18# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.

19

20# pylint: disable=consider-using-f-string

21

22""" Switching the power mode to check if the power mode can be switched. """

23 from pathlib import Path

24 import subprocess

25 import contextlib

26

27

28 def get_sysfs_content(path):

29 """

30 Reads the content of a sysfs file.

31 Args:

32 path (pathlib.Path): Path to the sysfs file.

33 Raises:

34 SystemExit: If the file could not be read.

35 """

36 with open(path, "rt", encoding="utf-8") as stream:

37 content = stream.read().strip()

38 if not content:

39 raise SystemExit("Failed to read sysfs file: {}".format(path))

40 return content

41

42

43 def set_power_profile(profile):

44 """

45 Sets the power profile to the specified value.

46 Args:

47 profile (str): The power profile to set (e.g., "power-saver").

48 Raises:

49 SystemExit: If the power profile could not be set.

50 """

51 # In sys file the modes are low-power, balanced, or performance

52 # but powerprofilesctl only accepts power-saver, balanced or performance

53 profile = "power-saver" if profile == "low-power" else profile

54 try:

55 subprocess.check_call(["powerprofilesctl", "set", profile])

56 except subprocess.CalledProcessError as e:

57 raise SystemExit(

58 "Failed to set power mode to {}.".format(profile)

59 ) from e

60

61

62@contextlib.contextmanager

63 def preserve_power_profile():

64 """

65 Rolls back the power profile to the original before calling

66 """

67 sysfs_root = Path("/sys/firmware/acpi/")

68 profile_path = sysfs_root / "platform_profile"

69 old_profile = get_sysfs_content(profile_path)

70 try:

71 yield

72 finally:

73 set_power_profile(old_profile)

74

75

76 def main():

77 """main function to switch the power mode."""

78 sysfs_root = Path("/sys/firmware/acpi/")

79 choices_path = sysfs_root / "platform_profile_choices"

80 profile_path = sysfs_root / "platform_profile"

81

82 # use a context manager to ensure the original power mode is restored

83 with preserve_power_profile():

84

85 choices = get_sysfs_content(choices_path).split()

86

87 print("Power mode choices: {}".format(choices))

88 for choice in choices:

89 set_power_profile(choice)

90 if get_sysfs_content(profile_path) == choice:

91 print("Switch to {} successfully.".format(choice))

92 else:

93 raise SystemExit(

94 "ERROR: Failed to switch power mode to {}".format(choice)

95 )

96

97

98 if __name__ == "__main__":

99 main()