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 threading as _threading
|
||||
|
||||
|
@ -60,10 +78,9 @@ class Device:
|
|||
if receiver:
|
||||
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.online = None
|
||||
self.online = self.descriptor = None
|
||||
|
||||
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._codename = None # Unifying peripherals report a codename.
|
||||
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._persister = None # persister holds settings
|
||||
|
||||
self._firmware = None
|
||||
self._keys = None
|
||||
self._remap_keys = None
|
||||
self._gestures = None
|
||||
self._firmware = self._keys = self._remap_keys = self._gestures = None
|
||||
self._polling_rate = self._power_switch = self._led_effects = None
|
||||
|
||||
self._gestures_lock = _threading.Lock()
|
||||
self._profiles = self._backlight = self._registers = self._settings = None
|
||||
self._feature_settings_checked = False
|
||||
self._settings_lock = _threading.Lock()
|
||||
self._polling_rate = None
|
||||
self._power_switch = None
|
||||
|
||||
# See `add_notification_handler`
|
||||
self._notification_handlers = {}
|
||||
|
@ -291,6 +305,12 @@ class Device:
|
|||
self._polling_rate = rate if rate else 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
|
||||
def keys(self):
|
||||
if not self._keys:
|
||||
|
|
|
@ -1163,42 +1163,19 @@ class LEDParam:
|
|||
|
||||
LEDRampChoices = _NamedInts(default=0, yes=1, no=2)
|
||||
LEDFormChoices = _NamedInts(default=0, sine=1, square=2, triangle=3, sawtooth=4, sharkfin=5, exponential=6)
|
||||
LEDParamSize = {
|
||||
LEDParam.color: 3,
|
||||
LEDParam.speed: 1,
|
||||
LEDParam.period: 2,
|
||||
LEDParam.intensity: 1,
|
||||
LEDParam.ramp: 1,
|
||||
LEDParam.form: 1
|
||||
}
|
||||
LEDEffects = {
|
||||
LEDParamSize = {LEDParam.color: 3, LEDParam.speed: 1, LEDParam.period: 2,
|
||||
LEDParam.intensity: 1, LEDParam.ramp: 1, LEDParam.form: 1} # yapf: disable
|
||||
LEDEffects = { # Wave=0x04, Stars=0x05, Press=0x06, Audio=0x07, # not implemented
|
||||
0x0: [_NamedInt(0x0, _('Disabled')), {}],
|
||||
0x1: [_NamedInt(0x1, _('Static')), {
|
||||
LEDParam.color: 0,
|
||||
LEDParam.ramp: 3
|
||||
}],
|
||||
0x2: [_NamedInt(0x2, _('Pulse')), {
|
||||
LEDParam.color: 0,
|
||||
LEDParam.speed: 3
|
||||
}],
|
||||
0x3: [_NamedInt(0x3, _('Cycle')), {
|
||||
LEDParam.period: 5,
|
||||
LEDParam.intensity: 7
|
||||
}],
|
||||
0x1: [_NamedInt(0x1, _('Static')), {LEDParam.color: 0, LEDParam.ramp: 3}],
|
||||
0x2: [_NamedInt(0x2, _('Pulse')), {LEDParam.color: 0, LEDParam.speed: 3}],
|
||||
0x3: [_NamedInt(0x3, _('Cycle')), {LEDParam.period: 5, LEDParam.intensity: 7}],
|
||||
0x8: [_NamedInt(0x8, _('Boot')), {}],
|
||||
0x9: [_NamedInt(0x9, _('Demo')), {}],
|
||||
0xA: [_NamedInt(0xA, _('Breathe')), {
|
||||
LEDParam.color: 0,
|
||||
LEDParam.period: 3,
|
||||
LEDParam.form: 5,
|
||||
LEDParam.intensity: 6
|
||||
}],
|
||||
0xB: [_NamedInt(0xB, _('Ripple')), {
|
||||
LEDParam.color: 0,
|
||||
LEDParam.period: 4
|
||||
}]
|
||||
}
|
||||
# Wave=0x04, Stars=0x05, Press=0x06, Audio=0x07, # not implemented
|
||||
0xA: [_NamedInt(0xA, _('Breathe')), {LEDParam.color: 0, LEDParam.period: 3,
|
||||
LEDParam.form: 5, LEDParam.intensity: 6}],
|
||||
0xB: [_NamedInt(0xB, _('Ripple')), {LEDParam.color: 0, LEDParam.period: 4}]
|
||||
} # yapf: disable
|
||||
|
||||
|
||||
class LEDEffectSetting: # an effect plus its parameters
|
||||
|
@ -1209,8 +1186,9 @@ class LEDEffectSetting: # an effect plus its parameters
|
|||
setattr(self, key, val)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, bytes):
|
||||
effect = LEDEffects[bytes[0]] if bytes[0] in LEDEffects else None
|
||||
def from_bytes(cls, bytes, options=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}
|
||||
if effect:
|
||||
for p, b in effect[1].items():
|
||||
|
@ -1219,20 +1197,22 @@ class LEDEffectSetting: # an effect plus its parameters
|
|||
args['bytes'] = bytes
|
||||
return cls(**args)
|
||||
|
||||
def to_bytes(self, ID=None):
|
||||
ID = self.ID if ID is None else ID
|
||||
def to_bytes(self, options=None):
|
||||
ID = self.ID
|
||||
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:
|
||||
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])
|
||||
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
|
||||
def from_yaml(cls, loader, node):
|
||||
args = loader.construct_mapping(node)
|
||||
return cls(**args)
|
||||
return cls(**loader.construct_mapping(node))
|
||||
|
||||
@classmethod
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
|
||||
def __init__(self, device, zindex, eindex):
|
||||
|
|
|
@ -1229,8 +1229,7 @@ class HeteroValidator(Validator):
|
|||
return reply_value
|
||||
|
||||
def prepare_write(self, new_value, current_value=None):
|
||||
new_value.options = self.options
|
||||
to_write = new_value.to_bytes()
|
||||
to_write = new_value.to_bytes(options=self.options)
|
||||
return to_write
|
||||
|
||||
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
|
||||
# reading the current setting for a zone returns zeros on some devices
|
||||
class LEDZoneSetting(_Setting):
|
||||
name = 'led_zone_'
|
||||
label = _('LED Zone Effects')
|
||||
|
@ -1458,12 +1457,12 @@ class LEDZoneSetting(_Setting):
|
|||
|
||||
@classmethod
|
||||
def build(cls, device):
|
||||
infos = _hidpp20.LEDEffectsInfo(device)
|
||||
infos = device.led_effects
|
||||
settings = []
|
||||
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)
|
||||
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.name = cls.name + str(int(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 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 SENSITIVITY_IGNORE as _SENSITIVITY_IGNORE
|
||||
from solaar.i18n import _, ngettext
|
||||
|
@ -574,7 +574,7 @@ class HeteroKeyControl(Gtk.HBox, Control):
|
|||
result = {}
|
||||
for k, (_lblbox, box) in self._items.items():
|
||||
result[str(k)] = box.get_value()
|
||||
result = _LEDEffectIndexed(**result)
|
||||
result = _LEDEffectSetting(**result)
|
||||
return result
|
||||
|
||||
def set_value(self, value):
|
||||
|
|
Loading…
Reference in New Issue