@@ -21,6 +21,7 @@ class GurgleAppsWebserver:
2121
2222 def __init__ (self , wifi_ssid , wifi_password , port = 80 , timeout = 20 , doc_root = "/www" , log_level = 0 ):
2323 print ("GurgleApps.com Webserver" )
24+ self .ip_address = '1.1.1.1'
2425 self .port = port
2526 self .timeout = timeout
2627 self .wifi_ssid = wifi_ssid
@@ -50,16 +51,17 @@ def __init__(self, wifi_ssid, wifi_password, port=80, timeout=20, doc_root="/www
5051 print ('waiting for connection...' )
5152 time .sleep (1 )
5253
53- #if self.wlan.status() != 3:
54+ # if self.wlan.status() != 3:
5455 if self .wlan .isconnected () == False :
5556 raise RuntimeError ('network connection failed' )
5657 else :
5758 print ('connected' )
5859 status = self .wlan .ifconfig ()
5960 print ('ip = ' + status [0 ])
6061 self .serving = True
62+ self .ip_address = status [0 ]
6163 print ('point your browser to http://' , status [0 ])
62- #asyncio.new_event_loop()
64+ # asyncio.new_event_loop()
6365 print ("exit constructor" )
6466
6567 # async def start_server(self):
@@ -96,7 +98,7 @@ async def serve_request(self, reader, writer):
9698 post_data = None
9799 while True :
98100 line = await reader .readline ()
99- #print("line: "+str(line))
101+ # print("line: "+str(line))
100102 line = line .decode ('utf-8' ).strip ()
101103 if line == "" :
102104 break
@@ -109,7 +111,7 @@ async def serve_request(self, reader, writer):
109111 method = match .group (1 )
110112 url = match .group (2 )
111113 print (method , url )
112- else : # regex didn't match, try splitting the request line
114+ else : # regex didn't match, try splitting the request line
113115 request_parts = request_raw .split (" " )
114116 if len (request_parts ) > 1 :
115117 method = request_parts [0 ]
@@ -144,14 +146,22 @@ async def serve_request(self, reader, writer):
144146 file_path = self .doc_root + url
145147 if self .log_level > 0 :
146148 print ("file_path: " + str (file_path ))
147- #if uos.stat(file_path)[6] > 0:
149+ # if uos.stat(file_path)[6] > 0:
148150 if self .file_exists (file_path ):
149151 content_type = self .get_content_type (url )
150152 if self .log_level > 1 :
151153 print ("content_type: " + str (content_type ))
152154 await response .send_file (file_path , content_type = content_type )
153155 return
154- print ("file not found" )
156+ if url == "/" :
157+ print ("root" )
158+ files_and_folders = self .list_files_and_folders (self .doc_root )
159+ await response .send_iterator (self .generate_root_page_html (files_and_folders ))
160+ return
161+ html = self .generate_root_page_html (files_and_folders )
162+ await response .send (html )
163+ return
164+ print ("file not found " + url )
155165 await response .send (self .html % "page not found " + url , status_code = 404 )
156166 if (url == "/shutdown" ):
157167 self .serving = False
@@ -163,7 +173,7 @@ def dir_exists(self, filename):
163173 return (os .stat (filename )[0 ] & 0x4000 ) != 0
164174 except OSError :
165175 return False
166-
176+
167177 def file_exists (self , filename ):
168178 try :
169179 return (os .stat (filename )[0 ] & 0x4000 ) == 0
@@ -220,8 +230,7 @@ def get_file_extension(self, file_path):
220230 return file_parts [- 1 ]
221231 return ''
222232
223-
224- def get_content_type (self ,file_path ):
233+ def get_content_type (self , file_path ):
225234 extension = self .get_file_extension (file_path )
226235 content_type_map = {
227236 'html' : 'text/html' ,
@@ -231,6 +240,127 @@ def get_content_type(self,file_path):
231240 'jpeg' : 'image/jpeg' ,
232241 'png' : 'image/png' ,
233242 'gif' : 'image/gif' ,
234- 'ico' : 'image/x-icon'
243+ 'webp' : 'image/webp' ,
244+ 'ico' : 'image/x-icon' ,
245+ 'svg' : 'image/svg+xml' ,
246+ 'json' : 'application/json' ,
247+ 'xml' : 'application/xml' ,
248+ 'pdf' : 'application/pdf' ,
249+ 'zip' : 'application/zip' ,
250+ 'txt' : 'text/plain' ,
251+ 'csv' : 'text/csv' ,
252+ 'mp3' : 'audio/mpeg' ,
253+ 'mp4' : 'video/mp4' ,
254+ 'wav' : 'audio/wav' ,
255+ 'ogg' : 'audio/ogg' ,
256+ 'webm' : 'video/webm' ,
235257 }
236258 return content_type_map .get (extension , 'text/plain' )
259+
260+ # long pause for dots 4 quick blinks for zero 2 quick for a dot
261+ async def blink_ip (self , led_pin , ip = None , repeat = 2 , delay_between_digits = 0.9 , last_only = False ):
262+ delay_between_repititions = 2
263+ if ip == None :
264+ ip = self .ip_address
265+ print ("blink_ip: " + str (ip ))
266+
267+ def blink_element (element , pin , duration = 0.27 ):
268+ if element == '-' :
269+ blinks = 9
270+ duration = 0.1
271+ elif element == '.' :
272+ blinks = 2
273+ duration = 0.1
274+ elif element == 0 :
275+ blinks = 4
276+ duration = 0.1
277+ else :
278+ blinks = element
279+
280+ for _ in range (blinks ):
281+ pin .on ()
282+ time .sleep (duration )
283+ pin .off ()
284+ time .sleep (duration )
285+
286+ ip_digits_and_dots = []
287+ ip_parts = ip .split ('.' )
288+ if last_only :
289+ # Only blink the last part of the IP address
290+ ip_parts = [ip_parts [- 1 ]]
291+
292+ for part in ip_parts :
293+ for digit in part :
294+ ip_digits_and_dots .append (int (digit ))
295+ # Add a dot to the list to represent the separator
296+ ip_digits_and_dots .append ('.' )
297+ ip_digits_and_dots .pop () # Remove the last dot
298+ # Add a dash to the list to represent the end of the IP address
299+ ip_digits_and_dots .append ('-' )
300+
301+ for _ in range (repeat ):
302+ for element in ip_digits_and_dots :
303+ blink_element (element , led_pin )
304+ await asyncio .sleep (delay_between_digits if element != '.' else 2 * delay_between_digits )
305+ await asyncio .sleep (delay_between_repititions )
306+
307+ def list_files_and_folders (self , path ):
308+ entries = uos .ilistdir (path )
309+ files_and_folders = []
310+ for entry in entries :
311+ name = entry [0 ]
312+ mode = entry [1 ]
313+ if mode & 0o170000 == 0o040000 : # Check if it's a directory
314+ files_and_folders .append ({"name" : name , "type" : "directory" })
315+ elif mode & 0o170000 == 0o100000 : # Check if it's a file
316+ files_and_folders .append ({"name" : name , "type" : "file" })
317+ return files_and_folders
318+
319+ def generate_root_page_html (self , files_and_folders ):
320+ yield """
321+ <!DOCTYPE html>
322+ <html>
323+ <head>
324+ <title>GurgleApps.com Webserver</title>
325+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
326+ <link href="/styles.css" rel="stylesheet">
327+ </head>
328+ <body class="bg-gray-100">
329+ """
330+ yield """
331+ <div class="relative flex min-h-screen flex-col justify-center overflow-hidden bg-gray-50 py-6 sm:py-12">
332+ <div class="relative bg-white px-6 pb-8 pt-10 shadow-xl ring-1 ring-gray-900/5 sm:mx-auto sm:max-w-lg sm:rounded-lg sm:px-10">
333+ <div class="mx-auto max-w-md">
334+ <img src="/img/logo.svg" class="h-12 w-auto" alt="GurgleApps.com">
335+ """
336+ yield """
337+ <div class="divide-y divide-gray-300/50">
338+ <div class="space-y-6 py-8 text-base leading-7 text-gray-600">
339+ <h1 class="text-lg font-semibold">Welcome to GurgleApps.com Webserver</h1>
340+ <h12 class="text-base font-semibold">File List:</h2>
341+ <ul class="space-y-2 mt-3">
342+ """
343+ folder_icon_svg = """
344+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6 fill-indigo-800">
345+ <path d="M19.5 21a3 3 0 003-3v-4.5a3 3 0 00-3-3h-15a3 3 0 00-3 3V18a3 3 0 003 3h15zM1.5 10.146V6a3 3 0 013-3h5.379a2.25 2.25 0 011.59.659l2.122 2.121c.14.141.331.22.53.22H19.5a3 3 0 013 3v1.146A4.483 4.483 0 0019.5 9h-15a4.483 4.483 0 00-3 1.146z" />
346+ </svg>
347+ """
348+ file_icon_svg = """
349+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6 fill-indigo-800">
350+ <path d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0016.5 9h-1.875a1.875 1.875 0 01-1.875-1.875V5.25A3.75 3.75 0 009 1.5H5.625z" />
351+ <path d="M12.971 1.816A5.23 5.23 0 0114.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 013.434 1.279 9.768 9.768 0 00-6.963-6.963z" />
352+ </svg>
353+ """
354+ for file_or_folder in files_and_folders :
355+ icon = folder_icon_svg if file_or_folder ['type' ] == 'directory' else file_icon_svg
356+ yield f"<li class='border-t pt-1'><a href='/{ file_or_folder ['name' ]} ' class='flex items-center font-semibold text-slate-800 hover:text-indigo-800'>{ icon } <p class='ml-2'>{ file_or_folder ['name' ]} </p></a></li>"
357+ yield "</ul>"
358+ # Closing tags for the body and container div
359+ yield """
360+ </div>
361+ <div class="pt-3 text-base font-semibold leading-7">
362+ <p class="text-gray-900">More information</p><p><a href="https://gurgleapps.com/learn/projects/micropython-web-server-control-raspberry-pi-pico-projects" class="text-indigo-500 hover:text-sky-600">Project Home →</a>
363+ </p></div></div></div></div></div></body></html>
364+ """
365+
366+
0 commit comments