From 513bf899599e3d02ae8769bc36be44f2ecfef5be Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Wed, 17 Jan 2024 16:31:38 +0100 Subject: [PATCH 1/2] power: Add support for battery charge limiting UPower supports setting the charge limit of a battery to reduce the battery wear by constantly trickle charging. The charge start/end thresholds are configured by hwdb, a user can only enable / disable the configured limits per available battery via a DBus method. As UPower applies the thresholds for us on startup this does not enable the charge limit setting via gnome-settings-daemon. When multiple batteries are provided the charge limit is set on all battery's. Laptops with multiple batteries are these days not a common occurrence to justify presenting them separate in the UI. Closes #2553 --- panels/power/cc-power-panel.c | 98 ++++++++++++++++++++++++++++++++++ panels/power/cc-power-panel.ui | 37 +++++++++++++ 2 files changed, 135 insertions(+) diff --git a/panels/power/cc-power-panel.c b/panels/power/cc-power-panel.c index a17e8490c5..13552dc4ba 100644 --- a/panels/power/cc-power-panel.c +++ b/panels/power/cc-power-panel.c @@ -43,6 +43,7 @@ struct _CcPowerPanel AdwSwitchRow *als_row; AdwDialog *automatic_suspend_dialog; CcListRow *automatic_suspend_row; + AdwPreferencesGroup *battery_charging_section; GtkListBox *battery_listbox; AdwSwitchRow *battery_percentage_row; AdwPreferencesGroup *battery_section; @@ -51,6 +52,8 @@ struct _CcPowerPanel AdwPreferencesGroup *device_section; AdwSwitchRow *dim_screen_row; AdwPreferencesGroup *general_section; + GtkCheckButton *maximize_charge_radio; + GtkCheckButton *preserve_battery_radio; CcNumberRow *power_button_row; GtkListBox *power_profile_listbox; GtkListBox *power_profile_info_listbox; @@ -134,6 +137,64 @@ update_power_saver_low_battery_row_visibility (CcPowerPanel *self) self->power_profiles_proxy && kind == UP_DEVICE_KIND_BATTERY); } +static void +battery_health_radio_changed_cb (CcPowerPanel *self) +{ + guint i; + g_autoptr (GDBusConnection) connection = NULL; + g_autoptr (GVariant) variant = NULL; + g_autoptr (GError) error = NULL; + gboolean enabled; + + enabled = gtk_check_button_get_active (GTK_CHECK_BUTTON (self->preserve_battery_radio)); + g_debug ("Setting preserve battery health enabled %s", enabled ? "on" : "off"); + + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, + cc_panel_get_cancellable (CC_PANEL (self)), + &error); + if (!connection) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("system bus not available: %s", error->message); + return; + } + + for (i = 0; self->devices != NULL && i < self->devices->len; i++) + { + UpDevice *device = (UpDevice*) g_ptr_array_index (self->devices, i); + UpDeviceKind kind; + gboolean is_power_supply = FALSE; + gboolean is_charge_threshold_supported = FALSE; + gboolean is_charge_threshold_enabled = FALSE; + g_object_get (device, + "kind", &kind, + "power-supply", &is_power_supply, + "charge-threshold-supported", &is_charge_threshold_supported, + "charge-threshold-enabled", &is_charge_threshold_enabled, + NULL); + if (kind == UP_DEVICE_KIND_BATTERY && is_power_supply && is_charge_threshold_supported) + { + g_debug ("%s charge limit for %s", enabled ? "Enable": "Disable", up_device_get_object_path (device)); + variant = g_dbus_connection_call_sync (connection, + "org.freedesktop.UPower", + up_device_get_object_path (device), + "org.freedesktop.UPower.Device", + "EnableChargeThreshold", + g_variant_new ("(b)", enabled), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (!variant) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_debug ("Failed to call %s(): %s", "EnableChargeThreshold", error->message); + } + } + } +} + static void up_client_changed (CcPowerPanel *self) { @@ -141,14 +202,19 @@ up_client_changed (CcPowerPanel *self) UpDeviceKind kind; guint n_batteries; gboolean on_ups; + gboolean charge_threshold_supported; + gboolean charge_threshold_enabled; g_autoptr(UpDevice) composite = NULL; empty_listbox (self->battery_listbox); gtk_widget_set_visible (GTK_WIDGET (self->battery_section), FALSE); + gtk_widget_set_visible (GTK_WIDGET (self->battery_charging_section), FALSE); empty_listbox (self->device_listbox); gtk_widget_set_visible (GTK_WIDGET (self->device_section), FALSE); + charge_threshold_supported = FALSE; + charge_threshold_enabled = FALSE; on_ups = FALSE; n_batteries = 0; composite = up_client_get_display_device (self->up_client); @@ -166,6 +232,9 @@ up_client_changed (CcPowerPanel *self) { UpDevice *device = (UpDevice*) g_ptr_array_index (self->devices, i); gboolean is_power_supply = FALSE; + gboolean is_charge_threshold_supported = FALSE; + gboolean is_charge_threshold_enabled = FALSE; + g_object_get (device, "kind", &kind, "power-supply", &is_power_supply, @@ -179,6 +248,18 @@ up_client_changed (CcPowerPanel *self) is_extra_battery = TRUE; g_object_set_data (G_OBJECT (device), "is-main-battery", GINT_TO_POINTER(TRUE)); } + + g_object_get (device, + "charge-threshold-enabled", &is_charge_threshold_enabled, + "charge-threshold-supported", &is_charge_threshold_supported, + NULL); + + /* If any of the batteries support setting charge thresholds show a switch */ + if (is_charge_threshold_supported) + charge_threshold_supported = TRUE; + + if (is_charge_threshold_enabled) + charge_threshold_enabled = TRUE; } } } @@ -227,6 +308,19 @@ up_client_changed (CcPowerPanel *self) } } + if (charge_threshold_supported) + { + if (charge_threshold_enabled) + { + gtk_check_button_set_active (self->preserve_battery_radio, TRUE); + } + else + { + gtk_check_button_set_active (self->maximize_charge_radio, TRUE); + } + gtk_widget_set_visible (GTK_WIDGET (self->battery_charging_section), TRUE); + } + update_power_saver_low_battery_row_visibility (self); } @@ -1163,6 +1257,7 @@ cc_power_panel_class_init (CcPowerPanelClass *klass) gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, als_row); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, automatic_suspend_dialog); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, automatic_suspend_row); + gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_charging_section); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_listbox); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_percentage_row); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, battery_section); @@ -1171,6 +1266,8 @@ cc_power_panel_class_init (CcPowerPanelClass *klass) gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, device_section); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, dim_screen_row); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, general_section); + gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, maximize_charge_radio); + gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, preserve_battery_radio); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_button_row); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_profile_listbox); gtk_widget_class_bind_template_child (widget_class, CcPowerPanel, power_profile_info_listbox); @@ -1184,6 +1281,7 @@ cc_power_panel_class_init (CcPowerPanelClass *klass) gtk_widget_class_bind_template_callback (widget_class, als_row_changed_cb); gtk_widget_class_bind_template_callback (widget_class, keynav_failed_cb); + gtk_widget_class_bind_template_callback (widget_class, battery_health_radio_changed_cb); } static void diff --git a/panels/power/cc-power-panel.ui b/panels/power/cc-power-panel.ui index 57ebe2a00a..fdb51801a5 100644 --- a/panels/power/cc-power-panel.ui +++ b/panels/power/cc-power-panel.ui @@ -25,6 +25,43 @@ + + + Battery Charging + + + + + maximize_charge_radio + _Maximize Charge + Uses all battery capacity. Degrades batteries more quickly. + True + + + center + + + + + + + + + preserve_battery_radio + Preserve Battery _Health + Increases battery longevity by maintaining lower charge levels + True + + + center + maximize_charge_radio + + + + + + + Connected Devices -- GitLab From e3d4131a03f152b5bd44e70b5de6fca56d1ece13 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Thu, 14 Nov 2024 11:29:37 +0100 Subject: [PATCH 2/2] meson: Bump upower version to 1.90.6 Required for the battery preservation MR and so that GNOME limits the battery to 75-80% instead of 60-80%. --- build-aux/flatpak/org.gnome.Settings.Devel.json | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-aux/flatpak/org.gnome.Settings.Devel.json b/build-aux/flatpak/org.gnome.Settings.Devel.json index 3746f43311..7134864e08 100644 --- a/build-aux/flatpak/org.gnome.Settings.Devel.json +++ b/build-aux/flatpak/org.gnome.Settings.Devel.json @@ -322,7 +322,7 @@ { "type" : "git", "url" : "https://gitlab.freedesktop.org/upower/upower.git", - "tag" : "v1.90.2" + "tag" : "v1.90.6" } ] }, diff --git a/meson.build b/meson.build index 6d762ccd7e..495aacd374 100644 --- a/meson.build +++ b/meson.build @@ -191,7 +191,7 @@ gsettings_desktop_dep = dependency('gsettings-desktop-schemas', version: '>= 47. libxml_dep = dependency('libxml-2.0') pulse_dep = dependency('libpulse', version: pulse_req_version) pulse_mainloop_dep = dependency('libpulse-mainloop-glib', version: pulse_req_version) -upower_glib_dep = dependency('upower-glib', version: '>= 0.99.8') +upower_glib_dep = dependency('upower-glib', version: '>= 1.90.6') gudev_dep = dependency('gudev-1.0', version: '>= 232') x11_dep = dependency('x11', version: '>= 1.8') xi_dep = dependency('xi', version: '>= 1.2') -- GitLab