adacam-api/adacam_api/app.py

115 lines
4.4 KiB
Python

"""Flask app factory."""
import subprocess
from flask import Flask, request, jsonify
from . import config, db, forwarder
from .auth import get_device_serial, get_api_token, require_auth
from .routes import landmarks, gnss, status, frames
def create_app():
"""Create and configure the Flask app."""
app = Flask(__name__)
# Load config and init DB
config.load()
db.init()
# Register blueprints
app.register_blueprint(landmarks.bp)
app.register_blueprint(gnss.bp)
app.register_blueprint(status.bp)
app.register_blueprint(frames.bp)
# ── PAIRING ENDPOINT ─────────────────────────────────────────────────────
@app.route('/pair')
@app.route('/api/1/pair')
def pair():
"""Pairing info for Varroa app."""
serial = get_device_serial()
return jsonify({
'serial': serial,
'version': '1.0',
'ap_ip': '10.77.0.1',
'api_port': 5000
})
# ── DEVICE INFO ──────────────────────────────────────────────────────────
@app.route('/api/1/info')
def device_info():
"""Device identity (unauthenticated)."""
serial = get_device_serial()
return jsonify({
'device_id': serial,
'version': '1.0',
'firmware': 'adacam',
'ap_ip': '10.77.0.1'
})
# ── WIFI STATUS/CONNECT ──────────────────────────────────────────────────
@app.route('/api/1/wifi/status')
def wifi_status():
"""Get WiFi client interface status (unauthenticated)."""
try:
result = subprocess.run(
['wpa_cli', '-i', 'wlp1s0f1', 'status'],
capture_output=True, text=True, timeout=5
)
lines = dict(
l.split('=', 1) for l in result.stdout.strip().split('\n') if '=' in l
)
return jsonify({
'ssid': lines.get('ssid', ''),
'ip': lines.get('ip_address', ''),
'state': lines.get('wpa_state', 'DISCONNECTED'),
'connected': lines.get('wpa_state') == 'COMPLETED'
})
except Exception as e:
return jsonify({'error': str(e), 'connected': False})
@app.route('/api/1/wifi/connect', methods=['POST'])
@require_auth
def wifi_connect():
"""Connect to a WiFi network (authenticated)."""
data = request.get_json()
ssid = data.get('ssid', '').strip()
password = data.get('password', '').strip()
if not ssid or not password:
return jsonify({'error': 'ssid and password required'}), 400
try:
result = subprocess.run(
['/usr/local/bin/adacam-wifi-connect', ssid, password],
capture_output=True, text=True, timeout=15
)
if result.returncode == 0:
return jsonify({'ok': True, 'message': f'Connecting to {ssid}'})
else:
return jsonify({'error': result.stderr or 'Failed'}), 500
except Exception as e:
return jsonify({'error': str(e)}), 500
# ── SSH CONTROL ──────────────────────────────────────────────────────────
@app.route('/api/1/ssh/status')
def ssh_status():
"""Get SSH daemon status (unauthenticated)."""
result = subprocess.run(
['systemctl', 'is-active', 'sshd'],
capture_output=True, text=True
)
return jsonify({'active': result.stdout.strip() == 'active'})
@app.route('/api/1/ssh/toggle', methods=['POST'])
@require_auth
def ssh_toggle():
"""Enable/disable SSH (authenticated)."""
data = request.get_json()
enable = data.get('enable', True)
subprocess.run(
['systemctl', 'start' if enable else 'stop', 'sshd'],
timeout=5
)
return jsonify({'ok': True, 'ssh_enabled': enable})
# Start background forwarder thread
forwarder.start_retry_thread()
return app