From 13d68c7c2471ab58cfc7c24482c81ef585b6e9da Mon Sep 17 00:00:00 2001 From: Simone Margaritelli Date: Tue, 15 Oct 2019 11:50:09 +0200 Subject: [PATCH] misc: attempted refactoring of the display support in something less shitty --- pwnagotchi/plugins/default/screen_refresh.py | 2 +- pwnagotchi/ui/display.py | 161 ++---------------- pwnagotchi/ui/hw/__init__.py | 23 +++ pwnagotchi/ui/hw/base.py | 40 +++++ pwnagotchi/ui/hw/inky.py | 72 ++++++++ .../ui/{inkyphat => hw/libs}/__init__.py | 0 .../{papirus => hw/libs/inkyphat}/__init__.py | 0 .../ui/{ => hw/libs}/inkyphat/inkyfast.py | 0 .../ui/{ => hw/libs}/inkyphat/inkyphatfast.py | 0 .../libs/papirus}/__init__.py | 0 pwnagotchi/ui/{ => hw/libs}/papirus/epd.py | 2 +- pwnagotchi/ui/{ => hw/libs}/papirus/lm75b.py | 0 .../oledhat => hw/libs/waveshare}/__init__.py | 0 .../{ => hw/libs}/waveshare/oledhat/SH1106.py | 5 +- .../libs/waveshare/oledhat}/__init__.py | 0 .../{ => hw/libs}/waveshare/oledhat/config.py | 0 .../ui/{ => hw/libs}/waveshare/oledhat/epd.py | 5 - .../v2 => hw/libs/waveshare/v1}/__init__.py | 0 .../ui/{ => hw/libs}/waveshare/v1/epd2in13.py | 1 - .../{ => hw/libs}/waveshare/v1/epd2in13bc.py | 2 - .../{ => hw/libs}/waveshare/v1/epdconfig.py | 0 .../ui/hw/libs/waveshare/v2/__init__.py | 0 .../{ => hw/libs}/waveshare/v2/waveshare.py | 0 pwnagotchi/ui/hw/oledhat.py | 45 +++++ pwnagotchi/ui/hw/papirus.py | 47 +++++ pwnagotchi/ui/hw/waveshare1.py | 86 ++++++++++ pwnagotchi/ui/hw/waveshare2.py | 69 ++++++++ pwnagotchi/ui/layout.py | 155 ----------------- pwnagotchi/ui/view.py | 11 +- pwnagotchi/utils.py | 20 +++ 30 files changed, 429 insertions(+), 317 deletions(-) create mode 100644 pwnagotchi/ui/hw/__init__.py create mode 100644 pwnagotchi/ui/hw/base.py create mode 100644 pwnagotchi/ui/hw/inky.py rename pwnagotchi/ui/{inkyphat => hw/libs}/__init__.py (100%) rename pwnagotchi/ui/{papirus => hw/libs/inkyphat}/__init__.py (100%) rename pwnagotchi/ui/{ => hw/libs}/inkyphat/inkyfast.py (100%) rename pwnagotchi/ui/{ => hw/libs}/inkyphat/inkyphatfast.py (100%) rename pwnagotchi/ui/{waveshare => hw/libs/papirus}/__init__.py (100%) rename pwnagotchi/ui/{ => hw/libs}/papirus/epd.py (99%) rename pwnagotchi/ui/{ => hw/libs}/papirus/lm75b.py (100%) rename pwnagotchi/ui/{waveshare/oledhat => hw/libs/waveshare}/__init__.py (100%) rename pwnagotchi/ui/{ => hw/libs}/waveshare/oledhat/SH1106.py (96%) rename pwnagotchi/ui/{waveshare/v1 => hw/libs/waveshare/oledhat}/__init__.py (100%) rename pwnagotchi/ui/{ => hw/libs}/waveshare/oledhat/config.py (100%) rename pwnagotchi/ui/{ => hw/libs}/waveshare/oledhat/epd.py (88%) rename pwnagotchi/ui/{waveshare/v2 => hw/libs/waveshare/v1}/__init__.py (100%) rename pwnagotchi/ui/{ => hw/libs}/waveshare/v1/epd2in13.py (99%) rename pwnagotchi/ui/{ => hw/libs}/waveshare/v1/epd2in13bc.py (99%) rename pwnagotchi/ui/{ => hw/libs}/waveshare/v1/epdconfig.py (100%) create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2/__init__.py rename pwnagotchi/ui/{ => hw/libs}/waveshare/v2/waveshare.py (100%) create mode 100644 pwnagotchi/ui/hw/oledhat.py create mode 100644 pwnagotchi/ui/hw/papirus.py create mode 100644 pwnagotchi/ui/hw/waveshare1.py create mode 100644 pwnagotchi/ui/hw/waveshare2.py delete mode 100644 pwnagotchi/ui/layout.py diff --git a/pwnagotchi/plugins/default/screen_refresh.py b/pwnagotchi/plugins/default/screen_refresh.py index 72c2a1c..94173ff 100644 --- a/pwnagotchi/plugins/default/screen_refresh.py +++ b/pwnagotchi/plugins/default/screen_refresh.py @@ -18,7 +18,7 @@ def on_ui_update(ui): global update_count update_count += 1 if update_count == OPTIONS['refresh_interval']: - ui._init_display() + ui.init_display() ui.set('status', "Screen cleaned") logging.info("Screen refreshing") update_count = 0 diff --git a/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py index 22f7f26..597691f 100644 --- a/pwnagotchi/ui/display.py +++ b/pwnagotchi/ui/display.py @@ -1,13 +1,12 @@ import _thread from threading import Lock -from PIL import Image import shutil import logging -import os import pwnagotchi, pwnagotchi.plugins as plugins -from pwnagotchi.ui.view import WHITE, View +import pwnagotchi.ui.hw as hw +from pwnagotchi.ui.view import View from http.server import BaseHTTPRequestHandler, HTTPServer @@ -88,24 +87,15 @@ class VideoHandler(BaseHTTPRequestHandler): class Display(View): def __init__(self, config, state={}): - super(Display, self).__init__(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._display_type = config['ui']['display']['type'] - self._display_color = config['ui']['display']['color'] - - self._render_cb = None - self._display = None self._httpd = None - if self._enabled: - self._init_display() - else: - self.on_render(self._on_view_rendered) - logging.warning("display module is disabled") + self.init_display() if self._video_enabled: _thread.start_new_thread(self._http_serve, ()) @@ -119,149 +109,33 @@ class Display(View): logging.info("could not get ip of usb0, video server not starting") def is_inky(self): - return self._display_type in ('inkyphat', 'inky') + return self._implementation.name == 'inky' def is_papirus(self): - return self._display_type in ('papirus', 'papi') + return self._implementation.name == 'papirus' def is_waveshare_v1(self): - return self._display_type in ('waveshare_1', 'ws_1', 'waveshare1', 'ws1') + return self._implementation.name == 'waveshare_1' def is_waveshare_v2(self): - return self._display_type in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2') + return self._implementation.name == 'waveshare_2' def is_oledhat(self): - return self._display_type in ('oledhat') + return self._implementation.name == 'oledhat' def is_waveshare_any(self): return self.is_waveshare_v1() or self.is_waveshare_v2() - def _init_display(self): - if self.is_inky(): - logging.info("initializing inky display") - from pwnagotchi.ui.inkyphat.inkyphatfast import InkyPHATFast - self._display = InkyPHATFast(self._display_color) - self._display.set_border(InkyPHATFast.BLACK) - self._render_cb = self._inky_render - - elif self.is_papirus(): - logging.info("initializing papirus display") - from pwnagotchi.ui.papirus.epd import EPD - os.environ['EPD_SIZE'] = '2.0' - self._display = EPD() - self._display.clear() - self._render_cb = self._papirus_render - - elif self.is_waveshare_v1(): - if self._display_color == 'black': - logging.info("initializing waveshare v1 display in monochromatic mode") - from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD - self._display = EPD() - self._display.init(self._display.lut_full_update) - self._display.Clear(0xFF) - self._display.init(self._display.lut_partial_update) - self._render_cb = self._waveshare_render - - else: - logging.info("initializing waveshare v1 display 3-color mode") - from pwnagotchi.ui.waveshare.v1.epd2in13bc import EPD - self._display = EPD() - self._display.init() - self._display.Clear() - self._render_cb = self._waveshare_bc_render - - elif self.is_waveshare_v2(): - logging.info("initializing waveshare v2 display") - from pwnagotchi.ui.waveshare.v2.waveshare import EPD - self._display = EPD() - self._display.init(self._display.FULL_UPDATE) - self._display.Clear(WHITE) - self._display.init(self._display.PART_UPDATE) - self._render_cb = self._waveshare_render - - elif self.is_oledhat(): - logging.info("initializing oledhat display") - from pwnagotchi.ui.waveshare.oledhat.epd import EPD - self._display = EPD() - self._display.init() - self._display.Clear() - self._render_cb = self._oledhat_render - + def init_display(self): + if self._enabled: + self._implementation.initialize() + plugins.on('display_setup', self._implementation) else: - logging.critical("unknown display type %s" % self._display_type) - - plugins.on('display_setup', self._display) - + logging.warning("display module is disabled") self.on_render(self._on_view_rendered) def clear(self): - if self._display is None: - logging.error("no display object created") - elif self.is_inky(): - self._display.Clear() - elif self.is_papirus(): - self._display.clear() - elif self.is_waveshare_any(): - self._display.Clear(WHITE) - elif self.is_oledhat(): - self._display.clear() - else: - logging.critical("unknown display type %s" % self._display_type) - - def _inky_render(self): - if self._display_color != 'mono': - display_colors = 3 - else: - display_colors = 2 - - img_buffer = self._canvas.convert('RGB').convert('P', palette=1, colors=display_colors) - if self._display_color == 'red': - img_buffer.putpalette([ - 255, 255, 255, # index 0 is white - 0, 0, 0, # index 1 is black - 255, 0, 0 # index 2 is red - ]) - elif self._display_color == 'yellow': - img_buffer.putpalette([ - 255, 255, 255, # index 0 is white - 0, 0, 0, # index 1 is black - 255, 255, 0 # index 2 is yellow - ]) - else: - img_buffer.putpalette([ - 255, 255, 255, # index 0 is white - 0, 0, 0 # index 1 is black - ]) - - self._display.set_image(img_buffer) - try: - self._display.show() - except: - logging.exception("error while rendering on inky") - - def _papirus_render(self): - self._display.display(self._canvas) - self._display.partial_update() - - def _waveshare_render(self): - buf = self._display.getbuffer(self._canvas) - if self.is_waveshare_v1(): - self._display.display(buf) - elif self.is_waveshare_v2(): - self._display.displayPartial(buf) - - def _oledhat_render(self): - self._display.display(self._canvas) - - def _waveshare_bc_render(self): - buf_black = self._display.getbuffer(self._canvas) - # emptyImage = Image.new('1', (self._display.height, self._display.width), 255) - # buf_color = self._display.getbuffer(emptyImage) - # self._display.display(buf_black,buf_color) - - # Custom display function that only handles black - # Was included in epd2in13bc.py - self._display.displayBlack(buf_black) + self._implementation.clear() def image(self): img = None @@ -271,8 +145,7 @@ class Display(View): def _on_view_rendered(self, img): VideoHandler.render(img) - if self._enabled: self._canvas = (img if self._rotation == 0 else img.rotate(self._rotation)) - if self._render_cb is not None: - self._render_cb() + if self._implementation is not None: + self._implementation.render(self._canvas) diff --git a/pwnagotchi/ui/hw/__init__.py b/pwnagotchi/ui/hw/__init__.py new file mode 100644 index 0000000..94dc671 --- /dev/null +++ b/pwnagotchi/ui/hw/__init__.py @@ -0,0 +1,23 @@ +from pwnagotchi.ui.hw.inky import Inky +from pwnagotchi.ui.hw.papirus import Papirus +from pwnagotchi.ui.hw.oledhat import OledHat +from pwnagotchi.ui.hw.waveshare1 import WaveshareV1 +from pwnagotchi.ui.hw.waveshare2 import WaveshareV2 + + +def display_for(config): + # config has been normalized already in utils.load_config + if config['ui']['display']['type'] == 'inky': + return Inky(config) + + elif config['ui']['display']['type'] == 'papirus': + return Papirus(config) + + if config['ui']['display']['type'] == 'oledhat': + return OledHat(config) + + elif config['ui']['display']['type'] == 'waveshare_1': + return WaveshareV1(config) + + elif config['ui']['display']['type'] == 'waveshare_2': + return WaveshareV2(config) diff --git a/pwnagotchi/ui/hw/base.py b/pwnagotchi/ui/hw/base.py new file mode 100644 index 0000000..e3e0db0 --- /dev/null +++ b/pwnagotchi/ui/hw/base.py @@ -0,0 +1,40 @@ +import pwnagotchi.ui.fonts as fonts + + +class DisplayImpl(object): + def __init__(self, config, name): + self.name = name + self.config = config['ui']['display'] + self._layout = { + 'width': 0, + 'height': 0, + 'face': (0, 0), + 'name': (0, 0), + 'channel': (0, 0), + 'aps': (0, 0), + 'uptime': (0, 0), + 'line1': (0, 0), + 'line2': (0, 0), + 'friend_face': (0, 0), + 'friend_name': (0, 0), + 'shakes': (0, 0), + 'mode': (0, 0), + # status is special :D + 'status': { + 'pos': (0, 0), + 'font': fonts.Medium, + 'max': 20 + } + } + + def layout(self): + raise NotImplementedError + + def initialize(self): + raise NotImplementedError + + def render(self, canvas): + raise NotImplementedError + + def clear(self): + raise NotImplementedError diff --git a/pwnagotchi/ui/hw/inky.py b/pwnagotchi/ui/hw/inky.py new file mode 100644 index 0000000..9157a4d --- /dev/null +++ b/pwnagotchi/ui/hw/inky.py @@ -0,0 +1,72 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Inky(DisplayImpl): + def __init__(self, config): + super(Inky, self).__init__(config, 'inky') + self._display = None + + def layout(self): + fonts.setup(10, 8, 10, 28) + self._layout['width'] = 212 + self._layout['height'] = 104 + self._layout['face'] = (0, 37) + self._layout['name'] = (5, 18) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (25, 0) + self._layout['uptime'] = (147, 0) + self._layout['line1'] = [0, 12, 212, 12] + self._layout['line2'] = [0, 92, 212, 92] + self._layout['friend_face'] = (0, 76) + self._layout['friend_name'] = (40, 78) + self._layout['shakes'] = (0, 93) + self._layout['mode'] = (187, 93) + self._layout['status'] = { + 'pos': (102, 18), + 'font': fonts.Small, + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing inky display") + from pwnagotchi.ui.hw.libs.inkyphat.inkyphatfast import InkyPHATFast + self._display = InkyPHATFast(self.config['color']) + self._display.set_border(InkyPHATFast.BLACK) + + def render(self, canvas): + if self.config['color'] != 'mono': + display_colors = 3 + else: + display_colors = 2 + + img_buffer = canvas.convert('RGB').convert('P', palette=1, colors=display_colors) + if self.config['color'] == 'red': + img_buffer.putpalette([ + 255, 255, 255, # index 0 is white + 0, 0, 0, # index 1 is black + 255, 0, 0 # index 2 is red + ]) + elif self.config['color'] == 'yellow': + img_buffer.putpalette([ + 255, 255, 255, # index 0 is white + 0, 0, 0, # index 1 is black + 255, 255, 0 # index 2 is yellow + ]) + else: + img_buffer.putpalette([ + 255, 255, 255, # index 0 is white + 0, 0, 0 # index 1 is black + ]) + + self._display.set_image(img_buffer) + try: + self._display.show() + except: + logging.exception("error while rendering on inky") + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/inkyphat/__init__.py b/pwnagotchi/ui/hw/libs/__init__.py similarity index 100% rename from pwnagotchi/ui/inkyphat/__init__.py rename to pwnagotchi/ui/hw/libs/__init__.py diff --git a/pwnagotchi/ui/papirus/__init__.py b/pwnagotchi/ui/hw/libs/inkyphat/__init__.py similarity index 100% rename from pwnagotchi/ui/papirus/__init__.py rename to pwnagotchi/ui/hw/libs/inkyphat/__init__.py diff --git a/pwnagotchi/ui/inkyphat/inkyfast.py b/pwnagotchi/ui/hw/libs/inkyphat/inkyfast.py similarity index 100% rename from pwnagotchi/ui/inkyphat/inkyfast.py rename to pwnagotchi/ui/hw/libs/inkyphat/inkyfast.py diff --git a/pwnagotchi/ui/inkyphat/inkyphatfast.py b/pwnagotchi/ui/hw/libs/inkyphat/inkyphatfast.py similarity index 100% rename from pwnagotchi/ui/inkyphat/inkyphatfast.py rename to pwnagotchi/ui/hw/libs/inkyphat/inkyphatfast.py diff --git a/pwnagotchi/ui/waveshare/__init__.py b/pwnagotchi/ui/hw/libs/papirus/__init__.py similarity index 100% rename from pwnagotchi/ui/waveshare/__init__.py rename to pwnagotchi/ui/hw/libs/papirus/__init__.py diff --git a/pwnagotchi/ui/papirus/epd.py b/pwnagotchi/ui/hw/libs/papirus/epd.py similarity index 99% rename from pwnagotchi/ui/papirus/epd.py rename to pwnagotchi/ui/hw/libs/papirus/epd.py index 923993b..bf8d572 100644 --- a/pwnagotchi/ui/papirus/epd.py +++ b/pwnagotchi/ui/hw/libs/papirus/epd.py @@ -15,7 +15,7 @@ from PIL import Image from PIL import ImageOps -from pwnagotchi.ui.papirus.lm75b import LM75B +from pwnagotchi.ui.hw.libs.papirus import LM75B import re import os import sys diff --git a/pwnagotchi/ui/papirus/lm75b.py b/pwnagotchi/ui/hw/libs/papirus/lm75b.py similarity index 100% rename from pwnagotchi/ui/papirus/lm75b.py rename to pwnagotchi/ui/hw/libs/papirus/lm75b.py diff --git a/pwnagotchi/ui/waveshare/oledhat/__init__.py b/pwnagotchi/ui/hw/libs/waveshare/__init__.py similarity index 100% rename from pwnagotchi/ui/waveshare/oledhat/__init__.py rename to pwnagotchi/ui/hw/libs/waveshare/__init__.py diff --git a/pwnagotchi/ui/waveshare/oledhat/SH1106.py b/pwnagotchi/ui/hw/libs/waveshare/oledhat/SH1106.py similarity index 96% rename from pwnagotchi/ui/waveshare/oledhat/SH1106.py rename to pwnagotchi/ui/hw/libs/waveshare/oledhat/SH1106.py index 68d4f18..a89716d 100644 --- a/pwnagotchi/ui/waveshare/oledhat/SH1106.py +++ b/pwnagotchi/ui/hw/libs/waveshare/oledhat/SH1106.py @@ -1,7 +1,6 @@ from . import config import RPi.GPIO as GPIO import time -import numpy as np Device_SPI = config.Device_SPI Device_I2C = config.Device_I2C @@ -121,9 +120,9 @@ class SH1106(object): GPIO.output(self._dc, GPIO.HIGH); for i in range(0,self.width):#for(int i=0;i