commit
34a83720c3
23
.travis.yml
Normal file
23
.travis.yml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
dist: bionic
|
||||||
|
language: generic
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- LANG=C
|
||||||
|
- LC_ALL=C
|
||||||
|
before_cache:
|
||||||
|
- mountpoint -q $TRAVIS_BUILD_DIR/tmp/mnt && sudo umount -R $TRAVIS_BUILD_DIR/tmp/mnt
|
||||||
|
- sudo find $TRAVIS_BUILD_DIR/tmp/ -name '*.img' -delete
|
||||||
|
cache:
|
||||||
|
apt: true
|
||||||
|
directories:
|
||||||
|
- tmp/
|
||||||
|
before_script:
|
||||||
|
- sudo apt-get -y update
|
||||||
|
- sudo apt-get -y install qemu-user-static binfmt-support qemu
|
||||||
|
- sudo update-binfmts --display
|
||||||
|
- unset GOROOT
|
||||||
|
script:
|
||||||
|
- sudo ./scripts/create_sibling.sh -n pwnagotchi -o pwnagotchi.img -s 4
|
||||||
|
- zip -s 2g pwnagotchi.zip pwnagotchi.img
|
||||||
|
|
||||||
|
# TODO: deploy!
|
71
README.md
71
README.md
@ -1,5 +1,13 @@
|
|||||||
# Pwnagotchi
|
# Pwnagotchi
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/evilsocket/pwnagotchi/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/evilsocket/pwnagotchi.svg?style=flat-square"></a>
|
||||||
|
<a href="https://github.com/evilsocket/pwnagotchi/blob/master/LICENSE.md"><img alt="Software License" src="https://img.shields.io/badge/license-GPL3-brightgreen.svg?style=flat-square"></a>
|
||||||
|
<a href="https://travis-ci.org/evilsocket/pwnagotchi"><img alt="Travis" src="https://img.shields.io/travis/evilsocket/pwnagotchi/master.svg?style=flat-square"></a>
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
|
||||||
[Pwnagotchi](https://twitter.com/pwnagotchi) is an "AI" that learns from the WiFi environment and instruments bettercap in order to maximize the WPA key material (any form of handshake that is crackable, including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/), full and half WPA handshakes) captured.
|
[Pwnagotchi](https://twitter.com/pwnagotchi) is an "AI" that learns from the WiFi environment and instruments bettercap in order to maximize the WPA key material (any form of handshake that is crackable, including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/), full and half WPA handshakes) captured.
|
||||||
|
|
||||||

|

|
||||||
@ -14,9 +22,9 @@ Multiple units can talk to each other, advertising their own presence using a pa
|
|||||||
|
|
||||||
Depending on the status of the unit, several states and states transitions are configurable and represented on the display as different moods, expressions and sentences.
|
Depending on the status of the unit, several states and states transitions are configurable and represented on the display as different moods, expressions and sentences.
|
||||||
|
|
||||||
If instead you are a boring person, you can disable the AI and have the algorithm run just with the preconfigured default parameters and enjoy a very portable bettercap + webui dedicated hardware.
|
If instead you just want to use your own parameters and save battery and CPU cycles, you can disable the AI in `config.yml` and enjoy an automated deauther, WPA handshake sniffer and portable bettercap + webui dedicated hardware.
|
||||||
|
|
||||||
**NOTE:** The software **requires bettercap v2.25**.
|
**NOTE:** The software **requires at least bettercap v2.25**.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -28,12 +36,22 @@ For hackers to learn reinforcement learning, WiFi networking and have an excuse
|
|||||||
|
|
||||||
**THIS IS STILL ALPHA STAGE SOFTWARE, IF YOU DECIDE TO TRY TO USE IT, YOU ARE ON YOUR OWN, NO SUPPORT WILL BE PROVIDED, NEITHER FOR INSTALLATION OR FOR BUGS**
|
**THIS IS STILL ALPHA STAGE SOFTWARE, IF YOU DECIDE TO TRY TO USE IT, YOU ARE ON YOUR OWN, NO SUPPORT WILL BE PROVIDED, NEITHER FOR INSTALLATION OR FOR BUGS**
|
||||||
|
|
||||||
|
However, there's [a Slack channel](https://join.slack.com/t/pwnagotchi/shared_invite/enQtNzc4NzY3MDE2OTAzLTg5NmNmNDJiMDM3ZWFkMWUwN2Y5NDk0Y2JlZWZjODlhMmRhNDZiOGMwYjJhM2UzNzA3YjA5NjJmZGY5NGI5NmI).
|
||||||
### Hardware
|
### Hardware
|
||||||
|
|
||||||
- Raspberry Pi Zero W
|
- Raspberry Pi Zero W
|
||||||
- [Waveshare eInk Display](https://www.waveshare.com/2.13inch-e-paper-hat.htm) (optional if you connect to usb0 and point your browser to the web ui, see config.yml)
|
|
||||||
- A decent power bank (with 1500 mAh you get ~2 hours with AI on)
|
- A decent power bank (with 1500 mAh you get ~2 hours with AI on)
|
||||||
|
|
||||||
|
#### Display (optional)
|
||||||
|
|
||||||
|
The display is optional if you connect to `usb0` (by using the data port on the unit) and point your browser to the web ui (see config.yml).
|
||||||
|
|
||||||
|
The supported models are:
|
||||||
|
|
||||||
|
- [Waveshare eInk Display (both V1 and V2)](https://www.waveshare.com/2.13inch-e-paper-hat.htm)
|
||||||
|
- [Pimoroni Inky pHAT](https://shop.pimoroni.com/products/inky-phat)
|
||||||
|
- [PaPiRus eInk Screen](https://uk.pi-supply.com/products/papirus-zero-epaper-screen-phat-pi-zero)
|
||||||
|
|
||||||
### Software
|
### Software
|
||||||
|
|
||||||
- Raspbian + [nexmon patches](https://re4son-kernel.com/re4son-pi-kernel/) for monitor mode, or any Linux with a monitor mode enabled interface (if you tune config.yml).
|
- Raspbian + [nexmon patches](https://re4son-kernel.com/re4son-pi-kernel/) for monitor mode, or any Linux with a monitor mode enabled interface (if you tune config.yml).
|
||||||
@ -48,13 +66,14 @@ You can use the `scripts/create_sibling.sh` script to create an - ready to flash
|
|||||||
usage: ./scripts/create_sibling.sh [OPTIONS]
|
usage: ./scripts/create_sibling.sh [OPTIONS]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-n <name> # Name of the pwnagotchi (default: pwnagotchi)
|
-n <name> # Name of the pwnagotchi (default: pwnagotchi)
|
||||||
-i <file> # Provide the path of an already downloaded raspbian image
|
-i <file> # Provide the path of an already downloaded raspbian image
|
||||||
-o <file> # Name of the img-file (default: pwnagotchi.img)
|
-o <file> # Name of the img-file (default: pwnagotchi.img)
|
||||||
-s <size> # Size which should be added to second partition (in Gigabyte) (default: 4)
|
-s <size> # Size which should be added to second partition (in Gigabyte) (default: 4)
|
||||||
-p # Only run provisioning (assumes the image is already mounted)
|
-v <version> # Version of raspbian (Supported: latest; default: latest)
|
||||||
-d # Only run dependencies checks
|
-p # Only run provisioning (assumes the image is already mounted)
|
||||||
-h # Show this help
|
-d # Only run dependencies checks
|
||||||
|
-h # Show this help
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Host Connection Share
|
#### Host Connection Share
|
||||||
@ -63,7 +82,7 @@ If you connect to the unit via `usb0` (thus using the data port), you might want
|
|||||||
|
|
||||||
### UI
|
### UI
|
||||||
|
|
||||||
The UI is available either via display if installed, or via http://10.0.0.2:8080/ if you connect to the unit via `usb0` and set a static address on the network interface.
|
The UI is available either via display if installed, or via http://pwnagotchi.local:8080/ if you connect to the unit via `usb0` and set a static address on the network interface (change `pwnagotchi` with the hostname of your unit).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -73,6 +92,34 @@ The UI is available either via display if installed, or via http://10.0.0.2:8080
|
|||||||
* **PWND**: Number of handshakes captured in this session and number of unique networks we own at least one handshake of, from the beginning.
|
* **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.
|
* **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 are:
|
||||||
|
|
||||||
|
* **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/rootfs/root/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/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po
|
||||||
|
./scripts/language.sh compile it
|
||||||
|
# DONE
|
||||||
|
```
|
||||||
|
|
||||||
### Random Info
|
### Random Info
|
||||||
|
|
||||||
- `hostname` sets the unit name.
|
- `hostname` sets the unit name.
|
||||||
@ -81,7 +128,7 @@ The UI is available either via display if installed, or via http://10.0.0.2:8080
|
|||||||
- `/var/log/pwnagotchi.log` is your friend.
|
- `/var/log/pwnagotchi.log` is your friend.
|
||||||
- if connected to a laptop via usb data port, with internet connectivity shared, magic things will happen.
|
- if connected to a laptop via usb data port, with internet connectivity shared, magic things will happen.
|
||||||
- checkout the `ui.video` section of the `config.yml` - if you don't want to use a display, you can connect to it with the browser and a cable.
|
- checkout the `ui.video` section of the `config.yml` - if you don't want to use a display, you can connect to it with the browser and a cable.
|
||||||
- If you get `[FAILED] Failed to start Remount Root and Kernel File Systems.` while booting pwnagotchi, make sure
|
- If you get `[FAILED] Failed to start Remount Root and Kernel File Systems.` while booting pwnagotchi, make sure
|
||||||
the `PARTUUID`s for `rootfs` and `boot` partitions are the same in `/etc/fstab`. Use `sudo blkid` to find those values when you are using `create_sibling.sh`.
|
the `PARTUUID`s for `rootfs` and `boot` partitions are the same in `/etc/fstab`. Use `sudo blkid` to find those values when you are using `create_sibling.sh`.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
# based on: https://wiki.debian.org/RaspberryPi/qemu-user-static
|
# based on: https://wiki.debian.org/RaspberryPi/qemu-user-static
|
||||||
## and https://z4ziggy.wordpress.com/2015/05/04/from-bochs-to-chroot/
|
## and https://z4ziggy.wordpress.com/2015/05/04/from-bochs-to-chroot/
|
||||||
|
|
||||||
@ -16,6 +16,9 @@ PWNI_SIZE="4"
|
|||||||
OPT_PROVISION_ONLY=0
|
OPT_PROVISION_ONLY=0
|
||||||
OPT_CHECK_DEPS_ONLY=0
|
OPT_CHECK_DEPS_ONLY=0
|
||||||
OPT_IMAGE_PROVIDED=0
|
OPT_IMAGE_PROVIDED=0
|
||||||
|
OPT_RASPBIAN_VERSION='latest'
|
||||||
|
|
||||||
|
SUPPORTED_RASPBIAN_VERSIONS=( 'latest' 'buster' 'stretch' )
|
||||||
|
|
||||||
if [[ "$EUID" -ne 0 ]]; then
|
if [[ "$EUID" -ne 0 ]]; then
|
||||||
echo "Run this script as root!"
|
echo "Run this script as root!"
|
||||||
@ -44,9 +47,23 @@ function check_dependencies() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function get_raspbian() {
|
function get_raspbian() {
|
||||||
|
VERSION="$1"
|
||||||
|
|
||||||
|
case "$VERSION" in
|
||||||
|
latest)
|
||||||
|
URL="https://downloads.raspberrypi.org/raspbian_lite_latest"
|
||||||
|
;;
|
||||||
|
buster)
|
||||||
|
URL="https://downloads.raspberrypi.org/raspbian/images/raspbian-2019-07-12/2019-07-10-raspbian-buster.zip"
|
||||||
|
;;
|
||||||
|
stretch)
|
||||||
|
URL="https://downloads.raspberrypi.org/raspbian/images/raspbian-2019-04-09/2019-04-08-raspbian-stretch.zip"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
echo "[+] Downloading raspbian.zip"
|
echo "[+] Downloading raspbian.zip"
|
||||||
mkdir -p "${TMP_DIR}"
|
mkdir -p "${TMP_DIR}"
|
||||||
wget --show-progress -qcO "${TMP_DIR}/raspbian.zip" "https://downloads.raspberrypi.org/raspbian_lite_latest"
|
wget --show-progress -qcO "${TMP_DIR}/raspbian.zip" "$URL"
|
||||||
echo "[+] Unpacking raspbian.zip to raspbian.img"
|
echo "[+] Unpacking raspbian.zip to raspbian.img"
|
||||||
gunzip -c "${TMP_DIR}/raspbian.zip" > "${TMP_DIR}/raspbian.img"
|
gunzip -c "${TMP_DIR}/raspbian.zip" > "${TMP_DIR}/raspbian.img"
|
||||||
}
|
}
|
||||||
@ -68,7 +85,7 @@ function setup_raspbian(){
|
|||||||
parted -s "$LOOP_PATH" rm 2
|
parted -s "$LOOP_PATH" rm 2
|
||||||
parted -s "$LOOP_PATH" mkpart primary "$PART2_START" 100%
|
parted -s "$LOOP_PATH" mkpart primary "$PART2_START" 100%
|
||||||
echo "[+] Check FS"
|
echo "[+] Check FS"
|
||||||
e2fsck -f "${LOOP_PATH}p2"
|
e2fsck -y -f "${LOOP_PATH}p2"
|
||||||
echo "[+] Resize FS"
|
echo "[+] Resize FS"
|
||||||
resize2fs "${LOOP_PATH}p2"
|
resize2fs "${LOOP_PATH}p2"
|
||||||
echo "[+] Device is ${LOOP_PATH}"
|
echo "[+] Device is ${LOOP_PATH}"
|
||||||
@ -82,9 +99,8 @@ function setup_raspbian(){
|
|||||||
mount --bind /sys "${MNT_DIR}/sys/"
|
mount --bind /sys "${MNT_DIR}/sys/"
|
||||||
mount --bind /proc "${MNT_DIR}/proc/"
|
mount --bind /proc "${MNT_DIR}/proc/"
|
||||||
mount --bind /dev/pts "${MNT_DIR}/dev/pts"
|
mount --bind /dev/pts "${MNT_DIR}/dev/pts"
|
||||||
mount --bind /etc/ssl/certs "${MNT_DIR}/etc/ssl/certs"
|
|
||||||
mount --bind /etc/ca-certificates "${MNT_DIR}/etc/ca-certificates"
|
|
||||||
cp /usr/bin/qemu-arm-static "${MNT_DIR}/usr/bin"
|
cp /usr/bin/qemu-arm-static "${MNT_DIR}/usr/bin"
|
||||||
|
cp /etc/resolv.conf "${MNT_DIR}/etc/resolv.conf"
|
||||||
}
|
}
|
||||||
|
|
||||||
function provision_raspbian() {
|
function provision_raspbian() {
|
||||||
@ -94,12 +110,17 @@ function provision_raspbian() {
|
|||||||
LANG=C chroot . bin/bash -x <<EOF
|
LANG=C chroot . bin/bash -x <<EOF
|
||||||
set -eu
|
set -eu
|
||||||
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
|
||||||
|
uname -a
|
||||||
|
|
||||||
apt-get -y update
|
apt-get -y update
|
||||||
apt-get -y upgrade
|
apt-get -y upgrade
|
||||||
apt-get -y install git vim screen build-essential golang python3-pip
|
apt-get -y install git vim screen build-essential golang python3-pip gawk
|
||||||
apt-get -y install libpcap-dev libusb-1.0-0-dev libnetfilter-queue-dev
|
apt-get -y install libpcap-dev libusb-1.0-0-dev libnetfilter-queue-dev
|
||||||
apt-get -y install dphys-swapfile libopenmpi-dev libatlas-base-dev
|
apt-get -y install dphys-swapfile libopenmpi-dev libatlas-base-dev
|
||||||
apt-get -y install libjasper-dev libqtgui4 libqt4-test
|
apt-get -y install libjasper-dev libqtgui4 libqt4-test libopenjp2-7
|
||||||
|
apt-get -y install tcpdump libilmbase23 libopenexr23 libgstreamer1.0-0
|
||||||
|
apt-get -y install libavcodec58 libavformat58 libswscale5
|
||||||
|
|
||||||
# setup dphys-swapfile
|
# setup dphys-swapfile
|
||||||
echo "CONF_SWAPSIZE=1024" >/etc/dphys-swapfile
|
echo "CONF_SWAPSIZE=1024" >/etc/dphys-swapfile
|
||||||
@ -110,19 +131,32 @@ function provision_raspbian() {
|
|||||||
git clone https://github.com/evilsocket/pwnagotchi.git
|
git clone https://github.com/evilsocket/pwnagotchi.git
|
||||||
rsync -aP pwnagotchi/sdcard/boot/* /boot/
|
rsync -aP pwnagotchi/sdcard/boot/* /boot/
|
||||||
rsync -aP pwnagotchi/sdcard/rootfs/* /
|
rsync -aP pwnagotchi/sdcard/rootfs/* /
|
||||||
|
rm -rf /tmp/pwnagotchi
|
||||||
|
|
||||||
# configure pwnagotchi
|
# configure pwnagotchi
|
||||||
echo -e "$PWNI_NAME" > /etc/hostname
|
echo -e "$PWNI_NAME" > /etc/hostname
|
||||||
sed -i "s@^127\.0\.0\.1 .*@127.0.0.1 localhost "$PWNI_NAME" "$PWNI_NAME".local@g" /etc/hosts
|
sed -i "s@^127\.0\.0\.1 .*@127.0.0.1 localhost "$PWNI_NAME" "$PWNI_NAME".local@g" /etc/hosts
|
||||||
sed -i "s@pwnagotchi@$PWNI_NAME@g" /etc/motd
|
sed -i "s@alpha@$PWNI_NAME@g" /etc/motd
|
||||||
|
|
||||||
chmod +x /etc/rc.local
|
chmod +x /etc/rc.local
|
||||||
|
|
||||||
|
# need armv6l version of tensorflow and opencv-python, not armv7l
|
||||||
|
# PIP_OPTS="--upgrade --only-binary :all: --abi cp37m --platform linux_armv6l --target /usr/lib/python3.7/site-packages/"
|
||||||
|
# pip3 install \$PIP_OPTS opencv-python
|
||||||
|
# Should work for tensorflow too, but BUG: Hash mismatch; therefore:
|
||||||
|
wget -P /root/ -c https://www.piwheels.org/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl
|
||||||
|
wget -P /root/ -c https://www.piwheels.org/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl
|
||||||
|
# we need to install these on first raspberry start...
|
||||||
|
sed -i '/startup\.sh/i pip3 install --no-deps --force-reinstall --upgrade /root/tensorflow-1.13.1-cp37-none-linux_armv6l.whl /root/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl && rm /root/tensorflow-1.13.1-cp37-none-linux_armv6l.whl /root/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl && sed -i "/tensorflow/d" /etc/rc.local' /etc/rc.local
|
||||||
|
|
||||||
|
# newer version is broken
|
||||||
|
pip3 install gast==0.2.2
|
||||||
|
|
||||||
</root/pwnagotchi/scripts/requirements.txt xargs -I{} --max-args=1 --max-procs="$(nproc)"\
|
</root/pwnagotchi/scripts/requirements.txt xargs -I{} --max-args=1 --max-procs="$(nproc)"\
|
||||||
pip3 install --trusted-host www.piwheels.org {} >/dev/null 2>&1
|
pip3 install --progress-bar off {}
|
||||||
|
|
||||||
# waveshare
|
# waveshare
|
||||||
pip3 install --trusted-host www.piwheels.org spidev RPi.GPIO
|
pip3 install spidev RPi.GPIO
|
||||||
|
|
||||||
# install bettercap
|
# install bettercap
|
||||||
export GOPATH=/root/go
|
export GOPATH=/root/go
|
||||||
@ -134,33 +168,36 @@ function provision_raspbian() {
|
|||||||
git clone https://github.com/bettercap/caplets.git
|
git clone https://github.com/bettercap/caplets.git
|
||||||
cd caplets
|
cd caplets
|
||||||
make install
|
make install
|
||||||
|
rm -rf /tmp/caplets
|
||||||
# monstart + monstop
|
cd /root # fixes getcwd error that was bugging me
|
||||||
cat <<"STOP" > /usr/bin/monstop
|
|
||||||
#!/bin/bash
|
|
||||||
interface=mon0
|
|
||||||
ifconfig \${interface} down
|
|
||||||
sleep 1
|
|
||||||
iw dev \${interface} del
|
|
||||||
STOP
|
|
||||||
|
|
||||||
cat <<"STOP" > /usr/bin/monstart
|
|
||||||
interface=mon0
|
|
||||||
echo "Bring up monitor mode interface \${interface}"
|
|
||||||
iw phy phy0 interface add \${interface} type monitor
|
|
||||||
ifconfig \${interface} up
|
|
||||||
if [ \$? -eq 0 ]; then
|
|
||||||
echo "started monitor interface on \${interface}"
|
|
||||||
fi
|
|
||||||
STOP
|
|
||||||
|
|
||||||
chmod +x /usr/bin/{monstart,monstop}
|
|
||||||
|
|
||||||
# Re4son-Kernel
|
# Re4son-Kernel
|
||||||
echo "deb http://http.re4son-kernel.com/re4son/ kali-pi main" > /etc/apt/sources.list.d/re4son.list
|
echo "deb http://http.re4son-kernel.com/re4son/ kali-pi main" > /etc/apt/sources.list.d/re4son.list
|
||||||
wget -O - https://re4son-kernel.com/keys/http/archive-key.asc | apt-key add -
|
wget -O - https://re4son-kernel.com/keys/http/archive-key.asc | apt-key add -
|
||||||
apt update
|
apt update
|
||||||
apt install -y kalipi-kernel kalipi-bootloader kalipi-re4son-firmware kalipi-kernel-headers libraspberrypi0 libraspberrypi-dev libraspberrypi-doc libraspberrypi-bin
|
apt install -y kalipi-kernel kalipi-bootloader kalipi-re4son-firmware kalipi-kernel-headers libraspberrypi0 libraspberrypi-dev libraspberrypi-doc libraspberrypi-bin
|
||||||
|
|
||||||
|
# Fix PARTUUID
|
||||||
|
PUUID_ROOT="\$(blkid "\$(df / --output=source | tail -1)" | grep -Po 'PARTUUID="\K[^"]+')"
|
||||||
|
PUUID_BOOT="\$(blkid "\$(df /boot --output=source | tail -1)" | grep -Po 'PARTUUID="\K[^"]+')"
|
||||||
|
|
||||||
|
# sed regex info: search for line containing / followed by whitespace or /boot (second sed)
|
||||||
|
# in this line, search for PARTUUID= followed by letters, numbers or "-"
|
||||||
|
# replace that match with the new PARTUUID
|
||||||
|
sed -i "/\/[ ]\+/s/PARTUUID=[A-Za-z0-9-]\+/PARTUUID=\$PUUID_ROOT/g" /etc/fstab
|
||||||
|
sed -i "/\/boot/s/PARTUUID=[A-Za-z0-9-]\+/PARTUUID=\$PUUID_BOOT/g" /etc/fstab
|
||||||
|
|
||||||
|
sed -i "s/root=[^ ]\+/root=PARTUUID=\${PUUID_ROOT}/g" /boot/cmdline.txt
|
||||||
|
|
||||||
|
# delete keys
|
||||||
|
find /etc/ssh/ -name "ssh_host_*key*" -delete
|
||||||
|
|
||||||
|
# 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
|
EOF
|
||||||
sed -i'' 's/^#//g' etc/ld.so.preload
|
sed -i'' 's/^#//g' etc/ld.so.preload
|
||||||
cd "${REPO_DIR}"
|
cd "${REPO_DIR}"
|
||||||
@ -175,20 +212,21 @@ function usage() {
|
|||||||
usage: $0 [OPTIONS]
|
usage: $0 [OPTIONS]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-n <name> # Name of the pwnagotchi (default: pwnagotchi)
|
-n <name> # Name of the pwnagotchi (default: pwnagotchi)
|
||||||
-i <file> # Provide the path of an already downloaded raspbian image
|
-i <file> # Provide the path of an already downloaded raspbian image
|
||||||
-o <file> # Name of the img-file (default: pwnagotchi.img)
|
-o <file> # Name of the img-file (default: pwnagotchi.img)
|
||||||
-s <size> # Size which should be added to second partition (in Gigabyte) (default: 4)
|
-s <size> # Size which should be added to second partition (in Gigabyte) (default: 4)
|
||||||
-p # Only run provisioning (assumes the image is already mounted)
|
-v <version> # Version of raspbian (Supported: ${SUPPORTED_RASPBIAN_VERSIONS[*]}; default: latest)
|
||||||
-d # Only run dependencies checks
|
-p # Only run provisioning (assumes the image is already mounted)
|
||||||
-h # Show this help
|
-d # Only run dependencies checks
|
||||||
|
-h # Show this help
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
while getopts ":n:i:o:s:dph" o; do
|
while getopts ":n:i:o:s:v:dph" o; do
|
||||||
case "${o}" in
|
case "${o}" in
|
||||||
n)
|
n)
|
||||||
PWNI_NAME="${OPTARG}"
|
PWNI_NAME="${OPTARG}"
|
||||||
@ -209,6 +247,13 @@ while getopts ":n:i:o:s:dph" o; do
|
|||||||
d)
|
d)
|
||||||
OPT_CHECK_DEPS_ONLY=1
|
OPT_CHECK_DEPS_ONLY=1
|
||||||
;;
|
;;
|
||||||
|
v)
|
||||||
|
if [[ "${SUPPORTED_RASPBIAN_VERSIONS[*]}" =~ ${OPTARG} ]]; then
|
||||||
|
OPT_RASPBIAN_VERSION="${OPTARG}"
|
||||||
|
else
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
;;
|
||||||
h)
|
h)
|
||||||
usage
|
usage
|
||||||
;;
|
;;
|
||||||
@ -232,7 +277,7 @@ check_dependencies
|
|||||||
if [[ "$OPT_IMAGE_PROVIDED" -eq 1 ]]; then
|
if [[ "$OPT_IMAGE_PROVIDED" -eq 1 ]]; then
|
||||||
provide_raspbian
|
provide_raspbian
|
||||||
else
|
else
|
||||||
get_raspbian
|
get_raspbian "$OPT_RASPBIAN_VERSION"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
setup_raspbian
|
setup_raspbian
|
||||||
|
71
scripts/language.sh
Executable file
71
scripts/language.sh
Executable file
@ -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
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# name of the ethernet gadget interface on the host
|
# name of the ethernet gadget interface on the host
|
||||||
USB_IFACE=${1:-enp0s20f0u1}
|
USB_IFACE=${1:-enp0s20f0u1}
|
||||||
@ -7,12 +7,12 @@ USB_IFACE_NET=10.0.0.0/24
|
|||||||
# host interface to use for upstream connection
|
# host interface to use for upstream connection
|
||||||
UPSTREAM_IFACE=${2:-enxe4b97aa99867}
|
UPSTREAM_IFACE=${2:-enxe4b97aa99867}
|
||||||
|
|
||||||
ip addr add $USB_IFACE_IP/24 dev $USB_IFACE
|
ip addr add "$USB_IFACE_IP/24" dev "$USB_IFACE"
|
||||||
ip link set $USB_IFACE up
|
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 -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||||
iptables -t nat -F POSTROUTING
|
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
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||||
|
@ -1195,3 +1195,8 @@ dtoverlay=dwc2
|
|||||||
dtparam=spi=on
|
dtparam=spi=on
|
||||||
dtoverlay=spi1-3cs
|
dtoverlay=spi1-3cs
|
||||||
|
|
||||||
|
# Disable bluetooth
|
||||||
|
dtoverlay=pi3-disable-bt
|
||||||
|
# Disable audio
|
||||||
|
dtparam=audio=off
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ iface lo inet loopback
|
|||||||
allow-hotplug wlan0
|
allow-hotplug wlan0
|
||||||
iface wlan0 inet static
|
iface wlan0 inet static
|
||||||
|
|
||||||
|
allow-hotplug eth0
|
||||||
|
iface eth0 inet dhcp
|
||||||
|
|
||||||
allow-hotplug usb0
|
allow-hotplug usb0
|
||||||
iface usb0 inet static
|
iface usb0 inet static
|
||||||
address 10.0.0.2
|
address 10.0.0.2
|
||||||
|
@ -10,5 +10,7 @@
|
|||||||
# bits.
|
# bits.
|
||||||
#
|
#
|
||||||
# By default this script does nothing.
|
# By default this script does nothing.
|
||||||
|
# Powersave (Disable HDMI) ~30ma
|
||||||
|
/opt/vc/bin/tvservice -o
|
||||||
/root/pwnagotchi/scripts/startup.sh &
|
/root/pwnagotchi/scripts/startup.sh &
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# main algorithm configuration
|
# main algorithm configuration
|
||||||
main:
|
main:
|
||||||
|
# currently implemented: en (default), de
|
||||||
|
lang: en
|
||||||
# monitor interface to use
|
# monitor interface to use
|
||||||
iface: mon0
|
iface: mon0
|
||||||
# command to run to bring the mon interface up in case it's not up already
|
# command to run to bring the mon interface up in case it's not up already
|
||||||
@ -11,13 +13,11 @@ main:
|
|||||||
# if true, will not restart the wifi module
|
# if true, will not restart the wifi module
|
||||||
no_restart: false
|
no_restart: false
|
||||||
# access points to ignore
|
# access points to ignore
|
||||||
whitelist:
|
whitelist: []
|
||||||
- Casa-2.4
|
|
||||||
- LOTS_OF_MALWARE
|
|
||||||
# if not null, filter access points by this regular expression
|
# if not null, filter access points by this regular expression
|
||||||
filter: null
|
filter: null
|
||||||
# cryptographic key for identity
|
# cryptographic key for identity
|
||||||
pubkey: /etc/ssh/ssh_host_rsa_key.pub
|
pubkey: /etc/ssh/ssh_host_rsa_key.pub
|
||||||
|
|
||||||
ai:
|
ai:
|
||||||
# if false, only the default 'personality' will be used
|
# if false, only the default 'personality' will be used
|
||||||
@ -36,7 +36,7 @@ ai:
|
|||||||
vf_coef: 0.25
|
vf_coef: 0.25
|
||||||
# entropy coefficient for the loss calculation
|
# entropy coefficient for the loss calculation
|
||||||
ent_coef: 0.01
|
ent_coef: 0.01
|
||||||
# maximum value for the gradient clipping
|
# maximum value for the gradient clipping
|
||||||
max_grad_norm: 0.5
|
max_grad_norm: 0.5
|
||||||
# the learning rate
|
# the learning rate
|
||||||
learning_rate: 0.0010
|
learning_rate: 0.0010
|
||||||
@ -83,7 +83,7 @@ personality:
|
|||||||
# number of active epochs that triggers the excited state
|
# number of active epochs that triggers the excited state
|
||||||
excited_num_epochs: 10
|
excited_num_epochs: 10
|
||||||
# number of inactive epochs that triggers the bored state
|
# number of inactive epochs that triggers the bored state
|
||||||
bored_num_epochs: 15
|
bored_num_epochs: 15
|
||||||
# number of inactive epochs that triggers the sad state
|
# number of inactive epochs that triggers the sad state
|
||||||
sad_num_epochs: 25
|
sad_num_epochs: 25
|
||||||
|
|
||||||
@ -94,6 +94,12 @@ ui:
|
|||||||
display:
|
display:
|
||||||
enabled: true
|
enabled: true
|
||||||
rotation: 180
|
rotation: 180
|
||||||
|
# Possible options inkyphat/inky, papirus/papi, waveshare_1/ws_1 or waveshare_2/ws_2
|
||||||
|
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:
|
video:
|
||||||
enabled: true
|
enabled: true
|
||||||
address: '10.0.0.2'
|
address: '10.0.0.2'
|
||||||
@ -115,7 +121,7 @@ bettercap:
|
|||||||
port: 8081
|
port: 8081
|
||||||
username: user
|
username: user
|
||||||
password: pass
|
password: pass
|
||||||
# folder where bettercap stores the WPA handshakes, given that
|
# folder where bettercap stores the WPA handshakes, given that
|
||||||
# wifi.handshakes.aggregate will be set to false and individual
|
# wifi.handshakes.aggregate will be set to false and individual
|
||||||
# pcap files will be created in order to minimize the chances
|
# pcap files will be created in order to minimize the chances
|
||||||
# of a single pcap file to get corrupted
|
# of a single pcap file to get corrupted
|
||||||
|
@ -5,6 +5,7 @@ defscrollback 1024
|
|||||||
startup_message off
|
startup_message off
|
||||||
altscreen on
|
altscreen on
|
||||||
autodetach on
|
autodetach on
|
||||||
|
zombie kr
|
||||||
|
|
||||||
activity "activity in %n (%t)"
|
activity "activity in %n (%t)"
|
||||||
bell_msg "bell in %n (%t)"
|
bell_msg "bell in %n (%t)"
|
||||||
|
@ -5,6 +5,7 @@ defscrollback 1024
|
|||||||
startup_message off
|
startup_message off
|
||||||
altscreen on
|
altscreen on
|
||||||
autodetach on
|
autodetach on
|
||||||
|
zombie kr
|
||||||
|
|
||||||
activity "activity in %n (%t)"
|
activity "activity in %n (%t)"
|
||||||
bell_msg "bell in %n (%t)"
|
bell_msg "bell in %n (%t)"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
for i in `seq 1 $1`;
|
for i in $(seq 1 "$1");
|
||||||
do
|
do
|
||||||
echo 0 >/sys/class/leds/led0/brightness
|
echo 0 >/sys/class/leds/led0/brightness
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
@ -10,3 +10,8 @@ done
|
|||||||
|
|
||||||
echo 0 >/sys/class/leds/led0/brightness
|
echo 0 >/sys/class/leds/led0/brightness
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
|
|
||||||
|
# Powersave options
|
||||||
|
# Disable power LED ~30ma
|
||||||
|
echo none >/sys/class/leds/led0/trigger
|
||||||
|
echo 1 >/sys/class/leds/led0/brightness
|
||||||
|
@ -8,7 +8,7 @@ import core
|
|||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
|
|
||||||
from pwnagotchi.log import SessionParser
|
from pwnagotchi.log import SessionParser
|
||||||
import pwnagotchi.voice as voice
|
from pwnagotchi.voice import Voice
|
||||||
from pwnagotchi.agent import Agent
|
from pwnagotchi.agent import Agent
|
||||||
from pwnagotchi.ui.display import Display
|
from pwnagotchi.ui.display import Display
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ if args.do_manual:
|
|||||||
|
|
||||||
core.log("detected a new session and internet connectivity!")
|
core.log("detected a new session and internet connectivity!")
|
||||||
|
|
||||||
picture = '/tmp/pwnagotchi.png'
|
picture = '/dev/shm/pwnagotchi.png'
|
||||||
|
|
||||||
display.update()
|
display.update()
|
||||||
display.image().save(picture, 'png')
|
display.image().save(picture, 'png')
|
||||||
@ -74,7 +74,7 @@ if args.do_manual:
|
|||||||
auth.set_access_token(config['twitter']['access_token_key'], config['twitter']['access_token_secret'])
|
auth.set_access_token(config['twitter']['access_token_key'], config['twitter']['access_token_secret'])
|
||||||
api = tweepy.API(auth)
|
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)
|
api.update_with_media(filename=picture, status=tweet)
|
||||||
log.save_session_id()
|
log.save_session_id()
|
||||||
|
|
||||||
@ -121,9 +121,9 @@ while True:
|
|||||||
# An interesting effect of this:
|
# An interesting effect of this:
|
||||||
#
|
#
|
||||||
# From Pwnagotchi's perspective, the more new access points
|
# From Pwnagotchi's perspective, the more new access points
|
||||||
# and / or client stations nearby, the longer one epoch of
|
# and / or client stations nearby, the longer one epoch of
|
||||||
# its relative time will take ... basically, in Pwnagotchi's universe,
|
# its relative time will take ... basically, in Pwnagotchi's universe,
|
||||||
# WiFi electromagnetic fields affect time like gravitational fields
|
# WiFi electromagnetic fields affect time like gravitational fields
|
||||||
# affect ours ... neat ^_^
|
# affect ours ... neat ^_^
|
||||||
agent.next_epoch()
|
agent.next_epoch()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -54,7 +54,7 @@ class Stats(object):
|
|||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
with self._lock:
|
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)
|
core.log("[ai] loading %s" % self.path)
|
||||||
with open(self.path, 'rt') as fp:
|
with open(self.path, 'rt') as fp:
|
||||||
obj = json.load(fp)
|
obj = json.load(fp)
|
||||||
|
Binary file not shown.
@ -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"
|
@ -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 ""
|
@ -2,6 +2,7 @@ import os
|
|||||||
import hashlib
|
import hashlib
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from pwnagotchi.mesh.peer import Peer
|
from pwnagotchi.mesh.peer import Peer
|
||||||
@ -129,7 +130,7 @@ class SessionParser(object):
|
|||||||
self.duration_human.append('%d seconds' % secs)
|
self.duration_human.append('%d seconds' % secs)
|
||||||
|
|
||||||
self.duration_human = ', '.join(self.duration_human)
|
self.duration_human = ', '.join(self.duration_human)
|
||||||
self.avg_reward /= self.epochs
|
self.avg_reward /= (self.epochs if self.epochs else 1)
|
||||||
|
|
||||||
def __init__(self, path='/var/log/pwnagotchi.log'):
|
def __init__(self, path='/var/log/pwnagotchi.log'):
|
||||||
self.path = path
|
self.path = path
|
||||||
@ -147,15 +148,17 @@ class SessionParser(object):
|
|||||||
'detected unit (.+)@(.+) \(v.+\) on channel \d+ \(([\d\-]+) dBm\) \[sid:(.+) pwnd_tot:(\d+) uptime:(\d+)\]')
|
'detected unit (.+)@(.+) \(v.+\) on channel \d+ \(([\d\-]+) dBm\) \[sid:(.+) pwnd_tot:(\d+) uptime:(\d+)\]')
|
||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
with FileReadBackwards(self.path, encoding="utf-8") as fp:
|
|
||||||
for line in fp:
|
if os.path.exists(self.path):
|
||||||
line = line.strip()
|
with FileReadBackwards(self.path, encoding="utf-8") as fp:
|
||||||
if line != "" and line[0] != '[':
|
for line in fp:
|
||||||
continue
|
line = line.strip()
|
||||||
lines.append(line)
|
if line != "" and line[0] != '[':
|
||||||
if SessionParser.START_TOKEN in line:
|
continue
|
||||||
break
|
lines.append(line)
|
||||||
lines.reverse()
|
if SessionParser.START_TOKEN in line:
|
||||||
|
break
|
||||||
|
lines.reverse()
|
||||||
|
|
||||||
self.last_session = lines
|
self.last_session = lines
|
||||||
self.last_session_id = hashlib.md5(lines[0].encode()).hexdigest()
|
self.last_session_id = hashlib.md5(lines[0].encode()).hexdigest()
|
||||||
|
@ -3,6 +3,7 @@ from threading import Lock
|
|||||||
|
|
||||||
import io
|
import io
|
||||||
import core
|
import core
|
||||||
|
import os
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
|
|
||||||
from pwnagotchi.ui.view import WHITE, View
|
from pwnagotchi.ui.view import WHITE, View
|
||||||
@ -19,7 +20,7 @@ class VideoHandler(BaseHTTPRequestHandler):
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<img src="/ui" id="ui"/>
|
<img src="/ui" id="ui"/>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
var image = document.getElementById("ui");
|
var image = document.getElementById("ui");
|
||||||
@ -77,6 +78,12 @@ class Display(View):
|
|||||||
self._video_enabled = config['ui']['display']['video']['enabled']
|
self._video_enabled = config['ui']['display']['video']['enabled']
|
||||||
self._video_port = config['ui']['display']['video']['port']
|
self._video_port = config['ui']['display']['video']['port']
|
||||||
self._video_address = config['ui']['display']['video']['address']
|
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._display = None
|
||||||
self._httpd = None
|
self._httpd = None
|
||||||
self.canvas = None
|
self.canvas = None
|
||||||
@ -98,26 +105,117 @@ class Display(View):
|
|||||||
else:
|
else:
|
||||||
core.log("could not get ip of usb0, video server not starting")
|
core.log("could not get ip of usb0, video server not starting")
|
||||||
|
|
||||||
|
def _is_inky(self):
|
||||||
|
return self._display_type in ('inkyphat', 'inky')
|
||||||
|
|
||||||
|
def _is_papirus(self):
|
||||||
|
return self._display_type in ('papirus', 'papi')
|
||||||
|
|
||||||
|
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):
|
def _init_display(self):
|
||||||
from pwnagotchi.ui.waveshare import EPD
|
if self._is_inky():
|
||||||
# core.log("display module started")
|
from inky import InkyPHAT
|
||||||
self._display = EPD()
|
self._display = InkyPHAT(self._display_color)
|
||||||
self._display.init(self._display.FULL_UPDATE)
|
self._display.set_border(InkyPHAT.BLACK)
|
||||||
self._display.Clear(WHITE)
|
self._render_cb = self._inky_render
|
||||||
self._display.init(self._display.PART_UPDATE)
|
|
||||||
|
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_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)
|
||||||
|
|
||||||
self.on_render(self._on_view_rendered)
|
self.on_render(self._on_view_rendered)
|
||||||
|
|
||||||
|
core.log("display type '%s' initialized (color:%s)" % (self._display_type, self._display_color))
|
||||||
|
|
||||||
def image(self):
|
def image(self):
|
||||||
img = None
|
img = None
|
||||||
if self.canvas is not None:
|
if self.canvas is not None:
|
||||||
img = self.canvas if self._rotation == 0 else self.canvas.rotate(-self._rotation)
|
img = self.canvas if self._rotation == 0 else self.canvas.rotate(-self._rotation)
|
||||||
return img
|
return img
|
||||||
|
|
||||||
|
def _inky_render(self):
|
||||||
|
if self._display_color != 'mono':
|
||||||
|
display_colors = 3
|
||||||
|
else:
|
||||||
|
display_colors = 2
|
||||||
|
|
||||||
|
imgbuf = self.canvas.convert('RGB').convert('P', palette=1, colors=display_colors)
|
||||||
|
|
||||||
|
if self._display_color == 'red':
|
||||||
|
imgbuf.putpalette([
|
||||||
|
255, 255, 255, # index 0 is white
|
||||||
|
0, 0, 0, # index 1 is black
|
||||||
|
255, 0, 0 # index 2 is red
|
||||||
|
])
|
||||||
|
elif self._display_color == 'yellow':
|
||||||
|
imgbuf.putpalette([
|
||||||
|
255, 255, 255, # index 0 is white
|
||||||
|
0, 0, 0, # index 1 is black
|
||||||
|
255, 255, 0 # index 2 is yellow
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
imgbuf.putpalette([
|
||||||
|
255, 255, 255, # index 0 is white
|
||||||
|
0, 0, 0 # index 1 is black
|
||||||
|
])
|
||||||
|
|
||||||
|
self._display.set_image(imgbuf)
|
||||||
|
self._display.show()
|
||||||
|
|
||||||
|
def _papirus_render(self):
|
||||||
|
self._display.display(self.canvas)
|
||||||
|
self._display.partial_update()
|
||||||
|
|
||||||
|
def _waveshare_render(self):
|
||||||
|
buf = self._display.getbuffer(self.canvas)
|
||||||
|
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):
|
def _on_view_rendered(self, img):
|
||||||
# core.log("display::_on_view_rendered")
|
# core.log("display::_on_view_rendered")
|
||||||
VideoHandler.render(img)
|
VideoHandler.render(img)
|
||||||
|
|
||||||
if self._enabled:
|
if self._enabled:
|
||||||
self.canvas = img if self._rotation == 0 else img.rotate(self._rotation)
|
self.canvas = img if self._rotation == 0 else img.rotate(self._rotation)
|
||||||
buf = self._display.getbuffer(self.canvas)
|
if self._render_cb is not None:
|
||||||
self._display.displayPartial(buf)
|
self._render_cb()
|
||||||
|
@ -3,6 +3,14 @@ from PIL import ImageFont
|
|||||||
PATH = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono'
|
PATH = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono'
|
||||||
|
|
||||||
Bold = ImageFont.truetype("%s-Bold.ttf" % PATH, 10)
|
Bold = ImageFont.truetype("%s-Bold.ttf" % PATH, 10)
|
||||||
BoldSmall = ImageFont.truetype("%s-Bold.ttf" % PATH, 9)
|
BoldSmall = ImageFont.truetype("%s-Bold.ttf" % PATH, 8)
|
||||||
Medium = ImageFont.truetype("%s.ttf" % PATH, 10)
|
Medium = ImageFont.truetype("%s.ttf" % PATH, 10)
|
||||||
Huge = ImageFont.truetype("%s-Bold.ttf" % PATH, 35)
|
Huge = ImageFont.truetype("%s-Bold.ttf" % PATH, 25)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bold, bold_small, medium, huge):
|
||||||
|
global PATH, Bold, BoldSmall, Medium, Huge
|
||||||
|
Bold = ImageFont.truetype("%s-Bold.ttf" % PATH, bold)
|
||||||
|
BoldSmall = ImageFont.truetype("%s-Bold.ttf" % PATH, bold_small)
|
||||||
|
Medium = ImageFont.truetype("%s.ttf" % PATH, medium)
|
||||||
|
Huge = ImageFont.truetype("%s-Bold.ttf" % PATH, huge)
|
||||||
|
@ -5,7 +5,7 @@ from PIL import Image, ImageDraw
|
|||||||
|
|
||||||
import core
|
import core
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
from pwnagotchi import voice
|
from pwnagotchi.voice import Voice
|
||||||
|
|
||||||
import pwnagotchi.ui.fonts as fonts
|
import pwnagotchi.ui.fonts as fonts
|
||||||
import pwnagotchi.ui.faces as faces
|
import pwnagotchi.ui.faces as faces
|
||||||
@ -14,8 +14,44 @@ from pwnagotchi.ui.state import State
|
|||||||
|
|
||||||
WHITE = 0xff
|
WHITE = 0xff
|
||||||
BLACK = 0x00
|
BLACK = 0x00
|
||||||
WIDTH = 122
|
|
||||||
HEIGHT = 250
|
|
||||||
|
def setup_display_specifics(config):
|
||||||
|
width = 0
|
||||||
|
height = 0
|
||||||
|
face_pos = (0, 0)
|
||||||
|
name_pos = (0, 0)
|
||||||
|
status_pos = (0, 0)
|
||||||
|
|
||||||
|
if config['ui']['display']['type'] in ('inky', 'inkyphat'):
|
||||||
|
fonts.setup(10, 8, 10, 25)
|
||||||
|
|
||||||
|
width = 212
|
||||||
|
height = 104
|
||||||
|
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)
|
||||||
|
|
||||||
|
width = 200
|
||||||
|
height = 96
|
||||||
|
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_1', 'ws1', 'waveshare_1', 'waveshare1',
|
||||||
|
'ws_2', 'ws2', 'waveshare_2', 'waveshare2'):
|
||||||
|
fonts.setup(10, 9, 10, 35)
|
||||||
|
|
||||||
|
width = 250
|
||||||
|
height = 122
|
||||||
|
face_pos = (0, 40)
|
||||||
|
name_pos = (125, 20)
|
||||||
|
status_pos = (125, 35)
|
||||||
|
|
||||||
|
return width, height, face_pos, name_pos, status_pos
|
||||||
|
|
||||||
|
|
||||||
class View(object):
|
class View(object):
|
||||||
@ -24,34 +60,46 @@ class View(object):
|
|||||||
self._config = config
|
self._config = config
|
||||||
self._canvas = None
|
self._canvas = None
|
||||||
self._lock = Lock()
|
self._lock = Lock()
|
||||||
|
self._voice = Voice(lang=config['main']['lang'])
|
||||||
|
|
||||||
|
self._width, self._height, \
|
||||||
|
face_pos, name_pos, status_pos = setup_display_specifics(config)
|
||||||
|
|
||||||
self._state = State(state={
|
self._state = State(state={
|
||||||
'channel': LabeledValue(color=BLACK, label='CH', value='00', position=(0, 0), label_font=fonts.Bold,
|
'channel': LabeledValue(color=BLACK, label='CH', value='00', position=(0, 0), label_font=fonts.Bold,
|
||||||
text_font=fonts.Medium),
|
text_font=fonts.Medium),
|
||||||
'aps': LabeledValue(color=BLACK, label='APS', value='0 (00)', position=(30, 0), label_font=fonts.Bold,
|
'aps': LabeledValue(color=BLACK, label='APS', value='0 (00)', position=(30, 0), label_font=fonts.Bold,
|
||||||
text_font=fonts.Medium),
|
text_font=fonts.Medium),
|
||||||
#'epoch': LabeledValue(color=BLACK, label='E', value='0000', position=(145, 0), label_font=fonts.Bold,
|
|
||||||
|
# 'epoch': LabeledValue(color=BLACK, label='E', value='0000', position=(145, 0), label_font=fonts.Bold,
|
||||||
# text_font=fonts.Medium),
|
# text_font=fonts.Medium),
|
||||||
'uptime': LabeledValue(color=BLACK, label='UP', value='00:00:00', position=(185, 0), label_font=fonts.Bold,
|
|
||||||
|
'uptime': LabeledValue(color=BLACK, label='UP', value='00:00:00', position=(self._width - 65, 0),
|
||||||
|
label_font=fonts.Bold,
|
||||||
text_font=fonts.Medium),
|
text_font=fonts.Medium),
|
||||||
|
|
||||||
# 'square': Rect([1, 11, 124, 111]),
|
# 'square': Rect([1, 11, 124, 111]),
|
||||||
'line1': Line([0, 13, 250, 13], color=BLACK),
|
'line1': Line([0, int(self._height * .12), self._width, int(self._height * .12)], color=BLACK),
|
||||||
'line2': Line([0, 109, 250, 109], color=BLACK),
|
'line2': Line(
|
||||||
|
[0, self._height - int(self._height * .12), self._width, self._height - int(self._height * .12)],
|
||||||
|
color=BLACK),
|
||||||
|
|
||||||
# 'histogram': Histogram([4, 94], color = BLACK),
|
# 'histogram': Histogram([4, 94], color = BLACK),
|
||||||
|
|
||||||
'face': Text(value=faces.SLEEP, position=(0, 40), color=BLACK, font=fonts.Huge),
|
'face': Text(value=faces.SLEEP, position=face_pos, color=BLACK, font=fonts.Huge),
|
||||||
|
|
||||||
'friend_face': Text(value=None, position=(0, 90), font=fonts.Bold, color=BLACK),
|
'friend_face': Text(value=None, position=(0, 90), font=fonts.Bold, color=BLACK),
|
||||||
'friend_name': Text(value=None, position=(40, 93), font=fonts.BoldSmall, color=BLACK),
|
'friend_name': Text(value=None, position=(40, 93), font=fonts.BoldSmall, color=BLACK),
|
||||||
|
|
||||||
'name': Text(value='%s>' % 'pwnagotchi', position=(125, 20), color=BLACK, font=fonts.Bold),
|
'name': Text(value='%s>' % 'pwnagotchi', position=name_pos, color=BLACK, font=fonts.Bold),
|
||||||
# 'face2': Bitmap( '/root/pwnagotchi/data/images/face_happy.bmp', (0, 20)),
|
# 'face2': Bitmap( '/root/pwnagotchi/data/images/face_happy.bmp', (0, 20)),
|
||||||
'status': Text(value=voice.default(), position=(125, 35), 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, 110), label_font=fonts.Bold,
|
'shakes': LabeledValue(label='PWND ', value='0 (00)', color=BLACK,
|
||||||
|
position=(0, self._height - int(self._height * .12) + 1), label_font=fonts.Bold,
|
||||||
text_font=fonts.Medium),
|
text_font=fonts.Medium),
|
||||||
'mode': Text(value='AUTO', position=(225, 110), font=fonts.Bold, color=BLACK),
|
'mode': Text(value='AUTO', position=(self._width - 25, self._height - int(self._height * .12) + 1),
|
||||||
|
font=fonts.Bold, color=BLACK),
|
||||||
})
|
})
|
||||||
|
|
||||||
for key, value in state.items():
|
for key, value in state.items():
|
||||||
@ -80,19 +128,19 @@ class View(object):
|
|||||||
self._state.set(key, value)
|
self._state.set(key, value)
|
||||||
|
|
||||||
def on_starting(self):
|
def on_starting(self):
|
||||||
self.set('status', voice.on_starting())
|
self.set('status', self._voice.on_starting())
|
||||||
self.set('face', faces.AWAKE)
|
self.set('face', faces.AWAKE)
|
||||||
|
|
||||||
def on_ai_ready(self):
|
def on_ai_ready(self):
|
||||||
self.set('mode', '')
|
self.set('mode', '')
|
||||||
self.set('face', faces.HAPPY)
|
self.set('face', faces.HAPPY)
|
||||||
self.set('status', voice.on_ai_ready())
|
self.set('status', self._voice.on_ai_ready())
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_manual_mode(self, log):
|
def on_manual_mode(self, log):
|
||||||
self.set('mode', 'MANU')
|
self.set('mode', 'MANU')
|
||||||
self.set('face', faces.SAD if log.handshakes == 0 else faces.HAPPY)
|
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('epoch', "%04d" % log.epochs)
|
||||||
self.set('uptime', log.duration)
|
self.set('uptime', log.duration)
|
||||||
self.set('channel', '-')
|
self.set('channel', '-')
|
||||||
@ -116,7 +164,7 @@ class View(object):
|
|||||||
|
|
||||||
def on_normal(self):
|
def on_normal(self):
|
||||||
self.set('face', faces.AWAKE)
|
self.set('face', faces.AWAKE)
|
||||||
self.set('status', voice.on_normal())
|
self.set('status', self._voice.on_normal())
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def set_closest_peer(self, peer):
|
def set_closest_peer(self, peer):
|
||||||
@ -144,17 +192,17 @@ class View(object):
|
|||||||
|
|
||||||
def on_new_peer(self, peer):
|
def on_new_peer(self, peer):
|
||||||
self.set('face', faces.FRIEND)
|
self.set('face', faces.FRIEND)
|
||||||
self.set('status', voice.on_new_peer(peer))
|
self.set('status', self._voice.on_new_peer(peer))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_lost_peer(self, peer):
|
def on_lost_peer(self, peer):
|
||||||
self.set('face', faces.LONELY)
|
self.set('face', faces.LONELY)
|
||||||
self.set('status', voice.on_lost_peer(peer))
|
self.set('status', self._voice.on_lost_peer(peer))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_free_channel(self, channel):
|
def on_free_channel(self, channel):
|
||||||
self.set('face', faces.SMART)
|
self.set('face', faces.SMART)
|
||||||
self.set('status', voice.on_free_channel(channel))
|
self.set('status', self._voice.on_free_channel(channel))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def wait(self, secs, sleeping=True):
|
def wait(self, secs, sleeping=True):
|
||||||
@ -163,19 +211,19 @@ class View(object):
|
|||||||
|
|
||||||
for step in range(0, 10):
|
for step in range(0, 10):
|
||||||
# if we weren't in a normal state before goin
|
# if we weren't in a normal state before goin
|
||||||
# to sleep, keep that face and status on for
|
# to sleep, keep that face and status on for
|
||||||
# a while, otherwise the sleep animation will
|
# a while, otherwise the sleep animation will
|
||||||
# always override any minor state change before it
|
# always override any minor state change before it
|
||||||
if was_normal or step > 5:
|
if was_normal or step > 5:
|
||||||
if sleeping:
|
if sleeping:
|
||||||
if secs > 1:
|
if secs > 1:
|
||||||
self.set('face', faces.SLEEP)
|
self.set('face', faces.SLEEP)
|
||||||
self.set('status', voice.on_napping(secs))
|
self.set('status', self._voice.on_napping(secs))
|
||||||
else:
|
else:
|
||||||
self.set('face', faces.SLEEP2)
|
self.set('face', faces.SLEEP2)
|
||||||
self.set('status', voice.on_awakening())
|
self.set('status', self._voice.on_awakening())
|
||||||
else:
|
else:
|
||||||
self.set('status', voice.on_waiting(secs))
|
self.set('status', self._voice.on_waiting(secs))
|
||||||
if step % 2 == 0:
|
if step % 2 == 0:
|
||||||
self.set('face', faces.LOOK_R)
|
self.set('face', faces.LOOK_R)
|
||||||
else:
|
else:
|
||||||
@ -188,112 +236,62 @@ class View(object):
|
|||||||
|
|
||||||
def on_bored(self):
|
def on_bored(self):
|
||||||
self.set('face', faces.BORED)
|
self.set('face', faces.BORED)
|
||||||
self.set('status', voice.on_bored())
|
self.set('status', self._voice.on_bored())
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_sad(self):
|
def on_sad(self):
|
||||||
self.set('face', faces.SAD)
|
self.set('face', faces.SAD)
|
||||||
self.set('status', voice.on_sad())
|
self.set('status', self._voice.on_sad())
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_motivated(self, reward):
|
def on_motivated(self, reward):
|
||||||
self.set('face', faces.MOTIVATED)
|
self.set('face', faces.MOTIVATED)
|
||||||
self.set('status', voice.on_motivated(reward))
|
self.set('status', self._voice.on_motivated(reward))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_demotivated(self, reward):
|
def on_demotivated(self, reward):
|
||||||
self.set('face', faces.DEMOTIVATED)
|
self.set('face', faces.DEMOTIVATED)
|
||||||
self.set('status', voice.on_demotivated(reward))
|
self.set('status', self._voice.on_demotivated(reward))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_excited(self):
|
def on_excited(self):
|
||||||
self.set('face', faces.EXCITED)
|
self.set('face', faces.EXCITED)
|
||||||
self.set('status', voice.on_excited())
|
self.set('status', self._voice.on_excited())
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_assoc(self, ap):
|
def on_assoc(self, ap):
|
||||||
self.set('face', faces.INTENSE)
|
self.set('face', faces.INTENSE)
|
||||||
self.set('status', voice.on_assoc(ap))
|
self.set('status', self._voice.on_assoc(ap))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_deauth(self, sta):
|
def on_deauth(self, sta):
|
||||||
self.set('face', faces.COOL)
|
self.set('face', faces.COOL)
|
||||||
self.set('status', voice.on_deauth(sta))
|
self.set('status', self._voice.on_deauth(sta))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_miss(self, who):
|
def on_miss(self, who):
|
||||||
self.set('face', faces.SAD)
|
self.set('face', faces.SAD)
|
||||||
self.set('status', voice.on_miss(who))
|
self.set('status', self._voice.on_miss(who))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_lonely(self):
|
def on_lonely(self):
|
||||||
self.set('face', faces.LONELY)
|
self.set('face', faces.LONELY)
|
||||||
self.set('status', voice.on_lonely())
|
self.set('status', self._voice.on_lonely())
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_handshakes(self, new_shakes):
|
def on_handshakes(self, new_shakes):
|
||||||
self.set('face', faces.HAPPY)
|
self.set('face', faces.HAPPY)
|
||||||
self.set('status', voice.on_handshakes(new_shakes))
|
self.set('status', self._voice.on_handshakes(new_shakes))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def on_rebooting(self):
|
def on_rebooting(self):
|
||||||
self.set('face', faces.BROKEN)
|
self.set('face', faces.BROKEN)
|
||||||
self.set('status', voice.on_rebooting())
|
self.set('status', self._voice.on_rebooting())
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""
|
|
||||||
ncalls tottime percall cumtime percall filename:lineno(function)
|
|
||||||
|
|
||||||
19 0.001 0.000 0.007 0.000 Image.py:1137(copy)
|
|
||||||
19 0.001 0.000 0.069 0.004 Image.py:1894(rotate)
|
|
||||||
19 0.001 0.000 0.068 0.004 Image.py:2388(transpose)
|
|
||||||
1 0.000 0.000 0.000 0.000 Image.py:2432(ImagePointHandler)
|
|
||||||
1 0.000 0.000 0.000 0.000 Image.py:2437(ImageTransformHandler)
|
|
||||||
19 0.001 0.000 0.001 0.000 Image.py:2455(_check_size)
|
|
||||||
19 0.002 0.000 0.010 0.001 Image.py:2473(new)
|
|
||||||
1 0.002 0.002 0.127 0.127 Image.py:30(<module>)
|
|
||||||
1 0.000 0.000 0.001 0.001 Image.py:3103(_apply_env_variables)
|
|
||||||
1 0.000 0.000 0.000 0.000 Image.py:3139(Exif)
|
|
||||||
1 0.000 0.000 0.000 0.000 Image.py:495(_E)
|
|
||||||
1 0.000 0.000 0.000 0.000 Image.py:536(Image)
|
|
||||||
76 0.003 0.000 0.003 0.000 Image.py:552(__init__)
|
|
||||||
19 0.000 0.000 0.000 0.000 Image.py:572(size)
|
|
||||||
57 0.004 0.000 0.006 0.000 Image.py:576(_new)
|
|
||||||
76 0.001 0.000 0.002 0.000 Image.py:595(__exit__)
|
|
||||||
76 0.001 0.000 0.003 0.000 Image.py:633(__del__)
|
|
||||||
1 0.000 0.000 0.000 0.000 Image.py:71(DecompressionBombWarning)
|
|
||||||
1 0.000 0.000 0.000 0.000 Image.py:75(DecompressionBombError)
|
|
||||||
1 0.000 0.000 0.000 0.000 Image.py:79(_imaging_not_installed)
|
|
||||||
95 0.002 0.000 0.014 0.000 Image.py:842(load)
|
|
||||||
19 0.001 0.000 0.008 0.000 Image.py:892(convert)
|
|
||||||
1 0.000 0.000 0.000 0.000 ImageColor.py:20(<module>)
|
|
||||||
297 0.012 0.000 0.042 0.000 ImageDraw.py:101(_getink)
|
|
||||||
38 0.001 0.000 0.026 0.001 ImageDraw.py:153(line)
|
|
||||||
295 0.005 0.000 0.007 0.000 ImageDraw.py:252(_multiline_check)
|
|
||||||
8 0.000 0.000 0.001 0.000 ImageDraw.py:258(_multiline_split)
|
|
||||||
267/247 0.033 0.000 1.741 0.007 ImageDraw.py:263(text)
|
|
||||||
8 0.003 0.000 0.237 0.030 ImageDraw.py:282(multiline_text)
|
|
||||||
28 0.001 0.000 0.064 0.002 ImageDraw.py:328(textsize)
|
|
||||||
1 0.000 0.000 0.008 0.008 ImageDraw.py:33(<module>)
|
|
||||||
19 0.002 0.000 0.006 0.000 ImageDraw.py:355(Draw)
|
|
||||||
1 0.000 0.000 0.000 0.000 ImageDraw.py:47(ImageDraw)
|
|
||||||
19 0.002 0.000 0.004 0.000 ImageDraw.py:48(__init__)
|
|
||||||
1 0.000 0.000 0.000 0.000 ImageFont.py:123(FreeTypeFont)
|
|
||||||
3 0.000 0.000 0.002 0.001 ImageFont.py:126(__init__)
|
|
||||||
28 0.001 0.000 0.062 0.002 ImageFont.py:185(getsize)
|
|
||||||
1 0.000 0.000 0.011 0.011 ImageFont.py:28(<module>)
|
|
||||||
259 0.020 0.000 1.435 0.006 ImageFont.py:337(getmask2)
|
|
||||||
1 0.000 0.000 0.000 0.000 ImageFont.py:37(_imagingft_not_installed)
|
|
||||||
1 0.000 0.000 0.000 0.000 ImageFont.py:474(TransposedFont)
|
|
||||||
3 0.000 0.000 0.003 0.001 ImageFont.py:517(truetype)
|
|
||||||
3 0.000 0.000 0.002 0.001 ImageFont.py:542(freetype)
|
|
||||||
1 0.000 0.000 0.000 0.000 ImageFont.py:65(ImageFont)
|
|
||||||
1 0.000 0.000 0.000 0.000 ImageMode.py:17(<module>)
|
|
||||||
1 0.000 0.000 0.000 0.000 ImageMode.py:20(ModeDescriptor)
|
|
||||||
"""
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._canvas = Image.new('1', (HEIGHT, WIDTH), WHITE)
|
self._canvas = Image.new('1', (self._width, self._height), WHITE)
|
||||||
drawer = ImageDraw.Draw(self._canvas)
|
drawer = ImageDraw.Draw(self._canvas)
|
||||||
|
|
||||||
for key, lv in self._state.items():
|
for key, lv in self._state.items():
|
||||||
|
@ -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 ###
|
||||||
|
|
@ -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 ###
|
@ -1,147 +1,160 @@
|
|||||||
import random
|
import random
|
||||||
|
import gettext
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
def default():
|
class Voice:
|
||||||
return 'ZzzzZZzzzzZzzz'
|
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():
|
def on_starting(self):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'Hi, I\'m Pwnagotchi!\nStarting ...',
|
self._('Hi, I\'m Pwnagotchi!\nStarting ...'),
|
||||||
'New day, new hunt,\nnew pwns!',
|
self._('New day, new hunt,\nnew pwns!'),
|
||||||
'Hack the Planet!'])
|
self._('Hack the Planet!')])
|
||||||
|
|
||||||
|
|
||||||
def on_ai_ready():
|
def on_ai_ready(self):
|
||||||
return random.choice([
|
return random.choice([
|
||||||
'AI ready.',
|
self._('AI ready.'),
|
||||||
'The neural network\nis ready.'])
|
self._('The neural network\nis ready.')])
|
||||||
|
|
||||||
|
|
||||||
def on_normal():
|
def on_normal(self):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'',
|
'',
|
||||||
'...'])
|
'...'])
|
||||||
|
|
||||||
|
|
||||||
def on_free_channel(channel):
|
def on_free_channel(channel):
|
||||||
return 'Hey, channel %d is\nfree! Your AP will\nsay thanks.' % channel
|
return self._('Hey, channel {channel} is\nfree! Your AP will\nsay thanks.').format(channel=channel)
|
||||||
|
|
||||||
|
|
||||||
def on_bored():
|
def on_bored():
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'I\'m bored ...',
|
self._('I\'m bored ...'),
|
||||||
'Let\'s go for a walk!'])
|
self._('Let\'s go for a walk!')])
|
||||||
|
|
||||||
|
|
||||||
def on_motivated(reward):
|
def on_motivated(self, reward):
|
||||||
return 'This is the best\nday of my life!'
|
return self._('This is the best\nday of my life!')
|
||||||
|
|
||||||
|
|
||||||
def on_demotivated(reward):
|
def on_demotivated(self, reward):
|
||||||
return 'Shitty day :/'
|
return self._('Shitty day :/')
|
||||||
|
|
||||||
|
|
||||||
def on_sad():
|
def on_sad(self):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'I\'m extremely bored ...',
|
self._('I\'m extremely bored ...'),
|
||||||
'I\'m very sad ...',
|
self._('I\'m very sad ...'),
|
||||||
'I\'m sad',
|
self._('I\'m sad'),
|
||||||
'...'])
|
'...'])
|
||||||
|
|
||||||
|
|
||||||
def on_excited():
|
def on_excited(self):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'I\'m living the life!',
|
self._('I\'m living the life!'),
|
||||||
'I pwn therefore I am.',
|
self._('I pwn therefore I am.'),
|
||||||
'So many networks!!!',
|
self._('So many networks!!!'),
|
||||||
'I\'m having so much\nfun!',
|
self._('I\'m having so much\nfun!'),
|
||||||
'My crime is that of\ncuriosity ...'])
|
self._('My crime is that of\ncuriosity ...')])
|
||||||
|
|
||||||
|
|
||||||
def on_new_peer(peer):
|
def on_new_peer(self, peer):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'Hello\n%s!\nNice to meet you.' % peer.name(),
|
self._('Hello\n{name}!\nNice to meet you. {name}').format(name=peer.name()),
|
||||||
'Unit\n%s\nis nearby!' % peer.name()])
|
self._('Unit\n{name}\nis nearby! {name}').format(name=peer.name())])
|
||||||
|
|
||||||
|
|
||||||
def on_lost_peer(peer):
|
def on_lost_peer(self, peer):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'Uhm ...\ngoodbye\n%s' % peer.name(),
|
self._('Uhm ...\ngoodbye\n{name}').format(name=peer.name()),
|
||||||
'%s\nis gone ...' % peer.name()])
|
self._('{name}\nis gone ...').format(name=peer.name())])
|
||||||
|
|
||||||
|
|
||||||
def on_miss(who):
|
def on_miss(self, who):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'Whops ...\n%s\nis gone.' % who,
|
self._('Whoops ...\n{name}\nis gone.').format(name=who),
|
||||||
'%s\nmissed!' % who,
|
self._('{name}\nmissed!').format(name=who),
|
||||||
'Missed!'])
|
self._('Missed!')])
|
||||||
|
|
||||||
|
|
||||||
def on_lonely():
|
def on_lonely(self):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'Nobody wants to\nplay with me ...',
|
self._('Nobody wants to\nplay with me ...'),
|
||||||
'I feel so alone ...',
|
self._('I feel so alone ...'),
|
||||||
'Where\'s everybody?!'])
|
self._('Where\'s everybody?!')])
|
||||||
|
|
||||||
|
|
||||||
def on_napping(secs):
|
def on_napping(self,secs):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'Napping for %ds ...' % secs,
|
self._('Napping for {secs}s ...').format(secs=secs),
|
||||||
'Zzzzz',
|
self._('Zzzzz'),
|
||||||
'ZzzZzzz (%ds)' % secs])
|
self._('ZzzZzzz ({secs}s)').format(secs=secs)])
|
||||||
|
|
||||||
|
|
||||||
def on_awakening():
|
def on_awakening(self):
|
||||||
return random.choice(['...', '!'])
|
return random.choice(['...', '!'])
|
||||||
|
|
||||||
|
|
||||||
def on_waiting(secs):
|
def on_waiting(self,secs):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'Waiting for %ds ...' % secs,
|
self._('Waiting for {secs}s ...').format(secs=secs),
|
||||||
'...',
|
'...',
|
||||||
'Looking around (%ds)' % secs])
|
self._('Looking around ({secs}s)').format(secs=secs)])
|
||||||
|
|
||||||
|
|
||||||
def on_assoc(ap):
|
def on_assoc(self,ap):
|
||||||
ssid, bssid = ap['hostname'], ap['mac']
|
ssid, bssid = ap['hostname'], ap['mac']
|
||||||
what = ssid if ssid != '' and ssid != '<hidden>' else bssid
|
what = ssid if ssid != '' and ssid != '<hidden>' else bssid
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'Hey\n%s\nlet\'s be friends!' % what,
|
self._('Hey\n{what}\nlet\'s be friends!').format(what=what),
|
||||||
'Associating to\n%s' % what,
|
self._('Associating to\n{what}').format(what=what),
|
||||||
'Yo\n%s!' % what])
|
self._('Yo\n{what}!').format(what=what)])
|
||||||
|
|
||||||
|
|
||||||
def on_deauth(sta):
|
def on_deauth(self,sta):
|
||||||
return random.choice([ \
|
return random.choice([ \
|
||||||
'Just decided that\n%s\nneeds no WiFi!' % sta['mac'],
|
self._('Just decided that\n{mac}\nneeds no WiFi!').format(mac=sta['mac']),
|
||||||
'Deauthenticating\n%s' % sta['mac'],
|
self._('Deauthenticating\n{mac}').format(mac=sta['mac']),
|
||||||
'Kickbanning\n%s!' % sta['mac']])
|
self._('Kickbanning\n{mac}!').format(mac=sta['mac'])])
|
||||||
|
|
||||||
|
|
||||||
def on_handshakes(new_shakes):
|
def on_handshakes(self,new_shakes):
|
||||||
s = 's' if new_shakes > 1 else ''
|
s = 's' if new_shakes > 1 else ''
|
||||||
return 'Cool, we got %d\nnew handshake%s!' % (new_shakes, s)
|
return self._('Cool, we got {num}\nnew handshake{plural}!').format(num=new_shakes, plural=s)
|
||||||
|
|
||||||
|
|
||||||
def on_rebooting():
|
def on_rebooting(self):
|
||||||
return "Ops, something\nwent wrong ...\nRebooting ..."
|
return self._("Ops, something\nwent wrong ...\nRebooting ...")
|
||||||
|
|
||||||
|
|
||||||
def on_log(log):
|
def on_log(self,log):
|
||||||
status = 'Kicked %d stations\n' % log.deauthed
|
status = self._('Kicked {num} stations\n').format(num=log.deauthed)
|
||||||
status += 'Made %d new friends\n' % log.associated
|
status += self._('Made {num} new friends\n').format(num=log.associated)
|
||||||
status += 'Got %d handshakes\n' % log.handshakes
|
status += self._('Got {num} handshakes\n').format(num=log.handshakes)
|
||||||
if log.peers == 1:
|
if log.peers == 1:
|
||||||
status += 'Met 1 peer'
|
status += self._('Met 1 peer')
|
||||||
elif log.peers > 0:
|
elif log.peers > 0:
|
||||||
status += 'Met %d peers' % log.peers
|
status += self._('Met {num} peers').format(num=log.peers)
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
|
||||||
def on_log_tweet(log):
|
def on_log_tweet(self,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' % ( \
|
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(
|
||||||
log.duration_human,
|
duration=log.duration_human,
|
||||||
log.deauthed,
|
deauthed=log.deauthed,
|
||||||
log.associated,
|
associated=log.associated,
|
||||||
log.handshakes)
|
handshakes=log.handshakes)
|
||||||
|
@ -8,3 +8,4 @@ tensorflow
|
|||||||
tweepy
|
tweepy
|
||||||
file_read_backwards
|
file_read_backwards
|
||||||
numpy
|
numpy
|
||||||
|
inky
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# blink 10 times to signal ready state
|
# blink 10 times to signal ready state
|
||||||
/root/pwnagotchi/scripts/blink.sh 10 &
|
/root/pwnagotchi/scripts/blink.sh 10 &
|
||||||
|
Loading…
x
Reference in New Issue
Block a user