Merge remote-tracking branch 'upstream/master' into patch-1

This commit is contained in:
gerard780 2020-01-14 16:10:30 -05:00
commit 3e5bece3cf
29 changed files with 1056 additions and 353 deletions

View File

@ -1,10 +1,11 @@
PACKER_VERSION=1.4.5
PWN_HOSTNAME=pwnagotchi
PWN_VERSION=master
all: clean install image
install:
curl https://releases.hashicorp.com/packer/1.3.5/packer_1.3.5_linux_amd64.zip -o /tmp/packer.zip
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
unzip /tmp/packer.zip -d /tmp
sudo mv /tmp/packer /usr/bin/packer
git clone https://github.com/solo-io/packer-builder-arm-image /tmp/packer-builder-arm-image

View File

@ -90,9 +90,9 @@ def do_auto_mode(agent):
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.yml',
parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.toml',
help='Main configuration file.')
parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.yml',
parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.toml',
help='If this file exists, configuration will be merged and this will override default values.')
parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.")
@ -119,7 +119,7 @@ if __name__ == '__main__':
config = utils.load_config(args)
if args.print_config:
print(yaml.dump(config, default_flow_style=False))
print(toml.dumps(config))
exit(0)
utils.setup_logging(args, config)

View File

@ -6,6 +6,7 @@ After=pwngrid-peer.service
[Service]
Type=simple
WorkingDirectory=/tmp
PermissionsStartOnly=true
ExecStart=/usr/bin/pwnagotchi-launcher
Restart=always

View File

@ -47,7 +47,7 @@
- firmware-misc-nonfree
- firmware-realtek
remove:
- rasberrypi-net-mods
- raspberrypi-net-mods
- dhcpcd5
- triggerhappy
- wpa_supplicant

View File

@ -30,7 +30,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
AsyncTrainer.__init__(self, config)
self._started_at = time.time()
self._filter = None if config['main']['filter'] is None else re.compile(config['main']['filter'])
self._filter = None if not config['main']['filter'] else re.compile(config['main']['filter'])
self._current_channel = 0
self._tot_aps = 0
self._aps_on_channel = 0

205
pwnagotchi/defaults.toml Normal file
View File

@ -0,0 +1,205 @@
main.name = ""
main.lang = "en"
main.custom_plugins = ""
main.iface = "mon0"
main.mon_start_cmd = "/usr/bin/monstart"
main.mon_stop_cmd = "/usr/bin/monstop"
main.mon_max_blind_epochs = 50.0
main.no_restart = false
main.whitelist = [
"EXAMPLE_NETWORK",
"ANOTHER_EXAMPLE_NETWORK",
"fo:od:ba:be:fo:od",
"fo:od:ba"
]
main.filter = ""
main.plugins.grid.enabled = true
main.plugins.grid.report = false
main.plugins.grid.exclude = [
"YourHomeNetworkHere"
]
main.plugins.auto-update.enabled = true
main.plugins.auto-update.install = true
main.plugins.auto-update.interval = 1.0
main.plugins.net-pos.enabled = false
main.plugins.net-pos.api_key = "test"
main.plugins.gps.enabled = false
main.plugins.gps.speed = 19200.0
main.plugins.gps.device = "/dev/ttyUSB0"
main.plugins.webgpsmap.enabled = false
main.plugins.onlinehashcrack.enabled = false
main.plugins.onlinehashcrack.email = ""
main.plugins.wpa-sec.enabled = false
main.plugins.wpa-sec.api_key = ""
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
main.plugins.wpa-sec.download_results = false
main.plugins.wigle.enabled = false
main.plugins.wigle.api_key = ""
main.plugins.bt-tether.enabled = false
main.plugins.bt-tether.devices.android-phone.enabled = false
main.plugins.bt-tether.devices.android-phone.search_order = 1.0
main.plugins.bt-tether.devices.android-phone.mac = ""
main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44"
main.plugins.bt-tether.devices.android-phone.netmask = 24.0
main.plugins.bt-tether.devices.android-phone.interval = 1.0
main.plugins.bt-tether.devices.android-phone.scantime = 10.0
main.plugins.bt-tether.devices.android-phone.max_tries = 10.0
main.plugins.bt-tether.devices.android-phone.share_internet = false
main.plugins.bt-tether.devices.android-phone.priority = 1.0
main.plugins.bt-tether.devices.ios-phone.enabled = false
main.plugins.bt-tether.devices.ios-phone.search_order = 2.0
main.plugins.bt-tether.devices.ios-phone.mac = ""
main.plugins.bt-tether.devices.ios-phone.ip = "172.20.10.6"
main.plugins.bt-tether.devices.ios-phone.netmask = 24.0
main.plugins.bt-tether.devices.ios-phone.interval = 5.0
main.plugins.bt-tether.devices.ios-phone.scantime = 20.0
main.plugins.bt-tether.devices.ios-phone.max_tries = 0.0
main.plugins.bt-tether.devices.ios-phone.share_internet = false
main.plugins.bt-tether.devices.ios-phone.priority = 999.0
main.plugins.memtemp.enabled = false
main.plugins.memtemp.scale = "celsius"
main.plugins.memtemp.orientation = "horizontal"
main.plugins.paw-gps.enabled = false
main.plugins.paw-gps.ip = ""
main.plugins.gpio_buttons.enabled = false
main.plugins.led.enabled = true
main.plugins.led.led = 0.0
main.plugins.led.delay = 200.0
main.plugins.led.patterns.loaded = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.updating = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.unread_inbox = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.ready = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.ai_ready = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.ai_training_start = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.ai_best_reward = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.ai_worst_reward = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.bored = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.sad = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.excited = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.lonely = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.rebooting = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.wait = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.sleep = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.wifi_update = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.association = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.deauthentication = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.handshake = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.epoch = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.peer_detected = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.peer_lost = "oo oo oo oo oo oo oo"
main.plugins.session-stats.enabled = true
main.log.path = "/var/log/pwnagotchi.log"
main.log.rotation.enabled = true
main.log.rotation.size = "10M"
ai.enabled = true
ai.path = "/root/brain.nn"
ai.laziness = 0.1
ai.epochs_per_episode = 50.0
ai.params.gamma = 0.99
ai.params.n_steps = 1.0
ai.params.vf_coef = 0.25
ai.params.ent_coef = 0.01
ai.params.max_grad_norm = 0.5
ai.params.learning_rate = 0.001
ai.params.alpha = 0.99
ai.params.epsilon = 0.00001
ai.params.verbose = 1.0
ai.params.lr_schedule = "constant"
personality.advertise = true
personality.deauth = true
personality.associate = true
personality.channels = []
personality.min_rssi = -200.0
personality.ap_ttl = 120.0
personality.sta_ttl = 300.0
personality.recon_time = 30.0
personality.max_inactive_scale = 2.0
personality.recon_inactive_multiplier = 2.0
personality.hop_recon_time = 10.0
personality.min_recon_time = 5.0
personality.max_interactions = 3.0
personality.max_misses_for_recon = 5.0
personality.excited_num_epochs = 10.0
personality.bored_num_epochs = 15.0
personality.sad_num_epochs = 25.0
personality.bond_encounters_factor = 20000.0
ui.fps = 0.0
ui.faces.look_r = "( ⚆_⚆)"
ui.faces.look_l = "(☉_☉ )"
ui.faces.look_r_happy = "( ◕‿◕)"
ui.faces.look_l_happy = "(◕‿◕ )"
ui.faces.sleep = "(⇀‿‿↼)"
ui.faces.sleep2 = "(≖‿‿≖)"
ui.faces.awake = "(◕‿‿◕)"
ui.faces.bored = "(-__-)"
ui.faces.intense = "(°▃▃°)"
ui.faces.cool = "(⌐■_■)"
ui.faces.happy = "(•‿‿•)"
ui.faces.excited = "(ᵔ◡◡ᵔ)"
ui.faces.grateful = "(^‿‿^)"
ui.faces.motivated = "(☼‿‿☼)"
ui.faces.demotivated = "(≖__≖)"
ui.faces.smart = "(✜‿‿✜)"
ui.faces.lonely = "(ب__ب)"
ui.faces.sad = "(╥☁╥ )"
ui.faces.angry = "(-_-')"
ui.faces.friend = "(♥‿‿♥)"
ui.faces.broken = "(☓‿‿☓)"
ui.faces.debug = "(#__#)"
ui.web.enabled = true
ui.web.address = "0.0.0.0"
ui.web.username = "changeme"
ui.web.password = "changeme"
ui.web.origin = ""
ui.web.port = 8080.0
ui.web.on_frame = ""
ui.display.enabled = true
ui.display.rotation = 180.0
ui.display.type = "waveshare_2"
ui.display.color = "black"
bettercap.scheme = "http"
bettercap.hostname = "localhost"
bettercap.port = 8081.0
bettercap.username = "pwnagotchi"
bettercap.password = "pwnagotchi"
bettercap.handshakes = "/root/handshakes"
bettercap.silence = [
"ble.device.new",
"ble.device.lost",
"ble.device.disconnected",
"ble.device.connected",
"ble.device.service.discovered",
"ble.device.characteristic.discovered",
"wifi.client.new",
"wifi.client.lost",
"wifi.client.probe",
"wifi.ap.new",
"wifi.ap.lost",
"mod.started"
]

View File

@ -1,295 +0,0 @@
#
# This file is recreated with default settings on every pwnagotchi restart,
# use /etc/pwnagotchi/config.yml to configure this unit.
#
#
# main algorithm configuration
main:
# if set this will set the hostname of the unit. min length is 2, max length 25, only a-zA-Z0-9- allowed
name: ''
# currently implemented: en (default), de, el, fr, it, mk, nl, ru, se, pt-BR, es, pt
lang: en
# custom plugins path, if null only default plugins with be loaded
custom_plugins:
# which plugins to load and enable
plugins:
grid:
enabled: true
report: false # don't report pwned networks by default!
exclude: # do not report the following networks (accepts both ESSIDs and BSSIDs)
- YourHomeNetworkHere
auto-update:
enabled: true
install: true # if false, it will only warn that updates are available, if true it will install them
interval: 1 # every 1 hour
net-pos:
enabled: false
api_key: 'test'
gps:
enabled: false
speed: 19200
device: /dev/ttyUSB0
webgpsmap:
enabled: false
onlinehashcrack:
enabled: false
email: ~
wpa-sec:
enabled: false
api_key: ~
api_url: "https://wpa-sec.stanev.org"
download_results: false
wigle:
enabled: false
api_key: ~
bt-tether:
enabled: false # if you want to use this, set ui.display.web.address to 0.0.0.0
devices:
android-phone:
enabled: false
search_order: 1 # search for this first
mac: ~ # mac of your bluetooth device
ip: '192.168.44.44' # ip from which your pwnagotchi should be reachable
netmask: 24
interval: 1 # check every minute for device
scantime: 10 # search for 10 seconds
max_tries: 10 # after 10 tries of "not found"; don't try anymore
share_internet: false
priority: 1 # low routing priority; ios (prio: 999) would win here
ios-phone:
enabled: false
search_order: 2 # search for this second
mac: ~ # mac of your bluetooth device
ip: '172.20.10.6' # ip from which your pwnagotchi should be reachable
netmask: 24
interval: 5 # check every 5 minutes for device
scantime: 20
max_tries: 0 # infinity
share_internet: false
priority: 999 # routing priority
memtemp: # Display memory usage, cpu load and cpu temperature on screen
enabled: false
scale: celsius
orientation: horizontal # horizontal/vertical
paw-gps:
enabled: false
#The IP Address of your phone with Paw Server running, default (option is empty) is 192.168.44.1
ip: ''
gpio_buttons:
enabled: false
#The following is a list of the GPIO number for your button, and the command you want to run when it is pressed
gpios:
#20: 'touch /root/.pwnagotchi-auto && systemctl restart pwnagotchi'
#21: 'shutdown -h now'
led:
enabled: true
# for /sys/class/leds/led0/brightness
led: 0
# time in milliseconds for each element of the patterns
delay: 200
# o=on space=off, comment the ones you don't want
patterns:
loaded: 'oo oo oo oo oo oo oo'
updating: 'oo oo oo oo oo oo oo'
# internet_available: 'oo oo oo oo oo oo oo'
unread_inbox: 'oo oo oo oo oo oo oo'
ready: 'oo oo oo oo oo oo oo'
ai_ready: 'oo oo oo oo oo oo oo'
ai_training_start: 'oo oo oo oo oo oo oo'
ai_best_reward: 'oo oo oo oo oo oo oo'
ai_worst_reward: 'oo oo oo oo oo oo oo'
bored: 'oo oo oo oo oo oo oo'
sad: 'oo oo oo oo oo oo oo'
excited: 'oo oo oo oo oo oo oo'
lonely: 'oo oo oo oo oo oo oo'
rebooting: 'oo oo oo oo oo oo oo'
wait: 'oo oo oo oo oo oo oo'
sleep: 'oo oo oo oo oo oo oo'
wifi_update: 'oo oo oo oo oo oo oo'
association: 'oo oo oo oo oo oo oo'
deauthentication: 'oo oo oo oo oo oo oo'
handshake: 'oo oo oo oo oo oo oo'
epoch: 'oo oo oo oo oo oo oo'
peer_detected: 'oo oo oo oo oo oo oo'
peer_lost: 'oo oo oo oo oo oo oo'
webcfg:
enabled: false
session-stats:
enabled: true
# monitor interface to use
iface: mon0
# command to run to bring the mon interface up in case it's not up already
mon_start_cmd: /usr/bin/monstart
mon_stop_cmd: /usr/bin/monstop
mon_max_blind_epochs: 50
# if true, will not restart the wifi module
no_restart: false
# access points to ignore. Could be the ssid, bssid or the vendor part of bssid.
whitelist:
- EXAMPLE_NETWORK
- ANOTHER_EXAMPLE_NETWORK
- fo:od:ba:be:fo:od # BSSID
- fo:od:ba # Vendor BSSID
# if not null, filter access points by this regular expression
filter: null
# logging
log:
# file to log to
path: /var/log/pwnagotchi.log
rotation:
enabled: true
# specify a maximum size to rotate ( format is 10/10B, 10K, 10M 10G )
size: '10M'
ai:
# if false, only the default 'personality' will be used
enabled: true
path: /root/brain.nn
# 1.0 - laziness = probability of start training
laziness: 0.1
# how many epochs to train on
epochs_per_episode: 50
params:
# discount factor
gamma: 0.99
# the number of steps to run for each environment per update
n_steps: 1
# value function coefficient for the loss calculation
vf_coef: 0.25
# entropy coefficient for the loss calculation
ent_coef: 0.01
# maximum value for the gradient clipping
max_grad_norm: 0.5
# the learning rate
learning_rate: 0.0010
# rmsprop decay parameter
alpha: 0.99
# rmsprop epsilon
epsilon: 0.00001
# the verbosity level: 0 none, 1 training information, 2 tensorflow debug
verbose: 1
# type of scheduler for the learning rate update ('linear', 'constant', 'double_linear_con', 'middle_drop' or 'double_middle_drop')
lr_schedule: 'constant'
# the log location for tensorboard (if None, no logging)
tensorboard_log: null
personality:
# advertise our presence
advertise: true
# perform a deauthentication attack to client stations in order to get full or half handshakes
deauth: true
# send association frames to APs in order to get the PMKID
associate: true
# list of channels to recon on, or empty for all channels
channels: []
# minimum WiFi signal strength in dBm
min_rssi: -200
# number of seconds for wifi.ap.ttl
ap_ttl: 120
# number of seconds for wifi.sta.ttl
sta_ttl: 300
# time in seconds to wait during channel recon
recon_time: 30
# number of inactive epochs after which recon_time gets multiplied by recon_inactive_multiplier
max_inactive_scale: 2
# if more than max_inactive_scale epochs are inactive, recon_time *= recon_inactive_multiplier
recon_inactive_multiplier: 2
# time in seconds to wait during channel hopping if activity has been performed
hop_recon_time: 10
# time in seconds to wait during channel hopping if no activity has been performed
min_recon_time: 5
# maximum amount of deauths/associations per BSSID per session
max_interactions: 3
# maximum amount of misses before considering the data stale and triggering a new recon
max_misses_for_recon: 5
# number of active epochs that triggers the excited state
excited_num_epochs: 10
# number of inactive epochs that triggers the bored state
bored_num_epochs: 15
# number of inactive epochs that triggers the sad state
sad_num_epochs: 25
# number of encounters (times met on a channel) with another unit before considering it a friend and bond
# also used for cumulative bonding score of nearby units
bond_encounters_factor: 20000
# ui configuration
ui:
# here you can customize the faces
faces:
look_r: '( ⚆_⚆)'
look_l: '(☉_☉ )'
look_r_happy: '( ◕‿◕)'
look_l_happy: '(◕‿◕ )'
sleep: '(⇀‿‿↼)'
sleep2: '(≖‿‿≖)'
awake: '(◕‿‿◕)'
bored: '(-__-)'
intense: '(°▃▃°)'
cool: '(⌐■_■)'
happy: '(•‿‿•)'
excited: '(ᵔ◡◡ᵔ)'
grateful: '(^‿‿^)'
motivated: '(☼‿‿☼)'
demotivated: '(≖__≖)'
smart: '(✜‿‿✜)'
lonely: '(ب__ب)'
sad: '(╥☁╥ )'
angry: "(-_-')"
friend: '(♥‿‿♥)'
broken: '(☓‿‿☓)'
debug: '(#__#)'
# ePaper display can update every 3 secs anyway, set to 0 to only refresh for major data changes
# IMPORTANT: The lifespan of an eINK display depends on the cumulative amount of refreshes. If you want to
# preserve your display over time, you should set this value to 0.0 so that the display will be refreshed only
# if any of the important data fields changed (the uptime and blinking cursor won't trigger a refresh).
fps: 0.0
# web ui
web:
enabled: true
address: '0.0.0.0'
username: changeme # !!! CHANGE THIS !!!
password: changeme # !!! CHANGE THIS !!!
origin: null
port: 8080
# command to be executed when a new png frame is available
# for instance, to use with framebuffer based displays:
# on_frame: 'fbi --noverbose -a -d /dev/fb1 -T 1 /root/pwnagotchi.png > /dev/null 2>&1'
on_frame: ''
# hardware display
display:
enabled: true
rotation: 180
# Possible options inkyphat/inky, papirus/papi, waveshare_1/ws_1 or waveshare_2/ws_2, oledhat, lcdhat, waveshare154inch, waveshare27inch, waveshare29inch, dfrobot/df, waveshare144lcd/ws144inch
type: 'waveshare_2'
# Possible options red/yellow/black (black used for monocromatic displays)
# Waveshare tri-color 2.13in display can be over-driven with color set as 'fastAndFurious'
# THIS IS POTENTIALLY DANGEROUS. DO NOT USE UNLESS YOU UNDERSTAND THAT IT COULD KILL YOUR DISPLAY
color: 'black'
# bettercap rest api configuration
bettercap:
# api scheme://hostname:port username and password
scheme: http
hostname: localhost
port: 8081
username: pwnagotchi
password: pwnagotchi
# folder where bettercap stores the WPA handshakes, given that
# wifi.handshakes.aggregate will be set to false and individual
# pcap files will be created in order to minimize the chances
# of a single pcap file to get corrupted
handshakes: /root/handshakes
# events to mute in bettercap's events stream
silence:
- ble.device.new
- ble.device.lost
- ble.device.disconnected
- ble.device.connected
- ble.device.service.discovered
- ble.device.characteristic.discovered
- wifi.client.new
- wifi.client.lost
- wifi.client.probe
- wifi.ap.new
- wifi.ap.lost
- mod.started

View File

@ -26,7 +26,7 @@ msgid "New day, new hunt, new pwns!"
msgstr "Neuer Tag, neue Jagd, neue Pwns!"
msgid "Hack the Planet!"
msgstr "Hack den Planet!"
msgstr "Hack den Planeten!"
msgid "AI ready."
msgstr "KI bereit."
@ -35,7 +35,7 @@ msgid "The neural network is ready."
msgstr "Das neurale Netz ist bereit."
msgid "Generating keys, do not turn off ..."
msgstr "Generiere Keys, nicht ausschalten..."
msgstr "Generiere Schlüssel, nicht ausschalten..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
@ -83,7 +83,7 @@ msgid "I pwn therefore I am."
msgstr "Ich pwne, also bin ich."
msgid "So many networks!!!"
msgstr "So viele Netwerke!!!"
msgstr "So viele Netzwerke!!!"
msgid "I'm having so much fun!"
msgstr "Ich habe sooo viel Spaß!"
@ -93,7 +93,7 @@ msgstr "Mein Verbrechen ist das der Neugier..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Hallo {name}, nett Dich kennenzulernen."
msgstr "Hallo {name}, schön Dich kennenzulernen."
#, python-brace-format
msgid "Yo {name}! Sup?"
@ -203,11 +203,11 @@ msgstr "Ops, da ist was schief gelaufen... Starte neu..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "{num} Stationen gekicked\n"
msgstr "{num} Stationen gekickt\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "{num} Freunde gefunden\n"
msgstr "{num} neue Freunde gefunden\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
@ -247,3 +247,4 @@ msgstr "Minute"
msgid "second"
msgstr "Sekunde"

View File

@ -72,13 +72,13 @@ msgstr "Je suis triste"
#, fuzzy
msgid "Leave me alone ..."
msgstr "Je me sens si seul..."
msgstr "Lache moi..."
msgid "I'm mad at you!"
msgstr "Je t'en veux !"
msgid "I'm living the life!"
msgstr "Je vis la vie !"
msgstr "Je vis la belle vie !"
msgid "I pwn therefore I am."
msgstr "Je pwn donc je suis."
@ -114,7 +114,7 @@ msgstr "Hum... au revoir {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} est part ..."
msgstr "{name} est parti ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
@ -144,7 +144,7 @@ msgstr "Où est tout le monde ?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Fais la sieste pendant {secs}s..."
msgstr "Je fais la sieste pendant {secs}s..."
msgid "Zzzzz"
msgstr "Zzzzz"
@ -161,11 +161,11 @@ msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Attends pendant {secs}s..."
msgstr "J'attends pendant {secs}s..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Regarde autour ({secs}s)"
msgstr "J'observe ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
@ -193,11 +193,11 @@ msgstr "Je kick et je bannis {mac} !"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Cool, on a {num} nouveaux handshake{plural} !"
msgstr "Cool, on a {num} nouve(l/aux) handshake{plural} !"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "Tu as {num} nouveaux message{plural} !"
msgstr "Tu as {num} nouveau(x) message{plural} !"
msgid "Ops, something went wrong ... Rebooting ..."
msgstr "Oups, quelque chose s'est mal passé... Redémarrage..."
@ -208,18 +208,18 @@ msgstr "{num} stations kick\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "A {num} nouveaux amis\n"
msgstr "A fait {num} nouve(l/aux) ami(s)\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "A {num} handshakes\n"
msgid "Met 1 peer"
msgstr "1 pair rencontré"
msgstr "1 camarade rencontré"
#, python-brace-format
msgid "Met {num} peers"
msgstr "{num} pairs recontrés"
msgstr "{num} camarades recontrés"
#, python-brace-format
msgid ""

View File

@ -60,6 +60,10 @@ def on(event_name, *args, **kwargs):
def locked_cb(lock_name, cb, *args, **kwargs):
global locks
if lock_name not in locks:
locks[lock_name] = threading.Lock()
with locks[lock_name]:
cb(*args, *kwargs)

View File

@ -154,7 +154,7 @@ class AutoUpdate(plugins.Plugin):
self.lock = Lock()
def on_loaded(self):
if 'interval' not in self.options or ('interval' in self.options and self.options['interval'] is None):
if 'interval' not in self.options or ('interval' in self.options and not self.options['interval']):
logging.error("[update] main.plugins.auto-update.interval is not set")
return
self.ready = True

View File

@ -437,7 +437,7 @@ class BTTether(plugins.Plugin):
for device_opt in ['enabled', 'priority', 'scantime', 'search_order',
'max_tries', 'share_internet', 'mac', 'ip',
'netmask', 'interval']:
if device_opt not in options or (device_opt in options and options[device_opt] is None):
if device_opt not in options or (device_opt in options and not options[device_opt]):
logging.error("BT-TETHER: Please specify the %s for device %s.",
device_opt, device)
break
@ -448,7 +448,7 @@ class BTTether(plugins.Plugin):
# legacy
if 'mac' in self.options:
for opt in ['share_internet', 'mac', 'ip', 'netmask', 'interval']:
if opt not in self.options or (opt in self.options and self.options[opt] is None):
if opt not in self.options or (opt in self.options and not self.options[opt]):
logging.error("BT-TETHER: Please specify the %s in your config.yml.", opt)
return

View File

@ -26,7 +26,7 @@ class NetPos(plugins.Plugin):
self.lock = threading.Lock()
def on_loaded(self):
if 'api_key' not in self.options or ('api_key' in self.options and self.options['api_key'] is None):
if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']):
logging.error("NET-POS: api_key isn't set. Can't use mozilla's api.")
return

View File

@ -28,7 +28,7 @@ class OnlineHashCrack(plugins.Plugin):
"""
Gets called when the plugin gets loaded
"""
if 'email' not in self.options or ('email' in self.options and self.options['email'] is None):
if 'email' not in self.options or ('email' in self.options and not self.options['email']):
logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com")
return

View File

@ -0,0 +1,147 @@
import os
import logging
from threading import Lock
from functools import partial
from pwnagotchi import plugins
from pwnagotchi import reboot
def systemd_dropin(name, content):
if not name.endswith('.service'):
name = '%s.service' % name
dropin_dir = "/etc/systemd/system/%s.d/" % name
os.makedirs(dropin_dir, exist_ok=True)
with open(os.path.join(dropin_dir, "switcher.conf"), "wt") as dropin:
dropin.write(content)
systemctl("daemon-reload")
def systemctl(command, unit=None):
if unit:
os.system("/bin/systemctl %s %s" % (command, unit))
else:
os.system("/bin/systemctl %s" % command)
def run_task(name, options):
task_service_name = "switcher-%s-task.service" % name
# save all the commands to a shell script
script_dir = '/usr/local/bin/'
script_path = os.path.join(script_dir, 'switcher-%s.sh' % name)
os.makedirs(script_dir, exist_ok=True)
with open(script_path, 'wt') as script_file:
script_file.write('#!/bin/bash\n')
for cmd in options['commands']:
script_file.write('%s\n' % cmd)
os.system("chmod a+x %s" % script_path)
# here we create the service which runs the tasks
with open('/etc/systemd/system/%s' % task_service_name, 'wt') as task_service:
task_service.write("""
[Unit]
Description=Executes the tasks of the pwnagotchi switcher plugin
After=pwnagotchi.service bettercap.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=-/usr/local/bin/switcher-%s.sh
ExecStart=-/bin/rm /etc/systemd/system/%s
ExecStart=-/bin/rm /usr/local/bin/switcher-%s.sh
[Install]
WantedBy=multi-user.target
""" % (name, task_service_name, name))
if 'reboot' in options and options['reboot']:
# create a indication file!
# if this file is set, we want the switcher-tasks to run
open('/root/.switcher', 'a').close()
# add condition
systemd_dropin("pwnagotchi.service", """
[Unit]
ConditionPathExists=!/root/.switcher""")
systemd_dropin("bettercap.service", """
[Unit]
ConditionPathExists=!/root/.switcher""")
systemd_dropin(task_service_name, """
[Service]
ExecStart=-/bin/rm /root/.switcher
ExecStart=-/bin/rm /etc/systemd/system/switcher-reboot.timer""")
with open('/etc/systemd/system/switcher-reboot.timer', 'wt') as reboot_timer:
reboot_timer.write("""
[Unit]
Description=Reboot when time is up
ConditionPathExists=/root/.switcher
[Timer]
OnBootSec=%sm
Unit=reboot.target
[Install]
WantedBy=timers.target
""" % options['stopwatch'])
systemctl("daemon-reload")
systemctl("enable", "switcher-reboot.timer")
systemctl("enable", task_service_name)
reboot()
return
systemctl("daemon-reload")
systemctl("start", task_service_name)
class Switcher(plugins.Plugin):
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '0.0.1'
__name__ = 'switcher'
__license__ = 'GPL3'
__description__ = 'This plugin is a generic task scheduler.'
def __init__(self):
self.ready = False
self.lock = Lock()
def trigger(self, name, *args, **kwargs):
with self.lock:
function_name = name.lstrip('on_')
if function_name in self.tasks:
task = self.tasks[function_name]
# is this task enabled?
if 'enabled' not in task or ('enabled' in task and not task['enabled']):
return
run_task(function_name, task)
def on_loaded(self):
if 'tasks' in self.options and self.options['tasks']:
self.tasks = self.options['tasks']
else:
logging.debug('[switcher] No tasks found...')
return
logging.info("[switcher] is loaded.")
# create hooks
logging.debug("[switcher] creating hooks...")
methods = ['webhook', 'internet_available', 'ui_setup', 'ui_update',
'unload', 'display_setup', 'ready', 'ai_ready', 'ai_policy',
'ai_training_start', 'ai_training_step', 'ai_training_end',
'ai_best_reward', 'ai_worst_reward', 'free_channel',
'bored', 'sad', 'excited', 'lonely', 'rebooting', 'wait',
'sleep', 'wifi_update', 'unfiltered_ap_list', 'association',
'deauthentication', 'channel_hop', 'handshake', 'epoch',
'peer_detected', 'peer_lost']
for m in methods:
setattr(Switcher, 'on_%s' % m, partial(self.trigger, m))
logging.debug("[switcher] triggers are ready to fire...")

View File

@ -7,12 +7,14 @@
# For Raspberry Pi Zero Ups Power Expansion Board with Integrated Serial Port S3U4
# https://www.ebay.de/itm/For-Raspberry-Pi-Zero-Ups-Power-Expansion-Board-with-Integrated-Serial-Port-S3U4/323873804310
# https://www.aliexpress.com/item/32888533624.html
import logging
import struct
from pwnagotchi.ui.components import LabeledValue
from pwnagotchi.ui.view import BLACK
import pwnagotchi.ui.fonts as fonts
import pwnagotchi.plugins as plugins
import pwnagotchi
# TODO: add enable switch in config.yml an cleanup all to the best place
@ -63,4 +65,9 @@ class UPSLite(plugins.Plugin):
ui.remove_element('ups')
def on_ui_update(self, ui):
ui.set('ups', "%2i%%" % self.ups.capacity())
capacity = self.ups.capacity()
ui.set('ups', "%2i%%" % capacity)
if capacity <= self.options['shutdown']:
logging.info('[ups_lite] Empty battery (<= %s%%): shuting down' % self.options['shutdown'])
ui.update(force=True, new_data={'status': 'Battery exhausted, bye ...'})
pwnagotchi.shutdown()

View File

@ -1,13 +1,12 @@
import logging
import json
import yaml
import toml
import _thread
import pwnagotchi.plugins as plugins
from pwnagotchi import restart
from flask import abort
from flask import render_template_string
INDEX = """
<html>
<head>
@ -500,13 +499,13 @@ class WebConfig(plugins.Plugin):
elif request.method == "POST":
if path == "save-config":
try:
parsed_yaml = yaml.safe_load(str(request.get_json()))
with open('/etc/pwnagotchi/config.yml', 'w') as config_file:
yaml.safe_dump(parsed_yaml, config_file, encoding='utf-8',
allow_unicode=True, default_flow_style=False)
parsed_toml = toml.loads(request.get_json())
with open('/etc/pwnagotchi/config.toml') as config_file:
toml.dump(parsed_toml, config_file)
_thread.start_new_thread(restart, (self.mode,))
return "success"
except yaml.YAMLError as yaml_ex:
except Exception as ex:
logging.error(ex)
return "config error"
abort(404)

View File

@ -270,5 +270,16 @@
positionsLoaded = true;
drawPositions();
});
// get current position and set marker in interval
var myLocationMarker = {};
function onLocationFound(e) {
if (myLocationMarker != undefined) {
mymap.removeLayer(myLocationMarker);
};
myLocationMarker = L.marker(e.latlng).addTo(mymap);
setTimeout(function(){ mymap.locate(); }, 30000);
}
mymap.on('locationfound', onLocationFound);
mymap.locate({setView: true});
</script>
</body></html>

View File

@ -23,7 +23,7 @@ from dateutil.parser import parse
class Webgpsmap(plugins.Plugin):
__author__ = 'https://github.com/xenDE and https://github.com/dadav'
__version__ = '1.3.0'
__version__ = '1.3.1'
__name__ = 'webgpsmap'
__license__ = 'GPL3'
__description__ = 'a plugin for pwnagotchi that shows a openstreetmap with positions of ap-handshakes in your webbrowser'
@ -50,6 +50,7 @@ class Webgpsmap(plugins.Plugin):
"""
# defaults:
response_header_contenttype = None
response_header_contentdisposition = None
response_mimetype = "application/xhtml+xml"
if not self.ready:
try:
@ -90,6 +91,21 @@ class Webgpsmap(plugins.Plugin):
except Exception as error:
logging.error(f"[webgpsmap] error: {error}")
return
elif path.startswith('offlinemap'):
# for download an all-in-one html file with positions.json inside
try:
self.ALREADY_SENT = list()
json_data = json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']))
html_data = self.get_html()
html_data = html_data.replace('var positions = [];', 'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();')
response_data = bytes(html_data, "utf-8")
response_status = 200
response_mimetype = "application/xhtml+xml"
response_header_contenttype = 'text/html'
response_header_contentdisposition = 'attachment; filename=webgpsmap.html';
except Exception as error:
logging.error(f"[webgpsmap] offlinemap: error: {error}")
return
# elif path.startswith('/newest'):
# # returns all positions newer then timestamp
# response_data = bytes(json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']), newest_only=True), "utf-8")
@ -120,6 +136,8 @@ class Webgpsmap(plugins.Plugin):
r = Response(response=response_data, status=response_status, mimetype=response_mimetype)
if response_header_contenttype is not None:
r.headers["Content-Type"] = response_header_contenttype
if response_header_contentdisposition is not None:
r.headers["Content-Disposition"] = response_header_contentdisposition
return r
except Exception as error:
logging.error(f"[webgpsmap] error: {error}")

View File

@ -70,11 +70,11 @@ class WpaSec(plugins.Plugin):
"""
Gets called when the plugin gets loaded
"""
if 'api_key' not in self.options or ('api_key' in self.options and self.options['api_key'] is None):
if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']):
logging.error("WPA_SEC: API-KEY isn't set. Can't upload to wpa-sec.stanev.org")
return
if 'api_url' not in self.options or ('api_url' in self.options and self.options['api_url'] is None):
if 'api_url' not in self.options or ('api_url' in self.options and not self.options['api_url']):
logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.")
return

View File

@ -61,6 +61,9 @@ class Display(View):
def is_waveshare213d(self):
return self._implementation.name == 'waveshare213d'
def is_waveshare213bc(self):
return self._implementation.name == 'waveshare213bc'
def is_spotpear24inch(self):
return self._implementation.name == 'spotpear24inch'

View File

@ -10,6 +10,7 @@ from pwnagotchi.ui.hw.waveshare29inch import Waveshare29inch
from pwnagotchi.ui.hw.waveshare144lcd import Waveshare144lcd
from pwnagotchi.ui.hw.waveshare154inch import Waveshare154inch
from pwnagotchi.ui.hw.waveshare213d import Waveshare213d
from pwnagotchi.ui.hw.waveshare213bc import Waveshare213bc
from pwnagotchi.ui.hw.spotpear24inch import Spotpear24inch
def display_for(config):
@ -37,10 +38,10 @@ def display_for(config):
elif config['ui']['display']['type'] == 'waveshare27inch':
return Waveshare27inch(config)
elif config['ui']['display']['type'] == 'waveshare29inch':
return Waveshare29inch(config)
elif config['ui']['display']['type'] == 'waveshare144lcd':
return Waveshare144lcd(config)
@ -50,5 +51,8 @@ def display_for(config):
elif config['ui']['display']['type'] == 'waveshare213d':
return Waveshare213d(config)
elif config['ui']['display']['type'] == 'waveshare213bc':
return Waveshare213bc(config)
elif config['ui']['display']['type'] == 'spotpear24inch':
return Spotpear24inch(config)
return Spotpear24inch(config)

View File

@ -0,0 +1,378 @@
# *****************************************************************************
# * | File : epd2in13bc.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V4.0
# * | Date : 2019-06-20
# # | Info : python demo
# -----------------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import logging
from . import epdconfig
from PIL import Image
# Display resolution
EPD_WIDTH = 104
EPD_HEIGHT = 212
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
lut_vcomDC = [
0x00, 0x08, 0x00, 0x00, 0x00, 0x02,
0x60, 0x28, 0x28, 0x00, 0x00, 0x01,
0x00, 0x14, 0x00, 0x00, 0x00, 0x01,
0x00, 0x12, 0x12, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
]
lut_ww = [
0x40, 0x08, 0x00, 0x00, 0x00, 0x02,
0x90, 0x28, 0x28, 0x00, 0x00, 0x01,
0x40, 0x14, 0x00, 0x00, 0x00, 0x01,
0xA0, 0x12, 0x12, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bw = [
0x40, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03,
0x40, 0x0A, 0x01, 0x00, 0x00, 0x01,
0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_wb = [
0x80, 0x08, 0x00, 0x00, 0x00, 0x02,
0x90, 0x28, 0x28, 0x00, 0x00, 0x01,
0x80, 0x14, 0x00, 0x00, 0x00, 0x01,
0x50, 0x12, 0x12, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bb = [
0x80, 0x08, 0x00, 0x00, 0x00, 0x02,
0x90, 0x28, 0x28, 0x00, 0x00, 0x01,
0x80, 0x14, 0x00, 0x00, 0x00, 0x01,
0x50, 0x12, 0x12, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_vcom1 = [
0x00, 0x19, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
]
lut_ww1 = [
0x00, 0x19, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bw1 = [
0x80, 0x19, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_wb1 = [
0x40, 0x19, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bb1 = [
0x00, 0x19, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(10)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, 1)
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self):
logging.debug("e-Paper busy")
while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
epdconfig.delay_ms(100)
logging.debug("e-Paper busy release")
def TurnOnDisplay(self):
self.send_command(0x12)
epdconfig.delay_ms(10)
self.ReadBusy()
def init(self):
if (epdconfig.module_init() != 0):
return -1
logging.debug("e-Paper 2.13bc preboot Freeze recovery")
while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
epdconfig.delay_ms(100)
self.reset()
epdconfig.delay_ms(200)
self.send_command(0x01) # POWER SETTING
self.send_data(0x03)
self.send_data(0x00)
self.send_data(0x2b)
self.send_data(0x2b)
self.send_data(0x03)
epdconfig.delay_ms(200)
self.send_command(0x06) # BOOSTER_SOFT_START
self.send_data(0x17)
self.send_data(0x17)
self.send_data(0x17)
self.send_command(0x04) # POWER_ON
epdconfig.delay_ms(200)
self.send_command(0X50)
self.send_data(0xf7)
self.send_command(0X02) # power off
self.send_command(0X07) # deep sleep
self.send_data(0xA5)
epdconfig.GPIO.output(epdconfig.RST_PIN, 0)
epdconfig.GPIO.output(epdconfig.DC_PIN, 0)
epdconfig.GPIO.output(epdconfig.CS_PIN, 0)
#logging.debug("Reset, powerdown, voltage off done")
logging.debug("e-Paper is not frozen now :)")
self.reset()
self.send_command(0x01) # POWER SETTING
self.send_data(0x03)
self.send_data(0x00)
self.send_data(0x2b)
self.send_data(0x2b)
self.send_data(0x03)
self.send_command(0x06) # BOOSTER_SOFT_START
self.send_data(0x17)
self.send_data(0x17)
self.send_data(0x17)
self.send_command(0x04) # POWER_ON
logging.debug("e-Paper 2.13bc bootup busy")
while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
epdconfig.delay_ms(100)
# self.send_command(0x00) # PANEL_SETTING
# self.send_data(0x8F)
# self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING
# self.send_data(0xF0)
# self.send_command(0x61) # RESOLUTION_SETTING
# self.send_data(self.width & 0xff)
# self.send_data(self.height >> 8)
# self.send_data(self.height & 0xff)
self.send_command(0x00) # panel setting
self.send_data(0xbf) # LUT from OTP,128x296
self.send_data(0x0d) # VCOM to 0V fast
self.send_command(0x30) # PLL setting
self.send_data(0x3a) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ
self.send_command(0x61) # resolution setting
self.send_data(self.width & 0xff)
self.send_data((self.height >> 8) & 0xff)
self.send_data(self.height& 0xff)
self.send_command(0x82) # vcom_DC setting
self.send_data(0x28)
#self.Clear()
logging.debug("e-Paper booted")
return 0
def SetFullReg(self):
self.send_command(0x82)
self.send_data(0x00)
self.send_command(0X50)
self.send_data(0x97)
self.send_command(0x20) # vcom
for count in range(0, 44):
self.send_data(self.lut_vcomDC[count])
self.send_command(0x21) # ww --
for count in range(0, 42):
self.send_data(self.lut_ww[count])
self.send_command(0x22) # bw r
for count in range(0, 42):
self.send_data(self.lut_bw[count])
self.send_command(0x23) # wb w
for count in range(0, 42):
self.send_data(self.lut_wb[count])
self.send_command(0x24) # bb b
for count in range(0, 42):
self.send_data(self.lut_bb[count])
def getbuffer(self, image):
# logging.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width/8) * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
# logging.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if(imwidth == self.width and imheight == self.height):
logging.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif(imwidth == self.height and imheight == self.width):
logging.debug("Horizontal")
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8))
return buf
def display(self, imageblack, imagered):
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(imageblack[i])
self.send_command(0x92)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(imagered[i])
self.send_command(0x92)
self.send_command(0x12) # REFRESH
self.ReadBusy()
def pwndisplay(self, imageblack):
if (Image == None):
return
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0x00)
epdconfig.delay_ms(10)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(imageblack[i])
epdconfig.delay_ms(10)
self.SetFullReg()
self.TurnOnDisplay()
def Clear(self):
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0xFF)
self.send_command(0x92)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0xFF)
self.send_command(0x92)
self.send_command(0x12) # REFRESH
self.ReadBusy()
def pwnclear(self):
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0xFF)
epdconfig.delay_ms(10)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0xFF)
epdconfig.delay_ms(10)
self.SetFullReg()
self.TurnOnDisplay()
def sleep(self):
self.send_command(0x02) # POWER_OFF
self.ReadBusy()
self.send_command(0x07) # DEEP_SLEEP
self.send_data(0xA5) # check code
epdconfig.module_exit()
### END OF FILE ###

View File

@ -0,0 +1,154 @@
# /*****************************************************************************
# * | File : epdconfig.py
# * | Author : Waveshare team
# * | Function : Hardware underlying interface
# * | Info :
# *----------------
# * | This version: V1.0
# * | Date : 2019-06-21
# * | Info :
# ******************************************************************************
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import os
import logging
import sys
import time
class RaspberryPi:
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
def __init__(self):
import spidev
import RPi.GPIO
self.GPIO = RPi.GPIO
# SPI device, bus = 0, device = 0
self.SPI = spidev.SpiDev(0, 0)
def digital_write(self, pin, value):
self.GPIO.output(pin, value)
def digital_read(self, pin):
return self.GPIO.input(pin)
def delay_ms(self, delaytime):
time.sleep(delaytime / 1000.0)
def spi_writebyte(self, data):
self.SPI.writebytes(data)
def module_init(self):
self.GPIO.setmode(self.GPIO.BCM)
self.GPIO.setwarnings(False)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
self.SPI.max_speed_hz = 4000000
self.SPI.mode = 0b00
return 0
def module_exit(self):
logging.debug("spi end")
self.SPI.close()
logging.debug("close 5V, Module enters 0 power consumption ...")
self.GPIO.output(self.RST_PIN, 0)
self.GPIO.output(self.DC_PIN, 0)
self.GPIO.cleanup()
class JetsonNano:
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
def __init__(self):
import ctypes
find_dirs = [
os.path.dirname(os.path.realpath(__file__)),
'/usr/local/lib',
'/usr/lib',
]
self.SPI = None
for find_dir in find_dirs:
so_filename = os.path.join(find_dir, 'sysfs_software_spi.so')
if os.path.exists(so_filename):
self.SPI = ctypes.cdll.LoadLibrary(so_filename)
break
if self.SPI is None:
raise RuntimeError('Cannot find sysfs_software_spi.so')
import Jetson.GPIO
self.GPIO = Jetson.GPIO
def digital_write(self, pin, value):
self.GPIO.output(pin, value)
def digital_read(self, pin):
return self.GPIO.input(self.BUSY_PIN)
def delay_ms(self, delaytime):
time.sleep(delaytime / 1000.0)
def spi_writebyte(self, data):
self.SPI.SYSFS_software_spi_transfer(data[0])
def module_init(self):
self.GPIO.setmode(self.GPIO.BCM)
self.GPIO.setwarnings(False)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
self.SPI.SYSFS_software_spi_begin()
return 0
def module_exit(self):
logging.debug("spi end")
self.SPI.SYSFS_software_spi_end()
logging.debug("close 5V, Module enters 0 power consumption ...")
self.GPIO.output(self.RST_PIN, 0)
self.GPIO.output(self.DC_PIN, 0)
self.GPIO.cleanup()
if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'):
implementation = RaspberryPi()
else:
implementation = JetsonNano()
for func in [x for x in dir(implementation) if not x.startswith('_')]:
setattr(sys.modules[__name__], func, getattr(implementation, func))
### END OF FILE ###

View File

@ -0,0 +1,47 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare213bc(DisplayImpl):
def __init__(self, config):
super(Waveshare213bc, self).__init__(config, 'waveshare213bc')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 25)
self._layout['width'] = 212
self._layout['height'] = 104
self._layout['face'] = (0, 26)
self._layout['name'] = (5, 15)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (147, 0)
self._layout['line1'] = [0, 12, 212, 12]
self._layout['line2'] = [0, 92, 212, 92]
self._layout['friend_face'] = (0, 76)
self._layout['friend_name'] = (40, 78)
self._layout['shakes'] = (0, 93)
self._layout['mode'] = (187, 93)
self._layout['status'] = {
'pos': (91, 15),
'font': fonts.Medium,
'max': 20
}
return self._layout
def initialize(self):
logging.info("initializing waveshare 213bc display")
from pwnagotchi.ui.hw.libs.waveshare.v213bc.epd2in13bc import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.pwndisplay(buf)
def clear(self):
#pass
self._display.pwnclear()

View File

@ -12,6 +12,7 @@ import shutil
import gzip
import contextlib
import tempfile
import toml
import pwnagotchi
@ -32,15 +33,17 @@ def load_config(args):
if not os.path.exists(default_config_path):
os.makedirs(default_config_path)
ref_defaults_file = os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.yml')
ref_defaults_file = os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.toml')
ref_defaults_data = None
# check for a config.yml file on /boot/
if os.path.exists("/boot/config.yml"):
# logging not configured here yet
print("installing /boot/config.yml to %s ...", args.user_config)
# https://stackoverflow.com/questions/42392600/oserror-errno-18-invalid-cross-device-link
shutil.move("/boot/config.yml", args.user_config)
for boot_conf in ['/boot/config.yml', '/boot/config.toml']:
if os.path.exists(boot_conf):
# logging not configured here yet
print("installing %s to %s ...", boot_conf, args.user_config)
# https://stackoverflow.com/questions/42392600/oserror-errno-18-invalid-cross-device-link
shutil.move(boot_conf, args.user_config)
break
# check for an entire pwnagotchi folder on /boot/
if os.path.isdir('/boot/pwnagotchi'):
@ -54,6 +57,7 @@ def load_config(args):
shutil.copy(ref_defaults_file, args.config)
else:
# check if the user messed with the defaults
with open(ref_defaults_file) as fp:
ref_defaults_data = fp.read()
@ -66,18 +70,28 @@ def load_config(args):
# load the defaults
with open(args.config) as fp:
config = yaml.safe_load(fp)
config = toml.load(fp)
# load the user config
try:
if os.path.exists(args.user_config):
with open(args.user_config) as fp:
user_config = yaml.safe_load(fp)
# if the file is empty, safe_load will return None and merge_config will boom.
if user_config:
config = merge_config(user_config, config)
except yaml.YAMLError as ex:
print("There was an error processing the configuration file:\n%s " % ex)
user_config = None
# migrate
yaml_name = args.user_config.replace('.toml', '.yml')
if not os.path.exists(args.user_config) and os.path.exists(yaml_name):
# no toml found; convert yaml
logging.info('Old yaml-config found. Converting to toml...')
with open(args.user_config, 'w') as toml_file, open(yaml_name) as yaml_file:
user_config = yaml.safe_load(yaml_file)
# convert to toml but use loaded yaml
toml.dump(user_config, toml_file)
elif os.path.exists(args.user_config):
with open(args.user_config) as toml_file:
user_config = toml.load(toml_file)
if user_config:
config = merge_config(user_config, config)
except Exception as ex:
logging.error("There was an error processing the configuration file:\n%s ",ex)
exit(1)
# the very first step is to normalize the display name so we don't need dozens of if/elif around
@ -117,6 +131,9 @@ def load_config(args):
elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare_213d', 'waveshare213d'):
config['ui']['display']['type'] = 'waveshare213d'
elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare_213bc', 'waveshare213bc'):
config['ui']['display']['type'] = 'waveshare213bc'
elif config['ui']['display']['type'] in ('spotpear24inch'):
config['ui']['display']['type'] = 'spotpear24inch'

View File

@ -1,4 +1,4 @@
crypto==1.4.1
pycryptodome==3.9.4
requests==2.21.0
PyYAML==5.1
scapy==2.4.3
@ -18,4 +18,5 @@ gast==0.2.2
flask==1.0.2
flask-cors==3.0.7
flask-wtf==0.14.2
toml==0.10.0
python-dateutil==2.8.1