device: clean up data for LED effects
This commit is contained in:
parent
7c441cc652
commit
23517048d4
|
@ -1,3 +1,21 @@
|
||||||
|
# -*- python-mode -*-
|
||||||
|
|
||||||
|
## Copyright (C) 2012-2013 Daniel Pavel
|
||||||
|
##
|
||||||
|
## 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
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License along
|
||||||
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import errno as _errno
|
import errno as _errno
|
||||||
import threading as _threading
|
import threading as _threading
|
||||||
|
|
||||||
|
@ -60,10 +78,9 @@ class Device:
|
||||||
if receiver:
|
if receiver:
|
||||||
assert number > 0 and number <= 15 # some receivers have devices past their max # of devices
|
assert number > 0 and number <= 15 # some receivers have devices past their max # of devices
|
||||||
self.number = number # will be None at this point for directly connected devices
|
self.number = number # will be None at this point for directly connected devices
|
||||||
self.online = None
|
self.online = self.descriptor = None
|
||||||
|
|
||||||
self.wpid = None # the Wireless PID is unique per device model
|
self.wpid = None # the Wireless PID is unique per device model
|
||||||
self.descriptor = None
|
|
||||||
self._kind = None # mouse, keyboard, etc (see _hidpp10.DEVICE_KIND)
|
self._kind = None # mouse, keyboard, etc (see _hidpp10.DEVICE_KIND)
|
||||||
self._codename = None # Unifying peripherals report a codename.
|
self._codename = None # Unifying peripherals report a codename.
|
||||||
self._name = None # the full name of the model
|
self._name = None # the full name of the model
|
||||||
|
@ -74,16 +91,13 @@ class Device:
|
||||||
self._tid_map = None # map from transports to product identifiers
|
self._tid_map = None # map from transports to product identifiers
|
||||||
self._persister = None # persister holds settings
|
self._persister = None # persister holds settings
|
||||||
|
|
||||||
self._firmware = None
|
self._firmware = self._keys = self._remap_keys = self._gestures = None
|
||||||
self._keys = None
|
self._polling_rate = self._power_switch = self._led_effects = None
|
||||||
self._remap_keys = None
|
|
||||||
self._gestures = None
|
|
||||||
self._gestures_lock = _threading.Lock()
|
self._gestures_lock = _threading.Lock()
|
||||||
self._profiles = self._backlight = self._registers = self._settings = None
|
self._profiles = self._backlight = self._registers = self._settings = None
|
||||||
self._feature_settings_checked = False
|
self._feature_settings_checked = False
|
||||||
self._settings_lock = _threading.Lock()
|
self._settings_lock = _threading.Lock()
|
||||||
self._polling_rate = None
|
|
||||||
self._power_switch = None
|
|
||||||
|
|
||||||
# See `add_notification_handler`
|
# See `add_notification_handler`
|
||||||
self._notification_handlers = {}
|
self._notification_handlers = {}
|
||||||
|
@ -291,6 +305,12 @@ class Device:
|
||||||
self._polling_rate = rate if rate else self._polling_rate
|
self._polling_rate = rate if rate else self._polling_rate
|
||||||
return self._polling_rate
|
return self._polling_rate
|
||||||
|
|
||||||
|
@property
|
||||||
|
def led_effects(self):
|
||||||
|
if not self._led_effects and self.online and self.protocol >= 2.0:
|
||||||
|
self._led_effects = _hidpp20.LEDEffectsInfo(self)
|
||||||
|
return self._led_effects
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keys(self):
|
def keys(self):
|
||||||
if not self._keys:
|
if not self._keys:
|
||||||
|
|
|
@ -1163,42 +1163,19 @@ class LEDParam:
|
||||||
|
|
||||||
LEDRampChoices = _NamedInts(default=0, yes=1, no=2)
|
LEDRampChoices = _NamedInts(default=0, yes=1, no=2)
|
||||||
LEDFormChoices = _NamedInts(default=0, sine=1, square=2, triangle=3, sawtooth=4, sharkfin=5, exponential=6)
|
LEDFormChoices = _NamedInts(default=0, sine=1, square=2, triangle=3, sawtooth=4, sharkfin=5, exponential=6)
|
||||||
LEDParamSize = {
|
LEDParamSize = {LEDParam.color: 3, LEDParam.speed: 1, LEDParam.period: 2,
|
||||||
LEDParam.color: 3,
|
LEDParam.intensity: 1, LEDParam.ramp: 1, LEDParam.form: 1} # yapf: disable
|
||||||
LEDParam.speed: 1,
|
LEDEffects = { # Wave=0x04, Stars=0x05, Press=0x06, Audio=0x07, # not implemented
|
||||||
LEDParam.period: 2,
|
|
||||||
LEDParam.intensity: 1,
|
|
||||||
LEDParam.ramp: 1,
|
|
||||||
LEDParam.form: 1
|
|
||||||
}
|
|
||||||
LEDEffects = {
|
|
||||||
0x0: [_NamedInt(0x0, _('Disabled')), {}],
|
0x0: [_NamedInt(0x0, _('Disabled')), {}],
|
||||||
0x1: [_NamedInt(0x1, _('Static')), {
|
0x1: [_NamedInt(0x1, _('Static')), {LEDParam.color: 0, LEDParam.ramp: 3}],
|
||||||
LEDParam.color: 0,
|
0x2: [_NamedInt(0x2, _('Pulse')), {LEDParam.color: 0, LEDParam.speed: 3}],
|
||||||
LEDParam.ramp: 3
|
0x3: [_NamedInt(0x3, _('Cycle')), {LEDParam.period: 5, LEDParam.intensity: 7}],
|
||||||
}],
|
|
||||||
0x2: [_NamedInt(0x2, _('Pulse')), {
|
|
||||||
LEDParam.color: 0,
|
|
||||||
LEDParam.speed: 3
|
|
||||||
}],
|
|
||||||
0x3: [_NamedInt(0x3, _('Cycle')), {
|
|
||||||
LEDParam.period: 5,
|
|
||||||
LEDParam.intensity: 7
|
|
||||||
}],
|
|
||||||
0x8: [_NamedInt(0x8, _('Boot')), {}],
|
0x8: [_NamedInt(0x8, _('Boot')), {}],
|
||||||
0x9: [_NamedInt(0x9, _('Demo')), {}],
|
0x9: [_NamedInt(0x9, _('Demo')), {}],
|
||||||
0xA: [_NamedInt(0xA, _('Breathe')), {
|
0xA: [_NamedInt(0xA, _('Breathe')), {LEDParam.color: 0, LEDParam.period: 3,
|
||||||
LEDParam.color: 0,
|
LEDParam.form: 5, LEDParam.intensity: 6}],
|
||||||
LEDParam.period: 3,
|
0xB: [_NamedInt(0xB, _('Ripple')), {LEDParam.color: 0, LEDParam.period: 4}]
|
||||||
LEDParam.form: 5,
|
} # yapf: disable
|
||||||
LEDParam.intensity: 6
|
|
||||||
}],
|
|
||||||
0xB: [_NamedInt(0xB, _('Ripple')), {
|
|
||||||
LEDParam.color: 0,
|
|
||||||
LEDParam.period: 4
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
# Wave=0x04, Stars=0x05, Press=0x06, Audio=0x07, # not implemented
|
|
||||||
|
|
||||||
|
|
||||||
class LEDEffectSetting: # an effect plus its parameters
|
class LEDEffectSetting: # an effect plus its parameters
|
||||||
|
@ -1209,8 +1186,9 @@ class LEDEffectSetting: # an effect plus its parameters
|
||||||
setattr(self, key, val)
|
setattr(self, key, val)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bytes(cls, bytes):
|
def from_bytes(cls, bytes, options=None):
|
||||||
effect = LEDEffects[bytes[0]] if bytes[0] in LEDEffects else None
|
ID = next((ze.ID for ze in options if ze.index == bytes[0]), None) if options is not None else bytes[0]
|
||||||
|
effect = LEDEffects[ID] if ID in LEDEffects else None
|
||||||
args = {'ID': effect[0] if effect else None}
|
args = {'ID': effect[0] if effect else None}
|
||||||
if effect:
|
if effect:
|
||||||
for p, b in effect[1].items():
|
for p, b in effect[1].items():
|
||||||
|
@ -1219,20 +1197,22 @@ class LEDEffectSetting: # an effect plus its parameters
|
||||||
args['bytes'] = bytes
|
args['bytes'] = bytes
|
||||||
return cls(**args)
|
return cls(**args)
|
||||||
|
|
||||||
def to_bytes(self, ID=None):
|
def to_bytes(self, options=None):
|
||||||
ID = self.ID if ID is None else ID
|
ID = self.ID
|
||||||
if ID is None:
|
if ID is None:
|
||||||
return self.bytes if self.bytes else b'\xff' * 11
|
return self.bytes if hasattr(self, 'bytes') else b'\xff' * 11
|
||||||
else:
|
else:
|
||||||
bs = [0] * 10
|
bs = [0] * 10
|
||||||
for p, b in LEDEffects[self.ID][1].items():
|
for p, b in LEDEffects[ID][1].items():
|
||||||
bs[b:b + LEDParamSize[p]] = _int2bytes(getattr(self, str(p), 0), LEDParamSize[p])
|
bs[b:b + LEDParamSize[p]] = _int2bytes(getattr(self, str(p), 0), LEDParamSize[p])
|
||||||
return _int2bytes(ID, 1) + bytes(bs)
|
if options is not None:
|
||||||
|
ID = next((ze.index for ze in options if ze.ID == ID), None)
|
||||||
|
result = _int2bytes(ID, 1) + bytes(bs)
|
||||||
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_yaml(cls, loader, node):
|
def from_yaml(cls, loader, node):
|
||||||
args = loader.construct_mapping(node)
|
return cls(**loader.construct_mapping(node))
|
||||||
return cls(**args)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_yaml(cls, dumper, data):
|
def to_yaml(cls, dumper, data):
|
||||||
|
@ -1246,44 +1226,6 @@ _yaml.SafeLoader.add_constructor('!LEDEffectSetting', LEDEffectSetting.from_yaml
|
||||||
_yaml.add_representer(LEDEffectSetting, LEDEffectSetting.to_yaml)
|
_yaml.add_representer(LEDEffectSetting, LEDEffectSetting.to_yaml)
|
||||||
|
|
||||||
|
|
||||||
class LEDEffectIndexed(LEDEffectSetting): # an effect plus its parameters, using the effect indices from an effect zone
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_bytes(cls, bytes, options=None):
|
|
||||||
if options:
|
|
||||||
args = {'ID': next((ze.ID for ze in options if ze.index == bytes[0]), None)}
|
|
||||||
else:
|
|
||||||
args = {'ID': None}
|
|
||||||
if args['ID'] in LEDEffects:
|
|
||||||
for p, b in LEDEffects[args['ID']][1].items():
|
|
||||||
args[str(p)] = _bytes2int(bytes[1 + b:1 + b + LEDParamSize[p]])
|
|
||||||
else:
|
|
||||||
args['bytes'] = bytes
|
|
||||||
args['options'] = options
|
|
||||||
return cls(**args)
|
|
||||||
|
|
||||||
def to_bytes(self): # needs zone information
|
|
||||||
ID = next((ze.index for ze in self.options if ze.ID == self.ID), None)
|
|
||||||
if ID is None:
|
|
||||||
return self.bytes if hasattr(self, 'bytes') else b'\xff' * 11
|
|
||||||
else:
|
|
||||||
return super().to_bytes(ID)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def to_yaml(cls, dumper, data):
|
|
||||||
options = getattr(data, 'options', None)
|
|
||||||
if hasattr(data, 'options'):
|
|
||||||
delattr(data, 'options')
|
|
||||||
result = dumper.represent_mapping('!LEDEffectIndexed', data.__dict__, flow_style=True)
|
|
||||||
if options is not None:
|
|
||||||
data.options = options
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
_yaml.SafeLoader.add_constructor('!LEDEffectIndexed', LEDEffectIndexed.from_yaml)
|
|
||||||
_yaml.add_representer(LEDEffectIndexed, LEDEffectIndexed.to_yaml)
|
|
||||||
|
|
||||||
|
|
||||||
class LEDEffectInfo: # an effect that a zone can do
|
class LEDEffectInfo: # an effect that a zone can do
|
||||||
|
|
||||||
def __init__(self, device, zindex, eindex):
|
def __init__(self, device, zindex, eindex):
|
||||||
|
|
|
@ -1229,8 +1229,7 @@ class HeteroValidator(Validator):
|
||||||
return reply_value
|
return reply_value
|
||||||
|
|
||||||
def prepare_write(self, new_value, current_value=None):
|
def prepare_write(self, new_value, current_value=None):
|
||||||
new_value.options = self.options
|
to_write = new_value.to_bytes(options=self.options)
|
||||||
to_write = new_value.to_bytes()
|
|
||||||
return to_write
|
return to_write
|
||||||
|
|
||||||
def acceptable(self, args, current): # should this actually do some checking?
|
def acceptable(self, args, current): # should this actually do some checking?
|
||||||
|
|
|
@ -1442,7 +1442,6 @@ _LEDP = _hidpp20.LEDParam
|
||||||
|
|
||||||
|
|
||||||
# an LED Zone has an index, a set of possible LED effects, and an LED effect setting
|
# an LED Zone has an index, a set of possible LED effects, and an LED effect setting
|
||||||
# reading the current setting for a zone returns zeros on some devices
|
|
||||||
class LEDZoneSetting(_Setting):
|
class LEDZoneSetting(_Setting):
|
||||||
name = 'led_zone_'
|
name = 'led_zone_'
|
||||||
label = _('LED Zone Effects')
|
label = _('LED Zone Effects')
|
||||||
|
@ -1458,12 +1457,12 @@ class LEDZoneSetting(_Setting):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, device):
|
def build(cls, device):
|
||||||
infos = _hidpp20.LEDEffectsInfo(device)
|
infos = device.led_effects
|
||||||
settings = []
|
settings = []
|
||||||
for zone in infos.zones:
|
for zone in infos.zones:
|
||||||
prefix = zone.index.to_bytes(1)
|
prefix = _int2bytes(zone.index, 1)
|
||||||
rw = _FeatureRW(_F.COLOR_LED_EFFECTS, read_fnid=0xE0, write_fnid=0x30, prefix=prefix)
|
rw = _FeatureRW(_F.COLOR_LED_EFFECTS, read_fnid=0xE0, write_fnid=0x30, prefix=prefix)
|
||||||
validator = _HeteroV(data_class=_hidpp20.LEDEffectIndexed, options=zone.effects, readable=infos.readable)
|
validator = _HeteroV(data_class=_hidpp20.LEDEffectSetting, options=zone.effects, readable=infos.readable)
|
||||||
setting = cls(device, rw, validator)
|
setting = cls(device, rw, validator)
|
||||||
setting.name = cls.name + str(int(zone.location))
|
setting.name = cls.name + str(int(zone.location))
|
||||||
setting.label = _('LEDs') + ' ' + str(_hidpp20.LEDZoneLocations[zone.location])
|
setting.label = _('LEDs') + ' ' + str(_hidpp20.LEDZoneLocations[zone.location])
|
||||||
|
|
|
@ -24,7 +24,7 @@ from logging import getLogger
|
||||||
from threading import Timer as _Timer
|
from threading import Timer as _Timer
|
||||||
|
|
||||||
from gi.repository import Gdk, GLib, Gtk
|
from gi.repository import Gdk, GLib, Gtk
|
||||||
from logitech_receiver.hidpp20 import LEDEffectIndexed as _LEDEffectIndexed
|
from logitech_receiver.hidpp20 import LEDEffectSetting as _LEDEffectSetting
|
||||||
from logitech_receiver.settings import KIND as _SETTING_KIND
|
from logitech_receiver.settings import KIND as _SETTING_KIND
|
||||||
from logitech_receiver.settings import SENSITIVITY_IGNORE as _SENSITIVITY_IGNORE
|
from logitech_receiver.settings import SENSITIVITY_IGNORE as _SENSITIVITY_IGNORE
|
||||||
from solaar.i18n import _, ngettext
|
from solaar.i18n import _, ngettext
|
||||||
|
@ -574,7 +574,7 @@ class HeteroKeyControl(Gtk.HBox, Control):
|
||||||
result = {}
|
result = {}
|
||||||
for k, (_lblbox, box) in self._items.items():
|
for k, (_lblbox, box) in self._items.items():
|
||||||
result[str(k)] = box.get_value()
|
result[str(k)] = box.get_value()
|
||||||
result = _LEDEffectIndexed(**result)
|
result = _LEDEffectSetting(**result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
|
|
Loading…
Reference in New Issue