mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Merge pull request #165 from dadav/feature/multi_display_preview
Feature/multi display preview
This commit is contained in:
@ -55,7 +55,6 @@ If you changed the `voice.py`- File, the translations need an update. Do it like
|
|||||||
Now you can use the `preview.py`-script to preview the changes:
|
Now you can use the `preview.py`-script to preview the changes:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./scripts/preview.py --lang it --display ws2 --port 8080 &
|
./scripts/preview.py --lang it --display ws1 ws2 inky --output preview.png
|
||||||
./scripts/preview.py --lang it --display inky --port 8081 &
|
# Now open preview.png
|
||||||
# Now open http://localhost:8080 and http://localhost:8081
|
|
||||||
```
|
```
|
||||||
|
@ -14,64 +14,26 @@ sys.path.insert(0,
|
|||||||
'../sdcard/rootfs/root/pwnagotchi/scripts/'))
|
'../sdcard/rootfs/root/pwnagotchi/scripts/'))
|
||||||
|
|
||||||
from pwnagotchi.ui.display import Display, VideoHandler
|
from pwnagotchi.ui.display import Display, VideoHandler
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
class CustomDisplay(Display):
|
class CustomDisplay(Display):
|
||||||
|
|
||||||
|
def __init__(self, config, state):
|
||||||
|
self.last_image = None
|
||||||
|
super(CustomDisplay, self).__init__(config, state)
|
||||||
|
|
||||||
def _http_serve(self):
|
def _http_serve(self):
|
||||||
if self._video_address is not None:
|
# do nothing
|
||||||
self._httpd = HTTPServer((self._video_address, self._video_port),
|
|
||||||
CustomVideoHandler)
|
|
||||||
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 _on_view_rendered(self, img):
|
|
||||||
CustomVideoHandler.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()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomVideoHandler(VideoHandler):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def render(img):
|
|
||||||
with CustomVideoHandler._lock:
|
|
||||||
try:
|
|
||||||
img.save("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), format='PNG')
|
|
||||||
except BaseException:
|
|
||||||
logging.exception("could not write preview")
|
|
||||||
|
|
||||||
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 %
|
|
||||||
('localhost', 1000), "utf8"))
|
|
||||||
except BaseException:
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
elif self.path.startswith('/ui'):
|
def _on_view_rendered(self, img):
|
||||||
with self._lock:
|
self.last_image = img
|
||||||
self.send_response(200)
|
|
||||||
self.send_header('Content-type', 'image/png')
|
def get_image(self):
|
||||||
self.end_headers()
|
"""
|
||||||
try:
|
Return the saved image
|
||||||
with open("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), 'rb') as fp:
|
"""
|
||||||
shutil.copyfileobj(fp, self.wfile)
|
return self.last_image
|
||||||
except BaseException:
|
|
||||||
logging.exception("could not open preview")
|
|
||||||
else:
|
|
||||||
self.send_response(404)
|
|
||||||
|
|
||||||
|
|
||||||
class DummyPeer:
|
class DummyPeer:
|
||||||
@ -79,21 +41,43 @@ class DummyPeer:
|
|||||||
def name():
|
def name():
|
||||||
return "beta"
|
return "beta"
|
||||||
|
|
||||||
|
def append_images(images, horizontal=True, xmargin=0, ymargin=0):
|
||||||
|
w, h = zip(*(i.size for i in images))
|
||||||
|
|
||||||
|
if horizontal:
|
||||||
|
t_w = sum(w)
|
||||||
|
t_h = max(h)
|
||||||
|
else:
|
||||||
|
t_w = max(w)
|
||||||
|
t_h = sum(h)
|
||||||
|
|
||||||
|
result = Image.new('RGB', (t_w, t_h))
|
||||||
|
|
||||||
|
x_offset = 0
|
||||||
|
y_offset = 0
|
||||||
|
|
||||||
|
for im in images:
|
||||||
|
result.paste(im, (x_offset,y_offset))
|
||||||
|
if horizontal:
|
||||||
|
x_offset += im.size[0] + xmargin
|
||||||
|
else:
|
||||||
|
y_offset += im.size[1] + ymargin
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="This program emulates\
|
parser = argparse.ArgumentParser(description="This program emulates\
|
||||||
the pwnagotchi display")
|
the pwnagotchi display")
|
||||||
parser.add_argument('--display', help="Which display to use.",
|
parser.add_argument('--displays', help="Which displays to use.", nargs="+",
|
||||||
default="waveshare_2")
|
default="waveshare_2")
|
||||||
parser.add_argument('--port', help="Which port to use",
|
|
||||||
default=8080)
|
|
||||||
parser.add_argument('--sleep', type=int, help="Time between emotions",
|
|
||||||
default=2)
|
|
||||||
parser.add_argument('--lang', help="Language to use",
|
parser.add_argument('--lang', help="Language to use",
|
||||||
default="en")
|
default="en")
|
||||||
|
parser.add_argument('--output', help="Path to output image (PNG)", default="preview.png")
|
||||||
|
parser.add_argument('--xmargin', type=int, default=5)
|
||||||
|
parser.add_argument('--ymargin', type=int, default=5)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
CONFIG = yaml.load('''
|
config_template = '''
|
||||||
main:
|
main:
|
||||||
lang: {lang}
|
lang: {lang}
|
||||||
ui:
|
ui:
|
||||||
@ -107,65 +91,81 @@ def main():
|
|||||||
video:
|
video:
|
||||||
enabled: true
|
enabled: true
|
||||||
address: "0.0.0.0"
|
address: "0.0.0.0"
|
||||||
port: {port}
|
port: 8080
|
||||||
'''.format(display=args.display,
|
'''
|
||||||
port=args.port,
|
|
||||||
|
list_of_displays = list()
|
||||||
|
for display_type in args.displays:
|
||||||
|
config = yaml.safe_load(config_template.format(display=display_type,
|
||||||
lang=args.lang))
|
lang=args.lang))
|
||||||
|
display = CustomDisplay(config=config, state={'name': f"{display_type}>"})
|
||||||
|
list_of_displays.append(display)
|
||||||
|
|
||||||
DISPLAY = CustomDisplay(config=CONFIG, state={'name': '%s>' % 'preview'})
|
|
||||||
|
|
||||||
while True:
|
columns = list()
|
||||||
DISPLAY.on_starting()
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_ai_ready()
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_normal()
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_new_peer(DummyPeer())
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_lost_peer(DummyPeer())
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_free_channel('6')
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.wait(args.sleep)
|
|
||||||
DISPLAY.update()
|
|
||||||
DISPLAY.on_bored()
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_sad()
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_motivated(1)
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_demotivated(-1)
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_excited()
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_deauth({'mac': 'DE:AD:BE:EF:CA:FE'})
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_miss('test')
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_lonely()
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_handshakes(1)
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
DISPLAY.on_rebooting()
|
|
||||||
DISPLAY.update()
|
|
||||||
time.sleep(args.sleep)
|
|
||||||
|
|
||||||
|
for display in list_of_displays:
|
||||||
|
emotions = list()
|
||||||
|
# Starting
|
||||||
|
display.on_starting()
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_ai_ready()
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_normal()
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_new_peer(DummyPeer())
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_lost_peer(DummyPeer())
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_free_channel('6')
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.wait(2)
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_bored()
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_sad()
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_motivated(1)
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_demotivated(-1)
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_excited()
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_deauth({'mac': 'DE:AD:BE:EF:CA:FE'})
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_miss('test')
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_lonely()
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_handshakes(1)
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
display.on_rebooting()
|
||||||
|
display.update()
|
||||||
|
emotions.append(display.get_image())
|
||||||
|
|
||||||
|
# append them all together (vertical)
|
||||||
|
columns.append(append_images(emotions, horizontal=False, xmargin=args.xmargin, ymargin=args.ymargin))
|
||||||
|
|
||||||
|
|
||||||
|
# append columns side by side
|
||||||
|
final_image = append_images(columns, horizontal=True, xmargin=args.xmargin, ymargin=args.ymargin)
|
||||||
|
final_image.save(args.output, 'PNG')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
SystemExit(main())
|
SystemExit(main())
|
||||||
|
Reference in New Issue
Block a user