Expose ChargeType and PercentageTrusted on Device
Stock UPower maps the kernel power_supply 'status' to Device:State but drops 'charge_type' entirely, so the entire fast/trickle/taper distinction is invisible over D-Bus. A device reads 'Charging' the whole constant-voltage tail, which is how a lock surface ends up showing 'Charging N%' forever on a topped-off pack. Add ChargeType (mirroring POWER_SUPPLY_CHARGE_TYPE_*) as a separate uint property, read from sysfs 'charge_type' next to status. Also add PercentageTrusted: capacity is derived from charge_full/energy_full, and a driver change reporting wrong units makes a healthy pack read ~1%. Mark it untrusted when full is implausibly small vs design so consumers can suppress the meaningless number. Generated skeleton, lib props, Linux backend read, and introspection XML all wired. Builds clean, self-test passes.
This commit is contained in:
parent
3dc5323bf9
commit
5aae6d4c22
8 changed files with 307 additions and 1 deletions
|
|
@ -711,6 +711,76 @@ method return sender=:1.386 -> dest=:1.477 reply_serial=2
|
|||
</doc:doc>
|
||||
</property>
|
||||
|
||||
<property name="ChargeType" type="u" access="read">
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
<doc:para>
|
||||
The charging regime the charger is currently applying, as
|
||||
reported by the kernel <doc:tt>power_supply</doc:tt>
|
||||
<doc:tt>charge_type</doc:tt> attribute. This is distinct from
|
||||
<doc:ref type="property" to="Device:State">State</doc:ref>:
|
||||
a device may be <doc:tt>Charging</doc:tt> (state 1) while the
|
||||
regime moves from <doc:tt>Fast</doc:tt> to
|
||||
<doc:tt>Trickle</doc:tt> (the constant-voltage taper) over
|
||||
the cycle.
|
||||
</doc:para>
|
||||
<doc:list>
|
||||
<doc:item>
|
||||
<doc:term>0</doc:term><doc:definition>Unknown</doc:definition>
|
||||
</doc:item>
|
||||
<doc:item>
|
||||
<doc:term>1</doc:term><doc:definition>None (no charger active, e.g. discharging)</doc:definition>
|
||||
</doc:item>
|
||||
<doc:item>
|
||||
<doc:term>2</doc:term><doc:definition>Trickle</doc:definition>
|
||||
</doc:item>
|
||||
<doc:item>
|
||||
<doc:term>3</doc:term><doc:definition>Fast</doc:definition>
|
||||
</doc:item>
|
||||
<doc:item>
|
||||
<doc:term>4</doc:term><doc:definition>Standard</doc:definition>
|
||||
</doc:item>
|
||||
<doc:item>
|
||||
<doc:term>5</doc:term><doc:definition>Adaptive</doc:definition>
|
||||
</doc:item>
|
||||
<doc:item>
|
||||
<doc:term>6</doc:term><doc:definition>Custom</doc:definition>
|
||||
</doc:item>
|
||||
<doc:item>
|
||||
<doc:term>7</doc:term><doc:definition>Long Life</doc:definition>
|
||||
</doc:item>
|
||||
<doc:item>
|
||||
<doc:term>8</doc:term><doc:definition>Bypass</doc:definition>
|
||||
</doc:item>
|
||||
</doc:list>
|
||||
<doc:para>
|
||||
This property is only valid if the property
|
||||
<doc:ref type="property" to="Source:Type">type</doc:ref>
|
||||
has the value "battery". Not all drivers expose
|
||||
<doc:tt>charge_type</doc:tt>; in that case the value is
|
||||
<doc:tt>Unknown</doc:tt>.
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
</doc:doc>
|
||||
</property>
|
||||
|
||||
<property name="PercentageTrusted" type="b" access="read">
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
<doc:para>
|
||||
Whether <doc:ref type="property" to="Device:Percentage">Percentage</doc:ref>
|
||||
is considered trustworthy. False when the kernel reports a
|
||||
<doc:tt>charge_full</doc:tt> / <doc:tt>energy_full</doc:tt>
|
||||
that is implausibly small relative to the design capacity
|
||||
(typical after a driver change that reports capacity in the
|
||||
wrong units), in which case the derived percentage is
|
||||
meaningless and consumers should suppress it rather than
|
||||
display a misleading value like "1%".
|
||||
</doc:para>
|
||||
</doc:description>
|
||||
</doc:doc>
|
||||
</property>
|
||||
|
||||
<property name="IsRechargeable" type="b" access="read">
|
||||
<doc:doc>
|
||||
<doc:description>
|
||||
|
|
|
|||
|
|
@ -73,6 +73,8 @@ enum {
|
|||
PROP_HAS_STATISTICS,
|
||||
PROP_KIND,
|
||||
PROP_STATE,
|
||||
PROP_CHARGE_TYPE,
|
||||
PROP_PERCENTAGE_TRUSTED,
|
||||
PROP_TECHNOLOGY,
|
||||
PROP_CAPACITY,
|
||||
PROP_ENERGY,
|
||||
|
|
@ -332,8 +334,11 @@ up_device_to_text (UpDevice *device)
|
|||
kind == UP_DEVICE_KIND_MOUSE ||
|
||||
kind == UP_DEVICE_KIND_KEYBOARD ||
|
||||
kind == UP_DEVICE_KIND_UPS ||
|
||||
kind == UP_DEVICE_KIND_TOUCHPAD)
|
||||
kind == UP_DEVICE_KIND_TOUCHPAD) {
|
||||
g_string_append_printf (string, " state: %s\n", up_device_state_to_string (up_exported_device_get_state (priv->proxy_device)));
|
||||
g_string_append_printf (string, " charge-type: %s\n", up_device_charge_type_to_string (up_exported_device_get_charge_type (priv->proxy_device)));
|
||||
g_string_append_printf (string, " percentage-trusted: %s\n", up_exported_device_get_percentage_trusted (priv->proxy_device) ? "yes" : "no");
|
||||
}
|
||||
g_string_append_printf (string, " warning-level: %s\n", up_device_level_to_string (up_exported_device_get_warning_level (priv->proxy_device)));
|
||||
battery_level = up_exported_device_get_battery_level (priv->proxy_device);
|
||||
if (battery_level != UP_DEVICE_LEVEL_NONE)
|
||||
|
|
@ -676,6 +681,12 @@ up_device_set_property (GObject *object, guint prop_id, const GValue *value, GPa
|
|||
case PROP_STATE:
|
||||
up_exported_device_set_state (device->priv->proxy_device, g_value_get_uint (value));
|
||||
break;
|
||||
case PROP_CHARGE_TYPE:
|
||||
up_exported_device_set_charge_type (device->priv->proxy_device, g_value_get_uint (value));
|
||||
break;
|
||||
case PROP_PERCENTAGE_TRUSTED:
|
||||
up_exported_device_set_percentage_trusted (device->priv->proxy_device, g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_CAPACITY:
|
||||
up_exported_device_set_capacity (device->priv->proxy_device, g_value_get_double (value));
|
||||
break;
|
||||
|
|
@ -823,6 +834,12 @@ up_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpe
|
|||
case PROP_STATE:
|
||||
g_value_set_uint (value, up_exported_device_get_state (device->priv->proxy_device));
|
||||
break;
|
||||
case PROP_CHARGE_TYPE:
|
||||
g_value_set_uint (value, up_exported_device_get_charge_type (device->priv->proxy_device));
|
||||
break;
|
||||
case PROP_PERCENTAGE_TRUSTED:
|
||||
g_value_set_boolean (value, up_exported_device_get_percentage_trusted (device->priv->proxy_device));
|
||||
break;
|
||||
case PROP_TECHNOLOGY:
|
||||
g_value_set_uint (value, up_exported_device_get_technology (device->priv->proxy_device));
|
||||
break;
|
||||
|
|
@ -1096,6 +1113,38 @@ up_device_class_init (UpDeviceClass *klass)
|
|||
UP_DEVICE_STATE_LAST,
|
||||
UP_DEVICE_STATE_UNKNOWN,
|
||||
G_PARAM_READWRITE));
|
||||
/**
|
||||
* UpDevice:charge-type:
|
||||
*
|
||||
* The charging regime the charger is currently applying, e.g.
|
||||
* %UP_DEVICE_CHARGE_TYPE_TRICKLE for the constant-voltage taper.
|
||||
* Distinct from #UpDevice:state.
|
||||
*
|
||||
* Since: 1.91.4
|
||||
**/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_CHARGE_TYPE,
|
||||
g_param_spec_uint ("charge-type",
|
||||
NULL, NULL,
|
||||
UP_DEVICE_CHARGE_TYPE_UNKNOWN,
|
||||
UP_DEVICE_CHARGE_TYPE_LAST,
|
||||
UP_DEVICE_CHARGE_TYPE_UNKNOWN,
|
||||
G_PARAM_READWRITE));
|
||||
/**
|
||||
* UpDevice:percentage-trusted:
|
||||
*
|
||||
* Whether #UpDevice:percentage is considered trustworthy. FALSE when
|
||||
* the kernel-reported full capacity is implausibly small and the
|
||||
* derived percentage is meaningless.
|
||||
*
|
||||
* Since: 1.91.4
|
||||
**/
|
||||
g_object_class_install_property (object_class,
|
||||
PROP_PERCENTAGE_TRUSTED,
|
||||
g_param_spec_boolean ("percentage-trusted",
|
||||
NULL, NULL,
|
||||
TRUE,
|
||||
G_PARAM_READWRITE));
|
||||
/**
|
||||
* UpDevice:technology:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -240,6 +240,82 @@ up_device_state_from_string (const gchar *state)
|
|||
return UP_DEVICE_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_charge_type_to_string:
|
||||
*
|
||||
* Converts a #UpDeviceChargeType to a string. Matches the kernel
|
||||
* `power_supply` `charge_type` spelling where one exists.
|
||||
*
|
||||
* Return value: identifier string
|
||||
*
|
||||
* Since: 1.91.4
|
||||
**/
|
||||
const gchar *
|
||||
up_device_charge_type_to_string (UpDeviceChargeType charge_type_enum)
|
||||
{
|
||||
switch (charge_type_enum) {
|
||||
case UP_DEVICE_CHARGE_TYPE_NONE:
|
||||
return "none";
|
||||
case UP_DEVICE_CHARGE_TYPE_TRICKLE:
|
||||
return "trickle";
|
||||
case UP_DEVICE_CHARGE_TYPE_FAST:
|
||||
return "fast";
|
||||
case UP_DEVICE_CHARGE_TYPE_STANDARD:
|
||||
return "standard";
|
||||
case UP_DEVICE_CHARGE_TYPE_ADAPTIVE:
|
||||
return "adaptive";
|
||||
case UP_DEVICE_CHARGE_TYPE_CUSTOM:
|
||||
return "custom";
|
||||
case UP_DEVICE_CHARGE_TYPE_LONGLIFE:
|
||||
return "long-life";
|
||||
case UP_DEVICE_CHARGE_TYPE_BYPASS:
|
||||
return "bypass";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_charge_type_from_string:
|
||||
*
|
||||
* Converts a sysfs `charge_type` string to a #UpDeviceChargeType.
|
||||
* Spelling follows the kernel `POWER_SUPPLY_CHARGE_TYPE_*` strings
|
||||
* (e.g. "Trickle", "Fast", "Long Life"); matching is case-insensitive
|
||||
* and tolerates the spaces the kernel emits for "Long Life".
|
||||
*
|
||||
* Return value: enumerated value
|
||||
*
|
||||
* Since: 1.91.4
|
||||
**/
|
||||
UpDeviceChargeType
|
||||
up_device_charge_type_from_string (const gchar *charge_type)
|
||||
{
|
||||
if (charge_type == NULL || *charge_type == '\0')
|
||||
return UP_DEVICE_CHARGE_TYPE_NONE;
|
||||
if (g_ascii_strcasecmp (charge_type, "unknown") == 0)
|
||||
return UP_DEVICE_CHARGE_TYPE_UNKNOWN;
|
||||
if (g_ascii_strcasecmp (charge_type, "none") == 0)
|
||||
return UP_DEVICE_CHARGE_TYPE_NONE;
|
||||
if (g_ascii_strcasecmp (charge_type, "trickle") == 0)
|
||||
return UP_DEVICE_CHARGE_TYPE_TRICKLE;
|
||||
if (g_ascii_strcasecmp (charge_type, "fast") == 0)
|
||||
return UP_DEVICE_CHARGE_TYPE_FAST;
|
||||
if (g_ascii_strcasecmp (charge_type, "standard") == 0)
|
||||
return UP_DEVICE_CHARGE_TYPE_STANDARD;
|
||||
if (g_ascii_strcasecmp (charge_type, "adaptive") == 0)
|
||||
return UP_DEVICE_CHARGE_TYPE_ADAPTIVE;
|
||||
if (g_ascii_strcasecmp (charge_type, "custom") == 0)
|
||||
return UP_DEVICE_CHARGE_TYPE_CUSTOM;
|
||||
if (g_ascii_strcasecmp (charge_type, "long life") == 0 ||
|
||||
g_ascii_strcasecmp (charge_type, "long-life") == 0)
|
||||
return UP_DEVICE_CHARGE_TYPE_LONGLIFE;
|
||||
if (g_ascii_strcasecmp (charge_type, "bypass") == 0)
|
||||
return UP_DEVICE_CHARGE_TYPE_BYPASS;
|
||||
g_warning ("unknown charge_type string: %s", charge_type);
|
||||
return UP_DEVICE_CHARGE_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_technology_to_string:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -84,6 +84,34 @@ typedef enum {
|
|||
UP_DEVICE_STATE_LAST
|
||||
} UpDeviceState;
|
||||
|
||||
/**
|
||||
* UpDeviceChargeType:
|
||||
*
|
||||
* The charging regime the charger is currently applying, mirroring the
|
||||
* kernel `POWER_SUPPLY_CHARGE_TYPE_*` values. Distinct from
|
||||
* #UpDeviceState: a device can be `UP_DEVICE_STATE_CHARGING` while the
|
||||
* charge regime moves `FAST` → `TRICKLE` (constant-voltage taper) over
|
||||
* the cycle. Stock UPower collapsed this away; the Souveraine fork
|
||||
* surfaces it so the lock surface can distinguish "charging fast" from
|
||||
* "almost full / topping off" without poking sysfs.
|
||||
*
|
||||
* `UP_DEVICE_CHARGE_TYPE_NONE` is reported when no charger is active
|
||||
* (e.g. discharging); `UP_DEVICE_CHARGE_TYPE_UNKNOWN` when the driver
|
||||
* couldn't report a regime.
|
||||
**/
|
||||
typedef enum {
|
||||
UP_DEVICE_CHARGE_TYPE_UNKNOWN,
|
||||
UP_DEVICE_CHARGE_TYPE_NONE,
|
||||
UP_DEVICE_CHARGE_TYPE_TRICKLE,
|
||||
UP_DEVICE_CHARGE_TYPE_FAST,
|
||||
UP_DEVICE_CHARGE_TYPE_STANDARD,
|
||||
UP_DEVICE_CHARGE_TYPE_ADAPTIVE,
|
||||
UP_DEVICE_CHARGE_TYPE_CUSTOM,
|
||||
UP_DEVICE_CHARGE_TYPE_LONGLIFE,
|
||||
UP_DEVICE_CHARGE_TYPE_BYPASS,
|
||||
UP_DEVICE_CHARGE_TYPE_LAST
|
||||
} UpDeviceChargeType;
|
||||
|
||||
/**
|
||||
* UpDeviceTechnology:
|
||||
*
|
||||
|
|
@ -129,10 +157,12 @@ typedef enum {
|
|||
|
||||
const gchar *up_device_kind_to_string (UpDeviceKind type_enum);
|
||||
const gchar *up_device_state_to_string (UpDeviceState state_enum);
|
||||
const gchar *up_device_charge_type_to_string (UpDeviceChargeType charge_type_enum);
|
||||
const gchar *up_device_technology_to_string (UpDeviceTechnology technology_enum);
|
||||
const gchar *up_device_level_to_string (UpDeviceLevel level_enum);
|
||||
UpDeviceKind up_device_kind_from_string (const gchar *type);
|
||||
UpDeviceState up_device_state_from_string (const gchar *state);
|
||||
UpDeviceChargeType up_device_charge_type_from_string (const gchar *charge_type);
|
||||
UpDeviceTechnology up_device_technology_from_string (const gchar *technology);
|
||||
UpDeviceLevel up_device_level_from_string (const gchar *level);
|
||||
|
||||
|
|
|
|||
BIN
src/linux/__pycache__/integration-test.cpython-314.pyc
Normal file
BIN
src/linux/__pycache__/integration-test.cpython-314.pyc
Normal file
Binary file not shown.
BIN
src/linux/__pycache__/output_checker.cpython-314.pyc
Normal file
BIN
src/linux/__pycache__/output_checker.cpython-314.pyc
Normal file
Binary file not shown.
|
|
@ -198,6 +198,68 @@ up_device_supply_get_state (GUdevDevice *native)
|
|||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_supply_get_charge_type:
|
||||
*
|
||||
* Reads the kernel `charge_type` sysfs attribute and maps it to a
|
||||
* #UpDeviceChargeType. Returns %UP_DEVICE_CHARGE_TYPE_UNKNOWN if the
|
||||
* driver doesn't expose the attribute, and
|
||||
* %UP_DEVICE_CHARGE_TYPE_NONE for an empty/unset value (no charger
|
||||
* active).
|
||||
**/
|
||||
UpDeviceChargeType
|
||||
up_device_supply_get_charge_type (GUdevDevice *native)
|
||||
{
|
||||
gchar *charge_type_str;
|
||||
UpDeviceChargeType charge_type;
|
||||
|
||||
charge_type_str = up_device_supply_get_string (native, "charge_type");
|
||||
if (charge_type_str == NULL)
|
||||
return UP_DEVICE_CHARGE_TYPE_UNKNOWN;
|
||||
|
||||
charge_type = up_device_charge_type_from_string (charge_type_str);
|
||||
g_free (charge_type_str);
|
||||
|
||||
return charge_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_supply_percentage_is_trusted:
|
||||
*
|
||||
* Heuristic: percentage (derived from charge_full / energy_full) is
|
||||
* untrusted when the reported full capacity is implausibly small
|
||||
* relative to the design capacity. This happens when a driver reports
|
||||
* capacity in the wrong units after a change (e.g. µWh where µAh is
|
||||
* expected), making a healthy pack read ~1%. We treat anything under
|
||||
* 10% of design — or a missing/zero full where design is nonzero — as
|
||||
* untrusted.
|
||||
**/
|
||||
static gboolean
|
||||
up_device_supply_percentage_is_trusted (GUdevDevice *native)
|
||||
{
|
||||
gdouble full = -1.0, design = -1.0;
|
||||
|
||||
if (g_udev_device_has_sysfs_attr_uncached (native, "charge_full"))
|
||||
full = g_udev_device_get_sysfs_attr_as_double_uncached (native, "charge_full");
|
||||
if (full < 0.0 && g_udev_device_has_sysfs_attr_uncached (native, "energy_full"))
|
||||
full = g_udev_device_get_sysfs_attr_as_double_uncached (native, "energy_full");
|
||||
|
||||
if (g_udev_device_has_sysfs_attr_uncached (native, "charge_full_design"))
|
||||
design = g_udev_device_get_sysfs_attr_as_double_uncached (native, "charge_full_design");
|
||||
if (design < 0.0 && g_udev_device_has_sysfs_attr_uncached (native, "energy_full_design"))
|
||||
design = g_udev_device_get_sysfs_attr_as_double_uncached (native, "energy_full_design");
|
||||
|
||||
/* No design figure to compare against → trust the driver. */
|
||||
if (design <= 0.0)
|
||||
return TRUE;
|
||||
|
||||
/* Full reads zero/absurd while design is sane → untrusted. */
|
||||
if (full <= 0.0 || (full / design) < 0.10)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gdouble
|
||||
sysfs_get_capacity_level (GUdevDevice *native,
|
||||
UpDeviceLevel *level)
|
||||
|
|
@ -255,11 +317,13 @@ up_device_supply_refresh_device (UpDeviceSupply *supply,
|
|||
UpRefreshReason reason)
|
||||
{
|
||||
UpDeviceState state;
|
||||
UpDeviceChargeType charge_type;
|
||||
UpDevice *device = UP_DEVICE (supply);
|
||||
GUdevDevice *native;
|
||||
gdouble percentage = 0.0f;
|
||||
UpDeviceLevel level = UP_DEVICE_LEVEL_NONE;
|
||||
gboolean is_present = TRUE;
|
||||
gboolean percentage_trusted = TRUE;
|
||||
|
||||
native = G_UDEV_DEVICE (up_device_get_native (device));
|
||||
|
||||
|
|
@ -314,10 +378,25 @@ up_device_supply_refresh_device (UpDeviceSupply *supply,
|
|||
if (percentage == 100.0)
|
||||
state = UP_DEVICE_STATE_FULLY_CHARGED;
|
||||
|
||||
/* Charge regime — distinct from state. Stock UPower dropped this;
|
||||
* the Souveraine fork surfaces the kernel charge_type so consumers
|
||||
* can tell "fast" from "trickle" (constant-voltage taper) without
|
||||
* poking sysfs. Missing attribute → UNKNOWN. */
|
||||
charge_type = up_device_supply_get_charge_type (native);
|
||||
|
||||
/* Percentage-trust guard. capacity is derived from charge_full (or
|
||||
* energy_full); if that reads implausibly small vs the design
|
||||
* capacity — typical after a driver change reports units wrong —
|
||||
* percentage is meaningless (e.g. a healthy pack reads ~1%). Mark
|
||||
* it untrusted so consumers can suppress it. */
|
||||
percentage_trusted = up_device_supply_percentage_is_trusted (native);
|
||||
|
||||
g_object_set (device,
|
||||
"percentage", percentage,
|
||||
"battery-level", level,
|
||||
"state", state,
|
||||
"charge-type", charge_type,
|
||||
"percentage-trusted", percentage_trusted,
|
||||
"is-present", is_present,
|
||||
NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ GType up_device_supply_get_type (void);
|
|||
|
||||
UpDeviceState up_device_supply_get_state (GUdevDevice *native);
|
||||
|
||||
UpDeviceChargeType up_device_supply_get_charge_type (GUdevDevice *native);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __UP_DEVICE_SUPPLY_H__ */
|
||||
|
|
|
|||
Loading…
Reference in a new issue