PicoWireless: ppwhttp add wildcard routes

This slightly reckless extension to ppwhttp adds support for wildcard routes, eg:

/get_led/<int:index>

Which will serve URLs in the form:

/get_led/10
/get_led/22

etc.

The wildcard includes <type:key>, attempting to match the behaviour of Flask.

Only type "int" is supported currently.

/get_led/<index> - would set data["index"] to a string
/get_led/<int:index> - would attempt to parse the URL part to an int, and would not serve eg: /get_led/hi

See plasma_ws2812_http.py for example usage.
pull/208/head
Phil Howard 2021-09-29 16:27:02 +01:00
rodzic b92d77a2f9
commit 78d50c2986
2 zmienionych plików z 99 dodań i 8 usunięć

Wyświetl plik

@ -9,6 +9,8 @@ except ImportError:
"""
This example uses the Plasma WS2812 LED library to drive a string of LEDs alongside the built-in RGB LED.
You should wire your LEDs to VBUS/GND and connect the data pin to pin 27 (unused by Pico Wireless).
Go to: https://<address>:<port>/set_led/<index> to set a single LED
"""
NUM_LEDS = 30 # Number of connected LEDs
@ -27,7 +29,7 @@ led_strip = plasma.WS2812(NUM_LEDS, LED_PIO, LED_SM, LED_PIN)
# Edit your routes here
# Nothing fancy is supported, just plain ol' URLs and GET/POST methods
@ppwhttp.route("/", methods=["GET", "POST"])
def get_home(method, url, data=None):
def get_home(method, url, data):
if method == "POST":
global r, g, b
r = int(data.get("r", 0))
@ -46,6 +48,45 @@ def get_home(method, url, data=None):
</form>""".format(r=r, g=g, b=b)
# This wildcard route allows us to visit eg `/set_led/<index>`
# to get/set the state of LED <index>
# You should *probably* not modify state with GET, even though you can
# so we use a form and POST to handle changing things.
@ppwhttp.route("/set_led/<int:index>", methods=["GET", "POST"])
def set_led(method, url, data):
i = int(data.get("index", 0))
if method == "POST":
r = int(data.get("r", 0))
g = int(data.get("g", 0))
b = int(data.get("b", 0))
led_strip.set_rgb(i, r, g, b)
print("Set LED to {} {} {}".format(r, g, b))
else:
# TODO Fix WS2812 / APA102 get methods to correct for colour order/alignment
r, g, b, w = led_strip.get(i)
r = int(r)
g = int(g)
b = int(b)
return """LED: {i}<br /><form method="post" action="/set_led/{i}">
<input id="r" name="r" type="number" value="{r}" />
<input name="g" type="number" value="{g}" />
<input name="b" type="number" value="{b}" />
<input type="submit" value="Set LED" />
</form>""".format(i=i, r=r, g=g, b=b)
# This wildcard route allows us to visit eg `/get_led/<index>`
# to get the state of LED <index>
@ppwhttp.route("/get_led/<int:index>", methods="GET")
def get_led(method, url, data):
i = data.get("index", 0)
# TODO Fix WS2812 / APA102 get methods to correct for colour order/alignment
r, g, b, w = led_strip.get(i)
return "LED: {}<br />R: {:0.0f}<br />G: {:0.0f}<br />B: {:0.0f}".format(i, r, g, b)
@ppwhttp.route("/test", methods="GET")
def get_test(method, url):
return "Hello World!"

Wyświetl plik

@ -181,6 +181,38 @@ Connection: close
picowireless.client_stop(client_sock)
def find_route(route, url, method, data):
if len(url) > 0:
for key, value in route.items():
if key == url[0]:
return find_route(route[url[0]], url[1:], method, data)
elif key.startswith("<") and key.endswith(">"):
key = key[1:-1]
if ":" in key:
dtype, key = key.split(":")
else:
dtype = "str"
if dtype == "int":
try:
data[key] = int(url[0])
except ValueError:
continue
else:
data[key] = url[0]
return find_route(value, url[1:], method, data)
return None, None
if method in route:
return route[method], data
return None, None
def handle_http_request(server_sock, timeout=5000):
t_start = time.ticks_ms()
@ -221,16 +253,24 @@ def handle_http_request(server_sock, timeout=5000):
response = None
data = {}
if url.startswith("/"):
url = url[1:]
url = url.split("/")
handler, data = find_route(routes, url, method, data)
# Dispatch the request to the relevant route
if url in routes and method in routes[url] and callable(routes[url][method]):
if callable(handler):
if method == "POST":
data = {}
for var in body.split("&"):
key, value = var.split("=")
data[key] = value
response = routes[url][method](method, url, data)
if data == {}:
response = handler(method, url)
else:
response = routes[url][method](method, url)
response = handler(method, url, data)
if response is not None:
response = "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nContent-Type: text/html\r\n\r\n".format(len(response)) + response
@ -249,10 +289,20 @@ def route(url, methods="GET"):
if type(methods) is str:
methods = [methods]
if url.startswith("/"):
url = url[1:]
url = url.split("/")
def decorate(handler):
route = routes
for part in url:
if part not in route:
route[part] = {}
route = route[part]
for method in methods:
if url not in routes:
routes[url] = {}
routes[url][method] = handler
route[method] = handler
return decorate