|
@@ -0,0 +1,184 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+#
|
|
|
+# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
|
|
+# Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
|
|
|
+#
|
|
|
+# 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
+
|
|
|
+"""
|
|
|
+led: Control the LED on a single-board computer
|
|
|
+"""
|
|
|
+
|
|
|
+import sys,time
|
|
|
+from mmgen.common import *
|
|
|
+import threading
|
|
|
+
|
|
|
+class LEDControl:
|
|
|
+
|
|
|
+ binfo = namedtuple('board_info',['name','status','trigger','trigger_states'])
|
|
|
+ boards = {
|
|
|
+ 'raspi_pi': binfo(
|
|
|
+ name = 'Raspberry Pi',
|
|
|
+ status = '/sys/class/leds/led0/brightness',
|
|
|
+ trigger = '/sys/class/leds/led0/trigger',
|
|
|
+ trigger_states = ('none','mmc0') ),
|
|
|
+ 'orange_pi': binfo(
|
|
|
+ name = 'Orange Pi',
|
|
|
+ status = '/sys/class/leds/orangepi:red:status/brightness',
|
|
|
+ trigger = None,
|
|
|
+ trigger_states = None ),
|
|
|
+# 'rock_pi': binfo( # TODO
|
|
|
+# name = 'Rock Pi',
|
|
|
+# status = '/sys/class/leds/user-led1/brightness',
|
|
|
+# trigger = '/sys/class/leds/user-led1/trigger',
|
|
|
+# trigger_states = ('none','heartbeat') ),
|
|
|
+ 'dummy': binfo(
|
|
|
+ name = 'Fake',
|
|
|
+ status = '/tmp/led_status',
|
|
|
+ trigger = '/tmp/led_trigger',
|
|
|
+ trigger_states = ('none','original_value') ),
|
|
|
+ }
|
|
|
+
|
|
|
+ def __init__(self,enabled,simulate=False,debug=False):
|
|
|
+
|
|
|
+ self.enabled = enabled
|
|
|
+ self.debug = debug
|
|
|
+
|
|
|
+ if not enabled:
|
|
|
+ self.set = self.stop = self.noop
|
|
|
+ return
|
|
|
+
|
|
|
+ self.ev = threading.Event()
|
|
|
+ self.led_thread = None
|
|
|
+
|
|
|
+ if simulate:
|
|
|
+ type(self).create_dummy_control_files()
|
|
|
+ self.debug = True
|
|
|
+
|
|
|
+ for board_id,board in self.boards.items():
|
|
|
+ try: os.stat(board.status)
|
|
|
+ except: pass
|
|
|
+ else: break
|
|
|
+ else:
|
|
|
+ from mmgen.exception import NoLEDSupport
|
|
|
+ raise NoLEDSupport('Control files not found! LED control not supported on this system')
|
|
|
+
|
|
|
+ msg(f'{board.name} board detected')
|
|
|
+
|
|
|
+ if self.debug:
|
|
|
+ msg(fmt(f"""
|
|
|
+ Status file: {board.status}
|
|
|
+ Trigger file: {board.trigger}
|
|
|
+ """,indent=' ',strip_char='\t'))
|
|
|
+
|
|
|
+ if board_id == 'dummy' and not simulate:
|
|
|
+ if g.test_suite:
|
|
|
+ msg('Warning: no simulation requested but dummy LED control files detected')
|
|
|
+ self.debug = True
|
|
|
+ else:
|
|
|
+ die(1,fmt(f"""
|
|
|
+ No simulation requested but dummy LED control files detected:
|
|
|
+
|
|
|
+ {board.status}
|
|
|
+ {board.trigger}
|
|
|
+
|
|
|
+ You may wish to remove them and restart
|
|
|
+ """,indent=' ',strip_char='\t'))
|
|
|
+
|
|
|
+ def check_access(fn,desc,init_val=None):
|
|
|
+ try:
|
|
|
+ iv = init_val or open(fn).read().strip()
|
|
|
+ open(fn,'w').write(f'{iv}\n')
|
|
|
+ return True
|
|
|
+ except:
|
|
|
+ msg('\n'+fmt(f"""
|
|
|
+ You do not have access to the {desc} file
|
|
|
+ To allow access, run the following command:
|
|
|
+
|
|
|
+ sudo chmod 0666 {fn}
|
|
|
+ """,indent=' ',strip_char='\t'))
|
|
|
+ return False
|
|
|
+
|
|
|
+ if not check_access(board.status,desc='status LED control'):
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+ if board.trigger:
|
|
|
+ if not check_access(board.trigger,desc='LED trigger',init_val=board.trigger_states[0]):
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+ self.board = board
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def create_dummy_control_files(cls):
|
|
|
+ db = cls.boards['dummy']
|
|
|
+ open(db.status,'w').write('0\n')
|
|
|
+ open(db.trigger,'w').write(db.trigger_states[1]+'\n')
|
|
|
+
|
|
|
+ def noop(self,*args,**kwargs): pass
|
|
|
+
|
|
|
+ def ev_sleep(self,secs):
|
|
|
+ self.ev.wait(secs)
|
|
|
+ return self.ev.isSet()
|
|
|
+
|
|
|
+ def led_loop(self,on_secs,off_secs):
|
|
|
+
|
|
|
+ if self.debug:
|
|
|
+ msg(f'led_loop({on_secs},{off_secs})')
|
|
|
+
|
|
|
+ if not on_secs:
|
|
|
+ open(self.board.status,'w').write('0\n')
|
|
|
+ while True:
|
|
|
+ if self.ev_sleep(3600):
|
|
|
+ return
|
|
|
+
|
|
|
+ while True:
|
|
|
+ for s_time,val in ((on_secs,255),(off_secs,0)):
|
|
|
+ if self.debug:
|
|
|
+ msg_r(('^','+')[bool(val)])
|
|
|
+ open(self.board.status,'w').write(f'{val}\n')
|
|
|
+ if self.ev_sleep(s_time):
|
|
|
+ if self.debug:
|
|
|
+ msg('\n')
|
|
|
+ return
|
|
|
+
|
|
|
+ def set(self,state):
|
|
|
+ lt = namedtuple('led_timings',['on_secs','off_secs'])
|
|
|
+ timings = {
|
|
|
+ 'off': lt( 0, 0 ),
|
|
|
+ 'standby': lt( 2.2, 0.2 ),
|
|
|
+ 'busy': lt( 0.06, 0.06 ),
|
|
|
+ 'error': lt( 0.5, 0.5 ) }
|
|
|
+
|
|
|
+ if self.led_thread:
|
|
|
+ self.ev.set()
|
|
|
+ self.led_thread.join()
|
|
|
+ self.ev.clear()
|
|
|
+
|
|
|
+ if self.debug:
|
|
|
+ msg(f'Setting LED state to {state!r}')
|
|
|
+
|
|
|
+ self.led_thread = threading.Thread(target=self.led_loop,name='LED loop',args=timings[state])
|
|
|
+ self.led_thread.start()
|
|
|
+
|
|
|
+ def stop(self):
|
|
|
+
|
|
|
+ self.set('off')
|
|
|
+ self.ev.set()
|
|
|
+ self.led_thread.join()
|
|
|
+
|
|
|
+ if self.debug:
|
|
|
+ msg('Stopping LED')
|
|
|
+
|
|
|
+ if self.board.trigger:
|
|
|
+ open(self.board.trigger,'w').write(self.board.trigger_states[1]+'\n')
|