351 lines
10 KiB
Python
351 lines
10 KiB
Python
#!/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)
|