import logging import os, time, sys import html import json import pwnagotchi.plugins as plugins from pwnagotchi.ui.components import * from pwnagotchi.ui.view import BLACK from PIL import ImageFont import pwnagotchi.ui.fonts as fonts import pwnagotchi.utils as utils try: sys.path.append(os.path.dirname(__file__)) from Touch_UI import Touch_Button as Button except Exception as e: pass #logging.warn(repr(e)) from textwrap import TextWrapper from flask import abort from flask import render_template_string class Tweak_View(plugins.Plugin): __author__ = 'Sniffleupagus' __version__ = '1.0.0' __license__ = 'GPL3' __description__ = 'Edit the UI layout. Ugly interface, no guardrails. Be careful!!!' # load from save file, parse JSON. dict maps from view_state_state key to new val # store originals when tweaks are applie # # have multiple base tweak files (for different display types, whatever) # and choose from them in config # - tweaks can be "system-wide" or associated with a specific base # - when updating the display, create a dict adding base tweaks, system-wide mods, base-specific mods # so specific overrides global # def __init__(self): self._agent = None self._start = time.time() self._logger = logging.getLogger(__name__) self._tweaks = {} self._untweak = {} self._already_updated = [] self.myFonts = {"Small": fonts.Small, "BoldSmall": fonts.BoldSmall, "Medium": fonts.Medium, "Bold": fonts.Bold, "BoldBig": fonts.BoldBig, "Huge": fonts.Huge } pass def show_tweaks(self, request): res = "" res += '
' return res def dump_item(self, name, item, prefix=""): self._logger.debug("%s[[[%s:%s]]]" % (prefix, name, type(item))) res = "" if type(item) is int: res += '%s: ' % (name, prefix, name, item) elif type(item) is str: if item.startswith("{"): #print("********************\n%s JSON %s" % (prefix, item)) try: j = json.loads(item) res += "%sJSON\n" % prefix, json.dumps(j, sort_keys=True,indent=4) except Exception as inst: res += "%s%s = '%s'\n" % (prefix, name, item) else: res += '%s: ' % (name, prefix, name, item) res += "%s%s = '%s'\n" % (prefix, name, item) elif type(item) is float: res += "%s%s = %s\n" % (prefix, name, item) res += '%s: ' % (name, prefix, name, item) elif type(item) is bool: res += "%s%s is %s\n" % (prefix, name, item) elif type(item) is list: #if (prefix is ""): if len(item) > 1: res += "\n" res += "%s[%s]\n" % (prefix, name) i = 0 for key in item: i=i+1 self._logger.debug("%s<%i> %s\n" % (prefix, i, key)) res += self.dump_item("{%i}" % (i), key, " %s %s" % (" " * len(prefix), name)) + "\n" if (prefix is ""): res += "%s[%s END]\n" % (prefix, name) elif type(item) is dict: #res += "Dict: [%s] [%s]%s
" % repr(path) #ret += "
%s
" % self.dump_item("Request", request) if self._agent: view = self._agent.view() ret += '
' ret += "" return render_template_string(ret) else: abort(404) elif request.method == "POST": if path == "update": ret = '
%s
" % repr(path) ret += "
%s
" % self.dump_item("Request", request.values) ret += "
" % self.show_tweaks(request) ret += "" elif path == "delete_mods": ret = '
%s
" % repr(path) ret += "
%s
" % self.dump_item("Request", request.values) ret += "
" % self.show_tweaks(request) ret += "" else: ret = '
%s
" % repr(path) #ret += "
%s
" % self.dump_item("request", request) ret += "
%s
" % self.update_from_request(request) ret += "
" % self.show_tweaks(request) ret += "" return render_template_string(ret) else: ret = '
%s
" % repr(path) ret += "" return render_template_string(ret) except Exception as err: self._logger.warning("webhook err: %s" % repr(err)) return "
%s
" % html.escape(repr(err))
# called when the plugin is loaded
def on_loaded(self):
self._start = time.time()
self._state = 0
self._next = 0
# called when everything is ready and the main loop is about to start
def on_ready(self, agent):
self._agent = agent
# called before the plugin is unloaded
def on_unload(self, ui):
try:
state = ui._state._state
# go through list of untweaks
for tag, value in self._untweak.items():
vss,element,key = tag.split(".")
self._logger.debug("Reverting: %s to %s" % (tag, repr(value)))
if key in dir(ui._state._state[element]):
if key == "xy":
ui._state._state[element].xy = value
self._logger.debug("Reverted %s xy to %s" % (element, repr(ui._state._state[element].xy)))
else:
try:
self._logger.debug("Trying to revert %s" % tag)
if hasattr(state, key):
setattr(ui._state._state[element], key, value)
self._logger.debug("Reverted %s xy to %s" % (element, repr(getattr(ui._state._state[element], key))))
except Exception as err:
self._logger.warning("revert %s: %s, %s" % (tag, repr(err), repr(ui)))
except Exception as err:
self._logger.warning("ui unload: %s, %s" % (repr(err), repr(ui)))
# called to setup the ui elements
# look at config. Move items around as desired
def on_ui_setup(self, ui):
self._ui = ui
self.myFonts = {"Small": fonts.Small,
"BoldSmall": fonts.BoldSmall,
"Medium": fonts.Medium,
"Bold": fonts.Bold,
"BoldBig": fonts.BoldBig,
"Huge": fonts.Huge
}
# include lots more sizes
just_once = True
for p in [6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 25, 28, 30, 35, 42, 48, 52, 54, 60, 69, 72, 80, 90, 100, 120]:
try:
self.myFonts["Deja %s" % p] = ImageFont.truetype('DejaVuSansMono', p)
self.myFonts["DejaB %s" % p] = ImageFont.truetype('DejaVuSansMono-Bold', p)
self.myFonts["DejaO %s" % p] = ImageFont.truetype('DejaVuSansMono-Oblique', p)
except Exception as e:
if just_once:
logging.warn("Missing some fonts: %s" % repr(e))
just_once = False
# load a config file... /etc/pwnagotchi/tweak_view.json for default
self._conf_file = self.options["filename"] if "filename" in self.options else "/etc/pwnagotchi/tweak_view.json"
try:
if os.path.isfile(self._conf_file):
with open(self._conf_file, 'r') as f:
self._tweaks = json.load(f)
for i in self._tweaks:
self._logger.debug ("Ready tweak %s -> %s" % (i, self._tweaks[i]))
self._already_updated = []
self._logger.info("Tweak view ready.")
except Exception as err:
self._logger.warn("TweakUI loading failed: %s" % repr(err))
try:
self.update_elements(ui)
except Exception as err:
self._logger.warning("ui setup: %s" % repr(err))
def on_ui_update(self, ui):
self.update_elements(ui)
def update_elements(self, ui):
# update those elements
try:
state = ui._state._state
# go through list of tweaks
updated = []
for tag, value in self._tweaks.items():
vss,element,key = tag.split(".")
try:
if element not in self._already_updated and element in state and key in dir(state[element]):
if tag not in self._untweak:
#self._untweak[tag] = eval("ui._state._state[element].%s" % key)
self._untweak[tag] = getattr(ui._state._state[element], key)
self._logger.debug("Saved for unload: %s = %s" % (tag, self._untweak[tag]))
if key == "xy":
new_xy = value.split(",")
new_xy = [int(float(x.strip())) for x in new_xy]
if new_xy[0] < 0: new_xy[0] = ui.width() + new_xy[0]
if new_xy[1] < 0: new_xy[1] = ui.height() + new_xy[1]
if ui._state._state[element].xy != new_xy:
ui._state._state[element].xy = new_xy
self._logger.debug("Updated xy to %s" % repr(ui._state._state[element].xy))
elif key == "font":
if value in self.myFonts:
ui._state._state[element].font = self.myFonts[value]
elif key == "text_font":
if value in self.myFonts:
ui._state._state[element].text_font = self.myFonts[value]
elif key == "alt_font":
if value in self.myFonts:
ui._state._state[element].alt_font = self.myFonts[value]
elif key == "label_font":
if value in self.myFonts:
ui._state._state[element].label_font = self.myFonts[value]
elif key == "color":
logging.debug("Color: %s = %s" % (element, value))
ui._state._state[element].color = value
elif key == "label":
ui._state._state[element].label = value
elif key == "label_spacing":
ui._state._state[element].label_spacing = int(value)
elif key == "max_length":
uie = ui._state._state[element]
uie.max_length = int(value)
uie.wrapper = TextWrapper(width=int(value), replace_whitespace=False) if uie.wrap else None
if element not in updated:
updated.append(element)
elif element in self._already_updated and not element in state:
# like a plugin unloaded
self._already_updated.remove(element)
except Exception as err:
self._logger.warn("tweak failed for key %s: %s" % (tag, repr(err)))
for element in updated:
if element not in self._already_updated:
self._already_updated.append(element)
except Exception as err:
self._logger.warning("ui update: %s, %s" % (repr(err), repr(ui)))