diff --git a/README.md b/README.md
index 143fb0e..aa4b079 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,25 @@ Actions can be enabled or disabled by running:
powerprofilesctl configure-action --disable
```
+## Dynamic profile and action changes
+
+Some actions and profiles have the ability to respond to changes to the battery
+level and state of the AC adapter on the system. These change events are triggered
+by upower.
+
+If you would prefer not let any of these dynamic changes happen you can disable
+upower support by running:
+
+ ```sh
+ powerprofilesctl configure-battery-aware --disable
+ ```
+
+If disabled, it can be re-renabled using:
+
+ ```sh
+ powerprofilesctl configure-battery-aware --enable
+ ```
+
## Conflicts
If `power-profiles-daemon` refuses to start, it's likely that you have [a conflicting
diff --git a/data/power-profiles-daemon.policy b/data/power-profiles-daemon.policy
index 732d6eb..2de9bab 100644
--- a/data/power-profiles-daemon.policy
+++ b/data/power-profiles-daemon.policy
@@ -38,4 +38,14 @@
+
+ Configure action
+ Privileges are required to configure battery-awareness.
+
+ no
+ no
+ yes
+
+
+
diff --git a/src/org.freedesktop.UPower.PowerProfiles.xml.in b/src/org.freedesktop.UPower.PowerProfiles.xml.in
index 83ea079..fe1a0e5 100644
--- a/src/org.freedesktop.UPower.PowerProfiles.xml.in
+++ b/src/org.freedesktop.UPower.PowerProfiles.xml.in
@@ -166,5 +166,13 @@
-->
+
+
+
diff --git a/src/power-profiles-daemon.c b/src/power-profiles-daemon.c
index fc790a1..01aac72 100644
--- a/src/power-profiles-daemon.c
+++ b/src/power-profiles-daemon.c
@@ -86,6 +86,7 @@ typedef struct {
GPtrArray *actions;
GHashTable *profile_holds;
+ gboolean battery_support;
GDBusProxy *upower_proxy;
GDBusProxy *upower_display_proxy;
gulong upower_watch_id;
@@ -170,6 +171,7 @@ typedef enum {
PROP_DEGRADED = 1 << 4,
PROP_ACTIVE_PROFILE_HOLDS = 1 << 5,
PROP_VERSION = 1 << 6,
+ PROP_UPOWER = 1 << 7,
} PropertiesMask;
#define PROP_ALL (PROP_ACTIVE_PROFILE | \
@@ -178,7 +180,8 @@ typedef enum {
PROP_ACTIONS | \
PROP_DEGRADED | \
PROP_ACTIVE_PROFILE_HOLDS | \
- PROP_VERSION)
+ PROP_VERSION | \
+ PROP_UPOWER)
static gboolean
driver_profile_support (PpdDriver *driver,
@@ -287,6 +290,7 @@ get_legacy_actions_variant (PpdApp *data)
for (i = 0; i < data->actions->len; i++) {
PpdAction *action = g_ptr_array_index (data->actions, i);
+
if (!ppd_action_get_active (action))
continue;
@@ -310,11 +314,11 @@ get_modern_actions_variant (PpdApp *data)
g_variant_builder_init (&asv_builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&asv_builder, "{sv}", "Name",
- g_variant_new_string (ppd_action_get_action_name (action)));
+ g_variant_new_string (ppd_action_get_action_name (action)));
g_variant_builder_add (&asv_builder, "{sv}", "Description",
- g_variant_new_string (ppd_action_get_action_description (action)));
+ g_variant_new_string (ppd_action_get_action_description (action)));
g_variant_builder_add (&asv_builder, "{sv}", "Enabled",
- g_variant_new_boolean (ppd_action_get_active (action)));
+ g_variant_new_boolean (ppd_action_get_active (action)));
g_variant_builder_add (&builder, "a{sv}", &asv_builder);
}
@@ -400,6 +404,10 @@ send_dbus_event_iface (PpdApp *data,
g_variant_builder_add (&props_builder, "{sv}", "Version",
g_variant_new_string (VERSION));
}
+ if (mask & PROP_UPOWER) {
+ g_variant_builder_add (&props_builder, "{sv}", "BatteryAware",
+ g_variant_new_boolean (data->battery_support));
+ }
props_changed = g_variant_new ("(s@a{sv}@as)", iface,
g_variant_builder_end (&props_builder),
@@ -450,6 +458,8 @@ save_configuration (PpdApp *data)
ppd_action_get_active (action));
}
+ g_key_file_set_boolean (data->config, "State", "battery_aware", data->battery_support);
+
if (!g_key_file_save_to_file (data->config, data->config_path, &error))
g_warning ("Could not save configuration file '%s': %s", data->config_path, error->message);
}
@@ -460,8 +470,17 @@ apply_configuration (PpdApp *data)
g_autofree char *platform_driver = NULL;
g_autofree char *profile_str = NULL;
g_autofree char *cpu_driver = NULL;
+ g_autoptr(GError) error = NULL;
PpdProfile profile;
+ data->battery_support = g_key_file_get_boolean (data->config, "State", "battery_aware", &error);
+ if (error != NULL) {
+ g_debug("battery_aware key not found: %s, defaulting to TRUE", error->message);
+ data->battery_support = TRUE;
+ g_key_file_set_boolean (data->config, "State", "battery_aware", TRUE);
+ g_clear_error (&error);
+ }
+
cpu_driver = g_key_file_get_string (data->config, "State", "CpuDriver", NULL);
if (PPD_IS_DRIVER_CPU (data->cpu_driver) &&
g_strcmp0 (ppd_driver_get_driver_name (PPD_DRIVER (data->cpu_driver)), cpu_driver) != 0)
@@ -622,6 +641,24 @@ release_all_profile_holds (PpdApp *data)
g_hash_table_remove_all (data->profile_holds);
}
+static gboolean
+set_battery_support (PpdApp *data, gboolean battery_support, GError **error)
+{
+ if (data->battery_support == battery_support) {
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "battery_aware is already set to %s", battery_support ? "TRUE" : "FALSE");
+ return FALSE;
+ }
+
+ data->battery_support = battery_support;
+ g_key_file_set_boolean (data->config, "State", "battery_aware", data->battery_support);
+ g_debug("battery_aware set to %s", data->battery_support ? "TRUE" : "FALSE");
+
+ restart_profile_drivers_for_default_app ();
+
+ return TRUE;
+}
+
static gboolean
set_active_profile (PpdApp *data,
const char *profile,
@@ -919,6 +956,8 @@ handle_get_property (GDBusConnection *connection,
return get_modern_actions_variant (data);
if (g_str_equal (property_name, "Actions"))
return get_legacy_actions_variant (data);
+ if (g_str_equal (property_name, "BatteryAware"))
+ return g_variant_new_boolean (data->battery_support);
if (g_strcmp0 (property_name, "PerformanceDegraded") == 0) {
gchar *degraded = get_performance_degraded (data);
return g_variant_new_take_string (g_steal_pointer (°raded));
@@ -943,22 +982,33 @@ handle_set_property (GDBusConnection *connection,
gpointer user_data)
{
PpdApp *data = user_data;
- const char *profile;
g_return_val_if_fail (data->connection, FALSE);
- if (g_strcmp0 (property_name, "ActiveProfile") != 0) {
- g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
- "No such property: %s", property_name);
- return FALSE;
- }
- if (!check_action_permission (data, sender,
- POWER_PROFILES_POLICY_NAMESPACE ".switch-profile",
- error))
- return FALSE;
+ if (g_str_equal (property_name, "ActiveProfile")) {
+ const char *profile;
- g_variant_get (value, "&s", &profile);
- return set_active_profile (data, profile, error);
+ if (!check_action_permission (data, sender,
+ POWER_PROFILES_POLICY_NAMESPACE ".switch-profile",
+ error))
+ return FALSE;
+
+ g_variant_get (value, "&s", &profile);
+ return set_active_profile (data, profile, error);
+ } else if (g_str_equal (property_name, "BatteryAware")) {
+ if (!check_action_permission (data,
+ sender,
+ POWER_PROFILES_POLICY_NAMESPACE ".configure-battery-aware",
+ error)) {
+ return FALSE;
+ }
+
+ return set_battery_support (data, g_variant_get_boolean (value), error);
+ }
+
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "No such property: %s", property_name);
+ return FALSE;
}
static gboolean set_action_enabled (PpdApp *data,
@@ -1018,12 +1068,14 @@ handle_method_call (GDBusConnection *connection,
release_profile (data, parameters, invocation);
} else if (g_strcmp0 (method_name, "SetActionEnabled") == 0) {
g_autoptr(GError) local_error = NULL;
+
if (g_str_equal (interface_name, POWER_PROFILES_LEGACY_IFACE_NAME)) {
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
"Method %s is not available in interface %s", method_name,
interface_name);
return;
}
+
if (!check_action_permission (data,
g_dbus_method_invocation_get_sender (invocation),
POWER_PROFILES_POLICY_NAMESPACE ".configure-action",
@@ -1031,6 +1083,7 @@ handle_method_call (GDBusConnection *connection,
g_dbus_method_invocation_return_gerror (invocation, local_error);
return;
}
+
if (!set_action_enabled (data, parameters, &local_error)) {
g_dbus_method_invocation_return_gerror (invocation, local_error);
return;
@@ -1671,7 +1724,10 @@ start_profile_drivers (PpdApp *data)
send_dbus_event (data, PROP_ALL);
data->was_started = TRUE;
- if (data->debug_options->disable_upower) {
+ if (data->debug_options->disable_upower)
+ data->battery_support = FALSE;
+
+ if (!data->battery_support) {
g_debug ("upower is disabled, let's skip it");
} else if (needs_battery_state_monitor || needs_battery_change_monitor) {
/* start watching for power changes */
diff --git a/src/powerprofilesctl b/src/powerprofilesctl
index 40a2421..db3e35a 100755
--- a/src/powerprofilesctl
+++ b/src/powerprofilesctl
@@ -47,6 +47,14 @@ def _version(_args):
print(f"client: {client_version}\ndaemon: {daemon_ver}")
+@command
+def _set_profile(args):
+ proxy = get_proxy()
+ proxy.Set(
+ "(ssv)", PP_IFACE, "ActiveProfile", GLib.Variant.new_string(args.profile[0])
+ )
+
+
@command
def _get(_args):
proxy = get_proxy()
@@ -55,11 +63,16 @@ def _get(_args):
@command
-def _set(args):
+def _set_battery_aware(args):
+ enable = args.enable
+ disable = args.disable
+ if enable is False and disable is True:
+ raise ValueError("enable or disable is required")
+ if enable is True and disable is False:
+ raise ValueError("can't set both enable and disable")
+ enable = enable if enable is not None else not disable
proxy = get_proxy()
- proxy.Set(
- "(ssv)", PP_IFACE, "ActiveProfile", GLib.Variant.new_string(args.profile[0])
- )
+ proxy.Set("(ssv)", PP_IFACE, "BatteryAware", GLib.Variant.new_boolean(enable))
def get_profiles_property(prop):
@@ -166,6 +179,12 @@ def _launch(args):
sys.exit(ret)
+@command
+def _query_battery_aware(_args):
+ result = get_profiles_property("BatteryAware")
+ print(f"Dynamic changes from charger and battery events: {result}")
+
+
@command
def _list_actions(_args):
actions = get_profiles_property("ActionsInfo")
@@ -225,7 +244,7 @@ def get_parser():
help="Profile to use for set command",
choices=get_profile_choices(),
)
- parser_set.set_defaults(func=_set)
+ parser_set.set_defaults(func=_set_profile)
parser_set_action = subparsers.add_parser(
"configure-action", help="Configure the action to be taken for the profile"
)
@@ -245,6 +264,26 @@ def get_parser():
help="disable action",
)
parser_set_action.set_defaults(func=_configure_action)
+ parser_set_battery_aware = subparsers.add_parser(
+ "configure-battery-aware",
+ help="Turn on or off dynamic changes from battery level or power adapter",
+ )
+ parser_set_battery_aware.add_argument(
+ "--enable",
+ action="store_true",
+ help="enable battery aware",
+ )
+ parser_set_battery_aware.add_argument(
+ "--disable",
+ action="store_false",
+ help="disable battery aware",
+ )
+ parser_set_battery_aware.set_defaults(func=_set_battery_aware)
+ parser_query_battery_aware = subparsers.add_parser(
+ "query-battery-aware",
+ help="Query if dynamic changes from battery level or power adapter are enabled",
+ )
+ parser_query_battery_aware.set_defaults(func=_query_battery_aware)
parser_launch = subparsers.add_parser(
"launch",
help="Launch a command while holding a power profile",
diff --git a/src/ppd-action.c b/src/ppd-action.c
index 1edcb9c..72ed975 100644
--- a/src/ppd-action.c
+++ b/src/ppd-action.c
@@ -144,10 +144,10 @@ ppd_action_class_init (PpdActionClass *klass)
*/
g_object_class_install_property (object_class, PROP_ACTION_DESCRIPTION,
g_param_spec_string ("action-description",
- "Action description",
- "Action description",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ "Action description",
+ "Action description",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/**
* PpdAction::optin:
@@ -159,7 +159,7 @@ ppd_action_class_init (PpdActionClass *klass)
"Opt-in",
"Whether the action is opt-in or not",
FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
static void
diff --git a/tests/integration_test.py b/tests/integration_test.py
index cf30a23..e16e5fb 100644
--- a/tests/integration_test.py
+++ b/tests/integration_test.py
@@ -148,6 +148,7 @@ class Tests(dbusmock.DBusTestCase):
"org.freedesktop.UPower.PowerProfiles.switch-profile",
"org.freedesktop.UPower.PowerProfiles.hold-profile",
"org.freedesktop.UPower.PowerProfiles.configure-action",
+ "org.freedesktop.UPower.PowerProfiles.configure-battery-aware",
]
)
@@ -2483,6 +2484,52 @@ class Tests(dbusmock.DBusTestCase):
self.assertEqual(len(profiles), 3)
self.assertEqual(self.get_dbus_property("PerformanceDegraded"), "")
+ def test_powerprofilesctl_configure_battery_aware_command(self):
+ """Check powerprofilesctl configure-battery-aware command works"""
+
+ self.start_dbus_template(
+ "upower",
+ {"DaemonVersion": "0.99", "OnBattery": False},
+ )
+
+ self.start_daemon()
+
+ # verify argument is required
+ cmd = subprocess.run(
+ self.powerprofilesctl_command() + ["configure-battery-aware"],
+ capture_output=True,
+ check=False,
+ )
+ self.assertEqual(cmd.returncode, 1)
+
+ # check possible arguments
+ for key, value in {"--disable": "False", "--enable": "True"}.items():
+ cmd = subprocess.run(
+ self.powerprofilesctl_command() + ["configure-battery-aware", key],
+ capture_output=True,
+ check=True,
+ )
+ self.assertEqual(cmd.returncode, 0)
+
+ cmd = subprocess.run(
+ self.powerprofilesctl_command() + ["query-battery-aware"],
+ capture_output=True,
+ check=True,
+ )
+ self.assertEqual(cmd.returncode, 0)
+ self.assertIn(
+ f"Dynamic changes from charger and battery events: {value}",
+ cmd.stdout.decode("utf-8"),
+ )
+
+ # make sure can't be enabled twice
+ cmd = subprocess.run(
+ self.powerprofilesctl_command() + ["configure-battery-aware", "--enable"],
+ capture_output=True,
+ check=False,
+ )
+ self.assertEqual(cmd.returncode, 1)
+
def test_powerprofilesctl_configure_action_command(self):
"""Check powerprofilesctl configure-action command works"""