mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Add WebServer settings app
This commit is contained in:
@@ -18,6 +18,7 @@ set(MPOS_C_SOURCES
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/quirc/lib/version_db.c
|
${CMAKE_CURRENT_LIST_DIR}/quirc/lib/version_db.c
|
||||||
${CMAKE_CURRENT_LIST_DIR}/quirc/lib/decode.c
|
${CMAKE_CURRENT_LIST_DIR}/quirc/lib/decode.c
|
||||||
${CMAKE_CURRENT_LIST_DIR}/quirc/lib/quirc.c
|
${CMAKE_CURRENT_LIST_DIR}/quirc/lib/quirc.c
|
||||||
|
# ${CMAKE_CURRENT_LIST_DIR}/../lvgl_micropython/lib/micropython/extmod/modwebrepl.c
|
||||||
# ${CMAKE_CURRENT_LIST_DIR}/src/font_Noto_Sans_sat_emojis_compressed.c
|
# ${CMAKE_CURRENT_LIST_DIR}/src/font_Noto_Sans_sat_emojis_compressed.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ class LaunchHotspot(Activity):
|
|||||||
AppManager.start_app("com.micropythonos.hotspot")
|
AppManager.start_app("com.micropythonos.hotspot")
|
||||||
|
|
||||||
|
|
||||||
|
class LaunchWebServer(Activity):
|
||||||
|
|
||||||
|
def onCreate(self):
|
||||||
|
AppManager.start_app("com.micropythonos.webserver")
|
||||||
|
|
||||||
|
|
||||||
class Settings(SettingsActivity):
|
class Settings(SettingsActivity):
|
||||||
|
|
||||||
"""Override getIntent to provide prefs and settings via Intent extras"""
|
"""Override getIntent to provide prefs and settings via Intent extras"""
|
||||||
@@ -53,6 +59,7 @@ class Settings(SettingsActivity):
|
|||||||
intent.putExtra("settings", [
|
intent.putExtra("settings", [
|
||||||
{"title": "Wi-Fi", "key": "wifi_settings", "ui": "activity", "activity_class": LaunchWiFi},
|
{"title": "Wi-Fi", "key": "wifi_settings", "ui": "activity", "activity_class": LaunchWiFi},
|
||||||
{"title": "Hotspot", "key": "hotspot_settings", "ui": "activity", "activity_class": LaunchHotspot},
|
{"title": "Hotspot", "key": "hotspot_settings", "ui": "activity", "activity_class": LaunchHotspot},
|
||||||
|
{"title": "WebServer", "key": "webserver_settings", "ui": "activity", "activity_class": LaunchWebServer},
|
||||||
# Basic settings, alphabetically:
|
# Basic settings, alphabetically:
|
||||||
{"title": "Light/Dark Theme", "key": "theme_light_dark", "ui": "radiobuttons", "ui_options": [("Light", "light"), ("Dark", "dark")], "changed_callback": self.theme_changed},
|
{"title": "Light/Dark Theme", "key": "theme_light_dark", "ui": "radiobuttons", "ui_options": [("Light", "light"), ("Dark", "dark")], "changed_callback": self.theme_changed},
|
||||||
{"title": "Theme Color", "key": "theme_primary_color", "placeholder": "HTML hex color, like: EC048C", "ui": "dropdown", "ui_options": theme_colors, "changed_callback": self.theme_changed, "default_value": AppearanceManager.DEFAULT_PRIMARY_COLOR},
|
{"title": "Theme Color", "key": "theme_primary_color", "placeholder": "HTML hex color, like: EC048C", "ui": "dropdown", "ui_options": theme_colors, "changed_callback": self.theme_changed, "default_value": AppearanceManager.DEFAULT_PRIMARY_COLOR},
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "WebServer",
|
||||||
|
"publisher": "MicroPythonOS",
|
||||||
|
"short_description": "Configure and control the WebServer.",
|
||||||
|
"long_description": "Configure WebServer settings, start or stop the WebREPL web server.",
|
||||||
|
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.webserver/icons/com.micropythonos.webserver_0.1.0_64x64.png",
|
||||||
|
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.webserver/mpks/com.micropythonos.webserver_0.1.0.mpk",
|
||||||
|
"fullname": "com.micropythonos.webserver",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"category": "networking",
|
||||||
|
"activities": [
|
||||||
|
{
|
||||||
|
"entrypoint": "assets/webserver.py",
|
||||||
|
"classname": "WebServerApp",
|
||||||
|
"intent_filters": [
|
||||||
|
{
|
||||||
|
"action": "main",
|
||||||
|
"category": "launcher"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import lvgl as lv
|
||||||
|
|
||||||
|
from mpos import Activity, DisplayMetrics, Intent, SettingsActivity, SharedPreferences, WebServer
|
||||||
|
|
||||||
|
|
||||||
|
class WebServerApp(Activity):
|
||||||
|
status_label = None
|
||||||
|
detail_label = None
|
||||||
|
action_button = None
|
||||||
|
action_label = None
|
||||||
|
settings_button = None
|
||||||
|
prefs = None
|
||||||
|
|
||||||
|
def onCreate(self):
|
||||||
|
self.prefs = SharedPreferences(WebServer.PREFS_NAMESPACE, defaults=WebServer.DEFAULTS)
|
||||||
|
screen = lv.obj()
|
||||||
|
screen.set_style_border_width(0, lv.PART.MAIN)
|
||||||
|
screen.set_style_pad_all(DisplayMetrics.pct_of_width(3), lv.PART.MAIN)
|
||||||
|
screen.set_flex_flow(lv.FLEX_FLOW.COLUMN)
|
||||||
|
|
||||||
|
header = lv.label(screen)
|
||||||
|
header.set_text("WebServer")
|
||||||
|
header.set_style_text_font(lv.font_montserrat_20, lv.PART.MAIN)
|
||||||
|
|
||||||
|
self.status_label = lv.label(screen)
|
||||||
|
self.status_label.set_style_text_font(lv.font_montserrat_14, lv.PART.MAIN)
|
||||||
|
self.status_label.set_long_mode(lv.label.LONG_MODE.WRAP)
|
||||||
|
self.status_label.set_width(lv.pct(100))
|
||||||
|
|
||||||
|
self.detail_label = lv.label(screen)
|
||||||
|
self.detail_label.set_style_text_font(lv.font_montserrat_12, lv.PART.MAIN)
|
||||||
|
self.detail_label.set_long_mode(lv.label.LONG_MODE.WRAP)
|
||||||
|
self.detail_label.set_width(lv.pct(100))
|
||||||
|
|
||||||
|
button_row = lv.obj(screen)
|
||||||
|
button_row.set_width(lv.pct(100))
|
||||||
|
button_row.set_height(lv.SIZE_CONTENT)
|
||||||
|
button_row.set_style_border_width(0, lv.PART.MAIN)
|
||||||
|
button_row.set_style_pad_all(0, lv.PART.MAIN)
|
||||||
|
button_row.set_flex_flow(lv.FLEX_FLOW.ROW)
|
||||||
|
button_row.set_style_flex_main_place(lv.FLEX_ALIGN.SPACE_BETWEEN, lv.PART.MAIN)
|
||||||
|
|
||||||
|
self.action_button = lv.button(button_row)
|
||||||
|
self.action_button.set_size(lv.pct(45), lv.SIZE_CONTENT)
|
||||||
|
self.action_button.add_event_cb(self.toggle_webserver, lv.EVENT.CLICKED, None)
|
||||||
|
self.action_label = lv.label(self.action_button)
|
||||||
|
self.action_label.center()
|
||||||
|
|
||||||
|
self.settings_button = lv.button(button_row)
|
||||||
|
self.settings_button.set_size(lv.pct(45), lv.SIZE_CONTENT)
|
||||||
|
self.settings_button.add_event_cb(self.open_settings, lv.EVENT.CLICKED, None)
|
||||||
|
settings_label = lv.label(self.settings_button)
|
||||||
|
settings_label.set_text("Settings")
|
||||||
|
settings_label.center()
|
||||||
|
|
||||||
|
self.setContentView(screen)
|
||||||
|
|
||||||
|
def onResume(self, screen):
|
||||||
|
super().onResume(screen)
|
||||||
|
self.refresh_status()
|
||||||
|
|
||||||
|
def refresh_status(self):
|
||||||
|
status = WebServer.status()
|
||||||
|
state_text = "Running" if status.get("started") else "Stopped"
|
||||||
|
self.status_label.set_text(f"Status: {state_text}")
|
||||||
|
autostart_text = "On" if status.get("autostart") else "Off"
|
||||||
|
port = status.get("port")
|
||||||
|
self.detail_label.set_text(f"Port: {port}\nAutostart: {autostart_text}")
|
||||||
|
|
||||||
|
button_text = "Stop" if status.get("started") else "Start"
|
||||||
|
self.action_label.set_text(button_text)
|
||||||
|
self.action_label.center()
|
||||||
|
|
||||||
|
def toggle_webserver(self, event):
|
||||||
|
if WebServer.is_started():
|
||||||
|
WebServer.stop()
|
||||||
|
else:
|
||||||
|
WebServer.start()
|
||||||
|
self.refresh_status()
|
||||||
|
|
||||||
|
def open_settings(self, event):
|
||||||
|
intent = Intent(activity_class=SettingsActivity)
|
||||||
|
intent.putExtra("prefs", self.prefs)
|
||||||
|
intent.putExtra(
|
||||||
|
"settings",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Autostart",
|
||||||
|
"key": "autostart",
|
||||||
|
"ui": "radiobuttons",
|
||||||
|
"ui_options": [("On", "True"), ("Off", "False")],
|
||||||
|
"changed_callback": self.settings_changed,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Port",
|
||||||
|
"key": "port",
|
||||||
|
"placeholder": "WebServer port, e.g. 7890",
|
||||||
|
"changed_callback": self.settings_changed,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Password",
|
||||||
|
"key": "password",
|
||||||
|
"placeholder": "Max 9 characters",
|
||||||
|
"changed_callback": self.settings_changed,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.startActivity(intent)
|
||||||
|
|
||||||
|
def settings_changed(self, new_value):
|
||||||
|
WebServer.apply_settings(restart_if_running=True)
|
||||||
|
self.refresh_status()
|
||||||
@@ -19,6 +19,7 @@ from .build_info import BuildInfo
|
|||||||
|
|
||||||
# Battery manager (imported early for UI dependencies)
|
# Battery manager (imported early for UI dependencies)
|
||||||
from .battery_manager import BatteryManager
|
from .battery_manager import BatteryManager
|
||||||
|
from .webserver.webserver import WebServer
|
||||||
|
|
||||||
# Common activities
|
# Common activities
|
||||||
from .app.activities.chooser import ChooserActivity
|
from .app.activities.chooser import ChooserActivity
|
||||||
@@ -67,7 +68,7 @@ __all__ = [
|
|||||||
"Activity",
|
"Activity",
|
||||||
"SharedPreferences",
|
"SharedPreferences",
|
||||||
"ConnectivityManager", "DownloadManager", "WifiService", "AudioManager", "Intent",
|
"ConnectivityManager", "DownloadManager", "WifiService", "AudioManager", "Intent",
|
||||||
"ActivityNavigator", "AppManager", "TaskManager", "CameraManager", "BatteryManager",
|
"ActivityNavigator", "AppManager", "TaskManager", "CameraManager", "BatteryManager", "WebServer",
|
||||||
# Device and build info
|
# Device and build info
|
||||||
"DeviceInfo", "BuildInfo",
|
"DeviceInfo", "BuildInfo",
|
||||||
# Common activities
|
# Common activities
|
||||||
|
|||||||
@@ -229,11 +229,10 @@ async def asyncio_repl():
|
|||||||
TaskManager.create_task(asyncio_repl()) # only gets started after TaskManager.start()
|
TaskManager.create_task(asyncio_repl()) # only gets started after TaskManager.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import webrepl
|
from mpos import WebServer
|
||||||
from mpos.webserver import accept_handler as webrepl_accept_handler
|
WebServer.auto_start()
|
||||||
webrepl.start(port=7890, password="MPOSweb26", accept_handler=webrepl_accept_handler) # password is max 9 characters
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Could not start webrepl - this is normal on desktop systems: {e}")
|
print(f"Could not start webserver - this is normal on desktop systems: {e}")
|
||||||
|
|
||||||
async def ota_rollback_cancel():
|
async def ota_rollback_cancel():
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Web server helpers for MicroPythonOS."""
|
"""Web server helpers for MicroPythonOS."""
|
||||||
|
|
||||||
from .webrepl_http import accept_handler
|
from .webrepl_http import accept_handler
|
||||||
|
from .webserver import WebServer
|
||||||
|
|
||||||
__all__ = ["accept_handler"]
|
__all__ = ["accept_handler", "WebServer"]
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
# This module should be imported from REPL, not run from command line.
|
||||||
|
import binascii
|
||||||
|
import hashlib
|
||||||
|
from micropython import const
|
||||||
|
try:
|
||||||
|
import network
|
||||||
|
except ImportError:
|
||||||
|
network = None
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import websocket
|
||||||
|
import _webrepl
|
||||||
|
|
||||||
|
listen_s = None
|
||||||
|
client_s = None
|
||||||
|
|
||||||
|
DEBUG = 0
|
||||||
|
|
||||||
|
_DEFAULT_STATIC_HOST = const("https://micropython.org/webrepl/")
|
||||||
|
static_host = _DEFAULT_STATIC_HOST
|
||||||
|
|
||||||
|
|
||||||
|
def server_handshake(cl):
|
||||||
|
req = cl.makefile("rwb", 0)
|
||||||
|
# Skip HTTP GET line.
|
||||||
|
l = req.readline()
|
||||||
|
if DEBUG:
|
||||||
|
sys.stdout.write(repr(l))
|
||||||
|
|
||||||
|
webkey = None
|
||||||
|
upgrade = False
|
||||||
|
websocket = False
|
||||||
|
|
||||||
|
while True:
|
||||||
|
l = req.readline()
|
||||||
|
if not l:
|
||||||
|
# EOF in headers.
|
||||||
|
return False
|
||||||
|
if l == b"\r\n":
|
||||||
|
break
|
||||||
|
if DEBUG:
|
||||||
|
sys.stdout.write(l)
|
||||||
|
h, v = [x.strip() for x in l.split(b":", 1)]
|
||||||
|
if DEBUG:
|
||||||
|
print((h, v))
|
||||||
|
if h == b"Sec-WebSocket-Key":
|
||||||
|
webkey = v
|
||||||
|
elif h == b"Connection" and b"Upgrade" in v:
|
||||||
|
upgrade = True
|
||||||
|
elif h == b"Upgrade" and v == b"websocket":
|
||||||
|
websocket = True
|
||||||
|
|
||||||
|
if not (upgrade and websocket and webkey):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
print("Sec-WebSocket-Key:", webkey, len(webkey))
|
||||||
|
|
||||||
|
d = hashlib.sha1(webkey)
|
||||||
|
d.update(b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||||
|
respkey = d.digest()
|
||||||
|
respkey = binascii.b2a_base64(respkey)[:-1]
|
||||||
|
if DEBUG:
|
||||||
|
print("respkey:", respkey)
|
||||||
|
|
||||||
|
cl.send(
|
||||||
|
b"""\
|
||||||
|
HTTP/1.1 101 Switching Protocols\r
|
||||||
|
Upgrade: websocket\r
|
||||||
|
Connection: Upgrade\r
|
||||||
|
Sec-WebSocket-Accept: """
|
||||||
|
)
|
||||||
|
cl.send(respkey)
|
||||||
|
cl.send("\r\n\r\n")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def send_html(cl):
|
||||||
|
cl.send(
|
||||||
|
b"""\
|
||||||
|
HTTP/1.0 200 OK\r
|
||||||
|
\r
|
||||||
|
<base href=\""""
|
||||||
|
)
|
||||||
|
cl.send(static_host)
|
||||||
|
cl.send(
|
||||||
|
b"""\"></base>\r
|
||||||
|
<script src="webrepl_content.js"></script>\r
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
cl.close()
|
||||||
|
|
||||||
|
|
||||||
|
def setup_conn(port, accept_handler):
|
||||||
|
global listen_s
|
||||||
|
listen_s = socket.socket()
|
||||||
|
listen_s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
|
||||||
|
ai = socket.getaddrinfo("0.0.0.0", port)
|
||||||
|
addr = ai[0][4]
|
||||||
|
|
||||||
|
listen_s.bind(addr)
|
||||||
|
listen_s.listen(1)
|
||||||
|
if accept_handler:
|
||||||
|
listen_s.setsockopt(socket.SOL_SOCKET, 20, accept_handler)
|
||||||
|
if network:
|
||||||
|
for i in (network.WLAN.IF_AP, network.WLAN.IF_STA):
|
||||||
|
iface = network.WLAN(i)
|
||||||
|
if iface.active():
|
||||||
|
print("WebREPL server started on http://%s:%d/" % (iface.ifconfig()[0], port))
|
||||||
|
return listen_s
|
||||||
|
|
||||||
|
|
||||||
|
def accept_conn(listen_sock):
|
||||||
|
global client_s
|
||||||
|
cl, remote_addr = listen_sock.accept()
|
||||||
|
|
||||||
|
if not server_handshake(cl):
|
||||||
|
send_html(cl)
|
||||||
|
return False
|
||||||
|
|
||||||
|
prev = os.dupterm(None)
|
||||||
|
os.dupterm(prev)
|
||||||
|
if prev:
|
||||||
|
print("\nConcurrent WebREPL connection from", remote_addr, "rejected")
|
||||||
|
cl.close()
|
||||||
|
return False
|
||||||
|
print("\nWebREPL connection from:", remote_addr)
|
||||||
|
client_s = cl
|
||||||
|
|
||||||
|
ws = websocket.websocket(cl, True)
|
||||||
|
ws = _webrepl._webrepl(ws)
|
||||||
|
cl.setblocking(False)
|
||||||
|
# notify REPL on socket incoming data (ESP32/ESP8266-only)
|
||||||
|
if hasattr(os, "dupterm_notify"):
|
||||||
|
cl.setsockopt(socket.SOL_SOCKET, 20, os.dupterm_notify)
|
||||||
|
os.dupterm(ws)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def stop():
|
||||||
|
global listen_s, client_s
|
||||||
|
os.dupterm(None)
|
||||||
|
if client_s:
|
||||||
|
client_s.close()
|
||||||
|
if listen_s:
|
||||||
|
listen_s.close()
|
||||||
|
|
||||||
|
|
||||||
|
def start(port=8266, password=None, accept_handler=accept_conn):
|
||||||
|
global static_host
|
||||||
|
stop()
|
||||||
|
webrepl_pass = password
|
||||||
|
if webrepl_pass is None:
|
||||||
|
try:
|
||||||
|
import webrepl_cfg
|
||||||
|
|
||||||
|
webrepl_pass = webrepl_cfg.PASS
|
||||||
|
if hasattr(webrepl_cfg, "BASE"):
|
||||||
|
static_host = webrepl_cfg.BASE
|
||||||
|
except:
|
||||||
|
print("WebREPL is not configured, run 'import webrepl_setup'")
|
||||||
|
|
||||||
|
_webrepl.password(webrepl_pass)
|
||||||
|
s = setup_conn(port, accept_handler)
|
||||||
|
|
||||||
|
if accept_handler is None:
|
||||||
|
print("Starting webrepl in foreground mode")
|
||||||
|
# Run accept_conn to serve HTML until we get a websocket connection.
|
||||||
|
while not accept_conn(s):
|
||||||
|
pass
|
||||||
|
elif password is None:
|
||||||
|
print("Started webrepl in normal mode")
|
||||||
|
else:
|
||||||
|
print("Started webrepl in manual override mode")
|
||||||
|
|
||||||
|
|
||||||
|
def start_foreground(port=8266, password=None):
|
||||||
|
start(port, password, None)
|
||||||
@@ -3,7 +3,7 @@ import socket
|
|||||||
import uio
|
import uio
|
||||||
|
|
||||||
import _webrepl
|
import _webrepl
|
||||||
import webrepl
|
from . import webrepl
|
||||||
import websocket
|
import websocket
|
||||||
|
|
||||||
WEBREPL_HTML_PATH = "builtin/html/webrepl_inlined_minified.html"
|
WEBREPL_HTML_PATH = "builtin/html/webrepl_inlined_minified.html"
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
"""WebServer control for MicroPythonOS."""
|
||||||
|
|
||||||
|
from ..config import SharedPreferences
|
||||||
|
from .webrepl_http import accept_handler
|
||||||
|
|
||||||
|
|
||||||
|
class WebServer:
|
||||||
|
PREFS_NAMESPACE = "com.micropythonos.webserver"
|
||||||
|
DEFAULTS = {
|
||||||
|
"autostart": "False",
|
||||||
|
"port": "7890",
|
||||||
|
"password": "MPOSweb26",
|
||||||
|
}
|
||||||
|
|
||||||
|
_started = False
|
||||||
|
_port = None
|
||||||
|
_password = None
|
||||||
|
_autostart = None
|
||||||
|
_last_error = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _prefs(cls):
|
||||||
|
return SharedPreferences(cls.PREFS_NAMESPACE, defaults=cls.DEFAULTS)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _parse_bool(cls, value):
|
||||||
|
return str(value).lower() in ("true", "1", "yes", "on")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _parse_port(cls, value):
|
||||||
|
try:
|
||||||
|
return int(value)
|
||||||
|
except Exception:
|
||||||
|
return int(cls.DEFAULTS["port"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _sanitize_password(cls, value):
|
||||||
|
if not value:
|
||||||
|
value = cls.DEFAULTS["password"]
|
||||||
|
if len(value) > 9:
|
||||||
|
value = value[:9]
|
||||||
|
return value
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_settings(cls):
|
||||||
|
prefs = cls._prefs()
|
||||||
|
cls._autostart = cls._parse_bool(prefs.get_string("autostart", cls.DEFAULTS["autostart"]))
|
||||||
|
cls._port = cls._parse_port(prefs.get_string("port", cls.DEFAULTS["port"]))
|
||||||
|
cls._password = cls._sanitize_password(prefs.get_string("password", cls.DEFAULTS["password"]))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def status(cls):
|
||||||
|
cls.load_settings()
|
||||||
|
return {
|
||||||
|
"state": "started" if cls._started else "stopped",
|
||||||
|
"started": cls._started,
|
||||||
|
"port": cls._port,
|
||||||
|
"password": cls._password,
|
||||||
|
"autostart": cls._autostart,
|
||||||
|
"last_error": cls._last_error,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_started(cls):
|
||||||
|
return cls._started
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def start(cls):
|
||||||
|
cls.load_settings()
|
||||||
|
try:
|
||||||
|
from . import webrepl
|
||||||
|
|
||||||
|
webrepl.start(port=cls._port, password=cls._password, accept_handler=accept_handler)
|
||||||
|
cls._started = True
|
||||||
|
cls._last_error = None
|
||||||
|
print(f"WebServer started on port {cls._port}")
|
||||||
|
return True
|
||||||
|
except Exception as exc:
|
||||||
|
cls._last_error = exc
|
||||||
|
cls._started = False
|
||||||
|
print(f"WebServer start failed: {exc}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def stop(cls):
|
||||||
|
try:
|
||||||
|
from . import webrepl
|
||||||
|
|
||||||
|
if hasattr(webrepl, "stop"):
|
||||||
|
webrepl.stop()
|
||||||
|
cls._started = False
|
||||||
|
cls._last_error = None
|
||||||
|
print("WebServer stopped")
|
||||||
|
return True
|
||||||
|
except Exception as exc:
|
||||||
|
cls._last_error = exc
|
||||||
|
print(f"WebServer stop failed: {exc}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def apply_settings(cls, restart_if_running=True):
|
||||||
|
was_running = cls._started
|
||||||
|
cls.load_settings()
|
||||||
|
if was_running and restart_if_running:
|
||||||
|
cls.stop()
|
||||||
|
cls.start()
|
||||||
|
return cls.status()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def auto_start(cls):
|
||||||
|
cls.load_settings()
|
||||||
|
if cls._autostart:
|
||||||
|
return cls.start()
|
||||||
|
print("WebServer autostart disabled")
|
||||||
|
return False
|
||||||
@@ -155,6 +155,16 @@ elif [ "$target" == "unix" -o "$target" == "macOS" ]; then
|
|||||||
manifest=$(readlink -f "$codebasedir"/manifests/manifest.py)
|
manifest=$(readlink -f "$codebasedir"/manifests/manifest.py)
|
||||||
frozenmanifest="FROZEN_MANIFEST=$manifest"
|
frozenmanifest="FROZEN_MANIFEST=$manifest"
|
||||||
|
|
||||||
|
# Ensure WebREPL native module is enabled for unix/macOS builds.
|
||||||
|
mpconfig_unix="$codebasedir"/lvgl_micropython/lib/micropython/ports/unix/mpconfigport.h
|
||||||
|
if ! grep -q "MICROPY_PY_WEBREPL" "$mpconfig_unix"; then
|
||||||
|
echo "Enabling MICROPY_PY_WEBREPL in $mpconfig_unix"
|
||||||
|
sed -i.backup '/#include "mpconfigvariant.h"/a \
|
||||||
|
\n#ifndef MICROPY_PY_WEBREPL\n#define MICROPY_PY_WEBREPL (1)\n#endif\n' "$mpconfig_unix"
|
||||||
|
else
|
||||||
|
echo "MICROPY_PY_WEBREPL already configured in $mpconfig_unix"
|
||||||
|
fi
|
||||||
|
|
||||||
# Comment out @micropython.viper decorator for Unix/macOS builds
|
# Comment out @micropython.viper decorator for Unix/macOS builds
|
||||||
# (cross-compiler doesn't support Viper native code emitter)
|
# (cross-compiler doesn't support Viper native code emitter)
|
||||||
echo "Temporarily commenting out @micropython.viper decorator for Unix/macOS build..."
|
echo "Temporarily commenting out @micropython.viper decorator for Unix/macOS build..."
|
||||||
|
|||||||
Reference in New Issue
Block a user