device: change status battery fields to Battery objects
This commit is contained in:
parent
3916c189be
commit
64d8cad81a
|
@ -1,4 +1,5 @@
|
|||
## Copyright (C) 2012-2013 Daniel Pavel
|
||||
## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||
##
|
||||
## 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
|
||||
|
@ -18,9 +19,13 @@
|
|||
|
||||
from binascii import hexlify as _hexlify
|
||||
from collections import namedtuple
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
import yaml as _yaml
|
||||
|
||||
from .i18n import _
|
||||
|
||||
|
||||
def is_string(d):
|
||||
return isinstance(d, str)
|
||||
|
@ -546,9 +551,27 @@ class KwException(Exception):
|
|||
"""Firmware information."""
|
||||
FirmwareInfo = namedtuple("FirmwareInfo", ["kind", "name", "version", "extras"])
|
||||
|
||||
BATTERY_APPROX = NamedInts(empty=0, critical=5, low=20, good=50, full=90)
|
||||
|
||||
BATTERY_STATUS = NamedInts(
|
||||
@dataclass
|
||||
class Battery:
|
||||
"""Information about the current state of a battery"""
|
||||
|
||||
level: Optional[NamedInt | int]
|
||||
next_level: Optional[NamedInt | int]
|
||||
status: Optional[NamedInt]
|
||||
voltage: Optional[int]
|
||||
light_level: Optional[int] = None # light level for devices with solaar recharging
|
||||
|
||||
def __post_init__(self):
|
||||
if self.level is None: # infer level from status if needed and possible
|
||||
if self.status == Battery.STATUS.full:
|
||||
self.level = Battery.APPROX.full
|
||||
elif self.status in (Battery.STATUS.almost_full, Battery.STATUS.recharging):
|
||||
self.level = Battery.APPROX.good
|
||||
elif self.status == Battery.STATUS.slow_recharge:
|
||||
self.level = Battery.APPROX.low
|
||||
|
||||
STATUS = NamedInts(
|
||||
discharging=0x00,
|
||||
recharging=0x01,
|
||||
almost_full=0x02,
|
||||
|
@ -556,8 +579,29 @@ BATTERY_STATUS = NamedInts(
|
|||
slow_recharge=0x04,
|
||||
invalid_battery=0x05,
|
||||
thermal_error=0x06,
|
||||
)
|
||||
)
|
||||
|
||||
APPROX = NamedInts(empty=0, critical=5, low=20, good=50, full=90)
|
||||
|
||||
def BATTERY_OK(status):
|
||||
return status not in (BATTERY_STATUS.invalid_battery, BATTERY_STATUS.thermal_error)
|
||||
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
|
||||
)
|
||||
|
||||
def charging(self):
|
||||
return self.status in (
|
||||
Battery.STATUS.recharging,
|
||||
Battery.STATUS.almost_full,
|
||||
Battery.STATUS.full,
|
||||
Battery.STATUS.slow_recharge,
|
||||
)
|
||||
|
||||
def to_str(self):
|
||||
if isinstance(self.level, NamedInt):
|
||||
return _("Battery: %(level)s (%(status)s)") % {"level": _(self.level), "status": _(self.status)}
|
||||
elif isinstance(self.level, int):
|
||||
return _("Battery: %(percent)d%% (%(status)s)") % {"percent": self.level, "status": _(self.status)}
|
||||
else:
|
||||
return ""
|
||||
|
|
|
@ -342,10 +342,10 @@ class Device:
|
|||
if battery_feature != 0:
|
||||
result = _hidpp20.get_battery(self, battery_feature)
|
||||
try:
|
||||
feature, level, next, status, voltage = result
|
||||
feature, battery = result
|
||||
if self.persister and battery_feature is None:
|
||||
self.persister["_battery"] = feature
|
||||
return level, next, status, voltage
|
||||
return battery
|
||||
except Exception:
|
||||
if self.persister and battery_feature is None:
|
||||
self.persister["_battery"] = result
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
|
||||
import logging
|
||||
|
||||
from .common import BATTERY_APPROX as _BATTERY_APPROX
|
||||
from .common import BATTERY_STATUS as _BATTERY_STATUS
|
||||
from .common import Battery as _Battery
|
||||
from .common import FirmwareInfo as _FirmwareInfo
|
||||
from .common import bytes2int as _bytes2int
|
||||
from .common import int2bytes as _int2bytes
|
||||
|
@ -122,17 +121,17 @@ class Hidpp10:
|
|||
return
|
||||
|
||||
if battery_level is not None:
|
||||
if battery_level < _BATTERY_APPROX.critical:
|
||||
if battery_level < _Battery.APPROX.critical:
|
||||
# 1 orange, and force blink
|
||||
v1, v2 = 0x22, 0x00
|
||||
warning = True
|
||||
elif battery_level < _BATTERY_APPROX.low:
|
||||
elif battery_level < _Battery.APPROX.low:
|
||||
# 1 orange
|
||||
v1, v2 = 0x22, 0x00
|
||||
elif battery_level < _BATTERY_APPROX.good:
|
||||
elif battery_level < _Battery.APPROX.good:
|
||||
# 1 green
|
||||
v1, v2 = 0x20, 0x00
|
||||
elif battery_level < _BATTERY_APPROX.full:
|
||||
elif battery_level < _Battery.APPROX.full:
|
||||
# 2 greens
|
||||
v1, v2 = 0x20, 0x02
|
||||
else:
|
||||
|
@ -196,38 +195,38 @@ def parse_battery_status(register, reply):
|
|||
charge = ord(reply[:1])
|
||||
status_byte = ord(reply[2:3]) & 0xF0
|
||||
status_text = (
|
||||
_BATTERY_STATUS.discharging
|
||||
_Battery.STATUS.discharging
|
||||
if status_byte == 0x30
|
||||
else _BATTERY_STATUS.recharging
|
||||
else _Battery.STATUS.recharging
|
||||
if status_byte == 0x50
|
||||
else _BATTERY_STATUS.full
|
||||
else _Battery.STATUS.full
|
||||
if status_byte == 0x90
|
||||
else None
|
||||
)
|
||||
return charge, None, status_text, None
|
||||
return _Battery(charge, None, status_text, None)
|
||||
|
||||
if register == REGISTERS.battery_status:
|
||||
status_byte = ord(reply[:1])
|
||||
charge = (
|
||||
_BATTERY_APPROX.full
|
||||
_Battery.APPROX.full
|
||||
if status_byte == 7 # full
|
||||
else _BATTERY_APPROX.good
|
||||
else _Battery.APPROX.good
|
||||
if status_byte == 5 # good
|
||||
else _BATTERY_APPROX.low
|
||||
else _Battery.APPROX.low
|
||||
if status_byte == 3 # low
|
||||
else _BATTERY_APPROX.critical
|
||||
else _Battery.APPROX.critical
|
||||
if status_byte == 1 # critical
|
||||
# pure 'charging' notifications may come without a status
|
||||
else _BATTERY_APPROX.empty
|
||||
else _Battery.APPROX.empty
|
||||
)
|
||||
|
||||
charging_byte = ord(reply[1:2])
|
||||
if charging_byte == 0x00:
|
||||
status_text = _BATTERY_STATUS.discharging
|
||||
status_text = _Battery.STATUS.discharging
|
||||
elif charging_byte & 0x21 == 0x21:
|
||||
status_text = _BATTERY_STATUS.recharging
|
||||
status_text = _Battery.STATUS.recharging
|
||||
elif charging_byte & 0x22 == 0x22:
|
||||
status_text = _BATTERY_STATUS.full
|
||||
status_text = _Battery.STATUS.full
|
||||
else:
|
||||
logger.warning("could not parse 0x07 battery status: %02X (level %02X)", charging_byte, status_byte)
|
||||
status_text = None
|
||||
|
@ -237,4 +236,4 @@ def parse_battery_status(register, reply):
|
|||
charge = None
|
||||
|
||||
# Return None for next charge level and voltage as these are not in HID++ 1.0 spec
|
||||
return charge, None, status_text, None
|
||||
return _Battery(charge, None, status_text, None)
|
||||
|
|
|
@ -27,8 +27,7 @@ import yaml as _yaml
|
|||
|
||||
from . import exceptions, special_keys
|
||||
from . import hidpp10_constants as _hidpp10_constants
|
||||
from .common import BATTERY_APPROX as _BATTERY_APPROX
|
||||
from .common import BATTERY_STATUS as _BATTERY_STATUS
|
||||
from .common import Battery
|
||||
from .common import FirmwareInfo as _FirmwareInfo
|
||||
from .common import NamedInt as _NamedInt
|
||||
from .common import NamedInts as _NamedInts
|
||||
|
@ -1730,31 +1729,31 @@ battery_functions = {
|
|||
def decipher_battery_status(report):
|
||||
discharge, next, status = _unpack("!BBB", report[:3])
|
||||
discharge = None if discharge == 0 else discharge
|
||||
status = _BATTERY_STATUS[status]
|
||||
status = Battery.STATUS[status]
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("battery status %s%% charged, next %s%%, status %s", discharge, next, status)
|
||||
return FEATURE.BATTERY_STATUS, discharge, next, status, None
|
||||
return FEATURE.BATTERY_STATUS, Battery(discharge, next, status, None)
|
||||
|
||||
|
||||
def decipher_battery_voltage(report):
|
||||
voltage, flags = _unpack(">HB", report[:3])
|
||||
status = _BATTERY_STATUS.discharging
|
||||
status = Battery.STATUS.discharging
|
||||
charge_sts = ERROR.unknown
|
||||
charge_lvl = CHARGE_LEVEL.average
|
||||
charge_type = CHARGE_TYPE.standard
|
||||
if flags & (1 << 7):
|
||||
status = _BATTERY_STATUS.recharging
|
||||
status = Battery.STATUS.recharging
|
||||
charge_sts = CHARGE_STATUS[flags & 0x03]
|
||||
if charge_sts is None:
|
||||
charge_sts = ERROR.unknown
|
||||
elif charge_sts == CHARGE_STATUS.full:
|
||||
charge_lvl = CHARGE_LEVEL.full
|
||||
status = _BATTERY_STATUS.full
|
||||
status = Battery.STATUS.full
|
||||
if flags & (1 << 3):
|
||||
charge_type = CHARGE_TYPE.fast
|
||||
elif flags & (1 << 4):
|
||||
charge_type = CHARGE_TYPE.slow
|
||||
status = _BATTERY_STATUS.slow_recharge
|
||||
status = Battery.STATUS.slow_recharge
|
||||
elif flags & (1 << 5):
|
||||
charge_lvl = CHARGE_LEVEL.critical
|
||||
for level in battery_voltage_remaining:
|
||||
|
@ -1771,26 +1770,26 @@ def decipher_battery_voltage(report):
|
|||
charge_lvl,
|
||||
charge_type,
|
||||
)
|
||||
return FEATURE.BATTERY_VOLTAGE, charge_lvl, None, status, voltage
|
||||
return FEATURE.BATTERY_VOLTAGE, Battery(charge_lvl, None, status, voltage)
|
||||
|
||||
|
||||
def decipher_battery_unified(report):
|
||||
discharge, level, status, _ignore = _unpack("!BBBB", report[:4])
|
||||
status = _BATTERY_STATUS[status]
|
||||
status = Battery.STATUS[status]
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("battery unified %s%% charged, level %s, charging %s", discharge, level, status)
|
||||
level = (
|
||||
_BATTERY_APPROX.full
|
||||
Battery.APPROX.full
|
||||
if level == 8 # full
|
||||
else _BATTERY_APPROX.good
|
||||
else Battery.APPROX.good
|
||||
if level == 4 # good
|
||||
else _BATTERY_APPROX.low
|
||||
else Battery.APPROX.low
|
||||
if level == 2 # low
|
||||
else _BATTERY_APPROX.critical
|
||||
else Battery.APPROX.critical
|
||||
if level == 1 # critical
|
||||
else _BATTERY_APPROX.empty
|
||||
else Battery.APPROX.empty
|
||||
)
|
||||
return FEATURE.UNIFIED_BATTERY, discharge if discharge else level, None, status, None
|
||||
return FEATURE.UNIFIED_BATTERY, Battery(discharge if discharge else level, None, status, None)
|
||||
|
||||
|
||||
def decipher_adc_measurement(report):
|
||||
|
@ -1801,5 +1800,5 @@ def decipher_adc_measurement(report):
|
|||
charge_level = level[1]
|
||||
break
|
||||
if flags & 0x01:
|
||||
status = _BATTERY_STATUS.recharging if flags & 0x02 else _BATTERY_STATUS.discharging
|
||||
return FEATURE.ADC_MEASUREMENT, charge_level, None, status, adc
|
||||
status = Battery.STATUS.recharging if flags & 0x02 else Battery.STATUS.discharging
|
||||
return FEATURE.ADC_MEASUREMENT, Battery(charge_level, None, status, adc)
|
||||
|
|
|
@ -29,7 +29,7 @@ from . import hidpp10_constants as _hidpp10_constants
|
|||
from . import hidpp20_constants as _hidpp20_constants
|
||||
from . import settings_templates as _st
|
||||
from .base import DJ_MESSAGE_ID as _DJ_MESSAGE_ID
|
||||
from .common import BATTERY_STATUS as _BATTERY_STATUS
|
||||
from .common import Battery as _Battery
|
||||
from .common import strhex as _strhex
|
||||
from .i18n import _
|
||||
from .status import ALERT as _ALERT
|
||||
|
@ -218,11 +218,9 @@ def _process_hidpp10_custom_notification(device, status, n):
|
|||
logger.debug("%s (%s) custom notification %s", device, device.protocol, n)
|
||||
|
||||
if n.sub_id in (_R.battery_status, _R.battery_charge):
|
||||
# message layout: 10 ix <register> <xx> <yy> <zz> <00>
|
||||
assert n.data[-1:] == b"\x00"
|
||||
data = chr(n.address).encode() + n.data
|
||||
charge, next_charge, status_text, voltage = hidpp10.parse_battery_status(n.sub_id, data)
|
||||
status.set_battery_info(charge, next_charge, status_text, voltage)
|
||||
status.set_battery_info(hidpp10.parse_battery_status(n.sub_id, data))
|
||||
return True
|
||||
|
||||
logger.warning("%s: unrecognized %s", device, n)
|
||||
|
@ -296,8 +294,7 @@ def _process_feature_notification(device, status, n, feature):
|
|||
|
||||
if feature == _F.BATTERY_STATUS:
|
||||
if n.address == 0x00:
|
||||
_ignore, discharge_level, discharge_next_level, battery_status, voltage = hidpp20.decipher_battery_status(n.data)
|
||||
status.set_battery_info(discharge_level, discharge_next_level, battery_status, voltage)
|
||||
status.set_battery_info(hidpp20.decipher_battery_status(n.data)[1])
|
||||
elif n.address == 0x10:
|
||||
if logger.isEnabledFor(logging.INFO):
|
||||
logger.info("%s: spurious BATTERY status %s", device, n)
|
||||
|
@ -306,15 +303,13 @@ def _process_feature_notification(device, status, n, feature):
|
|||
|
||||
elif feature == _F.BATTERY_VOLTAGE:
|
||||
if n.address == 0x00:
|
||||
_ignore, level, nextl, battery_status, voltage = hidpp20.decipher_battery_voltage(n.data)
|
||||
status.set_battery_info(level, nextl, battery_status, voltage)
|
||||
status.set_battery_info(hidpp20.decipher_battery_voltage(n.data)[1])
|
||||
else:
|
||||
logger.warning("%s: unknown VOLTAGE %s", device, n)
|
||||
|
||||
elif feature == _F.UNIFIED_BATTERY:
|
||||
if n.address == 0x00:
|
||||
_ignore, level, nextl, battery_status, voltage = hidpp20.decipher_battery_unified(n.data)
|
||||
status.set_battery_info(level, nextl, battery_status, voltage)
|
||||
status.set_battery_info(hidpp20.decipher_battery_unified(n.data)[1])
|
||||
else:
|
||||
logger.warning("%s: unknown UNIFIED BATTERY %s", device, n)
|
||||
|
||||
|
@ -322,8 +317,7 @@ def _process_feature_notification(device, status, n, feature):
|
|||
if n.address == 0x00:
|
||||
result = hidpp20.decipher_adc_measurement(n.data)
|
||||
if result:
|
||||
_ignore, level, nextl, battery_status, voltage = result
|
||||
status.set_battery_info(level, nextl, battery_status, voltage)
|
||||
status.set_battery_info(result[1])
|
||||
else: # this feature is used to signal device becoming inactive
|
||||
status.changed(active=False)
|
||||
else:
|
||||
|
@ -334,15 +328,13 @@ def _process_feature_notification(device, status, n, feature):
|
|||
charge, lux, adc = _unpack("!BHH", n.data[:5])
|
||||
# guesstimate the battery voltage, emphasis on 'guess'
|
||||
# status_text = '%1.2fV' % (adc * 2.67793237653 / 0x0672)
|
||||
status_text = _BATTERY_STATUS.discharging
|
||||
status_text = _Battery.STATUS.discharging
|
||||
if n.address == 0x00:
|
||||
status[_K.LIGHT_LEVEL] = None
|
||||
status.set_battery_info(charge, None, status_text, None)
|
||||
status.set_battery_info(_Battery(charge, None, status_text, None))
|
||||
elif n.address == 0x10:
|
||||
status[_K.LIGHT_LEVEL] = lux
|
||||
if lux > 200:
|
||||
status_text = _BATTERY_STATUS.recharging
|
||||
status.set_battery_info(charge, None, status_text, None)
|
||||
status_text = _Battery.STATUS.recharging
|
||||
status.set_battery_info(_Battery(charge, None, status_text, None, lux))
|
||||
elif n.address == 0x20:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("%s: Light Check button pressed", device)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
## Copyright (C) 2012-2013 Daniel Pavel
|
||||
## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/
|
||||
##
|
||||
## 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
|
||||
|
@ -20,11 +21,7 @@ from . import hidpp10
|
|||
from . import hidpp10_constants as _hidpp10_constants
|
||||
from . import hidpp20_constants as _hidpp20_constants
|
||||
from . import settings as _settings
|
||||
from .common import BATTERY_APPROX as _BATTERY_APPROX
|
||||
from .common import BATTERY_OK as _BATTERY_OK
|
||||
from .common import BATTERY_STATUS as _BATTERY_STATUS
|
||||
from .common import NamedInt as _NamedInt
|
||||
from .common import NamedInts as _NamedInts
|
||||
from .common import Battery, NamedInts
|
||||
from .i18n import _, ngettext
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -33,27 +30,9 @@ _R = _hidpp10_constants.REGISTERS
|
|||
|
||||
_hidpp10 = hidpp10.Hidpp10()
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
ALERT = NamedInts(NONE=0x00, NOTIFICATION=0x01, SHOW_WINDOW=0x02, ATTENTION=0x04, ALL=0xFF)
|
||||
|
||||
ALERT = _NamedInts(NONE=0x00, NOTIFICATION=0x01, SHOW_WINDOW=0x02, ATTENTION=0x04, ALL=0xFF)
|
||||
|
||||
KEYS = _NamedInts(
|
||||
BATTERY_LEVEL=1,
|
||||
BATTERY_CHARGING=2,
|
||||
BATTERY_STATUS=3,
|
||||
LIGHT_LEVEL=4,
|
||||
LINK_ENCRYPTED=5,
|
||||
NOTIFICATION_FLAGS=6,
|
||||
ERROR=7,
|
||||
BATTERY_NEXT_LEVEL=8,
|
||||
BATTERY_VOLTAGE=9,
|
||||
)
|
||||
|
||||
# If the battery charge is under this percentage, trigger an attention event
|
||||
# (blink systray icon/notification/whatever).
|
||||
_BATTERY_ATTENTION_LEVEL = 5
|
||||
KEYS = NamedInts(LINK_ENCRYPTED=5, NOTIFICATION_FLAGS=6, ERROR=7)
|
||||
|
||||
|
||||
def attach_to(device, changed_callback):
|
||||
|
@ -67,11 +46,6 @@ def attach_to(device, changed_callback):
|
|||
device.status = DeviceStatus(device, changed_callback)
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
class ReceiverStatus(dict):
|
||||
"""The 'runtime' status of a receiver, mostly about the pairing process --
|
||||
is the pairing lock open or closed, any pairing errors, etc.
|
||||
|
@ -111,15 +85,10 @@ class ReceiverStatus(dict):
|
|||
self._changed_callback(self._receiver, alert=alert, reason=reason)
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
class DeviceStatus(dict):
|
||||
"""Holds the 'runtime' status of a peripheral -- things like
|
||||
active/inactive, battery charge, lux, etc. It updates them mostly by
|
||||
processing incoming notification events from the device itself.
|
||||
"""Holds the 'runtime' status of a peripheral
|
||||
Currently _active, battery -- dict entries are being moved to attributs
|
||||
Updates mostly come from incoming notification events from the device itself.
|
||||
"""
|
||||
|
||||
def __init__(self, device, changed_callback):
|
||||
|
@ -128,19 +97,10 @@ class DeviceStatus(dict):
|
|||
assert changed_callback
|
||||
self._changed_callback = changed_callback
|
||||
self._active = None # is the device active?
|
||||
self.battery = None
|
||||
|
||||
def to_string(self):
|
||||
status = ""
|
||||
battery_level = self.get(KEYS.BATTERY_LEVEL)
|
||||
if battery_level is not None:
|
||||
if isinstance(battery_level, _NamedInt):
|
||||
status = _("Battery: %(level)s") % {"level": _(str(battery_level))}
|
||||
else:
|
||||
status = _("Battery: %(percent)d%%") % {"percent": battery_level}
|
||||
battery_status = self.get(KEYS.BATTERY_STATUS)
|
||||
if battery_status is not None:
|
||||
status += " (%s)" % _(str(battery_status))
|
||||
return status
|
||||
return self.battery.to_str() if self.battery is not None else ""
|
||||
|
||||
def __repr__(self):
|
||||
return "{" + ", ".join("'%s': %r" % (k, v) for k, v in self.items()) + "}"
|
||||
|
@ -150,81 +110,38 @@ class DeviceStatus(dict):
|
|||
|
||||
__nonzero__ = __bool__
|
||||
|
||||
def set_battery_info(self, level, nextLevel, status, voltage):
|
||||
def set_battery_info(self, info):
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("%s: battery %s, %s", self._device, level, status)
|
||||
logger.debug("%s: battery %s, %s", self._device, info.level, info.status)
|
||||
if info.level is None and self.battery: # use previous level if missing from new information
|
||||
info.level = self.battery.level
|
||||
|
||||
if level is None:
|
||||
# Some notifications may come with no battery level info, just
|
||||
# charging state info, so do our best to infer a level (even if it is just the last level)
|
||||
# It is not always possible to do this well
|
||||
if status == _BATTERY_STATUS.full:
|
||||
level = _BATTERY_APPROX.full
|
||||
elif status in (_BATTERY_STATUS.almost_full, _BATTERY_STATUS.recharging):
|
||||
level = _BATTERY_APPROX.good
|
||||
elif status == _BATTERY_STATUS.slow_recharge:
|
||||
level = _BATTERY_APPROX.low
|
||||
else:
|
||||
level = self.get(KEYS.BATTERY_LEVEL)
|
||||
else:
|
||||
assert isinstance(level, int)
|
||||
changed = self.battery != info
|
||||
self.battery = info
|
||||
|
||||
# TODO: this is also executed when pressing Fn+F7 on K800.
|
||||
old_level, self[KEYS.BATTERY_LEVEL] = self.get(KEYS.BATTERY_LEVEL), level
|
||||
old_status, self[KEYS.BATTERY_STATUS] = self.get(KEYS.BATTERY_STATUS), status
|
||||
self[KEYS.BATTERY_NEXT_LEVEL] = nextLevel
|
||||
old_voltage, self[KEYS.BATTERY_VOLTAGE] = self.get(KEYS.BATTERY_VOLTAGE), voltage
|
||||
|
||||
charging = status in (
|
||||
_BATTERY_STATUS.recharging,
|
||||
_BATTERY_STATUS.almost_full,
|
||||
_BATTERY_STATUS.full,
|
||||
_BATTERY_STATUS.slow_recharge,
|
||||
)
|
||||
old_charging, self[KEYS.BATTERY_CHARGING] = self.get(KEYS.BATTERY_CHARGING), charging
|
||||
|
||||
changed = old_level != level or old_status != status or old_charging != charging or old_voltage != voltage
|
||||
alert, reason = ALERT.NONE, None
|
||||
|
||||
if _BATTERY_OK(status) and (level is None or level > _BATTERY_ATTENTION_LEVEL):
|
||||
if info.ok():
|
||||
self[KEYS.ERROR] = None
|
||||
else:
|
||||
logger.warning("%s: battery %d%%, ALERT %s", self._device, level, status)
|
||||
if self.get(KEYS.ERROR) != status:
|
||||
self[KEYS.ERROR] = status
|
||||
# only show the notification once
|
||||
logger.warning("%s: battery %d%%, ALERT %s", self._device, info.level, info.status)
|
||||
if self.get(KEYS.ERROR) != info.status:
|
||||
self[KEYS.ERROR] = info.status
|
||||
alert = ALERT.NOTIFICATION | ALERT.ATTENTION
|
||||
if isinstance(level, _NamedInt):
|
||||
reason = _("Battery: %(level)s (%(status)s)") % {"level": _(level), "status": _(status)}
|
||||
else:
|
||||
reason = _("Battery: %(percent)d%% (%(status)s)") % {"percent": level, "status": status.name}
|
||||
reason = info.to_str()
|
||||
|
||||
if changed or reason or not self._active: # a battery response means device is active
|
||||
# update the leds on the device, if any
|
||||
_hidpp10.set_3leds(self._device, level, charging=charging, warning=bool(alert))
|
||||
_hidpp10.set_3leds(self._device, info.level, charging=info.charging(), warning=bool(alert))
|
||||
self.changed(active=True, alert=alert, reason=reason)
|
||||
|
||||
# Retrieve and regularize battery status
|
||||
def read_battery(self):
|
||||
if self._active:
|
||||
assert self._device
|
||||
battery = self._device.battery()
|
||||
self.set_battery_keys(battery)
|
||||
|
||||
def set_battery_keys(self, battery):
|
||||
if battery is not None:
|
||||
level, nextLevel, status, voltage = battery
|
||||
self.set_battery_info(level, nextLevel, status, voltage)
|
||||
elif self.get(KEYS.BATTERY_STATUS, None) is not None:
|
||||
self[KEYS.BATTERY_STATUS] = None
|
||||
self[KEYS.BATTERY_CHARGING] = None
|
||||
self[KEYS.BATTERY_VOLTAGE] = None
|
||||
self.changed()
|
||||
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):
|
||||
assert self._changed_callback
|
||||
d = self._device
|
||||
# assert d # may be invalid when processing the 'unpaired' notification
|
||||
|
||||
if active is not None:
|
||||
d.online = active
|
||||
|
@ -236,12 +153,10 @@ class DeviceStatus(dict):
|
|||
# goes idle, and we can't tell the difference right now).
|
||||
if d.protocol < 2.0:
|
||||
self[KEYS.NOTIFICATION_FLAGS] = d.enable_connection_notifications()
|
||||
|
||||
# battery information may have changed so try to read it now
|
||||
self.read_battery()
|
||||
|
||||
# Push settings for new devices (was_active is None),
|
||||
# when devices request software reconfiguration
|
||||
# Push settings for new devices when devices request software reconfiguration
|
||||
# and when devices become active if they don't have wireless device status feature,
|
||||
if (
|
||||
was_active is None
|
||||
|
@ -253,10 +168,6 @@ class DeviceStatus(dict):
|
|||
logger.info("%s pushing device settings %s", d, d.settings)
|
||||
_settings.apply_all_settings(d)
|
||||
|
||||
else:
|
||||
if was_active: # don't clear status when devices go inactive
|
||||
pass
|
||||
|
||||
# if logger.isEnabledFor(logging.DEBUG):
|
||||
# logger.debug("device %d changed: active=%s %s", d.number, self._active, dict(self))
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("device %d changed: active=%s %s", d.number, self._active, self.battery)
|
||||
self._changed_callback(d, alert, reason)
|
||||
|
|
Loading…
Reference in New Issue