diff --git a/lib/logitech_receiver/hidpp20.py b/lib/logitech_receiver/hidpp20.py index 20406fae9..4659a0122 100644 --- a/lib/logitech_receiver/hidpp20.py +++ b/lib/logitech_receiver/hidpp20.py @@ -1503,25 +1503,6 @@ def feature_request(device, feature, function=0x00, *params, no_reply=False): return device.request((feature_index << 8) + (function & 0xFF), *params, no_reply=no_reply) -# voltage to remaining charge from Logitech -battery_voltage_remaining = ( - (4186, 100), - (4067, 90), - (3989, 80), - (3922, 70), - (3859, 60), - (3811, 50), - (3778, 40), - (3751, 30), - (3717, 20), - (3671, 10), - (3646, 5), - (3579, 2), - (3500, 0), - (-1000, 0), -) - - class Hidpp20: def get_firmware(self, device) -> tuple[common.FirmwareInfo] | None: """Reads a device's firmware info. @@ -1962,9 +1943,41 @@ def decipher_adc_measurement(report) -> tuple[SupportedFeature, Battery]: def estimate_battery_level_percentage(value_millivolt: int) -> int | None: - charge_level = None - for level in battery_voltage_remaining: - if level[0] < value_millivolt: - charge_level = level[1] - break - return charge_level + """Estimate battery level percentage based on battery voltage. + + Uses linear approximation to estimate the battery level in percent. + + Parameters + ---------- + value_millivolt + Measured battery voltage in millivolt. + """ + battery_voltage_to_percentage = [ + (4186, 100), + (4067, 90), + (3989, 80), + (3922, 70), + (3859, 60), + (3811, 50), + (3778, 40), + (3751, 30), + (3717, 20), + (3671, 10), + (3646, 5), + (3579, 2), + (3500, 0), + ] + + if value_millivolt >= battery_voltage_to_percentage[0][0]: + return battery_voltage_to_percentage[0][1] + if value_millivolt <= battery_voltage_to_percentage[-1][0]: + return battery_voltage_to_percentage[-1][1] + + for i in range(len(battery_voltage_to_percentage) - 1): + v_high, p_high = battery_voltage_to_percentage[i] + v_low, p_low = battery_voltage_to_percentage[i + 1] + if v_low <= value_millivolt <= v_high: + # Linear interpolation + percent = p_low + (p_high - p_low) * (value_millivolt - v_low) / (v_high - v_low) + return round(percent) + return 0 diff --git a/tests/logitech_receiver/test_hidpp20_simple.py b/tests/logitech_receiver/test_hidpp20_simple.py index 02d92f3c9..f68c09923 100644 --- a/tests/logitech_receiver/test_hidpp20_simple.py +++ b/tests/logitech_receiver/test_hidpp20_simple.py @@ -107,7 +107,7 @@ def test_get_battery_voltage(): feature, battery = _hidpp20.get_battery_voltage(device) assert feature == SupportedFeature.BATTERY_VOLTAGE - assert battery.level == 90 + assert battery.level == 92 assert common.BatteryStatus.RECHARGING in battery.status assert battery.voltage == 0x1000 @@ -130,7 +130,7 @@ def test_get_adc_measurement(): feature, battery = _hidpp20.get_adc_measurement(device) assert feature == SupportedFeature.ADC_MEASUREMENT - assert battery.level == 90 + assert battery.level == 92 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 @@ -389,7 +389,7 @@ def test_decipher_battery_voltage(): feature, battery = hidpp20.decipher_battery_voltage(report) assert feature == SupportedFeature.BATTERY_VOLTAGE - assert battery.level == 90 + assert battery.level == 92 assert common.BatteryStatus.RECHARGING in battery.status assert battery.voltage == 0x1000 @@ -410,7 +410,7 @@ def test_decipher_adc_measurement(): feature, battery = hidpp20.decipher_adc_measurement(report) assert feature == SupportedFeature.ADC_MEASUREMENT - assert battery.level == 90 + assert battery.level == 92 assert battery.status == common.BatteryStatus.RECHARGING assert battery.voltage == 0x1000 @@ -454,36 +454,27 @@ def test_led_zone_locations(code, expected_name): @pytest.mark.parametrize( "millivolt, expected_percentage", [ - (-1234, None), + (-1234, 0), (500, 0), (2000, 0), (3500, 0), - (3501, 0), (3519, 0), - (3520, 0), - (3579, 0), - (3646, 2), - (3580, 2), - (3671, 5), - (3672, 10), - (3717, 10), - (3718, 20), - (3751, 20), - (3752, 30), - (3778, 30), - (3779, 40), - (3811, 40), - (3812, 50), - (3859, 50), - (3860, 60), - (3922, 60), - (3923, 70), - (3989, 70), - (3990, 80), - (4067, 80), - (4068, 90), - (4186, 90), - (4187, 100), + (3520, 1), + (3559, 1), + (3579, 2), + (3646, 5), + (3671, 10), + (3717, 20), + (3751, 30), + (3778, 40), + (3811, 50), + (3859, 60), + (3922, 70), + (3989, 80), + (4067, 90), + (4180, 99), + (4181, 100), + (4186, 100), (4500, 100), ], )