@@ -21,6 +21,7 @@ class GurgleAppsWebserver:
21
21
22
22
def __init__ (self , wifi_ssid , wifi_password , port = 80 , timeout = 20 , doc_root = "/www" , log_level = 0 ):
23
23
print ("GurgleApps.com Webserver" )
24
+ self .ip_address = '1.1.1.1'
24
25
self .port = port
25
26
self .timeout = timeout
26
27
self .wifi_ssid = wifi_ssid
@@ -50,16 +51,17 @@ def __init__(self, wifi_ssid, wifi_password, port=80, timeout=20, doc_root="/www
50
51
print ('waiting for connection...' )
51
52
time .sleep (1 )
52
53
53
- #if self.wlan.status() != 3:
54
+ # if self.wlan.status() != 3:
54
55
if self .wlan .isconnected () == False :
55
56
raise RuntimeError ('network connection failed' )
56
57
else :
57
58
print ('connected' )
58
59
status = self .wlan .ifconfig ()
59
60
print ('ip = ' + status [0 ])
60
61
self .serving = True
62
+ self .ip_address = status [0 ]
61
63
print ('point your browser to http://' , status [0 ])
62
- #asyncio.new_event_loop()
64
+ # asyncio.new_event_loop()
63
65
print ("exit constructor" )
64
66
65
67
# async def start_server(self):
@@ -96,7 +98,7 @@ async def serve_request(self, reader, writer):
96
98
post_data = None
97
99
while True :
98
100
line = await reader .readline ()
99
- #print("line: "+str(line))
101
+ # print("line: "+str(line))
100
102
line = line .decode ('utf-8' ).strip ()
101
103
if line == "" :
102
104
break
@@ -109,7 +111,7 @@ async def serve_request(self, reader, writer):
109
111
method = match .group (1 )
110
112
url = match .group (2 )
111
113
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
113
115
request_parts = request_raw .split (" " )
114
116
if len (request_parts ) > 1 :
115
117
method = request_parts [0 ]
@@ -144,14 +146,22 @@ async def serve_request(self, reader, writer):
144
146
file_path = self .doc_root + url
145
147
if self .log_level > 0 :
146
148
print ("file_path: " + str (file_path ))
147
- #if uos.stat(file_path)[6] > 0:
149
+ # if uos.stat(file_path)[6] > 0:
148
150
if self .file_exists (file_path ):
149
151
content_type = self .get_content_type (url )
150
152
if self .log_level > 1 :
151
153
print ("content_type: " + str (content_type ))
152
154
await response .send_file (file_path , content_type = content_type )
153
155
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 )
155
165
await response .send (self .html % "page not found " + url , status_code = 404 )
156
166
if (url == "/shutdown" ):
157
167
self .serving = False
@@ -163,7 +173,7 @@ def dir_exists(self, filename):
163
173
return (os .stat (filename )[0 ] & 0x4000 ) != 0
164
174
except OSError :
165
175
return False
166
-
176
+
167
177
def file_exists (self , filename ):
168
178
try :
169
179
return (os .stat (filename )[0 ] & 0x4000 ) == 0
@@ -220,8 +230,7 @@ def get_file_extension(self, file_path):
220
230
return file_parts [- 1 ]
221
231
return ''
222
232
223
-
224
- def get_content_type (self ,file_path ):
233
+ def get_content_type (self , file_path ):
225
234
extension = self .get_file_extension (file_path )
226
235
content_type_map = {
227
236
'html' : 'text/html' ,
@@ -231,6 +240,127 @@ def get_content_type(self,file_path):
231
240
'jpeg' : 'image/jpeg' ,
232
241
'png' : 'image/png' ,
233
242
'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' ,
235
257
}
236
258
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