More CR grilling - add next_level_kind and normalize pid casing in show --json output

This commit is contained in:
Itay Avraham 2026-08-15 21:26:18 +03:00
parent 8cf195bc95
commit ea4ec77f1f
2 changed files with 34 additions and 13 deletions

View File

@ -149,18 +149,21 @@ def _battery_line(dev):
print(" Battery status unavailable.")
def _level_kind(level):
"""Distinguish a real percentage ("level") from a qualitative approximation."""
if isinstance(level, BatteryLevelApproximation):
return "approximation"
if isinstance(level, int):
return "level"
return None
def _battery_json(battery):
"""Serialize a Battery as a JSON-friendly dict, or None if unavailable."""
if battery is None:
return None
level = battery.level
if isinstance(level, BatteryLevelApproximation):
# A qualitative level (e.g. "good"), not an actual percentage
level_kind = "approximation"
elif isinstance(level, int):
level_kind = "level"
else:
level_kind = None
next_level = battery.next_level
status = getattr(battery.status, "name", None) # canonical member name; combined flags have none on Python < 3.11
if status is None and battery.status is not None:
# Decompose combined flags into single-bit members so the output
@ -173,8 +176,9 @@ def _battery_json(battery):
)
return {
"level": int(level) if level is not None else None,
"level_kind": level_kind,
"next_level": int(battery.next_level) if battery.next_level is not None else None,
"level_kind": _level_kind(level),
"next_level": int(next_level) if next_level is not None else None,
"next_level_kind": _level_kind(next_level),
"status": status,
"voltage": battery.voltage,
}
@ -193,8 +197,8 @@ def _receiver_json(receiver):
def _device_json(dev):
"""Serialize a device as a JSON-friendly dict, or None if the device is gone."""
# Save the descriptor-known protocol; the ping below may update it, and
# for an offline device the ping fails and would leave it unset.
# Save the descriptor-known protocol before the ping below updates it; for
# a descriptor-less device the property itself pings to determine it.
protocol = float(dev.protocol) if dev.protocol else None
try:
online = dev.ping()
@ -211,7 +215,7 @@ def _device_json(dev):
"name": dev.name,
"number": dev.number,
"receiver": _receiver_json(receiver),
"pid": str(dev.wpid or dev.product_id),
"pid": str(dev.wpid or dev.product_id).upper(), # hex, uppercase (product_id case varies by hid backend)
"path": dev.path,
"bluetooth": dev.bluetooth,
"mac": dev.hid_serial if dev.bluetooth else None,

View File

@ -82,6 +82,13 @@ class DeviceInfoStub:
pi_4066 = {"wpid": "4066", "kind": NamedInt(1, "keyboard"), "serial": "5678", "polling": "4ms", "power_switch": "left"}
@pytest.fixture(autouse=True)
def _reset_device_instances():
yield
Device.instances[:] = []
responses_receiver = [
fake_hidpp.Response("000000", 0x8003, "FF"),
fake_hidpp.Response("000300", 0x8102),
@ -114,6 +121,7 @@ def test_battery_json_percentage():
"level": 55,
"level_kind": "level",
"next_level": None,
"next_level_kind": None,
"status": "DISCHARGING",
"voltage": 3800,
}
@ -123,6 +131,7 @@ def test_battery_json_approximation():
info = _battery_json(_battery(BatteryLevelApproximation.GOOD))
assert info["level"] == 50
assert info["level_kind"] == "approximation"
assert info["next_level_kind"] is None
def test_battery_json_full_approximation():
@ -130,6 +139,7 @@ def test_battery_json_full_approximation():
assert info["level"] == 90
assert info["level_kind"] == "approximation"
assert info["next_level"] == 20
assert info["next_level_kind"] == "approximation"
@pytest.mark.parametrize(
@ -182,7 +192,14 @@ def test_device_json_online():
assert info["kind"] == "keyboard"
assert info["protocol"] == 4.5
assert info["online"] is True
assert info["battery"] == {"level": 18, "level_kind": "level", "next_level": 52, "status": None, "voltage": None}
assert info["battery"] == {
"level": 18,
"level_kind": "level",
"next_level": 52,
"next_level_kind": "level",
"status": None,
"voltage": None,
}
def test_device_json_direct_usb_device():