Refactor: Use dataclasses and enums

Replace unnecessary NamedInts in favour of default data types.
Simplify interfaces by reducing possible input from strings to members
of an enum.
This commit is contained in:
Matthias Hagmann 2024-04-12 02:44:57 +02:00 committed by Peter F. Patel-Schneider
parent 469c04faaf
commit c9dc232951
10 changed files with 172 additions and 138 deletions

View File

@ -18,8 +18,8 @@
# Some common functions and types. # Some common functions and types.
from binascii import hexlify as _hexlify from binascii import hexlify as _hexlify
from collections import namedtuple
from dataclasses import dataclass from dataclasses import dataclass
from enum import IntEnum
from typing import Optional from typing import Optional
from typing import Union from typing import Union
@ -28,15 +28,6 @@ import yaml as _yaml
from solaar.i18n import _ from solaar.i18n import _
def is_string(d):
return isinstance(d, str)
#
#
#
def crc16(data: bytes): def crc16(data: bytes):
""" """
CRC-16 (CCITT) implemented with a precomputed lookup table CRC-16 (CCITT) implemented with a precomputed lookup table
@ -314,7 +305,7 @@ class NamedInt(int):
(case-insensitive).""" (case-insensitive)."""
def __new__(cls, value, name): def __new__(cls, value, name):
assert is_string(name) assert isinstance(name, str)
obj = int.__new__(cls, value) obj = int.__new__(cls, value)
obj.name = str(name) obj.name = str(name)
return obj return obj
@ -329,7 +320,7 @@ class NamedInt(int):
return int(self) == int(other) and self.name == other.name return int(self) == int(other) and self.name == other.name
if isinstance(other, int): if isinstance(other, int):
return int(self) == int(other) return int(self) == int(other)
if is_string(other): if isinstance(other, str):
return self.name.lower() == other.lower() return self.name.lower() == other.lower()
# this should catch comparisons with bytes in Py3 # this should catch comparisons with bytes in Py3
if other is not None: if other is not None:
@ -430,7 +421,7 @@ class NamedInts:
self._sort_values() self._sort_values()
return value return value
elif is_string(index): elif isinstance(index, str):
if index in self.__dict__: if index in self.__dict__:
return self.__dict__[index] return self.__dict__[index]
return next((x for x in self._values if str(x) == index), None) return next((x for x in self._values if str(x) == index), None)
@ -469,7 +460,7 @@ class NamedInts:
if isinstance(name, NamedInt): if isinstance(name, NamedInt):
assert int(index) == int(name), repr(index) + " " + repr(name) assert int(index) == int(name), repr(index) + " " + repr(name)
value = name value = name
elif is_string(name): elif isinstance(name, str):
value = NamedInt(index, name) value = NamedInt(index, name)
else: else:
raise TypeError("name must be a string") raise TypeError("name must be a string")
@ -490,7 +481,7 @@ class NamedInts:
return self[value] == value return self[value] == value
elif isinstance(value, int): elif isinstance(value, int):
return value in self._indexed return value in self._indexed
elif is_string(value): elif isinstance(value, str):
return value in self.__dict__ or value in self._values return value in self.__dict__ or value in self._values
def __iter__(self): def __iter__(self):
@ -550,63 +541,81 @@ class KwException(Exception):
return self.args[0].get(k) # was self.args[0][k] return self.args[0].get(k) # was self.args[0][k]
"""Firmware information.""" @dataclass
FirmwareInfo = namedtuple("FirmwareInfo", ["kind", "name", "version", "extras"]) class FirmwareInfo:
kind: str
name: str
version: str
extras: str
class BatteryStatus(IntEnum):
DISCHARGING = 0x00
RECHARGING = 0x01
ALMOST_FULL = 0x02
FULL = 0x03
SLOW_RECHARGE = 0x04
INVALID_BATTERY = 0x05
THERMAL_ERROR = 0x06
class BatteryLevelApproximation(IntEnum):
EMPTY = 0
CRITICAL = 5
LOW = 20
GOOD = 50
FULL = 90
@dataclass @dataclass
class Battery: class Battery:
"""Information about the current state of a battery""" """Information about the current state of a battery"""
level: Optional[Union[NamedInt, int]] ATTENTION_LEVEL = 5
level: Optional[Union[BatteryLevelApproximation, int]]
next_level: Optional[Union[NamedInt, int]] next_level: Optional[Union[NamedInt, int]]
status: Optional[NamedInt] status: Optional[BatteryStatus]
voltage: Optional[int] voltage: Optional[int]
light_level: Optional[int] = None # light level for devices with solaar recharging light_level: Optional[int] = None # light level for devices with solaar recharging
def __post_init__(self): def __post_init__(self):
if self.level is None: # infer level from status if needed and possible if self.level is None: # infer level from status if needed and possible
if self.status == Battery.STATUS.full: if self.status == BatteryStatus.FULL:
self.level = Battery.APPROX.full self.level = BatteryLevelApproximation.FULL
elif self.status in (Battery.STATUS.almost_full, Battery.STATUS.recharging): elif self.status in (BatteryStatus.ALMOST_FULL, BatteryStatus.RECHARGING):
self.level = Battery.APPROX.good self.level = BatteryLevelApproximation.GOOD
elif self.status == Battery.STATUS.slow_recharge: elif self.status == BatteryStatus.SLOW_RECHARGE:
self.level = Battery.APPROX.low self.level = BatteryLevelApproximation.LOW
STATUS = NamedInts( def ok(self) -> bool:
discharging=0x00, return self.status not in (BatteryStatus.INVALID_BATTERY, BatteryStatus.THERMAL_ERROR) and (
recharging=0x01,
almost_full=0x02,
full=0x03,
slow_recharge=0x04,
invalid_battery=0x05,
thermal_error=0x06,
)
APPROX = NamedInts(empty=0, critical=5, low=20, good=50, full=90)
ATTENTION_LEVEL = 5
def ok(self):
return self.status not in (Battery.STATUS.invalid_battery, Battery.STATUS.thermal_error) and (
self.level is None or self.level > Battery.ATTENTION_LEVEL self.level is None or self.level > Battery.ATTENTION_LEVEL
) )
def charging(self): def charging(self) -> bool:
return self.status in ( return self.status in (
Battery.STATUS.recharging, BatteryStatus.RECHARGING,
Battery.STATUS.almost_full, BatteryStatus.ALMOST_FULL,
Battery.STATUS.full, BatteryStatus.FULL,
Battery.STATUS.slow_recharge, BatteryStatus.SLOW_RECHARGE,
) )
def to_str(self): def to_str(self) -> str:
if isinstance(self.level, NamedInt): if isinstance(self.level, BatteryLevelApproximation):
return _("Battery: %(level)s (%(status)s)") % {"level": _(self.level), "status": _(self.status)} level = self.level.name.lower()
status = self.status.name.lower().replace("_", " ")
return _("Battery: %(level)s (%(status)s)") % {"level": _(level), "status": _(status)}
elif isinstance(self.level, int): elif isinstance(self.level, int):
return _("Battery: %(percent)d%% (%(status)s)") % {"percent": self.level, "status": _(self.status)} status = self.status.name.lower().replace("_", " ")
return _("Battery: %(percent)d%% (%(status)s)") % {"percent": self.level, "status": _(status)}
else: else:
return "" return ""
ALERT = NamedInts(NONE=0x00, NOTIFICATION=0x01, SHOW_WINDOW=0x02, ATTENTION=0x04, ALL=0xFF) class Alert(IntEnum):
NONE = 0x00
NOTIFICATION = 0x01
SHOW_WINDOW = 0x02
ATTENTION = 0x04
ALL = 0xFF

View File

@ -34,7 +34,7 @@ from . import hidpp10_constants
from . import hidpp20 from . import hidpp20
from . import hidpp20_constants from . import hidpp20_constants
from . import settings from . import settings
from .common import ALERT from .common import Alert
from .common import Battery from .common import Battery
from .settings_templates import check_feature_settings as _check_feature_settings from .settings_templates import check_feature_settings as _check_feature_settings
@ -375,11 +375,11 @@ class Device:
if old_info is None: if old_info is None:
old_info = Battery(None, None, None, None) old_info = Battery(None, None, None, None)
alert, reason = ALERT.NONE, None alert, reason = Alert.NONE, None
if not info.ok(): if not info.ok():
logger.warning("%s: battery %d%%, ALERT %s", self, info.level, info.status) logger.warning("%s: battery %d%%, ALERT %s", self, info.level, info.status)
if old_info.status != info.status: if old_info.status != info.status:
alert = ALERT.NOTIFICATION | ALERT.ATTENTION alert = Alert.NOTIFICATION | Alert.ATTENTION
reason = info.to_str() reason = info.to_str()
if changed or reason: if changed or reason:
@ -393,7 +393,7 @@ class Device:
battery = self.battery() battery = self.battery()
self.set_battery_info(battery if battery is not None else Battery(None, None, None, None)) self.set_battery_info(battery if battery is not None else Battery(None, None, None, None))
def changed(self, active=None, alert=ALERT.NONE, reason=None, push=False): def changed(self, active=None, alert=Alert.NONE, reason=None, push=False):
"""The status of the device had changed, so invoke the status callback. """The status of the device had changed, so invoke the status callback.
Also push notifications and settings to the device when necessary.""" Also push notifications and settings to the device when necessary."""
if active is not None: if active is not None:

View File

@ -22,7 +22,7 @@ from typing import Any
from typing_extensions import Protocol from typing_extensions import Protocol
from .common import Battery from .common import Battery
from .common import BatteryChargeApproximation from .common import BatteryLevelApproximation
from .common import BatteryStatus from .common import BatteryStatus
from .common import FirmwareInfo as _FirmwareInfo from .common import FirmwareInfo as _FirmwareInfo
from .common import bytes2int as _bytes2int from .common import bytes2int as _bytes2int
@ -158,17 +158,17 @@ class Hidpp10:
return return
if battery_level is not None: if battery_level is not None:
if battery_level < BatteryChargeApproximation.CRITICAL: if battery_level < BatteryLevelApproximation.CRITICAL:
# 1 orange, and force blink # 1 orange, and force blink
v1, v2 = 0x22, 0x00 v1, v2 = 0x22, 0x00
warning = True warning = True
elif battery_level < BatteryChargeApproximation.LOW: elif battery_level < BatteryLevelApproximation.LOW:
# 1 orange # 1 orange
v1, v2 = 0x22, 0x00 v1, v2 = 0x22, 0x00
elif battery_level < BatteryChargeApproximation.GOOD: elif battery_level < BatteryLevelApproximation.GOOD:
# 1 green # 1 green
v1, v2 = 0x20, 0x00 v1, v2 = 0x20, 0x00
elif battery_level < BatteryChargeApproximation.FULL: elif battery_level < BatteryLevelApproximation.FULL:
# 2 greens # 2 greens
v1, v2 = 0x20, 0x02 v1, v2 = 0x20, 0x02
else: else:
@ -228,18 +228,18 @@ class Hidpp10:
def parse_battery_status(register, reply) -> Battery | None: def parse_battery_status(register, reply) -> Battery | None:
def status_byte_to_charge(status_byte_: int) -> BatteryChargeApproximation: def status_byte_to_charge(status_byte_: int) -> BatteryLevelApproximation:
if status_byte_ == 7: if status_byte_ == 7:
charge_ = BatteryChargeApproximation.FULL charge_ = BatteryLevelApproximation.FULL
elif status_byte_ == 5: elif status_byte_ == 5:
charge_ = BatteryChargeApproximation.GOOD charge_ = BatteryLevelApproximation.GOOD
elif status_byte_ == 3: elif status_byte_ == 3:
charge_ = BatteryChargeApproximation.LOW charge_ = BatteryLevelApproximation.LOW
elif status_byte_ == 1: elif status_byte_ == 1:
charge_ = BatteryChargeApproximation.CRITICAL charge_ = BatteryLevelApproximation.CRITICAL
else: else:
# pure 'charging' notifications may come without a status # pure 'charging' notifications may come without a status
charge_ = BatteryChargeApproximation.EMPTY charge_ = BatteryLevelApproximation.EMPTY
return charge_ return charge_
def status_byte_to_battery_status(status_byte_: int) -> BatteryStatus: def status_byte_to_battery_status(status_byte_: int) -> BatteryStatus:

View File

@ -21,8 +21,10 @@ import threading as _threading
from struct import pack as _pack from struct import pack as _pack
from struct import unpack as _unpack from struct import unpack as _unpack
from typing import Any
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import Tuple
import yaml as _yaml import yaml as _yaml
@ -33,6 +35,8 @@ from . import exceptions
from . import hidpp10_constants as _hidpp10_constants from . import hidpp10_constants as _hidpp10_constants
from . import special_keys from . import special_keys
from .common import Battery from .common import Battery
from .common import BatteryLevelApproximation
from .common import BatteryStatus
from .common import FirmwareInfo as _FirmwareInfo from .common import FirmwareInfo as _FirmwareInfo
from .common import NamedInt as _NamedInt from .common import NamedInt as _NamedInt
from .common import NamedInts as _NamedInts from .common import NamedInts as _NamedInts
@ -51,6 +55,8 @@ from .hidpp20_constants import GESTURE
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
FixedBytes5 = bytes
KIND_MAP = {kind: _hidpp10_constants.DEVICE_KIND[str(kind)] for kind in DEVICE_KIND} KIND_MAP = {kind: _hidpp10_constants.DEVICE_KIND[str(kind)] for kind in DEVICE_KIND}
@ -1747,34 +1753,41 @@ battery_functions = {
} }
def decipher_battery_status(report): def decipher_battery_status(report: FixedBytes5) -> Tuple[Any, Battery]:
discharge, next, status = _unpack("!BBB", report[:3]) battery_discharge_level, battery_discharge_next_level, battery_status = _unpack("!BBB", report[:3])
discharge = None if discharge == 0 else discharge if battery_discharge_level == 0:
status = Battery.STATUS[status] battery_discharge_level = None
try:
status = BatteryStatus(battery_status)
except ValueError:
status = None
logger.debug(f"Unknown battery status byte 0x{battery_status:02X}")
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
logger.debug("battery status %s%% charged, next %s%%, status %s", discharge, next, status) logger.debug(
return FEATURE.BATTERY_STATUS, Battery(discharge, next, status, None) "battery status %s%% charged, next %s%%, status %s", battery_discharge_level, battery_discharge_next_level, status
)
return FEATURE.BATTERY_STATUS, Battery(battery_discharge_level, battery_discharge_next_level, status, None)
def decipher_battery_voltage(report): def decipher_battery_voltage(report):
voltage, flags = _unpack(">HB", report[:3]) voltage, flags = _unpack(">HB", report[:3])
status = Battery.STATUS.discharging status = BatteryStatus.DISCHARGING
charge_sts = ERROR.unknown charge_sts = ERROR.unknown
charge_lvl = CHARGE_LEVEL.average charge_lvl = CHARGE_LEVEL.average
charge_type = CHARGE_TYPE.standard charge_type = CHARGE_TYPE.standard
if flags & (1 << 7): if flags & (1 << 7):
status = Battery.STATUS.recharging status = BatteryStatus.RECHARGING
charge_sts = CHARGE_STATUS[flags & 0x03] charge_sts = CHARGE_STATUS[flags & 0x03]
if charge_sts is None: if charge_sts is None:
charge_sts = ERROR.unknown charge_sts = ERROR.unknown
elif charge_sts == CHARGE_STATUS.full: elif charge_sts == CHARGE_STATUS.full:
charge_lvl = CHARGE_LEVEL.full charge_lvl = CHARGE_LEVEL.full
status = Battery.STATUS.full status = BatteryStatus.FULL
if flags & (1 << 3): if flags & (1 << 3):
charge_type = CHARGE_TYPE.fast charge_type = CHARGE_TYPE.fast
elif flags & (1 << 4): elif flags & (1 << 4):
charge_type = CHARGE_TYPE.slow charge_type = CHARGE_TYPE.slow
status = Battery.STATUS.slow_recharge status = BatteryStatus.SLOW_RECHARGE
elif flags & (1 << 5): elif flags & (1 << 5):
charge_lvl = CHARGE_LEVEL.critical charge_lvl = CHARGE_LEVEL.critical
for level in battery_voltage_remaining: for level in battery_voltage_remaining:
@ -1795,21 +1808,26 @@ def decipher_battery_voltage(report):
def decipher_battery_unified(report): def decipher_battery_unified(report):
discharge, level, status, _ignore = _unpack("!BBBB", report[:4]) discharge, level, status_byte, _ignore = _unpack("!BBBB", report[:4])
status = Battery.STATUS[status] try:
status = BatteryStatus(status_byte)
except ValueError:
status = None
logger.debug(f"Unknown battery status byte 0x{status_byte:02X}")
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
logger.debug("battery unified %s%% charged, level %s, charging %s", discharge, level, status) logger.debug("battery unified %s%% charged, level %s, charging %s", discharge, level, status)
level = (
Battery.APPROX.full if level == 8:
if level == 8 # full level = BatteryLevelApproximation.FULL
else Battery.APPROX.good elif level == 4:
if level == 4 # good level = BatteryLevelApproximation.GOOD
else Battery.APPROX.low elif level == 2:
if level == 2 # low level = BatteryLevelApproximation.LOW
else Battery.APPROX.critical elif level == 1:
if level == 1 # critical level = BatteryLevelApproximation.CRITICAL
else Battery.APPROX.empty else:
) level = BatteryLevelApproximation.EMPTY
return FEATURE.UNIFIED_BATTERY, Battery(discharge if discharge else level, None, status, None) return FEATURE.UNIFIED_BATTERY, Battery(discharge if discharge else level, None, status, None)
@ -1821,5 +1839,5 @@ def decipher_adc_measurement(report):
charge_level = level[1] charge_level = level[1]
break break
if flags & 0x01: if flags & 0x01:
status = Battery.STATUS.recharging if flags & 0x02 else Battery.STATUS.discharging status = BatteryStatus.RECHARGING if flags & 0x02 else BatteryStatus.DISCHARGING
return FEATURE.ADC_MEASUREMENT, Battery(charge_level, None, status, adc) return FEATURE.ADC_MEASUREMENT, Battery(charge_level, None, status, adc)

View File

@ -31,8 +31,9 @@ from . import hidpp20
from . import hidpp20_constants as _hidpp20_constants from . import hidpp20_constants as _hidpp20_constants
from . import settings_templates as _st from . import settings_templates as _st
from .base import DJ_MESSAGE_ID as _DJ_MESSAGE_ID from .base import DJ_MESSAGE_ID as _DJ_MESSAGE_ID
from .common import ALERT as _ALERT from .common import Alert
from .common import Battery as _Battery from .common import Battery as _Battery
from .common import BatteryStatus
from .common import strhex as _strhex from .common import strhex as _strhex
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -203,7 +204,7 @@ def _process_dj_notification(device, n):
connected = not n.address & 0x01 connected = not n.address & 0x01
if logger.isEnabledFor(logging.INFO): if logger.isEnabledFor(logging.INFO):
logger.info("%s: DJ connection: %s %s", device, connected, n) logger.info("%s: DJ connection: %s %s", device, connected, n)
device.changed(active=connected, alert=_ALERT.NONE, reason=_("connected") if connected else _("disconnected")) device.changed(active=connected, alert=Alert.NONE, reason=_("connected") if connected else _("disconnected"))
return True return True
logger.warning("%s: unrecognized DJ %s", device, n) logger.warning("%s: unrecognized DJ %s", device, n)
@ -229,7 +230,7 @@ def _process_hidpp10_notification(device, n):
device.wpid = None device.wpid = None
if device.number in device.receiver: if device.number in device.receiver:
del device.receiver[device.number] del device.receiver[device.number]
device.changed(active=False, alert=_ALERT.ALL, reason=_("unpaired")) device.changed(active=False, alert=Alert.ALL, reason=_("unpaired"))
## device.status = None ## device.status = None
else: else:
logger.warning("%s: disconnection with unknown type %02X: %s", device, n.address, n) logger.warning("%s: disconnection with unknown type %02X: %s", device, n.address, n)
@ -277,7 +278,7 @@ def _process_hidpp10_notification(device, n):
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
logger.debug("%s: device powered on", device) logger.debug("%s: device powered on", device)
reason = device.status_string() or _("powered on") reason = device.status_string() or _("powered on")
device.changed(active=True, alert=_ALERT.NOTIFICATION, reason=reason) device.changed(active=True, alert=Alert.NOTIFICATION, reason=reason)
else: else:
logger.warning("%s: unknown %s", device, n) logger.warning("%s: unknown %s", device, n)
return True return True
@ -325,17 +326,17 @@ def _process_feature_notification(device, n, feature):
charge, lux, adc = _unpack("!BHH", n.data[:5]) charge, lux, adc = _unpack("!BHH", n.data[:5])
# guesstimate the battery voltage, emphasis on 'guess' # guesstimate the battery voltage, emphasis on 'guess'
# status_text = '%1.2fV' % (adc * 2.67793237653 / 0x0672) # status_text = '%1.2fV' % (adc * 2.67793237653 / 0x0672)
status_text = _Battery.STATUS.discharging status_text = BatteryStatus.DISCHARGING
if n.address == 0x00: if n.address == 0x00:
device.set_battery_info(_Battery(charge, None, status_text, None)) device.set_battery_info(_Battery(charge, None, status_text, None))
elif n.address == 0x10: elif n.address == 0x10:
if lux > 200: if lux > 200:
status_text = _Battery.STATUS.recharging status_text = BatteryStatus.RECHARGING
device.set_battery_info(_Battery(charge, None, status_text, None, lux)) device.set_battery_info(_Battery(charge, None, status_text, None, lux))
elif n.address == 0x20: elif n.address == 0x20:
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
logger.debug("%s: Light Check button pressed", device) logger.debug("%s: Light Check button pressed", device)
device.changed(alert=_ALERT.SHOW_WINDOW) device.changed(alert=Alert.SHOW_WINDOW)
# first cancel any reporting # first cancel any reporting
# device.feature_request(_F.SOLAR_DASHBOARD) # device.feature_request(_F.SOLAR_DASHBOARD)
# trigger a new report chain # trigger a new report chain
@ -353,7 +354,7 @@ def _process_feature_notification(device, n, feature):
logger.debug("wireless status: %s", n) logger.debug("wireless status: %s", n)
reason = "powered on" if n.data[2] == 1 else None reason = "powered on" if n.data[2] == 1 else None
if n.data[1] == 1: # device is asking for software reconfiguration so need to change status if n.data[1] == 1: # device is asking for software reconfiguration so need to change status
alert = _ALERT.NONE alert = Alert.NONE
device.changed(active=True, alert=alert, reason=reason, push=True) device.changed(active=True, alert=alert, reason=reason, push=True)
else: else:
logger.warning("%s: unknown WIRELESS %s", device, n) logger.warning("%s: unknown WIRELESS %s", device, n)

View File

@ -32,7 +32,7 @@ from . import base as _base
from . import exceptions from . import exceptions
from . import hidpp10 from . import hidpp10
from . import hidpp10_constants from . import hidpp10_constants
from .common import ALERT from .common import Alert
from .device import Device from .device import Device
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -113,7 +113,7 @@ class Receiver:
def __del__(self): def __del__(self):
self.close() self.close()
def changed(self, alert=ALERT.NOTIFICATION, reason=None): def changed(self, alert=Alert.NOTIFICATION, reason=None):
"""The status of the device had changed, so invoke the status callback""" """The status of the device had changed, so invoke the status callback"""
if self.status_callback is not None: if self.status_callback is not None:
self.status_callback(self, alert=alert, reason=reason) self.status_callback(self, alert=alert, reason=reason)

View File

@ -20,7 +20,7 @@ import logging
import gi import gi
import yaml as _yaml import yaml as _yaml
from logitech_receiver.common import ALERT from logitech_receiver.common import Alert
from solaar.i18n import _ from solaar.i18n import _
from solaar.ui.config_panel import change_setting from solaar.ui.config_panel import change_setting
@ -109,21 +109,21 @@ def _status_changed(device, alert, reason, refresh=False):
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
logger.debug("status changed: %s (%s) %s", device, alert, reason) logger.debug("status changed: %s (%s) %s", device, alert, reason)
if alert is None: if alert is None:
alert = ALERT.NONE alert = Alert.NONE
tray.update(device) tray.update(device)
if alert & ALERT.ATTENTION: if alert & Alert.ATTENTION:
tray.attention(reason) tray.attention(reason)
need_popup = alert & ALERT.SHOW_WINDOW need_popup = alert & Alert.SHOW_WINDOW
window.update(device, need_popup, refresh) window.update(device, need_popup, refresh)
diversion_rules.update_devices() diversion_rules.update_devices()
if alert & (ALERT.NOTIFICATION | ALERT.ATTENTION): if alert & (Alert.NOTIFICATION | Alert.ATTENTION):
notify.show(device, reason) notify.show(device, reason)
def status_changed(device, alert=ALERT.NONE, reason=None, refresh=False): def status_changed(device, alert=Alert.NONE, reason=None, refresh=False):
GLib.idle_add(_status_changed, device, alert, reason, refresh) GLib.idle_add(_status_changed, device, alert, reason, refresh)

View File

@ -218,11 +218,17 @@ def test_kw_exception():
@pytest.mark.parametrize( @pytest.mark.parametrize(
"status, expected_level, expected_ok, expected_charging, expected_string", "status, expected_level, expected_ok, expected_charging, expected_string",
[ [
(common.Battery.STATUS.full, common.Battery.APPROX.full, True, True, "Battery: full (full)"), (common.BatteryStatus.FULL, common.BatteryLevelApproximation.FULL, True, True, "Battery: full (full)"),
(common.Battery.STATUS.almost_full, common.Battery.APPROX.good, True, True, "Battery: good (almost full)"), (common.BatteryStatus.ALMOST_FULL, common.BatteryLevelApproximation.GOOD, True, True, "Battery: good (almost full)"),
(common.Battery.STATUS.recharging, common.Battery.APPROX.good, True, True, "Battery: good (recharging)"), (common.BatteryStatus.RECHARGING, common.BatteryLevelApproximation.GOOD, True, True, "Battery: good (recharging)"),
(common.Battery.STATUS.slow_recharge, common.Battery.APPROX.low, True, True, "Battery: low (slow recharge)"), (
(common.Battery.STATUS.discharging, None, True, False, ""), common.BatteryStatus.SLOW_RECHARGE,
common.BatteryLevelApproximation.LOW,
True,
True,
"Battery: low (slow recharge)",
),
(common.BatteryStatus.DISCHARGING, None, True, False, ""),
], ],
) )
def test_battery(status, expected_level, expected_ok, expected_charging, expected_string): def test_battery(status, expected_level, expected_ok, expected_charging, expected_string):
@ -236,9 +242,9 @@ def test_battery(status, expected_level, expected_ok, expected_charging, expecte
def test_battery_2(): def test_battery_2():
battery = common.Battery(50, None, common.Battery.STATUS.discharging, None) battery = common.Battery(50, None, common.BatteryStatus.DISCHARGING, None)
assert battery.status == common.Battery.STATUS.discharging assert battery.status == common.BatteryStatus.DISCHARGING
assert battery.level == 50 assert battery.level == 50
assert battery.ok() assert battery.ok()
assert not battery.charging() assert not battery.charging()

View File

@ -135,53 +135,53 @@ device_status6 = device_status("NOSTATUS", "002200")
(device_leds, None, None), (device_leds, None, None),
( (
device_standard, device_standard,
common.Battery(common.Battery.APPROX.good, None, common.Battery.STATUS.recharging, None), common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None),
hidpp10_constants.REGISTERS.battery_status, hidpp10_constants.REGISTERS.battery_status,
), ),
( (
device_charge1, device_charge1,
common.Battery(0x55, None, common.Battery.STATUS.discharging, None), common.Battery(0x55, None, common.BatteryStatus.DISCHARGING, None),
hidpp10_constants.REGISTERS.battery_charge, hidpp10_constants.REGISTERS.battery_charge,
), ),
( (
device_charge2, device_charge2,
common.Battery(0x44, None, common.Battery.STATUS.recharging, None), common.Battery(0x44, None, common.BatteryStatus.RECHARGING, None),
hidpp10_constants.REGISTERS.battery_charge, hidpp10_constants.REGISTERS.battery_charge,
), ),
( (
device_charge3, device_charge3,
common.Battery(0x60, None, common.Battery.STATUS.full, None), common.Battery(0x60, None, common.BatteryStatus.FULL, None),
hidpp10_constants.REGISTERS.battery_charge, hidpp10_constants.REGISTERS.battery_charge,
), ),
(device_charge4, common.Battery(0x22, None, None, None), hidpp10_constants.REGISTERS.battery_charge), (device_charge4, common.Battery(0x22, None, None, None), hidpp10_constants.REGISTERS.battery_charge),
( (
device_status1, device_status1,
common.Battery(common.Battery.APPROX.full, None, common.Battery.STATUS.full, None), common.Battery(common.BatteryLevelApproximation.FULL, None, common.BatteryStatus.FULL, None),
hidpp10_constants.REGISTERS.battery_status, hidpp10_constants.REGISTERS.battery_status,
), ),
( (
device_status2, device_status2,
common.Battery(common.Battery.APPROX.good, None, common.Battery.STATUS.recharging, None), common.Battery(common.BatteryLevelApproximation.GOOD, None, common.BatteryStatus.RECHARGING, None),
hidpp10_constants.REGISTERS.battery_status, hidpp10_constants.REGISTERS.battery_status,
), ),
( (
device_status3, device_status3,
common.Battery(common.Battery.APPROX.low, None, common.Battery.STATUS.full, None), common.Battery(common.BatteryLevelApproximation.LOW, None, common.BatteryStatus.FULL, None),
hidpp10_constants.REGISTERS.battery_status, hidpp10_constants.REGISTERS.battery_status,
), ),
( (
device_status4, device_status4,
common.Battery(common.Battery.APPROX.critical, None, None, None), common.Battery(common.BatteryLevelApproximation.CRITICAL, None, None, None),
hidpp10_constants.REGISTERS.battery_status, hidpp10_constants.REGISTERS.battery_status,
), ),
( (
device_status5, device_status5,
common.Battery(common.Battery.APPROX.empty, None, common.Battery.STATUS.discharging, None), common.Battery(common.BatteryLevelApproximation.EMPTY, None, common.BatteryStatus.DISCHARGING, None),
hidpp10_constants.REGISTERS.battery_status, hidpp10_constants.REGISTERS.battery_status,
), ),
( (
device_status6, device_status6,
common.Battery(None, None, common.Battery.STATUS.full, None), common.Battery(None, None, common.BatteryStatus.FULL, None),
hidpp10_constants.REGISTERS.battery_status, hidpp10_constants.REGISTERS.battery_status,
), ),
], ],
@ -212,11 +212,11 @@ def test_hidpp10_get_firmware(device, expected_length):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"device, level, charging, warning, p1, p2", "device, level, charging, warning, p1, p2",
[ [
(device_leds, common.Battery.APPROX.empty, False, False, 0x33, 0x00), (device_leds, common.BatteryLevelApproximation.EMPTY, False, False, 0x33, 0x00),
(device_leds, common.Battery.APPROX.critical, False, False, 0x22, 0x00), (device_leds, common.BatteryLevelApproximation.CRITICAL, False, False, 0x22, 0x00),
(device_leds, common.Battery.APPROX.low, False, False, 0x20, 0x00), (device_leds, common.BatteryLevelApproximation.LOW, False, False, 0x20, 0x00),
(device_leds, common.Battery.APPROX.good, False, False, 0x20, 0x02), (device_leds, common.BatteryLevelApproximation.GOOD, False, False, 0x20, 0x02),
(device_leds, common.Battery.APPROX.full, False, False, 0x20, 0x22), (device_leds, common.BatteryLevelApproximation.FULL, False, False, 0x20, 0x22),
(device_leds, None, True, False, 0x30, 0x33), (device_leds, None, True, False, 0x30, 0x33),
(device_leds, None, False, True, 0x02, 0x00), (device_leds, None, False, True, 0x02, 0x00),
(device_leds, None, False, False, 0x11, 0x11), (device_leds, None, False, False, 0x11, 0x11),

View File

@ -96,7 +96,7 @@ def test_get_battery_status():
assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS
assert battery.level == 80 assert battery.level == 80
assert battery.next_level == 32 assert battery.next_level == 32
assert battery.status == common.Battery.STATUS.discharging assert battery.status == common.BatteryStatus.DISCHARGING
def test_get_battery_voltage(): def test_get_battery_voltage():
@ -107,7 +107,7 @@ def test_get_battery_voltage():
assert feature == hidpp20_constants.FEATURE.BATTERY_VOLTAGE assert feature == hidpp20_constants.FEATURE.BATTERY_VOLTAGE
assert battery.level == 90 assert battery.level == 90
assert battery.status == common.Battery.STATUS.recharging assert battery.status == common.BatteryStatus.RECHARGING
assert battery.voltage == 0x1000 assert battery.voltage == 0x1000
@ -119,7 +119,7 @@ def test_get_battery_unified():
assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY
assert battery.level == 80 assert battery.level == 80
assert battery.status == common.Battery.STATUS.discharging assert battery.status == common.BatteryStatus.DISCHARGING
def test_get_adc_measurement(): def test_get_adc_measurement():
@ -130,7 +130,7 @@ def test_get_adc_measurement():
assert feature == hidpp20_constants.FEATURE.ADC_MEASUREMENT assert feature == hidpp20_constants.FEATURE.ADC_MEASUREMENT
assert battery.level == 90 assert battery.level == 90
assert battery.status == common.Battery.STATUS.recharging assert battery.status == common.BatteryStatus.RECHARGING
assert battery.voltage == 0x1000 assert battery.voltage == 0x1000
@ -143,7 +143,7 @@ def test_get_battery():
assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS
assert battery.level == 80 assert battery.level == 80
assert battery.next_level == 32 assert battery.next_level == 32
assert battery.status == common.Battery.STATUS.discharging assert battery.status == common.BatteryStatus.DISCHARGING
def test_get_battery_none(): def test_get_battery_none():
@ -158,7 +158,7 @@ def test_get_battery_none():
assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY
assert battery.level == 80 assert battery.level == 80
assert battery.status == common.Battery.STATUS.discharging assert battery.status == common.BatteryStatus.DISCHARGING
# get_keys is in test_hidpp20_complex # get_keys is in test_hidpp20_complex
@ -379,7 +379,7 @@ def test_decipher_battery_status():
assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS assert feature == hidpp20_constants.FEATURE.BATTERY_STATUS
assert battery.level == 80 assert battery.level == 80
assert battery.next_level == 32 assert battery.next_level == 32
assert battery.status == common.Battery.STATUS.discharging assert battery.status == common.BatteryStatus.DISCHARGING
def test_decipher_battery_voltage(): def test_decipher_battery_voltage():
@ -389,7 +389,7 @@ def test_decipher_battery_voltage():
assert feature == hidpp20_constants.FEATURE.BATTERY_VOLTAGE assert feature == hidpp20_constants.FEATURE.BATTERY_VOLTAGE
assert battery.level == 90 assert battery.level == 90
assert battery.status == common.Battery.STATUS.recharging assert battery.status == common.BatteryStatus.RECHARGING
assert battery.voltage == 0x1000 assert battery.voltage == 0x1000
@ -400,7 +400,7 @@ def test_decipher_battery_unified():
assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY assert feature == hidpp20_constants.FEATURE.UNIFIED_BATTERY
assert battery.level == 80 assert battery.level == 80
assert battery.status == common.Battery.STATUS.discharging assert battery.status == common.BatteryStatus.DISCHARGING
def test_decipher_adc_measurement(): def test_decipher_adc_measurement():
@ -410,5 +410,5 @@ def test_decipher_adc_measurement():
assert feature == hidpp20_constants.FEATURE.ADC_MEASUREMENT assert feature == hidpp20_constants.FEATURE.ADC_MEASUREMENT
assert battery.level == 90 assert battery.level == 90
assert battery.status == common.Battery.STATUS.recharging assert battery.status == common.BatteryStatus.RECHARGING
assert battery.voltage == 0x1000 assert battery.voltage == 0x1000