feat: Pi hotspot SSID/password configurable from Settings page
- Settings page: SSID + password fields in In-car access card - GET /api/hotspot reads current SSID (password never returned) - POST /api/hotspot updates via nmcli (Hotspot-local profile on wlan0) - deploy/yaris-hotspot.sudoers for privileged nmcli access - Password is write-only with placeholder hint
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from flask import Flask, render_template, make_response, json, jsonify, request, send_file, Response
|
||||
from modules import app_gps, app_events, app_car, app_account, app_obd, app_sql, app_dash, app_upload, app_video, app_health, app_alerts, app_settings, app_network, app_device_config, app_device_sync, app_remote_config, app_status
|
||||
import os, time
|
||||
import os, subprocess, time
|
||||
from gevent.pywsgi import WSGIServer
|
||||
|
||||
|
||||
@@ -131,6 +131,59 @@ def uplink_connect():
|
||||
return jsonify(error=str(error)), 503
|
||||
return jsonify(status="connection_requested"), 202
|
||||
|
||||
_HOTSPOT_PROFILE = "Hotspot-local" # wlan0 AP profile name in NetworkManager
|
||||
|
||||
|
||||
@app.route('/api/hotspot')
|
||||
def hotspot_config():
|
||||
"""Read current Pi hotspot SSID from NetworkManager (password is never returned)."""
|
||||
ssid = ""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["sudo", "-n", "nmcli", "-t", "-f", "802-11-wireless.ssid", "con", "show", _HOTSPOT_PROFILE],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
for line in result.stdout.strip().splitlines():
|
||||
if line.startswith("802-11-wireless.ssid:"):
|
||||
ssid = line.split(":", 1)[1]
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
return jsonify(ssid=ssid)
|
||||
|
||||
@app.route('/api/hotspot', methods=['POST'])
|
||||
def hotspot_config_update():
|
||||
"""Update Pi hotspot SSID and/or password via NetworkManager."""
|
||||
body = request.get_json(silent=True) or {}
|
||||
ssid = (body.get("ssid") or "").strip()
|
||||
password = (body.get("password") or "")
|
||||
|
||||
if not ssid:
|
||||
return jsonify(error="SSID is required."), 400
|
||||
if len(ssid) > 32:
|
||||
return jsonify(error="SSID must be at most 32 characters."), 400
|
||||
if password and len(password) < 8:
|
||||
return jsonify(error="Password must be at least 8 characters."), 400
|
||||
if password and len(password) > 63:
|
||||
return jsonify(error="Password must be at most 63 characters."), 400
|
||||
|
||||
try:
|
||||
subprocess.run(
|
||||
["sudo", "-n", "nmcli", "con", "mod", _HOTSPOT_PROFILE, "802-11-wireless.ssid", ssid],
|
||||
capture_output=True, text=True, timeout=10, check=True,
|
||||
)
|
||||
if password:
|
||||
subprocess.run(
|
||||
["sudo", "-n", "nmcli", "con", "mod", _HOTSPOT_PROFILE, "802-11-wireless-security.psk", password],
|
||||
capture_output=True, text=True, timeout=10, check=True,
|
||||
)
|
||||
return jsonify(message=f"Hotspot SSID updated to '{ssid}'."
|
||||
+ (" Password saved." if password else "")
|
||||
+ " Restart hotspot or device to apply."),
|
||||
except subprocess.CalledProcessError as e:
|
||||
return jsonify(error=f"Could not update hotspot: {e.stderr.strip() or 'nmcli failed'}"), 500
|
||||
|
||||
@app.route('/api/trips')
|
||||
def trips():
|
||||
return jsonify({"trips": app_sql.list_trips()})
|
||||
|
||||
Reference in New Issue
Block a user