#!/usr/bin/env python3 """ Flask LED and Buzzer Control Application for reTerminal DM4 Provides REST API for controlling both the LED and buzzer """ from flask import Flask, jsonify, request import subprocess import time import threading app = Flask(__name__) LED_PATH = '/sys/class/leds/usr-led/brightness' BUZZER_PATH = '/sys/class/leds/usr-buzzer/brightness' class LEDController: """LED controller class""" def __init__(self): self.is_active = False self.thread = None def _write(self, value): """Internal method to write to LED""" try: subprocess.run(['sudo', 'tee', LED_PATH], input=str(value), text=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) return True except subprocess.CalledProcessError: return False def on(self): """Turn LED ON""" return self._write(1) def off(self): """Turn LED OFF""" return self._write(0) def toggle(self): """Toggle LED state""" try: result = subprocess.run(['cat', LED_PATH], capture_output=True, text=True, check=True) current = result.stdout.strip() new_state = '0' if current == '1' else '1' return self._write(new_state) except: return False def blink(self, count=5, on_time=0.2, off_time=0.2): """Blink LED specified number of times""" if self.is_active: return False def _blink(): self.is_active = True for _ in range(count): self.on() time.sleep(on_time) self.off() time.sleep(off_time) self.is_active = False self.thread = threading.Thread(target=_blink, daemon=True) self.thread.start() return True def pulse(self, duration=2, interval=0.1): """Pulse LED for specified duration""" if self.is_active: return False def _pulse(): self.is_active = True end_time = time.time() + duration while time.time() < end_time: self.on() time.sleep(interval) self.off() time.sleep(interval) self.is_active = False self.thread = threading.Thread(target=_pulse, daemon=True) self.thread.start() return True def status(self): """Get LED status""" try: result = subprocess.run(['cat', LED_PATH], capture_output=True, text=True, check=True) state = result.stdout.strip() return 'ON' if state == '1' else 'OFF' except: return 'UNKNOWN' class BuzzerController: """Buzzer controller class""" def __init__(self): self.is_playing = False self.play_thread = None def _write(self, value): """Internal method to write to buzzer""" try: subprocess.run(['sudo', 'tee', BUZZER_PATH], input=str(value), text=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True) return True except subprocess.CalledProcessError: return False def on(self): """Turn buzzer ON""" return self._write(1) def off(self): """Turn buzzer OFF""" return self._write(0) def beep(self, duration=0.2): """Play a single beep""" self.on() time.sleep(duration) self.off() def play_pattern(self, pattern): """Play a pattern""" if self.is_playing: return False def _play(): self.is_playing = True for on_time, off_time in pattern: self.on() time.sleep(on_time) self.off() time.sleep(off_time) self.is_playing = False self.play_thread = threading.Thread(target=_play, daemon=True) self.play_thread.start() return True def status(self): """Get buzzer status""" try: result = subprocess.run(['cat', BUZZER_PATH], capture_output=True, text=True, check=True) state = result.stdout.strip() return 'ON' if state == '1' else 'OFF' except: return 'UNKNOWN' # Create global controllers led = LEDController() buzzer = BuzzerController() @app.route('/') def index(): """API documentation endpoint""" return jsonify({ 'status': 'LED and Buzzer Control API', 'version': '1.0', 'endpoints': { 'LED': { '/led/on': 'Turn LED ON (POST, GET)', '/led/off': 'Turn LED OFF (POST, GET)', '/led/toggle': 'Toggle LED (POST, GET)', '/led/blink': 'Blink LED (GET: ?count=5&on_time=0.2&off_time=0.2)', '/led/pulse': 'Pulse LED (GET: ?duration=2&interval=0.1)', '/led/status': 'Get LED status (GET)' }, 'Buzzer': { '/buzzer/on': 'Turn buzzer ON (POST, GET)', '/buzzer/off': 'Turn buzzer OFF (POST, GET)', '/buzzer/beep': 'Play beep (GET: ?duration=0.2)', '/buzzer/status': 'Get buzzer status (GET)' }, 'Combined': { '/alert': 'Alert with LED and buzzer (GET: ?type=info|success|error|warning)' } } }) # LED Endpoints @app.route('/led/on', methods=['POST', 'GET']) def led_on(): """Turn LED ON""" if led.on(): return jsonify({'status': 'success', 'message': 'LED turned ON'}) return jsonify({'status': 'error', 'message': 'Failed to turn LED ON'}), 500 @app.route('/led/off', methods=['POST', 'GET']) def led_off(): """Turn LED OFF""" if led.off(): return jsonify({'status': 'success', 'message': 'LED turned OFF'}) return jsonify({'status': 'error', 'message': 'Failed to turn LED OFF'}), 500 @app.route('/led/toggle', methods=['POST', 'GET']) def led_toggle(): """Toggle LED state""" if led.toggle(): return jsonify({'status': 'success', 'message': 'LED toggled'}) return jsonify({'status': 'error', 'message': 'Failed to toggle LED'}), 500 @app.route('/led/blink', methods=['POST', 'GET']) def led_blink(): """Blink LED""" count = int(request.args.get('count', 5)) on_time = float(request.args.get('on_time', 0.2)) off_time = float(request.args.get('off_time', 0.2)) if led.blink(count, on_time, off_time): return jsonify({ 'status': 'success', 'message': f'LED blinking {count} times' }) return jsonify({ 'status': 'error', 'message': 'LED is already active' }), 409 @app.route('/led/pulse', methods=['POST', 'GET']) def led_pulse(): """Pulse LED""" duration = float(request.args.get('duration', 2)) interval = float(request.args.get('interval', 0.1)) if led.pulse(duration, interval): return jsonify({ 'status': 'success', 'message': f'LED pulsing for {duration} seconds' }) return jsonify({ 'status': 'error', 'message': 'LED is already active' }), 409 @app.route('/led/status', methods=['GET']) def led_status(): """Get LED status""" state = led.status() return jsonify({ 'status': 'success', 'led': state, 'is_active': led.is_active }) # Buzzer Endpoints @app.route('/buzzer/on', methods=['POST', 'GET']) def buzzer_on(): """Turn buzzer ON""" if buzzer.on(): return jsonify({'status': 'success', 'message': 'Buzzer turned ON'}) return jsonify({'status': 'error', 'message': 'Failed to turn buzzer ON'}), 500 @app.route('/buzzer/off', methods=['POST', 'GET']) def buzzer_off(): """Turn buzzer OFF""" if buzzer.off(): return jsonify({'status': 'success', 'message': 'Buzzer turned OFF'}) return jsonify({'status': 'error', 'message': 'Failed to turn buzzer OFF'}), 500 @app.route('/buzzer/beep', methods=['POST', 'GET']) def buzzer_beep(): """Play a beep""" duration = float(request.args.get('duration', 0.2)) if duration < 0 or duration > 5: return jsonify({ 'status': 'error', 'message': 'Duration must be between 0 and 5 seconds' }), 400 buzzer.beep(duration) return jsonify({ 'status': 'success', 'message': f'Beeped for {duration} seconds' }) @app.route('/buzzer/status', methods=['GET']) def buzzer_status(): """Get buzzer status""" state = buzzer.status() return jsonify({ 'status': 'success', 'buzzer': state, 'is_playing': buzzer.is_playing }) # Combined Alert Endpoint @app.route('/alert', methods=['GET']) def alert(): """Alert with LED and buzzer""" alert_type = request.args.get('type', 'info') patterns = { 'info': { 'led': (1, 0.2, 0.2), # 1 blink 'buzzer': (0.1, 0.1) # Short beep }, 'success': { 'led': (2, 0.1, 0.1), # 2 fast blinks 'buzzer': (0.2, 0.1) # Medium beep }, 'warning': { 'led': (2, 0.3, 0.3), # 2 slow blinks 'buzzer': (0.3, 0.2) # Longer beep }, 'error': { 'led': (3, 0.05, 0.05), # 3 very fast blinks 'buzzer': (0.2, 0.05) # Fast beeps } } if alert_type not in patterns: return jsonify({ 'status': 'error', 'message': 'Invalid alert type. Use: info, success, warning, error' }), 400 pattern = patterns[alert_type] # Start LED blink count, on_time, off_time = pattern['led'] led.blink(count, on_time, off_time) # Play buzzer pattern beep_duration, pause = pattern['buzzer'] buzzer_pattern = [(beep_duration, pause)] * count buzzer.play_pattern(buzzer_pattern) return jsonify({ 'status': 'success', 'message': f'Alert ({alert_type}) triggered' }) if __name__ == '__main__': print("Starting LED and Buzzer Control API...") print("API available at http://0.0.0.0:5000") print("Documentation at http://0.0.0.0:5000/") app.run(host='0.0.0.0', port=5000, debug=True)