From d435ef2ba9448b29d84c64f05e283a96d81ddeb0 Mon Sep 17 00:00:00 2001
From: foreign-sub <51928805+foreign-sub@users.noreply.github.com>
Date: Sun, 29 Dec 2019 22:10:22 +0100
Subject: [PATCH 1/2] Add PACKER_VERSION to Makefile, bump packer to 1.4.5

Signed-off-by: foreign-sub <51928805+foreign-sub@users.noreply.github.com>
---
 Makefile | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 117e222..cbf4f6f 100644
--- a/Makefile
+++ b/Makefile
@@ -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

From 6075296884e7e7b47df46a5cce433d2d0b508679 Mon Sep 17 00:00:00 2001
From: dadav <33197631+dadav@users.noreply.github.com>
Date: Wed, 18 Dec 2019 18:58:28 +0100
Subject: [PATCH 2/2] Switch to toml

---
 bin/pwnagotchi                                |   6 +-
 pwnagotchi/agent.py                           |   2 +-
 pwnagotchi/defaults.toml                      | 205 ++++++++++++
 pwnagotchi/defaults.yml                       | 307 ------------------
 pwnagotchi/plugins/default/auto-update.py     |   2 +-
 pwnagotchi/plugins/default/bt-tether.py       |   4 +-
 pwnagotchi/plugins/default/net-pos.py         |   2 +-
 pwnagotchi/plugins/default/onlinehashcrack.py |   2 +-
 pwnagotchi/plugins/default/webcfg.py          |  13 +-
 pwnagotchi/plugins/default/wpa-sec.py         |   4 +-
 pwnagotchi/utils.py                           |  44 ++-
 requirements.txt                              |   1 +
 12 files changed, 252 insertions(+), 340 deletions(-)
 create mode 100644 pwnagotchi/defaults.toml
 delete mode 100644 pwnagotchi/defaults.yml

diff --git a/bin/pwnagotchi b/bin/pwnagotchi
index e990793..ac488b6 100755
--- a/bin/pwnagotchi
+++ b/bin/pwnagotchi
@@ -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)
diff --git a/pwnagotchi/agent.py b/pwnagotchi/agent.py
index b5e2d2f..bfaf1db 100644
--- a/pwnagotchi/agent.py
+++ b/pwnagotchi/agent.py
@@ -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
diff --git a/pwnagotchi/defaults.toml b/pwnagotchi/defaults.toml
new file mode 100644
index 0000000..5f1b95e
--- /dev/null
+++ b/pwnagotchi/defaults.toml
@@ -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"
+]
diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml
deleted file mode 100644
index 8dc8623..0000000
--- a/pwnagotchi/defaults.yml
+++ /dev/null
@@ -1,307 +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:
-        switcher:
-            enabled: false
-            tasks:
-                bored:
-                    enabled: false
-                    reboot: true
-                    commands:
-                      - systemctl start fisch # see https://github.com/dadav/fisch
-                    stopwatch: 15 # timeout of the task (in minutes)
-        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
-        ups_lite:
-          enabled: false
-          shutdown: 2  # Auto-shutdown when <= 2%
-    # 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
diff --git a/pwnagotchi/plugins/default/auto-update.py b/pwnagotchi/plugins/default/auto-update.py
index cb8bc79..8a0db9e 100644
--- a/pwnagotchi/plugins/default/auto-update.py
+++ b/pwnagotchi/plugins/default/auto-update.py
@@ -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
diff --git a/pwnagotchi/plugins/default/bt-tether.py b/pwnagotchi/plugins/default/bt-tether.py
index a0408ee..0054ba3 100644
--- a/pwnagotchi/plugins/default/bt-tether.py
+++ b/pwnagotchi/plugins/default/bt-tether.py
@@ -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
 
diff --git a/pwnagotchi/plugins/default/net-pos.py b/pwnagotchi/plugins/default/net-pos.py
index 8158437..12c3492 100644
--- a/pwnagotchi/plugins/default/net-pos.py
+++ b/pwnagotchi/plugins/default/net-pos.py
@@ -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
 
diff --git a/pwnagotchi/plugins/default/onlinehashcrack.py b/pwnagotchi/plugins/default/onlinehashcrack.py
index 11b37f9..534ddc4 100644
--- a/pwnagotchi/plugins/default/onlinehashcrack.py
+++ b/pwnagotchi/plugins/default/onlinehashcrack.py
@@ -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
 
diff --git a/pwnagotchi/plugins/default/webcfg.py b/pwnagotchi/plugins/default/webcfg.py
index 40f7477..d870953 100644
--- a/pwnagotchi/plugins/default/webcfg.py
+++ b/pwnagotchi/plugins/default/webcfg.py
@@ -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)
diff --git a/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py
index 4c4dac3..373789a 100644
--- a/pwnagotchi/plugins/default/wpa-sec.py
+++ b/pwnagotchi/plugins/default/wpa-sec.py
@@ -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
 
diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py
index c7ac6d5..99dc936 100644
--- a/pwnagotchi/utils.py
+++ b/pwnagotchi/utils.py
@@ -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
diff --git a/requirements.txt b/requirements.txt
index 1cb7d5e..9a93bdc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,3 +18,4 @@ gast==0.2.2
 flask==1.0.2
 flask-cors==3.0.7
 flask-wtf==0.14.2
+toml==0.10.0