piTop[4] Web Server Control LEDs

I am trying to make the WebServer examples found in the Labs work.
I have a Pi-top[4] with the Robotics Kit. I have plugged in 4 LEDs.
I am using Visual Studio Code with the remorte SSH extension on my windows laptop to SSH tunnel into the Pi-top and modify and run the code on the Pi-top.
I want a WebController with 4 html buttons to turn on and off the LEDs on the Pi-Top.
I have been working through all of the examples in the SDK but I am stuck getting my HTML to correctly extend and modify the jinja blueprint templates.
I have this Python Code:

from pitop import Pitop
from pitop import LED, Button, UltrasonicSensor
from pitop.labs import RoverWebController
from time import sleep

from PIL import Image, ImageDraw
from datetime import datetime
import gevent
#setup Robot
print("Creating Robot Pitop")
myRobot = Pitop()
myRobot.add_component(Button("D6"), "Back Button")
myRobot.add_component(LED("D1"), "GreenLED")
myRobot.add_component(LED("D0"), "YellowLED")
myRobot.add_component(LED("D2"), "redRightLED")
myRobot.add_component(LED("D7"), "redLeftLED")
myRobot.add_component(UltrasonicSensor("D3"))
print("Created!")
print(type(myRobot.state))
for _ in myRobot.state:
    print(_, myRobot.state[_])
#SETUP MiniSCREEN
ms = myRobot.miniscreen

#Setup MultiThreed Event handler
hub = gevent.get_hub()
#Setup Server
myServer = RoverWebController()

elapsed = 0.0
#Functions

def manageMiniScreen():
    # global startTime
    screenInvert =False
    global elapsed 
    tick = 0.2
    while True:
        sleep(tick)
        elapsed += tick
        if ms.select_button.is_pressed:
            if screenInvert == True:
                screenInvert = False
            else:
                screenInvert = True
        
        if ms.cancel_button.is_pressed:
            print("Stopping Server")
            print("X button on PiTop Pressed")
            quit()  #Close Python Script since ms.server_froever(), canot be stopped
        
        makeScreen(elapsed, screenInvert)
           
def makeScreen(inputTime, invert=False):
    if invert:
        bgColor = "#000000"
        penColor = "#FFFFFF"
    else:
        penColor = "#000000"
        bgColor = "#FFFFFF"
    thisScreen = Image.new(ms.mode, ms.size, bgColor)
    thisCanvas = ImageDraw.Draw(thisScreen)
    thisSize = thisScreen.size
    # print(thisSize)
    thisCanvas.text((5,10), "Server Running", fill=penColor, align ="left")
    thisCanvas.text((5,20),f"Run Time: {inputTime:.1f}",fill=penColor, align ="left")
    
    thisCanvas.text((thisSize[0]-40,0), "Exit:->",fill=penColor , align="right")
    thisCanvas.text((thisSize[0]-60,50), "Invert:->",fill=penColor , align="right")
    ms.display_image(thisScreen)

def broadcast_runTime():
    global elapsed
    while True:
        sleep(1)
        packet = {"time", elapsed}
        print(packet)
        myServer.broadcast(packet)
def broadcast_robot_state():
    while True:
        sleep(0.1)
        myServer.broadcast(myRobot.state)

#Start Multi Threading Events
hub.threadpool.spawn(manageMiniScreen)
hub.threadpool.spawn(broadcast_runTime)
# hub.threadpool.spawn(broadcast_robot_state)

#Start Server
ms.display_text("Starting Server" )
myServer.serve_forever()

The manageMiniScreen Thread works and I can see my elapsed run time on the Mini screen and use the MiniScreen buttons.
My Terminal in Visual Studio Code has this output:

I am not Sure the broadcast_runTime is working.
I am not getting the Web server to display my custom pages correctly.
I created my own HTML file myTimeO.html which was based on the dashboard example:

<!DOCTYPE html>
{% extends "base-controller.html" %}

{% block head %}
    {{ super() }}
    
{% endblock %}

{% block body %}
  <header>
    <h1>
      Time Update?
    </h1>
  </header>

  <main>
    <table>
        <thead>
          <tr>
            <th colspan="2">TIME!</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>blank</td>
            <td id="this_time">0</td>
          </tr>
          <tr>
            <td>value</td>
            <td id="time_value">0</td>
          </tr>
        </tbody>
      </table>
    
  </main>

  <script>
    subscribe((message) => {
      const { time } = message;
      document.getElementById("time_value").innerHTML = time;
      
    });
  </script>
{% endblock %}

Which when viewed at http://127.0.0.1:8070/myTimeO.html Displays:

My File does not seem to be extending the base blueprints correctly. Using developer tools the html generated does not have any links to java script files. as shown by this error:


So I looked through the blueprint file that is the default index.html served at http://127.0.0.1:8070 and found the scripting lines that are included there. I modified my HTML file to now be myTimeM.html:

<!DOCTYPE html>

{% extends "base-controller.html" %}

{% block head %}
    {{ super() }}
    <link rel="stylesheet" href="/base/index.css">
    <link rel="stylesheet" href="/video/styles.css">
    <script type="text/javascript" src="/messaging/pubsub.js"></script>
    <script type="text/javascript" src="/webcomponents/vendor/nipplejs.min.js"></script>
    <script type="text/javascript" src="/webcomponents/joystick-component.js"></script>
    <link rel="stylesheet" href="styles.css"></link>
{% endblock %}

{% block body %}
  <header>
    <h1>
      Time Update?
    </h1>
  </header>

  <main>
    <table>
        <thead>
          <tr>
            <th colspan="2">TIME!</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>blank</td>
            <td id="this_time">0</td>
          </tr>
          <tr>
            <td>value</td>
            <td id="time_value">0</td>
          </tr>
        </tbody>
      </table>
    
  </main>

  <script>
    subscribe((message) => {
      const { time } = message;
      document.getElementById("time_value").innerHTML = time;
      
    });
  </script>
{% endblock %}

Which now views as:


I can see that the CSS is now applied, and comparing the sources, I now have the Js file is now loaded into my html:

But to do this I had to copy the code in. As the images show I seem to be having a problem with the extending of the Jinja templates. I should not see the {%extends … lines at the top of my pages. This webpage stays static and the value number is not changing. So I think somewhere my html pages are not correctly extending the blueprint templates and therefore are not currently able to respond to the broadcast_runTime event of the webserver.

So My current task is to try and get the value in the html table of the webserver to match the elapsed time value that is showing on the miniscreen.

Any help would be greatly appreciated.

I solved the extends issue and the webpages not displaying correctly.
I went thru the web lesson on further and found this subtle little detail.

And noticed in the index.html file that it was creating links as such:

<a href="/dashboard">Dashboard</a>

but when I went to type that in I instinctively completed the file name as dashboard .html

<a href="/dashboard.html">Dashboard</a>

Well, I discovered that when you link to the file with the complete file type it does not work.

I went back and verified this in my original server that was not using the index.html, but I was typing in the http://127.0.0.1:8070/myTimeM.html which would not render correctly. When I type in the url http://127.0.0.1:8070/myTimeM it does render correctly.

So, can someone explain this functionality of the Jinja extends style html that sdoes not want us to use the full file name?