mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
*.pyc
|
@ -4,7 +4,8 @@ import os
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
import _thread
|
#import _thread
|
||||||
|
import threading
|
||||||
|
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
import pwnagotchi.utils as utils
|
import pwnagotchi.utils as utils
|
||||||
@ -304,7 +305,8 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
def start_session_fetcher(self):
|
def start_session_fetcher(self):
|
||||||
_thread.start_new_thread(self._fetch_stats, ())
|
#_thread.start_new_thread(self._fetch_stats, ())
|
||||||
|
threading.Thread(target=self._fetch_stats, args=(), name="Session Fetcher", daemon=True).start()
|
||||||
|
|
||||||
def _fetch_stats(self):
|
def _fetch_stats(self):
|
||||||
while True:
|
while True:
|
||||||
@ -387,7 +389,8 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
|
|
||||||
def start_event_polling(self):
|
def start_event_polling(self):
|
||||||
# start a thread and pass in the mainloop
|
# start a thread and pass in the mainloop
|
||||||
_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
#_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
||||||
|
threading.Thread(target=self._event_poller, args=(asyncio.get_event_loop(),), name="Event Polling", daemon=True)
|
||||||
|
|
||||||
def is_module_running(self, module):
|
def is_module_running(self, module):
|
||||||
s = self.session()
|
s = self.session()
|
||||||
|
@ -111,7 +111,8 @@ class AsyncTrainer(object):
|
|||||||
return self._training_epochs
|
return self._training_epochs
|
||||||
|
|
||||||
def start_ai(self):
|
def start_ai(self):
|
||||||
_thread.start_new_thread(self._ai_worker, ())
|
#_thread.start_new_thread(self._ai_worker, ())
|
||||||
|
threading.Thread(target=self._ai_worker, args=(), name="AI Worker" daemon=True).start()
|
||||||
|
|
||||||
def _save_ai(self):
|
def _save_ai(self):
|
||||||
logging.info("[AI] saving model to %s ..." % self._nn_path)
|
logging.info("[AI] saving model to %s ..." % self._nn_path)
|
||||||
|
@ -112,6 +112,7 @@ main.mon_max_blind_epochs = 50
|
|||||||
main.no_restart = false
|
main.no_restart = false
|
||||||
|
|
||||||
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
|
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
|
||||||
|
main.log.path-debug = "/etc/pwnagotchi/log/pwnagotchi-debug.log"
|
||||||
main.log.rotation.enabled = true
|
main.log.rotation.enabled = true
|
||||||
main.log.rotation.size = "10M"
|
main.log.rotation.size = "10M"
|
||||||
|
|
||||||
|
@ -3,7 +3,8 @@ import re
|
|||||||
import tempfile
|
import tempfile
|
||||||
import contextlib
|
import contextlib
|
||||||
import shutil
|
import shutil
|
||||||
import _thread
|
#import _thread
|
||||||
|
import threading
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
@ -85,7 +86,8 @@ def setup_mounts(config):
|
|||||||
if interval:
|
if interval:
|
||||||
logging.debug("[FS] Starting thread to sync %s (interval: %d)",
|
logging.debug("[FS] Starting thread to sync %s (interval: %d)",
|
||||||
options['mount'], interval)
|
options['mount'], interval)
|
||||||
_thread.start_new_thread(m.daemonize, (interval,))
|
threading.Thread(target=m.daemonize, args=(interval,),name="File Sys", daemon=True).start()
|
||||||
|
#_thread.start_new_thread(m.daemonize, (interval,))
|
||||||
else:
|
else:
|
||||||
logging.debug("[FS] Not syncing %s, because interval is 0",
|
logging.debug("[FS] Not syncing %s, because interval is 0",
|
||||||
options['mount'])
|
options['mount'])
|
||||||
|
@ -217,25 +217,45 @@ class LastSession(object):
|
|||||||
def setup_logging(args, config):
|
def setup_logging(args, config):
|
||||||
cfg = config['main']['log']
|
cfg = config['main']['log']
|
||||||
filename = cfg['path']
|
filename = cfg['path']
|
||||||
|
filenameDebug = cfg['path-debug']
|
||||||
|
|
||||||
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s")
|
#global formatter
|
||||||
root = logging.getLogger()
|
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] [%(threadName)s] : %(message)s")
|
||||||
|
logger = logging.getLogger()
|
||||||
root.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
|
||||||
|
for handler in logger.handlers:
|
||||||
|
handler.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
|
||||||
|
logger.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
||||||
|
|
||||||
if filename:
|
if filename:
|
||||||
# since python default log rotation might break session data in different files,
|
# since python default log rotation might break session data in different files,
|
||||||
# we need to do log rotation ourselves
|
# we need to do log rotation ourselves
|
||||||
log_rotation(filename, cfg)
|
log_rotation(filename, cfg)
|
||||||
|
log_rotation(filenameDebug, cfg)
|
||||||
|
|
||||||
file_handler = logging.FileHandler(filename)
|
|
||||||
file_handler.setFormatter(formatter)
|
|
||||||
root.addHandler(file_handler)
|
# File handler for logging all normal messages
|
||||||
|
file_handler = logging.FileHandler(filename) #creates new
|
||||||
|
file_handler.setLevel(logging.INFO)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
console_handler = logging.StreamHandler()
|
# File handler for logging all debug messages
|
||||||
console_handler.setFormatter(formatter)
|
file_handler = logging.FileHandler(filenameDebug) #creates new
|
||||||
root.addHandler(console_handler)
|
file_handler.setLevel(logging.DEBUG)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
# Console handler for logging debug messages if args.debug is true else just log normal
|
||||||
|
#console_handler = logging.StreamHandler() #creates new
|
||||||
|
#console_handler.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
||||||
|
#console_handler.setFormatter(formatter)
|
||||||
|
#logger.addHandler(console_handler)
|
||||||
|
|
||||||
if not args.debug:
|
if not args.debug:
|
||||||
# disable scapy and tensorflow logging
|
# disable scapy and tensorflow logging
|
||||||
logging.getLogger("scapy").disabled = True
|
logging.getLogger("scapy").disabled = True
|
||||||
@ -250,6 +270,8 @@ def setup_logging(args, config):
|
|||||||
requests_log.prpagate = False
|
requests_log.prpagate = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def log_rotation(filename, cfg):
|
def log_rotation(filename, cfg):
|
||||||
rotation = cfg['rotation']
|
rotation = cfg['rotation']
|
||||||
if not rotation['enabled']:
|
if not rotation['enabled']:
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import _thread
|
#import _thread
|
||||||
|
import threading
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -41,7 +42,8 @@ class AsyncAdvertiser(object):
|
|||||||
|
|
||||||
def start_advertising(self):
|
def start_advertising(self):
|
||||||
if self._config['personality']['advertise']:
|
if self._config['personality']['advertise']:
|
||||||
_thread.start_new_thread(self._adv_poller, ())
|
#_thread.start_new_thread(self._adv_poller, ())
|
||||||
|
threading.Thread(target=self._adv_poller,args=(), name="Grid", daemon=True).start()
|
||||||
|
|
||||||
grid.set_advertisement_data(self._advertisement)
|
grid.set_advertisement_data(self._advertisement)
|
||||||
grid.advertise(True)
|
grid.advertise(True)
|
||||||
|
@ -6,6 +6,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import pwnagotchi.grid
|
import pwnagotchi.grid
|
||||||
|
import prctl
|
||||||
|
|
||||||
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
||||||
loaded = {}
|
loaded = {}
|
||||||
@ -72,6 +73,7 @@ def toggle_plugin(name, enable=True):
|
|||||||
|
|
||||||
def on(event_name, *args, **kwargs):
|
def on(event_name, *args, **kwargs):
|
||||||
for plugin_name in loaded.keys():
|
for plugin_name in loaded.keys():
|
||||||
|
|
||||||
one(plugin_name, event_name, *args, **kwargs)
|
one(plugin_name, event_name, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -82,7 +84,10 @@ def locked_cb(lock_name, cb, *args, **kwargs):
|
|||||||
locks[lock_name] = threading.Lock()
|
locks[lock_name] = threading.Lock()
|
||||||
|
|
||||||
with locks[lock_name]:
|
with locks[lock_name]:
|
||||||
cb(*args, *kwargs)
|
# Setting the thread name using prctl
|
||||||
|
plugin_name, plugin_cb = lock_name.split("::")
|
||||||
|
prctl.set_name(f"{plugin_name}.{plugin_cb}")
|
||||||
|
cb(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def one(plugin_name, event_name, *args, **kwargs):
|
def one(plugin_name, event_name, *args, **kwargs):
|
||||||
@ -95,8 +100,10 @@ def one(plugin_name, event_name, *args, **kwargs):
|
|||||||
if callback is not None and callable(callback):
|
if callback is not None and callable(callback):
|
||||||
try:
|
try:
|
||||||
lock_name = "%s::%s" % (plugin_name, cb_name)
|
lock_name = "%s::%s" % (plugin_name, cb_name)
|
||||||
locked_cb_args = (lock_name, callback, *args, *kwargs)
|
thread_name = f'{plugin_name}.{cb_name}'
|
||||||
_thread.start_new_thread(locked_cb, locked_cb_args)
|
thread = threading.Thread(target=locked_cb, args=(lock_name, callback, *args, *kwargs), name=thread_name, daemon=True)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e))
|
logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e))
|
||||||
logging.error(e, exc_info=True)
|
logging.error(e, exc_info=True)
|
||||||
@ -144,4 +151,4 @@ def load(config):
|
|||||||
plugin.options = config['main']['plugins'][name]
|
plugin.options = config['main']['plugins'][name]
|
||||||
|
|
||||||
on('loaded')
|
on('loaded')
|
||||||
on('config_changed', config)
|
on('config_changed', config)
|
33
pwnagotchi/ui/colors.py
Normal file
33
pwnagotchi/ui/colors.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
LOOK_R = '( ⚆_⚆)'
|
||||||
|
LOOK_L = '(☉_☉ )'
|
||||||
|
LOOK_R_HAPPY = '( ◕‿◕)'
|
||||||
|
LOOK_L_HAPPY = '(◕‿◕ )'
|
||||||
|
SLEEP = '(⇀‿‿↼)'
|
||||||
|
SLEEP2 = '(≖‿‿≖)'
|
||||||
|
AWAKE = '(◕‿‿◕)'
|
||||||
|
BORED = '(-__-)'
|
||||||
|
INTENSE = '(°▃▃°)'
|
||||||
|
COOL = '(⌐■_■)'
|
||||||
|
HAPPY = '(•‿‿•)'
|
||||||
|
GRATEFUL = '(^‿‿^)'
|
||||||
|
EXCITED = '(ᵔ◡◡ᵔ)'
|
||||||
|
MOTIVATED = '(☼‿‿☼)'
|
||||||
|
DEMOTIVATED = '(≖__≖)'
|
||||||
|
SMART = '(✜‿‿✜)'
|
||||||
|
LONELY = '(ب__ب)'
|
||||||
|
SAD = '(╥☁╥ )'
|
||||||
|
ANGRY = "(-_-')"
|
||||||
|
FRIEND = '(♥‿‿♥)'
|
||||||
|
BROKEN = '(☓‿‿☓)'
|
||||||
|
DEBUG = '(#__#)'
|
||||||
|
UPLOAD = '(1__0)'
|
||||||
|
UPLOAD1 = '(1__1)'
|
||||||
|
UPLOAD2 = '(0__1)'
|
||||||
|
PNG = False
|
||||||
|
POSITION_X = 0
|
||||||
|
POSITION_Y = 40
|
||||||
|
|
||||||
|
|
||||||
|
def load_from_config(config):
|
||||||
|
for face_name, face_value in config.items():
|
||||||
|
globals()[face_name.upper()] = face_value
|
@ -21,7 +21,8 @@ class Display(View):
|
|||||||
self._canvas_next = None
|
self._canvas_next = None
|
||||||
self._render_thread_instance = threading.Thread(
|
self._render_thread_instance = threading.Thread(
|
||||||
target=self._render_thread,
|
target=self._render_thread,
|
||||||
daemon=True
|
daemon=True,
|
||||||
|
name="Renderer"
|
||||||
)
|
)
|
||||||
self._render_thread_instance.start()
|
self._render_thread_instance.start()
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ from pwnagotchi.ui.hw.base import DisplayImpl
|
|||||||
class DisplayHatMini(DisplayImpl):
|
class DisplayHatMini(DisplayImpl):
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
super(DisplayHatMini, self).__init__(config, 'displayhatmini')
|
super(DisplayHatMini, self).__init__(config, 'displayhatmini')
|
||||||
|
self.mode = "RGB" # its actually BGR;16 5,6,5 bit, but display lib converts it
|
||||||
|
|
||||||
def layout(self):
|
def layout(self):
|
||||||
fonts.setup(12, 10, 12, 70, 25, 9)
|
fonts.setup(12, 10, 12, 70, 25, 9)
|
||||||
|
@ -7,7 +7,8 @@ from pwnagotchi.ui.hw.base import DisplayImpl
|
|||||||
class Wavesharelcd1in8(DisplayImpl):
|
class Wavesharelcd1in8(DisplayImpl):
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
super(Wavesharelcd1in8, self).__init__(config, 'wavesharelcd1in8')
|
super(Wavesharelcd1in8, self).__init__(config, 'wavesharelcd1in8')
|
||||||
|
self.mode = "RGB"
|
||||||
|
|
||||||
def layout(self):
|
def layout(self):
|
||||||
fonts.setup(10, 8, 10, 18, 25, 9)
|
fonts.setup(10, 8, 10, 18, 25, 9)
|
||||||
self._layout['width'] = 160
|
self._layout['width'] = 160
|
||||||
|
@ -3,7 +3,7 @@ from threading import Lock
|
|||||||
|
|
||||||
class State(object):
|
class State(object):
|
||||||
def __init__(self, state={}):
|
def __init__(self, state={}):
|
||||||
self._state = state
|
self._state = state # all ui elements
|
||||||
self._lock = Lock()
|
self._lock = Lock()
|
||||||
self._listeners = {}
|
self._listeners = {}
|
||||||
self._changes = {}
|
self._changes = {}
|
||||||
|
@ -5,6 +5,7 @@ import time
|
|||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
from PIL import ImageDraw
|
from PIL import ImageDraw
|
||||||
|
from PIL import ImageColor as colors
|
||||||
|
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
import pwnagotchi.plugins as plugins
|
import pwnagotchi.plugins as plugins
|
||||||
@ -12,29 +13,106 @@ import pwnagotchi.ui.faces as faces
|
|||||||
import pwnagotchi.ui.fonts as fonts
|
import pwnagotchi.ui.fonts as fonts
|
||||||
import pwnagotchi.ui.web as web
|
import pwnagotchi.ui.web as web
|
||||||
import pwnagotchi.utils as utils
|
import pwnagotchi.utils as utils
|
||||||
|
|
||||||
from pwnagotchi.ui.components import *
|
from pwnagotchi.ui.components import *
|
||||||
from pwnagotchi.ui.state import State
|
from pwnagotchi.ui.state import State
|
||||||
from pwnagotchi.voice import Voice
|
from pwnagotchi.voice import Voice
|
||||||
|
|
||||||
WHITE = 0x00
|
WHITE = 0x00 # white is actually black on jays image
|
||||||
BLACK = 0xFF
|
BLACK = 0xFF # black is actually white on jays image
|
||||||
|
|
||||||
|
BACKGROUND_1 = 0
|
||||||
|
FOREGROUND_1 = 1
|
||||||
|
|
||||||
|
BACKGROUND_L = 0
|
||||||
|
FOREGROUND_L = 255
|
||||||
|
|
||||||
|
BACKGROUND_BGR_16 = (0,0,0)
|
||||||
|
FOREGROUND_BGR_16 = (31,63,31)
|
||||||
|
|
||||||
|
BACKGROUND_RGB = (0,0,0)
|
||||||
|
FOREGROUND_RGB = (255,255,255)
|
||||||
|
|
||||||
|
|
||||||
ROOT = None
|
ROOT = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#1 (1-bit pixels, black and white, stored with one pixel per byte)
|
||||||
|
|
||||||
|
#L (8-bit pixels, grayscale)
|
||||||
|
|
||||||
|
#P (8-bit pixels, mapped to any other mode using a color palette)
|
||||||
|
|
||||||
|
#BGR;16 (5,6,5 bits, for 65k color)
|
||||||
|
|
||||||
|
#RGB (3x8-bit pixels, true color)
|
||||||
|
|
||||||
|
#RGBA (4x8-bit pixels, true color with transparency mask)
|
||||||
|
|
||||||
|
#CMYK (4x8-bit pixels, color separation)
|
||||||
|
|
||||||
|
#YCbCr (3x8-bit pixels, color video format)
|
||||||
|
|
||||||
|
#self.FOREGROUND is the main color
|
||||||
|
#self.BACKGROUNDGROUND is the 2ndary color, used for background
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class View(object):
|
class View(object):
|
||||||
def __init__(self, config, impl, state=None):
|
def __init__(self, config, impl, state=None):
|
||||||
global ROOT, BLACK, WHITE
|
global ROOT, BLACK, WHITE
|
||||||
|
|
||||||
|
#values/code for display color mode
|
||||||
|
|
||||||
|
self.mode = '1' # 1 = (1-bit pixels, black and white, stored with one pixel per byte)
|
||||||
|
if hasattr(impl, 'mode'):
|
||||||
|
self.mode = impl.mode
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
match self.mode:
|
||||||
|
case '1':
|
||||||
|
self.BACKGROUND = BACKGROUND_1
|
||||||
|
self.FOREGROUND = FOREGROUND_1
|
||||||
|
# do stuff is color mode is 1 when View object is created.
|
||||||
|
case 'L':
|
||||||
|
self.BACKGROUND = BACKGROUND_L # black 0 to 255
|
||||||
|
self.FOREGROUND = FOREGROUND_L
|
||||||
|
# do stuff is color mode is L when View object is created.
|
||||||
|
case 'P':
|
||||||
|
pass
|
||||||
|
# do stuff is color mode is P when View object is created.
|
||||||
|
case 'BGR;16':
|
||||||
|
self.BACKGROUND = BACKGROUND_BGR_16 #black tuple
|
||||||
|
self.FOREGROUND = FOREGROUND_BGR_16 #white tuple
|
||||||
|
case 'RGB':
|
||||||
|
self.BACKGROUND = BACKGROUND_RGB #black tuple
|
||||||
|
self.FOREGROUND = FOREGROUND_RGB #white tuple
|
||||||
|
# do stuff is color mode is RGB when View object is created.
|
||||||
|
case 'RGBA':
|
||||||
|
# do stuff is color mode is RGBA when View object is created.
|
||||||
|
pass
|
||||||
|
case 'CMYK':
|
||||||
|
# do stuff is color mode is CMYK when View object is created.
|
||||||
|
pass
|
||||||
|
case 'YCbCr':
|
||||||
|
# do stuff is color mode is YCbCr when View object is created.
|
||||||
|
pass
|
||||||
|
case _:
|
||||||
|
# do stuff when color mode doesnt exist for display
|
||||||
|
self.BACKGROUND = BACKGROUND_1
|
||||||
|
self.FOREGROUND = FOREGROUND_1
|
||||||
|
|
||||||
|
|
||||||
self.invert = 0
|
self.invert = 0
|
||||||
self._black = 0xFF
|
if 'invert' in config['ui'] and config['ui']['invert'] == True:
|
||||||
self._white = 0x00
|
logging.debug("INVERT:" + str(config['ui']['invert']))
|
||||||
if 'invert' in config['ui'] and config['ui']['invert']:
|
|
||||||
logging.debug("INVERT BLACK/WHITES:" + str(config['ui']['invert']))
|
|
||||||
self.invert = 1
|
self.invert = 1
|
||||||
BLACK = 0x00
|
tmp = self.FOREGROUND
|
||||||
WHITE = 0xFF
|
self.FOREGROUND = self.FOREGROUND
|
||||||
self._black = 0x00
|
self.FOREGROUND = tmp
|
||||||
self._white = 0xFF
|
|
||||||
|
|
||||||
# setup faces from the configuration in case the user customized them
|
# setup faces from the configuration in case the user customized them
|
||||||
faces.load_from_config(config['ui']['faces'])
|
faces.load_from_config(config['ui']['faces'])
|
||||||
@ -51,40 +129,40 @@ class View(object):
|
|||||||
self._width = self._layout['width']
|
self._width = self._layout['width']
|
||||||
self._height = self._layout['height']
|
self._height = self._layout['height']
|
||||||
self._state = State(state={
|
self._state = State(state={
|
||||||
'channel': LabeledValue(color=BLACK, label='CH', value='00', position=self._layout['channel'],
|
'channel': LabeledValue(color=self.FOREGROUND, label='CH', value='00', position=self._layout['channel'],
|
||||||
label_font=fonts.Bold,
|
label_font=fonts.Bold,
|
||||||
text_font=fonts.Medium),
|
text_font=fonts.Medium),
|
||||||
'aps': LabeledValue(color=BLACK, label='APS', value='0 (00)', position=self._layout['aps'],
|
'aps': LabeledValue(color=self.FOREGROUND, label='APS', value='0 (00)', position=self._layout['aps'],
|
||||||
label_font=fonts.Bold,
|
label_font=fonts.Bold,
|
||||||
text_font=fonts.Medium),
|
text_font=fonts.Medium),
|
||||||
|
|
||||||
'uptime': LabeledValue(color=BLACK, label='UP', value='00:00:00', position=self._layout['uptime'],
|
'uptime': LabeledValue(color=self.FOREGROUND, label='UP', value='00:00:00', position=self._layout['uptime'],
|
||||||
label_font=fonts.Bold,
|
label_font=fonts.Bold,
|
||||||
text_font=fonts.Medium),
|
text_font=fonts.Medium),
|
||||||
|
|
||||||
'line1': Line(self._layout['line1'], color=BLACK),
|
'line1': Line(self._layout['line1'], color=self.FOREGROUND),
|
||||||
'line2': Line(self._layout['line2'], color=BLACK),
|
'line2': Line(self._layout['line2'], color=self.FOREGROUND),
|
||||||
|
|
||||||
'face': Text(value=faces.SLEEP, position=(config['ui']['faces']['position_x'], config['ui']['faces']['position_y']), color=BLACK, font=fonts.Huge, png=config['ui']['faces']['png']),
|
'face': Text(value=faces.SLEEP, position=(config['ui']['faces']['position_x'], config['ui']['faces']['position_y']), color=self.FOREGROUND, font=fonts.Huge, png=config['ui']['faces']['png']),
|
||||||
|
|
||||||
# 'friend_face': Text(value=None, position=self._layout['friend_face'], font=fonts.Bold, color=BLACK),
|
# 'friend_face': Text(value=None, position=self._layout['friend_face'], font=fonts.Bold, color=self.FOREGROUND),
|
||||||
'friend_name': Text(value=None, position=self._layout['friend_face'], font=fonts.BoldSmall, color=BLACK),
|
'friend_name': Text(value=None, position=self._layout['friend_face'], font=fonts.BoldSmall, color=self.FOREGROUND),
|
||||||
|
|
||||||
'name': Text(value='%s>' % 'pwnagotchi', position=self._layout['name'], color=BLACK, font=fonts.Bold),
|
'name': Text(value='%s>' % 'pwnagotchi', position=self._layout['name'], color=self.FOREGROUND, font=fonts.Bold),
|
||||||
|
|
||||||
'status': Text(value=self._voice.default(),
|
'status': Text(value=self._voice.default(),
|
||||||
position=self._layout['status']['pos'],
|
position=self._layout['status']['pos'],
|
||||||
color=BLACK,
|
color=self.FOREGROUND,
|
||||||
font=self._layout['status']['font'],
|
font=self._layout['status']['font'],
|
||||||
wrap=True,
|
wrap=True,
|
||||||
# the current maximum number of characters per line, assuming each character is 6 pixels wide
|
# the current maximum number of characters per line, assuming each character is 6 pixels wide
|
||||||
max_length=self._layout['status']['max']),
|
max_length=self._layout['status']['max']),
|
||||||
|
|
||||||
'shakes': LabeledValue(label='PWND ', value='0 (00)', color=BLACK,
|
'shakes': LabeledValue(label='PWND ', value='0 (00)', color=self.FOREGROUND,
|
||||||
position=self._layout['shakes'], label_font=fonts.Bold,
|
position=self._layout['shakes'], label_font=fonts.Bold,
|
||||||
text_font=fonts.Medium),
|
text_font=fonts.Medium),
|
||||||
'mode': Text(value='AUTO', position=self._layout['mode'],
|
'mode': Text(value='AUTO', position=self._layout['mode'],
|
||||||
font=fonts.Bold, color=BLACK),
|
font=fonts.Bold, color=self.FOREGROUND),
|
||||||
})
|
})
|
||||||
|
|
||||||
if state:
|
if state:
|
||||||
@ -387,12 +465,13 @@ class View(object):
|
|||||||
state = self._state
|
state = self._state
|
||||||
changes = state.changes(ignore=self._ignore_changes)
|
changes = state.changes(ignore=self._ignore_changes)
|
||||||
if force or len(changes):
|
if force or len(changes):
|
||||||
self._canvas = Image.new('1', (self._width, self._height), self._white)
|
self._canvas = Image.new(self.mode, (self._width, self._height), self.BACKGROUND)
|
||||||
drawer = ImageDraw.Draw(self._canvas)
|
drawer = ImageDraw.Draw(self._canvas, self.mode)
|
||||||
|
|
||||||
plugins.on('ui_update', self)
|
plugins.on('ui_update', self)
|
||||||
|
|
||||||
for key, lv in state.items():
|
for key, lv in state.items():
|
||||||
|
#lv is a ui element
|
||||||
lv.draw(self._canvas, drawer)
|
lv.draw(self._canvas, drawer)
|
||||||
|
|
||||||
web.update_frame(self._canvas)
|
web.update_frame(self._canvas)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import _thread
|
#import _thread
|
||||||
|
import threading
|
||||||
import secrets
|
import secrets
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -25,7 +26,9 @@ class Server:
|
|||||||
self._origin = self._config['origin']
|
self._origin = self._config['origin']
|
||||||
|
|
||||||
if self._enabled:
|
if self._enabled:
|
||||||
_thread.start_new_thread(self._http_serve, ())
|
#_thread.start_new_thread(self._http_serve, ())
|
||||||
|
logging.info("Starting WebServer thread")
|
||||||
|
self._thread = threading.Thread(target=self._http_serve, name="WebServer", daemon = True).start()
|
||||||
|
|
||||||
def _http_serve(self):
|
def _http_serve(self):
|
||||||
if self._address is not None:
|
if self._address is not None:
|
||||||
|
Reference in New Issue
Block a user