Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ format-python: ## Format the Python code
.PHONY: lint
lint: ## Lints the python code and documents
markdownlint -c .markdownlint.yaml **/*.md
pylint src/ --ignore Microdot.py
pylint src/ --ignore-paths src/server/microdot,src/server/utemplate

help: ## Show this help.
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
51 changes: 47 additions & 4 deletions src/server/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ async def index(self, request: Request):
"""
led_list = [{"name": led.name()} for led in self.gundam.get_all_leds()]
show_list = self.gundam.config['lightshow']

running_show = self._is_lightshow_running()
return await Template('index.html').render_async(
name_of_title="Gundam LED Control",
all_leds=led_list,
lightshows=show_list
lightshows=show_list,
running_show=running_show
), 200, {'Content-Type': 'text/html'}

@safe_execution
Expand All @@ -45,14 +46,28 @@ async def canary(self, request: Request):
return "chirp", 202

def all_on(self, request: Request):
"""
Turns on all LEDs.
:param request: Ignored.
:return: HTTP 202 and message
"""
self.gundam.all_on()
return "All leds are on", 202

def all_off(self, request: Request):
"""
Turns off all LEDs.
:param request: Ignored.
:return: HTTP 202 and message
"""
self.gundam.all_off()
return "All leds are off", 202

async def _connect_to_wifi(self):
"""
Attempts to connect the Pico to WiFi. If succesful, will blink the onboard LED.
If it fails, it will halt the system.
"""
ipaddress: str = await self.hardware.networking().connect_to_wifi(self.settings['ssid'], self.settings['password'])
if ipaddress:
print(f"Server started on {ipaddress}")
Expand All @@ -73,14 +88,20 @@ async def run(self):

await self.app.start_server(host='0.0.0.0', port=80, debug=True)

def _is_lightshow_running(self):
"""
:return: True if lightshow is running, False otherwise
"""
existing_task = getattr(self.gundam, "current_task", None)
return existing_task is not None

def _add_routes(self):
"""
Given a server adds all endpoints for Leds and lightshows
"""
self.app.route("/")(self.index)
self.app.route("/index")(self.index)
self.app.route("/canary")(self.canary)
# TODO add a /stop route to stop all lightshows

@self.app.route("/led/<led_name>/on")
@safe_execution
Expand All @@ -102,6 +123,28 @@ async def led_off_handler(request, led_name):

self.app.route(path)(create_show_handler(method_func, self.gundam))

@self.app.route("/lightshow/stop")
@safe_execution
async def stop_lightshow(request):
"""
Stops any currently running lightshow task on the gundam instance.
"""
existing_task = getattr(self.gundam, "current_task", None)

if existing_task and not existing_task.done():
existing_task.cancel()
try:
# Wait for the task to acknowledge the cancellation
await existing_task
except asyncio.CancelledError:
pass

self.gundam.all_off()
return {"status": "stopped", "message": "Lightshow terminated"}, 200

self.gundam.all_off()
return {"status": "idle", "message": "No active lightshow to stop"}, 200

# 404 Handler
@self.app.errorhandler(404)
async def not_found(request):
Expand All @@ -112,7 +155,7 @@ async def not_found(request):
path = route[1].url_pattern # The regex pattern of the URL

if "<led_name>" in path:
x = [led.name() for led in self.gundam.get_all_leds() if led.enabled() ]
x = [led.name() for led in self.gundam.get_all_leds() if led.enabled()]
for led_name in x:
complete_path = path.replace("<led_name>", led_name)
urls.append(complete_path)
Expand Down
12 changes: 11 additions & 1 deletion src/templates/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% args name_of_title, all_leds, lightshows %}
{% args name_of_title, all_leds, lightshows, running_show %}
<!DOCTYPE html>
<html>
<head>
Expand All @@ -22,6 +22,11 @@ <h3>Control individual LEDs</h3>
</ul>

<h3>Light Shows</h3>
{% if running_show %}
<p>A lightshow is currently running.</p>
{% else %}
<p>No lightshow is running.</p>
{% endif %}
<ul>
{% for show in lightshows %}
<li>
Expand All @@ -30,6 +35,11 @@ <h3>Light Shows</h3>
</form>
</li>
{% endfor %}
<li>
<form action="./lightshow/stop">
<input type="submit" value="Halt any running lightshow">
</form>
</li>
</ul>

<br>
Expand Down
14 changes: 14 additions & 0 deletions tests/LocalServerTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ async def activation(self):
print("Mobile Doll activation")
return

async def infinite(self):
try:
while True:
await asyncio.sleep(1)
print("Hi")
except asyncio.CancelledError:
print("Infinite loop cancelled")
raise


def main():

Expand All @@ -42,6 +51,11 @@ def main():
"name": "Activate Virgo",
"path": "activation",
"method": "activation"
},
{
"name": "Infinitelooop test",
"path": "infinite",
"method": "infinite"
}
]
}
Expand Down