|
|
@@ -26,36 +26,49 @@ from subprocess import run
|
|
|
|
|
|
from .util import msg, msg_r, die, have_sudo
|
|
|
from .color import blue, orange
|
|
|
+from .base_obj import Lockable
|
|
|
|
|
|
class LEDControl:
|
|
|
|
|
|
- binfo = namedtuple('board_info', ['name', 'status', 'trigger', 'trigger_states'])
|
|
|
+ class binfo(Lockable):
|
|
|
+ _reset_ok = ('trigger_reset',)
|
|
|
+
|
|
|
+ def __init__(self, name, control, trigger=None, trigger_dfl='heartbeat', trigger_disable='none'):
|
|
|
+ self.name = name
|
|
|
+ self.control = control
|
|
|
+ self.trigger = trigger
|
|
|
+ self.trigger_dfl = trigger_dfl
|
|
|
+ self.trigger_reset = trigger_dfl
|
|
|
+ self.trigger_disable = trigger_disable
|
|
|
+
|
|
|
boards = {
|
|
|
'raspi_pi': binfo(
|
|
|
name = 'Raspberry Pi',
|
|
|
- status = '/sys/class/leds/led0/brightness',
|
|
|
+ control = '/sys/class/leds/led0/brightness',
|
|
|
trigger = '/sys/class/leds/led0/trigger',
|
|
|
- trigger_states = ('none', 'mmc0')),
|
|
|
+ trigger_dfl = 'mmc0'),
|
|
|
'orange_pi': binfo(
|
|
|
name = 'Orange Pi (Armbian)',
|
|
|
- status = '/sys/class/leds/orangepi:red:status/brightness',
|
|
|
- trigger = None,
|
|
|
- trigger_states = None),
|
|
|
+ control = '/sys/class/leds/orangepi:red:status/brightness'),
|
|
|
'orange_pi_5': binfo(
|
|
|
name = 'Orange Pi 5 (Armbian)',
|
|
|
- status = '/sys/class/leds/status_led/brightness',
|
|
|
- trigger = None,
|
|
|
- trigger_states = None),
|
|
|
+ control = '/sys/class/leds/status_led/brightness'),
|
|
|
'rock_pi': binfo(
|
|
|
name = 'Rock Pi (Armbian)',
|
|
|
- status = '/sys/class/leds/status/brightness',
|
|
|
- trigger = '/sys/class/leds/status/trigger',
|
|
|
- trigger_states = ('none', 'heartbeat')),
|
|
|
+ control = '/sys/class/leds/status/brightness',
|
|
|
+ trigger = '/sys/class/leds/status/trigger'),
|
|
|
+ 'rock_5': binfo(
|
|
|
+ name = 'Rock 5 (Armbian)',
|
|
|
+ control = '/sys/class/leds/user-led2/brightness',
|
|
|
+ trigger = '/sys/class/leds/user-led2/trigger'),
|
|
|
+ 'banana_pi_f3': binfo(
|
|
|
+ name = 'Banana Pi F3 (Armbian)',
|
|
|
+ control = '/sys/class/leds/sys-led/brightness',
|
|
|
+ trigger = '/sys/class/leds/sys-led/trigger'),
|
|
|
'dummy': binfo(
|
|
|
- name = 'Fake',
|
|
|
- status = '/tmp/led_status',
|
|
|
- trigger = '/tmp/led_trigger',
|
|
|
- trigger_states = ('none', 'original_value')),
|
|
|
+ name = 'Fake Board',
|
|
|
+ control = '/tmp/led_status',
|
|
|
+ trigger = '/tmp/led_trigger'),
|
|
|
}
|
|
|
|
|
|
def __init__(self, enabled, simulate=False, debug=False):
|
|
|
@@ -74,58 +87,78 @@ class LEDControl:
|
|
|
if board_id == 'dummy' and not simulate:
|
|
|
continue
|
|
|
try:
|
|
|
- os.stat(board.status)
|
|
|
- except:
|
|
|
- pass
|
|
|
- else:
|
|
|
+ os.stat(board.control)
|
|
|
break
|
|
|
+ except FileNotFoundError:
|
|
|
+ pass
|
|
|
else:
|
|
|
die('NoLEDSupport', 'Control files not found! LED control not supported on this system')
|
|
|
|
|
|
msg(f'{board.name} board detected')
|
|
|
|
|
|
if self.debug:
|
|
|
- msg(f'\n Status file: {board.status}\n Trigger file: {board.trigger}')
|
|
|
-
|
|
|
- def check_access(fn, desc, init_val=None):
|
|
|
-
|
|
|
- def write_init_val(init_val):
|
|
|
- if not init_val:
|
|
|
- with open(fn) as fp:
|
|
|
- init_val = fp.read().strip()
|
|
|
- with open(fn, 'w') as fp:
|
|
|
- fp.write(f'{init_val}\n')
|
|
|
-
|
|
|
+ msg(f'\n Status file: {board.control}\n Trigger file: {board.trigger}')
|
|
|
+
|
|
|
+ def write_init_val(fn, init_val):
|
|
|
+ if not init_val:
|
|
|
+ with open(fn) as fp:
|
|
|
+ init_val = fp.read().strip()
|
|
|
+ with open(fn, 'w') as fp:
|
|
|
+ fp.write(f'{init_val}\n')
|
|
|
+
|
|
|
+ def permission_error_action(fn, desc):
|
|
|
+ cmd = f'sudo chmod 0666 {fn}'
|
|
|
+ if have_sudo():
|
|
|
+ msg(orange(f'Running ‘{cmd}’'))
|
|
|
+ run(cmd.split(), check=True)
|
|
|
+ else:
|
|
|
+ msg('\n{}\n{}\n{}'.format(
|
|
|
+ blue(f'You do not have write access to the {desc}'),
|
|
|
+ blue(f'To allow access, run the following command:\n\n {cmd}'),
|
|
|
+ orange('[To prevent this message in the future, enable sudo without a password]')
|
|
|
+ ))
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+ def init_state(fn, desc, init_val=None):
|
|
|
try:
|
|
|
- write_init_val(init_val)
|
|
|
+ write_init_val(fn, init_val)
|
|
|
except PermissionError:
|
|
|
- cmd = f'sudo chmod 0666 {fn}'
|
|
|
- if have_sudo():
|
|
|
- msg(orange(f'Running ‘{cmd}’'))
|
|
|
- run(cmd.split(), check=True)
|
|
|
- write_init_val(init_val)
|
|
|
- else:
|
|
|
- msg('\n{}\n{}\n{}'.format(
|
|
|
- blue(f'You do not have access to the {desc} file'),
|
|
|
- blue(f'To allow access, run the following command:\n\n {cmd}'),
|
|
|
- orange('[To prevent this message in the future, enable sudo without a password]')
|
|
|
- ))
|
|
|
- sys.exit(1)
|
|
|
-
|
|
|
- check_access(board.status, desc='status LED control')
|
|
|
+ permission_error_action(fn, desc)
|
|
|
+ write_init_val(fn, init_val)
|
|
|
|
|
|
+ # Writing to control file can alter trigger file, so read and initialize trigger file first:
|
|
|
if board.trigger:
|
|
|
- check_access(board.trigger, desc='LED trigger', init_val=board.trigger_states[0])
|
|
|
+ def get_cur_state():
|
|
|
+ try:
|
|
|
+ with open(board.trigger) as fh:
|
|
|
+ states = fh.read()
|
|
|
+ except PermissionError:
|
|
|
+ permission_error_action(board.trigger, 'status LED trigger file')
|
|
|
+ with open(board.trigger) as fh:
|
|
|
+ states = fh.read()
|
|
|
+
|
|
|
+ res = [a for a in states.split() if a.startswith('[') and a.endswith(']')]
|
|
|
+ return res[0][1:-1] if len(res) == 1 else None
|
|
|
+
|
|
|
+ if cur_state := get_cur_state():
|
|
|
+ msg(f'Saving current LED trigger state: [{cur_state}]')
|
|
|
+ board.trigger_reset = cur_state
|
|
|
+ else:
|
|
|
+ msg('Unable to determine current LED trigger state')
|
|
|
+
|
|
|
+ init_state(board.trigger, desc='status LED trigger file', init_val=board.trigger_disable)
|
|
|
+
|
|
|
+ init_state(board.control, desc='status LED control file')
|
|
|
|
|
|
self.board = board
|
|
|
|
|
|
@classmethod
|
|
|
def create_dummy_control_files(cls):
|
|
|
db = cls.boards['dummy']
|
|
|
- with open(db.status, 'w') as fp:
|
|
|
+ with open(db.control, 'w') as fp:
|
|
|
fp.write('0\n')
|
|
|
with open(db.trigger, 'w') as fp:
|
|
|
- fp.write(db.trigger_states[1]+'\n')
|
|
|
+ fp.write(db.trigger_dfl + '\n')
|
|
|
|
|
|
def noop(self, *args, **kwargs):
|
|
|
pass
|
|
|
@@ -140,7 +173,7 @@ class LEDControl:
|
|
|
msg(f'led_loop({on_secs}, {off_secs})')
|
|
|
|
|
|
if not on_secs:
|
|
|
- with open(self.board.status, 'w') as fp:
|
|
|
+ with open(self.board.control, 'w') as fp:
|
|
|
fp.write('0\n')
|
|
|
while True:
|
|
|
if self.ev_sleep(3600):
|
|
|
@@ -150,7 +183,7 @@ class LEDControl:
|
|
|
for s_time, val in ((on_secs, 255), (off_secs, 0)):
|
|
|
if self.debug:
|
|
|
msg_r(('^', '+')[bool(val)])
|
|
|
- with open(self.board.status, 'w') as fp:
|
|
|
+ with open(self.board.control, 'w') as fp:
|
|
|
fp.write(f'{val}\n')
|
|
|
if self.ev_sleep(s_time):
|
|
|
if self.debug:
|
|
|
@@ -192,4 +225,4 @@ class LEDControl:
|
|
|
|
|
|
if self.board.trigger:
|
|
|
with open(self.board.trigger, 'w') as fp:
|
|
|
- fp.write(self.board.trigger_states[1]+'\n')
|
|
|
+ fp.write(self.board.trigger_reset + '\n')
|