diff --git a/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py index 479324be..eac519f2 100644 --- a/pwnagotchi/ui/display.py +++ b/pwnagotchi/ui/display.py @@ -1,113 +1,22 @@ -import _thread -from threading import Lock - -import shutil import logging -import pwnagotchi, pwnagotchi.plugins as plugins +import pwnagotchi.plugins as plugins import pwnagotchi.ui.hw as hw +import pwnagotchi.ui.web as web from pwnagotchi.ui.view import View -from http.server import BaseHTTPRequestHandler, HTTPServer - - -class VideoHandler(BaseHTTPRequestHandler): - _lock = Lock() - _index = """ - - %s - - - -
- -
-
-
- -
-
- - - -""" - - @staticmethod - def render(img): - with VideoHandler._lock: - img.save("/root/pwnagotchi.png", format='PNG') - - def log_message(self, format, *args): - return - - def do_GET(self): - if self.path == '/': - self.send_response(200) - self.send_header('Content-type', 'text/html') - self.end_headers() - try: - self.wfile.write(bytes(self._index % (pwnagotchi.name(), 1000), "utf8")) - except: - pass - - elif self.path.startswith('/shutdown'): - pwnagotchi.shutdown() - - elif self.path.startswith('/ui'): - with self._lock: - self.send_response(200) - self.send_header('Content-type', 'image/png') - self.end_headers() - try: - with open("/root/pwnagotchi.png", 'rb') as fp: - shutil.copyfileobj(fp, self.wfile) - except: - pass - else: - self.send_response(404) - class Display(View): def __init__(self, config, state={}): super(Display, self).__init__(config, hw.display_for(config), state) - self._enabled = config['ui']['display']['enabled'] - self._rotation = config['ui']['display']['rotation'] - self._video_enabled = config['ui']['display']['video']['enabled'] - self._video_port = config['ui']['display']['video']['port'] - self._video_address = config['ui']['display']['video']['address'] - self._httpd = None + config = config['ui']['display'] + + self._enabled = config['enabled'] + self._rotation = config['rotation'] + self._webui = web.Server(config) self.init_display() - if self._video_enabled: - _thread.start_new_thread(self._http_serve, ()) - - def _http_serve(self): - if self._video_address is not None: - self._httpd = HTTPServer((self._video_address, self._video_port), VideoHandler) - logging.info("ui available at http://%s:%d/" % (self._video_address, self._video_port)) - self._httpd.serve_forever() - else: - logging.info("could not get ip of usb0, video server not starting") - def is_inky(self): return self._implementation.name == 'inky' @@ -126,7 +35,6 @@ class Display(View): def is_lcdhat(self): return self._implementation.name == 'lcdhat' - def is_waveshare_any(self): return self.is_waveshare_v1() or self.is_waveshare_v2() @@ -148,7 +56,7 @@ class Display(View): return img def _on_view_rendered(self, img): - VideoHandler.render(img) + web.update_frame(img) if self._enabled: self._canvas = (img if self._rotation == 0 else img.rotate(self._rotation)) if self._implementation is not None: diff --git a/pwnagotchi/ui/web.py b/pwnagotchi/ui/web.py new file mode 100644 index 00000000..1032e570 --- /dev/null +++ b/pwnagotchi/ui/web.py @@ -0,0 +1,144 @@ +import _thread +from http.server import BaseHTTPRequestHandler, HTTPServer +from threading import Lock +import shutil +import logging + +import pwnagotchi + +frame_path = '/root/pwnagotchi.png' +frame_format = 'PNG' +frame_ctype = 'image/png' +frame_lock = Lock() + + +def update_frame(img): + global frame_lock, frame_path, frame_format + with frame_lock: + img.save(frame_path, format=frame_format) + + +STYLE = """ +.block { + -webkit-appearance: button; + -moz-appearance: button; + appearance: button; + + display: block; + cursor: pointer; + text-align: center; +} +""" + +SCRIPT = """ +window.onload = function() { + var image = document.getElementById("ui"); + function updateImage() { + image.src = image.src.split("?")[0] + "?" + new Date().getTime(); + } + setInterval(updateImage, %d); +} +""" + +INDEX = """ + + %s + + + +
+ +
+
+
+ +
+
+ + + +""" + +SHUTDOWN = """ + + %s + + + +
+ Shutting down ... +
+ +""" + +class Handler(BaseHTTPRequestHandler): + # suppress internal logging + def log_message(self, format, *args): + return + + # just render some html in a 200 response + def _html(self, html): + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + try: + self.wfile.write(bytes(html, "utf8")) + except: + pass + + # serve the main html page + def _index(self): + self._html(INDEX % (pwnagotchi.name(), 1000)) + + # serve a message and shuts down the unit + def _shutdown(self): + self._html(SHUTDOWN % pwnagotchi.name()) + pwnagotchi.shutdown() + + # serve the PNG file with the display image + def _image(self): + global frame_path, frame_ctype + + with frame_lock: + self.send_response(200) + self.send_header('Content-type', frame_ctype) + self.end_headers() + try: + with open(frame_path, 'rb') as fp: + shutil.copyfileobj(fp, self.wfile) + except: + pass + + # main entry point of the http server + def do_GET(self): + global frame_lock + + if self.path == '/': + self._index() + + elif self.path.startswith('/shutdown'): + self._shutdown() + + elif self.path.startswith('/ui'): + self._image() + else: + self.send_response(404) + + +class Server(object): + def __init__(self, config): + self._enabled = config['video']['enabled'] + self._port = config['video']['port'] + self._address = config['video']['address'] + self._httpd = None + + if self._enabled: + _thread.start_new_thread(self._http_serve, ()) + + def _http_serve(self): + if self._address is not None: + self._httpd = HTTPServer((self._address, self._port), Handler) + logging.info("web ui available at http://%s:%d/" % (self._address, self._port)) + self._httpd.serve_forever() + else: + logging.info("could not get ip of usb0, video server not starting")