diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index fec25bfd..fbb3c19c 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -8,205 +8,65 @@ on:
required: true
jobs:
-
- publish:
+ build:
runs-on: ubuntu-latest
+
steps:
+ - uses: actions/checkout@v4
- - name: Remove unnecessary directories
- run: |
- sudo rm -rf /usr/share/dotnet
- sudo rm -rf /opt/ghc
- sudo rm -rf /usr/local/share/boost
- sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+ - name: Extract version from file
+ id: get_version
+ run: |
+ VERSION=$(cut -d "'" -f2 < pwnagotchi/_version.py)
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
- - name: Check disk space
- run: df -BG
+ - name: Get latest tag
+ uses: actions-ecosystem/action-get-latest-tag@v1
+ id: get-latest-tag
- - name: Checkout code
- uses: actions/checkout@v4
+ - name: Set LAST_VERSION as an environment variable
+ run: echo "LAST_VERSION=${{ steps.get-latest-tag.outputs.tag }}" >> $GITHUB_ENV
- - name: Validate tag
- id: tag-setter
- run: |
- TAG=${{ github.event.inputs.version }}
- if [[ $TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
- echo "Tag $TAG is valid."
- echo "TAG=$TAG" >> $GITHUB_OUTPUT
- else
- echo "Tag $TAG is not a valid semantic version. Aborting."
- exit 1
- fi
+ - name: Generate release notes
+ id: generate_release_notes
+ run: |
+ COMMITS=$(git log --merges --pretty=format:"* %s" $LAST_VERSION--$VERSION | sed 's/$/\\n/g')
+ CONTRIBUTORS=$(git shortlog -sn $LAST_VERSION--$VERSION | awk '{print "* @" $2}' | sed 's/$/\\n/g')
+ RELEASE_BODY="**Full Changelog**: https://github.com/jayofelony/pwnagotchi/compare/$LAST_VERSION...$VERSION"
+ echo "RELEASE_BODY=$RELEASE_BODY" >> $GITHUB_ENV
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: 3.9
+ - name: Install qemu dependencies
+ run: sudo apt update && sudo apt install qemu-user-static qemu-utils xz-utils -y
- - name: Install dependencies
- run: |
- sudo apt-get update && sudo apt-get install -y libdbus-1-dev curl unzip gettext qemu-utils qemu qemu-user-static binfmt-support
- pip install -r requirements.txt
+ - name: Build img file
+ run: ls -la .; pwd; make all
- - name: Update QEMU
- run: |
- sudo update-binfmts --enable qemu-aarch64
- echo $(ls /usr/bin/qemu-aarch64-static)
+ - name: Transfer 32bit.img to docker and give permissions
+ run: sudo chown runner:docker "pwnagotchi-32bit.img"
- - name: Restart binfmt-support
- run: sudo service binfmt-support restart
+ - name: Transfer 64bit.img to docker and give permissions
+ run: sudo chown runner:docker "pwnagotchi-64bit.img"
- - name: Mount binfmt_misc
- run: |
- if ! grep -qs '/proc/sys/fs/binfmt_misc ' /proc/mounts; then
- echo "Mounting binfmt_misc"
- sudo mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
- fi
+ - name: PiShrink
+ run: |
+ wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
+ chmod +x pishrink.sh
+ sudo mv pishrink.sh /usr/local/bin
+ find /home/runner/work/ -type f -name "*.img" -exec sudo pishrink.sh -aZ {} \;
- - name: Restart binfmt-support
- run: sudo service binfmt-support restart
-
- - name: Update Languages
- run: make update_langs
-
- - name: Compile Languages
- run: make compile_langs
+ - name: Change name of 32.img.xz to add version
+ run: mv "pwnagotchi-32bit.img.xz" "pwnagotchi-$VERSION-32bit.img.xz"
- - name: Check disk space
- run: df -BG
+ - name: Change name of 64.img.xz to add version
+ run: mv "pwnagotchi-64bit.img.xz" "pwnagotchi-$VERSION-64bit.img.xz"
- - name: Check qemu-user-static package
- run: |
- echo "Checking qemu-user-static package..."
- dpkg -s qemu-user-static && echo "qemu-user-static is installed." || echo "qemu-user-static is NOT installed."
-
- - name: Check binfmt-support service
- run: |
- echo "Checking binfmt-support service..."
- service binfmt-support status && echo "binfmt-support service is running." || echo "binfmt-support service is NOT running."
-
- - name: Check binfmt_misc filesystem
- run: |
- echo "Checking binfmt_misc filesystem..."
- mount | grep binfmt_misc && echo "binfmt_misc is mounted." || echo "binfmt_misc is NOT mounted."
- echo $(ls /proc/sys/fs/binfmt_misc | grep qemu-aarch64)
-
- - name: Run Makefile
- run: make
- env:
- PWN_VERSION: ${{ steps.tag-setter.outputs.TAG }}
-
- - name: PiShrink
- run: |
- wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
- chmod +x pishrink.sh
- sudo mv pishrink.sh /usr/local/bin
- find /home/runner/work/ -type f -name "*.img" -exec sudo pishrink.sh {} \;
-
- - name: Compress .img files
- run: |
- find /home/runner/work/ -type f -name "*.img" -exec xz --no-warn {} \;
-
- - name: Create tag
- uses: actions/github-script@v7
- with:
- script: |
- const version = "${{ steps.tag-setter.outputs.TAG }}"
- console.log(`Creating tag ${version}`)
- await github.rest.git.createRef({
- owner: context.repo.owner,
- repo: context.repo.repo,
- ref: `refs/tags/${version}`,
- sha: context.sha
- })
-
- - name: Create Release
- id: create_release
- uses: actions/github-script@v7
- with:
- script: |
- const tag = "${{ steps.tag-setter.outputs.TAG }}"
- console.log(`Creating release with tag: ${tag}`)
- const release = await github.rest.repos.createRelease({
- owner: context.repo.owner,
- repo: context.repo.repo,
- tag_name: tag,
- name: tag,
- draft: false,
- prerelease: true,
- generate_release_notes: true
- })
- console.log(`Created release with id: ${release.data.id}`)
- return release.data.id
-
- - name: Upload Release Asset
- id: upload-release-asset
- uses: actions/github-script@v7
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- script: |
- const fs = require('fs');
- const path = require('path');
- const release_id = "${{ steps.create_release.outputs.result }}";
- const asset_content_type = 'application/octet-stream';
- const distDir = '/home/runner/work/';
-
- const uploadFile = async (filePath) => {
- if (fs.lstatSync(filePath).isDirectory()) {
- const files = fs.readdirSync(filePath);
- for (const file of files) {
- await uploadFile(path.join(filePath, file));
- }
- } else {
- // Check if the file has a .xz extension
- if (path.extname(filePath) === '.xz') {
- console.log(`Uploading ${filePath}...`);
-
- const asset_name = path.basename(filePath);
- const asset_size = fs.statSync(filePath).size;
- const asset = fs.createReadStream(filePath);
-
- const response = await github.rest.repos.uploadReleaseAsset({
- owner: context.repo.owner,
- repo: context.repo.repo,
- release_id: release_id,
- name: asset_name,
- data: asset,
- headers: {
- 'content-type': asset_content_type,
- 'content-length': asset_size
- }
- });
-
- console.log(`Uploaded ${filePath}: ${response.data.browser_download_url}`);
- }
- }
- }
-
- await uploadFile(distDir);
-
- - name: Update Release
- uses: actions/github-script@v7
- with:
- script: |
- const release_id = "${{ steps.create_release.outputs.result }}"
- console.log(`Updating release with id: ${release_id}`)
- github.rest.repos.updateRelease({
- owner: context.repo.owner,
- repo: context.repo.repo,
- release_id: release_id,
- tag_name: "${{ steps.tag-setter.outputs.TAG }}",
- name: "${{ steps.tag-setter.outputs.TAG }}",
- draft: false,
- prerelease: false
- })
-
- - name: Save environment variable
- run: echo "${{ steps.tag-setter.outputs.TAG }}" > env_var.txt
-
- - name: Upload artifact
- uses: actions/upload-artifact@v4
- with:
- name: env-var
- path: env_var.txt
\ No newline at end of file
+ - name: Release
+ uses: softprops/action-gh-release@v1
+ with:
+ prerelease: true
+ tag_name: v${{ env.VERSION }}
+ name: Pwnagotchi v${{ env.VERSION }}
+ files: |
+ pwnagotchi-${{ env.VERSION }}-32bit.img.xz
+ pwnagotchi-${{ env.VERSION }}-64bit.img.xz
+ body: ${{ env.RELEASE_BODY }}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 898da0ea..668b4fa4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
-PACKER_VERSION := 1.10.0
+PACKER_VERSION := 1.10.1
PWN_HOSTNAME := pwnagotchi
-PWN_VERSION := ${PWN_VERSION}
+PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py)
MACHINE_TYPE := $(shell uname -m)
ifneq (,$(filter x86_64,$(MACHINE_TYPE)))
@@ -25,7 +25,8 @@ ifneq (,$(UNSHARE))
UNSHARE := $(UNSHARE) --uts
endif
-all: clean image clean
+# sudo apt-get install qemu-user-static qemu-utils
+all: clean packer image
update_langs:
@for lang in pwnagotchi/locale/*/; do\
@@ -39,30 +40,24 @@ compile_langs:
./scripts/language.sh compile $$(basename $$lang); \
done
-PACKER := ~/pwnagotchi/packer
-PACKER_URL := https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_$(GOARCH).zip
-$(PACKER):
- mkdir -p $(@D)
- curl -L "$(PACKER_URL)" -o $(PACKER).zip
- unzip $(PACKER).zip -d $(@D)
- rm $(PACKER).zip
- chmod +x $@
+packer: clean
+ curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
+ unzip /tmp/packer.zip -d /tmp
+ sudo mv /tmp/packer /usr/bin/packer
-SDIST := dist/pwnagotchi-$(PWN_VERSION).tar.gz
-$(SDIST): setup.py pwnagotchi
- python3 setup.py sdist
+image: clean packer
+ export LC_ALL=en_GB.UTF-8
+ cd builder && sudo /usr/bin/packer init combined.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" combined.json.pkr.hcl
-# Building the image requires packer, but don't rebuild the image just because packer updated.
-pwnagotchi: | $(PACKER)
+bullseye: clean packer
+ export LC_ALL=en_GB.UTF-8
+ cd builder && sudo /usr/bin/packer init data/32bit/raspberrypi32.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/32bit/raspberrypi32.json.pkr.hcl
+ sudo pishrink -vaZ pwnagotchi-32bit.img
-# If the packer or ansible files are updated, rebuild the image.
-pwnagotchi: $(SDIST) builder/pwnagotchi.json.pkr.hcl builder/raspberrypi64.yml $(shell find builder/data -type f)
-
- cd builder && $(PACKER) init pwnagotchi.json.pkr.hcl && sudo $(UNSHARE) $(PACKER) build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json.pkr.hcl
-
-.PHONY: image
-image: pwnagotchi
+bookworm: clean packer
+ export LC_ALL=en_GB.UTF-8
+ cd builder && sudo /usr/bin/packer init data/64bit/raspberrypi64.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/64bit/raspberrypi64.json.pkr.hcl
+ sudo pishrink -vaZ pwnagotchi-64bit.img
clean:
- - rm -rf dist pwnagotchi.egg-info
- - rm -f $(PACKER)
\ No newline at end of file
+ - rm -rf /tmp/packer*
diff --git a/README-google.md b/README-google.md
index 7a942f3b..e9b75b94 100644
--- a/README-google.md
+++ b/README-google.md
@@ -11,7 +11,7 @@ Select ‘Credentials’ from the left menu, click ‘Create Credentials’, sel
Now, the product name and consent screen need to be set -> click ‘Configure consent screen’ and follow the instructions. Once finished:
-Select ‘Application type’ to be Web application.
+Select ‘Application type’ to be Desktop application.
Enter an appropriate name.
@@ -19,7 +19,7 @@ Input http://localhost/ for ‘Authorized redirect URIs’.
Select the correct oauth scope:
- - drive.file
+ - drive
- drive.install
Click ‘Create’.
diff --git a/README.md b/README.md
index aa005160..ceaa1a67 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,7 @@
-# Pwnagotchi-Torch
-
-**This fork of [Pwnagotchi](https://www.pwnagotchi.ai) is only for 64-bit Raspberry Pi's. Such as the 02W, 3(b+) and 4(b) and the new Raspberry Pi 5!!.**
-
-The RPi5 can only be used headless currently. (without display.)
-
-
-If you are using an older 32-bit version Raspberry Pi, ZeroWH, use this [fork](https://github.com/jayofelony/pwnagotchi-torch/releases/tag/v2.6.4) and make sure you download the `armhf` version.
-
----
-Download the latest image file [here](https://github.com/jayofelony/pwnagotchi-bookworm/releases/tag/v2.7.9), and let it auto-update from here on out.
-
-**Use RPi imager to flash, please don't flash a new user as this will mess with logs created.**
- - Select `Use Custom Image`
- - Browse for the downloaded image file
- - Select No under `Use OS Customization`
-
-SSH credentials are `pi/raspberry`.
-
-# Donations:
-I would like to thank
-- [findingmoist](https://github.com/findingmoist)
-- [kr4k0n](https://github.com/kr4k0n)
-
-for donating!
-
-[Pwnagotchi-Torch](https://www.patreon.com/pwnagotchi_torch)
+# Pwnagotchi
+This is the main source for all forks:
+- RPiZeroW (32bit)
+- RPiZero2W, RPi3, RPi4, RPi5 (64bit)
[GH Sponsor](https://github.com/sponsors/jayofelony)
diff --git a/bin/pwnagotchi b/bin/pwnagotchi
index 81f2f0f2..df5a8724 100755
--- a/bin/pwnagotchi
+++ b/bin/pwnagotchi
@@ -7,6 +7,7 @@ import sys
import toml
import requests
import os
+import re
import pwnagotchi
from pwnagotchi import utils
@@ -50,7 +51,6 @@ def pwnagotchi_cli():
agent.mode = 'auto'
agent.start()
- config = agent.config()
while True:
try:
@@ -60,7 +60,7 @@ def pwnagotchi_cli():
channels = agent.get_access_points_by_channel()
# for each channel
for ch, aps in channels:
- time.sleep(0.2) # This is to make sure it doesn't error (https://github.com/seemoo-lab/nexmon/issues/596)
+ time.sleep(0.2)
agent.set_channel(ch)
if not agent.is_stale() and agent.any_activity():
@@ -68,9 +68,6 @@ def pwnagotchi_cli():
# for each ap on this channel
for ap in aps:
- if ap['mac'][:13].lower in config['main']['whitelist'] or ap['hostname'] in config['main']['whitelist']:
- logging.info(f"Found your MAC address {ap['mac']} - {config['main']['whitelist']}")
- continue
# send an association frame in order to get for a PMKID
agent.associate(ap)
# deauth all client stations in order to get a full handshake
@@ -92,10 +89,8 @@ def pwnagotchi_cli():
except Exception as e:
if str(e).find("wifi.interface not set") > 0:
- logging.exception(
- "main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e)
- logging.info(
- "sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger")
+ logging.exception("main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e)
+ logging.info("sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger")
time.sleep(60)
agent.next_epoch()
else:
@@ -137,6 +132,8 @@ def pwnagotchi_cli():
help="Print the configuration.")
# Jayofelony added these
+ parser.add_argument('--wizard', dest="wizard", action="store_true", default=False,
+ help="Interactive installation of your personal configuration.")
parser.add_argument('--check-update', dest="check_update", action="store_true", default=False,
help="Check for updates on Pwnagotchi. And tells current version.")
parser.add_argument('--donate', dest="donate", action="store_true", default=False,
@@ -161,30 +158,137 @@ def pwnagotchi_cli():
print(pwnagotchi.__version__)
sys.exit(0)
+ if args.wizard:
+ def is_valid_hostname(hostname):
+ if len(hostname) > 255:
+ return False
+ if hostname[-1] == ".":
+ hostname = hostname[:-1] # strip exactly one dot from the right, if present
+ allowed = re.compile("(?!-)[A-Z\d-]{1,63}(? 0:
+ f.write("main.whitelist = [\n")
+ for x in range(int(pwn_whitelist)):
+ ssid = input("SSID (Name): ")
+ bssid = input("BSSID (MAC): ")
+ f.write(f"\t\"{ssid}\",\n")
+ f.write(f"\t\"{bssid}\",\n")
+ f.write("]\n")
+ # set bluetooth tether
+ pwn_bluetooth = input("Do you want to enable BT-Tether?\n\n"
+ "[Y/N] ")
+ if pwn_bluetooth.lower() in ('y', 'yes'):
+ f.write("main.plugins.bt-tether.enabled = true\n\n")
+ pwn_bluetooth_device = input("What device do you use? Android or iOS?\n\n"
+ "Device: ")
+ if pwn_bluetooth_device.lower() == "android":
+ f.write("main.plugins.bt-tether.devices.android-phone.enabled = true\n")
+ pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
+ "MAC: ")
+ if pwn_bluetooth_mac != "":
+ f.write(f"main.plugins.bt-tether.devices.android-phone.mac = \"{pwn_bluetooth_mac}\"\n")
+ elif pwn_bluetooth_device.lower() == "ios":
+ f.write("main.plugins.bt-tether.devices.ios-phone.enabled = true\n")
+ pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
+ "MAC: ")
+ if pwn_bluetooth_mac != "":
+ f.write(f"main.plugins.bt-tether.devices.ios-phone.mac = \"{pwn_bluetooth_mac}\"\n")
+ # set up display settings
+ pwn_display_enabled = input("Do you want to enable a display?\n\n"
+ "[Y/N]: ")
+ if pwn_display_enabled.lower() in ('y', 'yes'):
+ f.write("ui.display.enabled = true\n")
+ pwn_display_type = input("What display do you use?\n\n"
+ "Be sure to check for the correct display type @ \n"
+ "https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L431\n\n"
+ "Display type: ")
+ if pwn_display_type != "":
+ f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
+ pwn_display_invert = input("Do you want to invert the display colors?\n"
+ "N = Black background\n"
+ "Y = White background\n\n"
+ "[Y/N]: ")
+ if pwn_display_invert.lower() in ('y', 'yes'):
+ f.write("ui.invert = true\n")
+ f.close()
+ if pwn_bluetooth.lower() in ('y', 'yes'):
+ if pwn_bluetooth_device.lower == "android":
+ print("To visit the webui when connected with your phone, visit: http://192.168.44.44:8080\n"
+ "Your configuration is done, and I will restart in 5 seconds.")
+ elif pwn_bluetooth_device.lower == "ios":
+ print("To visit the webui when connected with your phone, visit: http://172.20.10.6:8080\n"
+ "Your configuration is done, and I will restart in 5 seconds.")
+ else:
+ print("Your configuration is done, and I will restart in 5 seconds.")
+ time.sleep(5)
+ os.system("service pwnagotchi restart")
+ else:
+ print("Ok, doing nothing.")
+ sys.exit(0)
+
if args.donate:
- print("Donations can made @ https://github.com/sponsors/jayofelony \n\nBut only if you really want to!")
+ print("Donations can made @ \n "
+ "https://www.patreon.com/pwnagotchi_torch \n "
+ "https://github.com/sponsors/jayofelony \n\n"
+ "But only if you really want to!")
sys.exit(0)
if args.check_update:
- resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi-bookworm/releases/latest")
+ resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi/releases/latest")
latest = resp.json()
latest_ver = latest['tag_name'].replace('v', '')
local = version_to_tuple(pwnagotchi.__version__)
remote = version_to_tuple(latest_ver)
if remote > local:
- user_input = input("There is a new version available! Update from v%s to v%s?\n[y(es)/n(o)]"
+ user_input = input("There is a new version available! Update from v%s to v%s?\n[Y/N] "
% (pwnagotchi.__version__, latest_ver))
# input validation
if user_input.lower() in ('y', 'yes'):
if os.path.exists('/root/.auto-update'):
os.system("rm /root/.auto-update && systemctl restart pwnagotchi")
- os.system("systemctl restart pwnagotchi")
+ else:
+ logging.error("You should make sure auto-update is enabled!")
print("Okay, give me a couple minutes. Just watch pwnlog while you wait.")
- elif user_input.lower() in ('n', 'no'):
+ elif user_input.lower() in ('n', 'no'): # using this elif for readability
print("Okay, guess not!")
- else:
- print("Invalid input.")
else:
print("You are currently on the latest release, v%s." % pwnagotchi.__version__)
sys.exit(0)
@@ -230,6 +334,5 @@ def pwnagotchi_cli():
else:
do_auto_mode(agent)
-
if __name__ == '__main__':
- pwnagotchi_cli()
+ pwnagotchi_cli()
\ No newline at end of file
diff --git a/builder/combined.json.pkr.hcl b/builder/combined.json.pkr.hcl
new file mode 100644
index 00000000..c69f0bc9
--- /dev/null
+++ b/builder/combined.json.pkr.hcl
@@ -0,0 +1,173 @@
+packer {
+ required_plugins {
+ arm = {
+ version = "1.0.0"
+ source = "github.com/cdecoux/builder-arm"
+ }
+ ansible = {
+ source = "github.com/hashicorp/ansible"
+ version = ">= 1.1.1"
+ }
+ }
+}
+
+variable "pwn_hostname" {
+ type = string
+}
+
+variable "pwn_version" {
+ type = string
+}
+
+source "arm" "rpi64-pwnagotchi" {
+ file_checksum_url = "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz.sha256"
+ file_urls = ["https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz"]
+ file_checksum_type = "sha256"
+ file_target_extension = "xz"
+ file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
+ image_path = "../pwnagotchi-64bit.img"
+ qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
+ qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
+ image_build_method = "resize"
+ image_size = "9G"
+ image_type = "dos"
+ image_partitions {
+ name = "boot"
+ type = "c"
+ start_sector = "8192"
+ filesystem = "fat"
+ size = "256M"
+ mountpoint = "/boot/firmware"
+ }
+ image_partitions {
+ name = "root"
+ type = "83"
+ start_sector = "532480"
+ filesystem = "ext4"
+ size = "0"
+ mountpoint = "/"
+ }
+}
+
+source "arm" "rpi32-pwnagotchi" {
+ file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz.sha256"
+ file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz"]
+ file_checksum_type = "sha256"
+ file_target_extension = "xz"
+ file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
+ image_path = "../pwnagotchi-32bit.img"
+ qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
+ qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
+ image_build_method = "resize"
+ image_size = "9G"
+ image_type = "dos"
+ image_partitions {
+ name = "boot"
+ type = "c"
+ start_sector = "8192"
+ filesystem = "fat"
+ size = "256M"
+ mountpoint = "/boot"
+ }
+ image_partitions {
+ name = "root"
+ type = "83"
+ start_sector = "532480"
+ filesystem = "ext4"
+ size = "0"
+ mountpoint = "/"
+ }
+}
+
+# a build block invokes sources and runs provisioning steps on them. The
+# documentation for build blocks can be found here:
+# https://www.packer.io/docs/from-1.5/blocks/build
+build {
+ name = "Raspberry Pi 64 Pwnagotchi"
+ sources = ["source.arm.rpi64-pwnagotchi"]
+
+ provisioner "file" {
+ destination = "/usr/bin/"
+ sources = [
+ "data/64bit/usr/bin/bettercap-launcher",
+ "data/64bit/usr/bin/hdmioff",
+ "data/64bit/usr/bin/hdmion",
+ "data/64bit/usr/bin/monstart",
+ "data/64bit/usr/bin/monstop",
+ "data/64bit/usr/bin/pwnagotchi-launcher",
+ "data/64bit/usr/bin/pwnlib",
+ ]
+ }
+ provisioner "shell" {
+ inline = ["chmod +x /usr/bin/*"]
+ }
+
+ provisioner "file" {
+ destination = "/etc/systemd/system/"
+ sources = [
+ "data/64bit/etc/systemd/system/bettercap.service",
+ "data/64bit/etc/systemd/system/pwnagotchi.service",
+ "data/64bit/etc/systemd/system/pwngrid-peer.service",
+ ]
+ }
+ provisioner "file" {
+ destination = "/etc/update-motd.d/01-motd"
+ source = "data/64bit/etc/update-motd.d/01-motd"
+ }
+ provisioner "shell" {
+ inline = ["chmod +x /etc/update-motd.d/*"]
+ }
+ provisioner "shell" {
+ inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
+ }
+ provisioner "ansible-local" {
+ command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
+ extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
+ playbook_file = "data/64bit/raspberrypi64.yml"
+ }
+}
+
+build {
+ name = "Raspberry Pi 32 Pwnagotchi"
+ sources = ["source.arm.rpi32-pwnagotchi"]
+ provisioner "file" {
+ destination = "/usr/bin/"
+ sources = [
+ "data/32bit/usr/bin/bettercap-launcher",
+ "data/32bit/usr/bin/hdmioff",
+ "data/32bit/usr/bin/hdmion",
+ "data/32bit/usr/bin/monstart",
+ "data/32bit/usr/bin/monstop",
+ "data/32bit/usr/bin/pwnagotchi-launcher",
+ "data/32bit/usr/bin/pwnlib",
+ ]
+ }
+ provisioner "shell" {
+ inline = ["chmod +x /usr/bin/*"]
+ }
+
+ provisioner "file" {
+ destination = "/etc/systemd/system/"
+ sources = [
+ "data/32bit/etc/systemd/system/bettercap.service",
+ "data/32bit/etc/systemd/system/pwnagotchi.service",
+ "data/32bit/etc/systemd/system/pwngrid-peer.service",
+ ]
+ }
+ provisioner "file" {
+ destination = "/etc/update-motd.d/01-motd"
+ source = "data/32bit/etc/update-motd.d/01-motd"
+ }
+ provisioner "shell" {
+ inline = ["chmod +x /etc/update-motd.d/*"]
+ }
+ provisioner "shell" {
+ inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
+ }
+ provisioner "ansible-local" {
+ command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
+ extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
+ playbook_dir = "data/32bit/extras/"
+ playbook_file = "data/32bit/raspberrypi32.yml"
+ }
+}
\ No newline at end of file
diff --git a/builder/data/etc/bash_completion.d/pwnagotchi_completion.sh b/builder/data/32bit/etc/bash_completion.d/pwnagotchi_completion.sh
similarity index 100%
rename from builder/data/etc/bash_completion.d/pwnagotchi_completion.sh
rename to builder/data/32bit/etc/bash_completion.d/pwnagotchi_completion.sh
diff --git a/builder/data/32bit/etc/dhcpcd.conf b/builder/data/32bit/etc/dhcpcd.conf
new file mode 100644
index 00000000..90444ad0
--- /dev/null
+++ b/builder/data/32bit/etc/dhcpcd.conf
@@ -0,0 +1,62 @@
+# A sample configuration for dhcpcd.
+# See dhcpcd.conf(5) for details.
+
+# Allow users of this group to interact with dhcpcd via the control socket.
+#controlgroup wheel
+
+# Inform the DHCP server of our hostname for DDNS.
+hostname
+
+# Use the hardware address of the interface for the Client ID.
+clientid
+# or
+# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
+# Some non-RFC compliant DHCP servers do not reply with this set.
+# In this case, comment out duid and enable clientid above.
+#duid
+
+# Persist interface configuration when dhcpcd exits.
+persistent
+
+# Rapid commit support.
+# Safe to enable by default because it requires the equivalent option set
+# on the server to actually work.
+option rapid_commit
+
+# A list of options to request from the DHCP server.
+option domain_name_servers, domain_name, domain_search, host_name
+option classless_static_routes
+# Respect the network MTU. This is applied to DHCP routes.
+option interface_mtu
+
+# Most distributions have NTP support.
+#option ntp_servers
+
+# A ServerID is required by RFC2131.
+require dhcp_server_identifier
+
+# Generate SLAAC address using the Hardware Address of the interface
+#slaac hwaddr
+# OR generate Stable Private IPv6 Addresses based from the DUID
+slaac private
+
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# !! DO NOT EDIT THESE LINES BELOW PLEASE !!
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+# static IP configuration:
+denyinterfaces wlan0
+
+interface eth0
+static domain_name_servers=8.8.8.8 1.1.1.1
+metric 201
+
+interface usb0
+static ip_address=10.0.0.2/24
+static routers=10.0.0.1
+static domain_name_servers=10.0.0.1 8.8.8.8 1.1.1.1
+metric 202
+
+interface bnep0
+static domain_name_servers=8.8.8.8 1.1.1.1
+metric 203
\ No newline at end of file
diff --git a/builder/data/32bit/etc/dphys-swapfile b/builder/data/32bit/etc/dphys-swapfile
new file mode 100644
index 00000000..1c908a10
--- /dev/null
+++ b/builder/data/32bit/etc/dphys-swapfile
@@ -0,0 +1,26 @@
+# /etc/dphys-swapfile - user settings for dphys-swapfile package
+# author Neil Franklin, last modification 2010.05.05
+# copyright ETH Zuerich Physics Departement
+# use under either modified/non-advertising BSD or GPL license
+
+# this file is sourced with . so full normal sh syntax applies
+
+# the default settings are added as commented out CONF_*=* lines
+
+
+# where we want the swapfile to be, this is the default
+#CONF_SWAPFILE=/var/swap
+
+# set size to absolute value, leaving empty (default) then uses computed value
+# you most likely don't want this, unless you have an special disk situation
+CONF_SWAPSIZE=2048
+
+# set size to computed value, this times RAM size, dynamically adapts,
+# guarantees that there is enough swap without wasting disk space on excess
+#CONF_SWAPFACTOR=2
+
+# restrict size (computed and absolute!) to maximally this limit
+# can be set to empty for no limit, but beware of filled partitions!
+# this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it
+# but is also sensible on 64bit to prevent filling /var or even / partition
+#CONF_MAXSWAP=2048
\ No newline at end of file
diff --git a/builder/data/etc/systemd/system/bettercap.service b/builder/data/32bit/etc/systemd/system/bettercap.service
similarity index 100%
rename from builder/data/etc/systemd/system/bettercap.service
rename to builder/data/32bit/etc/systemd/system/bettercap.service
diff --git a/builder/data/32bit/etc/systemd/system/bluetooth.service b/builder/data/32bit/etc/systemd/system/bluetooth.service
new file mode 100644
index 00000000..6780e4b8
--- /dev/null
+++ b/builder/data/32bit/etc/systemd/system/bluetooth.service
@@ -0,0 +1,20 @@
+[Unit]
+Description=Bluetooth service
+Documentation=man:bluetoothd(8)
+ConditionPathIsDirectory=/sys/class/bluetooth
+
+[Service]
+Type=dbus
+BusName=org.bluez
+ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp
+NotifyAccess=main
+#WatchdogSec=10
+#Restart=on-failure
+CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
+LimitNPROC=1
+ProtectHome=true
+ProtectSystem=full
+
+[Install]
+WantedBy=bluetooth.target
+Alias=dbus-org.bluez.service
diff --git a/builder/data/32bit/etc/systemd/system/bthelper@.service.d/override.conf b/builder/data/32bit/etc/systemd/system/bthelper@.service.d/override.conf
new file mode 100644
index 00000000..e15c3c4b
--- /dev/null
+++ b/builder/data/32bit/etc/systemd/system/bthelper@.service.d/override.conf
@@ -0,0 +1,6 @@
+[Unit]
+After=hciuart.service bluetooth.service
+Before=
+
+[Service]
+ExecStartPre=/bin/sleep 5
\ No newline at end of file
diff --git a/builder/data/etc/systemd/system/pwnagotchi.service b/builder/data/32bit/etc/systemd/system/pwnagotchi.service
similarity index 100%
rename from builder/data/etc/systemd/system/pwnagotchi.service
rename to builder/data/32bit/etc/systemd/system/pwnagotchi.service
diff --git a/builder/data/etc/systemd/system/pwngrid-peer.service b/builder/data/32bit/etc/systemd/system/pwngrid-peer.service
similarity index 85%
rename from builder/data/etc/systemd/system/pwngrid-peer.service
rename to builder/data/32bit/etc/systemd/system/pwngrid-peer.service
index 0ea07f92..9a55eb3f 100644
--- a/builder/data/etc/systemd/system/pwngrid-peer.service
+++ b/builder/data/32bit/etc/systemd/system/pwngrid-peer.service
@@ -8,7 +8,7 @@ After=bettercap.service
Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1
Environment=LD_LIBRARY_PATH=/usr/local/lib
Type=simple
-ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /home/pi/logs/pwngrid-peer.log -iface wlan0mon
+ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /etc/pwnagotchi/log/pwngrid-peer.log -iface wlan0mon
Restart=always
RestartSec=30
diff --git a/builder/data/32bit/etc/update-motd.d/01-motd b/builder/data/32bit/etc/update-motd.d/01-motd
new file mode 100755
index 00000000..8c13ae9f
--- /dev/null
+++ b/builder/data/32bit/etc/update-motd.d/01-motd
@@ -0,0 +1,33 @@
+#!/bin/sh
+_hostname=$(hostname)
+_version=$(cut -d"'" -f2 < /usr/local/lib/python3.9/dist-packages/pwnagotchi/_version.py)
+echo
+echo "(◕‿‿◕) $_hostname"
+echo
+echo " Hi! I'm a pwnagotchi $_version, please take good care of me!"
+echo " Here are some basic things you need to know to raise me properly!"
+echo
+echo " If you want to change my configuration, use /etc/pwnagotchi/config.toml"
+echo " All plugin config files are located in /etc/pwnagotchi/conf.d/"
+echo " Read the readme if you want to use gdrivesync plugin!!"
+echo
+echo " All the configuration options can be found on /etc/pwnagotchi/default.toml,"
+echo " but don't change this file because I will recreate it every time I'm restarted!"
+echo
+echo " I use oPwnGrid as my main API, you can check stats at https://opwngrid.xyz"
+echo
+echo " I'm managed by systemd. Here are some basic commands."
+echo
+echo " If you want to know what I'm doing, you can check my logs with the command"
+echo " - pwnlog"
+echo " - sudo pwnagotchi --version, to check the current version"
+echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
+echo " - sudo pwnagotchi --check-update, to see if there is a new version available"
+echo
+echo " If you want to know if I'm running, you can use"
+echo " sudo systemctl status pwnagotchi"
+echo
+echo " You can restart me using"
+echo " pwnkill"
+echo
+echo " You learn more about me at https://pwnagotchi.ai/"
\ No newline at end of file
diff --git a/builder/data/32bit/extras/nexmon.yml b/builder/data/32bit/extras/nexmon.yml
new file mode 100644
index 00000000..6cdaafac
--- /dev/null
+++ b/builder/data/32bit/extras/nexmon.yml
@@ -0,0 +1,40 @@
+# Install nexmon to fix wireless scanning (takes 2.5G of space)
+- name: clone nexmon repository
+ git:
+ repo: https://github.com/DrSchottky/nexmon.git
+ dest: /usr/local/src/nexmon
+
+- name: make firmware
+ shell: "source ./setup_env.sh && make"
+ args:
+ executable: /bin/bash
+ chdir: /usr/local/src/nexmon/
+
+- name: "make firmware patch ({{ item.name }})"
+ shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/ && make"
+ args:
+ executable: /bin/bash
+ chdir: /usr/local/src/nexmon/
+ environment:
+ QEMU_UNAME: "{{ item.kernel }}"
+ ARCHFLAGS: "{{ item.arch_flags }}"
+
+- name: "install new firmware ({{ item.name }})"
+ copy:
+ src: "/usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/{{ item.firmware }}"
+ dest: "/usr/lib/firmware/brcm/{{ item.firmware }}"
+ follow: true
+ environment:
+ QEMU_UNAME: "{{ item.kernel }}"
+ ARCHFLAGS: "{{ item.arch_flags }}"
+
+- name: backup original driver
+ command: "mv /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
+
+- name: copy modified driver
+ copy:
+ src: "/usr/local/src/nexmon/patches/driver/brcmfmac_6.1.y-nexmon/brcmfmac.ko"
+ dest: "/usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
+
+- name : load brcmfmac drivers
+ command: "/sbin/depmod -a {{ item.kernel }}"
diff --git a/builder/data/32bit/raspberrypi32.json.pkr.hcl b/builder/data/32bit/raspberrypi32.json.pkr.hcl
new file mode 100644
index 00000000..a20e747f
--- /dev/null
+++ b/builder/data/32bit/raspberrypi32.json.pkr.hcl
@@ -0,0 +1,94 @@
+packer {
+ required_plugins {
+ arm = {
+ version = "1.0.0"
+ source = "github.com/cdecoux/builder-arm"
+ }
+ ansible = {
+ source = "github.com/hashicorp/ansible"
+ version = ">= 1.1.1"
+ }
+ }
+}
+
+variable "pwn_hostname" {
+ type = string
+}
+
+variable "pwn_version" {
+ type = string
+}
+
+source "arm" "rpi32-pwnagotchi" {
+ file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz.sha256"
+ file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz"]
+ file_checksum_type = "sha256"
+ file_target_extension = "xz"
+ file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
+ image_path = "../../pwnagotchi-32bit.img"
+ qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
+ qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
+ image_build_method = "resize"
+ image_size = "9G"
+ image_type = "dos"
+ image_partitions {
+ name = "boot"
+ type = "c"
+ start_sector = "8192"
+ filesystem = "fat"
+ size = "256M"
+ mountpoint = "/boot"
+ }
+ image_partitions {
+ name = "root"
+ type = "83"
+ start_sector = "532480"
+ filesystem = "ext4"
+ size = "0"
+ mountpoint = "/"
+ }
+}
+build {
+ name = "Raspberry Pi 32 Pwnagotchi"
+ sources = ["source.arm.rpi32-pwnagotchi"]
+ provisioner "file" {
+ destination = "/usr/bin/"
+ sources = [
+ "data/32bit/usr/bin/bettercap-launcher",
+ "data/32bit/usr/bin/hdmioff",
+ "data/32bit/usr/bin/hdmion",
+ "data/32bit/usr/bin/monstart",
+ "data/32bit/usr/bin/monstop",
+ "data/32bit/usr/bin/pwnagotchi-launcher",
+ "data/32bit/usr/bin/pwnlib",
+ ]
+ }
+ provisioner "shell" {
+ inline = ["chmod +x /usr/bin/*"]
+ }
+
+ provisioner "file" {
+ destination = "/etc/systemd/system/"
+ sources = [
+ "data/32bit/etc/systemd/system/bettercap.service",
+ "data/32bit/etc/systemd/system/pwnagotchi.service",
+ "data/32bit/etc/systemd/system/pwngrid-peer.service",
+ ]
+ }
+ provisioner "file" {
+ destination = "/etc/update-motd.d/01-motd"
+ source = "data/32bit/etc/update-motd.d/01-motd"
+ }
+ provisioner "shell" {
+ inline = ["chmod +x /etc/update-motd.d/*"]
+ }
+ provisioner "shell" {
+ inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
+ }
+ provisioner "ansible-local" {
+ command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
+ extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
+ playbook_dir = "data/32bit/extras/"
+ playbook_file = "data/32bit/raspberrypi32.yml"
+ }
+}
\ No newline at end of file
diff --git a/builder/data/32bit/raspberrypi32.yml b/builder/data/32bit/raspberrypi32.yml
new file mode 100644
index 00000000..453de361
--- /dev/null
+++ b/builder/data/32bit/raspberrypi32.yml
@@ -0,0 +1,594 @@
+---
+- hosts:
+ - 127.0.0.1
+ gather_facts: true
+ become: true
+ vars:
+ boards:
+ - {
+ kernel: "6.1.21+",
+ name: "PiZeroW",
+ firmware: "brcmfmac43430-sdio.bin",
+ patch: "bcm43430a1/7_45_41_46",
+ cpu: arm1176,
+ arch_flags: "-arch armv6l"
+ }
+ - {
+ kernel: "6.1.21-v7+",
+ name: "PiZero2W",
+ firmware: "brcmfmac43436-sdio.bin",
+ patch: "bcm43436b0/9_88_4_65",
+ cpu: any, #cortex-a53
+ arch_flags: "-arch armv7l"
+ }
+ - {
+ kernel: "6.1.21-v7l+",
+ name: "Pi4b_32",
+ firmware: "brcmfmac43455-sdio.bin",
+ patch: "bcm43455c0/7_45_206",
+ cpu: any, #cortex-a72
+ arch_flags: "-arch armv7l"
+ }
+ kernel:
+ min: "6.1"
+ full: "6.1.21+"
+ full_2w: "6.1.21-v7+"
+ full_4b: "6.1.21-v7l+"
+ arch: "v6l"
+ pwnagotchi:
+ hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
+ version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}"
+ custom_plugin_dir: "/usr/local/share/pwnagotchi/custom-plugins"
+ system:
+ boot_options:
+ - "#### pwnagotchi additions"
+ - "# this pwnagotchi image is 32-bit only no v8+ headers to build nexmon for 64 bit"
+ - "arm_64bit=0"
+ - "# dwc2 for RNDIS. comment out, and remove dwc2 and g_ether from cmdline.txt for X306 usb battery hat"
+ - "dtoverlay=dwc2"
+ - "dtoverlay=spi1-3cs"
+ - "dtparam=i2c1=on"
+ - "dtparam=i2c_arm=on"
+ - "dtparam=spi=on"
+ - "gpu_mem=16"
+ - "#### audio out on pins 18 and 19"
+ - "#dtoverlay=audremap,pins_18_19"
+ - "#### touchscreen on waveshare touch e-paper"
+ - "#dtoverlay=goodix,interrupt=27,reset=22"
+ - "#### for PWM backlighting on pimoroni displayhatmini"
+ - "dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4"
+ modules:
+ - "i2c-dev"
+ services:
+ enable:
+ - bettercap.service
+ - bluetooth.service
+ - dphys-swapfile.service
+ - fstrim.timer
+ - pwnagotchi.service
+ - pwngrid-peer.service
+ disable:
+ - apt-daily-upgrade.service
+ - apt-daily-upgrade.timer
+ - apt-daily.service
+ - apt-daily.timer
+ - ifup@wlan0.service
+ - triggerhappy.service
+ - wpa_supplicant.service
+ packages:
+ caplets:
+ source: "https://github.com/jayofelony/caplets.git"
+ bettercap:
+ source: "https://github.com/jayofelony/bettercap.git"
+ url: "https://github.com/jayofelony/bettercap/releases/download/2.32.2/bettercap-2.32.2-armhf.zip"
+ ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
+ opwngrid:
+ source: "https://github.com/jayofelony/pwngrid.git"
+ url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.7/pwngrid-1.10.7-armhf.zip"
+ torch:
+ wheel: "torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl"
+ url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl"
+ torchvision:
+ wheel: "torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
+ url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
+ apt:
+ downgrade:
+ - libpcap-dev_1.9.1-4_armhf.deb
+ - libpcap0.8-dbg_1.9.1-4_armhf.deb
+ - libpcap0.8-dev_1.9.1-4_armhf.deb
+ - libpcap0.8_1.9.1-4_armhf.deb
+ hold:
+ - firmware-atheros
+ - firmware-brcm80211
+ - firmware-libertas
+ - firmware-misc-nonfree
+ - firmware-realtek
+ - libpcap-dev
+ - libpcap0.8
+ - libpcap0.8-dev
+ - libpcap0.8-dbg
+ remove:
+ - avahi-daemon
+ - nfs-common
+ - triggerhappy
+ - wpasupplicant
+ install:
+ - autoconf
+ - bc
+ - bison
+ - bluez
+ - build-essential
+ - curl
+ - dkms
+ - dphys-swapfile
+ - espeak-ng
+ - evtest
+ - fbi
+ - flex
+ - fonts-dejavu
+ - fonts-dejavu-core
+ - fonts-dejavu-extra
+ - fonts-freefont-ttf
+ - g++
+ - gawk
+ - gcc-arm-none-eabi
+ - git
+ - libatlas-base-dev
+ - libavcodec58
+ - libavformat58
+ - libblas-dev
+ - libbluetooth-dev
+ - libbz2-dev
+ - libc-ares-dev
+ - libc6-dev
+ - libcpuinfo-dev
+ - libdbus-1-dev
+ - libdbus-glib-1-dev
+ - libeigen3-dev
+ - libelf-dev
+ - libffi-dev
+ - libfl-dev
+ - libfuse-dev
+ - libgdbm-dev
+ - libgl1-mesa-glx
+ - libgmp3-dev
+ - libgstreamer1.0-0
+ - libhdf5-dev
+ - liblapack-dev
+ - libncursesw5-dev
+ - libnetfilter-queue-dev
+ - libopenblas-dev
+ - libopenjp2-7
+ - libopenmpi-dev
+ - libopenmpi3
+ - libpcap-dev
+ - libprotobuf-dev
+ - libraspberrypi-bin
+ - libraspberrypi-dev
+ - libraspberrypi-doc
+ - libraspberrypi0
+ - libsleef-dev
+ - libsqlite3-dev
+ - libssl-dev
+ - libswscale5
+ - libtiff5
+ - libtool
+ - libts-bin
+ - libusb-1.0-0-dev
+ - lsof
+ - make
+ - ntp
+ - python3-flask
+ - python3-flask-cors
+ - python3-flaskext.wtf
+ - python3-pil
+ - python3-pip
+ - python3-protobuf
+ - python3-smbus
+ - qpdf
+ - raspberrypi-kernel-headers
+ - rsync
+ - screen
+ - tcpdump
+ - texinfo
+ - time
+ - tk-dev
+ - unzip
+ - vim
+ - wget
+ - wl
+ - xxd
+ - zlib1g-dev
+
+ tasks:
+ - name: Create pi user
+ copy:
+ dest: /boot/userconf
+ content: |
+ pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
+
+ - name: change hostname
+ lineinfile:
+ dest: /etc/hostname
+ regexp: '^raspberrypi'
+ line: "{{pwnagotchi.hostname}}"
+ state: present
+ when: lookup('file', '/etc/hostname') == "raspberrypi"
+ register: hostname
+
+ - name: add hostname to /etc/hosts
+ lineinfile:
+ dest: /etc/hosts
+ regexp: '^127\.0\.1\.1[ \t]+raspberrypi'
+ line: "127.0.1.1\t{{pwnagotchi.hostname}}"
+ state: present
+ when: hostname.changed
+
+ - name: Create custom plugin directory
+ file:
+ path: '{{ pwnagotchi.custom_plugin_dir }}'
+ state: directory
+
+ - name: update apt package cache
+ apt:
+ update_cache: yes
+
+ - name: install packages
+ apt:
+ name: "{{ packages.apt.install }}"
+ state: present
+
+ - name: update pip3, setuptools, wheel
+ shell: "python3 -m pip install --upgrade pip setuptools wheel"
+ args:
+ executable: /bin/bash
+ chdir: /usr/local/src
+
+ ###########################################
+ #
+ # libpcap v1.9 - build from source
+ #
+ ###########################################
+
+ # check for presence, then it can re-run in later parts if needed
+ # use the "make" built in
+
+ # install libpcap before bettercap and pwngrid, so they use it
+ - name: clone libpcap v1.9 from github
+ git:
+ repo: 'https://github.com/the-tcpdump-group/libpcap.git'
+ dest: /usr/local/src/libpcap
+ version: libpcap-1.9
+
+ - name: build and install libpcap into /usr/local/lib
+ shell: "./configure && make && make install"
+ args:
+ executable: /bin/bash
+ chdir: /usr/local/src/libpcap
+
+ - name: remove libpcap build folder
+ file:
+ state: absent
+ path: /usr/local/src/libpcap
+
+ - name: create symlink /usr/local/lib/libpcap.so.1.9.1
+ file:
+ src: /usr/local/lib/libpcap.so.1.9.1
+ dest: /usr/local/lib/libpcap.so.0.8
+ state: link
+
+ ###############################################################
+ # Install nexmon to fix wireless scanning (takes 2.5G of space)
+ ###############################################################
+
+ # Install nexmon for all boards
+ - name: build and install nexmon as needed
+ include_tasks: nexmon.yml
+ loop: "{{ boards }}"
+
+ # some pizero2w have the pizeroW wifi chip
+ # could this be a link instead of a copy? and force, only if not a link?
+ - name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
+ copy:
+ src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
+ dest: /usr/lib/firmware/brcm/brcmfmac43436s-sdio.bin
+ follow: true
+
+ # delete blob files that make nexmon sad
+ - name: Delete the firmware blob files to avoid some nexmon crashing
+ file:
+ state: absent
+ path: '{{ item }}'
+ loop:
+ - /usr/lib/firmware/brcm/brcmfmac43430-sdio.clm_blob
+ - /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob
+ - /usr/lib/firmware/brcm/brcmfmac43430b0-sdio.raspberrypi,model-zero-2-w.clm_blob
+ - /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
+ - /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob
+
+ # 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: clone pwnagotchi repository
+ git:
+ repo: https://github.com/jayofelony/pwnagotchi.git
+ dest: /usr/local/src/pwnagotchi
+ register: pwnagotchigit
+
+ # is this even necessary? Can't we just link from /home/pi/pwnagotchi to /usr/local/{bin,lib,etc}
+ # then just git update in the home dir and encourage hacking?
+ # make owned by pi.pi, and custom plugins.
+ - name: build pwnagotchi wheel
+ command: "python3 setup.py sdist bdist_wheel"
+ args:
+ chdir: /usr/local/src/pwnagotchi
+ when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
+
+ - name: download torch whl
+ get_url:
+ url: "{{ packages.torch.url }}"
+ dest: /usr/local/src/
+
+ - name: download torchvision whl
+ get_url:
+ url: "{{ packages.torchvision.url }}"
+ dest: /usr/local/src/
+
+ - name: install 32-bit pwnagotchi wheel and dependencies with 32-bit torch wheels
+ pip:
+ name:
+ - "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}"
+ - "{{ packages.torch.url }}"
+ - "{{ packages.torchvision.url }}"
+ extra_args: "--no-cache-dir"
+ environment:
+ QEMU_CPU: arm1176
+ QEMU_UNAME: "{{ kernel.full }}"
+ when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
+
+ - name: create /usr/local/share/pwnagotchi/ folder
+ file:
+ path: /usr/local/share/pwnagotchi/
+ state: directory
+
+ - name: remove pwnagotchi folder
+ file:
+ state: absent
+ path: /usr/local/src/pwnagotchi
+
+ - name: remove torch whl
+ file:
+ state: absent
+ path: "{{ lookup('fileglob', '/usr/local/src/torch*.whl') }}"
+
+ ##########################################
+ #
+ # pwngrid, bettercap
+ #
+ ##########################################
+
+ - name: Install go-1.21
+ unarchive:
+ src: https://go.dev/dl/go1.21.6.linux-armv6l.tar.gz
+ dest: /usr/local
+ remote_src: yes
+ register: golang
+
+ - name: Update .bashrc for go-1.21
+ blockinfile:
+ dest: /home/pi/.bashrc
+ state: present
+ block: |
+ export GOPATH=$HOME/go
+ export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
+ alias custom='cd /usr/local/share/pwnagotchi/custom-plugins/'
+ alias config='sudo nano /etc/pwnagotchi/config.toml'
+ alias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered "s/,[[:digit:]]\\{3\\}\\]//g" | cut -d " " -f 2-'
+ alias pwnver='python3 -c "import pwnagotchi as p; print(p.__version__)"'
+ alias pwnkill='sudo killall -USR1 pwnagotchi'
+ when: golang.changed
+
+ - name: download pwngrid
+ unarchive:
+ remote_src: yes
+ src: "{{ packages.opwngrid.url }}"
+ dest: /usr/local/bin/
+ mode: 0755
+
+ - name: download and install bettercap
+ unarchive:
+ src: "{{ packages.bettercap.url }}"
+ dest: /usr/local/bin
+ remote_src: yes
+ exclude:
+ - README.md
+ - LICENSE.md
+ mode: 0755
+
+ - name: clone bettercap caplets
+ git:
+ repo: "{{ packages.caplets.source }}"
+ dest: /tmp/caplets
+ register: capletsgit
+
+ - name: install bettercap caplets
+ make:
+ chdir: /tmp/caplets
+ target: install
+ when: capletsgit.changed
+
+ - name: download and install bettercap ui
+ unarchive:
+ src: "{{ packages.bettercap.ui }}"
+ dest: /usr/local/share/bettercap/
+ remote_src: yes
+ mode: 0755
+
+ # to always have the bettercap webui available (because why not?)
+ - name: copy pwnagotchi-manual over pwnagotchi-auto caplet
+ ansible.builtin.copy:
+ src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap
+ dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap
+ force: true
+ ignore_errors: true
+
+ - name: create /etc/pwnagotchi folder
+ file:
+ path: /etc/pwnagotchi
+ state: directory
+
+ - name: create log folder
+ file:
+ path: /home/pi/logs
+ state: directory
+
+ - name: check if user configuration exists
+ stat:
+ path: /etc/pwnagotchi/config.toml
+ register: user_config
+
+ - name: create /etc/pwnagotchi/config.toml
+ copy:
+ dest: /etc/pwnagotchi/config.toml
+ content: |
+ # Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
+ # Example:
+ # ui.display.enabled = true
+ # ui.display.type = "waveshare_4"
+ when: not user_config.stat.exists
+
+ - name: Delete motd 10-uname
+ file:
+ state: absent
+ path: /etc/update-motd.d/10-uname
+
+ - name: enable ssh on boot
+ file:
+ path: /boot/ssh
+ state: touch
+
+ - name: adjust /boot/config.txt
+ lineinfile:
+ dest: /boot/config.txt
+ insertafter: EOF
+ line: '{{ item }}'
+ with_items: "{{system.boot_options}}"
+
+ - name: adjust /etc/modules
+ lineinfile:
+ dest: /etc/modules
+ insertafter: EOF
+ line: '{{ item }}'
+ with_items: "{{system.modules}}"
+
+ - name: change root partition
+ replace:
+ dest: /boot/cmdline.txt
+ backup: no
+ regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+"
+ replace: "root=/dev/mmcblk0p2"
+
+ - name: configure /boot/cmdline.txt
+ lineinfile:
+ path: /boot/cmdline.txt
+ backrefs: True
+ state: present
+ backup: no
+ regexp: '(.*)$'
+ line: '\1 modules-load=dwc2,g_ether'
+
+ - name: add firmware packages to hold
+ dpkg_selections:
+ name: "{{ item }}"
+ selection: hold
+ with_items: "{{ packages.apt.hold }}"
+
+ - name: disable unnecessary services
+ systemd:
+ name: "{{ item }}"
+ state: stopped
+ enabled: no
+ with_items: "{{ services.disable }}"
+
+ - name: enable services
+ systemd:
+ name: "{{ item }}"
+ enabled: true
+ state: stopped
+ with_items: "{{ services.enable }}"
+
+ #- name: remove golang build libraries
+ # file:
+ # state: absent
+ # path: /root/go
+
+ #- name: remove golang
+ # file:
+ # state: absent
+ # path: /usr/local/go
+
+ - name: make /root readable, becauase that's where all the files are
+ file:
+ path: /root
+ mode: '755'
+
+ - name: fix permissions on /home/pi
+ file:
+ path: /home/pi
+ owner: pi
+ group: pi
+ recurse: true
+
+ - name: remove unnecessary apt packages
+ apt:
+ name: "{{ packages.apt.remove }}"
+ state: absent
+ purge: yes
+
+ - name: remove dependencies that are no longer required
+ apt:
+ autoremove: yes
+
+ - name: clean apt cache
+ apt:
+ autoclean: true
+
+ - name: remove golang build libraries
+ file:
+ state: absent
+ path: /root/go
+
+ - name: remove pre-collected packages zip
+ file:
+ path: /root/go_pkgs.tgz
+ state: absent
+
+ - name: remove golang
+ file:
+ state: absent
+ path: /usr/local/go
+
+ - name: remove /root/.cache (pip cache)
+ file:
+ state: absent
+ path: /root/.cache
+
+ - name: remove ssh keys
+ file:
+ state: absent
+ path: "{{ item }}"
+ with_fileglob:
+ - "/etc/ssh/ssh_host*_key*"
+
+ - name: regenerate ssh keys
+ shell: "dpkg-reconfigure openssh-server"
+ args:
+ executable: /bin/bash
+
+ handlers:
+ - name: reload systemd services
+ systemd:
+ daemon_reload: yes
\ No newline at end of file
diff --git a/builder/data/root/client_secrets.json b/builder/data/32bit/root/client_secrets.json
similarity index 100%
rename from builder/data/root/client_secrets.json
rename to builder/data/32bit/root/client_secrets.json
diff --git a/builder/data/root/settings.yaml b/builder/data/32bit/root/settings.yaml
similarity index 100%
rename from builder/data/root/settings.yaml
rename to builder/data/32bit/root/settings.yaml
diff --git a/builder/data/32bit/usr/bin/bettercap-launcher b/builder/data/32bit/usr/bin/bettercap-launcher
new file mode 100755
index 00000000..31b30621
--- /dev/null
+++ b/builder/data/32bit/usr/bin/bettercap-launcher
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+source /usr/bin/pwnlib
+
+# we need to decrypt something
+if is_crypted_mode; then
+ while ! is_decrypted; do
+ echo "Waiting for decryption..."
+ sleep 1
+ done
+fi
+
+# check if wifi driver is bugged
+if ! check_brcm; then
+ if ! reload_brcm; then
+ echo "Could not reload wifi driver. Reboot"
+ reboot
+ fi
+ sleep 10
+fi
+
+# start wlan0mon
+start_monitor_interface
+
+if is_auto_mode_no_delete; then
+ /usr/local/bin/bettercap -no-colors -caplet pwnagotchi-auto -iface wlan0mon
+else
+ /usr/local/bin/bettercap -no-colors -caplet pwnagotchi-manual -iface wlan0mon
+fi
diff --git a/builder/data/32bit/usr/bin/decryption-webserver b/builder/data/32bit/usr/bin/decryption-webserver
new file mode 100755
index 00000000..cbd4633c
--- /dev/null
+++ b/builder/data/32bit/usr/bin/decryption-webserver
@@ -0,0 +1,149 @@
+#!/usr/bin/env python3
+
+from http.server import HTTPServer, BaseHTTPRequestHandler
+from urllib.parse import parse_qsl
+
+
+_HTML_FORM_TEMPLATE = """
+
+
+
+ Decryption
+
+
+
+
+ Decryption
+ Some of your files are encrypted.
+ Please provide the decryption password.
+
+
+
+
+
+
+"""
+
+POST_RESPONSE = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+HTML_FORM = None
+
+
+class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
+
+ def do_GET(self):
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write(HTML_FORM.encode())
+
+ def do_POST(self):
+ content_length = int(self.headers['Content-Length'])
+ body = self.rfile.read(content_length)
+ for mapping, password in parse_qsl(body.decode('UTF-8')):
+ with open('/tmp/.pwnagotchi-secret-{}'.format(mapping), 'wt') as pwfile:
+ pwfile.write(password)
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write(POST_RESPONSE.encode())
+
+
+with open('/root/.pwnagotchi-crypted') as crypted_file:
+ mappings = [line.split()[0] for line in crypted_file.readlines()]
+ fields = ''.join(['\n
'.format(m=m)
+ for m in mappings])
+ HTML_FORM = _HTML_FORM_TEMPLATE.format(password_fields=fields)
+
+httpd = HTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler)
+httpd.serve_forever()
diff --git a/builder/data/usr/bin/hdmioff b/builder/data/32bit/usr/bin/hdmioff
similarity index 100%
rename from builder/data/usr/bin/hdmioff
rename to builder/data/32bit/usr/bin/hdmioff
diff --git a/builder/data/usr/bin/hdmion b/builder/data/32bit/usr/bin/hdmion
similarity index 100%
rename from builder/data/usr/bin/hdmion
rename to builder/data/32bit/usr/bin/hdmion
diff --git a/builder/data/usr/bin/monstart b/builder/data/32bit/usr/bin/monstart
similarity index 100%
rename from builder/data/usr/bin/monstart
rename to builder/data/32bit/usr/bin/monstart
diff --git a/builder/data/usr/bin/monstop b/builder/data/32bit/usr/bin/monstop
similarity index 100%
rename from builder/data/usr/bin/monstop
rename to builder/data/32bit/usr/bin/monstop
diff --git a/builder/data/usr/bin/pwnagotchi-launcher b/builder/data/32bit/usr/bin/pwnagotchi-launcher
similarity index 100%
rename from builder/data/usr/bin/pwnagotchi-launcher
rename to builder/data/32bit/usr/bin/pwnagotchi-launcher
diff --git a/builder/data/32bit/usr/bin/pwnlib b/builder/data/32bit/usr/bin/pwnlib
new file mode 100755
index 00000000..45b875c6
--- /dev/null
+++ b/builder/data/32bit/usr/bin/pwnlib
@@ -0,0 +1,184 @@
+#!/usr/bin/env bash
+
+# check if brcm is stuck
+check_brcm() {
+ if [[ "$(journalctl -n10 -k --since -5m | grep -c 'brcmf_cfg80211_nexmon_set_channel.*Set Channel failed')" -ge 5 ]]; then
+ return 1
+ fi
+ return 0
+}
+
+# reload mod
+reload_brcm() {
+ if ! modprobe -r brcmfmac; then
+ return 1
+ fi
+ sleep 1
+ if ! modprobe brcmfmac; then
+ return 1
+ fi
+ sleep 2
+ iw dev wlan0 set power_save off
+ return 0
+}
+
+# starts mon0
+start_monitor_interface() {
+ rfkill unblock all
+ ifconfig wlan0 up
+ iw dev wlan0 set power_save off
+ iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor
+ rfkill unblock all
+ ifconfig wlan0 down
+ ifconfig wlan0mon up
+ iw dev wlan0mon set power_save off
+}
+
+# stops mon0
+stop_monitor_interface() {
+ ifconfig wlan0mon down && iw dev wlan0mon del
+ reload_brcm
+ ifconfig wlan0 up
+}
+
+# returns 0 if the specificed network interface is up
+is_interface_up() {
+ if grep -qi 'up' /sys/class/net/"$1"/operstate; then
+ return 0
+ fi
+ return 1
+}
+
+# returns 0 if conditions for AUTO mode are met
+is_auto_mode() {
+ # check override file first
+ if [ -f /root/.pwnagotchi-manual ]; then
+ # remove the override file if found
+ rm -rf /root/.pwnagotchi-manual
+ return 1
+ fi
+
+ # check override file first
+ if [ -f /root/.pwnagotchi-auto ]; then
+ # remove the override file if found
+ rm -rf /root/.pwnagotchi-auto
+ return 0
+ fi
+
+ # if usb0 is up, we're in MANU
+ if is_interface_up usb0; then
+ return 1
+ fi
+
+ # if eth0 is up (for other boards), we're in MANU
+ if is_interface_up eth0; then
+ return 0
+ fi
+
+ # no override, but none of the interfaces is up -> AUTO
+ return 0
+}
+
+# returns 0 if conditions for AUTO mode are met
+is_auto_mode_no_delete() {
+ # check override file first
+ if [ -f /root/.pwnagotchi-manual ]; then
+ return 1
+ fi
+
+ # check override file first
+ if [ -f /root/.pwnagotchi-auto ]; then
+ return 0
+ fi
+
+ # if usb0 is up, we're in MANU
+ if is_interface_up usb0; then
+ return 1
+ fi
+
+ # if eth0 is up (for other boards), we're in MANU
+ if is_interface_up eth0; then
+ return 0
+ fi
+
+ # no override, but none of the interfaces is up -> AUTO
+ return 0
+}
+
+# check if we need to decrypt something
+is_crypted_mode() {
+ if [ -f /root/.pwnagotchi-crypted ]; then
+ return 0
+ fi
+ return 1
+}
+
+# decryption loop
+is_decrypted() {
+ while read -r mapping container mount; do
+ # mapping = name the device or file will be mapped to
+ # container = the luks encrypted device or file
+ # mount = the mountpoint
+
+ # fail if not mounted
+ if ! mountpoint -q "$mount" >/dev/null 2>&1; then
+ if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then
+ /dev/null 2>&1; then
+ echo "Container decrypted!"
+ fi
+ fi
+
+ if mount /dev/mapper/"$mapping" "$mount" >/dev/null 2>&1; then
+ echo "Mounted /dev/mapper/$mapping to $mount"
+ continue
+ fi
+ fi
+
+ if ! ip -4 addr show wlan0 | grep inet >/dev/null 2>&1; then
+ >/dev/null 2>&1 ip addr add 192.168.0.10/24 dev wlan0
+ fi
+
+ if ! pgrep -f decryption-webserver >/dev/null 2>&1; then
+ >/dev/null 2>&1 decryption-webserver &
+ fi
+
+ if ! pgrep wpa_supplicant >/dev/null 2>&1; then
+ >/tmp/wpa_supplicant.conf cat </dev/null 2>&1 wpa_supplicant -u -s -O -D nl80211 -i wlan0 -c /tmp/wpa_supplicant.conf &
+ fi
+
+ if ! pgrep dnsmasq >/dev/null 2>&1; then
+ >/dev/null 2>&1 dnsmasq -k -p 53 -h -O "6,192.168.0.10" -A "/#/192.168.0.10" -i wlan0 -K -F 192.168.0.50,192.168.0.60,255.255.255.0,24h &
+ fi
+
+ return 1
+ fi
+ done /dev/null
+ # delete
+ rm /tmp/.pwnagotchi-secret-*
+ sync # flush
+
+ pkill wpa_supplicant
+ pkill dnsmasq
+ pid="$(pgrep -f "decryption-webserver")"
+ [[ -n "$pid" ]] && kill "$pid"
+
+ return 0
+}
diff --git a/builder/data/etc/NetworkManager/NetworkManager.conf b/builder/data/64bit/etc/NetworkManager/NetworkManager.conf
similarity index 100%
rename from builder/data/etc/NetworkManager/NetworkManager.conf
rename to builder/data/64bit/etc/NetworkManager/NetworkManager.conf
diff --git a/builder/data/64bit/etc/bash_completion.d/pwnagotchi_completion.sh b/builder/data/64bit/etc/bash_completion.d/pwnagotchi_completion.sh
new file mode 100644
index 00000000..68edc4e9
--- /dev/null
+++ b/builder/data/64bit/etc/bash_completion.d/pwnagotchi_completion.sh
@@ -0,0 +1,36 @@
+_show_complete()
+{
+ local cur opts node_names all_options opt_line
+ all_options="
+pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --wizard --check-update --donate {plugins,google}
+pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
+pwnagotchi plugins list -i --installed -h --help
+pwnagotchi plugins install -h --help
+pwnagotchi plugins uninstall -h --help
+pwnagotchi plugins enable -h --help
+pwnagotchi plugins disable -h --help
+pwnagotchi plugins update -h --help
+pwnagotchi plugins upgrade -h --help
+pwnagotchi google -h --help {login,refresh}
+pwnagotchi google login -h --help
+pwnagotchi google refresh -h --help
+"
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ # shellcheck disable=SC2124
+ cmd="${COMP_WORDS[@]:0:${#COMP_WORDS[@]}-1}"
+ opt_line="$(grep -m1 "$cmd" <<<"$all_options")"
+ if [[ ${cur} == -* ]] ; then
+ opts="$(echo "$opt_line" | tr ' ' '\n' | awk '/^ *-/{gsub("[^a-zA-Z0-9-]","",$1);print $1}')"
+ # shellcheck disable=SC2207
+ COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
+ return 0
+ fi
+
+ # shellcheck disable=SC2086
+ opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')"
+ # shellcheck disable=SC2207
+ COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
+}
+
+complete -F _show_complete pwnagotchi
diff --git a/builder/data/64bit/etc/dphys-swapfile b/builder/data/64bit/etc/dphys-swapfile
new file mode 100644
index 00000000..1c908a10
--- /dev/null
+++ b/builder/data/64bit/etc/dphys-swapfile
@@ -0,0 +1,26 @@
+# /etc/dphys-swapfile - user settings for dphys-swapfile package
+# author Neil Franklin, last modification 2010.05.05
+# copyright ETH Zuerich Physics Departement
+# use under either modified/non-advertising BSD or GPL license
+
+# this file is sourced with . so full normal sh syntax applies
+
+# the default settings are added as commented out CONF_*=* lines
+
+
+# where we want the swapfile to be, this is the default
+#CONF_SWAPFILE=/var/swap
+
+# set size to absolute value, leaving empty (default) then uses computed value
+# you most likely don't want this, unless you have an special disk situation
+CONF_SWAPSIZE=2048
+
+# set size to computed value, this times RAM size, dynamically adapts,
+# guarantees that there is enough swap without wasting disk space on excess
+#CONF_SWAPFACTOR=2
+
+# restrict size (computed and absolute!) to maximally this limit
+# can be set to empty for no limit, but beware of filled partitions!
+# this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it
+# but is also sensible on 64bit to prevent filling /var or even / partition
+#CONF_MAXSWAP=2048
\ No newline at end of file
diff --git a/builder/data/etc/network/interfaces.d/eth0-cfg b/builder/data/64bit/etc/network/interfaces.d/eth0-cfg
similarity index 100%
rename from builder/data/etc/network/interfaces.d/eth0-cfg
rename to builder/data/64bit/etc/network/interfaces.d/eth0-cfg
diff --git a/builder/data/etc/network/interfaces.d/lo-cfg b/builder/data/64bit/etc/network/interfaces.d/lo-cfg
similarity index 100%
rename from builder/data/etc/network/interfaces.d/lo-cfg
rename to builder/data/64bit/etc/network/interfaces.d/lo-cfg
diff --git a/builder/data/etc/network/interfaces.d/usb0-cfg b/builder/data/64bit/etc/network/interfaces.d/usb0-cfg
similarity index 91%
rename from builder/data/etc/network/interfaces.d/usb0-cfg
rename to builder/data/64bit/etc/network/interfaces.d/usb0-cfg
index a5ba8508..3521780a 100644
--- a/builder/data/etc/network/interfaces.d/usb0-cfg
+++ b/builder/data/64bit/etc/network/interfaces.d/usb0-cfg
@@ -5,4 +5,4 @@ iface usb0 inet static
network 10.0.0.0
broadcast 10.0.0.255
gateway 10.0.0.1
- metric 101
\ No newline at end of file
+ metric 101
diff --git a/builder/data/etc/network/interfaces.d/wlan0-cfg b/builder/data/64bit/etc/network/interfaces.d/wlan0-cfg
similarity index 100%
rename from builder/data/etc/network/interfaces.d/wlan0-cfg
rename to builder/data/64bit/etc/network/interfaces.d/wlan0-cfg
diff --git a/builder/data/64bit/etc/systemd/system/bettercap.service b/builder/data/64bit/etc/systemd/system/bettercap.service
new file mode 100644
index 00000000..ce8e8290
--- /dev/null
+++ b/builder/data/64bit/etc/systemd/system/bettercap.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=bettercap api.rest service.
+Documentation=https://bettercap.org
+Wants=network.target
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/bettercap-launcher
+Restart=always
+RestartSec=30
+
+[Install]
+WantedBy=multi-user.target
diff --git a/builder/data/64bit/etc/systemd/system/bluetooth.service b/builder/data/64bit/etc/systemd/system/bluetooth.service
new file mode 100644
index 00000000..6780e4b8
--- /dev/null
+++ b/builder/data/64bit/etc/systemd/system/bluetooth.service
@@ -0,0 +1,20 @@
+[Unit]
+Description=Bluetooth service
+Documentation=man:bluetoothd(8)
+ConditionPathIsDirectory=/sys/class/bluetooth
+
+[Service]
+Type=dbus
+BusName=org.bluez
+ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp
+NotifyAccess=main
+#WatchdogSec=10
+#Restart=on-failure
+CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
+LimitNPROC=1
+ProtectHome=true
+ProtectSystem=full
+
+[Install]
+WantedBy=bluetooth.target
+Alias=dbus-org.bluez.service
diff --git a/builder/data/64bit/etc/systemd/system/pwnagotchi.service b/builder/data/64bit/etc/systemd/system/pwnagotchi.service
new file mode 100644
index 00000000..2810e3f5
--- /dev/null
+++ b/builder/data/64bit/etc/systemd/system/pwnagotchi.service
@@ -0,0 +1,20 @@
+[Unit]
+Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.
+Documentation=https://pwnagotchi.org
+Wants=network.target
+After=pwngrid-peer.service
+
+[Service]
+Type=simple
+WorkingDirectory=~
+ExecStart=/usr/bin/pwnagotchi-launcher
+ExecStopPost=/usr/bin/bash -c "if egrep -qi 'personality.clear_on_exit[ =]*true' /etc/pwnagotchi/config.toml ; then /usr/local/bin/pwnagotchi --clear; fi"
+Restart=always
+RestartSec=30
+TasksMax=infinity
+LimitNPROC=infinity
+StandardOutput=null
+StandardError=null
+
+[Install]
+WantedBy=multi-user.target
diff --git a/builder/data/64bit/etc/systemd/system/pwngrid-peer.service b/builder/data/64bit/etc/systemd/system/pwngrid-peer.service
new file mode 100644
index 00000000..9a55eb3f
--- /dev/null
+++ b/builder/data/64bit/etc/systemd/system/pwngrid-peer.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=pwngrid peer service.
+Documentation=https://pwnagotchi.ai
+Wants=network.target
+After=bettercap.service
+
+[Service]
+Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1
+Environment=LD_LIBRARY_PATH=/usr/local/lib
+Type=simple
+ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /etc/pwnagotchi/log/pwngrid-peer.log -iface wlan0mon
+Restart=always
+RestartSec=30
+
+[Install]
+WantedBy=multi-user.target
diff --git a/builder/data/etc/update-motd.d/01-motd b/builder/data/64bit/etc/update-motd.d/01-motd
similarity index 95%
rename from builder/data/etc/update-motd.d/01-motd
rename to builder/data/64bit/etc/update-motd.d/01-motd
index 31f6c6bd..1f9d52d2 100755
--- a/builder/data/etc/update-motd.d/01-motd
+++ b/builder/data/64bit/etc/update-motd.d/01-motd
@@ -20,6 +20,7 @@ echo " I'm managed by systemd. Here are some basic commands."
echo
echo " If you want to know what I'm doing, you can check my logs with the command"
echo " - pwnlog"
+echo " - sudo pwnagotchi --wizard, to help set up a config.toml"
echo " - sudo pwnagotchi --version, to check the current version"
echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
echo " - sudo pwnagotchi --check-update, to see if there is a new version available"
diff --git a/builder/pwnagotchi.json.pkr.hcl b/builder/data/64bit/raspberrypi64.json.pkr.hcl
similarity index 79%
rename from builder/pwnagotchi.json.pkr.hcl
rename to builder/data/64bit/raspberrypi64.json.pkr.hcl
index 363e7f0c..2dbe0cb5 100644
--- a/builder/pwnagotchi.json.pkr.hcl
+++ b/builder/data/64bit/raspberrypi64.json.pkr.hcl
@@ -6,7 +6,7 @@ packer {
}
ansible = {
source = "github.com/hashicorp/ansible"
- version = "~> 1"
+ version = ">= 1.1.1"
}
}
}
@@ -25,7 +25,7 @@ source "arm" "rpi64-pwnagotchi" {
file_checksum_type = "sha256"
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
- image_path = "../../../pwnagotchi-rpi-bookworm-${var.pwn_version}-arm64.img"
+ image_path = "../../../pwnagotchi-64bit.img"
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
image_build_method = "resize"
@@ -61,34 +61,30 @@ build {
provisioner "file" {
destination = "/usr/bin/"
sources = [
- "data/usr/bin/bettercap-launcher",
- "data/usr/bin/hdmioff",
- "data/usr/bin/hdmion",
- "data/usr/bin/monstart",
- "data/usr/bin/monstop",
- "data/usr/bin/pwnagotchi-launcher",
- "data/usr/bin/pwnlib",
+ "data/64bit/usr/bin/bettercap-launcher",
+ "data/64bit/usr/bin/hdmioff",
+ "data/64bit/usr/bin/hdmion",
+ "data/64bit/usr/bin/monstart",
+ "data/64bit/usr/bin/monstop",
+ "data/64bit/usr/bin/pwnagotchi-launcher",
+ "data/64bit/usr/bin/pwnlib",
]
}
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
- provisioner "shell" {
- inline = ["dpkg --add-architecture armhf"]
- }
-
provisioner "file" {
destination = "/etc/systemd/system/"
sources = [
- "data/etc/systemd/system/bettercap.service",
- "data/etc/systemd/system/pwnagotchi.service",
- "data/etc/systemd/system/pwngrid-peer.service",
+ "data/64bit/etc/systemd/system/bettercap.service",
+ "data/64bit/etc/systemd/system/pwnagotchi.service",
+ "data/64bit/etc/systemd/system/pwngrid-peer.service",
]
}
provisioner "file" {
destination = "/etc/update-motd.d/01-motd"
- source = "data/etc/update-motd.d/01-motd"
+ source = "data/64bit/etc/update-motd.d/01-motd"
}
provisioner "shell" {
inline = ["chmod +x /etc/update-motd.d/*"]
@@ -99,6 +95,6 @@ build {
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
- playbook_file = "raspberrypi64.yml"
+ playbook_file = "data/64bit/raspberrypi64.yml"
}
}
\ No newline at end of file
diff --git a/builder/raspberrypi64.yml b/builder/data/64bit/raspberrypi64.yml
similarity index 91%
rename from builder/raspberrypi64.yml
rename to builder/data/64bit/raspberrypi64.yml
index 6b7e8973..b26b1f7b 100644
--- a/builder/raspberrypi64.yml
+++ b/builder/data/64bit/raspberrypi64.yml
@@ -10,7 +10,7 @@
full_pi5: "6.1.0-rpi8-rpi-2712"
pwnagotchi:
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
- version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}"
+ version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
system:
boot_options:
- "dtoverlay=dwc2"
@@ -27,7 +27,6 @@
- fstrim.timer
- pwnagotchi.service
- pwngrid-peer.service
- - zramswap.service
disable:
- apt-daily-upgrade.service
- apt-daily-upgrade.timer
@@ -40,11 +39,11 @@
source: "https://github.com/jayofelony/caplets.git"
bettercap:
source: "https://github.com/jayofelony/bettercap.git"
- url: "https://github.com/jayofelony/bettercap/releases/download/v2.32.4/bettercap-2.32.4-aarch64.zip"
+ url: "https://github.com/jayofelony/bettercap/releases/download/2.32.4/bettercap-2.32.4.zip"
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
pwngrid:
source: "https://github.com/jayofelony/pwngrid.git"
- url: "https://github.com/jayofelony/pwngrid/releases/download/v1.11.0/pwngrid-1.11.0-aarch64.zip"
+ url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.5/pwngrid-1.10.5-aarch64.zip"
apt:
downgrade:
- libpcap-dev_1.9.1-4_arm64.deb
@@ -78,6 +77,7 @@
- build-essential
- curl
- dkms
+ - dphys-swapfile
- fbi
- firmware-atheros
- firmware-brcm80211
@@ -102,7 +102,6 @@
- libbz2-dev
- libc-ares-dev
- libc6-dev
- - libc6:armhf
- libcap-dev
- libcurl-ocaml-dev
- libdbus-1-dev
@@ -117,10 +116,7 @@
- libgmp3-dev
- libgstreamer1.0-0
- libhdf5-dev
- - libisl23:armhf
- liblapack-dev
- - libmpc3:armhf
- - libmpfr6:armhf
- libncursesw5-dev
- libnetfilter-queue-dev
- libopenblas-dev
@@ -135,13 +131,13 @@
- libsqlite3-dev
- libssl-dev
- libssl-ocaml-dev
- - libstdc++6:armhf
- libswscale5
- libtiff6
- libtool
- libusb-1.0-0-dev
- lsof
- make
+ - ntp
- python3-dbus
- python3-flask
- python3-flask-cors
@@ -173,10 +169,8 @@
- wl
- xxd
- zlib1g-dev
- - zram-tools
environment:
ARCHFLAGS: "-arch aarch64"
- QEMU_UNAME: "{{ kernel.full }}"
tasks:
# First we install packages
@@ -187,6 +181,12 @@
update_cache: yes
install_recommends: false
+ - name: update pip3, setuptools, wheel
+ shell: "python3 -m pip install --upgrade pip setuptools wheel --break-system-packages"
+ args:
+ executable: /bin/bash
+ chdir: /usr/local/src
+
# Now we set up /boot/firmware
- name: Create pi user
copy:
@@ -298,22 +298,9 @@
state: absent
path: /usr/local/src/hcxtools
- # Install nexmon to fix wireless scanning (takes 2.5G of space)
- - name: symlink 1
- file:
- src: "/usr/lib/arm-linux-gnueabihf/libisl.so.23.2.0"
- dest: "/usr/lib/arm-linux-gnueabihf/libisl.so.10"
- state: link
-
- - name: symlink 2
- file:
- src: "/usr/lib/arm-linux-gnueabihf/libmpfr.so.6.2.0"
- dest: "/usr/lib/arm-linux-gnueabihf/libmpfr.so.4"
- state: link
-
- name: clone nexmon repository
git:
- repo: https://github.com/seemoo-lab/nexmon.git
+ repo: https://github.com/DrSchottky/nexmon.git
dest: /usr/local/src/nexmon
# FIRST WE BUILD DRIVER FOR RPi5
@@ -366,7 +353,7 @@
- name: clone nexmon repository
git:
- repo: https://github.com/seemoo-lab/nexmon.git
+ repo: https://github.com/DrSchottky/nexmon.git
dest: /usr/local/src/nexmon
- name: make firmware, RPi4
@@ -479,7 +466,7 @@
- name: clone pwnagotchi repository
git:
- repo: https://github.com/jayofelony/pwnagotchi-bookworm.git
+ repo: https://github.com/jayofelony/pwnagotchi.git
dest: /usr/local/src/pwnagotchi
- name: build pwnagotchi wheel
@@ -511,6 +498,11 @@
block: |
export GOPATH=$HOME/go
export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
+ alias custom='cd /usr/local/share/pwnagotchi/custom-plugins/'
+ alias config='sudo nano /etc/pwnagotchi/config.toml'
+ alias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered "s/,[[:digit:]]\\{3\\}\\]//g" | cut -d " " -f 2-'
+ alias pwnver='python3 -c "import pwnagotchi as p; print(p.__version__)"'
+ alias pwnkill='sudo killall -USR1 pwnagotchi'
when: golang.changed
- name: download pwngrid
@@ -534,7 +526,7 @@
repo: "{{ packages.bettercap.source }}"
dest: /usr/local/src/bettercap
- - name: install bettercap 2.32.2
+ - name: install bettercap 2.32.4
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
args:
executable: /bin/bash
@@ -587,11 +579,6 @@
path: /etc/pwnagotchi
state: directory
- - name: create log folder
- file:
- path: /home/pi/logs
- state: directory
-
- name: check if user configuration exists
stat:
path: /etc/pwnagotchi/config.toml
@@ -604,7 +591,7 @@
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
# Example:
# ui.display.enabled = true
- # ui.display.type = "waveshare_2"
+ # ui.display.type = "waveshare_4"
when: not user_config.stat.exists
- name: Delete motd
@@ -617,24 +604,6 @@
state: absent
path: /etc/update-motd.d/10-uname
- - name: Add pwnlog alias
- lineinfile:
- dest: /home/pi/.bashrc
- line: "\nalias pwnlog='tail -f -n300 /home/pi/logs/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'"
- insertafter: EOF
-
- - name: Add pwnver alias
- lineinfile:
- dest: /home/pi/.bashrc
- line: "\nalias pwnver='python3 -c \"import pwnagotchi as p; print(p.__version__)\"'"
- insertafter: EOF
-
- - name: Add pwnkill alias to restart pwnagotchi with a signal
- lineinfile:
- dest: /home/pi/.bashrc
- line: "\nalias pwnkill='sudo killall -USR1 pwnagotchi'"
- insertafter: EOF
-
- name: add firmware packages to hold
dpkg_selections:
name: "{{ item }}"
diff --git a/builder/data/64bit/root/client_secrets.json b/builder/data/64bit/root/client_secrets.json
new file mode 100644
index 00000000..e69de29b
diff --git a/builder/data/64bit/root/settings.yaml b/builder/data/64bit/root/settings.yaml
new file mode 100644
index 00000000..5f198bde
--- /dev/null
+++ b/builder/data/64bit/root/settings.yaml
@@ -0,0 +1,15 @@
+client_config_backend: file
+client_config_file: /root/client_secrets.json
+client_config:
+ client_id:
+ client_secret:
+
+save_credentials: True
+save_credentials_backend: file
+save_credentials_file: /root/credentials.json
+
+get_refresh_token: True
+
+oauth_scope:
+ - https://www.googleapis.com/auth/drive
+ - https://www.googleapis.com/auth/drive.install
\ No newline at end of file
diff --git a/builder/data/usr/bin/bettercap-launcher b/builder/data/64bit/usr/bin/bettercap-launcher
similarity index 100%
rename from builder/data/usr/bin/bettercap-launcher
rename to builder/data/64bit/usr/bin/bettercap-launcher
diff --git a/builder/data/usr/bin/decryption-webserver b/builder/data/64bit/usr/bin/decryption-webserver
similarity index 100%
rename from builder/data/usr/bin/decryption-webserver
rename to builder/data/64bit/usr/bin/decryption-webserver
diff --git a/builder/data/64bit/usr/bin/hdmioff b/builder/data/64bit/usr/bin/hdmioff
new file mode 100755
index 00000000..5c32d62c
--- /dev/null
+++ b/builder/data/64bit/usr/bin/hdmioff
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+sudo /usr/bin/tvservice -o
\ No newline at end of file
diff --git a/builder/data/64bit/usr/bin/hdmion b/builder/data/64bit/usr/bin/hdmion
new file mode 100755
index 00000000..eec440fc
--- /dev/null
+++ b/builder/data/64bit/usr/bin/hdmion
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+sudo /usr/bin/tvservice -p
\ No newline at end of file
diff --git a/builder/data/64bit/usr/bin/monstart b/builder/data/64bit/usr/bin/monstart
new file mode 100755
index 00000000..4c00a5c5
--- /dev/null
+++ b/builder/data/64bit/usr/bin/monstart
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+source /usr/bin/pwnlib
+start_monitor_interface
diff --git a/builder/data/64bit/usr/bin/monstop b/builder/data/64bit/usr/bin/monstop
new file mode 100755
index 00000000..c90a3aef
--- /dev/null
+++ b/builder/data/64bit/usr/bin/monstop
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+source /usr/bin/pwnlib
+stop_monitor_interface
\ No newline at end of file
diff --git a/builder/data/64bit/usr/bin/pwnagotchi-launcher b/builder/data/64bit/usr/bin/pwnagotchi-launcher
new file mode 100755
index 00000000..9728c0cd
--- /dev/null
+++ b/builder/data/64bit/usr/bin/pwnagotchi-launcher
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+source /usr/bin/pwnlib
+
+# we need to decrypt something
+if is_crypted_mode; then
+ while ! is_decrypted; do
+ echo "Waiting for decryption..."
+ sleep 1
+ done
+fi
+
+if is_auto_mode; then
+ /usr/local/bin/pwnagotchi
+else
+ /usr/local/bin/pwnagotchi --manual
+fi
diff --git a/builder/data/usr/bin/pwnlib b/builder/data/64bit/usr/bin/pwnlib
similarity index 100%
rename from builder/data/usr/bin/pwnlib
rename to builder/data/64bit/usr/bin/pwnlib
diff --git a/builder/data/etc/default/zramswap b/builder/data/etc/default/zramswap
deleted file mode 100644
index 98f9b722..00000000
--- a/builder/data/etc/default/zramswap
+++ /dev/null
@@ -1,15 +0,0 @@
-# Specifies amount of zram devices to create.
-# By default, zramswap-start will use all available cores.
-#CORES=1
-
-# Specifies the amount of RAM that should be used for zram
-# based on a percentage the total amount of available memory
-PERCENTAGE=60
-
-# Specifies a static amount of RAM that should be used for
-# the ZRAM devices, this is in MiB
-#ALLOCATION=256
-
-# Specifies the priority for the swap devices, see swapon(2)
-# for more details.
-#PRIORITY=100
\ No newline at end of file
diff --git a/pwnagotchi/_version.py b/pwnagotchi/_version.py
index 964a32ab..af815f23 100644
--- a/pwnagotchi/_version.py
+++ b/pwnagotchi/_version.py
@@ -1 +1 @@
-__version__ = '2.8.2'
+__version__ = '2.8.7'
diff --git a/pwnagotchi/ai/__init__.py b/pwnagotchi/ai/__init__.py
index dd1c549a..24d798a5 100644
--- a/pwnagotchi/ai/__init__.py
+++ b/pwnagotchi/ai/__init__.py
@@ -23,6 +23,12 @@ def load(config, agent, epoch, from_disk=True):
from stable_baselines3 import A2C
logging.debug("[AI] A2C imported in %.2fs" % (time.time() - start))
+ # remove invalid ai.parameters leftover from tensor_flow, if present
+ for key in [ 'alpha', 'epsilon', 'lr_schedule' ]:
+ if key in config['params']:
+ logging.info("Removing legacy ai parameter %s" % key);
+ del config['params'][key]
+
start = time.time()
from stable_baselines3.a2c import MlpPolicy
logging.debug("[AI] MlpPolicy imported in %.2fs" % (time.time() - start))
diff --git a/pwnagotchi/defaults.toml b/pwnagotchi/defaults.toml
index b175fff1..5fdc276e 100644
--- a/pwnagotchi/defaults.toml
+++ b/pwnagotchi/defaults.toml
@@ -18,10 +18,6 @@ main.custom_plugin_repos = [
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/"
-main.plugins.aircrackonly.enabled = true
-
-main.plugins.hashie.enabled = true
-
main.plugins.auto-update.enabled = true
main.plugins.auto-update.install = true
main.plugins.auto-update.interval = 1
@@ -36,7 +32,7 @@ main.plugins.bt-tether.devices.android-phone.netmask = 24
main.plugins.bt-tether.devices.android-phone.interval = 1
main.plugins.bt-tether.devices.android-phone.scantime = 10
main.plugins.bt-tether.devices.android-phone.max_tries = 10
-main.plugins.bt-tether.devices.android-phone.share_internet = false
+main.plugins.bt-tether.devices.android-phone.share_internet = true
main.plugins.bt-tether.devices.android-phone.priority = 1
main.plugins.bt-tether.devices.ios-phone.enabled = false
@@ -47,7 +43,7 @@ main.plugins.bt-tether.devices.ios-phone.netmask = 24
main.plugins.bt-tether.devices.ios-phone.interval = 5
main.plugins.bt-tether.devices.ios-phone.scantime = 20
main.plugins.bt-tether.devices.ios-phone.max_tries = 0
-main.plugins.bt-tether.devices.ios-phone.share_internet = false
+main.plugins.bt-tether.devices.ios-phone.share_internet = true
main.plugins.bt-tether.devices.ios-phone.priority = 999
main.plugins.fix_services.enabled = true
@@ -118,7 +114,7 @@ main.no_restart = false
main.filter = ""
-main.log.path = "/home/pi/logs/pwnagotchi.log"
+main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
main.log.rotation.enabled = true
main.log.rotation.size = "10M"
@@ -156,6 +152,10 @@ personality.bond_encounters_factor = 20000
personality.throttle_a = 0.4
personality.throttle_d = 0.9
+personality.clear_on_exit = true # clear display when shutting down cleanly
+
+ui.invert = false # false = black background, true = white background
+
ui.fps = 0.0
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
ui.font.size_offset = 0 # will be added to the font size
@@ -185,6 +185,9 @@ ui.faces.debug = "(#__#)"
ui.faces.upload = "(1__0)"
ui.faces.upload1 = "(1__1)"
ui.faces.upload2 = "(0__1)"
+ui.faces.png = false
+ui.faces.position_x = 0
+ui.faces.position_y = 34
ui.web.enabled = true
ui.web.address = "::" # listening on both ipv4 and ipv6 - switch to 0.0.0.0 to listen on just ipv4
@@ -216,7 +219,7 @@ bettercap.silence = [
fs.memory.enabled = true
fs.memory.mounts.log.enabled = true
-fs.memory.mounts.log.mount = "/home/pi/logs"
+fs.memory.mounts.log.mount = "/etc/pwnagotchi/log/"
fs.memory.mounts.log.size = "50M"
fs.memory.mounts.log.sync = 60
fs.memory.mounts.log.zram = true
diff --git a/pwnagotchi/grid.py b/pwnagotchi/grid.py
index 6c908dbd..ee1aac8f 100644
--- a/pwnagotchi/grid.py
+++ b/pwnagotchi/grid.py
@@ -89,7 +89,7 @@ def update_data(last_session):
'uname': subprocess.getoutput("uname -a"),
'brain': brain,
'version': pwnagotchi.__version__,
- 'build': "Pwnagotchi-Torch by Jayofelony",
+ 'build': "Pwnagotchi by Jayofelony",
'plugins': enabled,
'language': language,
'bettercap': subprocess.getoutput("bettercap -version").split(".\n\n")[1],
diff --git a/pwnagotchi/locale/es/LC_MESSAGES/voice.mo b/pwnagotchi/locale/es/LC_MESSAGES/voice.mo
index 3d6639e5..12ce7f24 100644
Binary files a/pwnagotchi/locale/es/LC_MESSAGES/voice.mo and b/pwnagotchi/locale/es/LC_MESSAGES/voice.mo differ
diff --git a/pwnagotchi/locale/es/LC_MESSAGES/voice.po b/pwnagotchi/locale/es/LC_MESSAGES/voice.po
index b2f77686..fc5f8091 100644
--- a/pwnagotchi/locale/es/LC_MESSAGES/voice.po
+++ b/pwnagotchi/locale/es/LC_MESSAGES/voice.po
@@ -36,18 +36,18 @@ msgid "The neural network is ready."
msgstr "La red neuronal está lista."
msgid "Generating keys, do not turn off ..."
-msgstr ""
+msgstr "Generando llaves, no me apagues ..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "¡Oye, el canal {channel} está libre! Tu AP lo agradecerá."
msgid "Reading last session logs ..."
-msgstr ""
+msgstr "Leyendo los logs de la última sesión ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
-msgstr ""
+msgstr "Leyendo {lines_so_far} líneas de log hasta ahora ..."
msgid "I'm bored ..."
msgstr "Estoy aburrido ..."
@@ -74,7 +74,7 @@ msgid "Leave me alone ..."
msgstr "Me siento tan solo ..."
msgid "I'm mad at you!"
-msgstr ""
+msgstr "Toy re enojado con vos!"
msgid "I'm living the life!"
msgstr "¡Estoy viviendo la vida!"
@@ -97,11 +97,11 @@ msgstr "¡Hola {name}! Encantado de conocerte."
#, python-brace-format
msgid "Yo {name}! Sup?"
-msgstr ""
+msgstr "Que onda {name}!?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
-msgstr ""
+msgstr "Eh!, ¿Que haces {name}?"
#, python-brace-format
msgid "Unit {name} is nearby!"
@@ -127,10 +127,10 @@ msgid "Missed!"
msgstr "¡Perdido!"
msgid "Good friends are a blessing!"
-msgstr ""
+msgstr "Lxs buenxs amigxs son una masa!"
msgid "I love my friends!"
-msgstr ""
+msgstr "¡Amo a mis amigxs!"
msgid "Nobody wants to play with me ..."
msgstr "Nadie quiere jugar conmigo ..."
@@ -143,7 +143,7 @@ msgstr "¡¿Dónde está todo el mundo?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
-msgstr "Descansando durante {secs}s ..."
+msgstr "Descansando por {secs}s ..."
msgid "Zzzzz"
msgstr "Zzzzz"
@@ -203,7 +203,7 @@ msgstr "Oops, algo salió mal ... Reiniciando ..."
#, python-brace-format
msgid "Uploading data to {to} ..."
-msgstr ""
+msgstr "Subiendo data a {to} ..."
#, python-brace-format
msgid "Downloading from {name} ..."
diff --git a/pwnagotchi/locale/voice.pot b/pwnagotchi/locale/voice.pot
index 6bf175cd..a8a46eea 100644
--- a/pwnagotchi/locale/voice.pot
+++ b/pwnagotchi/locale/voice.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-01-25 23:40+0100\n"
+"POT-Creation-Date: 2024-02-16 15:26-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
diff --git a/pwnagotchi/plugins/cmd.py b/pwnagotchi/plugins/cmd.py
index 30e9fb18..9433c338 100644
--- a/pwnagotchi/plugins/cmd.py
+++ b/pwnagotchi/plugins/cmd.py
@@ -199,6 +199,9 @@ def list_plugins(args, config, pattern='*'):
available_not_installed = set(available.keys()) - set(installed.keys())
max_len_list = available_and_installed if args.installed else available_not_installed
+ if not max_len_list:
+ print('Maybe try: sudo pwnagotchi plugins update')
+ return 1
max_len = max(map(len, max_len_list))
header = line.format(name='Plugin', width=max_len, version='Version', enabled='Active', status='Status')
line_length = max(max_len, len('Plugin')) + len(header) - len('Plugin') - 12 # lol
@@ -239,7 +242,7 @@ def list_plugins(args, config, pattern='*'):
print('-' * line_length)
if not found:
- logging.info('Maybe try: pwnagotchi plugins update')
+ print('Maybe try: sudo pwnagotchi plugins update')
return 1
return 0
diff --git a/pwnagotchi/plugins/default/aircrackonly.py b/pwnagotchi/plugins/default/aircrackonly.py
deleted file mode 100644
index 98b1db52..00000000
--- a/pwnagotchi/plugins/default/aircrackonly.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import pwnagotchi.plugins as plugins
-
-import logging
-import subprocess
-import string
-import os
-
-'''
-Aircrack-ng needed, to install:
-> apt-get install aircrack-ng
-'''
-
-
-class AircrackOnly(plugins.Plugin):
- __author__ = 'pwnagotchi [at] rossmarks [dot] uk'
- __version__ = '1.0.1'
- __license__ = 'GPL3'
- __description__ = 'confirm pcap contains handshake/PMKID or delete it'
-
- def __init__(self):
- self.text_to_set = ""
- self.options = dict()
-
- def on_ready(self):
- return
-
- def on_loaded(self):
- logging.info("aircrackonly plugin loaded")
-
- if 'face' not in self.options:
- self.options['face'] = '(>.<)'
-
- check = subprocess.run(
- '/usr/bin/dpkg -l aircrack-ng | grep aircrack-ng | awk \'{print $2, $3}\'', shell=True,
- stdout=subprocess.PIPE)
- check = check.stdout.decode('utf-8').strip()
- if check != "aircrack-ng ":
- logging.info("aircrackonly: Found " + check)
- else:
- logging.warning("aircrack-ng is not installed!")
-
- def on_handshake(self, agent, filename, access_point, client_station):
- display = agent.view()
- to_delete = 0
- handshake_found = 0
-
- result = subprocess.run(('/usr/bin/aircrack-ng ' + filename + ' | grep "1 handshake" | awk \'{print $2}\''),
- shell=True, stdout=subprocess.PIPE)
- result = result.stdout.decode('utf-8').translate({ord(c): None for c in string.whitespace})
- if result:
- handshake_found = 1
- logging.info("[AircrackOnly] contains handshake")
-
- if handshake_found == 0:
- result = subprocess.run(('/usr/bin/aircrack-ng ' + filename + ' | grep "PMKID" | awk \'{print $2}\''),
- shell=True, stdout=subprocess.PIPE)
- result = result.stdout.decode('utf-8').translate({ord(c): None for c in string.whitespace})
- if result:
- logging.info("[AircrackOnly] contains PMKID")
- else:
- to_delete = 1
-
- if to_delete == 1:
- os.remove(filename)
- self.text_to_set = "Removed an uncrackable pcap"
- logging.warning("Removed uncrackable pcap " + filename)
- display.update(force=True)
-
- def on_ui_update(self, ui):
- if self.text_to_set:
- ui.set('face', self.options['face'])
- ui.set('status', self.text_to_set)
- self.text_to_set = ""
diff --git a/pwnagotchi/plugins/default/auto-update.py b/pwnagotchi/plugins/default/auto-update.py
index bd279f15..9cd918f8 100644
--- a/pwnagotchi/plugins/default/auto-update.py
+++ b/pwnagotchi/plugins/default/auto-update.py
@@ -28,7 +28,8 @@ def check(version, repo, native=True):
resp = requests.get("https://api.github.com/repos/%s/releases/latest" % repo)
latest = resp.json()
info['available'] = latest_ver = latest['tag_name'].replace('v', '')
- is_arm64 = info['arch'].startswith('aarch')
+ is_armhf = info['arch'].startswith('arm')
+ is_aarch = info['arch'].startswith('aarch')
local = version_to_tuple(info['current'])
remote = version_to_tuple(latest_ver)
@@ -36,12 +37,20 @@ def check(version, repo, native=True):
if not native:
info['url'] = "https://github.com/%s/archive/%s.zip" % (repo, latest['tag_name'])
else:
- if is_arm64:
- # check if this release is compatible with aarch64
+ if is_armhf:
+ # check if this release is compatible with armhf
for asset in latest['assets']:
download_url = asset['browser_download_url']
if (download_url.endswith('.zip') and
- (info['arch'] in download_url or (is_arm64 and 'aarch64' in download_url))):
+ (info['arch'] in download_url or (is_armhf and 'armhf' in download_url))):
+ info['url'] = download_url
+ break
+ elif is_aarch:
+ # check if this release is compatible with arm64/aarch64
+ for asset in latest['assets']:
+ download_url = asset['browser_download_url']
+ if (download_url.endswith('.zip') and
+ (info['arch'] in download_url or (is_aarch and 'aarch' in download_url))):
info['url'] = download_url
break
@@ -189,7 +198,7 @@ class AutoUpdate(plugins.Plugin):
to_check = [
('jayofelony/bettercap', parse_version('bettercap -version'), True, 'bettercap'),
('jayofelony/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'),
- ('jayofelony/pwnagotchi-bookworm', pwnagotchi.__version__, False, 'pwnagotchi')
+ ('jayofelony/pwnagotchi', pwnagotchi.__version__, False, 'pwnagotchi')
]
for repo, local_version, is_native, svc_name in to_check:
diff --git a/pwnagotchi/plugins/default/fix_services.py b/pwnagotchi/plugins/default/fix_services.py
index af265cf4..51f2398e 100644
--- a/pwnagotchi/plugins/default/fix_services.py
+++ b/pwnagotchi/plugins/default/fix_services.py
@@ -11,6 +11,10 @@ from pwnagotchi import plugins
import pwnagotchi.ui.faces as faces
from pwnagotchi.bettercap import Client
+from pwnagotchi.ui.components import Text
+from pwnagotchi.ui.view import BLACK
+import pwnagotchi.ui.fonts as fonts
+
class FixServices(plugins.Plugin):
__author__ = 'jayofelony'
@@ -21,19 +25,16 @@ class FixServices(plugins.Plugin):
__help__ = """
Reload brcmfmac module when blindbug is detected, instead of rebooting. Adapted from WATCHDOG.
"""
- __defaults__ = {
- 'enabled': True,
- }
def __init__(self):
self.options = dict()
- self.pattern1 = re.compile(r'wifi error while hopping to channel')
- self.pattern2 = re.compile(r'Firmware has halted or crashed')
- self.pattern3 = re.compile(r'error 400: could not find interface wlan0mon')
+ self.pattern = re.compile(r'brcmf_cfg80211_nexmon_set_channel.*?Set Channel failed')
+ self.pattern2 = re.compile(r'wifi error while hopping to channel')
+ self.pattern3 = re.compile(r'Firmware has halted or crashed')
+ self.pattern4 = re.compile(r'error 400: could not find interface wlan0mon')
self.isReloadingMon = False
self.connection = None
self.LASTTRY = 0
- self._count = 0
def on_loaded(self):
"""
@@ -42,14 +43,27 @@ class FixServices(plugins.Plugin):
logging.info("[Fix_Services] plugin loaded.")
def on_ready(self, agent):
- last_lines = self.get_last_lines('journalctl', ['-n10', '-k'], 10)
+ last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'],
+ stdout=subprocess.PIPE).stdout))[-10:])
try:
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
logging.debug("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
if ",UP," in str(cmd_output):
- logging.info("wlan0mon is up.")
+ logging.debug("wlan0mon is up.")
- logging.info("[Fix_Services] Logs look good!")
+ if len(self.pattern.findall(last_lines)) >= 3:
+ if hasattr(agent, 'view'):
+ display = agent.view()
+ display.set('status', 'Blind-Bug detected. Restarting.')
+ display.update(force=True)
+ logging.debug('[Fix_Services] Blind-Bug detected. Restarting.')
+ try:
+ self._tryTurningItOffAndOnAgain(agent)
+ except Exception as err:
+ logging.warning("[Fix_Services turnOffAndOn] %s" % repr(err))
+
+ else:
+ logging.debug("[Fix_Services] Logs look good!")
except Exception as err:
logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err))
@@ -63,12 +77,12 @@ class FixServices(plugins.Plugin):
# apparently this only gets messages from bettercap going to syslog, not from syslog
def on_bcap_sys_log(self, agent, event):
if re.search('wifi error while hopping to channel', event['data']['Message']):
- logging.info("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
- logging.info("[Fix_Services]**** restarting wifi.recon")
+ logging.debug("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
+ logging.debug("[Fix_Services]**** restarting wifi.recon")
try:
result = agent.run("wifi.recon off; wifi.recon on")
if result["success"]:
- logging.info("[Fix_Services] wifi.recon flip: success!")
+ logging.debug("[Fix_Services] wifi.recon flip: success!")
if hasattr(agent, 'view'):
display = agent.view()
if display:
@@ -82,23 +96,14 @@ class FixServices(plugins.Plugin):
logging.error("[Fix_Services]SYSLOG wifi.recon flip fail: %s" % err)
self._tryTurningItOffAndOnAgain(agent)
- def get_last_lines(self, command, args, n):
- try:
- process = subprocess.Popen([command] + args, stdout=subprocess.PIPE)
- output = TextIOWrapper(process.stdout)
- lines = output.readlines()
- last_n_lines = ''.join(lines[-n:])
- return last_n_lines
- except Exception as e:
- print(f"Error occurred: {e}")
- return None
-
def on_epoch(self, agent, epoch, epoch_data):
-
- last_lines = self.get_last_lines('journalctl', ['-n10', '-k'], 10)
- other_last_lines = self.get_last_lines('journalctl', ['-n10'], 10)
- other_other_last_lines = self.get_last_lines('tail', ['-n10', '/home/pi/logs/pwnagotchi.log'], 10)
-
+ last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'],
+ stdout=subprocess.PIPE).stdout))[-10:])
+ other_last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10'],
+ stdout=subprocess.PIPE).stdout))[-10:])
+ other_other_last_lines = ''.join(
+ list(TextIOWrapper(subprocess.Popen(['tail', '-n10', '/var/log/pwnagotchi.log'],
+ stdout=subprocess.PIPE).stdout))[-10:])
# don't check if we ran a reset recently
logging.debug("[Fix_Services]**** epoch")
if time.time() - self.LASTTRY > 180:
@@ -108,18 +113,31 @@ class FixServices(plugins.Plugin):
logging.debug("[Fix_Services]**** checking")
# Look for pattern 1
- if len(self.pattern1.findall(other_last_lines)) >= 5:
+ if len(self.pattern.findall(last_lines)) >= 3:
+ logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
+ if hasattr(agent, 'view'):
+ display = agent.view()
+ display.set('status', 'Blind-Bug detected. Restarting.')
+ display.update(force=True)
+ logging.debug('[Fix_Services] Blind-Bug detected. Restarting.')
+ try:
+ self._tryTurningItOffAndOnAgain(agent)
+ except Exception as err:
+ logging.warning("[Fix_Services] TTOAOA: %s" % repr(err))
+
+ # Look for pattern 2
+ elif len(self.pattern2.findall(other_last_lines)) >= 5:
logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Wifi channel stuck. Restarting recon.')
display.update(force=True)
- logging.info('[Fix_Services] Wifi channel stuck. Restarting recon.')
+ logging.debug('[Fix_Services] Wifi channel stuck. Restarting recon.')
try:
result = agent.run("wifi.recon off; wifi.recon on")
if result["success"]:
- logging.info("[Fix_Services] wifi.recon flip: success!")
+ logging.debug("[Fix_Services] wifi.recon flip: success!")
if display:
display.update(force=True, new_data={"status": "Wifi recon flipped!",
"face": faces.COOL})
@@ -131,9 +149,9 @@ class FixServices(plugins.Plugin):
except Exception as err:
logging.error("[Fix_Services wifi.recon flip] %s" % repr(err))
- # Look for pattern 2
- elif len(self.pattern2.findall(other_last_lines)) >= 1:
- logging.info("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
+ # Look for pattern 3
+ elif len(self.pattern3.findall(other_last_lines)) >= 1:
+ logging.debug("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Firmware has halted or crashed. Restarting wlan0mon.')
@@ -145,9 +163,9 @@ class FixServices(plugins.Plugin):
except Exception as err:
logging.error("[Fix_Services monstart]: %s" % repr(err))
- # Look for pattern 3
- elif len(self.pattern3.findall(other_other_last_lines)) >= 3:
- logging.info("[Fix_Services] wlan0 is down!")
+ # Look for pattern 4
+ elif len(self.pattern4.findall(other_other_last_lines)) >= 3:
+ logging.debug("[Fix_Services] wlan0 is down!")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Restarting wlan0 now!')
@@ -171,7 +189,7 @@ class FixServices(plugins.Plugin):
elif level == "debug":
logging.debug(message)
else:
- logging.info(message)
+ logging.debug(message)
if ui:
ui.update(force=force, new_data=displayData)
@@ -186,7 +204,7 @@ class FixServices(plugins.Plugin):
# avoid overlapping restarts, but allow it if it's been a while
# (in case the last attempt failed before resetting "isReloadingMon")
if self.isReloadingMon and (time.time() - self.LASTTRY) < 180:
- logging.info("[Fix_Services] Duplicate attempt ignored")
+ logging.debug("[Fix_Services] Duplicate attempt ignored")
else:
self.isReloadingMon = True
self.LASTTRY = time.time()
@@ -213,7 +231,7 @@ class FixServices(plugins.Plugin):
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
logging.debug("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
if ",UP," in str(cmd_output):
- logging.info("wlan0mon is up. Skip reset?")
+ logging.debug("wlan0mon is up. Skip reset?")
# not reliable, so don't skip just yet
# print("wlan0mon is up. Skipping reset.")
# self.isReloadingMon = False
@@ -234,7 +252,7 @@ class FixServices(plugins.Plugin):
except Exception as err:
logging.error("[Fix_Services wifi.recon off] error %s" % (repr(err)))
- logging.info("[Fix_Services] recon paused. Now trying wlan0mon reload")
+ logging.debug("[Fix_Services] recon paused. Now trying wlan0mon reload")
try:
cmd_output = subprocess.check_output("monstop", shell=True)
@@ -250,14 +268,13 @@ class FixServices(plugins.Plugin):
#
# Future: while "not fixed yet": blah blah blah. if "max_attemts", then reboot like the old days
#
- tries = 0
+ tries = 1
while tries < 3:
try:
# unload the module
cmd_output = subprocess.check_output("sudo modprobe -r brcmfmac", shell=True)
self.logPrintView("info", "[Fix_Services] unloaded brcmfmac", display,
{"status": "Turning it off #%s" % tries, "face": faces.SMART})
- time.sleep(1 + tries)
# reload the module
try:
@@ -265,27 +282,25 @@ class FixServices(plugins.Plugin):
cmd_output = subprocess.check_output("sudo modprobe brcmfmac", shell=True)
self.logPrintView("info", "[Fix_Services] reloaded brcmfmac")
- time.sleep(10 + 4 * tries) # give it some time for wlan device to stabilize, or whatever
# success! now make the mon0
try:
cmd_output = subprocess.check_output("monstart", shell=True)
- self.logPrintView("info", "[Fix_Services interface add wlan0mon] worked #%s: %s"
+ self.logPrintView("info", "[Fix_Services interface add wlan0mon worked #%s: %s"
% (tries, cmd_output))
- time.sleep(tries + 5)
try:
# try accessing mon0 in bettercap
result = connection.run("set wifi.interface wlan0mon")
if "success" in result:
- logging.info("[Fix_Services set wifi.interface wlan0mon] worked!")
- self._count = self._count + 1
- time.sleep(1)
+ logging.debug("[Fix_Services set wifi.interface wlan0mon worked!")
# stop looping and get back to recon
break
else:
- logging.debug("[Fix_Services set wifi.interfaceface wlan0mon] failed? %s" % repr(result))
+ logging.debug(
+ "[Fix_Services set wifi.interfaceface wlan0mon] failed? %s" % repr(result))
except Exception as err:
- logging.debug("[Fix_Services set wifi.interface wlan0mon] except: %s" % repr(err))
+ logging.debug(
+ "[Fix_Services set wifi.interface wlan0mon] except: %s" % repr(err))
except Exception as cerr: #
if not display:
print("failed loading wlan0mon attempt #%s: %s" % (tries, repr(cerr)))
@@ -303,11 +318,11 @@ class FixServices(plugins.Plugin):
tries = tries + 1
if tries < 3:
- logging.info("[Fix_Services] wlan0mon didn't make it. trying again")
+ logging.debug("[Fix_Services] wlan0mon didn't make it. trying again")
if not display:
print(" wlan0mon didn't make it. trying again")
else:
- logging.info("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
+ logging.debug("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
pwnagotchi.reboot()
# exited the loop, so hopefully it loaded
@@ -317,14 +332,14 @@ class FixServices(plugins.Plugin):
"face": faces.INTENSE})
else:
print("And back on again...")
- logging.info("[Fix_Services] wlan0mon back up")
+ logging.debug("[Fix_Services] wlan0mon back up")
else:
self.LASTTRY = time.time()
time.sleep(8 + tries * 2) # give it a bit before restarting recon in bettercap
self.isReloadingMon = False
- logging.info("[Fix_Services] re-enable recon")
+ logging.debug("[Fix_Services] re-enable recon")
try:
result = connection.run("wifi.clear; wifi.recon on")
@@ -345,13 +360,24 @@ class FixServices(plugins.Plugin):
logging.error("[Fix_Services wifi.recon on] %s" % repr(err))
pwnagotchi.reboot()
- def on_unload(self, ui):
+ # called to setup the ui elements
+ def on_ui_setup(self, ui):
with ui._lock:
- try:
- logging.info("[Fix_Services] unloaded")
- except Exception as err:
- logging.error("[Fix_Services] unload err %s " % repr(err))
- pass
+ # add custom UI elements
+ if "position" in self.options:
+ pos = self.options['position'].split(',')
+ pos = [int(x.strip()) for x in pos]
+ else:
+ pos = (ui.width() / 2 + 35, ui.height() - 11)
+
+ logging.debug("Got here")
+
+ # called when the ui is updated
+ def on_ui_update(self, ui):
+ return
+
+ def on_unload(self, ui):
+ return
# run from command line to brute force a reload
diff --git a/pwnagotchi/plugins/default/gps.py b/pwnagotchi/plugins/default/gps.py
index 0e5f958d..7bf72f8e 100644
--- a/pwnagotchi/plugins/default/gps.py
+++ b/pwnagotchi/plugins/default/gps.py
@@ -98,7 +98,7 @@ class GPS(plugins.Plugin):
lat_pos = (127, 74)
lon_pos = (122, 84)
alt_pos = (127, 94)
- elif ui.is_waveshare27inch():
+ elif ui.is_waveshare2in7():
lat_pos = (6, 120)
lon_pos = (1, 135)
alt_pos = (6, 150)
diff --git a/pwnagotchi/plugins/default/hashie.py b/pwnagotchi/plugins/default/hashie.py
deleted file mode 100644
index b5c8e18a..00000000
--- a/pwnagotchi/plugins/default/hashie.py
+++ /dev/null
@@ -1,179 +0,0 @@
-import logging
-import subprocess
-import os
-import json
-import pwnagotchi.plugins as plugins
-from threading import Lock
-
-'''
-hcxpcapngtool needed, to install:
-> git clone https://github.com/ZerBea/hcxtools.git
-> cd hcxtools
-> apt-get install libcurl4-openssl-dev libssl-dev zlib1g-dev
-> make
-> sudo make install
-'''
-
-
-class Hashie(plugins.Plugin):
- __author__ = 'Jayofelony'
- __version__ = '1.0.4'
- __license__ = 'GPL3'
- __description__ = '''
- Attempt to automatically convert pcaps to a crackable format.
- If successful, the files containing the hashes will be saved
- in the same folder as the handshakes.
- The files are saved in their respective Hashcat format:
- - EAPOL hashes are saved as *.22000
- - PMKID hashes are saved as *.16800
- All PCAP files without enough information to create a hash are
- stored in a file that can be read by the webgpsmap plugin.
-
- Why use it?:
- - Automatically convert handshakes to crackable formats!
- We dont all upload our hashes online ;)
- - Repair PMKID handshakes that hcxpcapngtool misses
- - If running at time of handshake capture, on_handshake can
- be used to improve the chance of the repair succeeding
- - Be a completionist! Not enough packets captured to crack a network?
- This generates an output file for the webgpsmap plugin, use the
- location data to revisit networks you need more packets for!
-
- Additional information:
- - Currently requires hcxpcapngtool compiled and installed
- - Attempts to repair PMKID hashes when hcxpcapngtool cant find the SSID
- - hcxpcapngtool sometimes has trouble extracting the SSID, so we
- use the raw 16800 output and attempt to retrieve the SSID via tcpdump
- - When access_point data is available (on_handshake), we leverage
- the reported AP name and MAC to complete the hash
- - The repair is very basic and could certainly be improved!
- Todo:
- Make it so users dont need hcxpcapngtool (unless it gets added to the base image)
- Phase 1: Extract/construct 22000/16800 hashes through tcpdump commands
- Phase 2: Extract/construct 22000/16800 hashes entirely in python
- Improve the code, a lot
- '''
-
- def __init__(self):
- self.lock = Lock()
- self.options = dict()
-
- def on_loaded(self):
- logging.info("[Hashie] Plugin loaded")
-
- def on_unloaded(self):
- logging.info("[Hashie] Plugin unloaded")
-
- # called when everything is ready and the main loop is about to start
- def on_ready(self, agent):
- config = agent.config()
- handshake_dir = config['bettercap']['handshakes']
-
- logging.info('[Hashie] Starting batch conversion of pcap files')
- with self.lock:
- self._process_stale_pcaps(handshake_dir)
-
- def on_handshake(self, agent, filename, access_point, client_station):
- with self.lock:
- handshake_status = []
- fullpathNoExt = filename.split('.')[0]
- name = filename.split('/')[-1:][0].split('.')[0]
-
- if os.path.isfile(fullpathNoExt + '.22000'):
- handshake_status.append('Already have {}.22000 (EAPOL)'.format(name))
- elif self._writeEAPOL(filename):
- handshake_status.append('Created {}.22000 (EAPOL) from pcapng'.format(name))
-
- if os.path.isfile(fullpathNoExt + '.16800'):
- handshake_status.append('Already have {}.16800 (PMKID)'.format(name))
- elif self._writePMKID(filename):
- handshake_status.append('Created {}.16800 (PMKID) from pcapng'.format(name))
-
- if handshake_status:
- logging.info('[Hashie] Good news:\n\t' + '\n\t'.join(handshake_status))
-
- def _writeEAPOL(self, fullpath):
- fullpathNoExt = fullpath.split('.')[0]
- filename = fullpath.split('/')[-1:][0].split('.')[0]
- subprocess.getoutput('hcxpcapngtool -o {}.22000 {} >/dev/null 2>&1'.format(fullpathNoExt, fullpath))
- if os.path.isfile(fullpathNoExt + '.22000'):
- logging.debug('[Hashie] [+] EAPOL Success: {}.22000 created'.format(filename))
- return True
- return False
-
- def _writePMKID(self, fullpath):
- fullpathNoExt = fullpath.split('.')[0]
- filename = fullpath.split('/')[-1:][0].split('.')[0]
- subprocess.getoutput('hcxpcapngtool -o {}.16800 {} >/dev/null 2>&1'.format(fullpathNoExt, fullpath))
- if os.path.isfile(fullpathNoExt + '.16800'):
- logging.debug('[Hashie] [+] PMKID Success: {}.16800 created'.format(filename))
- return True
- return False
-
- def _process_stale_pcaps(self, handshake_dir):
- handshakes_list = [os.path.join(handshake_dir, filename) for filename in os.listdir(handshake_dir) if filename.endswith('.pcapng')]
- failed_jobs = []
- successful_jobs = []
- lonely_pcaps = []
- for num, handshake in enumerate(handshakes_list):
- fullpathNoExt = handshake.split('.')[0]
- pcapFileName = handshake.split('/')[-1:][0]
- if not os.path.isfile(fullpathNoExt + '.22000'): # if no 22000, try
- if self._writeEAPOL(handshake):
- successful_jobs.append('22000: ' + pcapFileName)
- else:
- failed_jobs.append('22000: ' + pcapFileName)
- if not os.path.isfile(fullpathNoExt + '.16800'): # if no 16800, try
- if self._writePMKID(handshake):
- successful_jobs.append('16800: ' + pcapFileName)
- else:
- failed_jobs.append('16800: ' + pcapFileName)
- if not os.path.isfile(fullpathNoExt + '.22000'): # if no 16800 AND no 22000
- lonely_pcaps.append(handshake)
- logging.debug('[hashie] Batch job: added {} to lonely list'.format(pcapFileName))
- if ((num + 1) % 50 == 0) or (num + 1 == len(handshakes_list)): # report progress every 50, or when done
- logging.info('[Hashie] Batch job: {}/{} done ({} fails)'.format(num + 1, len(handshakes_list), len(lonely_pcaps)))
- if successful_jobs:
- logging.info('[Hashie] Batch job: {} new handshake files created'.format(len(successful_jobs)))
- if lonely_pcaps:
- logging.info('[Hashie] Batch job: {} networks without enough packets to create a hash'.format(len(lonely_pcaps)))
- self._getLocations(lonely_pcaps)
-
- def _getLocations(self, lonely_pcaps):
- # export a file for webgpsmap to load
- with open('/root/.incompletePcaps', 'w') as isIncomplete:
- count = 0
- for pcapFile in lonely_pcaps:
- filename = pcapFile.split('/')[-1:][0] # keep extension
- fullpathNoExt = pcapFile.split('.')[0]
- isIncomplete.write(filename + '\n')
- if os.path.isfile(fullpathNoExt + '.gps.json') or os.path.isfile(fullpathNoExt + '.geo.json'):
- count += 1
- if count != 0:
- logging.info('[Hashie] Used {} GPS/GEO files to find lonely networks, '
- 'go check webgpsmap! ;)'.format(str(count)))
- else:
- logging.info('[Hashie] Could not find any GPS/GEO files '
- 'for the lonely networks'.format(str(count)))
-
- def _getLocationsCSV(self, lonely_pcaps):
- # in case we need this later, export locations manually to CSV file, needs try/catch format/etc.
- locations = []
- for pcapFile in lonely_pcaps:
- filename = pcapFile.split('/')[-1:][0].split('.')[0]
- fullpathNoExt = pcapFile.split('.')[0]
- if os.path.isfile(fullpathNoExt + '.gps.json'):
- with open(fullpathNoExt + '.gps.json', 'r') as tempFileA:
- data = json.load(tempFileA)
- locations.append(filename + ',' + str(data['Latitude']) + ',' + str(data['Longitude']) + ',50')
- elif os.path.isfile(fullpathNoExt + '.geo.json'):
- with open(fullpathNoExt + '.geo.json', 'r') as tempFileB:
- data = json.load(tempFileB)
- locations.append(
- filename + ',' + str(data['location']['lat']) + ',' + str(data['location']['lng']) + ',' + str(data['accuracy']))
- if locations:
- with open('/root/locations.csv', 'w') as tempFileD:
- for loc in locations:
- tempFileD.write(loc + '\n')
- logging.info('[Hashie] Used {} GPS/GEO files to find lonely networks, '
- 'load /root/locations.csv into a mapping app and go say hi!'.format(len(locations)))
diff --git a/pwnagotchi/plugins/default/memtemp.py b/pwnagotchi/plugins/default/memtemp.py
index b9003be1..6298eca9 100644
--- a/pwnagotchi/plugins/default/memtemp.py
+++ b/pwnagotchi/plugins/default/memtemp.py
@@ -144,6 +144,9 @@ class MemTemp(plugins.Plugin):
elif ui.is_waveshare2in7():
h_pos = (192, 138)
v_pos = (211, 122)
+ elif ui.is_waveshare1in54V2():
+ h_pos = (53, 77)
+ v_pos = (154, 65)
else:
h_pos = (155, 76)
v_pos = (175, 61)
diff --git a/pwnagotchi/ui/components.py b/pwnagotchi/ui/components.py
index 1bc7ae9d..b6d4db49 100644
--- a/pwnagotchi/ui/components.py
+++ b/pwnagotchi/ui/components.py
@@ -1,4 +1,4 @@
-from PIL import Image
+from PIL import Image, ImageOps
from textwrap import TextWrapper
@@ -40,21 +40,37 @@ class FilledRect(Widget):
class Text(Widget):
- def __init__(self, value="", position=(0, 0), font=None, color=0, wrap=False, max_length=0):
+ def __init__(self, value="", position=(0, 0), font=None, color=0, wrap=False, max_length=0, png=False):
super().__init__(position, color)
self.value = value
self.font = font
self.wrap = wrap
self.max_length = max_length
self.wrapper = TextWrapper(width=self.max_length, replace_whitespace=False) if wrap else None
+ self.png = png
def draw(self, canvas, drawer):
if self.value is not None:
- if self.wrap:
- text = '\n'.join(self.wrapper.wrap(self.value))
+ if not self.png:
+ if self.wrap:
+ text = '\n'.join(self.wrapper.wrap(self.value))
+ else:
+ text = self.value
+ drawer.text(self.xy, text, font=self.font, fill=self.color)
else:
- text = self.value
- drawer.text(self.xy, text, font=self.font, fill=self.color)
+ self.image = Image.open(self.value)
+ self.image = self.image.convert('RGBA')
+ self.pixels = self.image.load()
+ for y in range(self.image.size[1]):
+ for x in range(self.image.size[0]):
+ if self.pixels[x,y][3] < 255: # check alpha
+ self.pixels[x,y] = (255, 255, 255, 255)
+ if self.color == 255:
+ self._image = ImageOps.colorize(self.image.convert('L'), black = "white", white = "black")
+ else:
+ self._image = self.image
+ self.image = self._image.convert('1')
+ canvas.paste(self.image, self.xy)
class LabeledValue(Widget):
diff --git a/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py
index 1f08c71b..6087c539 100644
--- a/pwnagotchi/ui/display.py
+++ b/pwnagotchi/ui/display.py
@@ -118,6 +118,9 @@ class Display(View):
def is_waveshare2in66g(self):
return self._implementation.name == 'waveshare2in66g'
+ def is_weact2in9(self):
+ return self._implementation.name == 'weact2in9'
+
def is_waveshare3in0g(self):
return self._implementation.name == 'waveshare3in0g'
diff --git a/pwnagotchi/ui/faces.py b/pwnagotchi/ui/faces.py
index 963e6967..63bd30f0 100644
--- a/pwnagotchi/ui/faces.py
+++ b/pwnagotchi/ui/faces.py
@@ -23,7 +23,9 @@ DEBUG = '(#__#)'
UPLOAD = '(1__0)'
UPLOAD1 = '(1__1)'
UPLOAD2 = '(0__1)'
-
+PNG = False
+POSITION_X = 0
+POSITION_Y = 40
def load_from_config(config):
for face_name, face_value in config.items():
diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/__init__.pyc b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/__init__.pyc
deleted file mode 100644
index d739fd19..00000000
Binary files a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/__init__.pyc and /dev/null differ
diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_display.pyc b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_display.pyc
deleted file mode 100644
index d9bcc266..00000000
Binary files a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_display.pyc and /dev/null differ
diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_fonts.pyc b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_fonts.pyc
deleted file mode 100644
index 274bbf43..00000000
Binary files a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_fonts.pyc and /dev/null differ
diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_printString.pyc b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_printString.pyc
deleted file mode 100644
index 51dc169e..00000000
Binary files a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_printString.pyc and /dev/null differ
diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/__init__.pyc b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/__init__.pyc
deleted file mode 100644
index 38130c00..00000000
Binary files a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/__init__.pyc and /dev/null differ
diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_8_16.pyc b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_8_16.pyc
deleted file mode 100644
index 96d1b66e..00000000
Binary files a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_8_16.pyc and /dev/null differ
diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/freetype_helper.pyc b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/freetype_helper.pyc
deleted file mode 100644
index b45f122f..00000000
Binary files a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/freetype_helper.pyc and /dev/null differ
diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.pyc b/pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.pyc
deleted file mode 100644
index 1837ca2a..00000000
Binary files a/pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.pyc and /dev/null differ
diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py
index 2f6fbfd9..75615187 100644
--- a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py
+++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py
@@ -12,6 +12,7 @@ from . import config
from . import LCD_1in44
from PIL import ImageOps
+
class EPD(object):
def __init__(self):
self.width = 128
@@ -24,9 +25,8 @@ class EPD(object):
pass
def clear(self):
- #self.LCD.LCD_Clear()
- pass
+ self.LCD.LCD_Clear()
def display(self, image):
- rgb_im = ImageOps.colorize(image.convert("L"), black ="green", white ="black")
+ rgb_im = ImageOps.colorize(image.convert("L"), black="green", white="black")
self.LCD.LCD_ShowImage(rgb_im, 0, 0)
diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py b/pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py
new file mode 100644
index 00000000..d767af73
--- /dev/null
+++ b/pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py
@@ -0,0 +1,242 @@
+# *****************************************************************************
+# * | File : epd2in13g.py
+# * | Author : Waveshare team
+# * | Function : Electronic paper driver
+# * | Info :
+# *----------------
+# * | This version: V1.0
+# * | Date : 2023-05-29
+# # | Info : python demo
+# -----------------------------------------------------------------------------
+# ******************************************************************************/
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documnetation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+import logging
+from .. import epdconfig
+
+import PIL
+from PIL import Image
+import io
+
+# Display resolution
+EPD_WIDTH = 122
+EPD_HEIGHT = 250
+
+logger = logging.getLogger(__name__)
+
+
+class EPD:
+ def __init__(self):
+ self.reset_pin = epdconfig.RST_PIN
+ self.dc_pin = epdconfig.DC_PIN
+ self.busy_pin = epdconfig.BUSY_PIN
+ self.cs_pin = epdconfig.CS_PIN
+ self.width = EPD_WIDTH
+ self.height = EPD_HEIGHT
+ self.BLACK = 0x000000 # 00 BGR
+ self.WHITE = 0xffffff # 01
+ self.YELLOW = 0x00ffff # 10
+ self.RED = 0x0000ff # 11
+ self.Gate_BITS = EPD_HEIGHT
+ if self.width < 128:
+ self.Source_BITS = 128
+ else:
+ self.Source_BITS = self.width
+
+ # Hardware reset
+ def reset(self):
+ epdconfig.digital_write(self.reset_pin, 1)
+ epdconfig.delay_ms(200)
+ epdconfig.digital_write(self.reset_pin, 0) # module reset
+ epdconfig.delay_ms(2)
+ epdconfig.digital_write(self.reset_pin, 1)
+ epdconfig.delay_ms(200)
+
+ def send_command(self, command):
+ epdconfig.digital_write(self.dc_pin, 0)
+ epdconfig.digital_write(self.cs_pin, 0)
+ epdconfig.spi_writebyte([command])
+ epdconfig.digital_write(self.cs_pin, 1)
+
+ def send_data(self, data):
+ epdconfig.digital_write(self.dc_pin, 1)
+ epdconfig.digital_write(self.cs_pin, 0)
+ epdconfig.spi_writebyte([data])
+ epdconfig.digital_write(self.cs_pin, 1)
+
+ def ReadBusy(self):
+ logger.debug("e-Paper busy H")
+ epdconfig.delay_ms(100)
+ while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
+ epdconfig.delay_ms(5)
+ logger.debug("e-Paper busy release")
+
+ def SetWindow(self):
+ self.send_command(0x61) # SET_RAM_X_ADDRESS_START_END_POSITION
+ # x point must be the multiple of 8 or the last 3 bits will be ignored
+ self.send_data(self.Source_BITS / 256)
+ self.send_data(self.Source_BITS % 256)
+ self.send_data(self.Gate_BITS / 256)
+ self.send_data(self.Gate_BITS % 256)
+
+ def TurnOnDisplay(self):
+ self.send_command(0x12) # DISPLAY_REFRESH
+ self.send_data(0X00)
+ self.ReadBusy()
+
+ def init(self):
+ if (epdconfig.module_init() != 0):
+ return -1
+ # EPD hardware init start
+
+ self.reset()
+
+ self.ReadBusy()
+ self.send_command(0x4D)
+ self.send_data(0x78)
+
+ self.send_command(0x00)
+ self.send_data(0x0F)
+ self.send_data(0x29)
+
+ self.send_command(0x01)
+ self.send_data(0x07)
+ self.send_data(0x00)
+
+ self.send_command(0x03)
+ self.send_data(0x10)
+ self.send_data(0x54)
+ self.send_data(0x44)
+
+ self.send_command(0x06)
+ self.send_data(0x05)
+ self.send_data(0x00)
+ self.send_data(0x3F)
+ self.send_data(0x0A)
+ self.send_data(0x25)
+ self.send_data(0x12)
+ self.send_data(0x1A)
+
+ self.send_command(0x50)
+ self.send_data(0x37)
+
+ self.send_command(0x60)
+ self.send_data(0x02)
+ self.send_data(0x02)
+
+ self.SetWindow()
+
+ self.send_command(0xE7)
+ self.send_data(0x1C)
+
+ self.send_command(0xE3)
+ self.send_data(0x22)
+
+ self.send_command(0xB4)
+ self.send_data(0xD0)
+ self.send_command(0xB5)
+ self.send_data(0x03)
+
+ self.send_command(0xE9)
+ self.send_data(0x01)
+
+ self.send_command(0x30)
+ self.send_data(0x08)
+
+ self.send_command(0x04)
+ self.ReadBusy()
+ return 0
+
+ def getbuffer(self, image):
+ # Create a pallette with the 4 colors supported by the panel
+ pal_image = Image.new("P", (1, 1))
+ pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252)
+
+ # Check if we need to rotate the image
+ imwidth, imheight = image.size
+ if (imwidth == self.width and imheight == self.height):
+ image_temp = image
+ elif (imwidth == self.height and imheight == self.width):
+ image_temp = image.rotate(90, expand=True)
+ else:
+ logger.warning(
+ "Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height))
+
+ # Convert the soruce image to the 4 colors, dithering if needed
+ image_4color = image_temp.convert("RGB").quantize(palette=pal_image)
+ buf_4color = bytearray(image_4color.tobytes('raw'))
+
+ # into a single byte to transfer to the panel
+ if self.width % 4 == 0:
+ Width = self.width // 4
+ else:
+ Width = self.width // 4 + 1
+ Height = self.height
+ buf = [0x00] * int(Width * Height)
+ idx = 0
+ for j in range(0, Height):
+ for i in range(0, Width):
+ if i == Width - 1:
+ buf[i + j * Width] = (buf_4color[idx] << 6) + (buf_4color[idx + 1] << 4)
+ idx = idx + 2
+ else:
+ buf[i + j * Width] = (buf_4color[idx] << 6) + (buf_4color[idx + 1] << 4) + (
+ buf_4color[idx + 2] << 2) + buf_4color[idx + 3]
+ idx = idx + 4
+ return buf
+
+ def display(self, image):
+ if self.width % 4 == 0:
+ Width = self.width // 4
+ else:
+ Width = self.width // 4 + 1
+ Height = self.height
+
+ self.send_command(0x10)
+ for j in range(0, Height):
+ for i in range(0, self.Source_BITS // 4):
+ if i < 31:
+ self.send_data(image[i + j * Width])
+ else:
+ self.send_data(0x00)
+
+ self.TurnOnDisplay()
+
+ def Clear(self, color=0x55):
+ Width = self.Source_BITS // 4
+ Height = self.height
+
+ self.send_command(0x10)
+ for j in range(0, Height):
+ for i in range(0, Width):
+ self.send_data(color)
+ self.TurnOnDisplay()
+
+ def sleep(self):
+ self.send_command(0x02) # POWER_OFF
+ self.ReadBusy()
+ epdconfig.delay_ms(100)
+
+ self.send_command(0x07) # DEEP_SLEEP
+ self.send_data(0XA5)
+
+ epdconfig.delay_ms(2000)
+ epdconfig.module_exit()
+### END OF FILE ###
diff --git a/pwnagotchi/ui/hw/libs/weact/epdconfig.py b/pwnagotchi/ui/hw/libs/weact/epdconfig.py
new file mode 100644
index 00000000..72dfd076
--- /dev/null
+++ b/pwnagotchi/ui/hw/libs/weact/epdconfig.py
@@ -0,0 +1,243 @@
+# /*****************************************************************************
+# * | File : epdconfig.py
+# * | Author : Waveshare team
+# * | Function : Hardware underlying interface
+# * | Info :
+# *----------------
+# * | This version: V1.2
+# * | Date : 2022-10-29
+# * | Info :
+# ******************************************************************************
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documnetation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+import os
+import logging
+import sys
+import time
+
+logger = logging.getLogger(__name__)
+
+
+class RaspberryPi:
+ # Pin definition
+ RST_PIN = 17
+ DC_PIN = 25
+ CS_PIN = 8
+ BUSY_PIN = 24
+ PWR_PIN = 18
+
+ def __init__(self):
+ import spidev
+ import RPi.GPIO
+
+ self.GPIO = RPi.GPIO
+ self.SPI = spidev.SpiDev()
+
+ def digital_write(self, pin, value):
+ self.GPIO.output(pin, value)
+
+ def digital_read(self, pin):
+ return self.GPIO.input(pin)
+
+ def delay_ms(self, delaytime):
+ time.sleep(delaytime / 1000.0)
+
+ def spi_writebyte(self, data):
+ self.SPI.writebytes(data)
+
+ def spi_writebyte2(self, data):
+ self.SPI.writebytes2(data)
+
+ def module_init(self):
+ self.GPIO.setmode(self.GPIO.BCM)
+ self.GPIO.setwarnings(False)
+ self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.PWR_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
+
+ self.GPIO.output(self.PWR_PIN, 1)
+
+ # SPI device, bus = 0, device = 0
+ self.SPI.open(0, 0)
+ self.SPI.max_speed_hz = 4000000
+ self.SPI.mode = 0b00
+ return 0
+
+ def module_exit(self):
+ logger.debug("spi end")
+ self.SPI.close()
+
+ logger.debug("close 5V, Module enters 0 power consumption ...")
+ self.GPIO.output(self.RST_PIN, 0)
+ self.GPIO.output(self.DC_PIN, 0)
+ self.GPIO.output(self.PWR_PIN, 0)
+
+ self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN, self.PWR_PIN])
+
+
+class JetsonNano:
+ # Pin definition
+ RST_PIN = 17
+ DC_PIN = 25
+ CS_PIN = 8
+ BUSY_PIN = 24
+ PWR_PIN = 18
+
+ def __init__(self):
+ import ctypes
+ find_dirs = [
+ os.path.dirname(os.path.realpath(__file__)),
+ '/usr/local/lib',
+ '/usr/lib',
+ ]
+ self.SPI = None
+ for find_dir in find_dirs:
+ so_filename = os.path.join(find_dir, 'sysfs_software_spi.so')
+ if os.path.exists(so_filename):
+ self.SPI = ctypes.cdll.LoadLibrary(so_filename)
+ break
+ if self.SPI is None:
+ raise RuntimeError('Cannot find sysfs_software_spi.so')
+
+ import Jetson.GPIO
+ self.GPIO = Jetson.GPIO
+
+ def digital_write(self, pin, value):
+ self.GPIO.output(pin, value)
+
+ def digital_read(self, pin):
+ return self.GPIO.input(self.BUSY_PIN)
+
+ def delay_ms(self, delaytime):
+ time.sleep(delaytime / 1000.0)
+
+ def spi_writebyte(self, data):
+ self.SPI.SYSFS_software_spi_transfer(data[0])
+
+ def spi_writebyte2(self, data):
+ for i in range(len(data)):
+ self.SPI.SYSFS_software_spi_transfer(data[i])
+
+ def module_init(self):
+ self.GPIO.setmode(self.GPIO.BCM)
+ self.GPIO.setwarnings(False)
+ self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.PWR_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
+
+ self.GPIO.output(self.PWR_PIN, 1)
+
+ self.SPI.SYSFS_software_spi_begin()
+ return 0
+
+ def module_exit(self):
+ logger.debug("spi end")
+ self.SPI.SYSFS_software_spi_end()
+
+ logger.debug("close 5V, Module enters 0 power consumption ...")
+ self.GPIO.output(self.RST_PIN, 0)
+ self.GPIO.output(self.DC_PIN, 0)
+ self.GPIO.output(self.PWR_PIN, 0)
+
+ self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN, self.PWR_PIN])
+
+
+class SunriseX3:
+ # Pin definition
+ RST_PIN = 17
+ DC_PIN = 25
+ CS_PIN = 8
+ BUSY_PIN = 24
+ PWR_PIN = 18
+ Flag = 0
+
+ def __init__(self):
+ import spidev
+ import Hobot.GPIO
+
+ self.GPIO = Hobot.GPIO
+ self.SPI = spidev.SpiDev()
+
+ def digital_write(self, pin, value):
+ self.GPIO.output(pin, value)
+
+ def digital_read(self, pin):
+ return self.GPIO.input(pin)
+
+ def delay_ms(self, delaytime):
+ time.sleep(delaytime / 1000.0)
+
+ def spi_writebyte(self, data):
+ self.SPI.writebytes(data)
+
+ def spi_writebyte2(self, data):
+ # for i in range(len(data)):
+ # self.SPI.writebytes([data[i]])
+ self.SPI.xfer3(data)
+
+ def module_init(self):
+ if self.Flag == 0:
+ self.Flag = 1
+ self.GPIO.setmode(self.GPIO.BCM)
+ self.GPIO.setwarnings(False)
+ self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.PWR_PIN, self.GPIO.OUT)
+ self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
+
+ self.GPIO.output(self.PWR_PIN, 1)
+
+ # SPI device, bus = 0, device = 0
+ self.SPI.open(2, 0)
+ self.SPI.max_speed_hz = 4000000
+ self.SPI.mode = 0b00
+ return 0
+ else:
+ return 0
+
+ def module_exit(self):
+ logger.debug("spi end")
+ self.SPI.close()
+
+ logger.debug("close 5V, Module enters 0 power consumption ...")
+ self.Flag = 0
+ self.GPIO.output(self.RST_PIN, 0)
+ self.GPIO.output(self.DC_PIN, 0)
+ self.GPIO.output(self.PWR_PIN, 0)
+
+ self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN], self.PWR_PIN)
+
+
+if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'):
+ implementation = RaspberryPi()
+elif os.path.exists('/sys/bus/platform/drivers/gpio-x3'):
+ implementation = SunriseX3()
+else:
+ implementation = RaspberryPi()
+
+for func in [x for x in dir(implementation) if not x.startswith('_')]:
+ setattr(sys.modules[__name__], func, getattr(implementation, func))
+
+### END OF FILE ###
\ No newline at end of file
diff --git a/pwnagotchi/ui/hw/libs/weact/v2in9/epd2in9.py b/pwnagotchi/ui/hw/libs/weact/v2in9/epd2in9.py
new file mode 100644
index 00000000..e5987bc7
--- /dev/null
+++ b/pwnagotchi/ui/hw/libs/weact/v2in9/epd2in9.py
@@ -0,0 +1,374 @@
+import logging
+from .. import epdconfig
+
+# Display resolution
+EPD_WIDTH = 296
+EPD_HEIGHT = 128
+
+logger = logging.getLogger(__name__)
+
+
+class EPD:
+ def __init__(self):
+ self.reset_pin = epdconfig.RST_PIN
+ self.dc_pin = epdconfig.DC_PIN
+ self.busy_pin = epdconfig.BUSY_PIN
+ self.cs_pin = epdconfig.CS_PIN
+ self.width = EPD_WIDTH
+ self.height = EPD_HEIGHT
+
+ lut_partial_update = [
+ 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
+ 0x22, 0x17, 0x41, 0x00, 0x32, 0x36,
+ ]
+
+ lut_full_update = [
+ 0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xF, 0x0, 0x0, 0xF, 0x0, 0x0, 0x2,
+ 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
+ 0x22, 0x17, 0x41, 0x0, 0x32, 0x36,
+ ]
+
+ '''
+ function :Hardware reset
+ parameter:
+ '''
+
+ def reset(self):
+ epdconfig.digital_write(self.reset_pin, 1)
+ epdconfig.delay_ms(20)
+ epdconfig.digital_write(self.reset_pin, 0)
+ epdconfig.delay_ms(2)
+ epdconfig.digital_write(self.reset_pin, 1)
+ epdconfig.delay_ms(20)
+
+ '''
+ function :send command
+ parameter:
+ command : Command register
+ '''
+
+ def send_command(self, command):
+ epdconfig.digital_write(self.dc_pin, 0)
+ epdconfig.digital_write(self.cs_pin, 0)
+ epdconfig.spi_writebyte([command])
+ epdconfig.digital_write(self.cs_pin, 1)
+
+ '''
+ function :send data
+ parameter:
+ data : Write data
+ '''
+
+ def send_data(self, data):
+ epdconfig.digital_write(self.dc_pin, 1)
+ epdconfig.digital_write(self.cs_pin, 0)
+ epdconfig.spi_writebyte([data])
+ epdconfig.digital_write(self.cs_pin, 1)
+
+ # send a lot of data
+ def send_data2(self, data):
+ epdconfig.digital_write(self.dc_pin, 1)
+ epdconfig.digital_write(self.cs_pin, 0)
+ epdconfig.spi_writebyte2(data)
+ epdconfig.digital_write(self.cs_pin, 1)
+
+ '''
+ function :Wait until the busy_pin goes LOW
+ parameter:
+ '''
+
+ def ReadBusy(self):
+ logger.debug("e-Paper busy")
+ while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy
+ epdconfig.delay_ms(10)
+ logger.debug("e-Paper busy release")
+
+ '''
+ function : Turn On Display
+ parameter:
+ '''
+
+ def TurnOnDisplay(self):
+ self.send_command(0x22) # Display Update Control
+ self.send_data(0xC7)
+ self.send_command(0x20) # Activate Display Update Sequence
+ self.ReadBusy()
+
+ '''
+ function : Turn On Display Part
+ parameter:
+ '''
+
+ def TurnOnDisplayPart(self):
+ self.send_command(0x22) # Display Update Control
+ self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf
+ self.send_command(0x20) # Activate Display Update Sequence
+ self.ReadBusy()
+
+ '''
+ function : Set lut
+ parameter:
+ lut : lut data
+ '''
+
+ def Lut(self, lut):
+ self.send_command(0x32)
+ for i in range(0, 153):
+ self.send_data(lut[i])
+ self.ReadBusy()
+
+ '''
+ function : Send lut data and configuration
+ parameter:
+ lut : lut data
+ '''
+
+ def SetLut(self, lut):
+ self.Lut(lut)
+ self.send_command(0x3f)
+ self.send_data(lut[153])
+ self.send_command(0x03) # gate voltage
+ self.send_data(lut[154])
+ self.send_command(0x04) # source voltage
+ self.send_data(lut[155]) # VSH
+ self.send_data(lut[156]) # VSH2
+ self.send_data(lut[157]) # VSL
+ self.send_command(0x2c) # VCOM
+ self.send_data(lut[158])
+
+ '''
+ function : Setting the display window
+ parameter:
+ xstart : X-axis starting position
+ ystart : Y-axis starting position
+ xend : End position of X-axis
+ yend : End position of Y-axis
+ '''
+
+ def SetWindow(self, x_start, y_start, x_end, y_end):
+ self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION
+ # 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_end >> 3) & 0xFF)
+
+ self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION
+ self.send_data(y_start & 0xFF)
+ self.send_data((y_start >> 8) & 0xFF)
+ self.send_data(y_end & 0xFF)
+ self.send_data((y_end >> 8) & 0xFF)
+
+ '''
+ function : Set Cursor
+ parameter:
+ x : X-axis starting position
+ y : Y-axis starting position
+ '''
+
+ def SetCursor(self, x, y):
+ self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER
+ # x point must be the multiple of 8 or the last 3 bits will be ignored
+ self.send_data(x & 0xFF)
+
+ self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER
+ self.send_data(y & 0xFF)
+ self.send_data((y >> 8) & 0xFF)
+
+ '''
+ function : Initialize the e-Paper register
+ parameter:
+ '''
+
+ def init(self):
+ if (epdconfig.module_init() != 0):
+ return -1
+ # EPD hardware init start
+ self.reset()
+
+ self.ReadBusy()
+ self.send_command(0x12) # SWRESET
+ self.ReadBusy()
+
+ self.send_command(0x01) # Driver output control
+ self.send_data(0xf9)
+ self.send_data(0x00)
+ self.send_data(0x00)
+
+ self.send_command(0x11) # data entry mode
+ self.send_data(0x03)
+
+ self.SetWindow(0, 0, self.width - 1, self.height - 1)
+ self.SetCursor(0, 0)
+
+ self.send_command(0x3c)
+ self.send_data(0x05)
+
+ self.send_command(0x21) # Display update control
+ self.send_data(0x00)
+ self.send_data(0x80)
+
+ self.send_command(0x18)
+ self.send_data(0x80)
+
+ self.ReadBusy()
+
+ self.SetLut(self.lut_full_update)
+ return 0
+
+ '''
+ function : Display images
+ parameter:
+ image : Image data
+ '''
+
+ def getbuffer(self, image):
+ img = image
+ imwidth, imheight = img.size
+ if (imwidth == self.width and imheight == self.height):
+ img = img.convert('1')
+ elif (imwidth == self.height and imheight == self.width):
+ # image has correct dimensions, but needs to be rotated
+ img = img.rotate(90, expand=True).convert('1')
+ else:
+ logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height))
+ # return a blank buffer
+ return [0x00] * (int(self.width / 8) * self.height)
+
+ buf = bytearray(img.tobytes('raw'))
+ return buf
+
+ '''
+ function : Sends the image buffer in RAM to e-Paper and displays
+ parameter:
+ image : Image data
+ '''
+
+ def display(self, image):
+ if self.width % 8 == 0:
+ linewidth = int(self.width / 8)
+ else:
+ linewidth = int(self.width / 8) + 1
+
+ self.send_command(0x24)
+ for j in range(0, self.height):
+ for i in range(0, linewidth):
+ self.send_data(image[i + j * linewidth])
+ self.TurnOnDisplay()
+
+ '''
+ function : Sends the image buffer in RAM to e-Paper and partial refresh
+ parameter:
+ image : Image data
+ '''
+
+ def displayPartial(self, image):
+ epdconfig.digital_write(self.reset_pin, 0)
+ epdconfig.delay_ms(1)
+ epdconfig.digital_write(self.reset_pin, 1)
+
+ self.SetLut(self.lut_partial_update)
+ self.send_command(0x37)
+ self.send_data(0x00)
+ self.send_data(0x00)
+ self.send_data(0x00)
+ self.send_data(0x00)
+ self.send_data(0x00)
+ self.send_data(0x40)
+ self.send_data(0x00)
+ self.send_data(0x00)
+ self.send_data(0x00)
+ self.send_data(0x00)
+
+ self.send_command(0x3C) # BorderWavefrom
+ self.send_data(0x80)
+
+ self.send_command(0x22)
+ self.send_data(0xC0)
+ self.send_command(0x20)
+ self.ReadBusy()
+
+ self.SetWindow(0, 0, self.width - 1, self.height - 1)
+ self.SetCursor(0, 0)
+
+ self.send_command(0x24) # WRITE_RAM
+ # for j in range(0, self.height):
+ # for i in range(0, linewidth):
+ # self.send_data(image[i + j * linewidth])
+ self.send_data2(image)
+ self.TurnOnDisplayPart()
+
+ '''
+ function : Refresh a base image
+ parameter:
+ image : Image data
+ '''
+
+ def displayPartBaseImage(self, image):
+ self.send_command(0x24)
+ self.send_data2(image)
+
+ self.send_command(0x26)
+ self.send_data2(image)
+ self.TurnOnDisplay()
+
+ '''
+ function : Clear screen
+ parameter:
+ '''
+
+ def Clear(self, color=0xFF):
+ if self.width % 8 == 0:
+ linewidth = int(self.width / 8)
+ else:
+ linewidth = int(self.width / 8) + 1
+ # logger.debug(linewidth)
+
+ self.send_command(0x24)
+ self.send_data2([color] * int(self.height * linewidth))
+ self.TurnOnDisplay()
+
+ '''
+ function : Enter sleep mode
+ parameter:
+ '''
+
+ def sleep(self):
+ self.send_command(0x10) # enter deep sleep
+ self.send_data(0x01)
+
+ epdconfig.delay_ms(2000)
+ epdconfig.module_exit()
+
+### END OF FILE ###
diff --git a/pwnagotchi/ui/hw/waveshare13in3k.py b/pwnagotchi/ui/hw/waveshare13in3k.py
index b57f8f29..5b464057 100644
--- a/pwnagotchi/ui/hw/waveshare13in3k.py
+++ b/pwnagotchi/ui/hw/waveshare13in3k.py
@@ -38,7 +38,8 @@ class Waveshare13in3k(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare1in02.py b/pwnagotchi/ui/hw/waveshare1in02.py
index 3cfef7a3..e5d5cee1 100644
--- a/pwnagotchi/ui/hw/waveshare1in02.py
+++ b/pwnagotchi/ui/hw/waveshare1in02.py
@@ -38,7 +38,8 @@ class Waveshare1in02(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare1in54.py b/pwnagotchi/ui/hw/waveshare1in54.py
index 7c04a2db..02a9e576 100644
--- a/pwnagotchi/ui/hw/waveshare1in54.py
+++ b/pwnagotchi/ui/hw/waveshare1in54.py
@@ -34,12 +34,12 @@ class Waveshare154(DisplayImpl):
logging.info("initializing waveshare v1in54 display")
from pwnagotchi.ui.hw.libs.waveshare.v1in54.epd1in54 import EPD
self._display = EPD()
- self._display.init(0x00)
+ self._display.init(self._display.lut_partial_update)
self._display.Clear()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
- self._display.display(buf, None)
+ self._display.display(buf)
def clear(self):
# pass
diff --git a/pwnagotchi/ui/hw/waveshare1in54_V2.py b/pwnagotchi/ui/hw/waveshare1in54_V2.py
index e50cf500..02cd85a3 100644
--- a/pwnagotchi/ui/hw/waveshare1in54_V2.py
+++ b/pwnagotchi/ui/hw/waveshare1in54_V2.py
@@ -31,15 +31,27 @@ class Waveshare154V2(DisplayImpl):
return self._layout
def initialize(self):
- logging.info("initializing waveshare v1in54_v2 display")
+ logging.info("initializing waveshare1in54_v2 display")
from pwnagotchi.ui.hw.libs.waveshare.v1in54_v2.epd1in54_V2 import EPD
- self._display = EPD()
- self._display.init(False)
- self._display.Clear()
+ try:
+ # Double initialization is a workaround for the display not working after a reboot, or mirrored/flipped screen
+ self._display = EPD()
+ self._display.init(0)
+ self.clear()
+ self._display.init(1)
+ self.clear()
+ except Exception as e:
+ logging.error(f"failed to initialize waveshare1in54_v2 display: {e}")
def render(self, canvas):
- buf = self._display.getbuffer(canvas)
- self._display.display(buf, None)
+ try:
+ buf = self._display.getbuffer(canvas)
+ self._display.displayPart(buf)
+ except Exception as e:
+ logging.error(f"failed to render to waveshare1in54_v2 display: {e}")
def clear(self):
- self._display.Clear()
+ try:
+ self._display.Clear(0xFF)
+ except Exception as e:
+ logging.error(f"failed to clear waveshare1in54_v2 display: {e}")
diff --git a/pwnagotchi/ui/hw/waveshare1in54c.py b/pwnagotchi/ui/hw/waveshare1in54c.py
index e9fd2c27..8b206f7a 100644
--- a/pwnagotchi/ui/hw/waveshare1in54c.py
+++ b/pwnagotchi/ui/hw/waveshare1in54c.py
@@ -38,7 +38,8 @@ class Waveshare1in54c(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare1in64g.py b/pwnagotchi/ui/hw/waveshare1in64g.py
index 5f994682..c84cac6e 100644
--- a/pwnagotchi/ui/hw/waveshare1in64g.py
+++ b/pwnagotchi/ui/hw/waveshare1in64g.py
@@ -38,7 +38,8 @@ class Waveshare1in64g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare2in13.py b/pwnagotchi/ui/hw/waveshare2in13.py
index de6c29b7..b720b555 100644
--- a/pwnagotchi/ui/hw/waveshare2in13.py
+++ b/pwnagotchi/ui/hw/waveshare2in13.py
@@ -9,81 +9,38 @@ class WaveshareV1(DisplayImpl):
super(WaveshareV1, self).__init__(config, 'waveshare_1')
def layout(self):
- if self.config['color'] == 'black':
- fonts.setup(10, 9, 10, 35, 25, 9)
- self._layout['width'] = 250
- self._layout['height'] = 122
- self._layout['face'] = (0, 40)
- self._layout['name'] = (5, 20)
- self._layout['channel'] = (0, 0)
- self._layout['aps'] = (28, 0)
- self._layout['uptime'] = (185, 0)
- self._layout['line1'] = [0, 14, 250, 14]
- self._layout['line2'] = [0, 108, 250, 108]
- self._layout['friend_face'] = (0, 92)
- self._layout['friend_name'] = (40, 94)
- self._layout['shakes'] = (0, 109)
- self._layout['mode'] = (225, 109)
- self._layout['status'] = {
- 'pos': (125, 20),
- 'font': fonts.status_font(fonts.Medium),
- 'max': 20
- }
- else:
- fonts.setup(10, 8, 10, 25, 25, 9)
- self._layout['width'] = 212
- self._layout['height'] = 104
- self._layout['face'] = (0, 26)
- self._layout['name'] = (5, 15)
- self._layout['channel'] = (0, 0)
- self._layout['aps'] = (28, 0)
- self._layout['uptime'] = (147, 0)
- self._layout['line1'] = [0, 12, 212, 12]
- self._layout['line2'] = [0, 92, 212, 92]
- self._layout['friend_face'] = (0, 76)
- self._layout['friend_name'] = (40, 78)
- self._layout['shakes'] = (0, 93)
- self._layout['mode'] = (187, 93)
- self._layout['status'] = {
- 'pos': (91, 15),
- 'font': fonts.status_font(fonts.Medium),
- 'max': 20
- }
+ fonts.setup(10, 8, 10, 35, 25, 9)
+ self._layout['width'] = 250
+ self._layout['height'] = 122
+ self._layout['face'] = (0, 40)
+ self._layout['name'] = (5, 20)
+ self._layout['channel'] = (0, 0)
+ self._layout['aps'] = (28, 0)
+ self._layout['uptime'] = (185, 0)
+ self._layout['line1'] = [0, 14, 250, 14]
+ self._layout['line2'] = [0, 108, 250, 108]
+ self._layout['friend_face'] = (0, 92)
+ self._layout['friend_name'] = (40, 94)
+ self._layout['shakes'] = (0, 109)
+ self._layout['mode'] = (225, 109)
+ self._layout['status'] = {
+ 'pos': (125, 20),
+ 'font': fonts.status_font(fonts.Medium),
+ 'max': 20
+ }
return self._layout
def initialize(self):
- if self.config['color'] == 'black':
- logging.info("initializing waveshare v2in13_V1 display in monochromatic mode")
- from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13 import EPD
- self._display = EPD()
- self._display.init(self._display.lut_full_update)
- self._display.Clear(0xFF)
- self._display.init(self._display.lut_partial_update)
- elif self.config['color'] == 'fastAndFurious':
- logging.info("initializing waveshare v2in13_V1 3-color display in FAST MODE")
- logging.info("THIS MAY BE POTENTIALLY DANGEROUS. NO WARRANTY IS PROVIDED")
- logging.info("USE THIS DISPLAY IN THIS MODE AT YOUR OWN RISK")
- from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13bcFAST import EPD
- self._display = EPD()
- self._display.init()
- self._display.Clear()
- else:
- logging.info("initializing waveshare v2in13_V1 display 3-color mode")
- from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13bc import EPD
- self._display = EPD()
- self._display.init()
- self._display.Clear()
+ logging.info("initializing waveshare v2in13_V1 display in monochromatic mode")
+ from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13 import EPD
+ self._display = EPD()
+ self._display.init(self._display.lut_full_update)
+ self._display.Clear(0xFF)
+ self._display.init(self._display.lut_partial_update)
def render(self, canvas):
- if self.config['color'] == 'black':
- buf = self._display.getbuffer(canvas)
- self._display.display(buf)
- elif self.config['color'] == 'fastAndFurious':
- buf_black = self._display.getbuffer(canvas)
- self._display.DisplayPartial(buf_black)
- else:
- buf_black = self._display.getbuffer(canvas)
- self._display.displayBlack(buf_black)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear(0xff)
diff --git a/pwnagotchi/ui/hw/waveshare2in13_V2.py b/pwnagotchi/ui/hw/waveshare2in13_V2.py
index 76c48ad0..48042746 100644
--- a/pwnagotchi/ui/hw/waveshare2in13_V2.py
+++ b/pwnagotchi/ui/hw/waveshare2in13_V2.py
@@ -9,47 +9,25 @@ class WaveshareV2(DisplayImpl):
super(WaveshareV2, self).__init__(config, 'waveshare_2')
def layout(self):
- if self.config['color'] == 'black':
- fonts.setup(10, 9, 10, 35, 25, 9)
- self._layout['width'] = 250
- self._layout['height'] = 122
- self._layout['face'] = (0, 40)
- self._layout['name'] = (5, 20)
- self._layout['channel'] = (0, 0)
- self._layout['aps'] = (28, 0)
- self._layout['uptime'] = (185, 0)
- self._layout['line1'] = [0, 14, 250, 14]
- self._layout['line2'] = [0, 108, 250, 108]
- self._layout['friend_face'] = (0, 92)
- self._layout['friend_name'] = (40, 94)
- self._layout['shakes'] = (0, 109)
- self._layout['mode'] = (225, 109)
- self._layout['status'] = {
- 'pos': (125, 20),
- 'font': fonts.status_font(fonts.Medium),
- 'max': 20
- }
- else:
- fonts.setup(10, 8, 10, 25, 25, 9)
- self._layout['width'] = 212
- self._layout['height'] = 104
- self._layout['face'] = (0, 26)
- self._layout['name'] = (5, 15)
- self._layout['channel'] = (0, 0)
- self._layout['aps'] = (28, 0)
- self._layout['status'] = (91, 15)
- self._layout['uptime'] = (147, 0)
- self._layout['line1'] = [0, 12, 212, 12]
- self._layout['line2'] = [0, 92, 212, 92]
- self._layout['friend_face'] = (0, 76)
- self._layout['friend_name'] = (40, 78)
- self._layout['shakes'] = (0, 93)
- self._layout['mode'] = (187, 93)
- self._layout['status'] = {
- 'pos': (125, 20),
- 'font': fonts.status_font(fonts.Medium),
- 'max': 14
- }
+ fonts.setup(10, 8, 10, 35, 25, 9)
+ self._layout['width'] = 250
+ self._layout['height'] = 122
+ self._layout['face'] = (0, 40)
+ self._layout['name'] = (5, 20)
+ self._layout['channel'] = (0, 0)
+ self._layout['aps'] = (28, 0)
+ self._layout['uptime'] = (185, 0)
+ self._layout['line1'] = [0, 14, 250, 14]
+ self._layout['line2'] = [0, 108, 250, 108]
+ self._layout['friend_face'] = (0, 92)
+ self._layout['friend_name'] = (40, 94)
+ self._layout['shakes'] = (0, 109)
+ self._layout['mode'] = (225, 109)
+ self._layout['status'] = {
+ 'pos': (125, 20),
+ 'font': fonts.status_font(fonts.Medium),
+ 'max': 20
+ }
return self._layout
def initialize(self):
diff --git a/pwnagotchi/ui/hw/waveshare2in13b_V3.py b/pwnagotchi/ui/hw/waveshare2in13b_V3.py
index 9c13436d..c5c96453 100644
--- a/pwnagotchi/ui/hw/waveshare2in13b_V3.py
+++ b/pwnagotchi/ui/hw/waveshare2in13b_V3.py
@@ -10,47 +10,25 @@ class Waveshare2in13bV3(DisplayImpl):
super(Waveshare2in13bV3, self).__init__(config, 'waveshare2in13b_v3')
def layout(self):
- if self.config['color'] == 'black':
- fonts.setup(10, 9, 10, 35, 25, 9)
- self._layout['width'] = 250
- self._layout['height'] = 122
- self._layout['face'] = (0, 40)
- self._layout['name'] = (5, 20)
- self._layout['channel'] = (0, 0)
- self._layout['aps'] = (28, 0)
- self._layout['uptime'] = (185, 0)
- self._layout['line1'] = [0, 14, 250, 14]
- self._layout['line2'] = [0, 108, 250, 108]
- self._layout['friend_face'] = (0, 92)
- self._layout['friend_name'] = (40, 94)
- self._layout['shakes'] = (0, 109)
- self._layout['mode'] = (225, 109)
- self._layout['status'] = {
- 'pos': (125, 20),
- 'font': fonts.status_font(fonts.Medium),
- 'max': 20
- }
- else:
- fonts.setup(10, 8, 10, 25, 25, 9)
- self._layout['width'] = 212
- self._layout['height'] = 104
- self._layout['face'] = (0, 26)
- self._layout['name'] = (5, 15)
- self._layout['channel'] = (0, 0)
- self._layout['aps'] = (28, 0)
- self._layout['status'] = (91, 15)
- self._layout['uptime'] = (147, 0)
- self._layout['line1'] = [0, 12, 212, 12]
- self._layout['line2'] = [0, 92, 212, 92]
- self._layout['friend_face'] = (0, 76)
- self._layout['friend_name'] = (40, 78)
- self._layout['shakes'] = (0, 93)
- self._layout['mode'] = (187, 93)
- self._layout['status'] = {
- 'pos': (125, 20),
- 'font': fonts.status_font(fonts.Medium),
- 'max': 14
- }
+ fonts.setup(10, 9, 10, 35, 25, 9)
+ self._layout['width'] = 250
+ self._layout['height'] = 122
+ self._layout['face'] = (0, 40)
+ self._layout['name'] = (5, 20)
+ self._layout['channel'] = (0, 0)
+ self._layout['aps'] = (28, 0)
+ self._layout['uptime'] = (185, 0)
+ self._layout['line1'] = [0, 14, 250, 14]
+ self._layout['line2'] = [0, 108, 250, 108]
+ self._layout['friend_face'] = (0, 92)
+ self._layout['friend_name'] = (40, 94)
+ self._layout['shakes'] = (0, 109)
+ self._layout['mode'] = (225, 109)
+ self._layout['status'] = {
+ 'pos': (125, 20),
+ 'font': fonts.status_font(fonts.Medium),
+ 'max': 20
+ }
return self._layout
def initialize(self):
diff --git a/pwnagotchi/ui/hw/waveshare2in13b_V4.py b/pwnagotchi/ui/hw/waveshare2in13b_V4.py
index 1b190d14..7cc5a543 100644
--- a/pwnagotchi/ui/hw/waveshare2in13b_V4.py
+++ b/pwnagotchi/ui/hw/waveshare2in13b_V4.py
@@ -10,47 +10,25 @@ class Waveshare213bV4(DisplayImpl):
super(Waveshare213bV4, self).__init__(config, 'waveshare2in13b_v4')
def layout(self):
- if self.config['color'] == 'black':
- fonts.setup(10, 9, 10, 35, 25, 9)
- self._layout['width'] = 250
- self._layout['height'] = 122
- self._layout['face'] = (0, 40)
- self._layout['name'] = (5, 20)
- self._layout['channel'] = (0, 0)
- self._layout['aps'] = (28, 0)
- self._layout['uptime'] = (185, 0)
- self._layout['line1'] = [0, 14, 250, 14]
- self._layout['line2'] = [0, 108, 250, 108]
- self._layout['friend_face'] = (0, 92)
- self._layout['friend_name'] = (40, 94)
- self._layout['shakes'] = (0, 109)
- self._layout['mode'] = (225, 109)
- self._layout['status'] = {
- 'pos': (125, 20),
- 'font': fonts.status_font(fonts.Medium),
- 'max': 20
- }
- else:
- fonts.setup(10, 8, 10, 25, 25, 9)
- self._layout['width'] = 212
- self._layout['height'] = 104
- self._layout['face'] = (0, 26)
- self._layout['name'] = (5, 15)
- self._layout['channel'] = (0, 0)
- self._layout['aps'] = (28, 0)
- self._layout['status'] = (91, 15)
- self._layout['uptime'] = (147, 0)
- self._layout['line1'] = [0, 12, 212, 12]
- self._layout['line2'] = [0, 92, 212, 92]
- self._layout['friend_face'] = (0, 76)
- self._layout['friend_name'] = (40, 78)
- self._layout['shakes'] = (0, 93)
- self._layout['mode'] = (187, 93)
- self._layout['status'] = {
- 'pos': (125, 20),
- 'font': fonts.status_font(fonts.Medium),
- 'max': 14
- }
+ fonts.setup(10, 9, 10, 35, 25, 9)
+ self._layout['width'] = 250
+ self._layout['height'] = 122
+ self._layout['face'] = (0, 40)
+ self._layout['name'] = (5, 20)
+ self._layout['channel'] = (0, 0)
+ self._layout['aps'] = (28, 0)
+ self._layout['uptime'] = (185, 0)
+ self._layout['line1'] = [0, 14, 250, 14]
+ self._layout['line2'] = [0, 108, 250, 108]
+ self._layout['friend_face'] = (0, 92)
+ self._layout['friend_name'] = (40, 94)
+ self._layout['shakes'] = (0, 109)
+ self._layout['mode'] = (225, 109)
+ self._layout['status'] = {
+ 'pos': (125, 20),
+ 'font': fonts.status_font(fonts.Medium),
+ 'max': 20
+ }
return self._layout
def initialize(self):
diff --git a/pwnagotchi/ui/hw/waveshare2in23g.py b/pwnagotchi/ui/hw/waveshare2in23g.py
new file mode 100644
index 00000000..e69de29b
diff --git a/pwnagotchi/ui/hw/waveshare2in36g.py b/pwnagotchi/ui/hw/waveshare2in36g.py
index 8865368a..2157d1f8 100644
--- a/pwnagotchi/ui/hw/waveshare2in36g.py
+++ b/pwnagotchi/ui/hw/waveshare2in36g.py
@@ -38,7 +38,8 @@ class Waveshare2in36g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare2in66.py b/pwnagotchi/ui/hw/waveshare2in66.py
index bf3afd35..0d58667b 100644
--- a/pwnagotchi/ui/hw/waveshare2in66.py
+++ b/pwnagotchi/ui/hw/waveshare2in66.py
@@ -34,11 +34,12 @@ class Waveshare2in66(DisplayImpl):
logging.info("initializing waveshare 2.66 inch lcd display")
from pwnagotchi.ui.hw.libs.waveshare.v2in66.epd2in66 import EPD
self._display = EPD()
- self._display.init(0)
+ self._display.init(1)
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare2in66g.py b/pwnagotchi/ui/hw/waveshare2in66g.py
index c5089ed5..6279487e 100644
--- a/pwnagotchi/ui/hw/waveshare2in66g.py
+++ b/pwnagotchi/ui/hw/waveshare2in66g.py
@@ -38,7 +38,8 @@ class Waveshare2in66g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare2in7_V2.py b/pwnagotchi/ui/hw/waveshare2in7_V2.py
index 42272fb6..60679835 100644
--- a/pwnagotchi/ui/hw/waveshare2in7_V2.py
+++ b/pwnagotchi/ui/hw/waveshare2in7_V2.py
@@ -35,8 +35,6 @@ class Waveshare27inchV2(DisplayImpl):
from pwnagotchi.ui.hw.libs.waveshare.v2in7_v2.epd2in7_V2 import EPD
self._display = EPD()
self._display.init()
- # this must have changed by waveshare
- # remove the 0xFF(Clear(0xFF)) other wise it errors. can't pass oxff and self
self._display.Clear()
def render(self, canvas):
diff --git a/pwnagotchi/ui/hw/waveshare2in7b.py b/pwnagotchi/ui/hw/waveshare2in7b.py
index 4d559b6e..1dc96dbd 100644
--- a/pwnagotchi/ui/hw/waveshare2in7b.py
+++ b/pwnagotchi/ui/hw/waveshare2in7b.py
@@ -39,7 +39,7 @@ class Waveshare27b(DisplayImpl):
def render(self, canvas):
buf = self._display.getbuffer(canvas)
- self._display.display(buf)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear(0xff)
diff --git a/pwnagotchi/ui/hw/waveshare2in7b_V2.py b/pwnagotchi/ui/hw/waveshare2in7b_V2.py
index d734ec38..8d676ce3 100644
--- a/pwnagotchi/ui/hw/waveshare2in7b_V2.py
+++ b/pwnagotchi/ui/hw/waveshare2in7b_V2.py
@@ -39,7 +39,7 @@ class Waveshare27bV2(DisplayImpl):
def render(self, canvas):
buf = self._display.getbuffer(canvas)
- self._display.display(buf)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear(0xff)
diff --git a/pwnagotchi/ui/hw/waveshare2in9b_V3.py b/pwnagotchi/ui/hw/waveshare2in9b_V3.py
index 93e14381..7505e502 100644
--- a/pwnagotchi/ui/hw/waveshare2in9b_V3.py
+++ b/pwnagotchi/ui/hw/waveshare2in9b_V3.py
@@ -35,12 +35,12 @@ class Waveshare29bV3(DisplayImpl):
from pwnagotchi.ui.hw.libs.waveshare.v2in9b_v3.epd2in9b_V3 import EPD
self._display = EPD()
self._display.init()
- self._display.Clear(0xFF)
+ self._display.Clear()
self._display.init()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
- self._display.display(buf)
+ self._display.display(buf, None)
def clear(self):
- self._display.Clear(0xFF)
+ self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare2in9b_V4.py b/pwnagotchi/ui/hw/waveshare2in9b_V4.py
index 0a7e2ee4..ab79a369 100644
--- a/pwnagotchi/ui/hw/waveshare2in9b_V4.py
+++ b/pwnagotchi/ui/hw/waveshare2in9b_V4.py
@@ -35,12 +35,12 @@ class Waveshare29bV4(DisplayImpl):
from pwnagotchi.ui.hw.libs.waveshare.v2in9b_v4.epd2in9b_V4 import EPD
self._display = EPD()
self._display.init()
- self._display.Clear(0xFF)
+ self._display.Clear()
self._display.init()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
- self._display.display(buf)
+ self._display.display(buf, None)
def clear(self):
- self._display.Clear(0xFF)
+ self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare2in9bc.py b/pwnagotchi/ui/hw/waveshare2in9bc.py
index b9d6004d..804000b0 100644
--- a/pwnagotchi/ui/hw/waveshare2in9bc.py
+++ b/pwnagotchi/ui/hw/waveshare2in9bc.py
@@ -38,7 +38,8 @@ class Waveshare2in9bc(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare2in9d.py b/pwnagotchi/ui/hw/waveshare2in9d.py
index 1b52ab17..47742db9 100644
--- a/pwnagotchi/ui/hw/waveshare2in9d.py
+++ b/pwnagotchi/ui/hw/waveshare2in9d.py
@@ -38,7 +38,8 @@ class Waveshare2in9d(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare3in0g.py b/pwnagotchi/ui/hw/waveshare3in0g.py
index 46c40f3b..d8a79e28 100644
--- a/pwnagotchi/ui/hw/waveshare3in0g.py
+++ b/pwnagotchi/ui/hw/waveshare3in0g.py
@@ -38,7 +38,8 @@ class Waveshare3in0g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare3in52.py b/pwnagotchi/ui/hw/waveshare3in52.py
index 3489728a..a980e40e 100644
--- a/pwnagotchi/ui/hw/waveshare3in52.py
+++ b/pwnagotchi/ui/hw/waveshare3in52.py
@@ -9,22 +9,22 @@ class Waveshare3in52(DisplayImpl):
super(Waveshare3in52, self).__init__(config, 'waveshare3in52')
def layout(self):
- fonts.setup(16, 14, 16, 100, 31, 15)
+ fonts.setup(16, 14, 16, 50, 31, 15)
self._layout['width'] = 360
self._layout['height'] = 240
- self._layout['face'] = (0, 40)
- self._layout['name'] = (0, 0)
- self._layout['channel'] = (300, 0)
- self._layout['aps'] = (0, 220)
- self._layout['uptime'] = (120, 0)
+ self._layout['face'] = (0, 85)
+ self._layout['name'] = (10, 30)
+ self._layout['channel'] = (1, 4)
+ self._layout['aps'] = (45, 4)
+ self._layout['uptime'] = (250, 4)
self._layout['line1'] = [0, 24, 360, 24]
self._layout['line2'] = [0, 220, 360, 220]
- self._layout['friend_face'] = (0, 195)
- self._layout['friend_name'] = (0, 185)
- self._layout['shakes'] = (100, 220)
- self._layout['mode'] = (0,200)
+ self._layout['friend_face'] = (0, 180)
+ self._layout['friend_name'] = (0, 170)
+ self._layout['shakes'] = (1, 223)
+ self._layout['mode'] = (320, 222)
self._layout['status'] = {
- 'pos': (3, 170),
+ 'pos': (185, 50),
'font': fonts.status_font(fonts.Small),
'max': 100
}
diff --git a/pwnagotchi/ui/hw/waveshare3in7.py b/pwnagotchi/ui/hw/waveshare3in7.py
index c901eb52..32523c16 100644
--- a/pwnagotchi/ui/hw/waveshare3in7.py
+++ b/pwnagotchi/ui/hw/waveshare3in7.py
@@ -36,9 +36,11 @@ class Waveshare3in7(DisplayImpl):
self._display = EPD()
self._display.init(0)
self._display.Clear(0)
+ self._display.init(1) # 1Gray mode
def render(self, canvas):
- self._display.display_4Gray(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display_1Gray(buf)
def clear(self):
self._display.Clear(0)
diff --git a/pwnagotchi/ui/hw/waveshare4in01f.py b/pwnagotchi/ui/hw/waveshare4in01f.py
index e5167efe..6951567c 100644
--- a/pwnagotchi/ui/hw/waveshare4in01f.py
+++ b/pwnagotchi/ui/hw/waveshare4in01f.py
@@ -38,7 +38,8 @@ class Waveshare4in01f(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare4in2.py b/pwnagotchi/ui/hw/waveshare4in2.py
index 857dcc4c..c5dc8b71 100644
--- a/pwnagotchi/ui/hw/waveshare4in2.py
+++ b/pwnagotchi/ui/hw/waveshare4in2.py
@@ -38,7 +38,8 @@ class Waveshare4in2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare4in26.py b/pwnagotchi/ui/hw/waveshare4in26.py
index 41def32e..1abc92fc 100644
--- a/pwnagotchi/ui/hw/waveshare4in26.py
+++ b/pwnagotchi/ui/hw/waveshare4in26.py
@@ -38,7 +38,8 @@ class Waveshare4in26(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare4in2_V2.py b/pwnagotchi/ui/hw/waveshare4in2_V2.py
index 96e8d162..27ac21ec 100644
--- a/pwnagotchi/ui/hw/waveshare4in2_V2.py
+++ b/pwnagotchi/ui/hw/waveshare4in2_V2.py
@@ -38,7 +38,8 @@ class Waveshare4in2V2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare4in2b_V2.py b/pwnagotchi/ui/hw/waveshare4in2b_V2.py
index a6dd942d..9569cfce 100644
--- a/pwnagotchi/ui/hw/waveshare4in2b_V2.py
+++ b/pwnagotchi/ui/hw/waveshare4in2b_V2.py
@@ -38,7 +38,8 @@ class Waveshare4in2bV2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare4in2bc.py b/pwnagotchi/ui/hw/waveshare4in2bc.py
index 7dbf967c..623a603a 100644
--- a/pwnagotchi/ui/hw/waveshare4in2bc.py
+++ b/pwnagotchi/ui/hw/waveshare4in2bc.py
@@ -38,7 +38,8 @@ class Waveshare4in2bc(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare4in37g.py b/pwnagotchi/ui/hw/waveshare4in37g.py
index 2aac16e5..3bc58503 100644
--- a/pwnagotchi/ui/hw/waveshare4in37g.py
+++ b/pwnagotchi/ui/hw/waveshare4in37g.py
@@ -38,7 +38,8 @@ class Waveshare4in37g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare5in65f.py b/pwnagotchi/ui/hw/waveshare5in65f.py
index d1bc9082..a06acf5d 100644
--- a/pwnagotchi/ui/hw/waveshare5in65f.py
+++ b/pwnagotchi/ui/hw/waveshare5in65f.py
@@ -38,7 +38,8 @@ class Waveshare5in65f(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare5in83.py b/pwnagotchi/ui/hw/waveshare5in83.py
index 636bd1f4..aeadd918 100644
--- a/pwnagotchi/ui/hw/waveshare5in83.py
+++ b/pwnagotchi/ui/hw/waveshare5in83.py
@@ -38,7 +38,8 @@ class Waveshare5in83(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare5in83_V2.py b/pwnagotchi/ui/hw/waveshare5in83_V2.py
index dda0850d..c37c9eb2 100644
--- a/pwnagotchi/ui/hw/waveshare5in83_V2.py
+++ b/pwnagotchi/ui/hw/waveshare5in83_V2.py
@@ -38,7 +38,8 @@ class Waveshare5in83V2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare5in83b_V2.py b/pwnagotchi/ui/hw/waveshare5in83b_V2.py
index 9b116c00..79fe7599 100644
--- a/pwnagotchi/ui/hw/waveshare5in83b_V2.py
+++ b/pwnagotchi/ui/hw/waveshare5in83b_V2.py
@@ -38,7 +38,8 @@ class Waveshare5in83bV2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare5in83bc.py b/pwnagotchi/ui/hw/waveshare5in83bc.py
index 8ab1d044..342f59dd 100644
--- a/pwnagotchi/ui/hw/waveshare5in83bc.py
+++ b/pwnagotchi/ui/hw/waveshare5in83bc.py
@@ -38,7 +38,8 @@ class Waveshare5in83bc(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare7in3f.py b/pwnagotchi/ui/hw/waveshare7in3f.py
index 3a35d112..7f1317d5 100644
--- a/pwnagotchi/ui/hw/waveshare7in3f.py
+++ b/pwnagotchi/ui/hw/waveshare7in3f.py
@@ -38,7 +38,8 @@ class Waveshare7in3f(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare7in3g.py b/pwnagotchi/ui/hw/waveshare7in3g.py
index ee9f13bf..5465cdbd 100644
--- a/pwnagotchi/ui/hw/waveshare7in3g.py
+++ b/pwnagotchi/ui/hw/waveshare7in3g.py
@@ -38,7 +38,8 @@ class Waveshare7in3g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare7in5.py b/pwnagotchi/ui/hw/waveshare7in5.py
index bb999563..6c756d95 100644
--- a/pwnagotchi/ui/hw/waveshare7in5.py
+++ b/pwnagotchi/ui/hw/waveshare7in5.py
@@ -38,7 +38,8 @@ class Waveshare7in5(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare7in5_HD.py b/pwnagotchi/ui/hw/waveshare7in5_HD.py
index 34fa7b53..2004889a 100644
--- a/pwnagotchi/ui/hw/waveshare7in5_HD.py
+++ b/pwnagotchi/ui/hw/waveshare7in5_HD.py
@@ -38,7 +38,8 @@ class Waveshare7in5HD(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare7in5_V2.py b/pwnagotchi/ui/hw/waveshare7in5_V2.py
index 524b8d3c..3d11ab03 100644
--- a/pwnagotchi/ui/hw/waveshare7in5_V2.py
+++ b/pwnagotchi/ui/hw/waveshare7in5_V2.py
@@ -51,7 +51,8 @@ class Waveshare7in5V2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare7in5b_HD.py b/pwnagotchi/ui/hw/waveshare7in5b_HD.py
index 5f04e5d2..550ef2f5 100644
--- a/pwnagotchi/ui/hw/waveshare7in5b_HD.py
+++ b/pwnagotchi/ui/hw/waveshare7in5b_HD.py
@@ -38,7 +38,8 @@ class Waveshare7in5bHD(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare7in5b_V2.py b/pwnagotchi/ui/hw/waveshare7in5b_V2.py
index 8463a691..c7463c0a 100644
--- a/pwnagotchi/ui/hw/waveshare7in5b_V2.py
+++ b/pwnagotchi/ui/hw/waveshare7in5b_V2.py
@@ -38,7 +38,8 @@ class Waveshare7in5bV2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/waveshare7in5bc.py b/pwnagotchi/ui/hw/waveshare7in5bc.py
index fe71c597..3df29469 100644
--- a/pwnagotchi/ui/hw/waveshare7in5bc.py
+++ b/pwnagotchi/ui/hw/waveshare7in5bc.py
@@ -38,7 +38,8 @@ class Waveshare7in5bc(DisplayImpl):
self._display.Clear()
def render(self, canvas):
- self._display.display(canvas)
+ buf = self._display.getbuffer(canvas)
+ self._display.display(buf, None)
def clear(self):
self._display.Clear()
diff --git a/pwnagotchi/ui/hw/weact_2in9.py b/pwnagotchi/ui/hw/weact_2in9.py
new file mode 100644
index 00000000..a87d244b
--- /dev/null
+++ b/pwnagotchi/ui/hw/weact_2in9.py
@@ -0,0 +1,47 @@
+import logging
+
+import pwnagotchi.ui.fonts as fonts
+from pwnagotchi.ui.hw.base import DisplayImpl
+
+
+class WeAct2in9(DisplayImpl):
+ def __init__(self, config):
+ super(WeAct2in9, self).__init__(config, 'weact2in9')
+
+ def layout(self):
+ fonts.setup(10, 8, 10, 35, 25, 9)
+ self._layout['width'] = 296
+ self._layout['height'] = 128
+ self._layout['face'] = (0, 40)
+ self._layout['name'] = (5, 20)
+ self._layout['channel'] = (0, 0)
+ self._layout['aps'] = (28, 0)
+ self._layout['uptime'] = (185, 0)
+ self._layout['line1'] = [0, 14, 250, 14]
+ self._layout['line2'] = [0, 108, 250, 108]
+ self._layout['friend_face'] = (0, 92)
+ self._layout['friend_name'] = (40, 94)
+ self._layout['shakes'] = (0, 109)
+ self._layout['mode'] = (225, 109)
+ self._layout['status'] = {
+ 'pos': (125, 20),
+ 'font': fonts.status_font(fonts.Medium),
+ 'max': 20
+ }
+
+ return self._layout
+
+ def initialize(self):
+ logging.info("initializing WeAct 2.9 inch display")
+ from pwnagotchi.ui.hw.libs.weact.v2in9.epd2in9 import EPD
+ self._display = EPD()
+ self._display.init()
+ self._display.Clear(0xFF)
+
+ def render(self, canvas):
+ buf = self._display.getbuffer(canvas)
+ self._display.displayPartial(buf)
+
+ def clear(self):
+ #pass
+ self._display.Clear(0xFF)
diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py
index 50b2ffc5..3d822190 100644
--- a/pwnagotchi/ui/view.py
+++ b/pwnagotchi/ui/view.py
@@ -23,7 +23,21 @@ ROOT = None
class View(object):
def __init__(self, config, impl, state=None):
- global ROOT
+ global ROOT, BLACK, WHITE
+
+ self.invert = 0
+ self._black = 0xFF
+ self._white = 0x00
+ if 'invert' in config['ui'] and config['ui']['invert'] == True:
+ logging.debug("INVERT BLACK/WHITES:" + str(config['ui']['invert']))
+ self.invert = 1
+ BLACK = 0x00
+ WHITE - 0xFF
+ self._black = 0x00
+ self._white = 0xFF
+
+
+
# setup faces from the configuration in case the user customized them
faces.load_from_config(config['ui']['faces'])
@@ -54,10 +68,10 @@ class View(object):
'line1': Line(self._layout['line1'], color=BLACK),
'line2': Line(self._layout['line2'], color=BLACK),
- 'face': Text(value=faces.SLEEP, position=self._layout['face'], color=BLACK, font=fonts.Huge),
+ 'face': Text(value=faces.SLEEP, position=(config['ui']['faces']['position_x'], config['ui']['faces']['position_y']), color=BLACK, font=fonts.Huge, png=config['ui']['faces']['png']),
# 'friend_face': Text(value=None, position=self._layout['friend_face'], font=fonts.Bold, color=BLACK),
- 'friend_name': Text(value=None, position=self._layout['friend_name'], font=fonts.BoldSmall, color=BLACK),
+ 'friend_name': Text(value=None, position=self._layout['friend_face'], font=fonts.BoldSmall, color=BLACK),
'name': Text(value='%s>' % 'pwnagotchi', position=self._layout['name'], color=BLACK, font=fonts.Bold),
@@ -98,6 +112,11 @@ class View(object):
self._state.has_element(key)
def add_element(self, key, elem):
+ if self.invert is 1 and elem.color:
+ if elem.color == 0xff:
+ elem.color = 0x00
+ elif elem.color == 0x00:
+ elem.color = 0xff
self._state.add_element(key, elem)
def remove_element(self, key):
@@ -371,7 +390,7 @@ class View(object):
state = self._state
changes = state.changes(ignore=self._ignore_changes)
if force or len(changes):
- self._canvas = Image.new('1', (self._width, self._height), WHITE)
+ self._canvas = Image.new('1', (self._width, self._height), self._white)
drawer = ImageDraw.Draw(self._canvas)
plugins.on('ui_update', self)
diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py
index eacf8899..72f2831c 100644
--- a/pwnagotchi/utils.py
+++ b/pwnagotchi/utils.py
@@ -238,14 +238,60 @@ def load_config(args):
config = merge_config(additional_config, config)
# the very first step is to normalize the display name, so we don't need dozens of if/elif around
+ # NON E-INK DISPLAYS---------------------------------------------------------------
if config['ui']['display']['type'] in ('inky', 'inkyphat'):
config['ui']['display']['type'] = 'inky'
+
elif config['ui']['display']['type'] in ('papirus', 'papi'):
config['ui']['display']['type'] = 'papirus'
elif config['ui']['display']['type'] in 'oledhat':
config['ui']['display']['type'] = 'oledhat'
+ elif config['ui']['display']['type'] in 'lcdhat':
+ config['ui']['display']['type'] = 'lcdhat'
+
+ elif config['ui']['display']['type'] in ('dfrobot_1', 'df1'):
+ config['ui']['display']['type'] = 'dfrobot_1'
+
+ elif config['ui']['display']['type'] in ('dfrobot_2', 'df2'):
+ config['ui']['display']['type'] = 'dfrobot_2'
+
+ elif config['ui']['display']['type'] in ('waveshare144lcd', 'ws_144', 'ws144', 'waveshare_144', 'waveshare144'):
+ config['ui']['display']['type'] = 'waveshare144lcd'
+
+ elif config['ui']['display']['type'] in ('spotpear24inch'):
+ config['ui']['display']['type'] = 'spotpear24inch'
+
+ elif config['ui']['display']['type'] in ('displayhatmini'):
+ config['ui']['display']['type'] = 'displayhatmini'
+
+ elif config['ui']['display']['type'] in ('waveshare35lcd'):
+ config['ui']['display']['type'] = 'waveshare35lcd'
+
+ # E-INK DISPLAYS ------------------------------------------------------------------------
+
+ elif config['ui']['display']['type'] in ('waveshare1in02', 'ws1in02', 'ws102', 'waveshare_102', 'waveshare_1in02'):
+ config['ui']['display']['type'] = 'waveshare1in02'
+
+ elif config['ui']['display']['type'] in ('ws_154inch', 'waveshare1in54', 'ws154inch', 'waveshare_154', 'waveshare154'):
+ config['ui']['display']['type'] = 'waveshare1in54'
+
+ elif config['ui']['display']['type'] in ('ws_154inchb', 'waveshare1in54b', 'ws154inchb', 'waveshare_154b', 'waveshare154b'):
+ config['ui']['display']['type'] = 'waveshare1in54b'
+
+ elif config['ui']['display']['type'] in ('waveshare1in54c', 'ws1in54c', 'ws154c', 'waveshare_154c', 'waveshare_1in54c'):
+ config['ui']['display']['type'] = 'waveshare1in54c'
+
+ elif config['ui']['display']['type'] in ('ws_154inchbv2', 'waveshare1in54bv2', 'waveshare1in54b_v2', 'ws154inchbv2', 'waveshare_154bv2', 'waveshare154bv2'):
+ config['ui']['display']['type'] = 'waveshare1in54b_v2'
+
+ elif config['ui']['display']['type'] in ('ws_154inchv2', 'waveshare1in54v2', 'ws154inchv2', 'waveshare_154inchv2', 'waveshare154v2', "waveshare1in54_v2"):
+ config['ui']['display']['type'] = 'waveshare1in54_v2'
+
+ elif config['ui']['display']['type'] in ('waveshare1in64g', 'ws1in64g', 'ws164g', 'waveshare_164g', 'waveshare_1in64g'):
+ config['ui']['display']['type'] = 'waveshare1in64g'
+
elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1'):
config['ui']['display']['type'] = 'waveshare_1'
@@ -258,50 +304,14 @@ def load_config(args):
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', 'waveshare2in7', 'waveshare_27inch', 'waveshare27inch'):
- config['ui']['display']['type'] = 'waveshare2in7'
+ elif config['ui']['display']['type'] in ('waveshare2in13b_v3', 'waveshare2in13b_v3', 'ws213bv3', 'waveshare_213bv3', 'waveshare213inb_v3'):
+ config['ui']['display']['type'] = 'waveshare2in13b_v3'
- elif config['ui']['display']['type'] in ('ws_27inchv2', 'waveshare2in7_v2', 'ws27inchv2', 'waveshare_27inchv2', 'waveshare27inchv2'):
- config['ui']['display']['type'] = 'waveshare2in7_v2'
+ elif config['ui']['display']['type'] in ('ws_213bv4', 'waveshare2in13b_v4', 'ws213bv4', 'waveshare_213bv4', 'waveshare213inb_v4'):
+ config['ui']['display']['type'] = 'waveshare2in13b_v4'
- elif config['ui']['display']['type'] in ('ws_27inchbv2', 'waveshare2in7b_v2', 'ws27inchbv2', 'waveshare_27inchbv2', 'waveshare27inchbv2'):
- config['ui']['display']['type'] = 'waveshare2in7b_v2'
-
- elif config['ui']['display']['type'] in ('ws_29inch', 'waveshare2in9', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'):
- config['ui']['display']['type'] = 'waveshare2in9'
-
- elif config['ui']['display']['type'] in ('ws_29inchv2', 'waveshare2in9_v2', 'ws29inchv2', 'waveshare_29inchv2', 'waveshare29inchv2'):
- config['ui']['display']['type'] = 'waveshare2in9_v2'
-
- elif config['ui']['display']['type'] in ('ws_29inchbv3', 'waveshare2in9b_v3', 'ws29inchbv3', 'waveshare_29inchbv3', 'waveshare29inchbv3'):
- config['ui']['display']['type'] = 'waveshare2in9b_v3'
-
- elif config['ui']['display']['type'] in ('ws_29inchbv4', 'waveshare2in9b_v4', 'ws29inchbv4', 'waveshare_29inchbv4', 'waveshare29inchbv4'):
- config['ui']['display']['type'] = 'waveshare2in9b_v4'
-
- elif config['ui']['display']['type'] in 'lcdhat':
- config['ui']['display']['type'] = 'lcdhat'
-
- elif config['ui']['display']['type'] in ('dfrobot_1', 'df1'):
- config['ui']['display']['type'] = 'dfrobot_1'
-
- elif config['ui']['display']['type'] in ('dfrobot_2', 'df2'):
- config['ui']['display']['type'] = 'dfrobot_2'
-
- elif config['ui']['display']['type'] in ('ws_154inch', 'waveshare1in54', 'ws154inch', 'waveshare_154inch', 'waveshare154inch'):
- config['ui']['display']['type'] = 'waveshare1in54'
-
- elif config['ui']['display']['type'] in ('ws_154inchb', 'waveshare1in54b', 'ws154inchb', 'waveshare_154inchb', 'waveshare154inchb'):
- config['ui']['display']['type'] = 'waveshare1in54b'
-
- elif config['ui']['display']['type'] in ('ws_154inchbv2', 'waveshare1in54bv2', 'ws154inchbv2', 'waveshare_154inchbv2', 'waveshare154inchbv2'):
- config['ui']['display']['type'] = 'waveshare1in54b_v2'
-
- elif config['ui']['display']['type'] in ('ws_154inchv2', 'waveshare1in54v2', 'ws154inchv2', 'waveshare_154inchv2', 'waveshare154inchv2'):
- config['ui']['display']['type'] = 'waveshare1in54_v2'
-
- elif config['ui']['display']['type'] in ('waveshare144lcd', 'ws_144inch', 'ws144inch', 'waveshare_144inch', 'waveshare144inch'):
- config['ui']['display']['type'] = 'waveshare144lcd'
+ elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare2in13bc', 'waveshare_213bc', 'waveshare213bc'):
+ config['ui']['display']['type'] = 'waveshare2in13bc'
elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare2in13d', 'waveshare_213d', 'waveshare213d'):
config['ui']['display']['type'] = 'waveshare2in13d'
@@ -309,123 +319,121 @@ def load_config(args):
elif config['ui']['display']['type'] in ('ws_213g', 'waveshare2in13g', 'waveshare213g', 'ws213g', 'waveshare_213g'):
config['ui']['display']['type'] = 'waveshare2in13g'
- elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare2in13bc', 'waveshare_213bc', 'waveshare213bc'):
- config['ui']['display']['type'] = 'waveshare2in13bc'
-
- elif config['ui']['display']['type'] in ('ws_213bv4', 'waveshare2in13b_v4', 'ws213bv4', 'waveshare_213bv4', 'waveshare213inb_v4'):
- config['ui']['display']['type'] = 'waveshare2in13b_v4'
-
- elif config['ui']['display']['type'] in 'spotpear24inch':
- config['ui']['display']['type'] = 'spotpear24inch'
-
- elif config['ui']['display']['type'] in 'displayhatmini':
- config['ui']['display']['type'] = 'displayhatmini'
-
- elif config['ui']['display']['type'] in 'waveshare35lcd':
- config['ui']['display']['type'] = 'waveshare35lcd'
-
- elif config['ui']['display']['type'] in 'waveshare1in54c':
- config['ui']['display']['type'] = 'waveshare1in54c'
-
- elif config['ui']['display']['type'] in 'waveshare1in64g':
- config['ui']['display']['type'] = 'waveshare1in64g'
-
- elif config['ui']['display']['type'] in 'waveshare1in02':
- config['ui']['display']['type'] = 'waveshare1in02'
-
- elif config['ui']['display']['type'] in 'waveshare2in9bc':
- config['ui']['display']['type'] = 'waveshare2in9bc'
-
- elif config['ui']['display']['type'] in 'waveshare2in9d':
- config['ui']['display']['type'] = 'waveshare2in9d'
-
- elif config['ui']['display']['type'] in 'waveshare2in13b_v3':
- config['ui']['display']['type'] = 'waveshare2in13b_v3'
-
- elif config['ui']['display']['type'] in 'waveshare2in36g':
+ elif config['ui']['display']['type'] in ('ws_2in36g', 'waveshare2in36g', 'waveshare236g', 'ws236g', 'waveshare_236g'):
config['ui']['display']['type'] = 'waveshare2in36g'
- elif config['ui']['display']['type'] in 'waveshare2in66':
+ elif config['ui']['display']['type'] in ('ws_2in66', 'waveshare2in66', 'waveshare266', 'ws266', 'waveshare_266'):
config['ui']['display']['type'] = 'waveshare2in66'
- elif config['ui']['display']['type'] in 'waveshare2in66b':
+ elif config['ui']['display']['type'] in ('ws_2in66b', 'waveshare2in66b', 'waveshare266b', 'ws266b', 'waveshare_266b'):
config['ui']['display']['type'] = 'waveshare2in66b'
- elif config['ui']['display']['type'] in 'waveshare2in66g':
+ elif config['ui']['display']['type'] in ('ws_2in66g', 'waveshare2in66g', 'waveshare266g', 'ws266g', 'waveshare_266g'):
config['ui']['display']['type'] = 'waveshare2in66g'
- elif config['ui']['display']['type'] in 'waveshare3in0g':
+ elif config['ui']['display']['type'] in ('ws_27inch', 'ws27inch', 'waveshare2in7', 'waveshare_27inch', 'waveshare27'):
+ config['ui']['display']['type'] = 'waveshare2in7'
+
+ elif config['ui']['display']['type'] in ('ws_2in7v2', 'waveshare2in7_v2', 'waveshare2in7v2', 'ws27inchv2', 'waveshare_27v2', 'waveshare27v2'):
+ config['ui']['display']['type'] = 'waveshare2in7_v2'
+
+ elif config['ui']['display']['type'] in ('ws_2in7bv2', 'waveshare2in7b_v2', 'waveshare2in7bv2', 'ws27inchbv2', 'waveshare_27bv2', 'waveshare27bv2'):
+ config['ui']['display']['type'] = 'waveshare2in7b_v2'
+
+ elif config['ui']['display']['type'] in ('ws_2in9', 'waveshare2in9', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'):
+ config['ui']['display']['type'] = 'waveshare2in9'
+
+ elif config['ui']['display']['type'] in ('ws_2in9bc', 'waveshare2in9bc', 'ws2in9bc', 'ws29bc', 'waveshare_29bc', 'waveshare_2in9bc'):
+ config['ui']['display']['type'] = 'waveshare2in9bc'
+
+ elif config['ui']['display']['type'] in ('ws_2in9d', 'waveshare2in9d', 'ws2in9d', 'ws29d', 'waveshare_29d', 'waveshare_2in9d'):
+ config['ui']['display']['type'] = 'waveshare2in9d'
+
+ elif config['ui']['display']['type'] in ('ws_2in9v2', 'waveshare2in9_v2', 'waveshare2in9v2', 'ws2in9v2', 'waveshare_29v2', 'waveshare29v2'):
+ config['ui']['display']['type'] = 'waveshare2in9_v2'
+
+ elif config['ui']['display']['type'] in ('ws_2in9bv3', 'waveshare2in9b_v3', 'waveshare2in9bv3', 'ws2in9bv3', 'waveshare_29bv3', 'waveshare29bv3'):
+ config['ui']['display']['type'] = 'waveshare2in9b_v3'
+
+ elif config['ui']['display']['type'] in ('ws_2in9bv4', 'waveshare2in9b_v4', 'waveshare2in9bv4', 'ws2in9bv4', 'waveshare_29bv4', 'waveshare29bv4'):
+ config['ui']['display']['type'] = 'waveshare2in9b_v4'
+
+ elif config['ui']['display']['type'] in ('ws_3in0g', 'waveshare3in0g', 'ws3in0g', 'waveshare_30g', 'waveshare30g'):
config['ui']['display']['type'] = 'waveshare3in0g'
- elif config['ui']['display']['type'] in 'waveshare3in7':
+ elif config['ui']['display']['type'] in ('ws_3in7', 'waveshare3in7', 'ws3in7', 'waveshare_37', 'waveshare37'):
config['ui']['display']['type'] = 'waveshare3in7'
- elif config['ui']['display']['type'] in 'waveshare3in52':
+ elif config['ui']['display']['type'] in ('ws_3in52', 'waveshare3in52', 'ws3in52', 'waveshare_352', 'waveshare352'):
config['ui']['display']['type'] = 'waveshare3in52'
- elif config['ui']['display']['type'] in 'waveshare4in01f':
+ elif config['ui']['display']['type'] in ('ws_4in01f', 'waveshare4in01f', 'ws4in01f', 'waveshare_401f', 'waveshare401f'):
config['ui']['display']['type'] = 'waveshare4in01f'
- elif config['ui']['display']['type'] in 'waveshare4in2':
+ elif config['ui']['display']['type'] in ('ws_4in2', 'waveshare4in2', 'ws4in2', 'waveshare_42', 'waveshare42'):
config['ui']['display']['type'] = 'waveshare4in2'
- elif config['ui']['display']['type'] in 'waveshare4in2_v2':
+ elif config['ui']['display']['type'] in ('ws_4in2v2', 'waveshare4in2v2', 'ws4in2v2', 'waveshare_42v2', 'waveshare42v2'):
config['ui']['display']['type'] = 'waveshare4in2_v2'
- elif config['ui']['display']['type'] in 'waveshare4in2b_v2':
+ elif config['ui']['display']['type'] in ('ws_4in2bv2', 'waveshare4in2bv2', 'ws4in2bv2', 'waveshare_42bv2', 'waveshare42bv2'):
config['ui']['display']['type'] = 'waveshare4in2b_v2'
- elif config['ui']['display']['type'] in 'waveshare4in2bc':
+ elif config['ui']['display']['type'] in ('ws_4in2bc', 'waveshare4in2bc', 'ws4in2bc', 'waveshare_42bc', 'waveshare42bc'):
config['ui']['display']['type'] = 'waveshare4in2bc'
- elif config['ui']['display']['type'] in 'waveshare4in26':
+ elif config['ui']['display']['type'] in ('ws_4in26', 'waveshare4in26', 'ws4in26', 'waveshare_426', 'waveshare426'):
config['ui']['display']['type'] = 'waveshare4in26'
- elif config['ui']['display']['type'] in 'waveshare4in37g':
+ elif config['ui']['display']['type'] in ('ws_4in37g', 'waveshare4in37g', 'ws4in37g', 'waveshare_37g', 'waveshare437g'):
config['ui']['display']['type'] = 'waveshare4in37g'
- elif config['ui']['display']['type'] in 'waveshare5in65f':
+ elif config['ui']['display']['type'] in ('ws_5in65f', 'waveshare5in65f', 'ws5in65f', 'waveshare_565f', 'waveshare565f'):
config['ui']['display']['type'] = 'waveshare5in65f'
- elif config['ui']['display']['type'] in 'waveshare5in83':
+ elif config['ui']['display']['type'] in ('ws_5in83', 'waveshare5in83', 'ws5in83', 'waveshare_583', 'waveshare583'):
config['ui']['display']['type'] = 'waveshare5in83'
- elif config['ui']['display']['type'] in 'waveshare5in83_v2':
+ elif config['ui']['display']['type'] in ('ws_5in83v2', 'waveshare5in83v2', 'ws5in83v2', 'waveshare_583v2', 'waveshare583v2'):
config['ui']['display']['type'] = 'waveshare5in83_v2'
- elif config['ui']['display']['type'] in 'waveshare5in83b_v2':
+ elif config['ui']['display']['type'] in ('ws_5in83bv2', 'waveshare5in83bv2', 'ws5in83bv2', 'waveshare_583bv2', 'waveshare583bv2'):
config['ui']['display']['type'] = 'waveshare5in83b_v2'
- elif config['ui']['display']['type'] in 'waveshare5in83bc':
+ elif config['ui']['display']['type'] in ('ws_5in83bc', 'waveshare5in83bc', 'ws5in83bc', 'waveshare_583bc', 'waveshare583bc'):
config['ui']['display']['type'] = 'waveshare5in83bc'
- elif config['ui']['display']['type'] in 'waveshare7in3f':
+ elif config['ui']['display']['type'] in ('ws_7in3f', 'waveshare7in3f', 'ws7in3f', 'waveshare_73f', 'waveshare73f'):
config['ui']['display']['type'] = 'waveshare7in3f'
- elif config['ui']['display']['type'] in 'waveshare7in3g':
+ elif config['ui']['display']['type'] in ('ws_7in3g', 'waveshare7in3g', 'ws7in3g', 'waveshare_73g', 'waveshare73g'):
config['ui']['display']['type'] = 'waveshare7in3g'
- elif config['ui']['display']['type'] in 'waveshare7in5':
+ elif config['ui']['display']['type'] in ('ws_7in5', 'waveshare7in5', 'ws7in5', 'waveshare_75', 'waveshare75'):
config['ui']['display']['type'] = 'waveshare7in5'
- elif config['ui']['display']['type'] in 'waveshare7in5_HD':
+ elif config['ui']['display']['type'] in ('ws_7in5hd', 'waveshare7in5hd', 'ws7in5hd', 'waveshare_75hd', 'waveshare75hd'):
config['ui']['display']['type'] = 'waveshare7in5_HD'
- elif config['ui']['display']['type'] in 'waveshare7in5_v2':
+ elif config['ui']['display']['type'] in ('ws_7in5v2', 'waveshare7in5v2', 'ws7in5v2', 'waveshare_75v2', 'waveshare75v2'):
config['ui']['display']['type'] = 'waveshare7in5_v2'
- elif config['ui']['display']['type'] in 'waveshare7in5b_HD':
+ elif config['ui']['display']['type'] in ('ws_7in5bhd', 'waveshare7in5bhd', 'ws7in5bhd', 'waveshare_75bhd', 'waveshare75bhd'):
config['ui']['display']['type'] = 'waveshare7in5b_HD'
- elif config['ui']['display']['type'] in 'waveshare7in5b_v2':
+ elif config['ui']['display']['type'] in ('ws_7in5bv2', 'waveshare7in5bv2', 'ws7in5bv2', 'waveshare_75bv2', 'waveshare75bv2'):
config['ui']['display']['type'] = 'waveshare7in5b_v2'
- elif config['ui']['display']['type'] in 'waveshare7in5bc':
+ elif config['ui']['display']['type'] in ('ws_7in5bc', 'waveshare7in5bc', 'ws7in5bc', 'waveshare_75bc', 'waveshare75bc'):
config['ui']['display']['type'] = 'waveshare7in5bc'
- elif config['ui']['display']['type'] in 'waveshare13in3k':
+ elif config['ui']['display']['type'] in ('ws_13in3k', 'waveshare13in3k', 'ws13in3k', 'waveshare_133k', 'waveshare133k'):
config['ui']['display']['type'] = 'waveshare13in3k'
+ # WeAct e-ink
+ elif config['ui']['display']['type'] in ('weact2in9', 'weact29in'):
+ config['ui']['display']['type'] = 'weact2in9'
+
else:
print("unsupported display type %s" % config['ui']['display']['type'])
sys.exit(1)
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..ff8eaa25
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,60 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "pwnagotchi"
+dynamic = ["version"]
+dependencies = [
+ "Pillow",
+ "PyYAML",
+ "RPi.GPIO",
+ "file-read-backwards",
+ "flask",
+ "flask-cors",
+ "flask-wtf",
+ "gast",
+ "gym",
+ "inky",
+ "pycryptodome",
+ "pydrive2",
+ "python-dateutil",
+ "requests",
+ "rpi_hardware_pwm",
+ "scapy",
+ "shimmy",
+ "smbus2",
+ "spidev",
+ "stable_baselines3",
+ "toml",
+ "torch",
+ "torchvision",
+ "tweepy",
+ "websockets"
+]
+requires-python = ">=3.9"
+authors = [
+ {name = "Evilsocket", email = "evilsocket@gmail.com"},
+ {name = "Jayofelony", email = "oudshoorn.jeroen@gmail.com"}
+]
+maintainers = [
+ {name = "Jayofelony", email = "oudshoorn.jeroen@gmail.com"}
+]
+description = "(⌐■_■) - Deep Reinforcement Learning instrumenting bettercap for WiFI pwning."
+readme = "README.md"
+license = {file = "LICENSE.md"}
+classifiers = [
+ 'Programming Language :: Python :: 3',
+ 'Development Status :: 5 - Production/Stable',
+ 'License :: OSI Approved :: GNU General Public License (GPL)',
+ 'Environment :: Console',
+]
+
+[project.urls]
+Homepage = "https://pwnagotchi.org/"
+Documentation = "https://pwnagotchi.org/"
+Repository = "https://github.com/jayofelony/pwnagotchi.git"
+Issues = "https://github.com/jayofelony/pwnagotchi/issues"
+
+[project.scripts]
+pwnagotchi_cli = "bin.pwnagotchi:pwnagotchi_cli"
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 13f0fea6..ab51bdcb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,25 +1,26 @@
-Pillow
+gym
+shimmy
+pycryptodome
+requests
PyYAML
-RPi.GPIO
+scapy
+tweepy
file-read-backwards
+inky
+smbus2
+Pillow
+spidev
+gast
flask
flask-cors
flask-wtf
-gast
-gym
-inky
-pycryptodome
-pydrive2
-python-dateutil
-requests
-rpi_hardware_pwm
-scapy
-shimmy
-smbus2
-spidev
-stable_baselines3
+dbus-python
toml
+python-dateutil
+websockets
torch
torchvision
-tweepy
-websockets
\ No newline at end of file
+stable_baselines3
+RPi.GPIO
+rpi_hardware_pwm
+pydrive2
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 0e187ef8..6202183a 100644
--- a/setup.py
+++ b/setup.py
@@ -8,6 +8,7 @@ import os
import re
import shutil
import warnings
+import platform
log = logging.getLogger(__name__)
@@ -31,7 +32,10 @@ def install_file(source_filename, dest_filename):
def install_system_files():
setup_path = os.path.dirname(__file__)
- data_path = os.path.join(setup_path, "builder/data")
+ if platform.machine().startswith('arm'):
+ data_path = os.path.join(setup_path, "builder/data/32bit")
+ elif platform.machine().startswith('aarch'):
+ data_path = os.path.join(setup_path, "builder/data/64bit")
for source_filename in glob.glob("%s/**" % data_path, recursive=True):
if os.path.isfile(source_filename):
@@ -94,7 +98,7 @@ setup(name='pwnagotchi',
"install": CustomInstall,
},
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,
packages=find_packages(),
classifiers=[