diff --git a/README.md b/README.md
index ff8a934..ff02349 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,34 @@ The UI is available either via display if installed, or via http://pwnagotchi.lo
 * **PWND**: Number of handshakes captured in this session and number of unique networks we own at least one handshake of, from the beginning.
 * **AUTO**: This indicates that the algorithm is running with AI disabled (or still loading), it disappears once the AI dependencies have been bootrapped and the neural network loaded.
 
+#### Languages
+
+Pwnagotchi is able to speak multiple languages!! Currently supported is:
+
+* **english** (default)
+* german
+
+If you want to add a language use the `language.sh` script.
+If you want to add for example the language **italian** you would type:
+
+```shell
+./scripts/language.sh add it
+# Now make your changes to the file
+# sdcard/scripts/rootfs/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po
+./scripts/language.sh compile it
+# DONE
+```
+
+If you changed the `voice.py`- File, the translations need an update. Do it like this:
+
+```shell
+./scripts/language.sh update it
+# Now make your changes to the file (changed lines are marked with "fuzzy")
+# sdcard/scripts/rootfs/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po
+./scripts/language.sh compile it
+# DONE
+```
+
 ### Random Info
 
 - `hostname` sets the unit name.
diff --git a/scripts/create_sibling.sh b/scripts/create_sibling.sh
index 81bff79..d9f1027 100755
--- a/scripts/create_sibling.sh
+++ b/scripts/create_sibling.sh
@@ -195,6 +195,9 @@ function provision_raspbian() {
   # slows down boot
   systemctl disable apt-daily.timer apt-daily.service apt-daily-upgrade.timer apt-daily-upgrade.service
 
+  # unecessary services
+  systemctl disable triggerhappy bluetooth wpa_supplicant
+
 EOF
   sed -i'' 's/^#//g' etc/ld.so.preload
   cd "${REPO_DIR}"
@@ -213,7 +216,7 @@ usage: $0 [OPTIONS]
     -i <file>    # Provide the path of an already downloaded raspbian image
     -o <file>    # Name of the img-file (default: pwnagotchi.img)
     -s <size>    # Size which should be added to second partition (in Gigabyte) (default: 4)
-    -v <version> # Version of raspbian (Supported: $SUPPORTED_RASPBIAN_VERSIONS; default: latest)
+    -v <version> # Version of raspbian (Supported: ${SUPPORTED_RASPBIAN_VERSIONS[*]}; default: latest)
     -p           # Only run provisioning (assumes the image is already mounted)
     -d           # Only run dependencies checks
     -h           # Show this help
diff --git a/scripts/language.sh b/scripts/language.sh
new file mode 100755
index 0000000..c56b784
--- /dev/null
+++ b/scripts/language.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+set -eu
+
+DEPENDENCIES=( 'xgettext' 'msgfmt' 'msgmerge' )
+COMMANDS=( 'add' 'update' 'delete' 'compile' )
+
+REPO_DIR="$(dirname "$(dirname "$(realpath "$0")")")"
+LOCALE_DIR="${REPO_DIR}/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale"
+VOICE_FILE="${REPO_DIR}/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py"
+
+function usage() {
+cat <<EOF
+
+usage: $0 <command> [options]
+
+  Commands:
+    add <language>
+    delete <language>
+    compile <language>
+    update <language>
+
+EOF
+}
+
+for REQ in "${DEPENDENCIES[@]}"; do
+  if ! type "$REQ" >/dev/null 2>&1; then
+    echo "Dependency check failed for ${REQ}"
+    exit 1
+  fi
+done
+
+
+if [[ ! "${COMMANDS[*]}" =~ $1 ]]; then
+  usage
+fi
+
+
+function add_lang() {
+  mkdir -p "$LOCALE_DIR/$1/LC_MESSAGES"
+  cp -n "$LOCALE_DIR/voice.pot" "$LOCALE_DIR/$1/LC_MESSAGES/voice.po"
+}
+
+function del_lang() {
+  # set -eu is present; so not dangerous
+  rm -rf "$LOCALE_DIR/$1"
+}
+
+function comp_lang() {
+  msgfmt -o  "$LOCALE_DIR/$1/LC_MESSAGES/voice.mo"  "$LOCALE_DIR/$1/LC_MESSAGES/voice.po"
+}
+
+function update_lang() {
+  xgettext -d voice -o "$LOCALE_DIR/voice.pot" "$VOICE_FILE"
+  msgmerge --update "$LOCALE_DIR/$1/LC_MESSAGES/voice.po" "$LOCALE_DIR/voice.pot"
+}
+
+case "$1" in
+  add)
+    add_lang "$2"
+    ;;
+  delete)
+    del_lang "$2"
+    ;;
+  compile)
+    comp_lang "$2"
+    ;;
+  update)
+    update_lang "$2"
+    ;;
+esac
diff --git a/scripts/linux_connection_share.sh b/scripts/linux_connection_share.sh
index 57fe718..78df842 100755
--- a/scripts/linux_connection_share.sh
+++ b/scripts/linux_connection_share.sh
@@ -7,12 +7,12 @@ USB_IFACE_NET=10.0.0.0/24
 # host interface to use for upstream connection
 UPSTREAM_IFACE=${2:-enxe4b97aa99867}
 
-ip addr add $USB_IFACE_IP/24 dev $USB_IFACE
-ip link set $USB_IFACE up
+ip addr add "$USB_IFACE_IP/24" dev "$USB_IFACE"
+ip link set "$USB_IFACE" up
 
-iptables -A FORWARD -o $UPSTREAM_IFACE -i $USB_IFACE -s $USB_IFACE_NET -m conntrack --ctstate NEW -j ACCEPT
+iptables -A FORWARD -o "$UPSTREAM_IFACE" -i "$USB_IFACE" -s "$USB_IFACE_NET" -m conntrack --ctstate NEW -j ACCEPT
 iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
 iptables -t nat -F POSTROUTING
-iptables -t nat -A POSTROUTING -o $UPSTREAM_IFACE -j MASQUERADE
+iptables -t nat -A POSTROUTING -o "$UPSTREAM_IFACE" -j MASQUERADE
 
 echo 1 > /proc/sys/net/ipv4/ip_forward
diff --git a/sdcard/boot/config.txt b/sdcard/boot/config.txt
index 98231bc..4d69af8 100755
--- a/sdcard/boot/config.txt
+++ b/sdcard/boot/config.txt
@@ -1195,3 +1195,8 @@ dtoverlay=dwc2
 dtparam=spi=on
 dtoverlay=spi1-3cs
 
+# Disable bluetooth
+dtoverlay=pi3-disable-bt
+# Disable audio
+dtparam=audio=off
+
diff --git a/sdcard/rootfs/etc/network/interfaces b/sdcard/rootfs/etc/network/interfaces
index 1d8ee6b..2c84f3c 100644
--- a/sdcard/rootfs/etc/network/interfaces
+++ b/sdcard/rootfs/etc/network/interfaces
@@ -5,6 +5,9 @@ iface lo inet loopback
 allow-hotplug wlan0
 iface wlan0 inet static
 
+allow-hotplug eth0
+iface eth0 inet dhcp
+
 allow-hotplug usb0
 iface usb0 inet static
         address 10.0.0.2
diff --git a/sdcard/rootfs/etc/rc.local b/sdcard/rootfs/etc/rc.local
index 15fb894..e93d2d8 100755
--- a/sdcard/rootfs/etc/rc.local
+++ b/sdcard/rootfs/etc/rc.local
@@ -10,5 +10,7 @@
 # bits.
 #
 # By default this script does nothing.
+# Powersave (Disable HDMI) ~30ma
+/opt/vc/bin/tvservice -o
 /root/pwnagotchi/scripts/startup.sh &
 exit 0
diff --git a/sdcard/rootfs/root/pwnagotchi/config.yml b/sdcard/rootfs/root/pwnagotchi/config.yml
index 9a2a146..ca583ec 100644
--- a/sdcard/rootfs/root/pwnagotchi/config.yml
+++ b/sdcard/rootfs/root/pwnagotchi/config.yml
@@ -1,5 +1,7 @@
 # main algorithm configuration
 main:
+    # currently implemented: en (default), de
+    lang: en
     # monitor interface to use
     iface: mon0
     # command to run to bring the mon interface up in case it's not up already
@@ -93,9 +95,11 @@ ui:
         enabled: true
         rotation: 180
         # Possible options inkyphat/inky, papirus/papi or waveshare/ws
-        type: 'waveshare'
+        type: 'waveshare_2'
         # Possible options red/yellow/black (black used for monocromatic displays)
         color: 'black'
+        # How often to do a full refresh 0 all the time, -1 never
+        refresh: 50
         video:
             enabled: true
             address: '10.0.0.2'
diff --git a/sdcard/rootfs/root/pwnagotchi/data/screenrc.auto b/sdcard/rootfs/root/pwnagotchi/data/screenrc.auto
index 0d775f9..9319672 100644
--- a/sdcard/rootfs/root/pwnagotchi/data/screenrc.auto
+++ b/sdcard/rootfs/root/pwnagotchi/data/screenrc.auto
@@ -5,6 +5,7 @@ defscrollback 1024
 startup_message off
 altscreen on
 autodetach on
+zombie kr
 
 activity        "activity in %n (%t)"
 bell_msg        "bell in %n (%t)"
diff --git a/sdcard/rootfs/root/pwnagotchi/data/screenrc.manual b/sdcard/rootfs/root/pwnagotchi/data/screenrc.manual
index 4e66adf..1d62528 100644
--- a/sdcard/rootfs/root/pwnagotchi/data/screenrc.manual
+++ b/sdcard/rootfs/root/pwnagotchi/data/screenrc.manual
@@ -5,6 +5,7 @@ defscrollback 1024
 startup_message off
 altscreen on
 autodetach on
+zombie kr
 
 activity        "activity in %n (%t)"
 bell_msg        "bell in %n (%t)"
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/blink.sh b/sdcard/rootfs/root/pwnagotchi/scripts/blink.sh
index 48345ef..b7e8977 100755
--- a/sdcard/rootfs/root/pwnagotchi/scripts/blink.sh
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/blink.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-for i in `seq 1 $1`;
+for i in $(seq 1 "$1");
 do
 	echo 0 >/sys/class/leds/led0/brightness
 	sleep 0.3
@@ -10,3 +10,8 @@ done
 
 echo 0 >/sys/class/leds/led0/brightness
 sleep 0.3
+
+# Powersave options
+# Disable power LED ~30ma
+echo none >/sys/class/leds/led0/trigger
+echo 1 >/sys/class/leds/led0/brightness
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/main.py b/sdcard/rootfs/root/pwnagotchi/scripts/main.py
index 6f44622..de44012 100755
--- a/sdcard/rootfs/root/pwnagotchi/scripts/main.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/main.py
@@ -8,7 +8,7 @@ import core
 import pwnagotchi
 
 from pwnagotchi.log import SessionParser
-import pwnagotchi.voice as voice
+from pwnagotchi.voice import Voice
 from pwnagotchi.agent import Agent
 from pwnagotchi.ui.display import Display
 
@@ -74,7 +74,7 @@ if args.do_manual:
                 auth.set_access_token(config['twitter']['access_token_key'], config['twitter']['access_token_secret'])
                 api = tweepy.API(auth)
 
-                tweet = voice.on_log_tweet(log)
+                tweet = Voice(lang=config['main']['lang']).on_log_tweet(log)
                 api.update_with_media(filename=picture, status=tweet)
                 log.save_session_id()
 
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/train.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/train.py
index f081681..97e9a6d 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/train.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/train.py
@@ -54,7 +54,7 @@ class Stats(object):
 
     def load(self):
         with self._lock:
-            if os.path.exists(self.path):
+            if os.path.exists(self.path) and os.path.getsize(self.path) > 0:
                 core.log("[ai] loading %s" % self.path)
                 with open(self.path, 'rt') as fp:
                     obj = json.load(fp)
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.mo b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.mo
new file mode 100644
index 0000000..dcede01
Binary files /dev/null and b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.mo differ
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.po
new file mode 100644
index 0000000..c85e254
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.po
@@ -0,0 +1,344 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: 0.0.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-09-29 13:34+0200\n"
+"PO-Revision-Date: 2019-09-29 14:00+0200\n"
+"Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n"
+"Language-Team: DE <33197631+dadav@users.noreply.github.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: voice.py:16
+msgid "ZzzzZZzzzzZzzz"
+msgstr ""
+
+#: voice.py:21
+msgid ""
+"Hi, I'm Pwnagotchi!\n"
+"Starting ..."
+msgstr ""
+"Hi, ich bin\n"
+"ein Pwnagotchi!\n"
+"Starte ..."
+
+#: voice.py:22
+msgid ""
+"New day, new hunt,\n"
+"new pwns!"
+msgstr ""
+"Neuer Tag, neue Jagd,\n"
+"neue Pwns!"
+
+#: voice.py:23
+msgid "Hack the Planet!"
+msgstr "Hack den Planet!"
+
+#: voice.py:28
+msgid "AI ready."
+msgstr "KI bereit."
+
+#: voice.py:29
+msgid ""
+"The neural network\n"
+"is ready."
+msgstr ""
+"Das neurale Netz\n"
+"ist bereit."
+
+#: voice.py:39
+#, python-brace-format
+msgid ""
+"Hey, channel {channel} is\n"
+"free! Your AP will\n"
+"say thanks."
+msgstr ""
+"Hey, Channel {channel} ist\n"
+"frei! Dein AP wird\n"
+"es dir danken."
+
+#: voice.py:44
+msgid "I'm bored ..."
+msgstr "Mir ist langweilig..."
+
+#: voice.py:45
+msgid "Let's go for a walk!"
+msgstr "Lass uns laufen gehen!"
+
+#: voice.py:49
+msgid ""
+"This is the best\n"
+"day of my life!"
+msgstr ""
+"Das ist der beste\n"
+"Tag meines Lebens."
+
+#: voice.py:53
+msgid "Shitty day :/"
+msgstr "Scheis Tag :/"
+
+#: voice.py:58
+msgid "I'm extremely bored ..."
+msgstr "Mir ist sau langweilig..."
+
+#: voice.py:59
+msgid "I'm very sad ..."
+msgstr "Ich bin sehr traurig..."
+
+#: voice.py:60
+msgid "I'm sad"
+msgstr "Ich bin traurig"
+
+#: voice.py:66
+msgid "I'm living the life!"
+msgstr "Ich lebe das Leben!"
+
+#: voice.py:67
+msgid "I pwn therefore I am."
+msgstr "Ich pwne, also bin ich."
+
+#: voice.py:68
+msgid "So many networks!!!"
+msgstr "So viele Netwerke!!!"
+
+#: voice.py:69
+msgid ""
+"I'm having so much\n"
+"fun!"
+msgstr ""
+"Ich habe sooo viel\n"
+"Spaß!"
+
+#: voice.py:70
+msgid ""
+"My crime is that of\n"
+"curiosity ..."
+msgstr ""
+"Mein Verbrechen ist\n"
+"das der Neugier ..."
+
+#: voice.py:75
+#, python-brace-format
+msgid ""
+"Hello\n"
+"{name}!\n"
+"Nice to meet you. {name}"
+msgstr ""
+"Hallo {name},\n"
+"Nett Dich\n"
+"kennenzulernen."
+
+#: voice.py:76
+#, python-brace-format
+msgid ""
+"Unit\n"
+"{name}\n"
+"is nearby! {name}"
+msgstr ""
+"Gerät {name}\n"
+"ist in der\n"
+"nähe!!"
+
+#: voice.py:81
+#, python-brace-format
+msgid ""
+"Uhm ...\n"
+"goodbye\n"
+"{name}"
+msgstr ""
+"Uhm ...\n"
+"tschüß\n"
+"{name}"
+
+#: voice.py:82
+#, python-brace-format
+msgid ""
+"{name}\n"
+"is gone ..."
+msgstr ""
+"{name}\n"
+"ist weg ..."
+
+#: voice.py:87
+#, python-brace-format
+msgid ""
+"Whoops ...\n"
+"{name}\n"
+"is gone."
+msgstr ""
+"Whoops ...\n"
+"{name}\n"
+"ist weg."
+
+#: voice.py:88
+#, python-brace-format
+msgid ""
+"{name}\n"
+"missed!"
+msgstr ""
+"{name}\n"
+"verpasst!"
+
+#: voice.py:89
+msgid "Missed!"
+msgstr "Verpasst!"
+
+#: voice.py:94
+msgid ""
+"Nobody wants to\n"
+"play with me ..."
+msgstr ""
+"Niemand will mit\n"
+"mir spielen ..."
+
+#: voice.py:95
+msgid "I feel so alone ..."
+msgstr ""
+"Ich fühl mich\n"
+"so alleine ..."
+
+#: voice.py:96
+msgid "Where's everybody?!"
+msgstr "Wo sind denn alle?"
+
+#: voice.py:101
+#, python-brace-format
+msgid "Napping for {secs}s ..."
+msgstr "Schlafe für {secs}s"
+
+#: voice.py:102
+msgid "Zzzzz"
+msgstr ""
+
+#: voice.py:103
+#, python-brace-format
+msgid "ZzzZzzz ({secs}s)"
+msgstr ""
+
+#: voice.py:112
+#, python-brace-format
+msgid "Waiting for {secs}s ..."
+msgstr "Warte für {secs}s ..."
+
+#: voice.py:114
+#, python-brace-format
+msgid "Looking around ({secs}s)"
+msgstr "Schaue mich um ({secs}s)"
+
+#: voice.py:121
+#, python-brace-format
+msgid ""
+"Hey\n"
+"{what}\n"
+"let's be friends!"
+msgstr ""
+"Hey\n"
+"{what}\n"
+"lass uns Freunde sein!"
+
+#: voice.py:122
+#, python-brace-format
+msgid ""
+"Associating to\n"
+"{what}"
+msgstr ""
+"Verbinde mit\n"
+"{what}"
+
+#: voice.py:123
+#, python-brace-format
+msgid ""
+"Yo\n"
+"{what}!"
+msgstr ""
+
+#: voice.py:128
+#, python-brace-format
+msgid ""
+"Just decided that\n"
+"{mac}\n"
+"needs no WiFi!"
+msgstr ""
+"Habe gerade entschieden,\n"
+"dass {mac}\n"
+"kein WiFi brauch!"
+
+#: voice.py:129
+#, python-brace-format
+msgid ""
+"Deauthenticating\n"
+"{mac}"
+msgstr ""
+"Deauthentifiziere\n"
+"{mac}"
+
+#: voice.py:130
+#, python-brace-format
+msgid ""
+"Kickbanning\n"
+"{mac}!"
+msgstr ""
+"Kicke\n"
+"{mac}!"
+
+#: voice.py:135
+#, python-brace-format
+msgid ""
+"Cool, we got {num}\n"
+"new handshake{plural}!"
+msgstr ""
+"Cool, wir haben {num}\n"
+"neue Handshake{plural}!"
+
+#: voice.py:139
+msgid ""
+"Ops, something\n"
+"went wrong ...\n"
+"Rebooting ..."
+msgstr ""
+"Ops, da ist etwas\n"
+"schief gelaufen ...\n"
+"Starte neu ..."
+
+#: voice.py:143
+#, python-brace-format
+msgid "Kicked {num} stations\n"
+msgstr "{num} Stationen gekicked\n"
+
+#: voice.py:144
+#, python-brace-format
+msgid "Made {num} new friends\n"
+msgstr "{num} Freunde gefunden\n"
+
+#: voice.py:145
+#, python-brace-format
+msgid "Got {num} handshakes\n"
+msgstr "{num} Handshakes aufgez.\n"
+
+#: voice.py:147
+msgid "Met 1 peer"
+msgstr "1 Peer getroffen."
+
+#: voice.py:149
+#, python-brace-format
+msgid "Met {num} peers"
+msgstr "{num} Peers getroffen"
+
+#: voice.py:154
+#, python-brace-format
+msgid ""
+"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
+"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
+"#pwnlog #pwnlife #hacktheplanet #skynet"
+msgstr ""
+"Ich war {duration} am Pwnen und habe {deauthed} Clients gekickt! Außerdem habe ich "
+"{associated} neue Freunde getroffen und {handshakes} Handshakes gefressen! #pwnagotchi "
+"#pwnlog #pwnlife #hacktheplanet #skynet"
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/voice.pot b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/voice.pot
new file mode 100644
index 0000000..671e44d
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/voice.pot
@@ -0,0 +1,288 @@
+# pwnigotchi voice data
+# Copyright (C) 2019
+# This file is distributed under the same license as the pwnagotchi package.
+# FIRST AUTHOR <33197631+dadav@users.noreply.github.com>, 2019.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: 0.0.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-09-29 13:42+0200\n"
+"PO-Revision-Date: 2019-09-29 14:00+0200\n"
+"Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n"
+"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
+"Language: english\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: voice.py:16
+msgid "ZzzzZZzzzzZzzz"
+msgstr ""
+
+#: voice.py:21
+msgid ""
+"Hi, I'm Pwnagotchi!\n"
+"Starting ..."
+msgstr ""
+
+#: voice.py:22
+msgid ""
+"New day, new hunt,\n"
+"new pwns!"
+msgstr ""
+
+#: voice.py:23
+msgid "Hack the Planet!"
+msgstr ""
+
+#: voice.py:28
+msgid "AI ready."
+msgstr ""
+
+#: voice.py:29
+msgid ""
+"The neural network\n"
+"is ready."
+msgstr ""
+
+#: voice.py:39
+#, python-brace-format
+msgid ""
+"Hey, channel {channel} is\n"
+"free! Your AP will\n"
+"say thanks."
+msgstr ""
+
+#: voice.py:44
+msgid "I'm bored ..."
+msgstr ""
+
+#: voice.py:45
+msgid "Let's go for a walk!"
+msgstr ""
+
+#: voice.py:49
+msgid ""
+"This is the best\n"
+"day of my life!"
+msgstr ""
+
+#: voice.py:53
+msgid "Shitty day :/"
+msgstr ""
+
+#: voice.py:58
+msgid "I'm extremely bored ..."
+msgstr ""
+
+#: voice.py:59
+msgid "I'm very sad ..."
+msgstr ""
+
+#: voice.py:60
+msgid "I'm sad"
+msgstr ""
+
+#: voice.py:66
+msgid "I'm living the life!"
+msgstr ""
+
+#: voice.py:67
+msgid "I pwn therefore I am."
+msgstr ""
+
+#: voice.py:68
+msgid "So many networks!!!"
+msgstr ""
+
+#: voice.py:69
+msgid ""
+"I'm having so much\n"
+"fun!"
+msgstr ""
+
+#: voice.py:70
+msgid ""
+"My crime is that of\n"
+"curiosity ..."
+msgstr ""
+
+#: voice.py:75
+#, python-brace-format
+msgid ""
+"Hello\n"
+"{name}!\n"
+"Nice to meet you. {name}"
+msgstr ""
+
+#: voice.py:76
+#, python-brace-format
+msgid ""
+"Unit\n"
+"{name}\n"
+"is nearby! {name}"
+msgstr ""
+
+#: voice.py:81
+#, python-brace-format
+msgid ""
+"Uhm ...\n"
+"goodbye\n"
+"{name}"
+msgstr ""
+
+#: voice.py:82
+#, python-brace-format
+msgid ""
+"{name}\n"
+"is gone ..."
+msgstr ""
+
+#: voice.py:87
+#, python-brace-format
+msgid ""
+"Whoops ...\n"
+"{name}\n"
+"is gone."
+msgstr ""
+
+#: voice.py:88
+#, python-brace-format
+msgid ""
+"{name}\n"
+"missed!"
+msgstr ""
+
+#: voice.py:89
+msgid "Missed!"
+msgstr ""
+
+#: voice.py:94
+msgid ""
+"Nobody wants to\n"
+"play with me ..."
+msgstr ""
+
+#: voice.py:95
+msgid "I feel so alone ..."
+msgstr ""
+
+#: voice.py:96
+msgid "Where's everybody?!"
+msgstr ""
+
+#: voice.py:101
+#, python-brace-format
+msgid "Napping for {secs}s ..."
+msgstr ""
+
+#: voice.py:102
+msgid "Zzzzz"
+msgstr ""
+
+#: voice.py:103
+#, python-brace-format
+msgid "ZzzZzzz ({secs}s)"
+msgstr ""
+
+#: voice.py:112
+#, python-brace-format
+msgid "Waiting for {secs}s ..."
+msgstr ""
+
+#: voice.py:114
+#, python-brace-format
+msgid "Looking around ({secs}s)"
+msgstr ""
+
+#: voice.py:121
+#, python-brace-format
+msgid ""
+"Hey\n"
+"{what}\n"
+"let's be friends!"
+msgstr ""
+
+#: voice.py:122
+#, python-brace-format
+msgid ""
+"Associating to\n"
+"{what}"
+msgstr ""
+
+#: voice.py:123
+#, python-brace-format
+msgid ""
+"Yo\n"
+"{what}!"
+msgstr ""
+
+#: voice.py:128
+#, python-brace-format
+msgid ""
+"Just decided that\n"
+"{mac}\n"
+"needs no WiFi!"
+msgstr ""
+
+#: voice.py:129
+#, python-brace-format
+msgid ""
+"Deauthenticating\n"
+"{mac}"
+msgstr ""
+
+#: voice.py:130
+#, python-brace-format
+msgid ""
+"Kickbanning\n"
+"{mac}!"
+msgstr ""
+
+#: voice.py:135
+#, python-brace-format
+msgid ""
+"Cool, we got {num}\n"
+"new handshake{plural}!"
+msgstr ""
+
+#: voice.py:139
+msgid ""
+"Ops, something\n"
+"went wrong ...\n"
+"Rebooting ..."
+msgstr ""
+
+#: voice.py:143
+#, python-brace-format
+msgid "Kicked {num} stations\n"
+msgstr ""
+
+#: voice.py:144
+#, python-brace-format
+msgid "Made {num} new friends\n"
+msgstr ""
+
+#: voice.py:145
+#, python-brace-format
+msgid "Got {num} handshakes\n"
+msgstr ""
+
+#: voice.py:147
+msgid "Met 1 peer"
+msgstr ""
+
+#: voice.py:149
+#, python-brace-format
+msgid "Met {num} peers"
+msgstr ""
+
+#: voice.py:154
+#, python-brace-format
+msgid ""
+"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
+"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
+"#pwnlog #pwnlife #hacktheplanet #skynet"
+msgstr ""
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py
index c1d71e7..4f9bb4a 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py
@@ -80,6 +80,9 @@ class Display(View):
         self._video_address = config['ui']['display']['video']['address']
         self._display_type = config['ui']['display']['type']
         self._display_color = config['ui']['display']['color']
+        self.full_refresh_count = 0
+        self.full_refresh_trigger = config['ui']['display']['refresh'] 
+
         self._render_cb = None
         self._display = None
         self._httpd = None
@@ -108,8 +111,11 @@ class Display(View):
     def _is_papirus(self):
         return self._display_type in ('papirus', 'papi')
 
-    def _is_waveshare(self):
-        return self._display_type in ('waveshare', 'ws')
+    def _is_waveshare1(self):
+        return self._display_type in ('waveshare_1', 'ws_1', 'waveshare1', 'ws1')
+
+    def _is_waveshare2(self):
+        return self._display_type in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2')
 
     def _init_display(self):
         if self._is_inky():
@@ -117,20 +123,32 @@ class Display(View):
             self._display = InkyPHAT(self._display_color)
             self._display.set_border(InkyPHAT.BLACK)
             self._render_cb = self._inky_render
+            
         elif self._is_papirus():
             from papirus import Papirus
             os.environ['EPD_SIZE'] = '2.0'
             self._display = Papirus()
             self._display.clear()
             self._render_cb = self._papirus_render
-        elif self._is_waveshare():
-            from pwnagotchi.ui.waveshare import EPD
+
+        elif self._is_waveshare1():
+            from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
+            # core.log("display module started")
+            self._display = EPD()
+            self._display.init(self._display.lut_full_update)
+            self._display.Clear(0xFF)
+            self._display.init(self._display.lut_partial_update)
+            self._render_cb = self._waveshare_render
+            
+        elif self._is_waveshare2():
+            from pwnagotchi.ui.waveshare.v2.waveshare import EPD
             # core.log("display module started")
             self._display = EPD()
             self._display.init(self._display.FULL_UPDATE)
             self._display.Clear(WHITE)
             self._display.init(self._display.PART_UPDATE)
             self._render_cb = self._waveshare_render
+            
         else:
             core.log("unknown display type %s" % self._display_type)
 
@@ -179,7 +197,19 @@ class Display(View):
 
     def _waveshare_render(self):
         buf = self._display.getbuffer(self.canvas)
-        self._display.displayPartial(buf)
+        if self._is_waveshare1:
+            if self.full_refresh_trigger >= 0 and self.full_refresh_count == self.full_refresh_trigger:
+                self._display.Clear(0x00)
+            self._display.display(buf)
+        elif self._is_waveshare2:
+            if self.full_refresh_trigger >= 0 and self.full_refresh_count == self.full_refresh_trigger:
+                self._display.Clear(BLACK)
+            self._display.displayPartial(buf)
+        self._display.sleep()
+        if self.full_refresh_trigger >= 0 and self.full_refresh_count == self.full_refresh_trigger:
+           self.full_refresh_count = 0
+        elif self.full_refresh_trigger >= 0:
+           self.full_refresh_count += 1
 
     def _on_view_rendered(self, img):
         # core.log("display::_on_view_rendered")
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py
index a960642..19e7999 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py
@@ -5,7 +5,7 @@ from PIL import Image, ImageDraw
 
 import core
 import pwnagotchi
-from pwnagotchi import voice
+from pwnagotchi.voice import Voice
 
 import pwnagotchi.ui.fonts as fonts
 import pwnagotchi.ui.faces as faces
@@ -31,6 +31,7 @@ def setup_display_specifics(config):
         face_pos = (0, int(height / 4))
         name_pos = (int(width / 2) - 15, int(height * .15))
         status_pos = (int(width / 2) - 15, int(height * .30))
+
     elif config['ui']['display']['type'] in ('papirus', 'papi'):
         fonts.setup(10, 8, 10, 23)
 
@@ -39,7 +40,9 @@ def setup_display_specifics(config):
         face_pos = (0, int(height / 4))
         name_pos = (int(width / 2) - 15, int(height * .15))
         status_pos = (int(width / 2) - 15, int(height * .30))
-    elif config['ui']['display']['type'] in ('ws', 'waveshare'):
+
+    elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1', 
+                                             'ws_2', 'ws2', 'waveshare_2', 'waveshare2'):
         fonts.setup(10, 9, 10, 35)
 
         width = 250
@@ -57,6 +60,7 @@ class View(object):
         self._config = config
         self._canvas = None
         self._lock = Lock()
+        self._voice = Voice(lang=config['main']['lang'])
 
         self._width, self._height, \
         face_pos, name_pos, status_pos = setup_display_specifics(config)
@@ -89,7 +93,7 @@ class View(object):
 
             'name': Text(value='%s>' % 'pwnagotchi', position=name_pos, color=BLACK, font=fonts.Bold),
             # 'face2':   Bitmap( '/root/pwnagotchi/data/images/face_happy.bmp', (0, 20)),
-            'status': Text(value=voice.default(), position=status_pos, color=BLACK, font=fonts.Medium),
+            'status': Text(value=self._voice.default(), position=status_pos, color=BLACK, font=fonts.Medium),
 
             'shakes': LabeledValue(label='PWND ', value='0 (00)', color=BLACK,
                                    position=(0, self._height - int(self._height * .12) + 1), label_font=fonts.Bold,
@@ -124,19 +128,19 @@ class View(object):
         self._state.set(key, value)
 
     def on_starting(self):
-        self.set('status', voice.on_starting())
+        self.set('status', self._voice.on_starting())
         self.set('face', faces.AWAKE)
 
     def on_ai_ready(self):
         self.set('mode', '')
         self.set('face', faces.HAPPY)
-        self.set('status', voice.on_ai_ready())
+        self.set('status', self._voice.on_ai_ready())
         self.update()
 
     def on_manual_mode(self, log):
         self.set('mode', 'MANU')
         self.set('face', faces.SAD if log.handshakes == 0 else faces.HAPPY)
-        self.set('status', voice.on_log(log))
+        self.set('status', self._voice.on_log(log))
         self.set('epoch', "%04d" % log.epochs)
         self.set('uptime', log.duration)
         self.set('channel', '-')
@@ -160,7 +164,7 @@ class View(object):
 
     def on_normal(self):
         self.set('face', faces.AWAKE)
-        self.set('status', voice.on_normal())
+        self.set('status', self._voice.on_normal())
         self.update()
 
     def set_closest_peer(self, peer):
@@ -188,17 +192,17 @@ class View(object):
 
     def on_new_peer(self, peer):
         self.set('face', faces.FRIEND)
-        self.set('status', voice.on_new_peer(peer))
+        self.set('status', self._voice.on_new_peer(peer))
         self.update()
 
     def on_lost_peer(self, peer):
         self.set('face', faces.LONELY)
-        self.set('status', voice.on_lost_peer(peer))
+        self.set('status', self._voice.on_lost_peer(peer))
         self.update()
 
     def on_free_channel(self, channel):
         self.set('face', faces.SMART)
-        self.set('status', voice.on_free_channel(channel))
+        self.set('status', self._voice.on_free_channel(channel))
         self.update()
 
     def wait(self, secs, sleeping=True):
@@ -214,12 +218,12 @@ class View(object):
                 if sleeping:
                     if secs > 1:
                         self.set('face', faces.SLEEP)
-                        self.set('status', voice.on_napping(secs))
+                        self.set('status', self._voice.on_napping(secs))
                     else:
                         self.set('face', faces.SLEEP2)
-                        self.set('status', voice.on_awakening())
+                        self.set('status', self._voice.on_awakening())
                 else:
-                    self.set('status', voice.on_waiting(secs))
+                    self.set('status', self._voice.on_waiting(secs))
                     if step % 2 == 0:
                         self.set('face', faces.LOOK_R)
                     else:
@@ -232,57 +236,57 @@ class View(object):
 
     def on_bored(self):
         self.set('face', faces.BORED)
-        self.set('status', voice.on_bored())
+        self.set('status', self._voice.on_bored())
         self.update()
 
     def on_sad(self):
         self.set('face', faces.SAD)
-        self.set('status', voice.on_sad())
+        self.set('status', self._voice.on_sad())
         self.update()
 
     def on_motivated(self, reward):
         self.set('face', faces.MOTIVATED)
-        self.set('status', voice.on_motivated(reward))
+        self.set('status', self._voice.on_motivated(reward))
         self.update()
 
     def on_demotivated(self, reward):
         self.set('face', faces.DEMOTIVATED)
-        self.set('status', voice.on_demotivated(reward))
+        self.set('status', self._voice.on_demotivated(reward))
         self.update()
 
     def on_excited(self):
         self.set('face', faces.EXCITED)
-        self.set('status', voice.on_excited())
+        self.set('status', self._voice.on_excited())
         self.update()
 
     def on_assoc(self, ap):
         self.set('face', faces.INTENSE)
-        self.set('status', voice.on_assoc(ap))
+        self.set('status', self._voice.on_assoc(ap))
         self.update()
 
     def on_deauth(self, sta):
         self.set('face', faces.COOL)
-        self.set('status', voice.on_deauth(sta))
+        self.set('status', self._voice.on_deauth(sta))
         self.update()
 
     def on_miss(self, who):
         self.set('face', faces.SAD)
-        self.set('status', voice.on_miss(who))
+        self.set('status', self._voice.on_miss(who))
         self.update()
 
     def on_lonely(self):
         self.set('face', faces.LONELY)
-        self.set('status', voice.on_lonely())
+        self.set('status', self._voice.on_lonely())
         self.update()
 
     def on_handshakes(self, new_shakes):
         self.set('face', faces.HAPPY)
-        self.set('status', voice.on_handshakes(new_shakes))
+        self.set('status', self._voice.on_handshakes(new_shakes))
         self.update()
 
     def on_rebooting(self):
         self.set('face', faces.BROKEN)
-        self.set('status', voice.on_rebooting())
+        self.set('status', self._voice.on_rebooting())
         self.update()
 
     def update(self):
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/__init__.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/__init__.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epd2in13.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epd2in13.py
new file mode 100644
index 0000000..5805086
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epd2in13.py
@@ -0,0 +1,218 @@
+# //*****************************************************************************
+# * | File        :	  epd2in13.py
+# * | Author      :   Waveshare team
+# * | Function    :   Electronic paper driver
+# * | Info        :
+# *----------------
+# * |	This version:   V3.1
+# * | Date        :   2019-03-20
+# * | Info        :   python3 demo
+# * fix: TurnOnDisplay()
+# ******************************************************************************//
+# 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.
+#
+
+
+from . import epdconfig
+from PIL import Image
+import RPi.GPIO as GPIO
+# import numpy as np
+
+# Display resolution
+EPD_WIDTH       = 122
+EPD_HEIGHT      = 250
+
+class EPD:
+    def __init__(self):
+        self.reset_pin = epdconfig.RST_PIN
+        self.dc_pin = epdconfig.DC_PIN
+        self.busy_pin = epdconfig.BUSY_PIN
+        self.width = EPD_WIDTH
+        self.height = EPD_HEIGHT
+    lut_full_update = [
+        0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+        0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+    ]
+
+    lut_partial_update  = [
+        0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ]
+        
+    # Hardware reset
+    def reset(self):
+        epdconfig.digital_write(self.reset_pin, GPIO.HIGH)
+        epdconfig.delay_ms(200) 
+        epdconfig.digital_write(self.reset_pin, GPIO.LOW)         # module reset
+        epdconfig.delay_ms(200)
+        epdconfig.digital_write(self.reset_pin, GPIO.HIGH)
+        epdconfig.delay_ms(200)   
+
+    def send_command(self, command):
+        epdconfig.digital_write(self.dc_pin, GPIO.LOW)
+        epdconfig.spi_writebyte([command])
+
+    def send_data(self, data):
+        epdconfig.digital_write(self.dc_pin, GPIO.HIGH)
+        epdconfig.spi_writebyte([data])
+        
+    def wait_until_idle(self):
+        # print("busy")
+        while(epdconfig.digital_read(self.busy_pin) == 1):      # 0: idle, 1: busy
+            epdconfig.delay_ms(100)
+        # print("free busy")
+
+    def TurnOnDisplay(self):
+        self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2
+        self.send_data(0xC4)
+        self.send_command(0x20) # MASTER_ACTIVATION
+        self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE
+        self.wait_until_idle()
+
+    def init(self, lut):
+        if (epdconfig.module_init() != 0):
+            return -1
+        # EPD hardware init start
+        self.reset()
+        self.send_command(0x01) # DRIVER_OUTPUT_CONTROL
+        self.send_data((EPD_HEIGHT - 1) & 0xFF)
+        self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF)
+        self.send_data(0x00) # GD = 0 SM = 0 TB = 0
+        
+        self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL
+        self.send_data(0xD7)
+        self.send_data(0xD6)
+        self.send_data(0x9D)
+        
+        self.send_command(0x2C) # WRITE_VCOM_REGISTER
+        self.send_data(0xA8) # VCOM 7C
+        
+        self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD
+        self.send_data(0x1A) # 4 dummy lines per gate
+        
+        self.send_command(0x3B) # SET_GATE_TIME
+        self.send_data(0x08) # 2us per line
+        
+        self.send_command(0X3C) # BORDER_WAVEFORM_CONTROL
+        self.send_data(0x03)      
+        
+        self.send_command(0X11) # DATA_ENTRY_MODE_SETTING
+        self.send_data(0x03) # X increment; Y increment
+        
+        # WRITE_LUT_REGISTER
+        self.send_command(0x32)
+        for count in range(30):
+            self.send_data(lut[count])
+
+        return 0
+        
+##
+ #  @brief: specify the memory area for data R//W
+ ##
+    def SetWindows(self, x_start, y_start, x_end, y_end):
+        self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION
+        self.send_data((x_start >> 3) & 0xFF)
+        self.send_data((x_end >> 3) & 0xFF)
+        self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION
+        self.send_data(y_start & 0xFF)
+        self.send_data((y_start >> 8) & 0xFF)
+        self.send_data(y_end & 0xFF)
+        self.send_data((y_end >> 8) & 0xFF)
+
+##
+ #  @brief: specify the start point for data R//W
+ ##
+    def SetCursor(self, x, y):
+        self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER
+        # x point must be the multiple of 8 or the last 3 bits will be ignored
+        self.send_data((x >> 3) & 0xFF)
+        self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER
+        self.send_data(y & 0xFF)
+        self.send_data((y >> 8) & 0xFF)
+        self.wait_until_idle()
+        
+    def getbuffer(self, image):
+        if self.width%8 == 0:
+            linewidth = self.width//8
+        else:
+            linewidth = self.width//8 + 1
+         
+        buf = [0xFF] * (linewidth * self.height)
+        image_monocolor = image.convert('1')
+        imwidth, imheight = image_monocolor.size
+        pixels = image_monocolor.load()
+        
+        if(imwidth == self.width and imheight == self.height):
+            # print("Vertical")
+            for y in range(imheight):
+                for x in range(imwidth):                    
+                    if pixels[x, y] == 0:
+                        # x = imwidth - x
+                        buf[x // 8 + y * linewidth] &= ~(0x80 >> (x % 8))
+        elif(imwidth == self.height and imheight == self.width):
+            # print("Horizontal")
+            for y in range(imheight):
+                for x in range(imwidth):
+                    newx = y
+                    newy = self.height - x - 1
+                    if pixels[x, y] == 0:
+                        # newy = imwidth - newy - 1
+                        buf[newx // 8 + newy*linewidth] &= ~(0x80 >> (y % 8))
+        return buf   
+
+        
+    def display(self, image):
+        if self.width%8 == 0:
+            linewidth = self.width//8
+        else:
+            linewidth = self.width//8 + 1
+
+        self.SetWindows(0, 0, EPD_WIDTH, EPD_HEIGHT);
+        for j in range(0, self.height):
+            self.SetCursor(0, j);
+            self.send_command(0x24);
+            for i in range(0, linewidth):
+                self.send_data(image[i + j * linewidth])   
+        self.TurnOnDisplay()
+    
+    def Clear(self, color):
+        if self.width%8 == 0:
+            linewidth = self.width//8
+        else:
+            linewidth = self.width//8 + 1
+
+        self.SetWindows(0, 0, EPD_WIDTH, EPD_HEIGHT);
+        for j in range(0, self.height):
+            self.SetCursor(0, j);
+            self.send_command(0x24);
+            for i in range(0, linewidth):
+                self.send_data(color)   
+        self.TurnOnDisplay()
+
+    def sleep(self):
+        self.send_command(0x10) #enter deep sleep
+        # self.send_data(0x01)
+        epdconfig.delay_ms(100)    
+
+### END OF FILE ###
+
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epdconfig.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epdconfig.py
new file mode 100644
index 0000000..78ff647
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v1/epdconfig.py
@@ -0,0 +1,73 @@
+# /*****************************************************************************
+# * | File        :	  EPD_1in54.py
+# * | Author      :   Waveshare team
+# * | Function    :   Hardware underlying interface
+# * | Info        :
+# *----------------
+# * |	This version:   V2.0
+# * | Date        :   2018-11-01
+# * | Info        :   
+# * 1.Remove:
+#   digital_write(self, pin, value)
+#   digital_read(self, pin)
+#   delay_ms(self, delaytime)
+#   set_lut(self, lut)
+#   self.lut = self.lut_full_update
+# ******************************************************************************/
+# 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 spidev
+import RPi.GPIO as GPIO
+import time
+
+# Pin definition
+RST_PIN         = 17
+DC_PIN          = 25
+CS_PIN          = 8
+BUSY_PIN        = 24
+
+# SPI device, bus = 0, device = 0
+SPI = spidev.SpiDev(0, 0)
+
+def digital_write(pin, value):
+    GPIO.output(pin, value)
+
+def digital_read(pin):
+    return GPIO.input(BUSY_PIN)
+
+def delay_ms(delaytime):
+    time.sleep(delaytime / 1000.0)
+
+def spi_writebyte(data):
+    SPI.writebytes(data)
+
+def module_init():
+    GPIO.setmode(GPIO.BCM)
+    GPIO.setwarnings(False)
+    GPIO.setup(RST_PIN, GPIO.OUT)
+    GPIO.setup(DC_PIN, GPIO.OUT)
+    GPIO.setup(CS_PIN, GPIO.OUT)
+    GPIO.setup(BUSY_PIN, GPIO.IN)
+    SPI.max_speed_hz = 2000000
+    SPI.mode = 0b00
+    return 0;
+
+### END OF FILE ###
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v2/__init__.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v2/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v2/waveshare.py
similarity index 100%
rename from sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare.py
rename to sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/waveshare/v2/waveshare.py
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py
index 65b904f..3ff9e0e 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py
@@ -1,147 +1,160 @@
 import random
+import gettext
+import os
 
 
-def default():
-    return 'ZzzzZZzzzzZzzz'
+class Voice:
+    def __init__(self, lang):
+        localedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locale')
+        translation = gettext.translation(
+            'voice', localedir,
+            languages=[lang],
+            fallback=True,
+        )
+        translation.install()
+        self._ = translation.gettext
+
+    def default(self):
+        return self._('ZzzzZZzzzzZzzz')
 
 
-def on_starting():
-    return random.choice([ \
-        'Hi, I\'m Pwnagotchi!\nStarting ...',
-        'New day, new hunt,\nnew pwns!',
-        'Hack the Planet!'])
+    def on_starting(self):
+        return random.choice([ \
+            self._('Hi, I\'m Pwnagotchi!\nStarting ...'),
+            self._('New day, new hunt,\nnew pwns!'),
+            self._('Hack the Planet!')])
 
 
-def on_ai_ready():
-    return random.choice([
-        'AI ready.',
-        'The neural network\nis ready.'])
+    def on_ai_ready(self):
+        return random.choice([
+            self._('AI ready.'),
+            self._('The neural network\nis ready.')])
 
 
-def on_normal():
-    return random.choice([ \
-        '',
-        '...'])
+    def on_normal(self):
+        return random.choice([ \
+            '',
+            '...'])
 
 
-def on_free_channel(channel):
-    return 'Hey, channel %d is\nfree! Your AP will\nsay thanks.' % channel
+    def on_free_channel(channel):
+        return self._('Hey, channel {channel} is\nfree! Your AP will\nsay thanks.').format(channel=channel)
 
 
-def on_bored():
-    return random.choice([ \
-        'I\'m bored ...',
-        'Let\'s go for a walk!'])
+    def on_bored():
+        return random.choice([ \
+            self._('I\'m bored ...'),
+            self._('Let\'s go for a walk!')])
 
 
-def on_motivated(reward):
-    return 'This is the best\nday of my life!'
+    def on_motivated(self, reward):
+        return self._('This is the best\nday of my life!')
 
 
-def on_demotivated(reward):
-    return 'Shitty day :/'
+    def on_demotivated(self, reward):
+        return self._('Shitty day :/')
 
 
-def on_sad():
-    return random.choice([ \
-        'I\'m extremely bored ...',
-        'I\'m very sad ...',
-        'I\'m sad',
-        '...'])
+    def on_sad(self):
+        return random.choice([ \
+            self._('I\'m extremely bored ...'),
+            self._('I\'m very sad ...'),
+            self._('I\'m sad'),
+            '...'])
 
 
-def on_excited():
-    return random.choice([ \
-        'I\'m living the life!',
-        'I pwn therefore I am.',
-        'So many networks!!!',
-        'I\'m having so much\nfun!',
-        'My crime is that of\ncuriosity ...'])
+    def on_excited(self):
+        return random.choice([ \
+            self._('I\'m living the life!'),
+            self._('I pwn therefore I am.'),
+            self._('So many networks!!!'),
+            self._('I\'m having so much\nfun!'),
+            self._('My crime is that of\ncuriosity ...')])
 
 
-def on_new_peer(peer):
-    return random.choice([ \
-        'Hello\n%s!\nNice to meet you.' % peer.name(),
-        'Unit\n%s\nis nearby!' % peer.name()])
+    def on_new_peer(self, peer):
+        return random.choice([ \
+            self._('Hello\n{name}!\nNice to meet you. {name}').format(name=peer.name()),
+            self._('Unit\n{name}\nis nearby! {name}').format(name=peer.name())])
 
 
-def on_lost_peer(peer):
-    return random.choice([ \
-        'Uhm ...\ngoodbye\n%s' % peer.name(),
-        '%s\nis gone ...' % peer.name()])
+    def on_lost_peer(self, peer):
+        return random.choice([ \
+            self._('Uhm ...\ngoodbye\n{name}').format(name=peer.name()),
+            self._('{name}\nis gone ...').format(name=peer.name())])
 
 
-def on_miss(who):
-    return random.choice([ \
-        'Whoops ...\n%s\nis gone.' % who,
-        '%s\nmissed!' % who,
-        'Missed!'])
+    def on_miss(self, who):
+        return random.choice([ \
+            self._('Whoops ...\n{name}\nis gone.').format(name=who),
+            self._('{name}\nmissed!').format(name=who),
+            self._('Missed!')])
 
 
-def on_lonely():
-    return random.choice([ \
-        'Nobody wants to\nplay with me ...',
-        'I feel so alone ...',
-        'Where\'s everybody?!'])
+    def on_lonely(self):
+        return random.choice([ \
+            self._('Nobody wants to\nplay with me ...'),
+            self._('I feel so alone ...'),
+            self._('Where\'s everybody?!')])
 
 
-def on_napping(secs):
-    return random.choice([ \
-        'Napping for %ds ...' % secs,
-        'Zzzzz',
-        'ZzzZzzz (%ds)' % secs])
+    def on_napping(self,secs):
+        return random.choice([ \
+            self._('Napping for {secs}s ...').format(secs=secs),
+            self._('Zzzzz'),
+            self._('ZzzZzzz ({secs}s)').format(secs=secs)])
 
 
-def on_awakening():
-    return random.choice(['...', '!'])
+    def on_awakening(self):
+        return random.choice(['...', '!'])
 
 
-def on_waiting(secs):
-    return random.choice([ \
-        'Waiting for %ds ...' % secs,
-        '...',
-        'Looking around (%ds)' % secs])
+    def on_waiting(self,secs):
+        return random.choice([ \
+            self._('Waiting for {secs}s ...').format(secs=secs),
+            '...',
+            self._('Looking around ({secs}s)').format(secs=secs)])
 
 
-def on_assoc(ap):
-    ssid, bssid = ap['hostname'], ap['mac']
-    what = ssid if ssid != '' and ssid != '<hidden>' else bssid
-    return random.choice([ \
-        'Hey\n%s\nlet\'s be friends!' % what,
-        'Associating to\n%s' % what,
-        'Yo\n%s!' % what])
+    def on_assoc(self,ap):
+        ssid, bssid = ap['hostname'], ap['mac']
+        what = ssid if ssid != '' and ssid != '<hidden>' else bssid
+        return random.choice([ \
+            self._('Hey\n{what}\nlet\'s be friends!').format(what=what),
+            self._('Associating to\n{what}').format(what=what),
+            self._('Yo\n{what}!').format(what=what)])
 
 
-def on_deauth(sta):
-    return random.choice([ \
-        'Just decided that\n%s\nneeds no WiFi!' % sta['mac'],
-        'Deauthenticating\n%s' % sta['mac'],
-        'Kickbanning\n%s!' % sta['mac']])
+    def on_deauth(self,sta):
+        return random.choice([ \
+            self._('Just decided that\n{mac}\nneeds no WiFi!').format(mac=sta['mac']),
+            self._('Deauthenticating\n{mac}').format(mac=sta['mac']),
+            self._('Kickbanning\n{mac}!').format(mac=sta['mac'])])
 
 
-def on_handshakes(new_shakes):
-    s = 's' if new_shakes > 1 else ''
-    return 'Cool, we got %d\nnew handshake%s!' % (new_shakes, s)
+    def on_handshakes(self,new_shakes):
+        s = 's' if new_shakes > 1 else ''
+        return self._('Cool, we got {num}\nnew handshake{plural}!').format(num=new_shakes, plural=s)
 
 
-def on_rebooting():
-    return "Ops, something\nwent wrong ...\nRebooting ..."
+    def on_rebooting(self):
+        return self._("Ops, something\nwent wrong ...\nRebooting ...")
 
 
-def on_log(log):
-    status = 'Kicked %d stations\n' % log.deauthed
-    status += 'Made %d new friends\n' % log.associated
-    status += 'Got %d handshakes\n' % log.handshakes
-    if log.peers == 1:
-        status += 'Met 1 peer'
-    elif log.peers > 0:
-        status += 'Met %d peers' % log.peers
-    return status
+    def on_log(self,log):
+        status = self._('Kicked {num} stations\n').format(num=log.deauthed)
+        status += self._('Made {num} new friends\n').format(num=log.associated)
+        status += self._('Got {num} handshakes\n').format(num=log.handshakes)
+        if log.peers == 1:
+            status += self._('Met 1 peer')
+        elif log.peers > 0:
+            status += self._('Met {num} peers').format(num=log.peers)
+        return status
 
 
-def on_log_tweet(log):
-    return 'I\'ve been pwning for %s and kicked %d clients! I\'ve also met %d new friends and ate %d handshakes! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet' % ( \
-        log.duration_human,
-        log.deauthed,
-        log.associated,
-        log.handshakes)
+    def on_log_tweet(self,log):
+        return self._('I\'ve been pwning for {duration} and kicked {deauthed} clients! I\'ve also met {associated} new friends and ate {handshakes} handshakes! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet').format(
+                duration=log.duration_human,
+                deauthed=log.deauthed,
+                associated=log.associated,
+                handshakes=log.handshakes)