new: integrated python standard logger (closes #84)

This commit is contained in:
Simone Margaritelli 2019-10-03 17:06:40 +02:00
parent 335e6b1311
commit 4cf2866737
19 changed files with 171 additions and 173 deletions

View File

@ -202,14 +202,14 @@ running = False
def on_loaded(): def on_loaded():
core.log("GPS plugin loaded for %s" % device) logging.info("GPS plugin loaded for %s" % device)
def on_ready(agent): def on_ready(agent):
global running global running
if os.path.exists(device): if os.path.exists(device):
core.log("enabling GPS bettercap's module for %s" % device) logging.info("enabling GPS bettercap's module for %s" % device)
try: try:
agent.run('gps off') agent.run('gps off')
except: except:
@ -220,7 +220,7 @@ def on_ready(agent):
agent.run('gps on') agent.run('gps on')
running = True running = True
else: else:
core.log("no GPS detected") logging.info("no GPS detected")
def on_handshake(agent, filename, access_point, client_station): def on_handshake(agent, filename, access_point, client_station):
@ -229,7 +229,7 @@ def on_handshake(agent, filename, access_point, client_station):
gps = info['gps'] gps = info['gps']
gps_filename = filename.replace('.pcap', '.gps.json') gps_filename = filename.replace('.pcap', '.gps.json')
core.log("saving GPS to %s (%s)" % (gps_filename, gps)) logging.info("saving GPS to %s (%s)" % (gps_filename, gps))
with open(gps_filename, 'w+t') as fp: with open(gps_filename, 'w+t') as fp:
json.dump(gps, fp) json.dump(gps, fp)
``` ```

View File

@ -6,6 +6,7 @@ import time
import argparse import argparse
from http.server import HTTPServer from http.server import HTTPServer
import shutil import shutil
import logging
import yaml import yaml
sys.path.insert(0, sys.path.insert(0,
@ -13,7 +14,6 @@ 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
import core
class CustomDisplay(Display): class CustomDisplay(Display):
@ -22,11 +22,11 @@ class CustomDisplay(Display):
if self._video_address is not None: if self._video_address is not None:
self._httpd = HTTPServer((self._video_address, self._video_port), self._httpd = HTTPServer((self._video_address, self._video_port),
CustomVideoHandler) CustomVideoHandler)
core.log("ui available at http://%s:%d/" % (self._video_address, logging.info("ui available at http://%s:%d/" % (self._video_address,
self._video_port)) self._video_port))
self._httpd.serve_forever() self._httpd.serve_forever()
else: else:
core.log("could not get ip of usb0, video server not starting") logging.info("could not get ip of usb0, video server not starting")
def _on_view_rendered(self, img): def _on_view_rendered(self, img):
CustomVideoHandler.render(img) CustomVideoHandler.render(img)
@ -45,7 +45,7 @@ class CustomVideoHandler(VideoHandler):
try: try:
img.save("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), format='PNG') img.save("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), format='PNG')
except BaseException: except BaseException:
core.log("could not write preview") logging.exception("could not write preview")
def do_GET(self): def do_GET(self):
if self.path == '/': if self.path == '/':
@ -69,7 +69,7 @@ class CustomVideoHandler(VideoHandler):
with open("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), 'rb') as fp: with open("/tmp/pwnagotchi-{rand}.png".format(rand=id(CustomVideoHandler)), 'rb') as fp:
shutil.copyfileobj(fp, self.wfile) shutil.copyfileobj(fp, self.wfile)
except BaseException: except BaseException:
core.log("could not open preview") logging.exception("could not open preview")
else: else:
self.send_response(404) self.send_response(404)

View File

@ -1,8 +1,7 @@
import logging
import requests import requests
from requests.auth import HTTPBasicAuth from requests.auth import HTTPBasicAuth
import core
class Client(object): class Client(object):
def __init__(self, hostname='localhost', scheme='http', port=8081, username='user', password='pass'): def __init__(self, hostname='localhost', scheme='http', port=8081, username='user', password='pass'):
@ -19,11 +18,11 @@ class Client(object):
return r.json() return r.json()
except Exception as e: except Exception as e:
if r.status_code == 200: if r.status_code == 200:
core.log("error while decoding json: error='%s' resp='%s'" % (e, r.text)) logging.error("error while decoding json: error='%s' resp='%s'" % (e, r.text))
else: else:
err = "error %d: %s" % (r.status_code, r.text.strip()) err = "error %d: %s" % (r.status_code, r.text.strip())
if verbose_errors: if verbose_errors:
core.log(err) logging.info(err)
raise Exception(err) raise Exception(err)
return r.text return r.text

View File

@ -1,24 +1,7 @@
import sys
import glob import glob
import os import os
import time import time
import subprocess import subprocess
from threading import Lock
from datetime import datetime
logfile = None
loglock = Lock()
def log(msg):
tstamp = str(datetime.now())
line = "[%s] %s" % (tstamp, msg.rstrip())
print(line)
sys.stdout.flush()
if logfile is not None:
with loglock:
with open(logfile, 'a+t') as fp:
fp.write("%s\n" % line)
def secs_to_hhmmss(secs): def secs_to_hhmmss(secs):

View File

@ -2,11 +2,10 @@
import os import os
import argparse import argparse
import time import time
import traceback import logging
import yaml import yaml
import core
import pwnagotchi import pwnagotchi
import pwnagotchi.utils as utils import pwnagotchi.utils as utils
import pwnagotchi.version as version import pwnagotchi.version as version
@ -28,8 +27,12 @@ parser.add_argument('--manual', dest="do_manual", action="store_true", default=F
parser.add_argument('--clear', dest="do_clear", action="store_true", default=False, parser.add_argument('--clear', dest="do_clear", action="store_true", default=False,
help="Clear the ePaper display and exit.") help="Clear the ePaper display and exit.")
parser.add_argument('--debug', dest="debug", action="store_true", default=False,
help="Enable debug logs.")
args = parser.parse_args() args = parser.parse_args()
config = utils.load_config(args) config = utils.load_config(args)
utils.setup_logging(args, config)
if args.do_clear: if args.do_clear:
print("clearing the display ...") print("clearing the display ...")
@ -75,19 +78,18 @@ plugins.on('loaded')
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()}) display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
agent = Agent(view=display, config=config) agent = Agent(view=display, config=config)
core.log("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, version.version)) logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, version.version))
# for key, value in config['personality'].items(): # for key, value in config['personality'].items():
# core.log(" %s: %s" % (key, value)) # logging.info(" %s: %s" % (key, value))
for _, plugin in plugins.loaded.items(): for _, plugin in plugins.loaded.items():
core.log("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__)) logging.info("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__))
if args.do_manual: if args.do_manual:
core.log("entering manual mode ...") logging.info("entering manual mode ...")
log = SessionParser(config['main']['log']) log = SessionParser(config['main']['log'])
logging.info("the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
core.log("the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
log.duration_human, log.duration_human,
log.epochs, log.epochs,
log.train_epochs, log.train_epochs,
@ -98,17 +100,18 @@ if args.do_manual:
while True: while True:
display.on_manual_mode(log) display.on_manual_mode(log)
time.sleep(1) time.sleep(1)
if config['twitter']['enabled'] and log.is_new() and Agent.is_connected() and log.handshakes > 0: if config['twitter']['enabled'] and log.is_new() and Agent.is_connected() and log.handshakes > 0:
import tweepy import tweepy
core.log("detected a new session and internet connectivity!") logging.info("detected a new session and internet connectivity!")
picture = '/dev/shm/pwnagotchi.png' picture = '/dev/shm/pwnagotchi.png'
display.update() display.update(force=True)
display.image().save(picture, 'png') display.image().save(picture, 'png')
display.set('status', 'Tweeting...') display.set('status', 'Tweeting...')
display.update() display.update(force=True)
try: try:
auth = tweepy.OAuthHandler(config['twitter']['consumer_key'], config['twitter']['consumer_secret']) auth = tweepy.OAuthHandler(config['twitter']['consumer_key'], config['twitter']['consumer_secret'])
@ -119,14 +122,12 @@ if args.do_manual:
api.update_with_media(filename=picture, status=tweet) api.update_with_media(filename=picture, status=tweet)
log.save_session_id() log.save_session_id()
core.log("tweeted: %s" % tweet) logging.info("tweeted: %s" % tweet)
except Exception as e: except Exception as e:
core.log("error: %s" % e) logging.exception("error while tweeting")
quit() quit()
core.logfile = config['main']['log']
agent.start_ai() agent.start_ai()
agent.setup_events() agent.setup_events()
agent.set_starting() agent.set_starting()
@ -151,7 +152,7 @@ while True:
agent.set_channel(ch) agent.set_channel(ch)
if not agent.is_stale() and agent.any_activity(): if not agent.is_stale() and agent.any_activity():
core.log("%d access points on channel %d" % (len(aps), ch)) logging.info("%d access points on channel %d" % (len(aps), ch))
# for each ap on this channel # for each ap on this channel
for ap in aps: for ap in aps:
@ -170,5 +171,4 @@ while True:
# affect ours ... neat ^_^ # affect ours ... neat ^_^
agent.next_epoch() agent.next_epoch()
except Exception as e: except Exception as e:
core.log("main loop exception: %s" % e) logging.exception("main loop exception")
core.log("%s" % traceback.format_exc())

View File

@ -4,6 +4,7 @@ import os
import re import re
import socket import socket
from datetime import datetime from datetime import datetime
import logging
import _thread import _thread
import core import core
@ -82,7 +83,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
plugins.on('rebooting', self) plugins.on('rebooting', self)
def setup_events(self): def setup_events(self):
core.log("connecting to %s ..." % self.url) logging.info("connecting to %s ..." % self.url)
for tag in self._config['bettercap']['silence']: for tag in self._config['bettercap']['silence']:
try: try:
@ -109,30 +110,30 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
s = self.session() s = self.session()
for iface in s['interfaces']: for iface in s['interfaces']:
if iface['name'] == mon_iface: if iface['name'] == mon_iface:
core.log("found monitor interface: %s" % iface['name']) logging.info("found monitor interface: %s" % iface['name'])
has_mon = True has_mon = True
break break
if has_mon is False: if has_mon is False:
if mon_start_cmd is not None and mon_start_cmd != '': if mon_start_cmd is not None and mon_start_cmd != '':
core.log("starting monitor interface ...") logging.info("starting monitor interface ...")
self.run('!%s' % mon_start_cmd) self.run('!%s' % mon_start_cmd)
else: else:
core.log("waiting for monitor interface %s ..." % mon_iface) logging.info("waiting for monitor interface %s ..." % mon_iface)
time.sleep(1) time.sleep(1)
core.log("supported channels: %s" % self._supported_channels) logging.info("supported channels: %s" % self._supported_channels)
core.log("handshakes will be collected inside %s" % self._config['bettercap']['handshakes']) logging.info("handshakes will be collected inside %s" % self._config['bettercap']['handshakes'])
self._reset_wifi_settings() self._reset_wifi_settings()
wifi_running = self.is_module_running('wifi') wifi_running = self.is_module_running('wifi')
if wifi_running and restart: if wifi_running and restart:
core.log("restarting wifi module ...") logging.debug("restarting wifi module ...")
self.restart('wifi.recon') self.restart('wifi.recon')
self.run('wifi.clear') self.run('wifi.clear')
elif not wifi_running: elif not wifi_running:
core.log("starting wifi module ...") logging.debug("starting wifi module ...")
self.start('wifi.recon') self.start('wifi.recon')
self.start_advertising() self.start_advertising()
@ -150,13 +151,13 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
for ch in self._epoch.non_overlapping_channels: for ch in self._epoch.non_overlapping_channels:
if ch not in busy_channels: if ch not in busy_channels:
self._epoch.non_overlapping_channels[ch] += 1 self._epoch.non_overlapping_channels[ch] += 1
core.log("channel %d is free from %d epochs" % (ch, self._epoch.non_overlapping_channels[ch])) logging.info("channel %d is free from %d epochs" % (ch, self._epoch.non_overlapping_channels[ch]))
elif self._epoch.non_overlapping_channels[ch] > 0: elif self._epoch.non_overlapping_channels[ch] > 0:
self._epoch.non_overlapping_channels[ch] -= 1 self._epoch.non_overlapping_channels[ch] -= 1
# report any channel that has been free for at least 3 epochs # report any channel that has been free for at least 3 epochs
for ch, num_epochs_free in self._epoch.non_overlapping_channels.items(): for ch, num_epochs_free in self._epoch.non_overlapping_channels.items():
if num_epochs_free >= 3: if num_epochs_free >= 3:
core.log("channel %d has been free for %d epochs" % (ch, num_epochs_free)) logging.info("channel %d has been free for %d epochs" % (ch, num_epochs_free))
self.set_free_channel(ch) self.set_free_channel(ch)
def recon(self): def recon(self):
@ -172,14 +173,14 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
if not channels: if not channels:
self._current_channel = 0 self._current_channel = 0
# core.log("RECON %ds" % recon_time) logging.debug("RECON %ds" % recon_time)
self.run('wifi.recon.channel clear') self.run('wifi.recon.channel clear')
else: else:
# core.log("RECON %ds ON CHANNELS %s" % (recon_time, ','.join(map(str, channels)))) logging.debug("RECON %ds ON CHANNELS %s" % (recon_time, ','.join(map(str, channels))))
try: try:
self.run('wifi.recon.channel %s' % ','.join(map(str, channels))) self.run('wifi.recon.channel %s' % ','.join(map(str, channels)))
except Exception as e: except Exception as e:
core.log("error: %s" % e) logging.exception("error")
self.wait_for(recon_time, sleeping=False) self.wait_for(recon_time, sleeping=False)
@ -204,7 +205,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
if self._filter_included(ap): if self._filter_included(ap):
aps.append(ap) aps.append(ap)
except Exception as e: except Exception as e:
core.log("error: %s" % e) logging.exception("error")
aps.sort(key=lambda ap: ap['channel']) aps.sort(key=lambda ap: ap['channel'])
return self.set_access_points(aps) return self.set_access_points(aps)
@ -289,7 +290,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
self._view.set_closest_peer(peer) self._view.set_closest_peer(peer)
def _save_recovery_data(self): def _save_recovery_data(self):
core.log("writing recovery data to %s ..." % RECOVERY_DATA_FILE) logging.warning("writing recovery data to %s ..." % RECOVERY_DATA_FILE)
with open(RECOVERY_DATA_FILE, 'w') as fp: with open(RECOVERY_DATA_FILE, 'w') as fp:
data = { data = {
'started_at': self._started_at, 'started_at': self._started_at,
@ -304,7 +305,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
try: try:
with open(RECOVERY_DATA_FILE, 'rt') as fp: with open(RECOVERY_DATA_FILE, 'rt') as fp:
data = json.load(fp) data = json.load(fp)
core.log("found recovery data: %s" % data) logging.info("found recovery data: %s" % data)
self._started_at = data['started_at'] self._started_at = data['started_at']
self._epoch.epoch = data['epoch'] self._epoch.epoch = data['epoch']
self._handshakes = data['handshakes'] self._handshakes = data['handshakes']
@ -312,7 +313,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
self._last_pwnd = data['last_pwnd'] self._last_pwnd = data['last_pwnd']
if delete: if delete:
core.log("deleting %s" % RECOVERY_DATA_FILE) logging.info("deleting %s" % RECOVERY_DATA_FILE)
os.unlink(RECOVERY_DATA_FILE) os.unlink(RECOVERY_DATA_FILE)
except: except:
if not no_exceptions: if not no_exceptions:
@ -323,7 +324,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
self.run('events.clear') self.run('events.clear')
core.log("event polling started ...") logging.debug("event polling started ...")
while True: while True:
time.sleep(1) time.sleep(1)
@ -349,21 +350,21 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
new_shakes += 1 new_shakes += 1
ap_and_station = self._find_ap_sta_in(sta_mac, ap_mac, s) ap_and_station = self._find_ap_sta_in(sta_mac, ap_mac, s)
if ap_and_station is None: if ap_and_station is None:
core.log("!!! captured new handshake: %s !!!" % key) logging.warning("!!! captured new handshake: %s !!!" % key)
self._last_pwnd = ap_mac self._last_pwnd = ap_mac
plugins.on('handshake', self, filename, ap_mac, sta_mac) plugins.on('handshake', self, filename, ap_mac, sta_mac)
else: else:
(ap, sta) = ap_and_station (ap, sta) = ap_and_station
self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap[ self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap[
'hostname'] != '<hidden>' else ap_mac 'hostname'] != '<hidden>' else ap_mac
core.log("!!! captured new handshake on channel %d: %s (%s) -> %s [%s (%s)] !!!" % ( \ logging.warning("!!! captured new handshake on channel %d: %s (%s) -> %s [%s (%s)] !!!" % ( \
ap['channel'], ap['channel'],
sta['mac'], sta['vendor'], sta['mac'], sta['vendor'],
ap['hostname'], ap['mac'], ap['vendor'])) ap['hostname'], ap['mac'], ap['vendor']))
plugins.on('handshake', self, filename, ap, sta) plugins.on('handshake', self, filename, ap, sta)
except Exception as e: except Exception as e:
core.log("error: %s" % e) logging.exception("error")
finally: finally:
self._update_handshakes(new_shakes) self._update_handshakes(new_shakes)
@ -404,7 +405,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
return self._history[who] < self._config['personality']['max_interactions'] return self._history[who] < self._config['personality']['max_interactions']
def _on_miss(self, who): def _on_miss(self, who):
core.log("it looks like %s is not in range anymore :/" % who) logging.info("it looks like %s is not in range anymore :/" % who)
self._epoch.track(miss=True) self._epoch.track(miss=True)
self._view.on_miss(who) self._view.on_miss(who)
@ -416,18 +417,18 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
if 'is an unknown BSSID' in error: if 'is an unknown BSSID' in error:
self._on_miss(who) self._on_miss(who)
else: else:
core.log("error: %s" % e) logging.error("%s" % e)
def associate(self, ap, throttle=0): def associate(self, ap, throttle=0):
if self.is_stale(): if self.is_stale():
core.log("recon is stale, skipping assoc(%s)" % ap['mac']) logging.debug("recon is stale, skipping assoc(%s)" % ap['mac'])
return return
if self._config['personality']['associate'] and self._should_interact(ap['mac']): if self._config['personality']['associate'] and self._should_interact(ap['mac']):
self._view.on_assoc(ap) self._view.on_assoc(ap)
try: try:
core.log("sending association frame to %s (%s %s) on channel %d [%d clients]..." % ( \ logging.info("sending association frame to %s (%s %s) on channel %d [%d clients]..." % ( \
ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], len(ap['clients']))) ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], len(ap['clients'])))
self.run('wifi.assoc %s' % ap['mac']) self.run('wifi.assoc %s' % ap['mac'])
self._epoch.track(assoc=True) self._epoch.track(assoc=True)
@ -441,14 +442,14 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
def deauth(self, ap, sta, throttle=0): def deauth(self, ap, sta, throttle=0):
if self.is_stale(): if self.is_stale():
core.log("recon is stale, skipping deauth(%s)" % sta['mac']) logging.debug("recon is stale, skipping deauth(%s)" % sta['mac'])
return return
if self._config['personality']['deauth'] and self._should_interact(sta['mac']): if self._config['personality']['deauth'] and self._should_interact(sta['mac']):
self._view.on_deauth(sta) self._view.on_deauth(sta)
try: try:
core.log("deauthing %s (%s) from %s (%s %s) on channel %d ..." % ( logging.info("deauthing %s (%s) from %s (%s %s) on channel %d ..." % (
sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor'], ap['channel'])) sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor'], ap['channel']))
self.run('wifi.deauth %s' % sta['mac']) self.run('wifi.deauth %s' % sta['mac'])
self._epoch.track(deauth=True) self._epoch.track(deauth=True)
@ -462,7 +463,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
def set_channel(self, channel, verbose=True): def set_channel(self, channel, verbose=True):
if self.is_stale(): if self.is_stale():
core.log("recon is stale, skipping set_channel(%d)" % channel) logging.debug("recon is stale, skipping set_channel(%d)" % channel)
return return
# if in the previous loop no client stations has been deauthenticated # if in the previous loop no client stations has been deauthenticated
@ -478,10 +479,12 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
if channel != self._current_channel: if channel != self._current_channel:
if self._current_channel != 0 and wait > 0: if self._current_channel != 0 and wait > 0:
if verbose: if verbose:
core.log("waiting for %ds on channel %d ..." % (wait, self._current_channel)) logging.info("waiting for %ds on channel %d ..." % (wait, self._current_channel))
else:
logging.debug("waiting for %ds on channel %d ..." % (wait, self._current_channel))
self.wait_for(wait) self.wait_for(wait)
if verbose and self._epoch.any_activity: if verbose and self._epoch.any_activity:
core.log("CHANNEL %d" % channel) logging.info("CHANNEL %d" % channel)
try: try:
self.run('wifi.recon.channel %d' % channel) self.run('wifi.recon.channel %d' % channel)
self._current_channel = channel self._current_channel = channel
@ -491,7 +494,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
plugins.on('channel_hop', self, channel) plugins.on('channel_hop', self, channel)
except Exception as e: except Exception as e:
core.log("error: %s" % e) logging.error("error: %s" % e)
def is_stale(self): def is_stale(self):
return self._epoch.num_missed > self._config['personality']['max_misses_for_recon'] return self._epoch.num_missed > self._config['personality']['max_misses_for_recon']
@ -502,7 +505,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
def _reboot(self): def _reboot(self):
self.set_rebooting() self.set_rebooting()
self._save_recovery_data() self._save_recovery_data()
core.log("rebooting the system ...") logging.warning("rebooting the system ...")
os.system("/usr/bin/sync") os.system("/usr/bin/sync")
os.system("/usr/sbin/shutdown -r now") os.system("/usr/sbin/shutdown -r now")
@ -514,24 +517,24 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
# after X misses during an epoch, set the status to lonely # after X misses during an epoch, set the status to lonely
if was_stale: if was_stale:
core.log("agent missed %d interactions -> lonely" % did_miss) logging.warning("agent missed %d interactions -> lonely" % did_miss)
self.set_lonely() self.set_lonely()
# after X times being bored, the status is set to sad # after X times being bored, the status is set to sad
elif self._epoch.inactive_for >= self._config['personality']['sad_num_epochs']: elif self._epoch.inactive_for >= self._config['personality']['sad_num_epochs']:
core.log("%d epochs with no activity -> sad" % self._epoch.inactive_for) logging.warning("%d epochs with no activity -> sad" % self._epoch.inactive_for)
self.set_sad() self.set_sad()
# after X times being inactive, the status is set to bored # after X times being inactive, the status is set to bored
elif self._epoch.inactive_for >= self._config['personality']['bored_num_epochs']: elif self._epoch.inactive_for >= self._config['personality']['bored_num_epochs']:
core.log("%d epochs with no activity -> bored" % self._epoch.inactive_for) logging.warning("%d epochs with no activity -> bored" % self._epoch.inactive_for)
self.set_bored() self.set_bored()
# after X times being active, the status is set to happy / excited # after X times being active, the status is set to happy / excited
elif self._epoch.active_for >= self._config['personality']['excited_num_epochs']: elif self._epoch.active_for >= self._config['personality']['excited_num_epochs']:
core.log("%d epochs with activity -> excited" % self._epoch.active_for) logging.warning("%d epochs with activity -> excited" % self._epoch.active_for)
self.set_excited() self.set_excited()
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data()) plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']: if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']:
core.log("%d epochs without visible access points -> rebooting ..." % self._epoch.blind_for) logging.critical("%d epochs without visible access points -> rebooting ..." % self._epoch.blind_for)
self._reboot() self._reboot()
self._epoch.blind_for = 0 self._epoch.blind_for = 0

View File

@ -7,16 +7,16 @@ import warnings
# https://stackoverflow.com/questions/15777951/how-to-suppress-pandas-future-warning # https://stackoverflow.com/questions/15777951/how-to-suppress-pandas-future-warning
warnings.simplefilter(action='ignore', category=FutureWarning) warnings.simplefilter(action='ignore', category=FutureWarning)
import core import logging
def load(config, agent, epoch, from_disk=True): def load(config, agent, epoch, from_disk=True):
config = config['ai'] config = config['ai']
if not config['enabled']: if not config['enabled']:
core.log("ai disabled") logging.info("ai disabled")
return False return False
core.log("[ai] bootstrapping dependencies ...") logging.info("[ai] bootstrapping dependencies ...")
from stable_baselines import A2C from stable_baselines import A2C
from stable_baselines.common.policies import MlpLstmPolicy from stable_baselines.common.policies import MlpLstmPolicy
@ -27,16 +27,16 @@ def load(config, agent, epoch, from_disk=True):
env = wrappers.Environment(agent, epoch) env = wrappers.Environment(agent, epoch)
env = DummyVecEnv([lambda: env]) env = DummyVecEnv([lambda: env])
core.log("[ai] bootstrapping model ...") logging.info("[ai] bootstrapping model ...")
a2c = A2C(MlpLstmPolicy, env, **config['params']) a2c = A2C(MlpLstmPolicy, env, **config['params'])
if from_disk and os.path.exists(config['path']): if from_disk and os.path.exists(config['path']):
core.log("[ai] loading %s ..." % config['path']) logging.info("[ai] loading %s ..." % config['path'])
a2c.load(config['path'], env) a2c.load(config['path'], env)
else: else:
core.log("[ai] model created:") logging.info("[ai] model created:")
for key, value in config['params'].items(): for key, value in config['params'].items():
core.log(" %s: %s" % (key, value)) logging.info(" %s: %s" % (key, value))
return a2c return a2c

View File

@ -1,5 +1,6 @@
import time import time
import threading import threading
import logging
import core import core
import pwnagotchi import pwnagotchi
@ -87,13 +88,13 @@ class Epoch(object):
aps_per_chan[ch_idx] += 1.0 aps_per_chan[ch_idx] += 1.0
sta_per_chan[ch_idx] += len(ap['clients']) sta_per_chan[ch_idx] += len(ap['clients'])
except IndexError as e: except IndexError as e:
core.log("got data on channel %d, we can store %d channels" % (ap['channel'], wifi.NumChannels)) logging.error("got data on channel %d, we can store %d channels" % (ap['channel'], wifi.NumChannels))
for peer in peers: for peer in peers:
try: try:
peers_per_chan[peer.last_channel - 1] += 1.0 peers_per_chan[peer.last_channel - 1] += 1.0
except IndexError as e: except IndexError as e:
core.log( logging.error(
"got peer data on channel %d, we can store %d channels" % (peer.last_channel, wifi.NumChannels)) "got peer data on channel %d, we can store %d channels" % (peer.last_channel, wifi.NumChannels))
# normalize # normalize
@ -172,7 +173,7 @@ class Epoch(object):
self._epoch_data['reward'] = self._reward(self.epoch + 1, self._epoch_data) self._epoch_data['reward'] = self._reward(self.epoch + 1, self._epoch_data)
self._epoch_data_ready.set() self._epoch_data_ready.set()
core.log("[epoch %d] duration=%s slept_for=%s blind=%d inactive=%d active=%d hops=%d missed=%d " logging.info("[epoch %d] duration=%s slept_for=%s blind=%d inactive=%d active=%d hops=%d missed=%d "
"deauths=%d assocs=%d handshakes=%d cpu=%d%% mem=%d%% temperature=%dC reward=%s" % ( "deauths=%d assocs=%d handshakes=%d cpu=%d%% mem=%d%% temperature=%dC reward=%s" % (
self.epoch, self.epoch,
core.secs_to_hhmmss(self.epoch_duration), core.secs_to_hhmmss(self.epoch_duration),

View File

@ -1,8 +1,8 @@
import logging
import gym import gym
from gym import spaces from gym import spaces
import numpy as np import numpy as np
import core
import pwnagotchi.ai.featurizer as featurizer import pwnagotchi.ai.featurizer as featurizer
import pwnagotchi.ai.reward as reward import pwnagotchi.ai.reward as reward
from pwnagotchi.ai.parameter import Parameter from pwnagotchi.ai.parameter import Parameter
@ -83,7 +83,7 @@ class Environment(gym.Env):
return params return params
def _next_epoch(self): def _next_epoch(self):
# core.log("[ai] waiting for epoch to finish ...") logging.debug("[ai] waiting for epoch to finish ...")
return self._epoch.wait_for_epoch_data() return self._epoch.wait_for_epoch_data()
def _apply_policy(self, policy): def _apply_policy(self, policy):
@ -110,7 +110,7 @@ class Environment(gym.Env):
return self.last['state_v'], self.last['reward'], not self._agent.is_training(), {} return self.last['state_v'], self.last['reward'], not self._agent.is_training(), {}
def reset(self): def reset(self):
# core.log("[ai] resetting environment ...") # logging.info("[ai] resetting environment ...")
self._epoch_num = 0 self._epoch_num = 0
state = self._next_epoch() state = self._next_epoch()
self.last['state'] = state self.last['state'] = state
@ -120,7 +120,7 @@ class Environment(gym.Env):
def _render_histogram(self, hist): def _render_histogram(self, hist):
for ch in range(featurizer.histogram_size): for ch in range(featurizer.histogram_size):
if hist[ch]: if hist[ch]:
core.log(" CH %d: %s" % (ch + 1, hist[ch])) logging.info(" CH %d: %s" % (ch + 1, hist[ch]))
def render(self, mode='human', close=False, force=False): def render(self, mode='human', close=False, force=False):
# when using a vectorialized environment, render gets called twice # when using a vectorialized environment, render gets called twice
@ -133,18 +133,13 @@ class Environment(gym.Env):
self._last_render = self._epoch_num self._last_render = self._epoch_num
core.log("[ai] --- training epoch %d/%d ---" % (self._epoch_num, self._agent.training_epochs())) logging.info("[ai] --- training epoch %d/%d ---" % (self._epoch_num, self._agent.training_epochs()))
core.log("[ai] REWARD: %f" % self.last['reward']) logging.info("[ai] REWARD: %f" % self.last['reward'])
# core.log("[ai] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items())) logging.debug("[ai] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items()))
core.log("[ai] observation:") logging.info("[ai] observation:")
for name, value in self.last['state'].items(): for name, value in self.last['state'].items():
if 'histogram' in name: if 'histogram' in name:
core.log(" %s" % name.replace('_histogram', '')) logging.info(" %s" % name.replace('_histogram', ''))
self._render_histogram(value) self._render_histogram(value)
# core.log("[ai] outcome:")
# for name, value in self.last['state'].items():
# if 'histogram' not in name:
# core.log(" %s: %s" % (name, value))

View File

@ -4,8 +4,7 @@ import time
import random import random
import os import os
import json import json
import logging
import core
import pwnagotchi.plugins as plugins import pwnagotchi.plugins as plugins
import pwnagotchi.ai as ai import pwnagotchi.ai as ai
@ -56,7 +55,7 @@ class Stats(object):
def load(self): def load(self):
with self._lock: with self._lock:
if os.path.exists(self.path) and os.path.getsize(self.path) > 0: if os.path.exists(self.path) and os.path.getsize(self.path) > 0:
core.log("[ai] loading %s" % self.path) logging.info("[ai] loading %s" % self.path)
with open(self.path, 'rt') as fp: with open(self.path, 'rt') as fp:
obj = json.load(fp) obj = json.load(fp)
@ -66,7 +65,7 @@ class Stats(object):
def save(self): def save(self):
with self._lock: with self._lock:
core.log("[ai] saving %s" % self.path) logging.info("[ai] saving %s" % self.path)
data = json.dumps({ data = json.dumps({
'born_at': self.born_at, 'born_at': self.born_at,
@ -114,7 +113,7 @@ class AsyncTrainer(object):
_thread.start_new_thread(self._ai_worker, ()) _thread.start_new_thread(self._ai_worker, ())
def _save_ai(self): def _save_ai(self):
core.log("[ai] saving model to %s ..." % self._nn_path) logging.info("[ai] saving model to %s ..." % self._nn_path)
temp = "%s.tmp" % self._nn_path temp = "%s.tmp" % self._nn_path
self._model.save(temp) self._model.save(temp)
os.replace(temp, self._nn_path) os.replace(temp, self._nn_path)
@ -133,15 +132,15 @@ class AsyncTrainer(object):
def on_ai_policy(self, new_params): def on_ai_policy(self, new_params):
plugins.on('ai_policy', self, new_params) plugins.on('ai_policy', self, new_params)
core.log("[ai] setting new policy:") logging.info("[ai] setting new policy:")
for name, value in new_params.items(): for name, value in new_params.items():
if name in self._config['personality']: if name in self._config['personality']:
curr_value = self._config['personality'][name] curr_value = self._config['personality'][name]
if curr_value != value: if curr_value != value:
core.log("[ai] ! %s: %s -> %s" % (name, curr_value, value)) logging.info("[ai] ! %s: %s -> %s" % (name, curr_value, value))
self._config['personality'][name] = value self._config['personality'][name] = value
else: else:
core.log("[ai] param %s not in personality configuration!" % name) logging.error("[ai] param %s not in personality configuration!" % name)
self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl']) self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl'])
self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl']) self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl'])
@ -152,12 +151,12 @@ class AsyncTrainer(object):
plugins.on('ai_ready', self) plugins.on('ai_ready', self)
def on_ai_best_reward(self, r): def on_ai_best_reward(self, r):
core.log("[ai] best reward so far: %s" % r) logging.info("[ai] best reward so far: %s" % r)
self._view.on_motivated(r) self._view.on_motivated(r)
plugins.on('ai_best_reward', self, r) plugins.on('ai_best_reward', self, r)
def on_ai_worst_reward(self, r): def on_ai_worst_reward(self, r):
core.log("[ai] worst reward so far: %s" % r) logging.info("[ai] worst reward so far: %s" % r)
self._view.on_demotivated(r) self._view.on_demotivated(r)
plugins.on('ai_worst_reward', self, r) plugins.on('ai_worst_reward', self, r)
@ -174,12 +173,12 @@ class AsyncTrainer(object):
self._model.env.render() self._model.env.render()
# enter in training mode? # enter in training mode?
if random.random() > self._config['ai']['laziness']: if random.random() > self._config['ai']['laziness']:
core.log("[ai] learning for %d epochs ..." % epochs_per_episode) logging.info("[ai] learning for %d epochs ..." % epochs_per_episode)
try: try:
self.set_training(True, epochs_per_episode) self.set_training(True, epochs_per_episode)
self._model.learn(total_timesteps=epochs_per_episode, callback=self.on_ai_training_step) self._model.learn(total_timesteps=epochs_per_episode, callback=self.on_ai_training_step)
except Exception as e: except Exception as e:
core.log("[ai] error while training: %s" % e) logging.exception("[ai] error while training")
finally: finally:
self.set_training(False) self.set_training(False)
obs = self._model.env.reset() obs = self._model.env.reset()

View File

@ -37,6 +37,8 @@ class SessionParser(object):
self.last_saved_session_id = self.last_session_id self.last_saved_session_id = self.last_session_id
def _parse_datetime(self, dt): def _parse_datetime(self, dt):
dt = dt.split('.')[0]
dt = dt.split(',')[0]
dt = datetime.strptime(dt.split('.')[0], '%Y-%m-%d %H:%M:%S') dt = datetime.strptime(dt.split('.')[0], '%Y-%m-%d %H:%M:%S')
return time.mktime(dt.timetuple()) return time.mktime(dt.timetuple())

View File

@ -2,9 +2,9 @@ import time
import json import json
import _thread import _thread
import threading import threading
import logging
from scapy.all import Dot11, Dot11FCS, Dot11Elt, RadioTap, sendp, sniff from scapy.all import Dot11, Dot11FCS, Dot11Elt, RadioTap, sendp, sniff
import core
import pwnagotchi.ui.faces as faces import pwnagotchi.ui.faces as faces
import pwnagotchi.mesh.wifi as wifi import pwnagotchi.mesh.wifi as wifi
@ -54,7 +54,6 @@ class Advertiser(object):
self._lost_peer_cb = lost_cb self._lost_peer_cb = lost_cb
def on_face_change(self, old, new): def on_face_change(self, old, new):
# core.log("face change: %s -> %s" % (old, new))
self.update({'face': new}) self.update({'face': new})
def start(self): def start(self):
@ -84,12 +83,12 @@ class Advertiser(object):
self._stopped.set() self._stopped.set()
def _sender(self): def _sender(self):
core.log("started advertiser thread (period:%s sid:%s) ..." % (str(self._period), self._me.session_id)) logging.info("started advertiser thread (period:%s sid:%s) ..." % (str(self._period), self._me.session_id))
while self._running: while self._running:
try: try:
sendp(self._frame, iface=self._iface, verbose=False, count=5, inter=self._period) sendp(self._frame, iface=self._iface, verbose=False, count=5, inter=self._period)
except Exception as e: except Exception as e:
core.log("error: %s" % e) logging.exception("error")
time.sleep(self._period) time.sleep(self._period)
def _on_advertisement(self, src_session_id, channel, rssi, adv): def _on_advertisement(self, src_session_id, channel, rssi, adv):
@ -97,7 +96,7 @@ class Advertiser(object):
with self._peers_lock: with self._peers_lock:
if ident not in self._peers: if ident not in self._peers:
peer = Peer(src_session_id, channel, rssi, adv) peer = Peer(src_session_id, channel, rssi, adv)
core.log("detected unit %s (v%s) on channel %d (%s dBm) [sid:%s pwnd_tot:%d uptime:%d]" % ( \ logging.info("detected unit %s (v%s) on channel %d (%s dBm) [sid:%s pwnd_tot:%d uptime:%d]" % ( \
peer.full_name(), peer.full_name(),
peer.version(), peer.version(),
channel, channel,
@ -158,10 +157,10 @@ class Advertiser(object):
raise Exception("unknown frame id %d" % dot11elt.ID) raise Exception("unknown frame id %d" % dot11elt.ID)
except Exception as e: except Exception as e:
core.log("error decoding packet from %s: %s" % (dot11.addr3, e)) logging.exception("error decoding packet from %s" % dot11.addr3)
def _listener(self): def _listener(self):
# core.log("started advertisements listener ...") # logging.info("started advertisements listener ...")
expr = "type mgt subtype beacon and ether src %s" % wifi.SignatureAddress expr = "type mgt subtype beacon and ether src %s" % wifi.SignatureAddress
sniff(iface=self._iface, filter=expr, prn=self._on_packet, store=0, stop_filter=lambda x: self._stopped.isSet()) sniff(iface=self._iface, filter=expr, prn=self._on_packet, store=0, stop_filter=lambda x: self._stopped.isSet())
@ -173,7 +172,7 @@ class Advertiser(object):
for ident, peer in self._peers.items(): for ident, peer in self._peers.items():
inactive_for = peer.inactive_for() inactive_for = peer.inactive_for()
if inactive_for >= Advertiser.MAX_STALE_TIME: if inactive_for >= Advertiser.MAX_STALE_TIME:
core.log("peer %s lost (inactive for %ds)" % (peer.full_name(), inactive_for)) logging.info("peer %s lost (inactive for %ds)" % (peer.full_name(), inactive_for))
self._lost_peer_cb(peer) self._lost_peer_cb(peer)
stale.append(ident) stale.append(ident)

View File

@ -1,8 +1,8 @@
import time import time
import logging
import pwnagotchi.mesh.wifi as wifi import pwnagotchi.mesh.wifi as wifi
import pwnagotchi.ui.faces as faces import pwnagotchi.ui.faces as faces
import core
class Peer(object): class Peer(object):
@ -18,10 +18,10 @@ class Peer(object):
def update(self, sid, channel, rssi, adv): def update(self, sid, channel, rssi, adv):
if self.name() != adv['name']: if self.name() != adv['name']:
core.log("peer %s changed name: %s -> %s" % (self.full_name(), self.name(), adv['name'])) logging.info("peer %s changed name: %s -> %s" % (self.full_name(), self.name(), adv['name']))
if self.session_id != sid: if self.session_id != sid:
core.log("peer %s changed session id: %s -> %s" % (self.full_name(), self.session_id, sid)) logging.info("peer %s changed session id: %s -> %s" % (self.full_name(), self.session_id, sid))
self.presence[channel - 1] += 1 self.presence[channel - 1] += 1
self.adv = adv self.adv = adv

View File

@ -1,6 +1,6 @@
import _thread import _thread
import logging
import core
import pwnagotchi import pwnagotchi
import pwnagotchi.version as version import pwnagotchi.version as version
import pwnagotchi.plugins as plugins import pwnagotchi.plugins as plugins
@ -35,7 +35,7 @@ class AsyncAdvertiser(object):
self._advertiser.start() self._advertiser.start()
self._view.on_state_change('face', self._advertiser.on_face_change) self._view.on_state_change('face', self._advertiser.on_face_change)
else: else:
core.log("advertising is disabled") logging.warning("advertising is disabled")
def _on_new_unit(self, peer): def _on_new_unit(self, peer):
self._view.on_new_peer(peer) self._view.on_new_peer(peer)

View File

@ -5,15 +5,16 @@ __license__ = 'GPL3'
__description__ = 'An example plugin for pwnagotchi that implements all the available callbacks.' __description__ = 'An example plugin for pwnagotchi that implements all the available callbacks.'
__enabled__ = False # IMPORTANT: set this to True to enable your plugin. __enabled__ = False # IMPORTANT: set this to True to enable your plugin.
import logging
from pwnagotchi.ui.components import LabeledValue from pwnagotchi.ui.components import LabeledValue
from pwnagotchi.ui.view import BLACK from pwnagotchi.ui.view import BLACK
import pwnagotchi.ui.fonts as fonts import pwnagotchi.ui.fonts as fonts
import core
# called when the plugin is loaded # called when the plugin is loaded
def on_loaded(): def on_loaded():
core.log("WARNING: plugin %s should be disabled!" % __name__) logging.warning("WARNING: plugin %s should be disabled!" % __name__)
# called to setup the ui elements # called to setup the ui elements
@ -39,7 +40,7 @@ def on_display_setup(display):
# called when everything is ready and the main loop is about to start # called when everything is ready and the main loop is about to start
def on_ready(agent): def on_ready(agent):
core.log("unit is ready") logging.info("unit is ready")
# you can run custom bettercap commands if you want # you can run custom bettercap commands if you want
# agent.run('ble.recon on') # agent.run('ble.recon on')
# or set a custom state # or set a custom state

View File

@ -5,7 +5,7 @@ __license__ = 'GPL3'
__description__ = 'Save GPS coordinates whenever an handshake is captured.' __description__ = 'Save GPS coordinates whenever an handshake is captured.'
__enabled__ = True # set to false if you just don't use GPS __enabled__ = True # set to false if you just don't use GPS
import core import logging
import json import json
import os import os
@ -15,14 +15,14 @@ running = False
def on_loaded(): def on_loaded():
core.log("GPS plugin loaded for %s" % device) logging.info("GPS plugin loaded for %s" % device)
def on_ready(agent): def on_ready(agent):
global running global running
if os.path.exists(device): if os.path.exists(device):
core.log("enabling GPS bettercap's module for %s" % device) logging.info("enabling GPS bettercap's module for %s" % device)
try: try:
agent.run('gps off') agent.run('gps off')
except: except:
@ -33,7 +33,7 @@ def on_ready(agent):
agent.run('gps on') agent.run('gps on')
running = True running = True
else: else:
core.log("no GPS detected") logging.warning("no GPS detected")
def on_handshake(agent, filename, access_point, client_station): def on_handshake(agent, filename, access_point, client_station):
@ -42,6 +42,6 @@ def on_handshake(agent, filename, access_point, client_station):
gps = info['gps'] gps = info['gps']
gps_filename = filename.replace('.pcap', '.gps.json') gps_filename = filename.replace('.pcap', '.gps.json')
core.log("saving GPS to %s (%s)" % (gps_filename, gps)) logging.info("saving GPS to %s (%s)" % (gps_filename, gps))
with open(gps_filename, 'w+t') as fp: with open(gps_filename, 'w+t') as fp:
json.dump(gps, fp) json.dump(gps, fp)

View File

@ -2,7 +2,7 @@ import _thread
from threading import Lock from threading import Lock
import shutil import shutil
import core import logging
import os import os
import pwnagotchi, pwnagotchi.plugins as plugins import pwnagotchi, pwnagotchi.plugins as plugins
@ -78,13 +78,12 @@ class Display(View):
self._render_cb = None self._render_cb = None
self._display = None self._display = None
self._httpd = None self._httpd = None
self.canvas = None
if self._enabled: if self._enabled:
self._init_display() self._init_display()
else: else:
self.on_render(self._on_view_rendered) self.on_render(self._on_view_rendered)
core.log("display module is disabled") logging.warning("display module is disabled")
if self._video_enabled: if self._video_enabled:
_thread.start_new_thread(self._http_serve, ()) _thread.start_new_thread(self._http_serve, ())
@ -92,10 +91,10 @@ class Display(View):
def _http_serve(self): def _http_serve(self):
if self._video_address is not None: if self._video_address is not None:
self._httpd = HTTPServer((self._video_address, self._video_port), VideoHandler) self._httpd = HTTPServer((self._video_address, self._video_port), VideoHandler)
core.log("ui available at http://%s:%d/" % (self._video_address, self._video_port)) logging.info("ui available at http://%s:%d/" % (self._video_address, self._video_port))
self._httpd.serve_forever() self._httpd.serve_forever()
else: else:
core.log("could not get ip of usb0, video server not starting") logging.info("could not get ip of usb0, video server not starting")
def _is_inky(self): def _is_inky(self):
return self._display_type in ('inkyphat', 'inky') return self._display_type in ('inkyphat', 'inky')
@ -111,14 +110,14 @@ class Display(View):
def _init_display(self): def _init_display(self):
if self._is_inky(): if self._is_inky():
core.log("initializing inky display") logging.info("initializing inky display")
from inky import InkyPHAT from inky import InkyPHAT
self._display = InkyPHAT(self._display_color) self._display = InkyPHAT(self._display_color)
self._display.set_border(InkyPHAT.BLACK) self._display.set_border(InkyPHAT.BLACK)
self._render_cb = self._inky_render self._render_cb = self._inky_render
elif self._is_papirus(): elif self._is_papirus():
core.log("initializing papirus display") logging.info("initializing papirus display")
from pwnagotchi.ui.papirus.epd import EPD from pwnagotchi.ui.papirus.epd import EPD
os.environ['EPD_SIZE'] = '2.0' os.environ['EPD_SIZE'] = '2.0'
self._display = EPD() self._display = EPD()
@ -126,7 +125,7 @@ class Display(View):
self._render_cb = self._papirus_render self._render_cb = self._papirus_render
elif self._is_waveshare1(): elif self._is_waveshare1():
core.log("initializing waveshare v1 display") logging.info("initializing waveshare v1 display")
from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
self._display = EPD() self._display = EPD()
self._display.init(self._display.lut_full_update) self._display.init(self._display.lut_full_update)
@ -135,7 +134,7 @@ class Display(View):
self._render_cb = self._waveshare_render self._render_cb = self._waveshare_render
elif self._is_waveshare2(): elif self._is_waveshare2():
core.log("initializing waveshare v2 display") logging.info("initializing waveshare v2 display")
from pwnagotchi.ui.waveshare.v2.waveshare import EPD from pwnagotchi.ui.waveshare.v2.waveshare import EPD
self._display = EPD() self._display = EPD()
self._display.init(self._display.FULL_UPDATE) self._display.init(self._display.FULL_UPDATE)
@ -144,63 +143,61 @@ class Display(View):
self._render_cb = self._waveshare_render self._render_cb = self._waveshare_render
else: else:
core.log("unknown display type %s" % self._display_type) logging.critical("unknown display type %s" % self._display_type)
plugins.on('display_setup', self._display) plugins.on('display_setup', self._display)
self.on_render(self._on_view_rendered) self.on_render(self._on_view_rendered)
def image(self):
img = None
if self.canvas is not None:
img = self.canvas if self._rotation == 0 else self.canvas.rotate(-self._rotation)
return img
def _inky_render(self): def _inky_render(self):
if self._display_color != 'mono': if self._display_color != 'mono':
display_colors = 3 display_colors = 3
else: else:
display_colors = 2 display_colors = 2
imgbuf = self.canvas.convert('RGB').convert('P', palette=1, colors=display_colors) img_buffer = self._canvas.convert('RGB').convert('P', palette=1, colors=display_colors)
if self._display_color == 'red': if self._display_color == 'red':
imgbuf.putpalette([ img_buffer.putpalette([
255, 255, 255, # index 0 is white 255, 255, 255, # index 0 is white
0, 0, 0, # index 1 is black 0, 0, 0, # index 1 is black
255, 0, 0 # index 2 is red 255, 0, 0 # index 2 is red
]) ])
elif self._display_color == 'yellow': elif self._display_color == 'yellow':
imgbuf.putpalette([ img_buffer.putpalette([
255, 255, 255, # index 0 is white 255, 255, 255, # index 0 is white
0, 0, 0, # index 1 is black 0, 0, 0, # index 1 is black
255, 255, 0 # index 2 is yellow 255, 255, 0 # index 2 is yellow
]) ])
else: else:
imgbuf.putpalette([ img_buffer.putpalette([
255, 255, 255, # index 0 is white 255, 255, 255, # index 0 is white
0, 0, 0 # index 1 is black 0, 0, 0 # index 1 is black
]) ])
self._display.set_image(imgbuf) self._display.set_image(img_buffer)
self._display.show() self._display.show()
def _papirus_render(self): def _papirus_render(self):
self._display.display(self.canvas) self._display.display(self._canvas)
self._display.partial_update() self._display.partial_update()
def _waveshare_render(self): def _waveshare_render(self):
buf = self._display.getbuffer(self.canvas) buf = self._display.getbuffer(self._canvas)
if self._is_waveshare1(): if self._is_waveshare1():
self._display.display(buf) self._display.display(buf)
elif self._is_waveshare2(): elif self._is_waveshare2():
self._display.displayPartial(buf) self._display.displayPartial(buf)
def image(self):
img = None
if self._canvas is not None:
img = self._canvas if self._rotation == 0 else self._canvas.rotate(-self._rotation)
return img
def _on_view_rendered(self, img): def _on_view_rendered(self, img):
# core.log("display::_on_view_rendered")
VideoHandler.render(img) VideoHandler.render(img)
if self._enabled: if self._enabled:
self.canvas = (img if self._rotation == 0 else img.rotate(self._rotation)) self._canvas = (img if self._rotation == 0 else img.rotate(self._rotation))
if self._render_cb is not None: if self._render_cb is not None:
self._render_cb() self._render_cb()

View File

@ -1,6 +1,7 @@
import _thread import _thread
from threading import Lock from threading import Lock
import time import time
import logging
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
import core import core
@ -114,7 +115,7 @@ class View(object):
_thread.start_new_thread(self._refresh_handler, ()) _thread.start_new_thread(self._refresh_handler, ())
self._ignore_changes = () self._ignore_changes = ()
else: else:
core.log("ui.fps is 0, the display will only update for major changes") logging.warning("ui.fps is 0, the display will only update for major changes")
self._ignore_changes = ('uptime', 'name') self._ignore_changes = ('uptime', 'name')
def add_element(self, key, elem): def add_element(self, key, elem):
@ -135,7 +136,7 @@ class View(object):
def _refresh_handler(self): def _refresh_handler(self):
delay = 1.0 / self._config['ui']['fps'] delay = 1.0 / self._config['ui']['fps']
# core.log("view refresh handler started with period of %.2fs" % delay) # logging.info("view refresh handler started with period of %.2fs" % delay)
while True: while True:
name = self._state.get('name') name = self._state.get('name')
@ -313,10 +314,10 @@ class View(object):
self.set('status', self._voice.custom(text)) self.set('status', self._voice.custom(text))
self.update() self.update()
def update(self): def update(self, force=False):
with self._lock: with self._lock:
changes = self._state.changes(ignore=self._ignore_changes) changes = self._state.changes(ignore=self._ignore_changes)
if len(changes): if force or len(changes):
self._canvas = Image.new('1', (self._width, self._height), WHITE) self._canvas = Image.new('1', (self._width, self._height), WHITE)
drawer = ImageDraw.Draw(self._canvas) drawer = ImageDraw.Draw(self._canvas)

View File

@ -1,5 +1,7 @@
import yaml import yaml
import os import os
import logging
# https://stackoverflow.com/questions/823196/yaml-merge-in-python # https://stackoverflow.com/questions/823196/yaml-merge-in-python
def merge_config(user, default): def merge_config(user, default):
@ -21,4 +23,20 @@ def load_config(args):
user_config = yaml.safe_load(fp) user_config = yaml.safe_load(fp)
config = merge_config(user_config, config) config = merge_config(user_config, config)
return config return config
def setup_logging(args, config):
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s")
root = logging.getLogger()
root.setLevel(logging.DEBUG if args.debug else logging.INFO)
if config['main']['log']:
file_handler = logging.FileHandler(config['main']['log'])
file_handler.setFormatter(formatter)
root.addHandler(file_handler)
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
root.addHandler(console_handler)