You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
3.0 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Optional, Union
from enum import Enum, auto
@dataclass
class LedState:
r: Optional[int]
g: Optional[int]
b: Optional[int]
brightness: Optional[int]
autonomous: Optional[bool]
class LED:
def __init__(self, alias):
self.alias = alias
self.name = unalias(alias)
self.dir = Path('/sys/class/leds/'+self.name+'/')
@property
def state(self) -> LedState:
with open(self.dir/'color') as f:
r, g, b = [int(x) for x in f.read().strip().split()]
with open(self.dir/'brightness') as f:
brightness = int(f.read().strip())
with open(self.dir/'autonomous') as f:
autonomous = bool(f.read().strip())
return LedState(r=r, g=g, b=b, brightness=brightness, autonomous=autonomous)
def set(self, state: LedState):
# TODO: sanity check: when autonomous, no color or warn
# None means do not change value
old_state = self.state
for attr in ['r', 'g', 'b', 'brightness', 'autonomous']:
if getattr(state, attr) == None:
old_value = getattr(old_state, attr)
setattr(state, attr, old_value)
# Set it
with open(self.dir/'color', 'w') as f:
f.write(f'{state.r} {state.g} {state.b}\n')
with open(self.dir/'brightness', 'w') as f:
f.write(str(state.brightness)+'\n')
with open(self.dir/'autonomous', 'w') as f:
f.write(str(int(state.autonomous))+'\n')
def unalias(s):
return 'omnia-led:' + s
def alias(s):
return s.removeprefix('omnia-led:')
class PatternFitting(Enum):
Repeat = auto()
Nearest = auto()
Linear = auto()
class LedStrip:
"Represents a line of LEDs"
def __init__(self, leds: Union[list[str], list[LED]]):
if all(isinstance(x, str) for x in leds):
leds = [LED(x) for x in leds]
self.leds = leds
def __len__(self):
return len(self.leds)
def __iter__(self):
return self.leds.__iter__()
@property
def pattern(self) -> list[LedState]:
return [x.state for x in self.leds]
def setpattern(self, pat: list[LedState]):
if len(pat) != len(self):
raise ValueError('The pattern must be a one-to-one matching to LEDs')
for led, st in zip(self.leds, pat):
led.set(st)
def apply(self, pat: list[LedState], fit: PatternFitting):
"""Sets the strip to given pattern, which will be fit using selected algorithm.
Unlike setpattern, this first / always modifies the pattern to somehow fit to the given strip
"""
patl = len(pat)
strl = len(self)
if False: pass
elif fit == fit.Repeat:
# Repeat and/or chop
new_pat = (pat * ((strl // patl) + 1))[:strl]
elif fit == fit.Nearest:
quantum = patl/strl
indices = [round(quantum * i) for i in range(strl)]
new_pat = [pat[i] for i in indices]
elif fit == fit.Linear:
# Too complex
raise NotImplementedError('Please submit a pull request :-)')
self.setpattern(new_pat)
omnia_led_order = [
'lan0',
'lan1',
'lan2',
'lan3',
'lan4',
'wan',
'pci1',
'pci2',
'pci3',
'power',
'user1',
'user2',
]
OmniaStrip = LedStrip(omnia_led_order)