Diff between f5043771c27b5d9c19b1415e4e4c1eb5f7d470f8 and c5d4a041483c52f16f9f47adae41225d8ad90595

Changed Files

File Additions Deletions Status
Makefile.am +2 -1 modified
src/battery.c +9 -1 modified
src/shared/battery.c +96 -0 added
src/shared/battery.h +21 -0 added

Full Patch

diff --git a/Makefile.am b/Makefile.am
index e8b31a5..8e7721b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -244,7 +244,8 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
 			src/shared/ccp.h src/shared/ccp.c \
 			src/shared/lc3.h src/shared/tty.h \
 			src/shared/bap-defs.h \
-			src/shared/asha.h src/shared/asha.c
+			src/shared/asha.h src/shared/asha.c \
+			src/shared/battery.h src/shared/battery.c
 
 if READLINE
 shared_sources += src/shared/shell.c src/shared/shell.h
diff --git a/src/battery.c b/src/battery.c
index 4c1ea79..fa30fde 100644
--- a/src/battery.c
+++ b/src/battery.c
@@ -19,6 +19,7 @@
 
 #include "gdbus/gdbus.h"
 #include "bluetooth/bluetooth.h"
+#include "src/shared/battery.h"
 #include "src/shared/queue.h"
 #include "src/shared/util.h"
 #include "battery.h"
@@ -39,6 +40,7 @@ struct btd_battery {
 	uint8_t percentage; /* valid between 0 to 100 inclusively */
 	char *source; /* Descriptive source of the battery info */
 	char *provider_path; /* The provider root path, if any */
+	struct bt_battery *filter;
 };
 
 struct btd_battery_provider_manager {
@@ -96,6 +98,7 @@ static struct btd_battery *battery_new(const char *path, const char *source,
 		battery->source = g_strdup(source);
 	if (provider_path)
 		battery->provider_path = g_strdup(provider_path);
+	battery->filter = bt_battery_new();
 
 	return battery;
 }
@@ -108,6 +111,11 @@ static void battery_free(struct btd_battery *battery)
 	if (battery->source)
 		g_free(battery->source);
 
+	if (battery->filter) {
+		bt_battery_free(battery->filter);
+		free(battery->filter);
+	}
+
 	free(battery);
 }
 
@@ -234,7 +242,7 @@ bool btd_battery_update(struct btd_battery *battery, uint8_t percentage)
 	if (battery->percentage == percentage)
 		return true;
 
-	battery->percentage = percentage;
+	battery->percentage = bt_battery_charge(battery->filter, percentage);
 	g_dbus_emit_property_changed(btd_get_dbus_connection(), battery->path,
 				     BATTERY_INTERFACE, "Percentage");
 
diff --git a/src/shared/battery.c b/src/shared/battery.c
new file mode 100644
index 0000000..33cec3a
--- /dev/null
+++ b/src/shared/battery.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2025  Open Mobile Platform LLC <community@omp.ru>
+ *
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "src/shared/battery.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+
+struct bt_battery {
+	struct queue *last_charges; /* last charges received */
+	uint8_t avg_charge; /* average battery charge */
+	bool is_fluctuating; /* true, if the battery sensor fluctuates */
+};
+
+struct bt_battery *bt_battery_new(void)
+{
+	struct bt_battery *battery;
+
+	battery = new0(struct bt_battery, 1);
+	battery->last_charges = queue_new();
+	battery->avg_charge = 0;
+	battery->is_fluctuating = false;
+
+	return battery;
+}
+
+void bt_battery_free(struct bt_battery *battery)
+{
+	if (battery->last_charges)
+		queue_destroy(battery->last_charges, NULL);
+}
+
+static void bt_battery_check_fluctuations(struct bt_battery *battery)
+{
+	const struct queue_entry *entry;
+	uint8_t spikes = 0;
+	int8_t step;
+	int8_t direction = 0;
+	int8_t prev_direction;
+	uintptr_t prev_charge;
+	uintptr_t next_charge = 0;
+	uint16_t sum_charge = 0;
+
+	for (entry = queue_get_entries(battery->last_charges); entry->next;
+	     entry = entry->next) {
+		prev_direction = direction;
+		prev_charge = PTR_TO_UINT(entry->data);
+		next_charge = PTR_TO_UINT(entry->next->data);
+		step = next_charge - prev_charge;
+		sum_charge += prev_charge;
+
+		/*
+		 * The battery charge fluctuates too much,
+		 * which may indicate a battery problem, so
+		 * the actual value should be displayed.
+		 */
+		if (abs(step) >= MAX_CHARGE_STEP) {
+			battery->is_fluctuating = false;
+			return;
+		}
+
+		if (step > 0)
+			direction = 1;
+		else if (step < 0)
+			direction = -1;
+
+		if (direction != prev_direction && prev_direction)
+			spikes++;
+	}
+
+	sum_charge += next_charge;
+	battery->avg_charge = sum_charge / LAST_CHARGES_SIZE;
+
+	battery->is_fluctuating = (spikes > 1) ? true : false;
+}
+
+uint8_t bt_battery_charge(struct bt_battery *battery, uint8_t percentage)
+{
+	queue_push_tail(battery->last_charges, UINT_TO_PTR(percentage));
+
+	if (queue_length(battery->last_charges) == LAST_CHARGES_SIZE) {
+		bt_battery_check_fluctuations(battery);
+		queue_pop_head(battery->last_charges);
+	}
+
+	return (battery->is_fluctuating) ? battery->avg_charge : percentage;
+}
diff --git a/src/shared/battery.h b/src/shared/battery.h
new file mode 100644
index 0000000..457acbc
--- /dev/null
+++ b/src/shared/battery.h
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2025  Open Mobile Platform LLC <community@omp.ru>
+ *
+ *
+ */
+
+#include <stdint.h>
+
+#define LAST_CHARGES_SIZE 8
+#define MAX_CHARGE_STEP 5
+
+struct bt_battery;
+
+struct bt_battery *bt_battery_new(void);
+void bt_battery_free(struct bt_battery *battery);
+
+uint8_t bt_battery_charge(struct bt_battery *battery, uint8_t percentage);