Updated and coverted
Updated and coverted my fork from evilsocket/pwnagotchi master branch to aluminum-ice/pwnagotchi master branch removed hannadiamond repository changed pwnagotchi community plugin repository to my pwnagotchi community plugin repository removed mastodon plugin removed screenrc configuration cloned pwnagotchi community plugin repository only once removed configure pwnagotchi for the custom plugin directory from builder/pwnagotchi.yml reconfigured auto-update to point to the scifijunkie repo edited main.custom_plugins to point to /usr/local/share/pwnagotchi/custom-plugins in pwnagotchi/defaults.toml removed mastodon configuration from defaults.toml removed ntfy configuration from defaults.toml removed handshakes-m.py from default plugin removed mastodon.py from default plugin removed ntfy.py from default plugin addressed [ERROR] [update] 'tag_name' addressed rate limit exceeded addressed TypeError: Descriptors cannot not be created directly. Reran pip-compile
This commit is contained in:
parent
c34aed94c0
commit
eb77f29135
@ -1,9 +1,10 @@
|
|||||||
exclude *.pyc .DS_Store .gitignore MANIFEST.in
|
exclude *.pyc .DS_Store .gitignore MANIFEST.in
|
||||||
|
include requirements.txt
|
||||||
include setup.py
|
include setup.py
|
||||||
include distribute_setup.py
|
|
||||||
include README.md
|
include README.md
|
||||||
include LICENSE
|
include LICENSE
|
||||||
recursive-include bin *
|
recursive-include bin *
|
||||||
|
recursive-include builder/data *
|
||||||
recursive-include pwnagotchi *.py
|
recursive-include pwnagotchi *.py
|
||||||
recursive-include pwnagotchi *.yml
|
recursive-include pwnagotchi *.yml
|
||||||
recursive-include pwnagotchi *.*
|
recursive-include pwnagotchi *.*
|
||||||
|
85
Makefile
85
Makefile
@ -1,6 +1,31 @@
|
|||||||
PACKER_VERSION=1.7.8
|
PACKER_VERSION := 1.8.3
|
||||||
PWN_HOSTNAME=pwnagotchi
|
PWN_HOSTNAME := pwnagotchi
|
||||||
PWN_VERSION=master
|
# PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py)
|
||||||
|
PWN_VERSION := $(or ${PWN_VERSION},$(shell cut -d"'" -f2 < pwnagotchi/_version.py))
|
||||||
|
PWN_RELEASE := pwnagotchi-$(PWN_VERSION)
|
||||||
|
|
||||||
|
MACHINE_TYPE := $(shell uname -m)
|
||||||
|
ifneq (,$(filter x86_64,$(MACHINE_TYPE)))
|
||||||
|
GOARCH := amd64
|
||||||
|
else ifneq (,$(filter i686,$(MACHINE_TYPE)))
|
||||||
|
GOARCH := 386
|
||||||
|
else ifneq (,$(filter arm64% aarch64%,$(MACHINE_TYPE)))
|
||||||
|
GOARCH := arm64
|
||||||
|
else ifneq (,$(filter arm%,$(MACHINE_TYPE)))
|
||||||
|
GOARCH := arm
|
||||||
|
else
|
||||||
|
GOARCH := amd64
|
||||||
|
$(warning Unable to detect CPU arch from machine type $(MACHINE_TYPE), assuming $(GOARCH))
|
||||||
|
endif
|
||||||
|
|
||||||
|
# The Ansible part of the build can inadvertently change the active hostname of
|
||||||
|
# the build machine while updating the permanent hostname of the build image.
|
||||||
|
# If the unshare command is available, use it to create a separate namespace
|
||||||
|
# so hostname changes won't affect the build machine.
|
||||||
|
UNSHARE := $(shell command -v unshare)
|
||||||
|
ifneq (,$(UNSHARE))
|
||||||
|
UNSHARE := $(UNSHARE) --uts
|
||||||
|
endif
|
||||||
|
|
||||||
all: clean install image
|
all: clean install image
|
||||||
|
|
||||||
@ -8,23 +33,47 @@ langs:
|
|||||||
@for lang in pwnagotchi/locale/*/; do\
|
@for lang in pwnagotchi/locale/*/; do\
|
||||||
echo "compiling language: $$lang ..."; \
|
echo "compiling language: $$lang ..."; \
|
||||||
./scripts/language.sh compile $$(basename $$lang); \
|
./scripts/language.sh compile $$(basename $$lang); \
|
||||||
done
|
done
|
||||||
|
|
||||||
install:
|
install:
|
||||||
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
|
PACKER := /tmp/pwnagotchi/packer
|
||||||
unzip /tmp/packer.zip -d /tmp
|
PACKER_URL := https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_$(GOARCH).zip
|
||||||
sudo mv /tmp/packer /usr/bin/packer
|
$(PACKER):
|
||||||
git clone https://github.com/solo-io/packer-builder-arm-image /tmp/packer-builder-arm-image
|
mkdir -p $(@D)
|
||||||
cd /tmp/packer-builder-arm-image && go get -d ./... && go build
|
curl -L "$(PACKER_URL)" -o $(PACKER).zip
|
||||||
sudo cp /tmp/packer-builder-arm-image/packer-plugin-arm-image /usr/bin
|
unzip $(PACKER).zip -d $(@D)
|
||||||
|
rm $(PACKER).zip
|
||||||
|
chmod +x $@
|
||||||
|
|
||||||
image:
|
SDIST := dist/pwnagotchi-$(PWN_VERSION).tar.gz
|
||||||
cd builder && sudo /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json
|
$(SDIST): setup.py pwnagotchi
|
||||||
sudo mv builder/output-pwnagotchi/image pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
python3 setup.py sdist
|
||||||
sudo sha256sum pwnagotchi-raspbian-lite-$(PWN_VERSION).img > pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256
|
|
||||||
sudo zip pwnagotchi-raspbian-lite-$(PWN_VERSION).zip pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256 pwnagotchi-raspbian-lite-$(PWN_VERSION).img
|
# Building the image requires packer, but don't rebuild the image just because packer updated.
|
||||||
|
$(PWN_RELEASE).img: | $(PACKER)
|
||||||
|
|
||||||
|
# If the packer or ansible files are updated, rebuild the image.
|
||||||
|
$(PWN_RELEASE).img: $(SDIST) builder/pwnagotchi.json builder/pwnagotchi.yml $(shell find builder/data -type f)
|
||||||
|
sudo $(PACKER) plugins install github.com/solo-io/arm-image
|
||||||
|
cd builder && sudo $(UNSHARE) $(PACKER) build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json
|
||||||
|
sudo chown -R $$USER:$$USER builder/output-pwnagotchi
|
||||||
|
mv builder/output-pwnagotchi/image $@
|
||||||
|
|
||||||
|
# If any of these files are updated, rebuild the checksums.
|
||||||
|
$(PWN_RELEASE).sha256: $(PWN_RELEASE).img
|
||||||
|
sha256sum $^ > $@
|
||||||
|
|
||||||
|
# If any of the input files are updated, rebuild the archive.
|
||||||
|
$(PWN_RELEASE).zip: $(PWN_RELEASE).img $(PWN_RELEASE).sha256
|
||||||
|
zip $(PWN_RELEASE).zip $^
|
||||||
|
|
||||||
|
.PHONY: image
|
||||||
|
image: $(PWN_RELEASE).zip
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
sudo rm -rf /tmp/packer-builder-arm-image
|
- python3 setup.py clean --all
|
||||||
sudo rm -f pwnagotchi-raspbian-lite-*.zip pwnagotchi-raspbian-lite-*.img pwnagotchi-raspbian-lite-*.sha256
|
- rm -rf dist pwnagotchi.egg-info
|
||||||
sudo rm -rf builder/output-pwnagotchi builder/packer_cache
|
- rm -f $(PACKER)
|
||||||
|
- rm -f $(PWN_RELEASE).*
|
||||||
|
- sudo rm -rf builder/output-pwnagotchi builder/packer_cache
|
||||||
|
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
allow-hotplug wlan0
|
allow-hotplug wlan0
|
||||||
iface wlan0 inet static
|
iface wlan0 inet manual
|
||||||
|
pre-up ifconfig $IFACE up
|
||||||
|
post-down ifconfig $IFACE down
|
||||||
|
@ -19,7 +19,12 @@ if ! check_brcm; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# start mon0
|
# start mon0
|
||||||
start_monitor_interface
|
if ! is_interface_up 'mon0'; then
|
||||||
|
start_monitor_interface
|
||||||
|
else
|
||||||
|
stop_monitor_interface
|
||||||
|
start_monitor_interface
|
||||||
|
fi
|
||||||
|
|
||||||
if is_auto_mode_no_delete; then
|
if is_auto_mode_no_delete; then
|
||||||
/usr/bin/bettercap -no-colors -caplet pwnagotchi-auto -iface mon0
|
/usr/bin/bettercap -no-colors -caplet pwnagotchi-auto -iface mon0
|
||||||
|
@ -3,12 +3,30 @@
|
|||||||
# well ... it blinks the led
|
# well ... it blinks the led
|
||||||
blink_led() {
|
blink_led() {
|
||||||
for i in $(seq 1 "$1"); do
|
for i in $(seq 1 "$1"); do
|
||||||
echo 0 >/sys/class/leds/led0/brightness
|
if [ -d /sys/class/leds/led0 ]
|
||||||
|
then
|
||||||
|
echo 0 | tee /sys/class/leds/led0/brightness
|
||||||
|
else
|
||||||
|
echo 0 | tee /sys/class/leds/ACT/brightness
|
||||||
|
fi
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
echo 1 >/sys/class/leds/led0/brightness
|
|
||||||
|
if [ -d /sys/class/leds/led0 ]
|
||||||
|
then
|
||||||
|
echo 1 | tee /sys/class/leds/led0/brightness
|
||||||
|
else
|
||||||
|
echo 1 | tee /sys/class/leds/ACT/brightness
|
||||||
|
fi
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
|
|
||||||
done
|
done
|
||||||
echo 0 >/sys/class/leds/led0/brightness
|
|
||||||
|
if [ -d /sys/class/leds/led0 ]
|
||||||
|
then
|
||||||
|
echo 0 | tee /sys/class/leds/led0/brightness
|
||||||
|
else
|
||||||
|
echo 0 | tee /sys/class/leds/ACT/brightness
|
||||||
|
fi
|
||||||
sleep 0.3
|
sleep 0.3
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,20 +51,31 @@ reload_brcm() {
|
|||||||
|
|
||||||
# starts mon0
|
# starts mon0
|
||||||
start_monitor_interface() {
|
start_monitor_interface() {
|
||||||
|
rfkill unblock all
|
||||||
|
iw dev wlan0 set power_save off
|
||||||
|
|
||||||
|
ifconfig wlan0 up
|
||||||
|
|
||||||
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add mon0 type monitor && ifconfig mon0 up
|
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add mon0 type monitor && ifconfig mon0 up
|
||||||
|
|
||||||
|
# If wlan0 is NOT taken down after bringing up mon0, then when switching to AUTO you will get:
|
||||||
|
# error 400: error while initializing mon0 to channel 1: iw: out=command failed: Device or resource busy (-16) err=exit status 240
|
||||||
|
ifconfig wlan0 down
|
||||||
}
|
}
|
||||||
|
|
||||||
# stops mon0
|
# stops mon0
|
||||||
stop_monitor_interface() {
|
stop_monitor_interface() {
|
||||||
ifconfig mon0 down && iw dev mon0 del
|
ifconfig mon0 down && iw dev mon0 del
|
||||||
|
ifconfig wlan0 up
|
||||||
}
|
}
|
||||||
|
|
||||||
# returns 0 if the specificed network interface is up
|
# returns 0 if the specificed network interface is up
|
||||||
is_interface_up() {
|
is_interface_up() {
|
||||||
if grep -qi 'up' /sys/class/net/$1/operstate; then
|
if grep -qi 'up' /sys/class/net/$1/operstate; then
|
||||||
return 0
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# returns 0 if conditions for AUTO mode are met
|
# returns 0 if conditions for AUTO mode are met
|
||||||
|
@ -3,95 +3,34 @@
|
|||||||
{
|
{
|
||||||
"name": "pwnagotchi",
|
"name": "pwnagotchi",
|
||||||
"type": "arm-image",
|
"type": "arm-image",
|
||||||
"iso_url": "https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2020-02-14/2020-02-13-raspbian-buster-lite.zip",
|
"iso_url": "https://downloads.raspberrypi.org/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-05-03/2023-05-03-raspios-buster-armhf-lite.img.xz",
|
||||||
"iso_checksum": "12ae6e17bf95b6ba83beca61e7394e7411b45eba7e6a520f434b0748ea7370e8",
|
"iso_checksum": "3d210e61b057de4de90eadb46e28837585a9b24247c221998f5bead04f88624c",
|
||||||
"target_image_size": 6442450944
|
"target_image_size": 9368709120,
|
||||||
|
"qemu_args": ["-cpu", "arm1176"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"provisioners": [
|
"provisioners": [
|
||||||
{
|
{
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"inline": [
|
"inline": [
|
||||||
"sed -i 's/^\\([^#]\\)/#\\1/g' /etc/ld.so.preload",
|
"mv /etc/ld.so.preload /etc/ld.so.preload.DISABLED",
|
||||||
|
"uname -a",
|
||||||
"dpkg-architecture",
|
"dpkg-architecture",
|
||||||
"apt -y update --allow-releaseinfo-change",
|
"mkdir -p /usr/local/src/pwnagotchi"
|
||||||
"apt install -y ansible"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"source": "data/usr/bin/pwnlib",
|
"sources": [
|
||||||
"destination": "/usr/bin/pwnlib"
|
"../dist/pwnagotchi-{{user `pwn_version`}}.tar.gz"
|
||||||
},
|
],
|
||||||
{
|
"destination": "/usr/local/src/pwnagotchi/"
|
||||||
"type": "file",
|
|
||||||
"source": "data/usr/bin/bettercap-launcher",
|
|
||||||
"destination": "/usr/bin/bettercap-launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/usr/bin/pwnagotchi-launcher",
|
|
||||||
"destination": "/usr/bin/pwnagotchi-launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/usr/bin/monstop",
|
|
||||||
"destination": "/usr/bin/monstop"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/usr/bin/monstart",
|
|
||||||
"destination": "/usr/bin/monstart"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/usr/bin/hdmion",
|
|
||||||
"destination": "/usr/bin/hdmion"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/usr/bin/hdmioff",
|
|
||||||
"destination": "/usr/bin/hdmioff"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/etc/network/interfaces.d/lo-cfg",
|
|
||||||
"destination": "/etc/network/interfaces.d/lo-cfg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/etc/network/interfaces.d/wlan0-cfg",
|
|
||||||
"destination": "/etc/network/interfaces.d/wlan0-cfg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/etc/network/interfaces.d/usb0-cfg",
|
|
||||||
"destination": "/etc/network/interfaces.d/usb0-cfg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/etc/network/interfaces.d/eth0-cfg",
|
|
||||||
"destination": "/etc/network/interfaces.d/eth0-cfg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/etc/systemd/system/pwngrid-peer.service",
|
|
||||||
"destination": "/etc/systemd/system/pwngrid-peer.service"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/etc/systemd/system/pwnagotchi.service",
|
|
||||||
"destination": "/etc/systemd/system/pwnagotchi.service"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"source": "data/etc/systemd/system/bettercap.service",
|
|
||||||
"destination": "/etc/systemd/system/bettercap.service"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"inline": [
|
"inline": [
|
||||||
"chmod +x /usr/bin/*"
|
"apt-get -y --allow-releaseinfo-change update",
|
||||||
|
"apt-get install -y --no-install-recommends ansible"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -103,7 +42,7 @@
|
|||||||
{
|
{
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"inline": [
|
"inline": [
|
||||||
"sed -i 's/^#\\(.+\\)/\\1/g' /etc/ld.so.preload"
|
"mv /etc/ld.so.preload.DISABLED /etc/ld.so.preload"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
---
|
---
|
||||||
- hosts:
|
- hosts:
|
||||||
- 127.0.0.1
|
- 127.0.0.1
|
||||||
|
gather_facts: yes
|
||||||
become: yes
|
become: yes
|
||||||
vars:
|
vars:
|
||||||
pwnagotchi:
|
pwnagotchi:
|
||||||
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
|
||||||
version: "{{ lookup('env', 'PWN_VERSION') | default('master', true) }}"
|
version: "{{ lookup('env', 'PWN_VERSION') | default('master', true) }}"
|
||||||
system:
|
system:
|
||||||
boot_options4:
|
boot_options:
|
||||||
- "dtoverlay=disable-wifi"
|
|
||||||
- "arm_freq=800"
|
|
||||||
boot_optionsall:
|
|
||||||
- "dtoverlay=dwc2"
|
- "dtoverlay=dwc2"
|
||||||
- "dtoverlay=spi1-3cs"
|
- "dtoverlay=spi1-3cs"
|
||||||
|
- "dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4"
|
||||||
- "dtparam=spi=on"
|
- "dtparam=spi=on"
|
||||||
- "dtparam=i2c_arm=on"
|
- "dtparam=i2c_arm=on"
|
||||||
- "dtparam=i2c1=on"
|
- "dtparam=i2c1=on"
|
||||||
@ -39,7 +38,8 @@
|
|||||||
- dnsmasq.service
|
- dnsmasq.service
|
||||||
packages:
|
packages:
|
||||||
bettercap:
|
bettercap:
|
||||||
url: "https://github.com/bettercap/bettercap/releases/download/v2.31.0/bettercap_linux_armhf_v2.31.0.zip"
|
# We will install bettercap v2.32 from source
|
||||||
|
# url: "https://github.com/bettercap/bettercap/releases/download/v2.31.0/bettercap_linux_armhf_v2.31.0.zip"
|
||||||
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||||
pwngrid:
|
pwngrid:
|
||||||
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.10.3/pwngrid_linux_armhf_v1.10.3.zip"
|
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.10.3/pwngrid_linux_armhf_v1.10.3.zip"
|
||||||
@ -56,12 +56,13 @@
|
|||||||
- triggerhappy
|
- triggerhappy
|
||||||
- wpa_supplicant
|
- wpa_supplicant
|
||||||
- nfs-common
|
- nfs-common
|
||||||
|
# Remove every golang package because we will install go-1.20.2
|
||||||
|
- golang*
|
||||||
- python2*
|
- python2*
|
||||||
install:
|
install:
|
||||||
- rsync
|
- rsync
|
||||||
- vim
|
- vim
|
||||||
- screen
|
- screen
|
||||||
- golang
|
|
||||||
- git
|
- git
|
||||||
- build-essential
|
- build-essential
|
||||||
- python3-pip
|
- python3-pip
|
||||||
@ -72,6 +73,7 @@
|
|||||||
- libopenmpi-dev
|
- libopenmpi-dev
|
||||||
- libatlas-base-dev
|
- libatlas-base-dev
|
||||||
- libjasper-dev
|
- libjasper-dev
|
||||||
|
- libgtk-3-0
|
||||||
- libqtgui4
|
- libqtgui4
|
||||||
- libqt4-test
|
- libqt4-test
|
||||||
- libopenjp2-7
|
- libopenjp2-7
|
||||||
@ -89,10 +91,6 @@
|
|||||||
- libnetfilter-queue-dev
|
- libnetfilter-queue-dev
|
||||||
- libopenmpi3
|
- libopenmpi3
|
||||||
- dphys-swapfile
|
- dphys-swapfile
|
||||||
- kalipi-kernel
|
|
||||||
- kalipi-bootloader
|
|
||||||
- kalipi-re4son-firmware
|
|
||||||
- kalipi-kernel-headers
|
|
||||||
- libraspberrypi0
|
- libraspberrypi0
|
||||||
- libraspberrypi-dev
|
- libraspberrypi-dev
|
||||||
- libraspberrypi-doc
|
- libraspberrypi-doc
|
||||||
@ -109,10 +107,33 @@
|
|||||||
- fonts-ipaexfont-gothic
|
- fonts-ipaexfont-gothic
|
||||||
- cryptsetup
|
- cryptsetup
|
||||||
- dnsmasq
|
- dnsmasq
|
||||||
- python3-rpi.gpio
|
- aircrack-ng
|
||||||
- firmware-ralink
|
- raspberrypi-kernel-headers
|
||||||
|
- libgmp3-dev
|
||||||
|
- qpdf
|
||||||
|
- bison
|
||||||
|
- flex
|
||||||
|
- make
|
||||||
|
- autoconf
|
||||||
|
- libtool
|
||||||
|
- texinfo
|
||||||
|
- binutils
|
||||||
|
- lnav
|
||||||
|
- p7zip-full
|
||||||
|
|
||||||
|
environment:
|
||||||
|
ARCHFLAGS: "-arch armv7l"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
- name: System details
|
||||||
|
debug:
|
||||||
|
msg="{{ item }}"
|
||||||
|
with_items:
|
||||||
|
- "{{ ansible_distribution }}"
|
||||||
|
- "{{ ansible_distribution_version }}"
|
||||||
|
- "{{ ansible_distribution_major_version }}"
|
||||||
|
- "{{ ansible_architecture }}"
|
||||||
|
- "{{ ansible_machine }}"
|
||||||
- name: change hostname
|
- name: change hostname
|
||||||
hostname:
|
hostname:
|
||||||
name: "{{pwnagotchi.hostname}}"
|
name: "{{pwnagotchi.hostname}}"
|
||||||
@ -134,26 +155,6 @@
|
|||||||
line: 'ExecStart=/usr/lib/bluetooth/bluetoothd --noplugin=sap'
|
line: 'ExecStart=/usr/lib/bluetooth/bluetoothd --noplugin=sap'
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: Add re4son-kernel repo key
|
|
||||||
apt_key:
|
|
||||||
keyserver: pgp.mit.edu
|
|
||||||
id: 11764EE8AC24832F
|
|
||||||
|
|
||||||
- name: Add re4son-kernel repository
|
|
||||||
apt_repository:
|
|
||||||
repo: deb http://http.re4son-kernel.com/re4son/ kali-pi main
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: create /etc/apt/preferences.d/kali.pref
|
|
||||||
copy:
|
|
||||||
dest: /etc/apt/preferences.d/kali.pref
|
|
||||||
force: yes
|
|
||||||
content: |
|
|
||||||
# ensure kali packages that are installed take precedence
|
|
||||||
Package: *
|
|
||||||
Pin: release n=kali-pi
|
|
||||||
Pin-Priority: 999
|
|
||||||
|
|
||||||
- name: add firmware packages to hold
|
- name: add firmware packages to hold
|
||||||
dpkg_selections:
|
dpkg_selections:
|
||||||
name: "{{ item }}"
|
name: "{{ item }}"
|
||||||
@ -172,17 +173,26 @@
|
|||||||
|
|
||||||
- name: upgrade apt distro
|
- name: upgrade apt distro
|
||||||
apt:
|
apt:
|
||||||
upgrade: full
|
upgrade: dist
|
||||||
|
|
||||||
- name: install packages
|
- name: install packages
|
||||||
apt:
|
apt:
|
||||||
name: "{{ packages.apt.install }}"
|
name: "{{ packages.apt.install }}"
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
|
- name: Update .bashrc (root)
|
||||||
|
blockinfile:
|
||||||
|
dest: /root/.bashrc
|
||||||
|
state: present
|
||||||
|
block: |
|
||||||
|
export MAKEFLAGS=-j$(nproc)
|
||||||
|
insertafter: EOF
|
||||||
|
|
||||||
- name: configure dphys-swapfile
|
- name: configure dphys-swapfile
|
||||||
file:
|
lineinfile:
|
||||||
path: /etc/dphys-swapfile
|
path: /etc/dphys-swapfile
|
||||||
content: "CONF_SWAPSIZE=1024"
|
regexp: "^CONF_SWAPSIZE=.*$"
|
||||||
|
line: "CONF_SWAPSIZE=512"
|
||||||
|
|
||||||
- name: clone papirus repository
|
- name: clone papirus repository
|
||||||
git:
|
git:
|
||||||
@ -214,67 +224,55 @@
|
|||||||
regexp: "#EPD_SIZE=2.0"
|
regexp: "#EPD_SIZE=2.0"
|
||||||
line: "EPD_SIZE=2.0"
|
line: "EPD_SIZE=2.0"
|
||||||
|
|
||||||
- name: collect python pip package list
|
- name: Delete papirus content & directory
|
||||||
command: "pip3 list"
|
|
||||||
register: pip_output
|
|
||||||
|
|
||||||
- name: set python pip package facts
|
|
||||||
set_fact:
|
|
||||||
pip_packages: >
|
|
||||||
{{ pip_packages | default({}) | combine( { item.split()[0]: item.split()[1] } ) }}
|
|
||||||
with_items: "{{ pip_output.stdout_lines }}"
|
|
||||||
|
|
||||||
- name: acquire python3 pip target
|
|
||||||
command: "python3 -c 'import sys;print(sys.path.pop())'"
|
|
||||||
register: pip_target
|
|
||||||
|
|
||||||
- name: clone pwnagotchi repository
|
|
||||||
git:
|
|
||||||
repo: https://git.chadwaltercummings.me/scifijunkie/pwnagotchi.git
|
|
||||||
dest: /usr/local/src/pwnagotchi
|
|
||||||
register: pwnagotchigit
|
|
||||||
|
|
||||||
- name: create /usr/local/share/pwnagotchi/ folder
|
|
||||||
file:
|
file:
|
||||||
path: /usr/local/share/pwnagotchi/
|
state: absent
|
||||||
|
path: /usr/local/src/gratis
|
||||||
|
when: gratisgit.changed
|
||||||
|
|
||||||
|
# pip v20.3 uses a newer dependency resolver that better handles our unique situation.
|
||||||
|
# Specifically, it handles mismatches between direct requirements without extras and
|
||||||
|
# indirect requirements that do want extras (e.g. gym vs stable-baselines->gym[atari]).
|
||||||
|
- name: Upgrade pip and install rpi-hardware-pwm
|
||||||
|
pip:
|
||||||
|
name:
|
||||||
|
- pip>=20.3
|
||||||
|
- rpi-hardware-pwm
|
||||||
|
|
||||||
|
# We need the --ignore-installed option so that pip simply overwrites/upgrades existing
|
||||||
|
# packages instead of trying to uninstall them first. While this sounds dangerous,
|
||||||
|
# this matches the legacy behavior of pip. This is required to prevent pip from trying
|
||||||
|
# (and failing) to uninstall python packages that were originally installed via apt.
|
||||||
|
- name: Install pwnagotchi from source archive
|
||||||
|
pip:
|
||||||
|
name: /usr/local/src/pwnagotchi/pwnagotchi-{{ pwnagotchi.version }}.tar.gz
|
||||||
|
extra_args: --verbose --prefer-binary --ignore-installed --retries 50 --index-url https://nexus.chadwaltercummings.me/repository/pypi.org/simple --extra-index-url https://nexus.chadwaltercummings.me/repository/www.piwheels.org/simple
|
||||||
|
|
||||||
|
- name: create custom plugin directory
|
||||||
|
file:
|
||||||
|
path: /usr/local/share/pwnagotchi/custom-plugins/
|
||||||
state: directory
|
state: directory
|
||||||
|
|
||||||
- name: clone pwnagotchi plugins repository
|
- name: clone pwnagotchi plugins repository
|
||||||
git:
|
git:
|
||||||
repo: https://git.chadwaltercummings.me/scifijunkie/pwnagotchi-plugins-contrib.git
|
repo: https://git.chadwaltercummings.me/scifijunkie/pwnagotchi-plugins-contrib.git
|
||||||
dest: /usr/local/share/pwnagotchi/availaible-plugins
|
dest: /usr/local/share/pwnagotchi/available-plugins
|
||||||
|
|
||||||
- name: fetch pwnagotchi version
|
- name: Copy aircrackonly.py
|
||||||
set_fact:
|
copy:
|
||||||
pwnagotchi_version: "{{ lookup('file', '/usr/local/src/pwnagotchi/pwnagotchi/_version.py') | regex_replace('.*__version__.*=.*''([0-9]+\\.[0-9]+\\.[0-9]+[A-Za-z0-9]*)''.*', '\\1') }}"
|
src: /usr/local/share/pwnagotchi/available-plugins/aircrackonly.py
|
||||||
|
dest: /usr/local/share/pwnagotchi/custom-plugins/aircrackonly.py
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '644'
|
||||||
|
|
||||||
- name: pwnagotchi version found
|
- name: Copy handshakes-dl.py
|
||||||
debug:
|
copy:
|
||||||
msg: "{{ pwnagotchi_version }}"
|
src: /usr/local/share/pwnagotchi/available-plugins/handshakes-dl.py
|
||||||
|
dest: /usr/local/share/pwnagotchi/custom-plugins/handshakes-dl.py
|
||||||
- name: build pwnagotchi wheel
|
owner: root
|
||||||
command: "python3 setup.py sdist bdist_wheel"
|
group: root
|
||||||
args:
|
mode: '644'
|
||||||
chdir: /usr/local/src/pwnagotchi
|
|
||||||
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
|
|
||||||
|
|
||||||
- name: install opencv-python
|
|
||||||
pip:
|
|
||||||
name: "https://www.piwheels.org/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl"
|
|
||||||
extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}"
|
|
||||||
when: (pip_packages['opencv-python'] is undefined) or (pip_packages['opencv-python'] != '3.4.3.18')
|
|
||||||
|
|
||||||
- name: install tensorflow
|
|
||||||
pip:
|
|
||||||
name: "https://www.piwheels.org/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl"
|
|
||||||
extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}"
|
|
||||||
when: (pip_packages['tensorflow'] is undefined) or (pip_packages['tensorflow'] != '1.13.1')
|
|
||||||
|
|
||||||
- name: install pwnagotchi wheel and dependencies
|
|
||||||
pip:
|
|
||||||
name: "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}"
|
|
||||||
extra_args: "--no-cache-dir"
|
|
||||||
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
|
|
||||||
|
|
||||||
- name: download and install pwngrid
|
- name: download and install pwngrid
|
||||||
unarchive:
|
unarchive:
|
||||||
@ -283,15 +281,33 @@
|
|||||||
remote_src: yes
|
remote_src: yes
|
||||||
mode: 0755
|
mode: 0755
|
||||||
|
|
||||||
- name: download and install bettercap
|
# Install go-1.21.5
|
||||||
|
- name: Install go-1.21.5
|
||||||
unarchive:
|
unarchive:
|
||||||
src: "{{ packages.bettercap.url }}"
|
src: https://go.dev/dl/go1.21.5.linux-armv6l.tar.gz
|
||||||
dest: /usr/bin
|
dest: /usr/local
|
||||||
remote_src: yes
|
remote_src: yes
|
||||||
exclude:
|
register: golang
|
||||||
- README.md
|
|
||||||
- LICENSE.md
|
- name: Update .bashrc for go-1.21.5 (pi)
|
||||||
mode: 0755
|
blockinfile:
|
||||||
|
dest: /home/pi/.bashrc
|
||||||
|
state: present
|
||||||
|
block: |
|
||||||
|
export GOPATH=$HOME/go
|
||||||
|
export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
|
||||||
|
insertafter: EOF
|
||||||
|
when: golang.changed
|
||||||
|
|
||||||
|
- name: Install bettercap v2.32
|
||||||
|
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go env -w GO111MODULE=off && go get github.com/bettercap/bettercap && cd $GOPATH/src/github.com/bettercap/bettercap && make build && make install"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
register: bettercap
|
||||||
|
|
||||||
|
- name: Link bettercap v2.32
|
||||||
|
command: ln -s /usr/local/bin/bettercap /usr/bin/bettercap
|
||||||
|
when: bettercap.changed
|
||||||
|
|
||||||
- name: clone bettercap caplets
|
- name: clone bettercap caplets
|
||||||
git:
|
git:
|
||||||
@ -312,6 +328,230 @@
|
|||||||
remote_src: yes
|
remote_src: yes
|
||||||
mode: 0755
|
mode: 0755
|
||||||
|
|
||||||
|
# Install nexmon to fix wireless scanning (takes 2.5G of space)
|
||||||
|
- name: clone nexmon repository
|
||||||
|
git:
|
||||||
|
repo: https://github.com/seemoo-lab/nexmon.git
|
||||||
|
dest: /usr/local/src/nexmon
|
||||||
|
# version: bfb3fe90c881498d7ee245b38f16722c1de26fa1
|
||||||
|
register: nexmongit
|
||||||
|
|
||||||
|
- name: configure libisl
|
||||||
|
command: chdir=/usr/local/src/nexmon/buildtools/isl-0.10/ ./configure
|
||||||
|
|
||||||
|
- name: make libisl
|
||||||
|
command: chdir=/usr/local/src/nexmon/buildtools/isl-0.10/ make
|
||||||
|
|
||||||
|
- name: install libisl
|
||||||
|
command: chdir=/usr/local/src/nexmon/buildtools/isl-0.10/ make install
|
||||||
|
|
||||||
|
- name: link libisl
|
||||||
|
command: ln -s /usr/local/lib/libisl.so /usr/lib/arm-linux-gnueabihf/libisl.so.10
|
||||||
|
|
||||||
|
- name: autoreconf libmpfr
|
||||||
|
command: chdir=/usr/local/src/nexmon/buildtools/mpfr-3.1.4/ autoreconf -f -i
|
||||||
|
|
||||||
|
- name: configure libmpfr
|
||||||
|
command: chdir=/usr/local/src/nexmon/buildtools/mpfr-3.1.4/ ./configure
|
||||||
|
|
||||||
|
- name: make libmpfr
|
||||||
|
command: chdir=/usr/local/src/nexmon/buildtools/mpfr-3.1.4/ make
|
||||||
|
|
||||||
|
- name: install libmpfr
|
||||||
|
command: chdir=/usr/local/src/nexmon/buildtools/mpfr-3.1.4/ make install
|
||||||
|
|
||||||
|
- name: link libmpfr
|
||||||
|
command: ln -s /usr/local/lib/libmpfr.so /usr/lib/arm-linux-gnueabihf/libmpfr.so.4
|
||||||
|
|
||||||
|
- name: make firmware
|
||||||
|
shell: "source ./setup_env.sh && make"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
- name: choose the right kernel version (bcm43436b0)
|
||||||
|
replace:
|
||||||
|
dest: /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/Makefile
|
||||||
|
backup: no
|
||||||
|
regexp: "KERNEL_VERSION = .*$"
|
||||||
|
replace: "KERNEL_VERSION = 5.10"
|
||||||
|
|
||||||
|
- name: choose the right kernel release (variable) (bcm43436b0)
|
||||||
|
lineinfile:
|
||||||
|
dest: /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/Makefile
|
||||||
|
insertafter: "DRIVER_FOLDER_NAME = .*$"
|
||||||
|
line: "KERNEL_RELEASE = 5.10.103-v7+"
|
||||||
|
|
||||||
|
- name: choose the right kernel release (replace string) (bcm43436b0)
|
||||||
|
replace:
|
||||||
|
dest: /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/Makefile
|
||||||
|
backup: no
|
||||||
|
regexp: "shell uname -r"
|
||||||
|
replace: "KERNEL_RELEASE"
|
||||||
|
|
||||||
|
- name: make firmware patch (bcm43436b0)
|
||||||
|
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
# - name: backup original firmware
|
||||||
|
# shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make backup-firmware"
|
||||||
|
# args:
|
||||||
|
# executable: /bin/bash
|
||||||
|
# chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
# - name: install new firmware
|
||||||
|
# shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make install-firmware"
|
||||||
|
# args:
|
||||||
|
# executable: /bin/bash
|
||||||
|
# chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
- name: install new firmware (bcm43436b0)
|
||||||
|
copy:
|
||||||
|
src: /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/brcmfmac43436-sdio.bin
|
||||||
|
dest: /lib/firmware/brcm/brcmfmac43436-sdio.bin
|
||||||
|
|
||||||
|
- name: choose the right kernel version (bcm43430a1)
|
||||||
|
replace:
|
||||||
|
dest: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/Makefile
|
||||||
|
backup: no
|
||||||
|
regexp: "KERNEL_VERSION = .*$"
|
||||||
|
replace: "KERNEL_VERSION = 5.10"
|
||||||
|
|
||||||
|
- name: choose the right kernel release (variable) (bcm43430a1)
|
||||||
|
lineinfile:
|
||||||
|
dest: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/Makefile
|
||||||
|
insertafter: "DRIVER_FOLDER_NAME = .*$"
|
||||||
|
line: "KERNEL_RELEASE = 5.10.103-v7+"
|
||||||
|
|
||||||
|
- name: choose the right kernel release (replace string) (bcm43430a1)
|
||||||
|
replace:
|
||||||
|
dest: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/Makefile
|
||||||
|
backup: no
|
||||||
|
regexp: "shell uname -r"
|
||||||
|
replace: "KERNEL_RELEASE"
|
||||||
|
|
||||||
|
- name: make firmware patch (bcm43430a1)
|
||||||
|
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/ && make"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
# - name: backup original firmware
|
||||||
|
# shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/ && make backup-firmware"
|
||||||
|
# args:
|
||||||
|
# executable: /bin/bash
|
||||||
|
# chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
# - name: install new firmware
|
||||||
|
# shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/ && make install-firmware"
|
||||||
|
# args:
|
||||||
|
# executable: /bin/bash
|
||||||
|
# chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
- name: install new firmware (bcm43430a1)
|
||||||
|
copy:
|
||||||
|
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
|
||||||
|
dest: /lib/firmware/brcm/brcmfmac43430-sdio.bin
|
||||||
|
|
||||||
|
- name: Delete the firmware blob to avoid it crashing
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /lib/firmware/brcm/brcmfmac43430-sdio.clm_blob
|
||||||
|
|
||||||
|
- name: Delete the RPiZW firmware blob to avoid it crashing
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob
|
||||||
|
|
||||||
|
- name: Delete the RPi3 firmware blob to avoid it crashing
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob
|
||||||
|
|
||||||
|
- name: choose the right kernel version (bcm43455c0)
|
||||||
|
replace:
|
||||||
|
dest: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/Makefile
|
||||||
|
backup: no
|
||||||
|
regexp: "KERNEL_VERSION = .*$"
|
||||||
|
replace: "KERNEL_VERSION = 5.10"
|
||||||
|
|
||||||
|
- name: choose the right kernel release (variable) (bcm43455c0)
|
||||||
|
lineinfile:
|
||||||
|
dest: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/Makefile
|
||||||
|
insertafter: "DRIVER_FOLDER_NAME = .*$"
|
||||||
|
line: "KERNEL_RELEASE = 5.10.103-v7+"
|
||||||
|
|
||||||
|
- name: choose the right kernel release (replace string) (bcm43455c0)
|
||||||
|
replace:
|
||||||
|
dest: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/Makefile
|
||||||
|
backup: no
|
||||||
|
regexp: "shell uname -r"
|
||||||
|
replace: "KERNEL_RELEASE"
|
||||||
|
|
||||||
|
- name: make firmware patch (bcm43455c0)
|
||||||
|
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
# - name: backup original firmware
|
||||||
|
# shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make backup-firmware"
|
||||||
|
# args:
|
||||||
|
# executable: /bin/bash
|
||||||
|
# chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
# - name: install new firmware
|
||||||
|
# shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make install-firmware"
|
||||||
|
# args:
|
||||||
|
# executable: /bin/bash
|
||||||
|
# chdir: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
- name: install new firmware (bcm43455c0)
|
||||||
|
copy:
|
||||||
|
src: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/brcmfmac43455-sdio.bin
|
||||||
|
dest: /lib/firmware/brcm/brcmfmac43455-sdio.bin
|
||||||
|
|
||||||
|
- name: make nexutil
|
||||||
|
command: chdir=/usr/local/src/nexmon/utilities/nexutil/ make
|
||||||
|
|
||||||
|
- name: make install nexutil
|
||||||
|
command: chdir=/usr/local/src/nexmon/utilities/nexutil/ make install
|
||||||
|
|
||||||
|
# - name: copy modified driver
|
||||||
|
# shell: "cd /usr/local/src/nexmon/patches/driver/brcmfmac_5.10.y-nexmon/ && cp brcmfmac.ko /lib/modules/5.10.103-v7+/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko && depmod -a"
|
||||||
|
# args:
|
||||||
|
# executable: /bin/bash
|
||||||
|
|
||||||
|
- name: copy modified driver (everyone but RPiZW)
|
||||||
|
copy:
|
||||||
|
src: /usr/local/src/nexmon/patches/driver/brcmfmac_5.10.y-nexmon/brcmfmac.ko
|
||||||
|
dest: /lib/modules/5.10.103-v7+/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko
|
||||||
|
|
||||||
|
- name: ensure depmod runs on reboot to load modified driver (brcmfmac)
|
||||||
|
lineinfile:
|
||||||
|
dest: /etc/rc.local
|
||||||
|
line: "/sbin/depmod -a"
|
||||||
|
|
||||||
|
# To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation
|
||||||
|
- name: Delete nexmon content & directory
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /usr/local/src/nexmon/
|
||||||
|
|
||||||
|
- name: Add pwnlog alias
|
||||||
|
lineinfile:
|
||||||
|
dest: /home/pi/.bashrc
|
||||||
|
line: "\nalias pwnlog='tail -f -n300 /var/log/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'"
|
||||||
|
insertafter: EOF
|
||||||
|
|
||||||
|
- name: install bettercap caplets
|
||||||
|
make:
|
||||||
|
chdir: /tmp/caplets
|
||||||
|
target: install
|
||||||
|
when: capletsgit.changed
|
||||||
|
|
||||||
- name: add HDMI powersave to rc.local
|
- name: add HDMI powersave to rc.local
|
||||||
blockinfile:
|
blockinfile:
|
||||||
path: /etc/rc.local
|
path: /etc/rc.local
|
||||||
@ -341,24 +581,41 @@
|
|||||||
# ui.display.type = "waveshare_2"
|
# ui.display.type = "waveshare_2"
|
||||||
when: not user_config.stat.exists
|
when: not user_config.stat.exists
|
||||||
|
|
||||||
|
# - name: append commented out parameters for usb_hat_c.py
|
||||||
|
# lineinfile:
|
||||||
|
# dest: /etc/pwnagotchi/config.toml
|
||||||
|
# line: "# main.plugins.ups_hat_c.enabled = true\n# main.plugins.ups_hat_c.label_on = true # show BAT label or just percentage\n# main.plugins.ups_hat_c.shutdown = 5 # battery percent at which the device will turn off\n# main.plugins.ups_hat_c.bat_x_coord = 140\n# main.plugins.ups_hat_c.bat_y_coord = 0"
|
||||||
|
# insertafter: EOF
|
||||||
|
|
||||||
|
#bizzarely changing the plugin code directly reverts to the old string
|
||||||
|
- name: Reconfigure auto-update to point to the scifijunk repo
|
||||||
|
replace:
|
||||||
|
dest: /usr/local/lib/python3.7/dist-packages/pwnagotchi/plugins/default/auto-update.py
|
||||||
|
backup: no
|
||||||
|
regexp: "evilsocket/pwnagotchi"
|
||||||
|
replace: "scifijunk/pwnagotchi"
|
||||||
|
|
||||||
|
- name: Delete unnecessary large folder to save space (/root/go)
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /root/go
|
||||||
|
|
||||||
|
- name: Delete unnecessary large folder to save space (/root/.cache)
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: /root/.cache
|
||||||
|
|
||||||
- name: enable ssh on boot
|
- name: enable ssh on boot
|
||||||
file:
|
file:
|
||||||
path: /boot/ssh
|
path: /boot/ssh
|
||||||
state: touch
|
state: touch
|
||||||
|
|
||||||
- name: adjust [pi4] /boot/config.txt
|
|
||||||
lineinfile:
|
|
||||||
dest: /boot/config.txt
|
|
||||||
insertafter: max_framebuffers=2
|
|
||||||
line: '{{ item }}'
|
|
||||||
with_items: "{{system.boot_options4}}"
|
|
||||||
|
|
||||||
- name: adjust [all] /boot/config.txt
|
- name: adjust /boot/config.txt
|
||||||
lineinfile:
|
lineinfile:
|
||||||
dest: /boot/config.txt
|
dest: /boot/config.txt
|
||||||
insertafter: EOF
|
insertafter: EOF
|
||||||
line: '{{ item }}'
|
line: '{{ item }}'
|
||||||
with_items: "{{system.boot_optionsall}}"
|
with_items: "{{system.boot_options}}"
|
||||||
|
|
||||||
- name: adjust /etc/modules
|
- name: adjust /etc/modules
|
||||||
lineinfile:
|
lineinfile:
|
||||||
@ -415,9 +672,14 @@
|
|||||||
You learn more about me at https://pwnagotchi.ai/
|
You learn more about me at https://pwnagotchi.ai/
|
||||||
when: hostname.changed
|
when: hostname.changed
|
||||||
|
|
||||||
|
# Ansible's apt module has an "autoclean" option but it only removes packages
|
||||||
|
# that can no longer be downloaded. Ansible v2.13 added the "clean" option
|
||||||
|
# which actually purges the apt cache, but that's newer than what we can
|
||||||
|
# install from the RasPiOS repos. Instead, we'll manually clean the cache.
|
||||||
- name: clean apt cache
|
- name: clean apt cache
|
||||||
apt:
|
command: "apt-get clean"
|
||||||
autoclean: yes
|
args:
|
||||||
|
warn: false
|
||||||
|
|
||||||
- name: remove dependencies that are no longer required
|
- name: remove dependencies that are no longer required
|
||||||
apt:
|
apt:
|
||||||
|
@ -41,7 +41,7 @@ def set_name(new_name):
|
|||||||
fp.write(patched)
|
fp.write(patched)
|
||||||
|
|
||||||
os.system("hostname '%s'" % new_name)
|
os.system("hostname '%s'" % new_name)
|
||||||
reboot()
|
pwnagotchi.reboot()
|
||||||
|
|
||||||
|
|
||||||
def name():
|
def name():
|
||||||
@ -119,7 +119,7 @@ def shutdown():
|
|||||||
from pwnagotchi import fs
|
from pwnagotchi import fs
|
||||||
for m in fs.mounts:
|
for m in fs.mounts:
|
||||||
m.sync()
|
m.sync()
|
||||||
|
|
||||||
os.system("sync")
|
os.system("sync")
|
||||||
os.system("halt")
|
os.system("halt")
|
||||||
|
|
||||||
@ -133,6 +133,7 @@ def restart(mode):
|
|||||||
os.system("touch /root/.pwnagotchi-manual")
|
os.system("touch /root/.pwnagotchi-manual")
|
||||||
|
|
||||||
os.system("service bettercap restart")
|
os.system("service bettercap restart")
|
||||||
|
time.sleep(2)
|
||||||
os.system("service pwnagotchi restart")
|
os.system("service pwnagotchi restart")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = '1.5.5'
|
__version__='1.8.4'
|
||||||
|
@ -324,6 +324,12 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
|||||||
found_handshake = False
|
found_handshake = False
|
||||||
jmsg = json.loads(msg)
|
jmsg = json.loads(msg)
|
||||||
|
|
||||||
|
# give plugins access to all raw bettercap events
|
||||||
|
try:
|
||||||
|
plugins.on('bcap_%s' % re.sub(r"[^a-z0-9_]+", "_", jmsg['tag'].lower()), self, jmsg)
|
||||||
|
except Exception as err:
|
||||||
|
logging.error("Processing event: %s" % err)
|
||||||
|
|
||||||
if jmsg['tag'] == 'wifi.client.handshake':
|
if jmsg['tag'] == 'wifi.client.handshake':
|
||||||
filename = jmsg['data']['file']
|
filename = jmsg['data']['file']
|
||||||
sta_mac = jmsg['data']['station']
|
sta_mac = jmsg['data']['station']
|
||||||
|
@ -2,9 +2,20 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import websockets
|
import websockets
|
||||||
|
import asyncio
|
||||||
|
import random
|
||||||
|
|
||||||
from requests.auth import HTTPBasicAuth
|
from requests.auth import HTTPBasicAuth
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
requests.adapters.DEFAULT_RETRIES = 5 # increase retries number
|
||||||
|
|
||||||
|
ping_timeout = 180
|
||||||
|
ping_interval = 15
|
||||||
|
max_queue = 10000
|
||||||
|
|
||||||
|
min_sleep = 0.5
|
||||||
|
max_sleep = 5.0
|
||||||
|
|
||||||
def decode(r, verbose_errors=True):
|
def decode(r, verbose_errors=True):
|
||||||
try:
|
try:
|
||||||
@ -31,25 +42,78 @@ class Client(object):
|
|||||||
self.websocket = "ws://%s:%s@%s:%d/api" % (username, password, hostname, port)
|
self.websocket = "ws://%s:%s@%s:%d/api" % (username, password, hostname, port)
|
||||||
self.auth = HTTPBasicAuth(username, password)
|
self.auth = HTTPBasicAuth(username, password)
|
||||||
|
|
||||||
def session(self):
|
# session takes optional argument to pull a sub-dictionary
|
||||||
r = requests.get("%s/session" % self.url, auth=self.auth)
|
# ex.: "session/wifi", "session/ble"
|
||||||
|
def session(self, sess="session"):
|
||||||
|
r = requests.get("%s/%s" % (self.url, sess), auth=self.auth)
|
||||||
return decode(r)
|
return decode(r)
|
||||||
|
|
||||||
async def start_websocket(self, consumer):
|
async def start_websocket(self, consumer):
|
||||||
s = "%s/events" % self.websocket
|
s = "%s/events" % self.websocket
|
||||||
|
|
||||||
|
# More modern version of the approach below
|
||||||
|
# logging.info("Creating new websocket...")
|
||||||
|
# async for ws in websockets.connect(s):
|
||||||
|
# try:
|
||||||
|
# async for msg in ws:
|
||||||
|
# try:
|
||||||
|
# await consumer(msg)
|
||||||
|
# except Exception as ex:
|
||||||
|
# logging.debug("Error while parsing event (%s)", ex)
|
||||||
|
# except websockets.exceptions.ConnectionClosedError:
|
||||||
|
# sleep_time = max_sleep*random.random()
|
||||||
|
# logging.warning('Retrying websocket connection in {} sec'.format(sleep_time))
|
||||||
|
# await asyncio.sleep(sleep_time)
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# restarted every time the connection fails
|
||||||
while True:
|
while True:
|
||||||
try:
|
logging.info("creating new websocket...")
|
||||||
async with websockets.connect(s, ping_interval=60, ping_timeout=90) as ws:
|
try:
|
||||||
async for msg in ws:
|
async with websockets.connect(s, ping_interval=ping_interval, ping_timeout=ping_timeout, max_queue=max_queue) as ws:
|
||||||
|
# listener loop
|
||||||
|
while True:
|
||||||
try:
|
try:
|
||||||
await consumer(msg)
|
async for msg in ws:
|
||||||
except Exception as ex:
|
try:
|
||||||
logging.debug("Error while parsing event (%s)", ex)
|
await consumer(msg)
|
||||||
except websockets.exceptions.ConnectionClosedError:
|
except Exception as ex:
|
||||||
logging.debug("Lost websocket connection. Reconnecting...")
|
logging.debug("error while parsing event (%s)", ex)
|
||||||
except websockets.exceptions.WebSocketException as wex:
|
except websockets.exceptions.ConnectionClosedError:
|
||||||
logging.debug("Websocket exception (%s)", wex)
|
try:
|
||||||
|
pong = await ws.ping()
|
||||||
|
await asyncio.wait_for(pong, timeout=ping_timeout)
|
||||||
|
logging.warning('ping OK, keeping connection alive...')
|
||||||
|
continue
|
||||||
|
except:
|
||||||
|
sleep_time = min_sleep + max_sleep*random.random()
|
||||||
|
logging.warning('ping error - retrying connection in {} sec'.format(sleep_time))
|
||||||
|
await asyncio.sleep(sleep_time)
|
||||||
|
break
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
sleep_time = min_sleep + max_sleep*random.random()
|
||||||
|
logging.warning('nobody seems to be listening at the bettercap endpoint...')
|
||||||
|
logging.warning('retrying connection in {} sec'.format(sleep_time))
|
||||||
|
await asyncio.sleep(sleep_time)
|
||||||
|
continue
|
||||||
|
except OSError:
|
||||||
|
sleep_time = min_sleep + max_sleep*random.random()
|
||||||
|
logging.warning('connection to the bettercap endpoint failed...')
|
||||||
|
logging.warning('retrying connection in {} sec'.format(sleep_time))
|
||||||
|
await asyncio.sleep(sleep_time)
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
def run(self, command, verbose_errors=True):
|
def run(self, command, verbose_errors=True):
|
||||||
r = requests.post("%s/session" % self.url, auth=self.auth, json={'cmd': command})
|
while True:
|
||||||
|
try:
|
||||||
|
r = requests.post("%s/session" % self.url, auth=self.auth, json={'cmd': command})
|
||||||
|
except requests.exceptions.ConnectionError as e:
|
||||||
|
sleep_time = min_sleep + max_sleep*random.random()
|
||||||
|
logging.warning("can't run my request... connection to the bettercap endpoint failed...")
|
||||||
|
logging.warning('retrying run in {} sec'.format(sleep_time))
|
||||||
|
sleep(sleep_time)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
return decode(r, verbose_errors=verbose_errors)
|
return decode(r, verbose_errors=verbose_errors)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
main.name = ""
|
main.name = ""
|
||||||
main.lang = "en"
|
main.lang = "en"
|
||||||
main.confd = "/etc/pwnagotchi/conf.d/"
|
main.confd = "/etc/pwnagotchi/conf.d/"
|
||||||
main.custom_plugins = ""
|
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins"
|
||||||
main.custom_plugin_repos = [
|
main.custom_plugin_repos = [
|
||||||
"https://github.com/evilsocket/pwnagotchi-plugins-contrib/archive/master.zip"
|
"https://git.chadwaltercummings.me/scifijunkie/pwnagotchi-plugins-contrib.git"
|
||||||
]
|
]
|
||||||
main.iface = "mon0"
|
main.iface = "mon0"
|
||||||
main.mon_start_cmd = "/usr/bin/monstart"
|
main.mon_start_cmd = "/usr/bin/monstart"
|
||||||
@ -18,6 +18,8 @@ main.whitelist = [
|
|||||||
]
|
]
|
||||||
main.filter = ""
|
main.filter = ""
|
||||||
|
|
||||||
|
main.log.debug = false
|
||||||
|
|
||||||
main.plugins.grid.enabled = true
|
main.plugins.grid.enabled = true
|
||||||
main.plugins.grid.report = false
|
main.plugins.grid.report = false
|
||||||
main.plugins.grid.exclude = [
|
main.plugins.grid.exclude = [
|
||||||
@ -33,7 +35,7 @@ main.plugins.net-pos.api_key = "test"
|
|||||||
|
|
||||||
main.plugins.gps.enabled = false
|
main.plugins.gps.enabled = false
|
||||||
main.plugins.gps.speed = 19200
|
main.plugins.gps.speed = 19200
|
||||||
main.plugins.gps.device = "/dev/ttyUSB0"
|
main.plugins.gps.device = "/dev/ttyUSB0" # for GPSD: "localhost:2947"
|
||||||
|
|
||||||
main.plugins.webgpsmap.enabled = false
|
main.plugins.webgpsmap.enabled = false
|
||||||
|
|
||||||
@ -47,6 +49,7 @@ main.plugins.wpa-sec.enabled = false
|
|||||||
main.plugins.wpa-sec.api_key = ""
|
main.plugins.wpa-sec.api_key = ""
|
||||||
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
|
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
|
||||||
main.plugins.wpa-sec.download_results = false
|
main.plugins.wpa-sec.download_results = false
|
||||||
|
main.plugins.wpa-sec.download_interval = 3600
|
||||||
main.plugins.wpa-sec.whitelist = []
|
main.plugins.wpa-sec.whitelist = []
|
||||||
|
|
||||||
main.plugins.wigle.enabled = false
|
main.plugins.wigle.enabled = false
|
||||||
@ -83,7 +86,10 @@ main.plugins.memtemp.scale = "celsius"
|
|||||||
main.plugins.memtemp.orientation = "horizontal"
|
main.plugins.memtemp.orientation = "horizontal"
|
||||||
|
|
||||||
main.plugins.paw-gps.enabled = false
|
main.plugins.paw-gps.enabled = false
|
||||||
main.plugins.paw-gps.ip = ""
|
main.plugins.paw-gps.ip = "192.168.44.1:8080"
|
||||||
|
|
||||||
|
main.plugins.ups_lite.enabled = false
|
||||||
|
main.plugins.ups_lite.shutdown = 2
|
||||||
|
|
||||||
main.plugins.gpio_buttons.enabled = false
|
main.plugins.gpio_buttons.enabled = false
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ def setup_logging(args, config):
|
|||||||
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s")
|
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s")
|
||||||
root = logging.getLogger()
|
root = logging.getLogger()
|
||||||
|
|
||||||
root.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
root.setLevel(logging.DEBUG if args.debug or cfg['debug']==True else logging.INFO)
|
||||||
|
|
||||||
if filename:
|
if filename:
|
||||||
# since python default log rotation might break session data in different files,
|
# since python default log rotation might break session data in different files,
|
||||||
@ -307,3 +307,5 @@ def do_rotate(filename, stats, cfg):
|
|||||||
with open(log_filename, 'rb') as src:
|
with open(log_filename, 'rb') as src:
|
||||||
with gzip.open(archive_filename, 'wb') as dst:
|
with gzip.open(archive_filename, 'wb') as dst:
|
||||||
dst.writelines(src)
|
dst.writelines(src)
|
||||||
|
|
||||||
|
os.remove(log_filename)
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
import _thread
|
|
||||||
import threading
|
import threading
|
||||||
import importlib, importlib.util
|
import importlib, importlib.util
|
||||||
import logging
|
import logging
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
|
|
||||||
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
||||||
loaded = {}
|
loaded = {}
|
||||||
database = {}
|
database = {}
|
||||||
locks = {}
|
locks = {}
|
||||||
|
|
||||||
|
THREAD_POOL_SIZE = 10
|
||||||
|
executor = ThreadPoolExecutor(max_workers=THREAD_POOL_SIZE)
|
||||||
|
|
||||||
class Plugin:
|
class Plugin:
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -30,7 +30,6 @@ class Plugin:
|
|||||||
if cb is not None and callable(cb):
|
if cb is not None and callable(cb):
|
||||||
locks["%s::%s" % (plugin_name, attr_name)] = threading.Lock()
|
locks["%s::%s" % (plugin_name, attr_name)] = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
def toggle_plugin(name, enable=True):
|
def toggle_plugin(name, enable=True):
|
||||||
"""
|
"""
|
||||||
Load or unload a plugin
|
Load or unload a plugin
|
||||||
@ -69,12 +68,10 @@ def toggle_plugin(name, enable=True):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def on(event_name, *args, **kwargs):
|
def on(event_name, *args, **kwargs):
|
||||||
for plugin_name in loaded.keys():
|
for plugin_name in loaded.keys():
|
||||||
one(plugin_name, event_name, *args, **kwargs)
|
one(plugin_name, event_name, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def locked_cb(lock_name, cb, *args, **kwargs):
|
def locked_cb(lock_name, cb, *args, **kwargs):
|
||||||
global locks
|
global locks
|
||||||
|
|
||||||
@ -84,7 +81,6 @@ def locked_cb(lock_name, cb, *args, **kwargs):
|
|||||||
with locks[lock_name]:
|
with locks[lock_name]:
|
||||||
cb(*args, *kwargs)
|
cb(*args, *kwargs)
|
||||||
|
|
||||||
|
|
||||||
def one(plugin_name, event_name, *args, **kwargs):
|
def one(plugin_name, event_name, *args, **kwargs):
|
||||||
global loaded
|
global loaded
|
||||||
|
|
||||||
@ -96,12 +92,11 @@ def one(plugin_name, event_name, *args, **kwargs):
|
|||||||
try:
|
try:
|
||||||
lock_name = "%s::%s" % (plugin_name, cb_name)
|
lock_name = "%s::%s" % (plugin_name, cb_name)
|
||||||
locked_cb_args = (lock_name, callback, *args, *kwargs)
|
locked_cb_args = (lock_name, callback, *args, *kwargs)
|
||||||
_thread.start_new_thread(locked_cb, locked_cb_args)
|
executor.submit(locked_cb, *locked_cb_args)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e))
|
logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e))
|
||||||
logging.error(e, exc_info=True)
|
logging.error(e, exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
def load_from_file(filename):
|
def load_from_file(filename):
|
||||||
logging.debug("loading %s" % filename)
|
logging.debug("loading %s" % filename)
|
||||||
plugin_name = os.path.basename(filename.replace(".py", ""))
|
plugin_name = os.path.basename(filename.replace(".py", ""))
|
||||||
@ -110,7 +105,6 @@ def load_from_file(filename):
|
|||||||
spec.loader.exec_module(instance)
|
spec.loader.exec_module(instance)
|
||||||
return plugin_name, instance
|
return plugin_name, instance
|
||||||
|
|
||||||
|
|
||||||
def load_from_path(path, enabled=()):
|
def load_from_path(path, enabled=()):
|
||||||
global loaded, database
|
global loaded, database
|
||||||
logging.debug("loading plugins from %s - enabled: %s" % (path, enabled))
|
logging.debug("loading plugins from %s - enabled: %s" % (path, enabled))
|
||||||
@ -126,7 +120,6 @@ def load_from_path(path, enabled=()):
|
|||||||
|
|
||||||
return loaded
|
return loaded
|
||||||
|
|
||||||
|
|
||||||
def load(config):
|
def load(config):
|
||||||
enabled = [name for name, options in config['main']['plugins'].items() if
|
enabled = [name for name, options in config['main']['plugins'].items() if
|
||||||
'enabled' in options and options['enabled']]
|
'enabled' in options and options['enabled']]
|
||||||
|
@ -10,7 +10,7 @@ from pwnagotchi.utils import download_file, unzip, save_config, parse_version, m
|
|||||||
from pwnagotchi.plugins import default_path
|
from pwnagotchi.plugins import default_path
|
||||||
|
|
||||||
|
|
||||||
SAVE_DIR = '/usr/local/share/pwnagotchi/availaible-plugins/'
|
SAVE_DIR = '/usr/local/share/pwnagotchi/available-plugins/'
|
||||||
DEFAULT_INSTALL_PATH = '/usr/local/share/pwnagotchi/installed-plugins/'
|
DEFAULT_INSTALL_PATH = '/usr/local/share/pwnagotchi/installed-plugins/'
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,14 +7,15 @@ import platform
|
|||||||
import shutil
|
import shutil
|
||||||
import glob
|
import glob
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
import time
|
||||||
|
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
import pwnagotchi.plugins as plugins
|
import pwnagotchi.plugins as plugins
|
||||||
from pwnagotchi.utils import StatusFile, parse_version as version_to_tuple
|
from pwnagotchi.utils import StatusFile, parse_version as version_to_tuple
|
||||||
|
|
||||||
|
|
||||||
def check(version, repo, native=True):
|
def check_remote_version(version, repo, native=True):
|
||||||
logging.debug("checking remote version for %s, local is %s" % (repo, version))
|
logging.debug("Checking remote version for %s, local is %s" % (repo, version))
|
||||||
info = {
|
info = {
|
||||||
'repo': repo,
|
'repo': repo,
|
||||||
'current': version,
|
'current': version,
|
||||||
@ -24,81 +25,92 @@ def check(version, repo, native=True):
|
|||||||
'arch': platform.machine()
|
'arch': platform.machine()
|
||||||
}
|
}
|
||||||
|
|
||||||
resp = requests.get("https://api.github.com/repos/%s/releases/latest" % repo)
|
try:
|
||||||
latest = resp.json()
|
resp = requests.get(f"https://api.github.com/repos/{repo}/releases/latest")
|
||||||
info['available'] = latest_ver = latest['tag_name'].replace('v', '')
|
resp.raise_for_status()
|
||||||
is_arm = info['arch'].startswith('arm')
|
latest = resp.json()
|
||||||
|
info['available'] = latest_ver = latest['tag_name'].replace('v', '')
|
||||||
|
|
||||||
local = version_to_tuple(info['current'])
|
is_arm = info['arch'].startswith('arm')
|
||||||
remote = version_to_tuple(latest_ver)
|
local = version_to_tuple(info['current'])
|
||||||
if remote > local:
|
remote = version_to_tuple(latest_ver)
|
||||||
if not native:
|
|
||||||
info['url'] = "https://github.com/%s/archive/%s.zip" % (repo, latest['tag_name'])
|
if remote > local:
|
||||||
else:
|
if not native:
|
||||||
# check if this release is compatible with arm6
|
info['url'] = f"https://github.com/{repo}/archive/{latest['tag_name']}.zip"
|
||||||
for asset in latest['assets']:
|
else:
|
||||||
download_url = asset['browser_download_url']
|
for asset in latest['assets']:
|
||||||
if download_url.endswith('.zip') and (
|
download_url = asset['browser_download_url']
|
||||||
info['arch'] in download_url or (is_arm and 'armhf' in download_url)):
|
if download_url.endswith('.zip') and (
|
||||||
info['url'] = download_url
|
info['arch'] in download_url or (is_arm and 'armhf' in download_url)):
|
||||||
break
|
info['url'] = download_url
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error checking remote version for {repo}: {e}")
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
def make_path_for(name):
|
def make_path_for(name):
|
||||||
path = os.path.join("/tmp/updates/", name)
|
path = os.path.join("/tmp/updates/", name)
|
||||||
if os.path.exists(path):
|
try:
|
||||||
logging.debug("[update] deleting %s" % path)
|
if os.path.exists(path):
|
||||||
shutil.rmtree(path, ignore_errors=True, onerror=None)
|
logging.debug("[update] Deleting %s" % path)
|
||||||
os.makedirs(path)
|
shutil.rmtree(path, ignore_errors=True, onerror=None)
|
||||||
|
os.makedirs(path)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error creating path for {name}: {e}")
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def download_and_unzip(name, path, display, update):
|
def download_and_unzip(name, path, display, update):
|
||||||
target = "%s_%s.zip" % (name, update['available'])
|
target = f"{name}_{update['available']}.zip"
|
||||||
target_path = os.path.join(path, target)
|
target_path = os.path.join(path, target)
|
||||||
|
|
||||||
logging.info("[update] downloading %s to %s ..." % (update['url'], target_path))
|
try:
|
||||||
display.update(force=True, new_data={'status': 'Downloading %s %s ...' % (name, update['available'])})
|
logging.info("[update] Downloading %s to %s ..." % (update['url'], target_path))
|
||||||
|
display.update(force=True, new_data={'status': f'Downloading {name} {update["available"]} ...'})
|
||||||
|
subprocess.run(['wget', '-q', update['url'], '-O', target_path], check=True)
|
||||||
|
|
||||||
os.system('wget -q "%s" -O "%s"' % (update['url'], target_path))
|
logging.info("[update] Extracting %s to %s ..." % (target_path, path))
|
||||||
|
display.update(force=True, new_data={'status': f'Extracting {name} {update["available"]} ...'})
|
||||||
|
subprocess.run(['unzip', target_path, '-d', path], check=True)
|
||||||
|
|
||||||
logging.info("[update] extracting %s to %s ..." % (target_path, path))
|
except Exception as e:
|
||||||
display.update(force=True, new_data={'status': 'Extracting %s %s ...' % (name, update['available'])})
|
logging.error(f"Error downloading and unzipping {name} update: {e}")
|
||||||
|
|
||||||
os.system('unzip "%s" -d "%s"' % (target_path, path))
|
|
||||||
|
|
||||||
|
|
||||||
def verify(name, path, source_path, display, update):
|
def verify(name, path, source_path, display, update):
|
||||||
display.update(force=True, new_data={'status': 'Verifying %s %s ...' % (name, update['available'])})
|
display.update(force=True, new_data={'status': f'Verifying {name} {update["available"]} ...'})
|
||||||
|
|
||||||
checksums = glob.glob("%s/*.sha256" % path)
|
try:
|
||||||
if len(checksums) == 0:
|
checksums = glob.glob(f"{path}/*.sha256")
|
||||||
if update['native']:
|
if len(checksums) == 0:
|
||||||
logging.warning("[update] native update without SHA256 checksum file")
|
if update['native']:
|
||||||
return False
|
logging.warning("[update] Native update without SHA256 checksum file")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
checksum = checksums[0]
|
||||||
|
logging.info(f"[update] Verifying {checksum} for {source_path} ...")
|
||||||
|
|
||||||
else:
|
with open(checksum, 'rt') as fp:
|
||||||
checksum = checksums[0]
|
expected = fp.read().split('=')[1].strip().lower()
|
||||||
|
|
||||||
logging.info("[update] verifying %s for %s ..." % (checksum, source_path))
|
real = subprocess.getoutput(f'sha256sum "{source_path}"').split(' ')[0].strip().lower()
|
||||||
|
|
||||||
with open(checksum, 'rt') as fp:
|
if real != expected:
|
||||||
expected = fp.read().split('=')[1].strip().lower()
|
logging.warning(f"[update] Checksum mismatch for {source_path}: expected={expected} got={real}")
|
||||||
|
return False
|
||||||
|
|
||||||
real = subprocess.getoutput('sha256sum "%s"' % source_path).split(' ')[0].strip().lower()
|
except Exception as e:
|
||||||
|
logging.error(f"Error verifying {name} update: {e}")
|
||||||
if real != expected:
|
return False
|
||||||
logging.warning("[update] checksum mismatch for %s: expected=%s got=%s" % (source_path, expected, real))
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def install(display, update):
|
def install(display, update):
|
||||||
name = update['repo'].split('/')[1]
|
name = update['repo'].split('/')[1]
|
||||||
|
|
||||||
path = make_path_for(name)
|
path = make_path_for(name)
|
||||||
|
|
||||||
download_and_unzip(name, path, display, update)
|
download_and_unzip(name, path, display, update)
|
||||||
@ -107,37 +119,70 @@ def install(display, update):
|
|||||||
if not verify(name, path, source_path, display, update):
|
if not verify(name, path, source_path, display, update):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logging.info("[update] installing %s ..." % name)
|
try:
|
||||||
display.update(force=True, new_data={'status': 'Installing %s %s ...' % (name, update['available'])})
|
logging.info("[update] Installing %s ..." % name)
|
||||||
|
display.update(force=True, new_data={'status': f'Installing {name} {update["available"]} ...'})
|
||||||
|
|
||||||
if update['native']:
|
if update['native']:
|
||||||
dest_path = subprocess.getoutput("which %s" % name)
|
dest_path = subprocess.getoutput(f"which {name}")
|
||||||
if dest_path == "":
|
if dest_path == "":
|
||||||
logging.warning("[update] can't find path for %s" % name)
|
logging.warning(f"[update] Can't find path for {name}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logging.info("[update] stopping %s ..." % update['service'])
|
logging.info(f"[update] Stopping {update['service']} ...")
|
||||||
os.system("service %s stop" % update['service'])
|
subprocess.run(["service", update['service'], "stop"], check=True)
|
||||||
os.system("mv %s %s" % (source_path, dest_path))
|
|
||||||
logging.info("[update] restarting %s ..." % update['service'])
|
|
||||||
os.system("service %s start" % update['service'])
|
|
||||||
else:
|
|
||||||
if not os.path.exists(source_path):
|
|
||||||
source_path = "%s-%s" % (source_path, update['available'])
|
|
||||||
|
|
||||||
# setup.py is going to install data files for us
|
subprocess.run(["mv", source_path, dest_path], check=True)
|
||||||
os.system("cd %s && pip3 install ." % source_path)
|
logging.info(f"[update] Restarting {update['service']} ...")
|
||||||
|
subprocess.run(["service", update['service'], "start"], check=True)
|
||||||
|
else:
|
||||||
|
if not os.path.exists(source_path):
|
||||||
|
source_path = f"{source_path}-{update['available']}"
|
||||||
|
|
||||||
|
subprocess.run(["cd", source_path, "&&", "pip3", "install", "."], check=True, shell=True)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error installing {name} update: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def parse_version(cmd):
|
def parse_version(cmd):
|
||||||
out = subprocess.getoutput(cmd)
|
try:
|
||||||
for part in out.split(' '):
|
out = subprocess.getoutput(cmd)
|
||||||
part = part.replace('v', '').strip()
|
for part in out.split(' '):
|
||||||
if re.search(r'^\d+\.\d+\.\d+.*$', part):
|
part = part.replace('v', '').strip()
|
||||||
return part
|
if re.search(r'^\d+\.\d+\.\d+.*$', part):
|
||||||
raise Exception('could not parse version from "%s": output=\n%s' % (cmd, out))
|
return part
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error parsing version from '{cmd}': {e}")
|
||||||
|
raise Exception(f'Could not parse version from "{cmd}": output=\n{out}')
|
||||||
|
|
||||||
|
|
||||||
|
def check_remote_version_with_retry(version, repo, native=True, max_retries=3):
|
||||||
|
retries = 0
|
||||||
|
while retries < max_retries:
|
||||||
|
try:
|
||||||
|
resp = requests.get(f"https://api.github.com/repos/{repo}/releases/latest")
|
||||||
|
resp.raise_for_status()
|
||||||
|
latest = resp.json()
|
||||||
|
return check_remote_version(version, repo, native)
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
if e.response.status_code == 403:
|
||||||
|
wait_time = 2 ** retries
|
||||||
|
print(f"Rate limit exceeded. Retrying after {wait_time} seconds...")
|
||||||
|
time.sleep(wait_time)
|
||||||
|
retries += 1
|
||||||
|
else:
|
||||||
|
print(f"Error checking remote version for {repo}: {e}")
|
||||||
|
raise e
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
wait_time = 2 ** retries
|
||||||
|
print(f"Connection error. Retrying after {wait_time} seconds...")
|
||||||
|
time.sleep(wait_time)
|
||||||
|
retries += 1
|
||||||
|
raise Exception(f"Failed to check remote version for {repo} after {max_retries} retries.")
|
||||||
|
|
||||||
|
|
||||||
class AutoUpdate(plugins.Plugin):
|
class AutoUpdate(plugins.Plugin):
|
||||||
@ -157,23 +202,23 @@ class AutoUpdate(plugins.Plugin):
|
|||||||
logging.error("[update] main.plugins.auto-update.interval is not set")
|
logging.error("[update] main.plugins.auto-update.interval is not set")
|
||||||
return
|
return
|
||||||
self.ready = True
|
self.ready = True
|
||||||
logging.info("[update] plugin loaded.")
|
logging.info("[update] Plugin loaded.")
|
||||||
|
|
||||||
def on_internet_available(self, agent):
|
def on_internet_available(self, agent):
|
||||||
if self.lock.locked():
|
if self.lock.locked():
|
||||||
return
|
return
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
logging.debug("[update] internet connectivity is available (ready %s)" % self.ready)
|
logging.debug("[update] Internet connectivity is available (ready %s)" % self.ready)
|
||||||
|
|
||||||
if not self.ready:
|
if not self.ready:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.status.newer_then_hours(self.options['interval']):
|
if self.status.newer_then_hours(self.options['interval']):
|
||||||
logging.debug("[update] last check happened less than %d hours ago" % self.options['interval'])
|
logging.debug("[update] Last check happened less than %d hours ago" % self.options['interval'])
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info("[update] checking for updates ...")
|
logging.info("[update] Checking for updates ...")
|
||||||
|
|
||||||
display = agent.view()
|
display = agent.view()
|
||||||
prev_status = display.get('status')
|
prev_status = display.get('status')
|
||||||
@ -189,11 +234,10 @@ class AutoUpdate(plugins.Plugin):
|
|||||||
]
|
]
|
||||||
|
|
||||||
for repo, local_version, is_native, svc_name in to_check:
|
for repo, local_version, is_native, svc_name in to_check:
|
||||||
info = check(local_version, repo, is_native)
|
info = check_remote_version_with_retry(local_version, repo, is_native)
|
||||||
if info['url'] is not None:
|
if info['url'] is not None:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"update for %s available (local version is '%s'): %s" % (
|
f"Update for {repo} available (local version is '{info['current']}'): {info['url']}")
|
||||||
repo, info['current'], info['url']))
|
|
||||||
info['service'] = svc_name
|
info['service'] = svc_name
|
||||||
to_install.append(info)
|
to_install.append(info)
|
||||||
|
|
||||||
@ -207,9 +251,9 @@ class AutoUpdate(plugins.Plugin):
|
|||||||
if install(display, update):
|
if install(display, update):
|
||||||
num_installed += 1
|
num_installed += 1
|
||||||
else:
|
else:
|
||||||
prev_status = '%d new update%c available!' % (num_updates, 's' if num_updates > 1 else '')
|
prev_status = f"{num_updates} new update{'s' if num_updates > 1 else ''} available!"
|
||||||
|
|
||||||
logging.info("[update] done")
|
logging.info("[update] Done")
|
||||||
|
|
||||||
self.status.update()
|
self.status.update()
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class GPS(plugins.Plugin):
|
|||||||
logging.info(f"gps plugin loaded for {self.options['device']}")
|
logging.info(f"gps plugin loaded for {self.options['device']}")
|
||||||
|
|
||||||
def on_ready(self, agent):
|
def on_ready(self, agent):
|
||||||
if os.path.exists(self.options["device"]):
|
if os.path.exists(self.options["device"]) or ":" in self.options["device"]:
|
||||||
logging.info(
|
logging.info(
|
||||||
f"enabling bettercap's gps module for {self.options['device']}"
|
f"enabling bettercap's gps module for {self.options['device']}"
|
||||||
)
|
)
|
||||||
|
@ -10,25 +10,30 @@ GUIDE HERE: https://community.pwnagotchi.ai/t/setting-up-paw-gps-on-android
|
|||||||
|
|
||||||
class PawGPS(plugins.Plugin):
|
class PawGPS(plugins.Plugin):
|
||||||
__author__ = 'leont'
|
__author__ = 'leont'
|
||||||
__version__ = '1.0.0'
|
__version__ = '1.0.1'
|
||||||
__name__ = 'pawgps'
|
__name__ = 'pawgps'
|
||||||
__license__ = 'GPL3'
|
__license__ = 'GPL3'
|
||||||
__description__ = 'Saves GPS coordinates whenever an handshake is captured. The GPS data is get from PAW on android '
|
__description__ = 'Saves GPS coordinates whenever an handshake is captured. The GPS data is get from PAW on android.'
|
||||||
|
|
||||||
def on_loaded(self):
|
def on_loaded(self):
|
||||||
logging.info("PAW-GPS loaded")
|
logging.info("[paw-gps] plugin loaded")
|
||||||
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None):
|
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None) or (len('ip' in self.options and self.options['ip']) is 0):
|
||||||
logging.info("PAW-GPS: No IP Address in the config file is defined, it uses the default (192.168.44.1:8080)")
|
logging.info("[paw-gps] no IP Address defined in the config file, will uses paw server default (192.168.44.1:8080)")
|
||||||
|
|
||||||
def on_handshake(self, agent, filename, access_point, client_station):
|
def on_handshake(self, agent, filename, access_point, client_station):
|
||||||
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None):
|
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None or (len('ip' in self.options and self.options['ip']) is 0)):
|
||||||
ip = "192.168.44.1:8080"
|
ip = "192.168.44.1:8080"
|
||||||
else:
|
else:
|
||||||
ip = self.options['ip']
|
ip = self.options['ip']
|
||||||
|
|
||||||
gps = requests.get('http://' + ip + '/gps.xhtml')
|
try:
|
||||||
gps_filename = filename.replace('.pcap', '.paw-gps.json')
|
gps = requests.get('http://' + ip + '/gps.xhtml')
|
||||||
|
try:
|
||||||
logging.info("saving GPS to %s (%s)" % (gps_filename, gps))
|
gps_filename = filename.replace('.pcap', '.paw-gps.json')
|
||||||
with open(gps_filename, 'w+t') as f:
|
logging.info("[paw-gps] saving GPS data to %s" % (gps_filename))
|
||||||
f.write(gps.text)
|
with open(gps_filename, 'w+t') as f:
|
||||||
|
f.write(gps.text)
|
||||||
|
except Exception as error:
|
||||||
|
logging.error(f"[paw-gps] encountered error while saving gps data: {error}")
|
||||||
|
except Exception as error:
|
||||||
|
logging.error(f"[paw-gps] encountered error while getting gps data: {error}")
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
# To display external power supply status you need to bridge the necessary pins on the UPS-Lite board. See instructions in the UPS-Lite repo.
|
# To display external power supply status you need to bridge the necessary pins on the UPS-Lite board. See instructions in the UPS-Lite repo.
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
|
import subprocess
|
||||||
|
|
||||||
import RPi.GPIO as GPIO
|
import RPi.GPIO as GPIO
|
||||||
|
|
||||||
@ -28,11 +29,16 @@ class UPS:
|
|||||||
import smbus
|
import smbus
|
||||||
# 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
|
# 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
|
||||||
self._bus = smbus.SMBus(1)
|
self._bus = smbus.SMBus(1)
|
||||||
|
# Version v1.1 and v1.2
|
||||||
|
self.address = 0x36
|
||||||
|
if subprocess.run(['i2cget', '-y', '1', '0x62']).returncode == 0:
|
||||||
|
# Version v1.3
|
||||||
|
self.address = 0X62
|
||||||
|
self._bus.write_word_data(self.address, 0X0A, 0x30)
|
||||||
|
|
||||||
def voltage(self):
|
def voltage(self):
|
||||||
try:
|
try:
|
||||||
address = 0x36
|
read = self._bus.read_word_data(self.address, 2)
|
||||||
read = self._bus.read_word_data(address, 2)
|
|
||||||
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
|
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
|
||||||
return swapped * 1.25 / 1000 / 16
|
return swapped * 1.25 / 1000 / 16
|
||||||
except:
|
except:
|
||||||
@ -40,8 +46,7 @@ class UPS:
|
|||||||
|
|
||||||
def capacity(self):
|
def capacity(self):
|
||||||
try:
|
try:
|
||||||
address = 0x36
|
read = self._bus.read_word_data(self.address, 4)
|
||||||
read = self._bus.read_word_data(address, 4)
|
|
||||||
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
|
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
|
||||||
return swapped / 256
|
return swapped / 256
|
||||||
except:
|
except:
|
||||||
@ -60,7 +65,7 @@ class UPSLite(plugins.Plugin):
|
|||||||
__author__ = 'evilsocket@gmail.com'
|
__author__ = 'evilsocket@gmail.com'
|
||||||
__version__ = '1.0.0'
|
__version__ = '1.0.0'
|
||||||
__license__ = 'GPL3'
|
__license__ = 'GPL3'
|
||||||
__description__ = 'A plugin that will add a voltage indicator for the UPS Lite v1.1'
|
__description__ = 'A plugin that will add a voltage indicator for the UPS Lite v1.1, v1.2, v1.3'
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ups = None
|
self.ups = None
|
||||||
|
@ -85,7 +85,6 @@ def _send_to_wigle(lines, api_key, donate=True, timeout=30):
|
|||||||
'Accept': 'application/json'}
|
'Accept': 'application/json'}
|
||||||
data = {'donate': 'on' if donate else 'false'}
|
data = {'donate': 'on' if donate else 'false'}
|
||||||
payload = {'file': dummy, 'type': 'text/csv'}
|
payload = {'file': dummy, 'type': 'text/csv'}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res = requests.post('https://api.wigle.net/api/v2/file/upload',
|
res = requests.post('https://api.wigle.net/api/v2/file/upload',
|
||||||
data=data,
|
data=data,
|
||||||
@ -141,7 +140,7 @@ class Wigle(plugins.Plugin):
|
|||||||
all_files = os.listdir(handshake_dir)
|
all_files = os.listdir(handshake_dir)
|
||||||
all_gps_files = [os.path.join(handshake_dir, filename)
|
all_gps_files = [os.path.join(handshake_dir, filename)
|
||||||
for filename in all_files
|
for filename in all_files
|
||||||
if filename.endswith('.gps.json' or filename.endswith('.paw-gps.json') or filename.endswith('.geo.json')]
|
if filename.endswith('.gps.json') or filename.endswith('.paw-gps.json') or filename.endswith('.geo.json')]
|
||||||
|
|
||||||
all_gps_files = remove_whitelisted(all_gps_files, self.options['whitelist'])
|
all_gps_files = remove_whitelisted(all_gps_files, self.options['whitelist'])
|
||||||
new_gps_files = set(all_gps_files) - set(reported) - set(self.skip)
|
new_gps_files = set(all_gps_files) - set(reported) - set(self.skip)
|
||||||
|
@ -132,7 +132,8 @@ class WpaSec(plugins.Plugin):
|
|||||||
cracked_file = os.path.join(handshake_dir, 'wpa-sec.cracked.potfile')
|
cracked_file = os.path.join(handshake_dir, 'wpa-sec.cracked.potfile')
|
||||||
if os.path.exists(cracked_file):
|
if os.path.exists(cracked_file):
|
||||||
last_check = datetime.fromtimestamp(os.path.getmtime(cracked_file))
|
last_check = datetime.fromtimestamp(os.path.getmtime(cracked_file))
|
||||||
if last_check is not None and ((datetime.now() - last_check).seconds / (60 * 60)) < 1:
|
download_interval = int(self.options['download_interval'])
|
||||||
|
if last_check is not None and ((datetime.now() - last_check).seconds / download_interval) < 1:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self._download_from_wpasec(os.path.join(handshake_dir, 'wpa-sec.cracked.potfile'))
|
self._download_from_wpasec(os.path.join(handshake_dir, 'wpa-sec.cracked.potfile'))
|
||||||
|
@ -40,9 +40,15 @@ class Display(View):
|
|||||||
def is_waveshare_v3(self):
|
def is_waveshare_v3(self):
|
||||||
return self._implementation.name == 'waveshare_3'
|
return self._implementation.name == 'waveshare_3'
|
||||||
|
|
||||||
|
def is_waveshare_v4(self):
|
||||||
|
return self._implementation.name == 'waveshare_4'
|
||||||
|
|
||||||
def is_waveshare27inch(self):
|
def is_waveshare27inch(self):
|
||||||
return self._implementation.name == 'waveshare27inch'
|
return self._implementation.name == 'waveshare27inch'
|
||||||
|
|
||||||
|
def is_waveshare27inchv2(self):
|
||||||
|
return self._implementation.name == 'waveshare27inchv2'
|
||||||
|
|
||||||
def is_waveshare29inch(self):
|
def is_waveshare29inch(self):
|
||||||
return self._implementation.name == 'waveshare29inch'
|
return self._implementation.name == 'waveshare29inch'
|
||||||
|
|
||||||
@ -67,15 +73,24 @@ class Display(View):
|
|||||||
def is_waveshare213d(self):
|
def is_waveshare213d(self):
|
||||||
return self._implementation.name == 'waveshare213d'
|
return self._implementation.name == 'waveshare213d'
|
||||||
|
|
||||||
|
def is_waveshare213g(self):
|
||||||
|
return self._implementation.name == 'waveshare213g'
|
||||||
|
|
||||||
def is_waveshare213bc(self):
|
def is_waveshare213bc(self):
|
||||||
return self._implementation.name == 'waveshare213bc'
|
return self._implementation.name == 'waveshare213bc'
|
||||||
|
|
||||||
|
def is_waveshare213inb_v4(self):
|
||||||
|
return self._implementation.name == 'waveshare213inb_v4'
|
||||||
|
|
||||||
def is_waveshare35lcd(self):
|
def is_waveshare35lcd(self):
|
||||||
return self._implementation.name == 'waveshare35lcd'
|
return self._implementation.name == 'waveshare35lcd'
|
||||||
|
|
||||||
def is_spotpear24inch(self):
|
def is_spotpear24inch(self):
|
||||||
return self._implementation.name == 'spotpear24inch'
|
return self._implementation.name == 'spotpear24inch'
|
||||||
|
|
||||||
|
def is_displayhatmini(self):
|
||||||
|
return self._implementation.name == 'displayhatmini'
|
||||||
|
|
||||||
def is_waveshare_any(self):
|
def is_waveshare_any(self):
|
||||||
return self.is_waveshare_v1() or self.is_waveshare_v2()
|
return self.is_waveshare_v1() or self.is_waveshare_v2()
|
||||||
|
|
||||||
|
@ -7,14 +7,19 @@ from pwnagotchi.ui.hw.dfrobot2 import DFRobotV2
|
|||||||
from pwnagotchi.ui.hw.waveshare1 import WaveshareV1
|
from pwnagotchi.ui.hw.waveshare1 import WaveshareV1
|
||||||
from pwnagotchi.ui.hw.waveshare2 import WaveshareV2
|
from pwnagotchi.ui.hw.waveshare2 import WaveshareV2
|
||||||
from pwnagotchi.ui.hw.waveshare3 import WaveshareV3
|
from pwnagotchi.ui.hw.waveshare3 import WaveshareV3
|
||||||
|
from pwnagotchi.ui.hw.waveshare4 import WaveshareV4
|
||||||
from pwnagotchi.ui.hw.waveshare27inch import Waveshare27inch
|
from pwnagotchi.ui.hw.waveshare27inch import Waveshare27inch
|
||||||
|
from pwnagotchi.ui.hw.waveshare27inchv2 import Waveshare27inchV2
|
||||||
from pwnagotchi.ui.hw.waveshare29inch import Waveshare29inch
|
from pwnagotchi.ui.hw.waveshare29inch import Waveshare29inch
|
||||||
from pwnagotchi.ui.hw.waveshare144lcd import Waveshare144lcd
|
from pwnagotchi.ui.hw.waveshare144lcd import Waveshare144lcd
|
||||||
from pwnagotchi.ui.hw.waveshare154inch import Waveshare154inch
|
from pwnagotchi.ui.hw.waveshare154inch import Waveshare154inch
|
||||||
from pwnagotchi.ui.hw.waveshare213d import Waveshare213d
|
from pwnagotchi.ui.hw.waveshare213d import Waveshare213d
|
||||||
|
from pwnagotchi.ui.hw.waveshare213g import Waveshare213g
|
||||||
from pwnagotchi.ui.hw.waveshare213bc import Waveshare213bc
|
from pwnagotchi.ui.hw.waveshare213bc import Waveshare213bc
|
||||||
|
from pwnagotchi.ui.hw.waveshare213inb_v4 import Waveshare213bV4
|
||||||
from pwnagotchi.ui.hw.waveshare35lcd import Waveshare35lcd
|
from pwnagotchi.ui.hw.waveshare35lcd import Waveshare35lcd
|
||||||
from pwnagotchi.ui.hw.spotpear24inch import Spotpear24inch
|
from pwnagotchi.ui.hw.spotpear24inch import Spotpear24inch
|
||||||
|
from pwnagotchi.ui.hw.displayhatmini import DisplayHatMini
|
||||||
|
|
||||||
def display_for(config):
|
def display_for(config):
|
||||||
# config has been normalized already in utils.load_config
|
# config has been normalized already in utils.load_config
|
||||||
@ -45,9 +50,15 @@ def display_for(config):
|
|||||||
elif config['ui']['display']['type'] == 'waveshare_3':
|
elif config['ui']['display']['type'] == 'waveshare_3':
|
||||||
return WaveshareV3(config)
|
return WaveshareV3(config)
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] == 'waveshare_4':
|
||||||
|
return WaveshareV4(config)
|
||||||
|
|
||||||
elif config['ui']['display']['type'] == 'waveshare27inch':
|
elif config['ui']['display']['type'] == 'waveshare27inch':
|
||||||
return Waveshare27inch(config)
|
return Waveshare27inch(config)
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] == 'waveshare27inchv2':
|
||||||
|
return Waveshare27inchV2(config)
|
||||||
|
|
||||||
elif config['ui']['display']['type'] == 'waveshare29inch':
|
elif config['ui']['display']['type'] == 'waveshare29inch':
|
||||||
return Waveshare29inch(config)
|
return Waveshare29inch(config)
|
||||||
|
|
||||||
@ -60,11 +71,20 @@ def display_for(config):
|
|||||||
elif config['ui']['display']['type'] == 'waveshare213d':
|
elif config['ui']['display']['type'] == 'waveshare213d':
|
||||||
return Waveshare213d(config)
|
return Waveshare213d(config)
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] == 'waveshare213g':
|
||||||
|
return Waveshare213g(config)
|
||||||
|
|
||||||
elif config['ui']['display']['type'] == 'waveshare213bc':
|
elif config['ui']['display']['type'] == 'waveshare213bc':
|
||||||
return Waveshare213bc(config)
|
return Waveshare213bc(config)
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] == 'waveshare213inb_v4':
|
||||||
|
return Waveshare213bV4(config)
|
||||||
|
|
||||||
elif config['ui']['display']['type'] == 'waveshare35lcd':
|
elif config['ui']['display']['type'] == 'waveshare35lcd':
|
||||||
return Waveshare35lcd(config)
|
return Waveshare35lcd(config)
|
||||||
|
|
||||||
elif config['ui']['display']['type'] == 'spotpear24inch':
|
elif config['ui']['display']['type'] == 'spotpear24inch':
|
||||||
return Spotpear24inch(config)
|
return Spotpear24inch(config)
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] == 'displayhatmini':
|
||||||
|
return DisplayHatMini(config)
|
||||||
|
@ -3,6 +3,8 @@ import pwnagotchi.ui.fonts as fonts
|
|||||||
|
|
||||||
class DisplayImpl(object):
|
class DisplayImpl(object):
|
||||||
def __init__(self, config, name):
|
def __init__(self, config, name):
|
||||||
|
if fonts.Medium is None:
|
||||||
|
fonts.init(config)
|
||||||
self.name = name
|
self.name = name
|
||||||
self.config = config['ui']['display']
|
self.config = config['ui']['display']
|
||||||
self._layout = {
|
self._layout = {
|
||||||
|
@ -46,7 +46,7 @@ class EPD:
|
|||||||
self.cs_pin = epdconfig.CS_PIN
|
self.cs_pin = epdconfig.CS_PIN
|
||||||
self.width = EPD_WIDTH
|
self.width = EPD_WIDTH
|
||||||
self.height = EPD_HEIGHT
|
self.height = EPD_HEIGHT
|
||||||
|
|
||||||
lut_partial_update= [
|
lut_partial_update= [
|
||||||
0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
|
0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
|
||||||
0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
|
0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
|
||||||
@ -90,7 +90,7 @@ class EPD:
|
|||||||
0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0,
|
0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0,
|
||||||
0x22,0x17,0x41,0x0,0x32,0x36,
|
0x22,0x17,0x41,0x0,0x32,0x36,
|
||||||
]
|
]
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function :Hardware reset
|
function :Hardware reset
|
||||||
parameter:
|
parameter:
|
||||||
@ -124,7 +124,7 @@ class EPD:
|
|||||||
epdconfig.digital_write(self.cs_pin, 0)
|
epdconfig.digital_write(self.cs_pin, 0)
|
||||||
epdconfig.spi_writebyte([data])
|
epdconfig.spi_writebyte([data])
|
||||||
epdconfig.digital_write(self.cs_pin, 1)
|
epdconfig.digital_write(self.cs_pin, 1)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function :Wait until the busy_pin goes LOW
|
function :Wait until the busy_pin goes LOW
|
||||||
parameter:
|
parameter:
|
||||||
@ -144,7 +144,7 @@ class EPD:
|
|||||||
self.send_data(0xC7)
|
self.send_data(0xC7)
|
||||||
self.send_command(0x20) # Activate Display Update Sequence
|
self.send_command(0x20) # Activate Display Update Sequence
|
||||||
self.ReadBusy()
|
self.ReadBusy()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function : Turn On Display Part
|
function : Turn On Display Part
|
||||||
parameter:
|
parameter:
|
||||||
@ -154,7 +154,7 @@ class EPD:
|
|||||||
self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf
|
self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf
|
||||||
self.send_command(0x20) # Activate Display Update Sequence
|
self.send_command(0x20) # Activate Display Update Sequence
|
||||||
self.ReadBusy()
|
self.ReadBusy()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function : Set lut
|
function : Set lut
|
||||||
parameter:
|
parameter:
|
||||||
@ -165,7 +165,7 @@ class EPD:
|
|||||||
for i in range(0, 153):
|
for i in range(0, 153):
|
||||||
self.send_data(lut[i])
|
self.send_data(lut[i])
|
||||||
self.ReadBusy()
|
self.ReadBusy()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function : Send lut data and configuration
|
function : Send lut data and configuration
|
||||||
parameter:
|
parameter:
|
||||||
@ -183,7 +183,7 @@ class EPD:
|
|||||||
self.send_data(lut[157]) # VSL
|
self.send_data(lut[157]) # VSL
|
||||||
self.send_command(0x2c) # VCOM
|
self.send_command(0x2c) # VCOM
|
||||||
self.send_data(lut[158])
|
self.send_data(lut[158])
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function : Setting the display window
|
function : Setting the display window
|
||||||
parameter:
|
parameter:
|
||||||
@ -197,7 +197,7 @@ class EPD:
|
|||||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||||
self.send_data((x_start>>3) & 0xFF)
|
self.send_data((x_start>>3) & 0xFF)
|
||||||
self.send_data((x_end>>3) & 0xFF)
|
self.send_data((x_end>>3) & 0xFF)
|
||||||
|
|
||||||
self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION
|
self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION
|
||||||
self.send_data(y_start & 0xFF)
|
self.send_data(y_start & 0xFF)
|
||||||
self.send_data((y_start >> 8) & 0xFF)
|
self.send_data((y_start >> 8) & 0xFF)
|
||||||
@ -214,11 +214,11 @@ class EPD:
|
|||||||
self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER
|
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
|
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||||
self.send_data(x & 0xFF)
|
self.send_data(x & 0xFF)
|
||||||
|
|
||||||
self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER
|
self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER
|
||||||
self.send_data(y & 0xFF)
|
self.send_data(y & 0xFF)
|
||||||
self.send_data((y >> 8) & 0xFF)
|
self.send_data((y >> 8) & 0xFF)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function : Initialize the e-Paper register
|
function : Initialize the e-Paper register
|
||||||
parameter:
|
parameter:
|
||||||
@ -228,7 +228,7 @@ class EPD:
|
|||||||
return -1
|
return -1
|
||||||
# EPD hardware init start
|
# EPD hardware init start
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
self.ReadBusy()
|
self.ReadBusy()
|
||||||
self.send_command(0x12) #SWRESET
|
self.send_command(0x12) #SWRESET
|
||||||
self.ReadBusy()
|
self.ReadBusy()
|
||||||
@ -237,25 +237,25 @@ class EPD:
|
|||||||
self.send_data(0xf9)
|
self.send_data(0xf9)
|
||||||
self.send_data(0x00)
|
self.send_data(0x00)
|
||||||
self.send_data(0x00)
|
self.send_data(0x00)
|
||||||
|
|
||||||
self.send_command(0x11) #data entry mode
|
self.send_command(0x11) #data entry mode
|
||||||
self.send_data(0x03)
|
self.send_data(0x03)
|
||||||
|
|
||||||
self.SetWindow(0, 0, self.width-1, self.height-1)
|
self.SetWindow(0, 0, self.width-1, self.height-1)
|
||||||
self.SetCursor(0, 0)
|
self.SetCursor(0, 0)
|
||||||
|
|
||||||
self.send_command(0x3c)
|
self.send_command(0x3c)
|
||||||
self.send_data(0x05)
|
self.send_data(0x05)
|
||||||
|
|
||||||
self.send_command(0x21) # Display update control
|
self.send_command(0x21) # Display update control
|
||||||
self.send_data(0x00)
|
self.send_data(0x00)
|
||||||
self.send_data(0x80)
|
self.send_data(0x80)
|
||||||
|
|
||||||
self.send_command(0x18)
|
self.send_command(0x18)
|
||||||
self.send_data(0x80)
|
self.send_data(0x80)
|
||||||
|
|
||||||
self.ReadBusy()
|
self.ReadBusy()
|
||||||
|
|
||||||
self.SetLut(self.lut_full_update)
|
self.SetLut(self.lut_full_update)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ class EPD:
|
|||||||
|
|
||||||
buf = bytearray(img.tobytes('raw'))
|
buf = bytearray(img.tobytes('raw'))
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function : Sends the image buffer in RAM to e-Paper and displays
|
function : Sends the image buffer in RAM to e-Paper and displays
|
||||||
parameter:
|
parameter:
|
||||||
@ -296,7 +296,7 @@ class EPD:
|
|||||||
for i in range(0, linewidth):
|
for i in range(0, linewidth):
|
||||||
self.send_data(image[i + j * linewidth])
|
self.send_data(image[i + j * linewidth])
|
||||||
self.TurnOnDisplay()
|
self.TurnOnDisplay()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function : Sends the image buffer in RAM to e-Paper and partial refresh
|
function : Sends the image buffer in RAM to e-Paper and partial refresh
|
||||||
parameter:
|
parameter:
|
||||||
@ -311,7 +311,7 @@ class EPD:
|
|||||||
epdconfig.digital_write(self.reset_pin, 0)
|
epdconfig.digital_write(self.reset_pin, 0)
|
||||||
epdconfig.delay_ms(1)
|
epdconfig.delay_ms(1)
|
||||||
epdconfig.digital_write(self.reset_pin, 1)
|
epdconfig.digital_write(self.reset_pin, 1)
|
||||||
|
|
||||||
self.SetLut(self.lut_partial_update)
|
self.SetLut(self.lut_partial_update)
|
||||||
self.send_command(0x37)
|
self.send_command(0x37)
|
||||||
self.send_data(0x00)
|
self.send_data(0x00)
|
||||||
@ -335,7 +335,7 @@ class EPD:
|
|||||||
|
|
||||||
self.SetWindow(0, 0, self.width - 1, self.height - 1)
|
self.SetWindow(0, 0, self.width - 1, self.height - 1)
|
||||||
self.SetCursor(0, 0)
|
self.SetCursor(0, 0)
|
||||||
|
|
||||||
self.send_command(0x24) # WRITE_RAM
|
self.send_command(0x24) # WRITE_RAM
|
||||||
for j in range(0, self.height):
|
for j in range(0, self.height):
|
||||||
for i in range(0, linewidth):
|
for i in range(0, linewidth):
|
||||||
@ -357,13 +357,13 @@ class EPD:
|
|||||||
for j in range(0, self.height):
|
for j in range(0, self.height):
|
||||||
for i in range(0, linewidth):
|
for i in range(0, linewidth):
|
||||||
self.send_data(image[i + j * linewidth])
|
self.send_data(image[i + j * linewidth])
|
||||||
|
|
||||||
self.send_command(0x26)
|
self.send_command(0x26)
|
||||||
for j in range(0, self.height):
|
for j in range(0, self.height):
|
||||||
for i in range(0, linewidth):
|
for i in range(0, linewidth):
|
||||||
self.send_data(image[i + j * linewidth])
|
self.send_data(image[i + j * linewidth])
|
||||||
self.TurnOnDisplay()
|
self.TurnOnDisplay()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
function : Clear screen
|
function : Clear screen
|
||||||
parameter:
|
parameter:
|
||||||
@ -374,12 +374,12 @@ class EPD:
|
|||||||
else:
|
else:
|
||||||
linewidth = int(self.width/8) + 1
|
linewidth = int(self.width/8) + 1
|
||||||
# logger.debug(linewidth)
|
# logger.debug(linewidth)
|
||||||
|
|
||||||
self.send_command(0x24)
|
self.send_command(0x24)
|
||||||
for j in range(0, self.height):
|
for j in range(0, self.height):
|
||||||
for i in range(0, linewidth):
|
for i in range(0, linewidth):
|
||||||
self.send_data(color)
|
self.send_data(color)
|
||||||
|
|
||||||
self.TurnOnDisplay()
|
self.TurnOnDisplay()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -389,7 +389,7 @@ class EPD:
|
|||||||
def sleep(self):
|
def sleep(self):
|
||||||
self.send_command(0x10) #enter deep sleep
|
self.send_command(0x10) #enter deep sleep
|
||||||
self.send_data(0x01)
|
self.send_data(0x01)
|
||||||
|
|
||||||
epdconfig.delay_ms(2000)
|
epdconfig.delay_ms(2000)
|
||||||
epdconfig.module_exit()
|
epdconfig.module_exit()
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class WaveshareV3(DisplayImpl):
|
|||||||
self._display = None
|
self._display = None
|
||||||
|
|
||||||
def layout(self):
|
def layout(self):
|
||||||
fonts.setup(10, 8, 10, 25, 25, 9)
|
fonts.setup(10, 8, 10, 35, 25, 9)
|
||||||
self._layout['width'] = 250
|
self._layout['width'] = 250
|
||||||
self._layout['height'] = 122
|
self._layout['height'] = 122
|
||||||
self._layout['face'] = (0, 40)
|
self._layout['face'] = (0, 40)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
{% block meta %}
|
{% block meta %}
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@ -15,6 +16,8 @@
|
|||||||
{% block styles %}
|
{% block styles %}
|
||||||
<link rel="stylesheet" href="/js/jquery.mobile/jquery.mobile-1.4.5.min.css"/>
|
<link rel="stylesheet" href="/js/jquery.mobile/jquery.mobile-1.4.5.min.css"/>
|
||||||
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
|
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
|
||||||
|
<link rel="apple-touch-icon" href="/images/pwnagotchi.png">
|
||||||
|
<link rel="icon" type="image/png" href="/images/pwnagotchi.png">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
@ -8,54 +8,61 @@ Plugins
|
|||||||
{% block styles %}
|
{% block styles %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<style>
|
<style>
|
||||||
.tooltip {
|
.plugins-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
margin: 10px;
|
||||||
.tooltip .tooltiptext {
|
}
|
||||||
visibility: hidden;
|
|
||||||
width: 200px;
|
|
||||||
background-color: #3388cc;
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 2px solid black;
|
|
||||||
padding: 20px 0;
|
|
||||||
|
|
||||||
position: absolute;
|
.tooltip {
|
||||||
z-index: 1;
|
position: relative;
|
||||||
top: 100%;
|
display: inline-block;
|
||||||
left: 50%;
|
}
|
||||||
margin-left: -100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip:hover .tooltiptext {
|
.tooltip .tooltiptext {
|
||||||
visibility: visible;
|
visibility: hidden;
|
||||||
}
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
background-color: #3388cc;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid black;
|
||||||
|
padding: 20px 0;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover .tooltiptext {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
$(function(){
|
$(function(){
|
||||||
$('input[type=checkbox]').change(function(e) {
|
$('input[type=checkbox]').change(function(e) {
|
||||||
var checkbox = $(this);
|
var checkbox = $(this);
|
||||||
var form = checkbox.closest('form');
|
var form = checkbox.closest('form');
|
||||||
var url = form.attr('action');
|
var url = form.attr('action');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: url,
|
url: url,
|
||||||
data: form.serialize(),
|
data: form.serialize(),
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
if( data.indexOf('failed') != -1 ) {
|
if (data.indexOf('failed') != -1) {
|
||||||
alert('Could not be toggled.');
|
alert('Could not be toggled.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="container">
|
<div id="container">
|
||||||
{% for name in database.keys() | sort %}
|
{% for name in database.keys() | sort %}
|
||||||
@ -65,6 +72,11 @@ $(function(){
|
|||||||
<h4>
|
<h4>
|
||||||
<a {% if name in loaded and loaded[name].on_webhook is defined %} href="/plugins/{{name}}" {% endif %}>{{name}}</a>
|
<a {% if name in loaded and loaded[name].on_webhook is defined %} href="/plugins/{{name}}" {% endif %}>{{name}}</a>
|
||||||
</h4>
|
</h4>
|
||||||
|
{% if has_info %}
|
||||||
|
{% if loaded[name].__version__ is defined %}
|
||||||
|
<p>v{{ loaded[name].__version__ }}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% if has_info %}
|
{% if has_info %}
|
||||||
<span class="tooltiptext">{{ loaded[name].__description__ }}</span>
|
<span class="tooltiptext">{{ loaded[name].__description__ }}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -251,9 +251,15 @@ def load_config(args):
|
|||||||
elif config['ui']['display']['type'] in ('ws_3', 'ws3', 'waveshare_3', 'waveshare3'):
|
elif config['ui']['display']['type'] in ('ws_3', 'ws3', 'waveshare_3', 'waveshare3'):
|
||||||
config['ui']['display']['type'] = 'waveshare_3'
|
config['ui']['display']['type'] = 'waveshare_3'
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] in ('ws_4', 'ws4', 'waveshare_4', 'waveshare4'):
|
||||||
|
config['ui']['display']['type'] = 'waveshare_4'
|
||||||
|
|
||||||
elif config['ui']['display']['type'] in ('ws_27inch', 'ws27inch', 'waveshare_27inch', 'waveshare27inch'):
|
elif config['ui']['display']['type'] in ('ws_27inch', 'ws27inch', 'waveshare_27inch', 'waveshare27inch'):
|
||||||
config['ui']['display']['type'] = 'waveshare27inch'
|
config['ui']['display']['type'] = 'waveshare27inch'
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] in ('ws_27inchv2', 'ws27inchv2', 'waveshare_27inchv2', 'waveshare27inchv2'):
|
||||||
|
config['ui']['display']['type'] = 'waveshare27inchv2'
|
||||||
|
|
||||||
elif config['ui']['display']['type'] in ('ws_29inch', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'):
|
elif config['ui']['display']['type'] in ('ws_29inch', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'):
|
||||||
config['ui']['display']['type'] = 'waveshare29inch'
|
config['ui']['display']['type'] = 'waveshare29inch'
|
||||||
|
|
||||||
@ -275,15 +281,24 @@ def load_config(args):
|
|||||||
elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare_213d', 'waveshare213d'):
|
elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare_213d', 'waveshare213d'):
|
||||||
config['ui']['display']['type'] = 'waveshare213d'
|
config['ui']['display']['type'] = 'waveshare213d'
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] in ('ws_213g', 'ws213g', 'waveshare_213g', 'waveshare213g'):
|
||||||
|
config['ui']['display']['type'] = 'waveshare213g'
|
||||||
|
|
||||||
elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare_213bc', 'waveshare213bc'):
|
elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare_213bc', 'waveshare213bc'):
|
||||||
config['ui']['display']['type'] = 'waveshare213bc'
|
config['ui']['display']['type'] = 'waveshare213bc'
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] in ('ws_213bv4', 'ws213bv4', 'waveshare_213bv4', 'waveshare213inb_v4'):
|
||||||
|
config['ui']['display']['type'] = 'waveshare213inb_v4'
|
||||||
|
|
||||||
elif config['ui']['display']['type'] in ('waveshare35lcd'):
|
elif config['ui']['display']['type'] in ('waveshare35lcd'):
|
||||||
config['ui']['display']['type'] = 'waveshare35lcd'
|
config['ui']['display']['type'] = 'waveshare35lcd'
|
||||||
|
|
||||||
elif config['ui']['display']['type'] in ('spotpear24inch'):
|
elif config['ui']['display']['type'] in ('spotpear24inch'):
|
||||||
config['ui']['display']['type'] = 'spotpear24inch'
|
config['ui']['display']['type'] = 'spotpear24inch'
|
||||||
|
|
||||||
|
elif config['ui']['display']['type'] in ('displayhatmini'):
|
||||||
|
config['ui']['display']['type'] = 'displayhatmini'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("unsupported display type %s" % config['ui']['display']['type'])
|
print("unsupported display type %s" % config['ui']['display']['type'])
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
217
requirements.txt
217
requirements.txt
@ -1,27 +1,194 @@
|
|||||||
pycryptodome==3.9.4
|
#
|
||||||
requests==2.21.0
|
# This file is autogenerated by pip-compile with Python 3.7
|
||||||
PyYAML==5.3.1
|
# by the following command:
|
||||||
scapy==2.4.3
|
#
|
||||||
gym==0.14.0
|
# pip-compile --output-file=requirements.txt --pip-args='--retries 50' --resolver=backtracking --strip-extras requirements.in
|
||||||
scipy==1.3.1
|
#
|
||||||
stable-baselines==2.7.0
|
--index-url https://nexus.chadwaltercummings.me/repository/www.piwheels.org/simple
|
||||||
tensorflow==1.13.1
|
--extra-index-url https://nexus.chadwaltercummings.me/repository/pypi.org/simple
|
||||||
tensorflow-estimator==1.14.0
|
|
||||||
tweepy==3.7.0
|
absl-py==2.1.0
|
||||||
|
# via
|
||||||
|
# tensorboard
|
||||||
|
# tensorflow
|
||||||
|
astor==0.8.1
|
||||||
|
# via tensorflow
|
||||||
|
atari-py==0.2.6
|
||||||
|
# via gym
|
||||||
|
certifi==2024.2.2
|
||||||
|
# via requests
|
||||||
|
charset-normalizer==3.3.2
|
||||||
|
# via requests
|
||||||
|
click==7.1.2
|
||||||
|
# via flask
|
||||||
|
cloudpickle==1.6.0
|
||||||
|
# via
|
||||||
|
# gym
|
||||||
|
# stable-baselines
|
||||||
|
cycler==0.11.0
|
||||||
|
# via matplotlib
|
||||||
|
dbus-python==1.3.2
|
||||||
|
# via -r requirements.in
|
||||||
file-read-backwards==2.0.0
|
file-read-backwards==2.0.0
|
||||||
numpy==1.20.2
|
# via -r requirements.in
|
||||||
inky==1.2.0
|
flask==1.1.4
|
||||||
smbus2==0.3.0
|
# via
|
||||||
Pillow==6.2.0
|
# -r requirements.in
|
||||||
spidev==3.4
|
# flask-cors
|
||||||
gast==0.2.2
|
# flask-wtf
|
||||||
flask==2.0.1
|
flask-cors==3.0.10
|
||||||
flask-cors==3.0.7
|
# via -r requirements.in
|
||||||
flask-wtf==0.14.3
|
flask-wtf==1.1.1
|
||||||
dbus-python==1.2.12
|
# via -r requirements.in
|
||||||
toml==0.10.0
|
fonttools==4.38.0
|
||||||
python-dateutil==2.8.1
|
# via matplotlib
|
||||||
|
gast==0.5.4
|
||||||
|
# via tensorflow
|
||||||
|
google-pasta==0.2.0
|
||||||
|
# via tensorflow
|
||||||
|
grpcio==1.62.2
|
||||||
|
# via
|
||||||
|
# tensorboard
|
||||||
|
# tensorflow
|
||||||
|
gym==0.19.0
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# stable-baselines
|
||||||
|
h5py==3.8.0
|
||||||
|
# via keras-applications
|
||||||
|
idna==3.7
|
||||||
|
# via requests
|
||||||
|
importlib-metadata==6.7.0
|
||||||
|
# via
|
||||||
|
# gym
|
||||||
|
# markdown
|
||||||
|
inky==1.5.0
|
||||||
|
# via -r requirements.in
|
||||||
|
itsdangerous==1.1.0
|
||||||
|
# via
|
||||||
|
# flask
|
||||||
|
# flask-wtf
|
||||||
|
jinja2==2.11.3
|
||||||
|
# via flask
|
||||||
|
joblib==1.3.2
|
||||||
|
# via stable-baselines
|
||||||
|
keras-applications==1.0.8
|
||||||
|
# via tensorflow
|
||||||
|
keras-preprocessing==1.1.2
|
||||||
|
# via tensorflow
|
||||||
|
kiwisolver==1.4.5
|
||||||
|
# via matplotlib
|
||||||
|
markdown==3.4.4
|
||||||
|
# via tensorboard
|
||||||
|
markupsafe==2.0.1
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# jinja2
|
||||||
|
# wtforms
|
||||||
|
matplotlib==3.5.3
|
||||||
|
# via stable-baselines
|
||||||
|
numpy==1.21.4
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# atari-py
|
||||||
|
# gym
|
||||||
|
# h5py
|
||||||
|
# inky
|
||||||
|
# keras-applications
|
||||||
|
# keras-preprocessing
|
||||||
|
# matplotlib
|
||||||
|
# opencv-python
|
||||||
|
# pandas
|
||||||
|
# scipy
|
||||||
|
# stable-baselines
|
||||||
|
# tensorboard
|
||||||
|
# tensorflow
|
||||||
|
opencv-python==4.7.0.72
|
||||||
|
# via
|
||||||
|
# gym
|
||||||
|
# stable-baselines
|
||||||
|
packaging==24.0
|
||||||
|
# via matplotlib
|
||||||
|
pandas==1.3.5
|
||||||
|
# via stable-baselines
|
||||||
|
pillow==9.5.0
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# matplotlib
|
||||||
|
protobuf==3.20.3
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# tensorboard
|
||||||
|
# tensorflow
|
||||||
|
pycryptodome==3.20.0
|
||||||
|
# via -r requirements.in
|
||||||
|
pyglet==2.0.10
|
||||||
|
# via gym
|
||||||
|
pyparsing==3.1.2
|
||||||
|
# via matplotlib
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# matplotlib
|
||||||
|
# pandas
|
||||||
|
pytz==2024.1
|
||||||
|
# via pandas
|
||||||
|
pyyaml==6.0.1
|
||||||
|
# via -r requirements.in
|
||||||
|
requests==2.31.0
|
||||||
|
# via -r requirements.in
|
||||||
|
scapy==2.5.0
|
||||||
|
# via -r requirements.in
|
||||||
|
scipy==1.7.3
|
||||||
|
# via stable-baselines
|
||||||
|
six==1.16.0
|
||||||
|
# via
|
||||||
|
# atari-py
|
||||||
|
# flask-cors
|
||||||
|
# google-pasta
|
||||||
|
# keras-preprocessing
|
||||||
|
# python-dateutil
|
||||||
|
# tensorboard
|
||||||
|
# tensorflow
|
||||||
|
smbus2==0.4.3
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# inky
|
||||||
|
spidev==3.6
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# inky
|
||||||
|
stable-baselines==2.10.2
|
||||||
|
# via -r requirements.in
|
||||||
|
tensorboard==1.13.1
|
||||||
|
# via tensorflow
|
||||||
|
tensorflow==1.13.1
|
||||||
|
# via -r requirements.in
|
||||||
|
tensorflow-estimator==1.14.0
|
||||||
|
# via tensorflow
|
||||||
|
termcolor==2.3.0
|
||||||
|
# via tensorflow
|
||||||
|
toml==0.10.2
|
||||||
|
# via -r requirements.in
|
||||||
|
typing-extensions==4.7.1
|
||||||
|
# via
|
||||||
|
# importlib-metadata
|
||||||
|
# kiwisolver
|
||||||
|
urllib3==2.0.7
|
||||||
|
# via requests
|
||||||
websockets==8.1
|
websockets==8.1
|
||||||
RPi.GPIO
|
# via -r requirements.in
|
||||||
Werkzeug==2.0.0
|
werkzeug==1.0.1
|
||||||
jinja2==3.0.3
|
# via
|
||||||
|
# flask
|
||||||
|
# tensorboard
|
||||||
|
wheel==0.42.0
|
||||||
|
# via
|
||||||
|
# tensorboard
|
||||||
|
# tensorflow
|
||||||
|
wrapt==1.16.0
|
||||||
|
# via tensorflow
|
||||||
|
wtforms==3.0.1
|
||||||
|
# via flask-wtf
|
||||||
|
zipp==3.15.0
|
||||||
|
# via importlib-metadata
|
||||||
|
@ -101,6 +101,10 @@ def main():
|
|||||||
main:
|
main:
|
||||||
lang: {lang}
|
lang: {lang}
|
||||||
ui:
|
ui:
|
||||||
|
font:
|
||||||
|
name: 'DejaVuSansMono'
|
||||||
|
size_offset: 0
|
||||||
|
size: 0
|
||||||
fps: 0.3
|
fps: 0.3
|
||||||
display:
|
display:
|
||||||
enabled: false
|
enabled: false
|
||||||
@ -110,9 +114,8 @@ def main():
|
|||||||
type: {display}
|
type: {display}
|
||||||
web:
|
web:
|
||||||
enabled: true
|
enabled: true
|
||||||
address: "0.0.0.0"
|
address: '::'
|
||||||
port: 8080
|
port: 8080
|
||||||
|
|
||||||
faces:
|
faces:
|
||||||
look_r: '( ⚆_⚆)'
|
look_r: '( ⚆_⚆)'
|
||||||
look_l: '(☉_☉ )'
|
look_l: '(☉_☉ )'
|
||||||
|
82
setup.py
82
setup.py
@ -1,29 +1,31 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
from distutils.util import strtobool
|
from setuptools.command.install import install
|
||||||
import os
|
|
||||||
import glob
|
import glob
|
||||||
import shutil
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
def install_file(source_filename, dest_filename):
|
def install_file(source_filename, dest_filename):
|
||||||
# do not overwrite network configuration if it exists already
|
# do not overwrite network configuration if it exists already
|
||||||
# https://github.com/evilsocket/pwnagotchi/issues/483
|
# https://github.com/evilsocket/pwnagotchi/issues/483
|
||||||
if dest_filename.startswith('/etc/network/interfaces.d/') and os.path.exists(dest_filename):
|
if dest_filename.startswith('/etc/network/interfaces.d/') and os.path.exists(dest_filename):
|
||||||
print("%s exists, skipping ..." % dest_filename)
|
log.info(f"{dest_filename} exists, skipping ...")
|
||||||
return
|
return
|
||||||
|
|
||||||
print("installing %s to %s ..." % (source_filename, dest_filename))
|
log.info(f"installing {source_filename} to {dest_filename} ...")
|
||||||
try:
|
dest_folder = os.path.dirname(dest_filename)
|
||||||
dest_folder = os.path.dirname(dest_filename)
|
if not os.path.isdir(dest_folder):
|
||||||
if not os.path.isdir(dest_folder):
|
os.makedirs(dest_folder)
|
||||||
os.makedirs(dest_folder)
|
|
||||||
|
|
||||||
shutil.copyfile(source_filename, dest_filename)
|
shutil.copyfile(source_filename, dest_filename)
|
||||||
except Exception as e:
|
if dest_filename.startswith("/usr/bin/"):
|
||||||
print("error installing %s: %s" % (source_filename, e))
|
os.chmod(dest_filename, 0o755)
|
||||||
|
|
||||||
|
|
||||||
def install_system_files():
|
def install_system_files():
|
||||||
@ -35,31 +37,54 @@ def install_system_files():
|
|||||||
dest_filename = source_filename.replace(data_path, '')
|
dest_filename = source_filename.replace(data_path, '')
|
||||||
install_file(source_filename, dest_filename)
|
install_file(source_filename, dest_filename)
|
||||||
|
|
||||||
|
|
||||||
|
def restart_services():
|
||||||
# reload systemd units
|
# reload systemd units
|
||||||
os.system("systemctl daemon-reload")
|
os.system("systemctl daemon-reload")
|
||||||
|
|
||||||
|
|
||||||
def installer():
|
|
||||||
install_system_files()
|
|
||||||
# for people updating https://github.com/evilsocket/pwnagotchi/pull/551/files
|
# for people updating https://github.com/evilsocket/pwnagotchi/pull/551/files
|
||||||
os.system("systemctl enable fstrim.timer")
|
os.system("systemctl enable fstrim.timer")
|
||||||
|
|
||||||
def version(version_file):
|
|
||||||
with open(version_file, 'rt') as vf:
|
|
||||||
version_file_content = vf.read()
|
|
||||||
|
|
||||||
version_match = re.search(r"__version__\s*=\s*[\"\']([^\"\']+)", version_file_content)
|
class CustomInstall(install):
|
||||||
if version_match:
|
def run(self):
|
||||||
return version_match.groups()[0]
|
super().run()
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
warnings.warn(
|
||||||
|
"Not running as root, can't install pwnagotchi system files!"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
install_system_files()
|
||||||
|
restart_services()
|
||||||
|
|
||||||
|
|
||||||
|
def version(version_file):
|
||||||
|
#with open(version_file, 'rt') as vf:
|
||||||
|
#version_file_content = vf.read()
|
||||||
|
|
||||||
|
#version_match = re.search(r"__version__\s*=\s*[\"\']([^\"\']+)", version_file_content)
|
||||||
|
#if version_match:
|
||||||
|
#return version_match.groups()[0]
|
||||||
|
|
||||||
|
if "PWN_VERSION" in os.environ:
|
||||||
|
return os.environ["PWN_VERSION"]
|
||||||
|
else:
|
||||||
|
with open(version_file, 'rt') as vf:
|
||||||
|
version_file_content = vf.read()
|
||||||
|
|
||||||
|
version_match = re.search(r"__version__\s*=\s*[\"\']([^\"\']+)", version_file_content)
|
||||||
|
|
||||||
|
if version_match:
|
||||||
|
return version_match.groups()[0]
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
if strtobool(os.environ.get("PWNAGOTCHI_ENABLE_INSTALLER", "1")):
|
|
||||||
installer()
|
|
||||||
|
|
||||||
with open('requirements.txt') as fp:
|
with open('requirements.txt') as fp:
|
||||||
required = [line.strip() for line in fp if line.strip() != ""]
|
required = [
|
||||||
|
line.strip()
|
||||||
|
for line in fp
|
||||||
|
if line.strip() and not line.startswith("--")
|
||||||
|
]
|
||||||
|
|
||||||
VERSION_FILE = 'pwnagotchi/_version.py'
|
VERSION_FILE = 'pwnagotchi/_version.py'
|
||||||
pwnagotchi_version = version(VERSION_FILE)
|
pwnagotchi_version = version(VERSION_FILE)
|
||||||
@ -72,8 +97,11 @@ setup(name='pwnagotchi',
|
|||||||
url='https://pwnagotchi.ai/',
|
url='https://pwnagotchi.ai/',
|
||||||
license='GPL',
|
license='GPL',
|
||||||
install_requires=required,
|
install_requires=required,
|
||||||
|
cmdclass={
|
||||||
|
"install": CustomInstall,
|
||||||
|
},
|
||||||
scripts=['bin/pwnagotchi'],
|
scripts=['bin/pwnagotchi'],
|
||||||
package_data={'pwnagotchi': ['defaults.yml', 'pwnagotchi/defaults.yml', 'locale/*/LC_MESSAGES/*.mo']},
|
package_data={'pwnagotchi': ['defaults.toml', 'pwnagotchi/defaults.toml', 'locale/*/LC_MESSAGES/*.mo']},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
Loading…
x
Reference in New Issue
Block a user