parent
574a95da50
commit
85149a809e
|
@ -30,6 +30,7 @@ from .settings_templates import check_feature_settings as _check_feature_setting
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_hidpp10 = hidpp10.Hidpp10()
|
||||||
_R = hidpp10_constants.REGISTERS
|
_R = hidpp10_constants.REGISTERS
|
||||||
_IR = hidpp10_constants.INFO_SUBREGISTERS
|
_IR = hidpp10_constants.INFO_SUBREGISTERS
|
||||||
|
|
||||||
|
@ -201,7 +202,7 @@ class Device:
|
||||||
if self.protocol >= 2.0:
|
if self.protocol >= 2.0:
|
||||||
self._firmware = hidpp20.get_firmware(self)
|
self._firmware = hidpp20.get_firmware(self)
|
||||||
else:
|
else:
|
||||||
self._firmware = hidpp10.get_firmware(self)
|
self._firmware = _hidpp10.get_firmware(self)
|
||||||
return self._firmware or ()
|
return self._firmware or ()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -315,7 +316,7 @@ class Device:
|
||||||
|
|
||||||
def battery(self): # None or level, next, status, voltage
|
def battery(self): # None or level, next, status, voltage
|
||||||
if self.protocol < 2.0:
|
if self.protocol < 2.0:
|
||||||
return hidpp10.get_battery(self)
|
return _hidpp10.get_battery(self)
|
||||||
else:
|
else:
|
||||||
battery_feature = self.persister.get("_battery", None) if self.persister else None
|
battery_feature = self.persister.get("_battery", None) if self.persister else None
|
||||||
if battery_feature != 0:
|
if battery_feature != 0:
|
||||||
|
@ -344,11 +345,11 @@ class Device:
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
set_flag_bits = 0
|
set_flag_bits = 0
|
||||||
ok = hidpp10.set_notification_flags(self, set_flag_bits)
|
ok = _hidpp10.set_notification_flags(self, set_flag_bits)
|
||||||
if not ok:
|
if not ok:
|
||||||
logger.warning("%s: failed to %s device notifications", self, "enable" if enable else "disable")
|
logger.warning("%s: failed to %s device notifications", self, "enable" if enable else "disable")
|
||||||
|
|
||||||
flag_bits = hidpp10.get_notification_flags(self)
|
flag_bits = _hidpp10.get_notification_flags(self)
|
||||||
flag_names = None if flag_bits is None else tuple(hidpp10_constants.NOTIFICATION_FLAG.flag_names(flag_bits))
|
flag_names = None if flag_bits is None else tuple(hidpp10_constants.NOTIFICATION_FLAG.flag_names(flag_bits))
|
||||||
if logger.isEnabledFor(logging.INFO):
|
if logger.isEnabledFor(logging.INFO):
|
||||||
logger.info("%s: device notifications %s %s", self, "enabled" if enable else "disabled", flag_names)
|
logger.info("%s: device notifications %s %s", self, "enabled" if enable else "disabled", flag_names)
|
||||||
|
|
|
@ -46,35 +46,158 @@ def write_register(device, register_number, *value):
|
||||||
return device.request(request_id, *value)
|
return device.request(request_id, *value)
|
||||||
|
|
||||||
|
|
||||||
def get_battery(device):
|
class Hidpp10:
|
||||||
assert device is not None
|
def get_battery(self, device):
|
||||||
assert device.kind is not None
|
assert device is not None
|
||||||
if not device.online:
|
assert device.kind is not None
|
||||||
return
|
if not device.online:
|
||||||
"""Reads a device's battery level, if provided by the HID++ 1.0 protocol."""
|
return
|
||||||
if device.protocol and device.protocol >= 2.0:
|
"""Reads a device's battery level, if provided by the HID++ 1.0 protocol."""
|
||||||
# let's just assume HID++ 2.0 devices do not provide the battery info in a register
|
if device.protocol and device.protocol >= 2.0:
|
||||||
return
|
# let's just assume HID++ 2.0 devices do not provide the battery info in a register
|
||||||
|
|
||||||
for r in (REGISTERS.battery_status, REGISTERS.battery_charge):
|
|
||||||
if r in device.registers:
|
|
||||||
reply = read_register(device, r)
|
|
||||||
if reply:
|
|
||||||
return parse_battery_status(r, reply)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# the descriptor does not tell us which register this device has, try them both
|
for r in (REGISTERS.battery_status, REGISTERS.battery_charge):
|
||||||
reply = read_register(device, REGISTERS.battery_charge)
|
if r in device.registers:
|
||||||
if reply:
|
reply = read_register(device, r)
|
||||||
# remember this for the next time
|
if reply:
|
||||||
device.registers.append(REGISTERS.battery_charge)
|
return parse_battery_status(r, reply)
|
||||||
return parse_battery_status(REGISTERS.battery_charge, reply)
|
return
|
||||||
|
|
||||||
reply = read_register(device, REGISTERS.battery_status)
|
# the descriptor does not tell us which register this device has, try them both
|
||||||
if reply:
|
reply = read_register(device, REGISTERS.battery_charge)
|
||||||
# remember this for the next time
|
if reply:
|
||||||
device.registers.append(REGISTERS.battery_status)
|
# remember this for the next time
|
||||||
return parse_battery_status(REGISTERS.battery_status, reply)
|
device.registers.append(REGISTERS.battery_charge)
|
||||||
|
return parse_battery_status(REGISTERS.battery_charge, reply)
|
||||||
|
|
||||||
|
reply = read_register(device, REGISTERS.battery_status)
|
||||||
|
if reply:
|
||||||
|
# remember this for the next time
|
||||||
|
device.registers.append(REGISTERS.battery_status)
|
||||||
|
return parse_battery_status(REGISTERS.battery_status, reply)
|
||||||
|
|
||||||
|
def get_firmware(self, device):
|
||||||
|
assert device is not None
|
||||||
|
|
||||||
|
firmware = [None, None, None]
|
||||||
|
|
||||||
|
reply = read_register(device, REGISTERS.firmware, 0x01)
|
||||||
|
if not reply:
|
||||||
|
# won't be able to read any of it now...
|
||||||
|
return
|
||||||
|
|
||||||
|
fw_version = _strhex(reply[1:3])
|
||||||
|
fw_version = "%s.%s" % (fw_version[0:2], fw_version[2:4])
|
||||||
|
reply = read_register(device, REGISTERS.firmware, 0x02)
|
||||||
|
if reply:
|
||||||
|
fw_version += ".B" + _strhex(reply[1:3])
|
||||||
|
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, "", fw_version, None)
|
||||||
|
firmware[0] = fw
|
||||||
|
|
||||||
|
reply = read_register(device, REGISTERS.firmware, 0x04)
|
||||||
|
if reply:
|
||||||
|
bl_version = _strhex(reply[1:3])
|
||||||
|
bl_version = "%s.%s" % (bl_version[0:2], bl_version[2:4])
|
||||||
|
bl = _FirmwareInfo(FIRMWARE_KIND.Bootloader, "", bl_version, None)
|
||||||
|
firmware[1] = bl
|
||||||
|
|
||||||
|
reply = read_register(device, REGISTERS.firmware, 0x03)
|
||||||
|
if reply:
|
||||||
|
o_version = _strhex(reply[1:3])
|
||||||
|
o_version = "%s.%s" % (o_version[0:2], o_version[2:4])
|
||||||
|
o = _FirmwareInfo(FIRMWARE_KIND.Other, "", o_version, None)
|
||||||
|
firmware[2] = o
|
||||||
|
|
||||||
|
if any(firmware):
|
||||||
|
return tuple(f for f in firmware if f)
|
||||||
|
|
||||||
|
def set_3leds(self, device, battery_level=None, charging=None, warning=None):
|
||||||
|
assert device is not None
|
||||||
|
assert device.kind is not None
|
||||||
|
if not device.online:
|
||||||
|
return
|
||||||
|
|
||||||
|
if REGISTERS.three_leds not in device.registers:
|
||||||
|
return
|
||||||
|
|
||||||
|
if battery_level is not None:
|
||||||
|
if battery_level < _BATTERY_APPROX.critical:
|
||||||
|
# 1 orange, and force blink
|
||||||
|
v1, v2 = 0x22, 0x00
|
||||||
|
warning = True
|
||||||
|
elif battery_level < _BATTERY_APPROX.low:
|
||||||
|
# 1 orange
|
||||||
|
v1, v2 = 0x22, 0x00
|
||||||
|
elif battery_level < _BATTERY_APPROX.good:
|
||||||
|
# 1 green
|
||||||
|
v1, v2 = 0x20, 0x00
|
||||||
|
elif battery_level < _BATTERY_APPROX.full:
|
||||||
|
# 2 greens
|
||||||
|
v1, v2 = 0x20, 0x02
|
||||||
|
else:
|
||||||
|
# all 3 green
|
||||||
|
v1, v2 = 0x20, 0x22
|
||||||
|
if warning:
|
||||||
|
# set the blinking flag for the leds already set
|
||||||
|
v1 |= v1 >> 1
|
||||||
|
v2 |= v2 >> 1
|
||||||
|
elif charging:
|
||||||
|
# blink all green
|
||||||
|
v1, v2 = 0x30, 0x33
|
||||||
|
elif warning:
|
||||||
|
# 1 red
|
||||||
|
v1, v2 = 0x02, 0x00
|
||||||
|
else:
|
||||||
|
# turn off all leds
|
||||||
|
v1, v2 = 0x11, 0x11
|
||||||
|
|
||||||
|
write_register(device, REGISTERS.three_leds, v1, v2)
|
||||||
|
|
||||||
|
def get_notification_flags(self, device):
|
||||||
|
assert device is not None
|
||||||
|
|
||||||
|
# Avoid a call if the device is not online,
|
||||||
|
# or the device does not support registers.
|
||||||
|
if device.kind is not None:
|
||||||
|
# peripherals with protocol >= 2.0 don't support registers
|
||||||
|
if device.protocol and device.protocol >= 2.0:
|
||||||
|
return
|
||||||
|
|
||||||
|
flags = read_register(device, REGISTERS.notifications)
|
||||||
|
if flags is not None:
|
||||||
|
assert len(flags) == 3
|
||||||
|
return _bytes2int(flags)
|
||||||
|
|
||||||
|
def set_notification_flags(self, device, *flag_bits):
|
||||||
|
assert device is not None
|
||||||
|
|
||||||
|
# Avoid a call if the device is not online,
|
||||||
|
# or the device does not support registers.
|
||||||
|
if device.kind is not None:
|
||||||
|
# peripherals with protocol >= 2.0 don't support registers
|
||||||
|
if device.protocol and device.protocol >= 2.0:
|
||||||
|
return
|
||||||
|
|
||||||
|
flag_bits = sum(int(b) for b in flag_bits)
|
||||||
|
assert flag_bits & 0x00FFFFFF == flag_bits
|
||||||
|
result = write_register(device, REGISTERS.notifications, _int2bytes(flag_bits, 3))
|
||||||
|
return result is not None
|
||||||
|
|
||||||
|
def get_device_features(self, device):
|
||||||
|
assert device is not None
|
||||||
|
|
||||||
|
# Avoid a call if the device is not online,
|
||||||
|
# or the device does not support registers.
|
||||||
|
if device.kind is not None:
|
||||||
|
# peripherals with protocol >= 2.0 don't support registers
|
||||||
|
if device.protocol and device.protocol >= 2.0:
|
||||||
|
return
|
||||||
|
|
||||||
|
flags = read_register(device, REGISTERS.mouse_button_flags)
|
||||||
|
if flags is not None:
|
||||||
|
assert len(flags) == 3
|
||||||
|
return _bytes2int(flags)
|
||||||
|
|
||||||
|
|
||||||
def parse_battery_status(register, reply):
|
def parse_battery_status(register, reply):
|
||||||
|
@ -124,130 +247,3 @@ def parse_battery_status(register, reply):
|
||||||
|
|
||||||
# Return None for next charge level and voltage as these are not in HID++ 1.0 spec
|
# Return None for next charge level and voltage as these are not in HID++ 1.0 spec
|
||||||
return charge, None, status_text, None
|
return charge, None, status_text, None
|
||||||
|
|
||||||
|
|
||||||
def get_firmware(device):
|
|
||||||
assert device is not None
|
|
||||||
|
|
||||||
firmware = [None, None, None]
|
|
||||||
|
|
||||||
reply = read_register(device, REGISTERS.firmware, 0x01)
|
|
||||||
if not reply:
|
|
||||||
# won't be able to read any of it now...
|
|
||||||
return
|
|
||||||
|
|
||||||
fw_version = _strhex(reply[1:3])
|
|
||||||
fw_version = "%s.%s" % (fw_version[0:2], fw_version[2:4])
|
|
||||||
reply = read_register(device, REGISTERS.firmware, 0x02)
|
|
||||||
if reply:
|
|
||||||
fw_version += ".B" + _strhex(reply[1:3])
|
|
||||||
fw = _FirmwareInfo(FIRMWARE_KIND.Firmware, "", fw_version, None)
|
|
||||||
firmware[0] = fw
|
|
||||||
|
|
||||||
reply = read_register(device, REGISTERS.firmware, 0x04)
|
|
||||||
if reply:
|
|
||||||
bl_version = _strhex(reply[1:3])
|
|
||||||
bl_version = "%s.%s" % (bl_version[0:2], bl_version[2:4])
|
|
||||||
bl = _FirmwareInfo(FIRMWARE_KIND.Bootloader, "", bl_version, None)
|
|
||||||
firmware[1] = bl
|
|
||||||
|
|
||||||
reply = read_register(device, REGISTERS.firmware, 0x03)
|
|
||||||
if reply:
|
|
||||||
o_version = _strhex(reply[1:3])
|
|
||||||
o_version = "%s.%s" % (o_version[0:2], o_version[2:4])
|
|
||||||
o = _FirmwareInfo(FIRMWARE_KIND.Other, "", o_version, None)
|
|
||||||
firmware[2] = o
|
|
||||||
|
|
||||||
if any(firmware):
|
|
||||||
return tuple(f for f in firmware if f)
|
|
||||||
|
|
||||||
|
|
||||||
def set_3leds(device, battery_level=None, charging=None, warning=None):
|
|
||||||
assert device is not None
|
|
||||||
assert device.kind is not None
|
|
||||||
if not device.online:
|
|
||||||
return
|
|
||||||
|
|
||||||
if REGISTERS.three_leds not in device.registers:
|
|
||||||
return
|
|
||||||
|
|
||||||
if battery_level is not None:
|
|
||||||
if battery_level < _BATTERY_APPROX.critical:
|
|
||||||
# 1 orange, and force blink
|
|
||||||
v1, v2 = 0x22, 0x00
|
|
||||||
warning = True
|
|
||||||
elif battery_level < _BATTERY_APPROX.low:
|
|
||||||
# 1 orange
|
|
||||||
v1, v2 = 0x22, 0x00
|
|
||||||
elif battery_level < _BATTERY_APPROX.good:
|
|
||||||
# 1 green
|
|
||||||
v1, v2 = 0x20, 0x00
|
|
||||||
elif battery_level < _BATTERY_APPROX.full:
|
|
||||||
# 2 greens
|
|
||||||
v1, v2 = 0x20, 0x02
|
|
||||||
else:
|
|
||||||
# all 3 green
|
|
||||||
v1, v2 = 0x20, 0x22
|
|
||||||
if warning:
|
|
||||||
# set the blinking flag for the leds already set
|
|
||||||
v1 |= v1 >> 1
|
|
||||||
v2 |= v2 >> 1
|
|
||||||
elif charging:
|
|
||||||
# blink all green
|
|
||||||
v1, v2 = 0x30, 0x33
|
|
||||||
elif warning:
|
|
||||||
# 1 red
|
|
||||||
v1, v2 = 0x02, 0x00
|
|
||||||
else:
|
|
||||||
# turn off all leds
|
|
||||||
v1, v2 = 0x11, 0x11
|
|
||||||
|
|
||||||
write_register(device, REGISTERS.three_leds, v1, v2)
|
|
||||||
|
|
||||||
|
|
||||||
def get_notification_flags(device):
|
|
||||||
assert device is not None
|
|
||||||
|
|
||||||
# Avoid a call if the device is not online,
|
|
||||||
# or the device does not support registers.
|
|
||||||
if device.kind is not None:
|
|
||||||
# peripherals with protocol >= 2.0 don't support registers
|
|
||||||
if device.protocol and device.protocol >= 2.0:
|
|
||||||
return
|
|
||||||
|
|
||||||
flags = read_register(device, REGISTERS.notifications)
|
|
||||||
if flags is not None:
|
|
||||||
assert len(flags) == 3
|
|
||||||
return _bytes2int(flags)
|
|
||||||
|
|
||||||
|
|
||||||
def set_notification_flags(device, *flag_bits):
|
|
||||||
assert device is not None
|
|
||||||
|
|
||||||
# Avoid a call if the device is not online,
|
|
||||||
# or the device does not support registers.
|
|
||||||
if device.kind is not None:
|
|
||||||
# peripherals with protocol >= 2.0 don't support registers
|
|
||||||
if device.protocol and device.protocol >= 2.0:
|
|
||||||
return
|
|
||||||
|
|
||||||
flag_bits = sum(int(b) for b in flag_bits)
|
|
||||||
assert flag_bits & 0x00FFFFFF == flag_bits
|
|
||||||
result = write_register(device, REGISTERS.notifications, _int2bytes(flag_bits, 3))
|
|
||||||
return result is not None
|
|
||||||
|
|
||||||
|
|
||||||
def get_device_features(device):
|
|
||||||
assert device is not None
|
|
||||||
|
|
||||||
# Avoid a call if the device is not online,
|
|
||||||
# or the device does not support registers.
|
|
||||||
if device.kind is not None:
|
|
||||||
# peripherals with protocol >= 2.0 don't support registers
|
|
||||||
if device.protocol and device.protocol >= 2.0:
|
|
||||||
return
|
|
||||||
|
|
||||||
flags = read_register(device, REGISTERS.mouse_button_flags)
|
|
||||||
if flags is not None:
|
|
||||||
assert len(flags) == 3
|
|
||||||
return _bytes2int(flags)
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import threading as _threading
|
||||||
from struct import unpack as _unpack
|
from struct import unpack as _unpack
|
||||||
|
|
||||||
from . import diversion as _diversion
|
from . import diversion as _diversion
|
||||||
from . import hidpp10 as _hidpp10
|
from . import hidpp10
|
||||||
from . import hidpp10_constants as _hidpp10_constants
|
from . import hidpp10_constants as _hidpp10_constants
|
||||||
from . import hidpp20 as _hidpp20
|
from . import hidpp20 as _hidpp20
|
||||||
from . import hidpp20_constants as _hidpp20_constants
|
from . import hidpp20_constants as _hidpp20_constants
|
||||||
|
@ -38,6 +38,7 @@ from .status import KEYS as _K
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_hidpp10 = hidpp10.Hidpp10()
|
||||||
_R = _hidpp10_constants.REGISTERS
|
_R = _hidpp10_constants.REGISTERS
|
||||||
_F = _hidpp20_constants.FEATURE
|
_F = _hidpp20_constants.FEATURE
|
||||||
|
|
||||||
|
@ -220,7 +221,7 @@ def _process_hidpp10_custom_notification(device, status, n):
|
||||||
# message layout: 10 ix <register> <xx> <yy> <zz> <00>
|
# message layout: 10 ix <register> <xx> <yy> <zz> <00>
|
||||||
assert n.data[-1:] == b"\x00"
|
assert n.data[-1:] == b"\x00"
|
||||||
data = chr(n.address).encode() + n.data
|
data = chr(n.address).encode() + n.data
|
||||||
charge, next_charge, status_text, voltage = _hidpp10.parse_battery_status(n.sub_id, 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(charge, next_charge, status_text, voltage)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from . import hidpp10 as _hidpp10
|
from . import hidpp10
|
||||||
from . import hidpp10_constants as _hidpp10_constants
|
from . import hidpp10_constants as _hidpp10_constants
|
||||||
from . import hidpp20_constants as _hidpp20_constants
|
from . import hidpp20_constants as _hidpp20_constants
|
||||||
from . import settings as _settings
|
from . import settings as _settings
|
||||||
|
@ -31,6 +31,8 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_R = _hidpp10_constants.REGISTERS
|
_R = _hidpp10_constants.REGISTERS
|
||||||
|
|
||||||
|
_hidpp10 = hidpp10.Hidpp10()
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
|
@ -17,11 +17,12 @@
|
||||||
from time import time as _timestamp
|
from time import time as _timestamp
|
||||||
|
|
||||||
from logitech_receiver import base as _base
|
from logitech_receiver import base as _base
|
||||||
from logitech_receiver import hidpp10 as _hidpp10
|
from logitech_receiver import hidpp10
|
||||||
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
||||||
from logitech_receiver import notifications as _notifications
|
from logitech_receiver import notifications as _notifications
|
||||||
from logitech_receiver import status as _status
|
from logitech_receiver import status as _status
|
||||||
|
|
||||||
|
_hidpp10 = hidpp10.Hidpp10()
|
||||||
_R = _hidpp10_constants.REGISTERS
|
_R = _hidpp10_constants.REGISTERS
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
## with this program; if not, write to the Free Software Foundation, Inc.,
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
from logitech_receiver import exceptions
|
from logitech_receiver import exceptions, hidpp10
|
||||||
from logitech_receiver import hidpp10 as _hidpp10
|
|
||||||
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
from logitech_receiver import hidpp10_constants as _hidpp10_constants
|
||||||
from logitech_receiver import hidpp20 as _hidpp20
|
from logitech_receiver import hidpp20 as _hidpp20
|
||||||
from logitech_receiver import hidpp20_constants as _hidpp20_constants
|
from logitech_receiver import hidpp20_constants as _hidpp20_constants
|
||||||
|
@ -26,7 +25,7 @@ from logitech_receiver.common import strhex as _strhex
|
||||||
|
|
||||||
from solaar import NAME, __version__
|
from solaar import NAME, __version__
|
||||||
|
|
||||||
_F = _hidpp20_constants.FEATURE
|
_hidpp10 = hidpp10.Hidpp10()
|
||||||
|
|
||||||
|
|
||||||
def _print_receiver(receiver):
|
def _print_receiver(receiver):
|
||||||
|
|
Loading…
Reference in New Issue