mirror of
https://gitlab.freedesktop.org/upower/upower.git
synced 2026-05-09 02:28:14 +02:00
Add generic UpDeviceBattery base class
This class can handle laptop battery related quirks and estimations.
This commit is contained in:
parent
c6cd9beff3
commit
d672eb1d90
4 changed files with 577 additions and 0 deletions
|
|
@ -31,6 +31,8 @@ upowerd_private = static_library('upowerd-private',
|
|||
'up-daemon.c',
|
||||
'up-device.h',
|
||||
'up-device.c',
|
||||
'up-device-battery.h',
|
||||
'up-device-battery.c',
|
||||
'up-device-list.h',
|
||||
'up-device-list.c',
|
||||
'up-enumerator.c',
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ G_BEGIN_DECLS
|
|||
|
||||
#define UP_DAEMON_UNKNOWN_TIMEOUT 1 /* second */
|
||||
#define UP_DAEMON_UNKNOWN_POLL_TIME 5 /* second */
|
||||
#define UP_DAEMON_ESTIMATE_TIMEOUT 5 /* second */
|
||||
#define UP_DAEMON_SHORT_TIMEOUT 30 /* seconds */
|
||||
#define UP_DAEMON_LONG_TIMEOUT 120 /* seconds */
|
||||
|
||||
|
|
|
|||
481
src/up-device-battery.c
Normal file
481
src/up-device-battery.c
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2022 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "up-constants.h"
|
||||
#include "up-config.h"
|
||||
#include "up-device-battery.h"
|
||||
|
||||
/* Chosen to be quite big, in case there was a lot of re-polling */
|
||||
#define MAX_ESTIMATION_POINTS 15
|
||||
|
||||
typedef struct {
|
||||
UpBatteryValues hw_data[MAX_ESTIMATION_POINTS];
|
||||
gint hw_data_last;
|
||||
gint hw_data_len;
|
||||
|
||||
gboolean present;
|
||||
gboolean units_changed_warning;
|
||||
|
||||
/* static values (only changed if plugged/unplugged) */
|
||||
gboolean disable_battery_poll;
|
||||
gdouble voltage_design;
|
||||
UpBatteryUnits units;
|
||||
|
||||
/* mostly static values */
|
||||
gdouble energy_full;
|
||||
gdouble energy_full_reported;
|
||||
gdouble energy_design;
|
||||
gint charge_cycles;
|
||||
|
||||
/* dynamic values */
|
||||
gint64 fast_repoll_until;
|
||||
gboolean have_good_estimates;
|
||||
} UpDeviceBatteryPrivate;
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (UpDeviceBattery, up_device_battery, UP_TYPE_DEVICE, 0,
|
||||
G_ADD_PRIVATE (UpDeviceBattery))
|
||||
|
||||
static gboolean
|
||||
up_device_battery_get_on_battery (UpDevice *device, gboolean *on_battery)
|
||||
{
|
||||
UpDeviceState state;
|
||||
|
||||
g_return_val_if_fail (on_battery != NULL, FALSE);
|
||||
|
||||
g_object_get (device,
|
||||
"state", &state,
|
||||
NULL);
|
||||
|
||||
*on_battery = (state == UP_DEVICE_STATE_DISCHARGING);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gdouble
|
||||
up_device_battery_charge_to_energy (UpDeviceBattery *self, gdouble charge)
|
||||
{
|
||||
UpDeviceBatteryPrivate *priv = up_device_battery_get_instance_private (self);
|
||||
|
||||
/* We want to work with energy internally.
|
||||
* Note that this is a pretty bad way of estimating the energy,
|
||||
* we just assume that the voltage is always the same, which is
|
||||
* obviously not true. The voltage depends on at least:
|
||||
* - output current
|
||||
* - temperature
|
||||
* - charge
|
||||
* The easiest way to improve this would likely be "machine learning",
|
||||
* i.e. statistics through which we can calculate the actual
|
||||
* performance based on the factors we have.
|
||||
*/
|
||||
return priv->voltage_design * charge;
|
||||
}
|
||||
|
||||
static void
|
||||
up_device_battery_estimate (UpDeviceBattery *self)
|
||||
{
|
||||
UpDeviceBatteryPrivate *priv = up_device_battery_get_instance_private (self);
|
||||
UpBatteryValues *ref = NULL;
|
||||
UpBatteryValues *cur;
|
||||
gdouble energy_rate = 0.0;
|
||||
gint64 ref_td = 999 * G_USEC_PER_SEC; /* We need to be able to do math with this */
|
||||
gint64 time_to_empty = 0;
|
||||
gint64 time_to_full = 0;
|
||||
gint i;
|
||||
|
||||
g_assert (priv->hw_data_len >= 1);
|
||||
|
||||
priv->have_good_estimates = FALSE;
|
||||
|
||||
cur = &priv->hw_data[priv->hw_data_last];
|
||||
if (cur->state != UP_DEVICE_STATE_CHARGING && cur->state != UP_DEVICE_STATE_DISCHARGING) {
|
||||
priv->have_good_estimates = TRUE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 1; i < priv->hw_data_len; i++) {
|
||||
int pos = (priv->hw_data_last - i + G_N_ELEMENTS (priv->hw_data)) % G_N_ELEMENTS (priv->hw_data);
|
||||
gint64 td;
|
||||
|
||||
/* Stop searching if the state changed. */
|
||||
if (priv->hw_data[pos].state != cur->state)
|
||||
break;
|
||||
|
||||
td = cur->ts_us - priv->hw_data[pos].ts_us;
|
||||
/* At least 15 seconds worth of data. */
|
||||
if (td < 15 * G_USEC_PER_SEC)
|
||||
continue;
|
||||
|
||||
/* Stop searching if the new reference is further away from the long timeout. */
|
||||
if (abs(UP_DAEMON_LONG_TIMEOUT * G_USEC_PER_SEC - abs (td)) > abs(UP_DAEMON_SHORT_TIMEOUT * G_USEC_PER_SEC - ref_td))
|
||||
break;
|
||||
|
||||
ref_td = td;
|
||||
ref = &priv->hw_data[pos];
|
||||
}
|
||||
|
||||
/* We rely solely on battery reports here, with dynamic power
|
||||
* usage (in particular during resume), lets just wait for a
|
||||
* bit longer before reporting anything to the user.
|
||||
*
|
||||
* Alternatively, we could assume that some old estimate for the
|
||||
* energy rate remains stable and do a time estimate based on that.
|
||||
*
|
||||
* For now, this is better than what we used to do.
|
||||
*/
|
||||
if (!ref)
|
||||
goto out;
|
||||
|
||||
/* energy is in Wh, rate in W */
|
||||
energy_rate = (cur->energy.cur - ref->energy.cur) / (ref_td / ((gdouble) 3600 * G_USEC_PER_SEC));
|
||||
|
||||
/* The rate is defined to be positive during both charge and discharge. */
|
||||
if (cur->state == UP_DEVICE_STATE_DISCHARGING)
|
||||
energy_rate *= -1.0;
|
||||
|
||||
/* This hopefully gives us sane values, but lets print a message if not. */
|
||||
if (energy_rate < 0.1 || energy_rate > 300) {
|
||||
g_message ("The estimated %scharge rate is %fW, which is not realistic",
|
||||
cur->state == UP_DEVICE_STATE_DISCHARGING ? "dis" : "",
|
||||
energy_rate);
|
||||
energy_rate = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Here we could factor in collected data about charge rates */
|
||||
/* FIXME: Use charge-stop-threshold here */
|
||||
if (cur->state == UP_DEVICE_STATE_CHARGING)
|
||||
time_to_full = 3600 * (priv->energy_full - cur->energy.cur) / energy_rate;
|
||||
else
|
||||
time_to_empty = 3600 * cur->energy.cur / energy_rate;
|
||||
|
||||
priv->have_good_estimates = TRUE;
|
||||
|
||||
out:
|
||||
g_object_set (self,
|
||||
"energy-rate", energy_rate,
|
||||
"time-to-empty", time_to_empty,
|
||||
"time-to-full", time_to_full,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
up_device_battery_update_poll_frequency (UpDeviceBattery *self,
|
||||
UpDeviceState state,
|
||||
UpRefreshReason reason)
|
||||
{
|
||||
UpDeviceBatteryPrivate *priv = up_device_battery_get_instance_private (self);
|
||||
gint slow_poll_timeout;
|
||||
|
||||
if (priv->disable_battery_poll)
|
||||
return;
|
||||
|
||||
slow_poll_timeout = priv->have_good_estimates ? UP_DAEMON_SHORT_TIMEOUT : UP_DAEMON_ESTIMATE_TIMEOUT;
|
||||
|
||||
/* We start fast-polling if the reason to update was not a normal POLL
|
||||
* and one of the following holds true:
|
||||
* 1. The current stat is unknown; we hope that this is transient
|
||||
* and re-poll.
|
||||
* 2. A change occured on a line power supply. This likely means that
|
||||
* batteries switch between charging/discharging which does not
|
||||
* always result in a separate uevent.
|
||||
*
|
||||
* For simplicity, we do the fast polling for a specific period of time.
|
||||
* If the reason to do fast-polling was an unknown state, then it would
|
||||
* also be reasonable to stop as soon as we got a proper state.
|
||||
*/
|
||||
if (reason != UP_REFRESH_POLL &&
|
||||
(state == UP_DEVICE_STATE_UNKNOWN ||
|
||||
reason == UP_REFRESH_LINE_POWER)) {
|
||||
g_debug ("unknown_poll: setting up fast re-poll");
|
||||
g_object_set (self, "poll-timeout", UP_DAEMON_UNKNOWN_TIMEOUT, NULL);
|
||||
priv->fast_repoll_until = g_get_monotonic_time () + UP_DAEMON_UNKNOWN_POLL_TIME * G_USEC_PER_SEC;
|
||||
|
||||
} else if (priv->fast_repoll_until == 0) {
|
||||
/* Not fast-repolling, check poll timeout is as expected */
|
||||
gint poll_timeout;
|
||||
g_object_get (self, "poll-timeout", &poll_timeout, NULL);
|
||||
if (poll_timeout != slow_poll_timeout)
|
||||
g_object_set (self, "poll-timeout", slow_poll_timeout, NULL);
|
||||
|
||||
} else if (priv->fast_repoll_until < g_get_monotonic_time ()) {
|
||||
g_debug ("unknown_poll: stopping fast repoll (giving up)");
|
||||
priv->fast_repoll_until = 0;
|
||||
g_object_set (self, "poll-timeout", slow_poll_timeout, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
up_device_battery_report (UpDeviceBattery *self,
|
||||
UpBatteryValues *values,
|
||||
UpRefreshReason reason)
|
||||
{
|
||||
UpDeviceBatteryPrivate *priv = up_device_battery_get_instance_private (self);
|
||||
|
||||
if (!priv->present) {
|
||||
g_warning ("Got a battery report for a battery that is not present");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Discard all old measurements (from before suspend). */
|
||||
if (reason == UP_REFRESH_RESUME)
|
||||
priv->hw_data_len = 0;
|
||||
|
||||
g_assert (priv->units != UP_BATTERY_UNIT_UNDEFINED);
|
||||
|
||||
values->ts_us = g_get_monotonic_time ();
|
||||
|
||||
/* QUIRK:
|
||||
*
|
||||
* There is an old bug where some Lenovo machine switched from reporting
|
||||
* energy to reporting charge numbers. The code used to react by
|
||||
* reloading everything, however, what apparently happens is that the
|
||||
* *energy* value simply starts being reported through *charge*
|
||||
* attributes.
|
||||
* The original report is
|
||||
* https://bugzilla.redhat.com/show_bug.cgi?id=587112
|
||||
* and inspecting the numbers it is clear that the values are
|
||||
* really energy values that are unrealistically high as they get
|
||||
* incorrectly multiplied by the voltage.
|
||||
*
|
||||
* Said differently, just assuming the units did *not* change should
|
||||
* give us a saner value. Obviously, things will fall appart if upower
|
||||
* is restarted and this should be fixed in the kernel or firmware.
|
||||
*
|
||||
* Unfortunately, the hardware is quite old (X201s) which makes it hard
|
||||
* to even confirm that the bug was not fixed in the kernel or firmware.
|
||||
*
|
||||
* Note that a race condition could be the user swapping the battery
|
||||
* during suspend and us re-polling energy data before noticing that
|
||||
* the battery has changed.
|
||||
*/
|
||||
if (G_UNLIKELY (priv->units != values->units)) {
|
||||
if (!priv->units_changed_warning) {
|
||||
g_warning ("Battery unit type changed, assuming the old unit is still valid. This is likely a firmware or driver issue, please report!");
|
||||
priv->units_changed_warning = TRUE;
|
||||
}
|
||||
values->units = priv->units;
|
||||
}
|
||||
|
||||
if (values->units == UP_BATTERY_UNIT_CHARGE) {
|
||||
values->units = UP_BATTERY_UNIT_ENERGY;
|
||||
values->energy.cur = up_device_battery_charge_to_energy (self, values->charge.cur);
|
||||
values->energy.rate = up_device_battery_charge_to_energy (self, values->charge.rate);
|
||||
}
|
||||
|
||||
/* QUIRK: Discard weird measurements (like a 300W power usage). */
|
||||
if (values->energy.rate > 300)
|
||||
values->energy.rate = 0;
|
||||
|
||||
/* Infer current energy if unknown */
|
||||
if (values->energy.cur < 0.01 && values->percentage > 0)
|
||||
values->energy.cur = priv->energy_full * values->percentage / 100.0;
|
||||
|
||||
/* QUIRK: Increase energy_full if energy.cur is higher */
|
||||
if (values->energy.cur > priv->energy_full) {
|
||||
priv->energy_full = values->energy.cur;
|
||||
g_object_set (self,
|
||||
/* How healthy the battery is (clamp to 100% if it can hold more charge than expected) */
|
||||
"capacity", MIN (priv->energy_full / priv->energy_design * 100.0, 100),
|
||||
"energy-full", priv->energy_full,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* Infer percentage if unknown */
|
||||
if (values->percentage <= 0)
|
||||
values->percentage = values->energy.cur / priv->energy_full * 100;
|
||||
|
||||
/* QUIRK: Some devices keep reporting PENDING_CHARGE even when full */
|
||||
if (values->state == UP_DEVICE_STATE_PENDING_CHARGE && values->percentage >= UP_FULLY_CHARGED_THRESHOLD)
|
||||
values->state = UP_DEVICE_STATE_FULLY_CHARGED;
|
||||
|
||||
/* NOTE: We used to do more for the UNKNOWN state. However, some of the
|
||||
* logic relies on only one battery device to be present. Plus, it
|
||||
* requires knowing the AC state.
|
||||
* Because of this complexity, the decision was made to only do this
|
||||
* type of inferring inside the DisplayDevice. There we can be sure
|
||||
* about the AC state and we only have "one" battery.
|
||||
*/
|
||||
|
||||
/* Push into our ring buffer */
|
||||
priv->hw_data_last = (priv->hw_data_last + 1) % G_N_ELEMENTS (priv->hw_data);
|
||||
priv->hw_data_len = MIN (priv->hw_data_len + 1, G_N_ELEMENTS (priv->hw_data));
|
||||
priv->hw_data[priv->hw_data_last] = *values;
|
||||
|
||||
/* Do estimations */
|
||||
up_device_battery_estimate (self);
|
||||
|
||||
/* Set the main properties (setting "update-time" last) */
|
||||
g_object_set (self,
|
||||
"energy", values->energy.cur,
|
||||
"percentage", values->percentage,
|
||||
"state", values->state,
|
||||
"voltage", values->voltage,
|
||||
"temperature", values->temperature,
|
||||
/* XXX: Move "update-time" updates elsewhere? */
|
||||
"update-time", (guint64) g_get_real_time () / G_USEC_PER_SEC,
|
||||
NULL);
|
||||
|
||||
up_device_battery_update_poll_frequency (self, values->state, reason);
|
||||
}
|
||||
|
||||
void
|
||||
up_device_battery_update_info (UpDeviceBattery *self, UpBatteryInfo *info)
|
||||
{
|
||||
UpDeviceBatteryPrivate *priv = up_device_battery_get_instance_private (self);
|
||||
|
||||
/* First, sanitize the information. */
|
||||
if (info->present && info->units == UP_BATTERY_UNIT_UNDEFINED) {
|
||||
g_warning ("Battery without defined units, assuming unplugged");
|
||||
info->present = FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* Still not present, ignore. */
|
||||
if (!info->present && !priv->present)
|
||||
return;
|
||||
|
||||
/* Emulate an unplug if present but vendor, etc. changed. */
|
||||
if (info->present && info->present == priv->present) {
|
||||
g_autofree gchar *vendor = NULL;
|
||||
g_autofree gchar *model = NULL;
|
||||
g_autofree gchar *serial = NULL;
|
||||
|
||||
g_object_get (self,
|
||||
"vendor", &vendor,
|
||||
"model", &model,
|
||||
"serial", &serial,
|
||||
NULL);
|
||||
if (g_strcmp0 (vendor, info->vendor) != 0 ||
|
||||
g_strcmp0 (model, info->model) != 0 ||
|
||||
g_strcmp0 (serial, info->serial) != 0) {
|
||||
UpBatteryInfo unplugged = { .present = FALSE };
|
||||
up_device_battery_update_info (self, &unplugged);
|
||||
}
|
||||
}
|
||||
|
||||
if (info->present) {
|
||||
gdouble energy_full;
|
||||
gdouble energy_design;
|
||||
gint charge_cycles;
|
||||
|
||||
/* See above, we have a (new) battery plugged in. */
|
||||
if (!priv->present) {
|
||||
g_object_set (self,
|
||||
"is-present", TRUE,
|
||||
"vendor", info->vendor,
|
||||
"model", info->model,
|
||||
"serial", info->serial,
|
||||
"technology", info->technology,
|
||||
"has-history", TRUE,
|
||||
"has-statistics", TRUE,
|
||||
NULL);
|
||||
|
||||
/* FIXME: The history needs to be re-loaded at this
|
||||
* point as the ID may have changed!
|
||||
*/
|
||||
priv->present = TRUE;
|
||||
priv->units = info->units;
|
||||
}
|
||||
|
||||
/* See comment in up_device_battery_report */
|
||||
if (priv->units != info->units && !priv->units_changed_warning) {
|
||||
g_warning ("Battery unit type changed, assuming the old unit is still valid. This is likely a firmware or driver issue, please report!");
|
||||
priv->units_changed_warning = TRUE;
|
||||
}
|
||||
|
||||
priv->voltage_design = info->voltage_design;
|
||||
if (priv->units == UP_BATTERY_UNIT_CHARGE) {
|
||||
energy_full = up_device_battery_charge_to_energy (self, info->charge.full);
|
||||
energy_design = up_device_battery_charge_to_energy (self, info->charge.design);
|
||||
} else {
|
||||
energy_full = info->energy.full;
|
||||
energy_design = info->energy.design;
|
||||
}
|
||||
|
||||
if (energy_full < 0.01)
|
||||
energy_full = energy_design;
|
||||
|
||||
/* Force -1 for unknown value (where 0 is also an unknown value) */
|
||||
charge_cycles = info->charge_cycles > 0 ? info->charge_cycles : -1;
|
||||
|
||||
if (energy_full != priv->energy_full_reported || energy_design != priv->energy_design) {
|
||||
priv->energy_full = energy_full;
|
||||
priv->energy_full_reported = energy_full;
|
||||
priv->energy_design = energy_design;
|
||||
|
||||
g_object_set (self,
|
||||
/* How healthy the battery is (clamp to 100% if it can hold more charge than expected) */
|
||||
"capacity", MIN (priv->energy_full / priv->energy_design * 100.0, 100),
|
||||
"energy-full", priv->energy_full,
|
||||
"energy-full-design", priv->energy_design,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (priv->charge_cycles != charge_cycles) {
|
||||
priv->charge_cycles = charge_cycles;
|
||||
g_object_set (self,
|
||||
"charge-cycles", charge_cycles,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* NOTE: Assume a normal refresh will follow immediately (do not update timestamp). */
|
||||
} else {
|
||||
priv->present = FALSE;
|
||||
priv->hw_data_len = 0;
|
||||
priv->units = UP_BATTERY_UNIT_UNDEFINED;
|
||||
|
||||
g_object_set (self,
|
||||
"is-present", FALSE,
|
||||
"vendor", NULL,
|
||||
"model", NULL,
|
||||
"serial", NULL,
|
||||
"technology", UP_DEVICE_TECHNOLOGY_UNKNOWN,
|
||||
"capacity", (gdouble) 0.0,
|
||||
"energy-full", (gdouble) 0.0,
|
||||
"energy-full-design", (gdouble) 0.0,
|
||||
"charge-cycles", -1,
|
||||
"has-history", FALSE,
|
||||
"has-statistics", FALSE,
|
||||
"update-time", (guint64) g_get_real_time () / G_USEC_PER_SEC,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
up_device_battery_init (UpDeviceBattery *self)
|
||||
{
|
||||
g_object_set (self,
|
||||
"type", UP_DEVICE_KIND_BATTERY,
|
||||
"power-supply", TRUE,
|
||||
"is-rechargeable", TRUE,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
up_device_battery_class_init (UpDeviceBatteryClass *klass)
|
||||
{
|
||||
UpDeviceClass *device_class = UP_DEVICE_CLASS (klass);
|
||||
|
||||
device_class->get_on_battery = up_device_battery_get_on_battery;
|
||||
}
|
||||
93
src/up-device-battery.h
Normal file
93
src/up-device-battery.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
*
|
||||
* Copyright (C) 2022 Benjamin Berg <bberg@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "up-device.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define MAX_DISCHARGE_RATE 300
|
||||
|
||||
#define UP_TYPE_DEVICE_BATTERY (up_device_battery_get_type ())
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (UpDeviceBattery, up_device_battery, UP, DEVICE_BATTERY, UpDevice)
|
||||
|
||||
struct _UpDeviceBatteryClass
|
||||
{
|
||||
UpDeviceClass parent_class;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
UP_BATTERY_UNIT_UNDEFINED = 0,
|
||||
UP_BATTERY_UNIT_ENERGY,
|
||||
UP_BATTERY_UNIT_CHARGE,
|
||||
} UpBatteryUnits;
|
||||
|
||||
typedef struct {
|
||||
gint64 ts_us;
|
||||
UpDeviceState state;
|
||||
UpBatteryUnits units;
|
||||
|
||||
union {
|
||||
struct {
|
||||
gdouble cur;
|
||||
gdouble rate;
|
||||
} energy;
|
||||
struct {
|
||||
gdouble cur;
|
||||
gdouble rate;
|
||||
} charge;
|
||||
};
|
||||
gdouble percentage;
|
||||
gdouble voltage;
|
||||
gdouble temperature;
|
||||
} UpBatteryValues;
|
||||
|
||||
typedef struct {
|
||||
gboolean present;
|
||||
|
||||
const char *vendor;
|
||||
const char *model;
|
||||
const char *serial;
|
||||
|
||||
UpBatteryUnits units;
|
||||
|
||||
union {
|
||||
struct {
|
||||
gdouble full;
|
||||
gdouble design;
|
||||
} energy;
|
||||
struct {
|
||||
gdouble full;
|
||||
gdouble design;
|
||||
} charge;
|
||||
};
|
||||
|
||||
UpDeviceTechnology technology;
|
||||
gdouble voltage_design;
|
||||
gint charge_cycles;
|
||||
} UpBatteryInfo;
|
||||
|
||||
|
||||
void up_device_battery_update_info (UpDeviceBattery *self, UpBatteryInfo *info);
|
||||
void up_device_battery_report (UpDeviceBattery *self, UpBatteryValues *values, UpRefreshReason reason);
|
||||
|
||||
G_END_DECLS
|
||||
Loading…
Add table
Reference in a new issue