Contributing
In this site, we’ll explain how to contribute to the Pwnagotchi community! Do you want to add a feature? Code a plugin! While beginners might fear tinkering with their Pwnagotchi, this is where the ultimate freedom begins.
This contributing page is not for this wiki, its for the Pwnagotchi framework in general. However if you made a new plugin or image, which you want to be displayed on this site, feel free to open a pull request or reach out to one of the developers.
Developing your own plugin
If you want to develop your own plugin, you have the following callbacks availaible:
Callback
Description
on_ai_best_reward
Called when the AI got the best reward so far.
on_ai_policy
Called when the AI finds a new set of parameters.
on_ai_ready
Called when the AI finished loading.
on_ai_training_end
Called when the AI has done training.
on_ai_training_start
Called when the AI starts training for a given number of epochs.
on_ai_training_step
Called after the AI completed a training epoch.
on_ai_worst_reward
Called when the AI got the worst reward so far.
on_association
Called when the agent is sending an association frame.
on_bored
Called when the status is set to bored.
on_channel_hop
callend when the agent is tuning on a specific channel.
on_config_changed
This will be triggered if the config has changed (also right after on_loaded).
on_deauthentication
Called when the agent is deauthenticating a client station from an AP.
on_display_setup
Called when the hardware display setup is done, display is an hardware specific object.
on_epoch
Called when an epoch is over (where an epoch is a single loop of the main algorithm).
on_excited
Called when the status is set to excited.
on_free_channel
Called when a non overlapping wifi channel is found to be free.
on_handshake
Called when a new handshake is captured, access_point and client_station are json objects if the agent could match the BSSIDs to the current list, otherwise they are just the strings of the BSSIDs.
on_internet_available
This will be triggered every few seconds during the time pwnagotchi has internet.
on_loaded
The plugin got loaded and is enabled.
on_lonely
Called when the status is set to lonely.
on_peer_detected
Called when a new peer is detected.
on_peer_lost
Called when a known peer is lost.
on_ready
Called when everything is ready and the main loop is about to start.
on_rebooting
Called when the agent is rebooting the board.
on_sad
Called when the status is set to sad.
on_sleep
Called when the agent is sleeping for t seconds.
on_ui_setup
Called to setup the ui elements.
on_ui_update
Called when the ui is updated.
on_unfiltered_ap_list
Called when the agent refreshed an unfiltered access point list this list contains all access points that were detected BEFORE filtering.
on_unload
This will be triggered if the plugin gets unloaded (e.g. the user toggled the enable/disable switch). You should remove unneeded ui-elements here.
on_wait
Called when the agent is waiting for t seconds.
on_webhook
You can provide some web-functionality here. Will be triggered if the user opens /plugins/<pluginname>
.
on_wifi_update
Called when the agent refreshed its access points list.
Exampleplugin
To illustrate how easy it is to add additional functionality via the plugin system, here is the code for the GPS plugin (gps.py
):
import logging
import json
import os
import pwnagotchi.plugins as plugins
class GPS(plugins.Plugin):
__author__ = '[email protected]'
__version__ = '1.0.0'
__license__ = 'GPL3'
__description__ = 'Save GPS coordinates whenever an handshake is captured.'
def __init__(self):
self.running = False
def on_loaded(self):
logging.info("gps plugin loaded for %s" % self.options['device'])
def on_ready(self, agent):
if os.path.exists(self.options['device']):
logging.info("enabling gps bettercap's module for %s" % self.options['device'])
try:
agent.run('gps off')
except:
pass
agent.run('set gps.device %s' % self.options['device'])
agent.run('set gps.speed %d' % self.options['speed'])
agent.run('gps on')
self.running = True
else:
logging.warning("no GPS detected")
def on_handshake(self, agent, filename, access_point, client_station):
if self.running:
info = agent.session()
gps = info['gps']
gps_filename = filename.replace('.pcap', '.gps.json')
logging.info("saving GPS to %s (%s)" % (gps_filename, gps))
with open(gps_filename, 'w+t') as fp:
json.dump(gps, fp)
Pwnagotchi’s developement environment is Raspbian + nexmon patches for monitor mode, or any Linux with a monitor mode enabled interface (if you tune config.toml
).
Do not try with Kali on the Raspberry Pi 0 W, it is compiled without hardware floating point support and TensorFlow is simply not available for it, use Raspbian.
Adding a new Display
Currently Pwnagotchi supports several displays and adding support for new ones is very easy! All you have to do is copying the specific Python libraries of the hardware into this folder and then create a new class in its parent folder that implements the methods of the following abstract class:
class DisplayImpl(object):
def __init__(self, config, name):
self.name = name
self.config = config['ui']['display']
self._layout = {
'width': 0,
'height': 0,
'face': (0, 0),
'name': (0, 0),
'channel': (0, 0),
'aps': (0, 0),
'uptime': (0, 0),
'line1': (0, 0),
'line2': (0, 0),
'friend_face': (0, 0),
'friend_name': (0, 0),
'shakes': (0, 0),
'mode': (0, 0),
# status is special :D
'status': {
'pos': (0, 0),
'font': fonts.Medium,
'max': 20
}
}
def layout(self):
raise NotImplementedError
def initialize(self):
raise NotImplementedError
def render(self, canvas):
raise NotImplementedError
def clear(self):
raise NotImplementedError
For instance, the pwnagotchi/ui/hw/oledhat.py file which supports this hat looks like this:
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class OledHat(DisplayImpl):
def __init__(self, config):
super(OledHat, self).__init__(config, 'oledhat')
self._display = None
def layout(self):
fonts.setup(8, 8, 8, 8)
self._layout['width'] = 128
self._layout['height'] = 64
self._layout['face'] = (0, 32)
self._layout['name'] = (0, 10)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (25, 0)
self._layout['uptime'] = (65, 0)
self._layout['line1'] = [0, 9, 128, 9]
self._layout['line2'] = [0, 53, 128, 53]
self._layout['friend_face'] = (0, 41)
self._layout['friend_name'] = (40, 43)
self._layout['shakes'] = (0, 53)
self._layout['mode'] = (103, 10)
self._layout['status'] = {
'pos': (30, 18),
'font': fonts.Small,
'max': 18
}
return self._layout
def initialize(self):
logging.info("initializing oledhat display")
from pwnagotchi.ui.hw.libs.waveshare.oledhat.epd import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
def clear(self):
self._display.clear()
Creating an Image
Linux
If you want to create a custom image for testing, developing or just hacking, you will need a GNU/Linux computer and the binaries for curl
, git
, make
, unzip
, go
, qemu-user-static
and kpartx
. The Makefile will also temporarily install packer and use sudo
as needed.
To create a zip file with the image and one with its sha256 checksum, just run:
make image
To remove the generated files:
sudo make clean
Windows
Download Win32Diskimager
Launch the program and select an .img file which you dont need anymore (or download one from Github). The select the BOOT drive of your sd-card and hit read. Once that is done the image you chose prior is now an exact copy of your current setup. Flash it on another sd-card or share it! Adding a Language
Contributing a new translation
If you want to contribute a new translation of Pwnagotchi’s status messages for the UI, do the following:
- Copy the language template (
voice.pot
); the template should NOT be changed manually.
./scripts/language.sh add <lang> (e.g. "de")
Now the user changes the file
pwnagotchi/locale/<lang>/LC_MESSAGES/voice.po
- The important part: be sure to change the
msgstr
part, NOT themsgid
part!
- The important part: be sure to change the
Now you’ll need to compile it; this will create the
.mo
files:
./scripts/language.sh compile <lang>
Updating an existing translation
Sometimes we change old or add new status messages in Pwnagotchi’s UI. If that’s happened and something in the voice.py
the code has changed, users can submit updated translations using the following procedure:
- Update the template and merges it with the already translated po-file:
./scripts/language.sh update <lang>
- Now you need to
- Look for
fuzzy
marked strings in the file pwnagotchi/locale//LC_MESSAGES/voice.po - Add your new/changed translation
- Remove the
fuzzy
string afterwards
- Look for
- Recompile the
.mo
file
./scripts/language.sh compile <lang>
Afterwards you can either compile it to a custom image, or submit it, in a pull request, to one ot multiple github image repos of your choice. After some time the developer/developers responsible for the repo should see your commit and hopefully add your translation.