-
Notifications
You must be signed in to change notification settings - Fork 40
feat: Add power profiles service #410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
a0f8fa2
add power profiles service
marzeq 0f54819
Fix formatting
marzeq 95066aa
remove lefover debug statement
marzeq 80197db
fix bug when switching to balanced - properly release hold
marzeq b8fa0a4
by default forcefully set active profile, and only hold if called exp…
marzeq 83b57a6
fix doc strings references
marzeq b05dabd
notify icon_name when ActiveProfile changes
marzeq c772dd4
add is_available property
marzeq f15e2e2
add docs for service in docs/api/services and fix docstring grammar f…
marzeq ee1ade3
make icon_name use a constant that gets formated
marzeq 795c9e4
fix rst format in docs page for service
marzeq e1c3b66
move dbus calls into private methods and throw exception when ppd not…
marzeq b2921ec
update docstring for service class
marzeq 98bb118
return empty list for profiles if not available
marzeq f6652b6
move dbus calls back into public functions
marzeq 67b9a17
error when getting/setting active_profile and service not availible
marzeq bf4e3fd
error when setting profile that does not exist
marzeq File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| Power Profiles | ||
| ============== | ||
|
|
||
| .. autoclass:: ignis.services.power_profiles.PowerProfilesService | ||
| :members: |
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| <node name="/"> | ||
| <interface name="org.freedesktop.UPower.PowerProfiles"> | ||
| <method name="HoldProfile"> | ||
| <arg type="s" name="profile" direction="in"> | ||
| </arg> | ||
| <arg type="s" name="reason" direction="in"> | ||
| </arg> | ||
| <arg type="s" name="application_id" direction="in"> | ||
| </arg> | ||
| <arg type="u" name="cookie" direction="out"> | ||
| </arg> | ||
| </method> | ||
| <method name="ReleaseProfile"> | ||
| <arg type="u" name="cookie" direction="in"> | ||
| </arg> | ||
| </method> | ||
| <method name="SetActionEnabled"> | ||
| <arg type="s" name="action" direction="in"> | ||
| </arg> | ||
| <arg type="b" name="enabled" direction="in"> | ||
| </arg> | ||
| </method> | ||
| <signal name="ProfileReleased"> | ||
| <arg type="u" name="cookie"> | ||
| </arg> | ||
| </signal> | ||
| <property type="s" name="ActiveProfile" access="readwrite"> | ||
| </property> | ||
| <property type="s" name="PerformanceInhibited" access="read"> | ||
| </property> | ||
| <property type="s" name="PerformanceDegraded" access="read"> | ||
| </property> | ||
| <property type="aa{sv}" name="Profiles" access="read"> | ||
| </property> | ||
| <property type="as" name="Actions" access="read"> | ||
| </property> | ||
| <property type="aa{sv}" name="ActionsInfo" access="read"> | ||
| </property> | ||
| <property type="aa{sv}" name="ActiveProfileHolds" access="read"> | ||
| </property> | ||
| <property type="s" name="Version" access="read"> | ||
| </property> | ||
| <property type="b" name="BatteryAware" access="readwrite"> | ||
| </property> | ||
| </interface> | ||
| </node> |
This file contains hidden or 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
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from .service import PowerProfilesService | ||
|
|
||
| __all__ = [ | ||
| "PowerProfilesService", | ||
| ] |
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| PP_ICON_TEMPLATE = "power-profile-{}-symbolic" |
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| from __future__ import annotations | ||
| from ignis.base_service import BaseService | ||
| from ignis.dbus import DBusProxy | ||
| from ignis.exceptions import PowerProfilesDaemonNotRunningError | ||
| from ignis.gobject import IgnisProperty | ||
| from gi.repository import GLib # type: ignore | ||
| from ignis import utils | ||
| from .constants import PP_ICON_TEMPLATE | ||
|
|
||
|
|
||
| class PowerProfilesService(BaseService): | ||
| """ | ||
| A service for managing power profiles through the DBus interface power-profiles-daemon provides. | ||
|
|
||
| Example usage: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| from ignis.services.power_profiles import PowerProfilesService | ||
|
|
||
| power_profiles = PowerProfilesService.get_default() | ||
|
|
||
| print(power_profiles.active_profile) | ||
| power_profiles.active_profile = "performance" | ||
|
|
||
| for profile in power_profiles.profiles: | ||
| print(profile) | ||
|
|
||
| power_profiles.connect("notify::active-profile", lambda x, y: print(power_profiles.active_profile)) | ||
| """ | ||
|
|
||
| def __init__(self) -> None: | ||
| super().__init__() | ||
|
|
||
| self._proxy = DBusProxy.new( | ||
| name="org.freedesktop.UPower.PowerProfiles", | ||
| object_path="/org/freedesktop/UPower/PowerProfiles", | ||
| interface_name="org.freedesktop.UPower.PowerProfiles", | ||
| info=utils.load_interface_xml("org.freedesktop.UPower.PowerProfiles"), | ||
| bus_type="system", | ||
| ) | ||
|
|
||
| if not self.is_available: | ||
| return | ||
|
|
||
| self._proxy.gproxy.connect("g-properties-changed", self.__on_properties_changed) | ||
|
|
||
| self._active_profile: str = self._proxy.ActiveProfile | ||
| self._profiles: list[str] = [p["Profile"] for p in self._proxy.Profiles] | ||
| self._cookie = -1 | ||
|
|
||
| @IgnisProperty | ||
| def is_available(self) -> bool: | ||
| """ | ||
| Whether power profiles capability in UPower is available and UPower is running. | ||
|
|
||
| If ``False``, this service will not be functional. | ||
| """ | ||
| return self._proxy.has_owner | ||
|
|
||
| @IgnisProperty | ||
| def active_profile( # type: ignore | ||
| self, | ||
| ) -> str: | ||
| """ | ||
| Current active power profile. | ||
|
|
||
| Should be either of: | ||
| - `performance` | ||
| - `balanced` | ||
| - `power-saver` | ||
| """ | ||
| if not self.is_available: | ||
| raise PowerProfilesDaemonNotRunningError() | ||
| return self._active_profile | ||
marzeq marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| @active_profile.setter | ||
| def active_profile( | ||
| self, | ||
| profile: str, | ||
| ) -> None: | ||
| if not self.is_available: | ||
| raise PowerProfilesDaemonNotRunningError() | ||
| if profile not in self.profiles: | ||
| raise ValueError(f"Profile '{profile}' is not available.") | ||
|
|
||
| self._cookie = -1 | ||
| self._proxy.ActiveProfile = GLib.Variant("s", profile) | ||
|
|
||
| def hold_profile(self, profile: str) -> None: | ||
| """ | ||
| This forces the passed profile (only `performance` or `power-saver`) to be activated until ignis exits, | ||
| :func:`~release_profile` is called, or the :attr:`~active_profile` is changed manually. | ||
|
|
||
| Use if you need to ensure a specific profile is active for a certain amount of time or while | ||
| a specific task is being performed. This way the previous state will not have to be managed by you. | ||
| """ | ||
| if profile not in self.profiles: | ||
| raise ValueError(f"Profile '{profile}' is not available.") | ||
| if profile == "balanced": | ||
| raise ValueError( | ||
| "Cannot hold the balanced profile, only performance or power-saver." | ||
| ) | ||
|
|
||
| if not self.is_available: | ||
| raise PowerProfilesDaemonNotRunningError() | ||
|
|
||
| if self._cookie != -1: | ||
| return | ||
|
|
||
| self._cookie = self._proxy.gproxy.HoldProfile( | ||
| "(sss)", profile, "", "com.github.linkfrg.ignis" | ||
| ) | ||
|
|
||
| def release_profile(self) -> None: | ||
| """ | ||
| Release the hold on the profile | ||
| """ | ||
| if not self.is_available: | ||
| raise PowerProfilesDaemonNotRunningError() | ||
linkfrg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if self._cookie == -1: | ||
| return | ||
|
|
||
| self._proxy.gproxy.ReleaseProfile("(u)", self._cookie) | ||
| self._cookie = -1 | ||
|
|
||
| @IgnisProperty | ||
| def profiles(self) -> list[str]: | ||
| """ | ||
| List of available power profiles. | ||
|
|
||
| Possible values are: | ||
| - `performance` | ||
| - `balanced` | ||
| - `power-saver` | ||
| """ | ||
| if not self.is_available: | ||
| return [] | ||
| return self._profiles | ||
marzeq marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| @IgnisProperty | ||
| def icon_name(self) -> str: | ||
| """ | ||
| Icon name representing the active power profile. | ||
| """ | ||
| return PP_ICON_TEMPLATE.format(self.active_profile) | ||
|
|
||
| def __on_properties_changed(self, _, properties: GLib.Variant, ignored): | ||
| prop_dict = properties.unpack() | ||
|
|
||
| if "ActiveProfile" in prop_dict: | ||
| self._active_profile = prop_dict["ActiveProfile"] | ||
| self.notify("active-profile") | ||
marzeq marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self.notify("icon-name") | ||
| if "Profiles" in prop_dict: | ||
| self._profiles = list(prop_dict["Profiles"].keys()) | ||
| self.notify("profiles") | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.