settings: implement and test per-key lighting

This commit is contained in:
Peter F. Patel-Schneider 2024-03-24 17:26:42 -04:00
parent 04a818f215
commit 1ed5f765e3
2 changed files with 76 additions and 1 deletions

View File

@ -45,6 +45,7 @@ from .settings import BitFieldWithOffsetAndMaskValidator as _BitFieldOMV
from .settings import ChoicesMapValidator as _ChoicesMapV from .settings import ChoicesMapValidator as _ChoicesMapV
from .settings import ChoicesValidator as _ChoicesV from .settings import ChoicesValidator as _ChoicesV
from .settings import FeatureRW as _FeatureRW from .settings import FeatureRW as _FeatureRW
from .settings import FeatureRWMap as _FeatureRWMap
from .settings import HeteroValidator as _HeteroV from .settings import HeteroValidator as _HeteroV
from .settings import LongSettings as _LongSettings from .settings import LongSettings as _LongSettings
from .settings import MultipleRangeValidator as _MultipleRangeV from .settings import MultipleRangeValidator as _MultipleRangeV
@ -1573,6 +1574,61 @@ class RGBEffectSetting(LEDZoneSetting):
return cls.setup(device, 0xE0, 0x10, b"\x01") return cls.setup(device, 0xE0, 0x10, b"\x01")
class PerKeyLighting(_Settings):
name = "per-key-lighting"
label = _("Per-key Lighting")
description = _("Control per-key lighting.")
feature = _F.PER_KEY_LIGHTING_V2
keys_universe = _NamedInts.range(1, 254)
choices_universe = _special_keys.COLORS
def read(self, cached=True):
self._pre_read(cached)
if cached and self._value is not None:
return self._value
reply_map = {}
for key in self._validator.choices:
reply_map[int(key)] = 0xFFFFFF # can't read so fake a value of white
self._value = reply_map
return reply_map
def write(self, map, save=True):
if self._device.online:
self.update(map, save)
data_bytes = b""
for key, value in map.items():
data_bytes += key.to_bytes(1, "big") + value.to_bytes(3, "big")
if len(data_bytes) >= 16: # up to four values are packed into a request
self._device.feature_request(self.feature, 0x10, data_bytes)
data_bytes = b""
if len(data_bytes) > 0:
self._device.feature_request(self.feature, 0x10, data_bytes)
self._device.feature_request(self.feature, 0x70, 0x00) # signal device to make the changes
return map
def write_key_value(self, key, value, save=True):
result = super().write_key_value(key, value, save)
if self._device.online:
self._device.feature_request(self.feature, 0x70, 0x00) # signal device to make the change
return result
class rw_class(_FeatureRWMap):
pass
class validator_class(_ChoicesMapV):
@classmethod
def build(cls, setting_class, device):
choices_map = {}
key_bitmap = device.feature_request(setting_class.feature, 0x00, 0x00, 0x00)[2:]
key_bitmap += device.feature_request(setting_class.feature, 0x00, 0x00, 0x01)[2:]
key_bitmap += device.feature_request(setting_class.feature, 0x00, 0x00, 0x02)[2:]
for i in range(1, 255):
if (key_bitmap[i // 8] >> i % 8) & 0x01:
choices_map[setting_class.keys_universe[i]] = setting_class.choices_universe
result = cls(choices_map) if choices_map else None
return result
SETTINGS = [ SETTINGS = [
RegisterHandDetection, # simple RegisterHandDetection, # simple
RegisterSmoothScroll, # simple RegisterSmoothScroll, # simple
@ -1607,6 +1663,7 @@ SETTINGS = [
RGBControl, RGBControl,
RGBEffectSetting, RGBEffectSetting,
BrightnessControl, BrightnessControl,
PerKeyLighting,
FnSwap, # simple FnSwap, # simple
NewFnSwap, # simple NewFnSwap, # simple
K375sFnSwap, # working K375sFnSwap, # working

View File

@ -610,6 +610,7 @@ responses_remappable_action = responses_reprog_controls + [
hidpp.Response("0051FF01005100", 0x0440, "0051FF01005100"), # right button set hidpp.Response("0051FF01005100", 0x0440, "0051FF01005100"), # right button set
] ]
tests = [ tests = [
[ [
FeatureTest( FeatureTest(
@ -642,6 +643,23 @@ tests = [
}, },
] ]
+ responses_remappable_action, + responses_remappable_action,
[
FeatureTest(settings_templates.PerKeyLighting, {0x01: 0xFFFFFF, 0x02: 0xFFFFFF}, {0x02: 0xFF0000}, 0x70, "00"),
{
common.NamedInt(1, "1"): special_keys.COLORS,
common.NamedInt(2, "2"): special_keys.COLORS,
common.NamedInt(9, "9"): special_keys.COLORS,
common.NamedInt(10, "10"): special_keys.COLORS,
common.NamedInt(113, "113"): special_keys.COLORS,
common.NamedInt(114, "114"): special_keys.COLORS,
},
hidpp.Response("040001", 0x0000, "8081"), # PER_KEY_LIGHTING_V2
hidpp.Response("00000606000000000000000000000000", 0x0400, "0000"),
hidpp.Response("00000600000000000000000000000000", 0x0400, "0001"),
hidpp.Response("00000000000000000000000000000000", 0x0400, "0002"),
hidpp.Response("02FF0000", 0x0410, "02FF0000"),
hidpp.Response("00", 0x0470, "00"),
],
] ]
@ -651,7 +669,6 @@ def test_key_template(test, mocker):
spy_feature_request = mocker.spy(device, "feature_request") spy_feature_request = mocker.spy(device, "feature_request")
setting = settings_templates.check_feature(device, test[0].sclass) setting = settings_templates.check_feature(device, test[0].sclass)
print("SETTING", setting)
assert setting is not None assert setting is not None
if isinstance(setting, list): if isinstance(setting, list):
setting = setting[0] setting = setting[0]
@ -664,6 +681,7 @@ def test_key_template(test, mocker):
value = setting.read(cached=False) value = setting.read(cached=False)
for k, v in test[0].initial_value.items(): for k, v in test[0].initial_value.items():
assert value[k] == v assert value[k] == v
setting.write(setting._value)
for key, value in test[0].write_value.items(): for key, value in test[0].write_value.items():
write_value = setting.write_key_value(key, value) write_value = setting.write_key_value(key, value)