Skip to content

Commit e2f9c05

Browse files
committed
add color type validation and custom type
1 parent b60f4d8 commit e2f9c05

File tree

4 files changed

+81
-12
lines changed

4 files changed

+81
-12
lines changed

docs/http.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ may not check if they exist.
2828
Each setting has a title which is meant to be shown in the UI and an optional
2929
hint meant to be shown under a hover hint or `?` button.
3030

31-
Each setting may include tags, which work like classes.
31+
Each setting may include a family, which has a number of tags that work like classes.
3232
For example, a keyboard mapping setting may have the tags
3333
`[razer_lycosa_123, razer_kbd, keyboard, advanced]`, which would allow the UI
3434
to customize the presentation based on the specific device make, manufacturer,

src/hhd/__main__.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import os
55
import signal
66
from os.path import join
7-
from threading import Condition, RLock
7+
from threading import Condition
88
from threading import Event as TEvent
9-
from threading import Lock
9+
from threading import Lock, RLock
1010
from time import sleep
1111
from typing import Sequence, cast
1212

@@ -23,15 +23,16 @@
2323
load_relative_yaml,
2424
)
2525
from .plugins.settings import (
26+
Validator,
2627
get_default_state,
28+
load_blacklist_yaml,
2729
load_profile_yaml,
2830
load_state_yaml,
2931
merge_settings,
32+
save_blacklist_yaml,
3033
save_profile_yaml,
3134
save_state_yaml,
3235
validate_config,
33-
load_blacklist_yaml,
34-
save_blacklist_yaml,
3536
)
3637
from .utils import expanduser, fix_perms, get_context, switch_priviledge
3738

@@ -198,6 +199,9 @@ def main():
198199
for plugs in plugins.values():
199200
sorted_plugins.extend(plugs)
200201
sorted_plugins.sort(key=lambda x: x.priority)
202+
validator: Validator = lambda family, config, value: any(
203+
p.validate(family, config, value) for p in sorted_plugins
204+
)
201205

202206
if not sorted_plugins:
203207
logger.error(f"No plugins started, exiting...")
@@ -276,7 +280,7 @@ def main():
276280
name = fn.replace(".yml", "")
277281
s = load_profile_yaml(join(profile_dir, fn))
278282
if s:
279-
validate_config(s, settings, use_defaults=False)
283+
validate_config(s, settings, validator, use_defaults=False)
280284
if name.startswith("_"):
281285
templates[name] = s
282286
else:
@@ -374,7 +378,10 @@ def main():
374378
with lock:
375379
profiles[ev["name"]] = ev["config"]
376380
validate_config(
377-
profiles[ev["name"]], settings, use_defaults=False
381+
profiles[ev["name"]],
382+
settings,
383+
validator,
384+
use_defaults=False,
378385
)
379386
else:
380387
with lock:
@@ -389,7 +396,7 @@ def main():
389396
logger.error(f"Invalid event type submitted: '{other}'")
390397

391398
# Validate config
392-
validate_config(conf, settings)
399+
validate_config(conf, settings, validator)
393400

394401
# If settings changed, the configuration needs to reload
395402
# but it needs to be saved first

src/hhd/plugins/plugin.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ def open(
7575
def settings(self) -> HHDSettings:
7676
return {}
7777

78+
def validate(self, family: Sequence[str], config: Any, value: Any):
79+
return False
80+
7881
def prepare(self, conf: Config):
7982
pass
8083

src/hhd/plugins/settings.py

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
Sequence,
99
TypedDict,
1010
cast,
11+
Protocol,
1112
)
1213
from copy import copy
1314

@@ -91,6 +92,12 @@ class IntegerSetting(TypedDict):
9192
default: int | None
9293

9394

95+
class Color(TypedDict):
96+
red: int
97+
green: int
98+
blue: int
99+
100+
94101
class ColorSetting(TypedDict):
95102
"""RGB color setting."""
96103

@@ -99,7 +106,29 @@ class ColorSetting(TypedDict):
99106
title: str
100107
hint: str | None
101108

102-
default: Mapping | None
109+
default: Color | None
110+
111+
112+
class CustomSetting(TypedDict):
113+
"""Custom plugin setting.
114+
115+
Can be used for any required custom setting that is not covered by the
116+
default ones (e.g., fan curves, deadzones).
117+
118+
The setting type is defined by family.
119+
Then, the config variable can be used to supply option specific information
120+
(e.g., for fan curves how many temperature points are available).
121+
122+
To validate this setting, each loaded plugin's validate function is called,
123+
with the family, config data, and the supplied value."""
124+
125+
type: Literal["custom"]
126+
family: Sequence[str]
127+
title: str
128+
hint: str | None
129+
130+
config: Any | None
131+
default: Any | None
103132

104133

105134
Setting = (
@@ -110,6 +139,7 @@ class ColorSetting(TypedDict):
110139
| NumericalSetting
111140
| IntegerSetting
112141
| ColorSetting
142+
| CustomSetting
113143
)
114144

115145
#
@@ -244,6 +274,8 @@ def fill_in_defaults(s: Setting | Container | Mode):
244274
case "integer" | "float":
245275
s["min"] = s.get("min", None)
246276
s["max"] = s.get("max", None)
277+
case "custom":
278+
s["config"] = s.get("config", None)
247279
return s
248280

249281

@@ -625,7 +657,14 @@ def unravel_options(settings: HHDSettings):
625657
return options
626658

627659

628-
def validate_config(conf: Config, settings: HHDSettings, use_defaults: bool = True):
660+
class Validator(Protocol):
661+
def __call__(self, family: Sequence[str], config: Any, value: Any) -> bool:
662+
return False
663+
664+
665+
def validate_config(
666+
conf: Config, settings: HHDSettings, validator: Validator, use_defaults: bool = True
667+
):
629668
options = unravel_options(settings)
630669

631670
for k, d in options.items():
@@ -667,5 +706,25 @@ def validate_config(conf: Config, settings: HHDSettings, use_defaults: bool = Tr
667706
if v > d["max"]:
668707
conf[k] = d["max"]
669708
case "color":
670-
# TODO
671-
pass
709+
invalid = False
710+
711+
if not isinstance(v, Mapping):
712+
invalid = True
713+
else:
714+
for c in ("red", "green", "blue"):
715+
if c not in v:
716+
invalid = True
717+
elif not (0 <= v[c] < 256):
718+
invalid = True
719+
720+
if invalid:
721+
if use_defaults:
722+
conf[k] = default
723+
else:
724+
del conf[k]
725+
case "custom":
726+
if not validator(d["family"], d["config"], v):
727+
if use_defaults:
728+
conf[k] = default
729+
else:
730+
del conf[k]

0 commit comments

Comments
 (0)