Compare commits

..

169 Commits

Author SHA1 Message Date
611a3e7fb5 fix for waveshare3in7.py 2024-02-15 20:51:55 +01:00
599c6211e4 version 2.8.4 2024-02-15 20:48:27 +01:00
eb48d29851 fix on 38 displays 2024-02-15 20:46:48 +01:00
0999b95be0 update build 2024-02-15 09:55:01 +01:00
ae351e5e9c update waveshare3in0g.py 2024-02-15 09:54:49 +01:00
fa58136c0e update waveshare3in7.py 2024-02-15 09:34:45 +01:00
3d5185f2c1 update waveshare3in7.py 2024-02-15 09:30:03 +01:00
b6bb7b9080 update waveshare3in7.py 2024-02-15 09:26:33 +01:00
20715351af revert back to drschottky 2024-02-14 08:53:54 +01:00
1d3c781d12 version 2.8.3 2024-02-13 23:25:55 +01:00
bd51b4330f version 2.8.3 2024-02-13 23:15:05 +01:00
f216a21132 update 2024-02-13 23:13:49 +01:00
5ae357c3dc Merge remote-tracking branch 'origin/master' 2024-02-13 23:11:10 +01:00
ed9afe6856 Revert "Changed from dphys-swapfile to zram-tools"
This reverts commit 26913016b9.
2024-02-13 23:10:53 +01:00
25d932fa9b Revert "zramswap"
This reverts commit 0dbd611ed5.
2024-02-13 23:10:52 +01:00
ebd25d50b0 Update README.md 2024-02-09 23:13:26 +01:00
27e784a5d7 update 2024-02-09 13:12:40 +01:00
c02efb5224 update Makefile 2024-02-09 13:11:02 +01:00
107d366c82 update Makefile 2024-02-08 21:40:28 +01:00
488968ba7f Revert "update Makefile"
This reverts commit 9a40567d
2024-02-08 20:18:58 +01:00
0dd8f72c95 Merge pull request #58 from jayofelony/dev
dev
2024-02-08 20:07:20 +01:00
0e274af5a0 Revert "Testing pcapng fileformat"
This reverts commit 7040be2d30.
2024-02-08 20:06:33 +01:00
8627c8800d Merge pull request #57 from jayofelony/dev
Dev
2024-02-08 07:53:53 +01:00
53ed6f61c6 Merge branch 'master' into dev
Signed-off-by: Jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-08 07:53:45 +01:00
835523241b update build 2024-02-08 07:51:19 +01:00
9a40567daa update Makefile 2024-02-06 22:12:05 +01:00
eab331686d Merge pull request #54 from crs-k/dev
Add PiShrink step to the workflow (#1)
2024-02-06 22:10:12 +01:00
e1d8001fb4 Add PiShrink step to the workflow (#1)
* Add PiShrink step to the workflow

* Add environment variables for Raspberry Pi 64-bit builder

* Update Makefile command to include debug information

* Add source distribution target to Makefile

* Remove unnecessary provisioner command

* Add armhf architecture support

* Remove armhf architecture provisioning

* Refactor Makefile to remove unnecessary files

* Update Makefile command in publish.yml

* Update pwngrid URL in raspberrypi64.yml

* Add permissions to publish workflow

* Remove unnecessary permissions in publish.yml
2024-02-06 12:53:45 -05:00
3cc172a526 update Makefile 2024-02-06 09:06:54 +01:00
41afe302d9 update Makefile 2024-02-06 09:05:42 +01:00
5bcca7e89b update README.md 2024-02-06 08:52:16 +01:00
4a12436942 add architecture armhf 2024-02-06 08:41:28 +01:00
d8b6363d37 Merge pull request #51
Waveshare 3.52 in e-ink update
2024-02-06 08:36:21 +01:00
b3d8a44208 Update waveshare3in52.py
Signed-off-by: Chris Kerins <clkerins@gmail.com>
2024-02-05 20:14:18 -05:00
ck
3fb91498cd Enable Makefile execution and remove disk image creation 2024-02-05 17:52:51 -05:00
ck
7587f38c28 Update disk.img path in publish.yml 2024-02-05 17:46:20 -05:00
ck
d7fefa78f0 Commented out make step and added code to create .img file 2024-02-05 17:40:38 -05:00
ck
1ef3a88679 Update distDir path in publish.yml 2024-02-05 17:34:23 -05:00
291ef39563 Update README.md to show support for Pi 5 2024-02-05 22:10:53 +01:00
16d80a09e6 Update README.md to show support for Pi 5 2024-02-05 22:10:34 +01:00
ck
4ffad03c91 Update distDir path in publish.yml 2024-02-05 15:44:50 -05:00
ck
0638832532 Update mountpoint for boot partition 2024-02-05 13:42:59 -05:00
ck
f895b06978 Update qemu binary paths for arm64 2024-02-05 13:19:56 -05:00
ck
f410feb2bb Add check for qemu-aarch64 in binfmt_misc 2024-02-05 13:09:36 -05:00
ck
1ffe6c4e3d Remove Makefile2 and update build process 2024-02-05 12:34:34 -05:00
ck
dfbfd7f891 Update pwngrid to version 1.11.1 2024-02-05 12:34:09 -05:00
ck
a50695aef2 Update publish workflow and add Makefile2 2024-02-05 12:18:04 -05:00
ck
829a6962d6 Update binfmt-support and add checks for qemu-user-static package, binfmt-support service, and binfmt_misc filesystem 2024-02-05 12:17:41 -05:00
ck
ce1315b85f Add binfmt_misc registration for qemu-aarch64 2024-02-05 12:08:07 -05:00
ck
6a2703cc27 Update QEMU in publish.yml 2024-02-05 11:34:03 -05:00
ck
502e856934 Update QEMU and restart binfmt-support 2024-02-05 11:22:39 -05:00
dffcbbf447 Revert "Revert "Testing pcapng fileformat""
This reverts commit d142840307.
2024-02-05 17:16:13 +01:00
ck
333804c3bb Remove unnecessary build configuration for Raspberry Pi 32 Pwnagotchi 2024-02-05 11:10:29 -05:00
ck
50b1ceed81 Remove unnecessary file from pwnagotchi build process 2024-02-05 11:01:49 -05:00
ck
877b9fbba3 Update Makefile and builder/pwnagotchi.json.pkr.hcl 2024-02-05 10:57:33 -05:00
ck
8724ca546d Update packer builder-arm source URL 2024-02-05 10:51:18 -05:00
961d3a536a Merge pull request #2 from crs-k/waveshare-352-support
Waveshare 352 support
2024-02-05 10:38:39 -05:00
ck
c882d0b67a Update arm plugin source in pwnagotchi.json.pkr.hcl 2024-02-05 10:29:01 -05:00
ck
12fd081ae0 Update required_plugins in pwnagotchi.json.pkr.hcl 2024-02-05 10:09:57 -05:00
ck
e1be0f7674 Remove unnecessary clean-up step in Makefile 2024-02-05 10:01:12 -05:00
396b30f780 Merge pull request #1 from crs-k:waveshare-352-support
Add publish workflow and update display layout
2024-02-05 09:31:24 -05:00
ck
e7d8d632a0 Add publish workflow and update display layout 2024-02-05 09:30:49 -05:00
5a6ec7b4b8 Next version will be 2.8.2 2024-02-04 18:28:09 +01:00
92cd5d3fdb Merge pull request #47 from jayofelony/dev
removed blind bug check from fix_services.py
2024-02-04 18:09:33 +01:00
99caa7a973 removed blind bug check from fix_services.py 2024-02-04 18:08:31 +01:00
a94e7eef02 Merge pull request #46 from jayofelony/dev
Dev to master
2024-02-04 18:04:44 +01:00
1cefae55d1 moved the majority of logging to debug 2024-02-04 17:48:22 +01:00
c5ee1df855 unused function 2024-02-04 17:44:05 +01:00
d142840307 Revert "Testing pcapng fileformat"
This reverts commit 7040be2d30.
2024-02-04 17:42:56 +01:00
e558146e28 moves the majority of logging to debug 2024-02-04 17:42:40 +01:00
11a3330153 small fix 2024-02-04 17:42:22 +01:00
53b2dd8628 update build for latest kernel 2024-02-04 17:42:10 +01:00
6381f9443b remove blind bug, as its not a blind bug anymore. 2024-02-04 17:41:57 +01:00
fa7e87b974 remove blind bug, as its not a blind bug anymore.
delayed hopping channels by .2 seconds to give firmware time to get ready for hopping.
2024-02-04 17:41:41 +01:00
cca2ff2da4 Merge pull request #45 from wpa-2/patch-1
MOTD updated
2024-02-03 17:09:03 +01:00
78415b3137 Update 01-motd
Maybe a good idea to point people to the new wiki instead. 


Signed-off-by: wpa-2 <9049886+wpa-2@users.noreply.github.com>
2024-02-03 15:13:44 +00:00
34284aa1bc Change to seemoo-lab nexmon 2024-02-01 13:50:51 +01:00
56ebb60662 Change to seemoo-lab nexmon 2024-02-01 13:26:20 +01:00
84f6624844 Change to seemoo-lab nexmon 2024-02-01 13:04:06 +01:00
3bcbb0ce9a Version 2.8.1 2024-01-29 22:52:16 +01:00
e01e457992 Remove bettercap client settings, so you cannot mess it up. 2024-01-29 22:24:20 +01:00
1780859889 Merge pull request #37 from Ravioli5/patch-1
Update 01-motd
2024-01-29 16:15:12 +01:00
fdd98bb37a Update 01-motd
-fixed grammar (added a word)

Signed-off-by: Ravioli5 <157115073+Ravioli5@users.noreply.github.com>
2024-01-29 13:33:47 +05:30
86991304a5 Merge remote-tracking branch 'origin/dev' into dev 2024-01-28 22:12:20 +01:00
4f62759d6d Revert grid name 2024-01-28 22:12:06 +01:00
7b7ba02aad Changed setup.py 2024-01-28 22:12:06 +01:00
7040be2d30 Testing pcapng fileformat 2024-01-28 22:12:05 +01:00
d0617ccfaf Revert grid name 2024-01-28 22:08:17 +01:00
a0b5078b64 Changed setup.py 2024-01-28 22:05:14 +01:00
54c1ffd63c Updated wiglewifi version to 1.6 2024-01-28 22:02:57 +01:00
7530709d0c Updated wiglewifi version to 1.6 2024-01-28 21:46:50 +01:00
a9a6fd424b Small changes/fixes for debugging 2024-01-28 21:46:22 +01:00
8c97301992 Readability changes for webgpsmap.py 2024-01-28 21:45:53 +01:00
91eaa22188 Fix for waveshare2in13b_V4.py implementation 2024-01-28 21:44:19 +01:00
31a4af4c21 sudo pwnagotchi plugins update
`sudo pwnagotchi plugins list`

Do not work, somehow I forgot to remove a test I did.
2024-01-28 21:44:04 +01:00
dceeaff1fb Next update will be 2.8.0 2024-01-28 21:43:17 +01:00
c0241dc8df Update README.md 2024-01-27 16:11:59 +01:00
8f405f4ab2 Merge pull request #35 from Kal-Toh/patch-1
Update README.md
2024-01-27 16:11:19 +01:00
bd79e71563 Update README.md
This is a small change to address questions I had about the installation process.

Signed-off-by: Rory <104321806+Kal-Toh@users.noreply.github.com>
2024-01-27 14:03:40 +00:00
7f662585aa Merge pull request #34 from findingmoist/master
Update 01-motd
2024-01-27 10:03:24 +01:00
d23ff8d47a Update 01-motd
* Changed face so it shows
* updated python3 version so $_version shows the update number

Signed-off-by: findingmoist <128169791+findingmoist@users.noreply.github.com>
2024-01-26 18:56:40 -05:00
04435229fe Update build for RPi5 2024-01-26 15:28:04 +01:00
42e236bafe Update build for RPi5 2024-01-26 10:45:18 +01:00
5081b72695 Merge remote-tracking branch 'origin/master' 2024-01-25 23:48:49 +01:00
652740f050 Version 2.7.9
Add Esperanzo language
Updated Pt-Br language
Updated Italian language
2024-01-25 23:48:41 +01:00
1b6b12bdf5 Add Esperanzo language
Updated Pt-Br language
Updated Italian language
2024-01-25 23:48:06 +01:00
c610335a34 Add Esperanzo language
Edited Pt-Br language
2024-01-25 22:23:01 +01:00
6c39ed97dd Update build for Pi 5 users
Version 2.7.8
2024-01-25 21:38:04 +01:00
967f1663c6 Update build for Pi 5 users
Version 2.7.8
2024-01-25 17:51:15 +01:00
ffe1e90ccd Update build for Pi 5 users
Version 2.7.8
2024-01-25 17:49:17 +01:00
f2d2bcbfdf Update build for Pi 5 users
Version 2.7.8
2024-01-25 17:30:15 +01:00
bfc0795fb8 Update build for Pi 5 users
Version 2.7.8
2024-01-25 16:31:35 +01:00
1a55afd74a Update build for Pi 5 users
Version 2.7.8
2024-01-25 15:16:33 +01:00
1594e7c129 Update build for Pi 5 users
Version 2.7.8
2024-01-24 15:51:09 +01:00
a244e70a1c Update build for Pi 5 users
Version 2.7.8
2024-01-24 11:00:32 +01:00
d35d5d6c3c Update build for Pi 5 users
Version 2.7.8
2024-01-24 10:51:02 +01:00
7aacf9fb44 Update build for Pi 5 users
Version 2.7.8
2024-01-24 10:24:01 +01:00
dde6fa4c2a Update build for Pi 5 users 2024-01-24 01:47:23 +01:00
7dfb2f2ff6 Update build for Pi 5 users 2024-01-23 14:56:34 +01:00
cf46ab3da9 Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 14:47:06 +01:00
6824e541a4 Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 14:40:41 +01:00
1c4e8fa750 Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 14:36:46 +01:00
a50ec4a65c Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 14:35:12 +01:00
5414fdf21d Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 13:29:31 +01:00
c9883bb4f9 Add hcxtools to apt_packages.txt 2024-01-23 12:27:59 +01:00
045f5853bc Test 2024-01-23 12:26:33 +01:00
6dfcaf05d5 Hashie enabled by default 2024-01-23 12:26:00 +01:00
e2f8119d2c Install apt-packages while installing pwnagotchi, if needed 2024-01-23 12:22:30 +01:00
d675b3d0dd Added hcxtools as installed package. 2024-01-23 11:46:50 +01:00
30850b6530 Added hcxtools as installed package. 2024-01-23 11:42:47 +01:00
1e9c9bae42 Added hcxtools as installed package. 2024-01-23 11:38:14 +01:00
64241515ad Added hashie to default plugins, to convert pcaps to a crackable format from the start. 2024-01-23 11:37:34 +01:00
c9f232dae2 Added hashie to default plugins, to convert pcaps to a crackable format from the start. 2024-01-23 11:37:23 +01:00
4f97bcaa83 Moved aircrackonly to default plugins, is also enabled by default. To immediately delete empty handshakes. 2024-01-23 11:25:04 +01:00
7d2a03c79f Changing behaviour when mon_max_blind_epochs is hit, from full reboot to simple restart to save uptime. 2024-01-23 11:13:23 +01:00
f89ba85f73 Couple updated displays from Waveshare, not 213g.
166c012cde
2024-01-23 10:59:25 +01:00
57f03f4359 Removed paw-gps.py, as it is no longer supported. 2024-01-22 23:56:13 +01:00
9bc266f9ff Make wigle, wpa-sec, onlinehashcrack and grid plugins check against main.whitelist instead of their own whitelist. Possibly reduces possibility of errors in config.
https://github.com/jayofelony/pwnagotchi-bookworm/issues/24
2024-01-22 23:29:43 +01:00
b0db0285bc Version 2.7.7 2024-01-22 23:12:59 +01:00
cca3e77d50 Version 2.7.6 2024-01-22 14:25:16 +01:00
4fe603bf5e Small edit to net-pos.py 2024-01-22 12:35:21 +01:00
9a9ee70a78 Fixed wigle plugin 2024-01-22 12:06:20 +01:00
7fc8838f76 2.7.5 2024-01-21 11:43:58 +01:00
94b2ff7047 2.7.5 2024-01-21 11:40:27 +01:00
8edb0fdaa4 2.7.5 2024-01-21 11:33:22 +01:00
b557768159 Fix gdrivesync.py 2024-01-21 11:32:39 +01:00
5a6967eb4d Fix gdrivesync.py 2024-01-21 11:14:17 +01:00
c0c35231cf Fix gdrivesync.py 2024-01-21 11:13:51 +01:00
88362b3354 Version 2.7.4 2024-01-21 00:00:45 +01:00
e50cf04967 Moved self._display = None to base.py for easier debugging display files.
Fixed waveshare2in13g.py
2024-01-20 23:54:32 +01:00
85a64a3914 Merge pull request #23 from Opalinus/master
Repair broken Waveshare213g #22
2024-01-20 23:27:02 +01:00
63d0c20a3e Repair broken Waveshare213g 2024-01-20 20:16:01 +01:00
044639a6d4 Fix 2.7V2 display 2024-01-20 10:23:50 +01:00
7fc2c6b25f Update README.md 2024-01-19 12:25:08 +01:00
c5d5d9bb14 Changed Makefile 2024-01-19 09:04:08 +01:00
129ae92447 Version 2.7.3 2024-01-19 08:51:56 +01:00
2560692ee2 Fixed a mistake on display loading 2024-01-19 08:50:16 +01:00
1424e0d510 Added display waveshare2in66b 2024-01-19 08:43:00 +01:00
2c9eaa436a Added display waveshare2in13g 2024-01-19 08:34:41 +01:00
0c5c6058a5 Added display waveshare2in13g 2024-01-19 07:28:31 +01:00
a3fb39d78c Update README.md 2024-01-18 23:36:25 +01:00
bac62bc4aa Fix for pwnlib 2024-01-17 23:17:48 +01:00
f7a5f2a554 Update build 2024-01-17 21:55:27 +01:00
6921fd14a3 Update Makefile 2024-01-17 20:46:03 +01:00
0dbd611ed5 zramswap 2024-01-17 20:42:28 +01:00
18b9093f70 Version 2.7.2 2024-01-17 20:11:38 +01:00
26913016b9 Changed from dphys-swapfile to zram-tools
Fix on auto-update.py
2024-01-17 19:55:07 +01:00
0786b6757e Changed from dphys-swapfile to zram-tools
Fix on auto-update.py
2024-01-17 19:53:36 +01:00
0f4eda8039 Changed from ext4 to f2fs
Changed from dphys-swapfile to zram-tools
Fix on auto-update.py
2024-01-17 19:51:38 +01:00
131 changed files with 2075 additions and 1087 deletions

1
.github/FUNDING.yml vendored
View File

@ -1,4 +1,3 @@
# These are supported funding model platforms
patreon: pwnagotchi_torch
github: jayofelony

212
.github/workflows/publish.yml vendored Normal file
View File

@ -0,0 +1,212 @@
name: Publish
on:
workflow_dispatch:
inputs:
version:
description: 'Version number'
required: true
jobs:
publish:
runs-on: ubuntu-latest
steps:
- 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: Check disk space
run: df -BG
- name: Checkout code
uses: actions/checkout@v4
- 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: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.9
- 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: Update QEMU
run: |
sudo update-binfmts --enable qemu-aarch64
echo $(ls /usr/bin/qemu-aarch64-static)
- name: Restart binfmt-support
run: sudo service binfmt-support restart
- 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: Restart binfmt-support
run: sudo service binfmt-support restart
- name: Update Languages
run: make update_langs
- name: Compile Languages
run: make compile_langs
- name: Check disk space
run: df -BG
- 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

21
.idea/deployment.xml generated
View File

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PublishConfigData" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" />
<component name="PublishConfigData" serverName="pwnagotchi" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
<option name="confirmBeforeUploading" value="false" />
<serverData>
<paths name="pwnagotchi">
<serverdata>
<mappings>
<mapping deploy="/usr/local/lib/python3.11/dist-packages/pwnagotchi" local="$PROJECT_DIR$/pwnagotchi" web="/" />
<mapping deploy="/usr/local/bin" local="$PROJECT_DIR$/bin" />
<mapping local="" />
</mappings>
<excludedPaths>
<excludedPath local="true" path="$PROJECT_DIR$/venv" />
<excludedPath local="true" path="$PROJECT_DIR$/pwnagotchi.egg-info" />
<excludedPath local="true" path="$PROJECT_DIR$/dist" />
<excludedPath local="true" path="$PROJECT_DIR$/builder/packer-builder-arm" />
</excludedPaths>
</serverdata>
</paths>
</serverData>
</component>
</project>

5
.idea/misc.xml generated
View File

@ -3,5 +3,8 @@
<component name="Black">
<option name="sdkName" value="Python 3.11 (pwnagotchi)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (pwnagotchi-torch-bookworm)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" />
</component>
</project>

2
.idea/pwnagotchi.iml generated
View File

@ -4,7 +4,7 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.10 (pwnagotchi-torch-bookworm)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">

View File

@ -1,4 +1,4 @@
PACKER_VERSION := 1.10.0
PACKER_VERSION := 1.10.1
PWN_HOSTNAME := pwnagotchi
PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py)
@ -64,5 +64,5 @@ pwnagotchi: $(SDIST) builder/pwnagotchi.json.pkr.hcl builder/raspberrypi64.yml $
image: pwnagotchi
clean:
- rm -rf build dist pwnagotchi.egg-info
- rm -f $(PACKER)
- rm -rf dist pwnagotchi.egg-info
- rm -f $(PACKER)

View File

@ -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 Desktop application.
Select Application type to be Web application.
Enter an appropriate name.
@ -19,7 +19,7 @@ Input http://localhost/ for Authorized redirect URIs.
Select the correct oauth scope:
- drive
- drive.file
- drive.install
Click Create.

View File

@ -1,15 +1,19 @@
# Pwnagotchi-Torch
<a href="https://github.com/jayofelony/pwnagotchi-bookworm/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/jayofelony/pwnagotchi-bookworm.svg"></a><br/>
**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~~!!.**
**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.)
It seems the Pi 5 is unable to run in monitor mode, will keep you updated on this.
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 latest image file [here](https://github.com/jayofelony/pwnagotchi-bookworm/releases/tag/v2.6.7), and let it auto-update from here on out.
Download the latest image file [here](https://github.com/jayofelony/pwnagotchi-bookworm/releases/tag/v2.8.2), 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`.

View File

@ -50,6 +50,7 @@ def pwnagotchi_cli():
agent.mode = 'auto'
agent.start()
config = agent.config()
while True:
try:
@ -59,6 +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)
agent.set_channel(ch)
if not agent.is_stale() and agent.any_activity():
@ -66,6 +68,9 @@ 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
@ -157,7 +162,7 @@ def pwnagotchi_cli():
sys.exit(0)
if args.donate:
print("Donations can made @ https://www.patreon.com/pwnagotchi_torch \n\nBut only if you really want to!")
print("Donations can made @ https://github.com/sponsors/jayofelony \n\nBut only if you really want to!")
sys.exit(0)
if args.check_update:

View File

@ -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
metric 101

View File

@ -1,6 +1,6 @@
[Unit]
Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.
Documentation=https://pwnagotchi.ai
Documentation=https://pwnagotchi.org
Wants=network.target
After=pwngrid-peer.service
@ -8,6 +8,7 @@ After=pwngrid-peer.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

View File

@ -1,8 +1,8 @@
#!/bin/sh
_hostname=$(hostname)
_version=$(cut -d"'" -f2 < /usr/local/lib/python3.9/dist-packages/pwnagotchi/_version.py)
_version=$(cut -d"'" -f2 < /usr/local/lib/python3.11/dist-packages/pwnagotchi/_version.py)
echo
echo "(◕‿‿◕) $_hostname"
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!"
@ -30,4 +30,4 @@ echo
echo " You can restart me using"
echo " pwnkill"
echo
echo " You learn more about me at https://pwnagotchi.ai/"
echo " You can learn more about me at https://pwnagotchi.org/"

View File

@ -9,15 +9,6 @@ if is_crypted_mode; then
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 mon0
start_monitor_interface

View File

@ -13,14 +13,6 @@ blink_led() {
sleep 0.3
}
# 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
@ -39,8 +31,10 @@ reload_brcm() {
start_monitor_interface() {
rfkill unblock all
ifconfig wlan0 up
sleep 3
iw dev wlan0 set power_save off
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor
sleep 2
rfkill unblock all
ifconfig wlan0 down
ifconfig wlan0mon up
@ -54,7 +48,7 @@ stop_monitor_interface() {
ifconfig wlan0 up
}
# returns 0 if the specificed network interface is up
# returns 0 if the specified network interface is up
is_interface_up() {
if grep -qi 'up' /sys/class/net/"$1"/operstate; then
return 0

View File

@ -1,11 +1,9 @@
# This is not working quite yet
# https://github.com/mkaczanowski/packer-builder-arm/pull/172
packer {
required_plugins {
#arm = {
# version = "~> 1"
# source = "github.com/cdecoux/builder-arm"
#}
arm = {
version = "1.0.0"
source = "github.com/cdecoux/builder-arm"
}
ansible = {
source = "github.com/hashicorp/ansible"
version = "~> 1"
@ -28,8 +26,8 @@ source "arm" "rpi64-pwnagotchi" {
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
image_path = "../../../pwnagotchi-rpi-bookworm-${var.pwn_version}-arm64.img"
qemu_binary_source_path = "/usr/bin/qemu-aarch64-static"
qemu_binary_destination_path = "/usr/bin/qemu-aarch64-static"
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"
@ -51,6 +49,8 @@ source "arm" "rpi64-pwnagotchi" {
}
}
# 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
@ -82,7 +82,6 @@ build {
"data/etc/systemd/system/pwngrid-peer.service",
]
}
provisioner "file" {
destination = "/etc/update-motd.d/01-motd"
source = "data/etc/update-motd.d/01-motd"

View File

@ -6,7 +6,8 @@
vars:
kernel:
min: "6.1"
full: "6.1.0-rpi7-rpi-v8"
full: "6.1.0-rpi8-rpi-v8"
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) }}"
@ -23,7 +24,6 @@
services:
enable:
- bettercap.service
- dphys-swapfile.service
- fstrim.timer
- pwnagotchi.service
- pwngrid-peer.service
@ -32,18 +32,14 @@
- apt-daily-upgrade.timer
- apt-daily.service
- apt-daily.timer
- avahi-daemon.service
- avahi-daemon.socket
- bluetooth.service
- 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.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"
@ -55,12 +51,20 @@
- libpcap0.8-dev_1.9.1-4_arm64.deb
- libpcap0.8_1.9.1-4_arm64.deb
hold:
- firmware-atheros
- firmware-brcm80211
- firmware-libertas
- firmware-misc-nonfree
- firmware-realtek
- libpcap-dev
- libpcap0.8
- libpcap0.8-dev
- libpcap0.8-dbg
- libpcap0.8-dev
remove:
- avahi-daemon
- dhpys-swapfile
- libcurl-ocaml-dev
- libssl-ocaml-dev
- nfs-common
- triggerhappy
- wpasupplicant
@ -75,6 +79,11 @@
- dkms
- dphys-swapfile
- fbi
- firmware-atheros
- firmware-brcm80211
- firmware-libertas
- firmware-misc-nonfree
- firmware-realtek
- flex
- fonts-dejavu
- fonts-dejavu-core
@ -84,6 +93,7 @@
- gawk
- gcc-arm-none-eabi
- git
- hcxtools
- libatlas-base-dev
- libavcodec59
- libavformat59
@ -93,6 +103,7 @@
- libc-ares-dev
- libc6-dev
- libcap-dev
- libcurl-ocaml-dev
- libdbus-1-dev
- libdbus-glib-1-dev
- libeigen3-dev
@ -119,34 +130,30 @@
- libraspberrypi0
- libsqlite3-dev
- libssl-dev
- libssl-ocaml-dev
- libswscale5
- libtiff6
- libtool
- libusb-1.0-0-dev
- lsof
- make
- python3-yaml
- python3-dbus
- python3-flask
- python3-flask-cors
- python3-flaskext.wtf
- python3-gast
- python3-pil
- python3-pip
- python3-pycryptodome
- python3-requests
- python3-scapy
- python3-setuptools
- python3-smbus
- python3-smbus2
- python3-spidev
- python3-tweepy
- python3-werkzeug
- firmware-atheros
- firmware-brcm80211
- firmware-libertas
- firmware-misc-nonfree
- firmware-realtek
- python3-pip
- python3-setuptools
- python3-smbus
- python3-yaml
- qpdf
- raspberrypi-kernel-headers
- rsync
@ -163,15 +170,51 @@
- zlib1g-dev
environment:
ARCHFLAGS: "-arch aarch64"
QEMU_UNAME: "{{ kernel.full }}"
tasks:
# First we install packages
- name: install packages
apt:
name: "{{ packages.apt.install }}"
state: present
update_cache: yes
install_recommends: false
# Now we set up /boot/firmware
- name: Create pi user
copy:
dest: /boot/firmware/userconf
content: |
pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
- name: enable ssh on boot
file:
path: /boot/firmware/ssh
state: touch
- name: adjust /boot/firmware/config.txt
lineinfile:
dest: /boot/firmware/config.txt
insertafter: EOF
line: '{{ item }}'
with_items: "{{ system.boot_options }}"
- name: change root partition
replace:
dest: /boot/firmware/cmdline.txt
backup: no
regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+"
replace: "root=/dev/mmcblk0p2"
- name: configure /boot/firmware/cmdline.txt
lineinfile:
path: /boot/firmware/cmdline.txt
backrefs: True
state: present
backup: no
regexp: '(.*)$'
line: '\1 modules-load=dwc2,g_ether'
- name: change hostname
lineinfile:
dest: /etc/hostname
@ -189,6 +232,7 @@
state: present
when: hostname.changed
# Now we disable sap and a2dp, we don't use them on rpi
- name: disable sap plugin for bluetooth.service
lineinfile:
dest: /lib/systemd/system/bluetooth.service
@ -196,19 +240,6 @@
line: 'ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp'
state: present
- name: configure dphys-swapfile
lineinfile:
path: /etc/dphys-swapfile
regexp: "^CONF_SWAPSIZE=.*$"
line: "CONF_SWAPSIZE=2048"
- name: install packages
apt:
name: "{{ packages.apt.install }}"
state: present
update_cache: yes
install_recommends: false
###########################################
#
# libpcap v1.9 - build from source
@ -242,35 +273,116 @@
dest: /usr/local/lib/libpcap.so.0.8
state: link
# Install nexmon to fix wireless scanning (takes 2.5G of space)
# install latest hcxtools
- name: clone hcxtools
git:
repo: https://github.com/ZerBea/hcxtools.git
dest: /usr/local/src/hcxtools
- name: install hcxtools
shell: "make && make install"
args:
executable: /bin/bash
chdir: /usr/local/src/hcxtools
- name: remove hcxtools directory
file:
state: absent
path: /usr/local/src/hcxtools
- name: clone nexmon repository
git:
repo: https://github.com/DrSchottky/nexmon.git
dest: /usr/local/src/nexmon
- name: make firmware
# FIRST WE BUILD DRIVER FOR RPi5
- name: make firmware, RPi5
shell: "source ./setup_env.sh && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full_pi5 }}"
ARCHFLAGS: "-arch aarch64"
- name: make firmware patch (bcm43455c0)
- name: make firmware patch (bcm43455c0), RPi5
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full_pi5 }}"
ARCHFLAGS: "-arch aarch64"
- name: install new firmware (bcm43455c0)
- name: copy modified driver, RPi5
copy:
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
dest: "/usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
environment:
QEMU_UNAME: "{{ kernel.full_pi5 }}"
ARCHFLAGS: "-arch aarch64"
- name: Delete the modified driver, RPi5
file:
state: absent
path: '/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko'
- name: backup original driver, RPi5
command: "mv /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
- name: load brcmfmac drivers
command: "/sbin/depmod {{ kernel.full_pi5 }}"
environment:
QEMU_UNAME: "{{ kernel.full_pi5 }}"
- name: Delete nexmon content & directory
file:
state: absent
path: /usr/local/src/nexmon/
# NOW WE BUILD DRIVERS FOR RPi4, RPizero2w and RPi3
- name: clone nexmon repository
git:
repo: https://github.com/DrSchottky/nexmon.git
dest: /usr/local/src/nexmon
- name: make firmware, RPi4
shell: "source ./setup_env.sh && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch aarch64"
- name: make firmware patch (bcm43455c0), RPi4
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch aarch64"
- name: install new firmware (bcm43455c0), RPi4 RPi5
copy:
src: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/brcmfmac43455-sdio.bin
dest: /usr/lib/firmware/brcm/brcmfmac43455-sdio.bin
follow: true
# NOW WE BUILD DRIVERS FOR RPiZero2W, RPi 3
- name: make firmware patch (bcm43436b0)
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch aarch64"
- name: install new firmware (bcm43436b0)
copy:
@ -283,6 +395,17 @@
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch aarch64"
- name: copy modified driver, RPi4
copy:
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
dest: "/usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch aarch64"
- name: install new firmware (bcm43430a1)
copy:
@ -313,13 +436,10 @@
- name: backup original driver
command: "mv /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
- name: copy modified driver
copy:
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
dest: "/usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
- name : load brcmfmac drivers
command: "/sbin/depmod -a"
- name: load brcmfmac drivers
command: "/sbin/depmod {{ kernel.full }}"
environment:
QEMU_UNAME: "{{ kernel.full }}"
# To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation
- name: Delete nexmon content & directory
@ -394,7 +514,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
@ -477,41 +597,6 @@
state: absent
path: /etc/update-motd.d/10-uname
- name: enable ssh on boot
file:
path: /boot/firmware/ssh
state: touch
- name: adjust /boot/config.txt
lineinfile:
dest: /boot/firmware/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/firmware/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 pwnlog alias
lineinfile:
dest: /home/pi/.bashrc
@ -563,18 +648,6 @@
group: pi
recurse: true
- name: remove unnecessary apt packages
apt:
name: "{{ packages.apt.remove }}"
state: absent
purge: yes
register: removed
- name: clean apt cache
apt:
autoclean: true
when: removed.changed
- name: remove pre-collected packages zip
file:
path: /root/go_pkgs.tgz
@ -595,11 +668,6 @@
state: absent
path: /root/.cache/pip
- name: remove dependencies that are no longer required
apt:
autoremove: yes
when: removed.changed
- name: remove ssh keys
file:
state: absent
@ -612,6 +680,24 @@
args:
executable: /bin/bash
# Now we remove packages
- name: remove unnecessary apt packages
apt:
name: "{{ packages.apt.remove }}"
state: absent
purge: yes
register: removed
- name: remove dependencies that are no longer required
apt:
autoremove: yes
when: removed.changed
- name: clean apt cache
apt:
autoclean: true
when: removed.changed
handlers:
- name: reload systemd services
systemd:

View File

@ -1 +1 @@
__version__ = '2.7.1'
__version__ = '2.8.4'

View File

@ -21,17 +21,17 @@ RECOVERY_DATA_FILE = '/root/.pwnagotchi-recovery'
class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
def __init__(self, view, config, keypair):
Client.__init__(self, config['bettercap']['hostname'],
config['bettercap']['scheme'],
config['bettercap']['port'],
config['bettercap']['username'],
config['bettercap']['password'])
Client.__init__(self,
"127.0.0.1" if "hostname" not in config['bettercap'] else config['bettercap']['hostname'],
"http" if "scheme" not in config['bettercap'] else config['bettercap']['scheme'],
8081 if "port" not in config['bettercap'] else config['bettercap']['port'],
"pwnagotchi" if "username" not in config['bettercap'] else config['bettercap']['username'],
"pwnagotchi" if "password" not in config['bettercap'] else config['bettercap']['password'])
Automata.__init__(self, config, view)
AsyncAdvertiser.__init__(self, config, view, keypair)
AsyncTrainer.__init__(self, config)
self._started_at = time.time()
self._filter = None if not config['main']['filter'] else re.compile(config['main']['filter'])
self._current_channel = 0
self._tot_aps = 0
self._aps_on_channel = 0
@ -164,11 +164,6 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
self.wait_for(recon_time, sleeping=False)
def _filter_included(self, ap):
return self._filter is None or \
self._filter.match(ap['hostname']) is not None or \
self._filter.match(ap['mac']) is not None
def set_access_points(self, aps):
self._access_points = aps
plugins.on('wifi_update', self, aps)
@ -184,13 +179,10 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
for ap in s['wifi']['aps']:
if ap['encryption'] == '' or ap['encryption'] == 'OPEN':
continue
elif ap['hostname'] in whitelist or ap['mac'][:8].lower() in whitelist:
elif ap['hostname'] in whitelist or ap['mac'][:13].lower() in whitelist or ap['mac'].lower() in whitelist:
continue
elif ap['hostname'] not in whitelist \
and ap['mac'].lower() not in whitelist \
and ap['mac'][:8].lower() not in whitelist:
if self._filter_included(ap):
aps.append(ap)
else:
aps.append(ap)
except Exception as e:
logging.exception("Error while getting access points (%s)", e)
@ -277,6 +269,10 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
self._save_recovery_data()
pwnagotchi.reboot()
def _restart(self):
self._save_recovery_data()
pwnagotchi.restart("AUTO")
def _save_recovery_data(self):
logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE)
with open(RECOVERY_DATA_FILE, 'w') as fp:
@ -429,7 +425,6 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
if self.is_stale():
logging.debug("recon is stale, skipping assoc(%s)", ap['mac'])
return
if throttle == -1 and "throttle_a" in self._config['personality']:
throttle = self._config['personality']['throttle_a']

View File

@ -59,7 +59,8 @@ def load(config, agent, epoch, from_disk=True):
return a2c
except Exception as e:
logging.exception("[AI] error while starting AI (%s)", e)
logging.info("[AI] Error while starting AI")
logging.debug("[AI] error while starting AI (%s)", e)
logging.info("[AI] Deleting brain and restarting.")
os.system("rm /root/brain.nn && service pwnagotchi restart")

View File

@ -138,6 +138,6 @@ class Automata(object):
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']:
logging.critical("%d epochs without visible access points -> rebooting ...", self._epoch.blind_for)
self._reboot()
logging.critical("%d epochs without visible access points -> restarting ...", self._epoch.blind_for)
self._restart()
self._epoch.blind_for = 0

View File

@ -1,5 +1,11 @@
main.name = "pwnagotchi"
main.lang = "en"
main.whitelist = [
"EXAMPLE_NETWORK",
"ANOTHER_EXAMPLE_NETWORK",
"fo:od:ba:be:fo:od",
"fo:od:ba"
]
main.confd = "/etc/pwnagotchi/conf.d/"
main.custom_plugin_repos = [
"https://github.com/jayofelony/pwnagotchi-torch-plugins/archive/master.zip",
@ -12,6 +18,10 @@ 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
@ -55,9 +65,6 @@ main.plugins.gps.device = "/dev/ttyUSB0" # for GPSD: "localhost:2947"
main.plugins.grid.enabled = true
main.plugins.grid.report = true
main.plugins.grid.exclude = [
"YourHomeNetworkHere"
]
main.plugins.logtail.enabled = false
main.plugins.logtail.max-lines = 10000
@ -73,10 +80,6 @@ main.plugins.onlinehashcrack.enabled = false
main.plugins.onlinehashcrack.email = ""
main.plugins.onlinehashcrack.dashboard = ""
main.plugins.onlinehashcrack.single_files = false
main.plugins.onlinehashcrack.whitelist = []
main.plugins.paw-gps.enabled = false
main.plugins.paw-gps.ip = "192.168.44.1:8080"
main.plugins.pisugar2.enabled = false
main.plugins.pisugar2.shutdown = 5
@ -100,26 +103,19 @@ main.plugins.webgpsmap.enabled = false
main.plugins.wigle.enabled = false
main.plugins.wigle.api_key = ""
main.plugins.wigle.whitelist = []
main.plugins.wigle.donate = true
main.plugins.wigle.donate = false
main.plugins.wpa-sec.enabled = false
main.plugins.wpa-sec.api_key = ""
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
main.plugins.wpa-sec.download_results = false
main.plugins.wpa-sec.whitelist = []
main.iface = "wlan0mon"
main.mon_start_cmd = "/usr/bin/monstart"
main.mon_stop_cmd = "/usr/bin/monstop"
main.mon_max_blind_epochs = 50
main.no_restart = false
main.whitelist = [
"EXAMPLE_NETWORK",
"ANOTHER_EXAMPLE_NETWORK",
"fo:od:ba:be:fo:od",
"fo:od:ba"
]
main.filter = ""
main.log.path = "/home/pi/logs/pwnagotchi.log"
@ -160,6 +156,8 @@ 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.fps = 0.0
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
ui.font.size_offset = 0 # will be added to the font size
@ -202,11 +200,6 @@ ui.display.enabled = false
ui.display.rotation = 180
ui.display.type = "waveshare_4"
bettercap.scheme = "http"
bettercap.hostname = "localhost"
bettercap.port = 8081
bettercap.username = "pwnagotchi"
bettercap.password = "pwnagotchi"
bettercap.handshakes = "/root/handshakes"
bettercap.silence = [
"ble.device.new",

View File

@ -1,14 +1,13 @@
# SOME DESCRIPTIVE TITLE.
# Pwnagotchi display English to Esperanto.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# FIRST AUTHOR MADE THIS IN YEAR 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
"POT-Creation-Date: 2024-01-25 23:40+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,218 +17,219 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr ""
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr ""
msgstr "Sal, mi estas Pwnagotchi! Komencante…"
msgid "New day, new hunt, new pwns!"
msgstr ""
msgstr "Nova tago, nova ĉaso, nova wifi!"
msgid "Hack the Planet!"
msgstr ""
msgstr "Eniru la elektronikon!"
msgid "AI ready."
msgstr ""
msgstr "Mi pretas."
msgid "The neural network is ready."
msgstr ""
msgstr "La elektronika reto estas preta."
msgid "Generating keys, do not turn off ..."
msgstr ""
msgstr "Mi generas ŝlosilojn, ne malŝaltu min!"
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr ""
msgstr "Hej, kanalo {channel} disponeblas! Via alirpunkto dankos vin"
msgid "Reading last session logs ..."
msgstr ""
msgstr "Legante protokolojn de antaŭa sesio…"
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr ""
msgstr "legi {lines_so_far} liniojn ĝis nun…"
msgid "I'm bored ..."
msgstr ""
msgstr "Mi enuas…"
msgid "Let's go for a walk!"
msgstr ""
msgstr "Ni iru promeni!"
msgid "This is the best day of my life!"
msgstr ""
msgstr "Plej bona tago de mia vivo!"
msgid "Shitty day :/"
msgstr ""
msgstr "Terura tago :/"
msgid "I'm extremely bored ..."
msgstr ""
msgstr "mi estas tre enuigita…"
msgid "I'm very sad ..."
msgstr ""
msgstr "Mi estas tre malĝoja…"
msgid "I'm sad"
msgstr ""
msgstr "Mi estas malfeliĉa"
msgid "Leave me alone ..."
msgstr ""
msgstr "Lasu min sola…"
msgid "I'm mad at you!"
msgstr ""
msgstr "Mi koleras kontraŭ vi!"
msgid "I'm living the life!"
msgstr ""
msgstr "Mi ĝuas la vivon!"
msgid "I pwn therefore I am."
msgstr ""
msgstr "Mi eniras tial mi estas."
msgid "So many networks!!!"
msgstr ""
msgstr "Tiom da ludiloj!"
msgid "I'm having so much fun!"
msgstr ""
msgstr "Mi tre amuzas!"
msgid "My crime is that of curiosity ..."
msgstr ""
msgstr "Scivolemo estas mia krimo…"
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr ""
msgstr "Sal {name}! Mi ĝojas renkonti vin."
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr ""
msgstr "Hej {name}! Sal?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr ""
msgstr "Sal {name}! Kiel vi fartas?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr ""
msgstr "Iu estas proksime! Ĝia nomo estas {name}."
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr ""
msgstr "Adiaŭ {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr ""
msgstr "{name} malaperis…"
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr ""
msgstr "Hups… {name} malaperis…"
#, python-brace-format
msgid "{name} missed!"
msgstr ""
msgstr "{name} mankis!"
msgid "Missed!"
msgstr ""
msgstr "Maltrafis!"
msgid "Good friends are a blessing!"
msgstr ""
msgstr "Bonaj amikoj estas beno!"
msgid "I love my friends!"
msgstr ""
msgstr "Mi amas miajn amikojn!"
msgid "Nobody wants to play with me ..."
msgstr ""
msgstr "Neniu volas ludi kun mi..."
msgid "I feel so alone ..."
msgstr ""
msgstr "Mi estas tiel sola..."
msgid "Where's everybody?!"
msgstr ""
msgstr "KIE ĈIUJ ESTAS?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr ""
msgstr "Dormeto por {sec}j…"
msgid "Zzzzz"
msgstr ""
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
msgstr "ZzzZzzz ({sec}j)"
msgid "Good night."
msgstr ""
msgstr "Bonan nokton"
msgid "Zzz"
msgstr ""
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr ""
msgstr "Atendas {sec}j…"
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr ""
msgstr "Ĉirkaŭrigardante ({sec}j)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr ""
msgstr "Hej {what}, ni estu amikoj!"
#, python-brace-format
msgid "Associating to {what}"
msgstr ""
msgstr "asociante al {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
msgstr "Hej {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr ""
msgstr "Ĵus decidis, ke {mac} ne bezonas konekton!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr ""
msgstr "Malaŭtentigi {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr ""
msgstr "Forigante {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr ""
msgstr "Mirinda, ni havas {num} novajn manpremojn!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr ""
msgstr "Vi nun havas {num} novajn mesaĝojn"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr ""
msgstr "Lo okazis... Rekomencante…"
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr ""
msgstr "alŝuti datumojn al {to}…"
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr ""
msgstr "Elŝutu de {name}…"
#, python-brace-format
#, fuzzy, python-brace-format
msgid "Kicked {num} stations\n"
msgstr ""
msgstr "Forigita de {num} stacioj"
#, fuzzy
msgid "Made >999 new friends\n"
msgstr ""
msgstr "Faris pli ol 999 novajn amikojn!"
#, python-brace-format
#, fuzzy, python-brace-format
msgid "Made {num} new friends\n"
msgstr ""
msgstr "faris (nombro) novajn amikojn"
#, python-brace-format
#, fuzzy, python-brace-format
msgid "Got {num} handshakes\n"
msgstr ""
msgstr "Ricevis {num} novajn manpremojn"
msgid "Met 1 peer"
msgstr ""
msgstr "Renkontita unu kolego"
#, python-brace-format
msgid "Met {num} peers"
msgstr ""
msgstr "renkontitajn {num} kolegojn"
#, python-brace-format
msgid ""
@ -237,21 +237,24 @@ msgid ""
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"Mi pwning dum {duration} kaj piedbatis {deauthed} klientojn!Mi ankaŭ "
"renkontis {associated} novajn amikojn kaj manĝis {handshakes} manpremojn!"
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr ""
msgstr "horror"
msgid "minutes"
msgstr ""
msgstr "minutoj"
msgid "seconds"
msgstr ""
msgstr "sekundoj"
msgid "hour"
msgstr ""
msgstr "horo"
msgid "minute"
msgstr ""
msgstr "minuto"
msgid "second"
msgstr ""
msgstr "dua"

View File

@ -16,7 +16,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr ""
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Ciao! Piacere Pwnagotchi! Caricamento ..."
@ -25,7 +25,7 @@ msgid "New day, new hunt, new pwns!"
msgstr "Nuovo giorno...nuovi handshakes!!!"
msgid "Hack the Planet!"
msgstr ""
msgstr "Hack il Pianeta"
msgid "AI ready."
msgstr "IA pronta."
@ -34,18 +34,18 @@ msgid "The neural network is ready."
msgstr "La rete neurale è pronta."
msgid "Generating keys, do not turn off ..."
msgstr ""
msgstr "Generazione di chiavi, non spegnere"
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Hey, il canale {channel} è libero! Il tuo AP ringrazia."
msgid "Reading last session logs ..."
msgstr ""
msgstr "Lettura dei log dell'ultima sessione ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr ""
msgstr "Leggi le righe di log {lines_so_far} finora ..."
msgid "I'm bored ..."
msgstr "Che noia ..."
@ -54,7 +54,7 @@ msgid "Let's go for a walk!"
msgstr "Andiamo a fare una passeggiata!"
msgid "This is the best day of my life!"
msgstr "Questo è il più bel giorno della mia vita!!!!"
msgstr "Questo e il miglior giorno della mia vita!!!!"
msgid "Shitty day :/"
msgstr "Giorno di merda :/"
@ -72,22 +72,22 @@ msgid "Leave me alone ..."
msgstr "Mi sento così solo..."
msgid "I'm mad at you!"
msgstr ""
msgstr "sono arabiata con te"
msgid "I'm living the life!"
msgstr "Mi sento vivo!"
msgstr "sono viva la vita!"
msgid "I pwn therefore I am."
msgstr "Pwn ergo sum."
msgid "So many networks!!!"
msgstr "Qui è pieno di reti!"
msgstr "Qui pieno di reti!"
msgid "I'm having so much fun!"
msgstr "Mi sto divertendo tantissimo!"
msgid "My crime is that of curiosity ..."
msgstr ""
msgstr "Il mio crimine ? quello della curiosit?"
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
@ -95,15 +95,15 @@ msgstr "Ciao {name}! E' un piacere."
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr ""
msgstr "Yo {name} Come va"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr ""
msgstr "Ehi {name} come stai?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr "L'Unità {name} è vicina!"
msgstr "L'Unit {name} e vicina!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
@ -111,30 +111,30 @@ msgstr "Uhm ... addio {name}, mi mancherai..."
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} se n'è andato ..."
msgstr "{name} se andato ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Whoops ...{name} se n'è andato."
msgstr "Whoops ...{name} se andato."
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} è scomparso..."
msgstr "{name} scomparso..."
msgid "Missed!"
msgstr "Ehi! Dove sei andato!?"
msgid "Good friends are a blessing!"
msgstr ""
msgstr "Buoni amici sono una benedizione"
msgid "I love my friends!"
msgstr ""
msgstr "Amo i miei amici"
msgid "Nobody wants to play with me ..."
msgstr "Nessuno vuole giocare con me..."
msgid "I feel so alone ..."
msgstr "Mi sento così solo..."
msgstr "Mi sento cos solo..."
msgid "Where's everybody?!"
msgstr "Dove sono tutti?!"
@ -144,17 +144,17 @@ msgid "Napping for {secs}s ..."
msgstr "Schiaccio un pisolino per {secs}s ..."
msgid "Zzzzz"
msgstr ""
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
msgstr "ZzzZzzz ({secs}s)"
msgid "Good night."
msgstr ""
msgstr "Buona notte"
msgid "Zzz"
msgstr ""
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
@ -182,7 +182,7 @@ msgstr "Ho appena deciso che {mac} non necessita di WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr ""
msgstr "Annullamento dell'autenticazione {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
@ -201,11 +201,11 @@ msgstr "Ops, qualcosa è andato storto ... Riavvio ..."
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr ""
msgstr "Caricamento dei dati in {to}"
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr ""
msgstr "Scaricamento da {name} ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
@ -255,4 +255,4 @@ msgid "minute"
msgstr "minuto"
msgid "second"
msgstr "secondo"
msgstr "secondo"

View File

@ -14,7 +14,7 @@ msgstr ""
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Portuguese (Brazil)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"

View File

@ -3,12 +3,12 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-17 15:46+0100\n"
"POT-Creation-Date: 2024-01-25 23:40+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -0,0 +1,73 @@
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 <none>":
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 = ""

View File

@ -130,7 +130,7 @@ def install(display, update):
source_path = "%s-%s" % (source_path, update['available'])
# setup.py is going to install data files for us
os.system("cd %s && pip3 install . --no-cache-dir --break-system-packages" % source_path)
os.system("cd %s && pip3 install . --break-system-packages" % source_path)
return True
@ -212,7 +212,7 @@ class AutoUpdate(plugins.Plugin):
if install(display, update):
num_installed += 1
else:
prev_status = '%d new update%c available!' % (num_updates, 's' if num_updates > 1 else '')
prev_status = '%d new update%s available!' % (num_updates, 's' if num_updates > 1 else '')
logging.info("[update] done")

View File

@ -21,19 +21,15 @@ class FixServices(plugins.Plugin):
__help__ = """
Reload brcmfmac module when blindbug is detected, instead of rebooting. Adapted from WATCHDOG.
"""
__dependencies__ = {
'pip': ['scapy']
}
__defaults__ = {
'enabled': True,
}
def __init__(self):
self.options = dict()
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.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.isReloadingMon = False
self.connection = None
self.LASTTRY = 0
@ -46,27 +42,14 @@ class FixServices(plugins.Plugin):
logging.info("[Fix_Services] plugin loaded.")
def on_ready(self, agent):
last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'],
stdout=subprocess.PIPE).stdout))[-10:])
last_lines = self.get_last_lines('journalctl', ['-n10', '-k'], 10)
try:
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
logging.info("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
logging.debug("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
if ",UP," in str(cmd_output):
logging.info("wlan0mon is up.")
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.info('[Fix_Services] Blind-Bug detected. Restarting.')
try:
self._tryTurningItOffAndOnAgain(agent)
except Exception as err:
logging.warning("[Fix_Services turnOffAndOn] %s" % repr(err))
else:
logging.info("[Fix_Services] Logs look good!")
logging.info("[Fix_Services] Logs look good!")
except Exception as err:
logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err))
@ -99,14 +82,23 @@ 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 = ''.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', '/home/pi/logs/pwnagotchi.log'],
stdout=subprocess.PIPE).stdout))[-10:])
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)
# don't check if we ran a reset recently
logging.debug("[Fix_Services]**** epoch")
if time.time() - self.LASTTRY > 180:
@ -116,21 +108,8 @@ class FixServices(plugins.Plugin):
logging.debug("[Fix_Services]**** checking")
# Look for pattern 1
if len(self.pattern.findall(last_lines)) >= 3:
logging.info("[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.info('[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.info("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
if len(self.pattern1.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.')
@ -152,8 +131,8 @@ class FixServices(plugins.Plugin):
except Exception as err:
logging.error("[Fix_Services wifi.recon flip] %s" % repr(err))
# Look for pattern 3
elif len(self.pattern3.findall(other_last_lines)) >= 1:
# Look for pattern 2
elif len(self.pattern2.findall(other_last_lines)) >= 1:
logging.info("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
if hasattr(agent, 'view'):
display = agent.view()
@ -162,12 +141,12 @@ class FixServices(plugins.Plugin):
try:
# Run the monstart command to restart wlan0mon
cmd_output = subprocess.check_output("monstart", shell=True)
logging.info("[Fix_Services monstart]: %s" % repr(cmd_output))
logging.debug("[Fix_Services monstart]: %s" % repr(cmd_output))
except Exception as err:
logging.error("[Fix_Services monstart]: %s" % repr(err))
# Look for pattern 4
elif len(self.pattern4.findall(other_other_last_lines)) >= 3:
# Look for pattern 3
elif len(self.pattern3.findall(other_other_last_lines)) >= 3:
logging.info("[Fix_Services] wlan0 is down!")
if hasattr(agent, 'view'):
display = agent.view()
@ -176,7 +155,7 @@ class FixServices(plugins.Plugin):
try:
# Run the monstart command to restart wlan0mon
cmd_output = subprocess.check_output("monstart", shell=True)
logging.info("[Fix_Services monstart]: %s" % repr(cmd_output))
logging.debug("[Fix_Services monstart]: %s" % repr(cmd_output))
except Exception as err:
logging.error("[Fix_Services monstart]: %s" % repr(err))
@ -232,7 +211,7 @@ class FixServices(plugins.Plugin):
# is it up?
try:
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
logging.info("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
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?")
# not reliable, so don't skip just yet
@ -304,11 +283,9 @@ class FixServices(plugins.Plugin):
# stop looping and get back to recon
break
else:
logging.info(
"[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.info(
"[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)))
@ -357,7 +334,7 @@ class FixServices(plugins.Plugin):
"face": faces.HAPPY})
else:
print("I can see again")
logging.info("[Fix_Services] wifi.recon on")
logging.debug("[Fix_Services] wifi.recon on")
self.LASTTRY = time.time() + 120 # 2-minute pause until next time.
else:
logging.error("[Fix_Services] wifi.recon did not start up")
@ -373,7 +350,7 @@ class FixServices(plugins.Plugin):
try:
logging.info("[Fix_Services] unloaded")
except Exception as err:
logging.info("[Fix_Services] unload err %s " % repr(err))
logging.error("[Fix_Services] unload err %s " % repr(err))
pass

View File

@ -14,19 +14,15 @@ import zipfile
class GdriveSync(plugins.Plugin):
__author__ = '@jayofelony'
__version__ = '1.0'
__version__ = '1.2'
__license__ = 'GPL3'
__description__ = 'A plugin to backup various pwnagotchi files and folders to Google Drive. Once every hour from loading plugin.'
__dependencies__ = {
'pip': ['pydrive2']
}
def __init__(self):
self.options = dict()
self.lock = Lock()
self.internet = False
self.ready = False
self.drive = None
self.status = StatusFile('/root/.gdrive-backup')
self.backup = True
self.backupfiles = [
@ -38,12 +34,11 @@ class GdriveSync(plugins.Plugin):
'/etc/pwnagotchi'
]
def on_loaded(self, agent):
def on_loaded(self):
"""
Called when the plugin is loaded
"""
# client_secrets.json needs to be not empty
display = agent.view()
if os.stat("/root/client_secrets.json").st_size == 0:
logging.error("[gDriveSync] /root/client_secrets.json is empty. Please RTFM!")
return
@ -78,25 +73,24 @@ class GdriveSync(plugins.Plugin):
# logging.warning(f"[gDriveSync] No files found in the folder with ID {root_file_list} and {pwnagotchi_file_list}")
if self.options['backupfiles'] is not None:
self.backupfiles = self.backupfiles + self.options['backupfiles']
self.backup_files(self.backupfiles, '/backup')
self.backup_files(self.backupfiles, '/home/pi/backup')
# Create a zip archive of the /backup folder
zip_file_path = os.path.join('/home/pi', 'backup.zip')
with zipfile.ZipFile(zip_file_path, 'w') as zip_ref:
for root, dirs, files in os.walk('/backup'):
for root, dirs, files in os.walk('/home/pi/backup'):
for file in files:
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, '/backup')
arcname = os.path.relpath(file_path, '/home/pi/backup')
zip_ref.write(file_path, arcname=arcname)
# Upload the zip archive to Google Drive
self.upload_to_gdrive(zip_file_path, self.get_folder_id_by_name(self.drive, self.options['backup_folder']))
display.on_uploading("Google Drive")
self.backup = True
self.status.update()
# Specify the local backup path
local_backup_path = '/'
local_backup_path = '/home/pi/'
# Download the zip archive from Google Drive
zip_file_id = self.get_latest_backup_file_id(self.options['backup_folder'])
@ -104,7 +98,6 @@ class GdriveSync(plugins.Plugin):
zip_file = self.drive.CreateFile({'id': zip_file_id})
zip_file.GetContentFile(os.path.join(local_backup_path, 'backup.zip'))
display.on_downloading("Google Drive")
logging.info("[gDriveSync] Downloaded backup.zip from Google Drive")
# Extract the zip archive to the root directory
@ -112,9 +105,12 @@ class GdriveSync(plugins.Plugin):
zip_ref.extractall('/')
self.status.update()
os.remove("/backup")
# Reboot so we can start opwngrid with the backup id
pwnagotchi.reboot()
shutil.rmtree("/home/pi/backup")
os.remove("/home/pi/backup.zip")
self.ready = True
logging.info("[gdrivesync] loaded")
# Restart so we can start opwngrid with the backup id
pwnagotchi.restart("AUTO")
# all set, gdriveSync is ready to run
self.ready = True
@ -186,15 +182,15 @@ class GdriveSync(plugins.Plugin):
logging.info("[gdrivesync] new handshake captured, backing up to gdrive")
if self.options['backupfiles'] is not None:
self.backupfiles = self.backupfiles + self.options['backupfiles']
self.backup_files(self.backupfiles, '/backup')
self.backup_files(self.backupfiles, '/home/pi/backup')
# Create a zip archive of the /backup folder
zip_file_path = os.path.join('/home/pi', 'backup.zip')
with zipfile.ZipFile(zip_file_path, 'w') as zip_ref:
for root, dirs, files in os.walk('/backup'):
for root, dirs, files in os.walk('/home/pi/backup'):
for file in files:
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, '/backup')
arcname = os.path.relpath(file_path, '/home/pi/backup')
zip_ref.write(file_path, arcname=arcname)
# Upload the zip archive to Google Drive
@ -203,7 +199,7 @@ class GdriveSync(plugins.Plugin):
# Cleanup the local zip file
os.remove(zip_file_path)
os.remove("/backup")
shutil.rmtree("/home/pi/backup")
self.status.update()
display = agent.view()
display.update(force=True, new_data={'Backing up to gdrive ...'})

View File

@ -20,6 +20,7 @@ class GPS(plugins.Plugin):
def __init__(self):
self.running = False
self.coordinates = None
self.options = dict()
def on_loaded(self):
logging.info(f"gps plugin loaded for {self.options['device']}")

View File

@ -58,8 +58,9 @@ class Grid(plugins.Plugin):
self.total_messages = 0
self.lock = Lock()
def is_excluded(self, what):
for skip in self.options['exclude']:
def is_excluded(self, what, agent):
config = agent.config()
for skip in config['main']['whitelist']:
skip = skip.lower()
what = what.lower()
if skip in what or skip.replace(':', '') in what:
@ -87,6 +88,7 @@ class Grid(plugins.Plugin):
def check_handshakes(self, agent):
logging.debug("checking pcaps")
config = agent.config()
pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap"))
num_networks = len(pcap_files)
@ -98,19 +100,19 @@ class Grid(plugins.Plugin):
if self.options['report']:
logging.info("grid: %d new networks to report" % num_new)
logging.debug("self.options: %s" % self.options)
logging.debug(" exclude: %s" % self.options['exclude'])
logging.debug(" exclude: %s" % config['main']['whitelist'])
for pcap_file in pcap_files:
net_id = os.path.basename(pcap_file).replace('.pcap', '')
if net_id not in reported:
if self.is_excluded(net_id):
if self.is_excluded(net_id, agent):
logging.debug("skipping %s due to exclusion filter" % pcap_file)
self.set_reported(reported, net_id)
continue
essid, bssid = parse_pcap(pcap_file)
if bssid:
if self.is_excluded(essid) or self.is_excluded(bssid):
if self.is_excluded(essid, agent) or self.is_excluded(bssid, agent):
logging.debug("not reporting %s due to exclusion filter" % pcap_file)
self.set_reported(reported, net_id)
else:

View File

@ -0,0 +1,179 @@
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 pcap'.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 pcap'.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('.pcap')]
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)))

View File

@ -24,6 +24,7 @@ class NetPos(plugins.Plugin):
self.skip = list()
self.ready = False
self.lock = threading.Lock()
self.options = dict()
def on_loaded(self):
if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']):

View File

@ -25,6 +25,7 @@ class OnlineHashCrack(plugins.Plugin):
self.report = StatusFile('/root/.ohc_uploads', data_format='json')
self.skip = list()
self.lock = Lock()
self.options = dict()
def on_loaded(self):
"""
@ -34,13 +35,9 @@ class OnlineHashCrack(plugins.Plugin):
logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com")
return
if 'whitelist' not in self.options:
self.options['whitelist'] = list()
self.ready = True
logging.info("OHC: OnlineHashCrack plugin loaded.")
def _upload_to_ohc(self, path, timeout=30):
"""
Uploads the file to onlinehashcrack.com
@ -78,7 +75,6 @@ class OnlineHashCrack(plugins.Plugin):
except OSError as os_e:
raise os_e
def on_webhook(self, path, request):
import requests
from flask import redirect
@ -87,7 +83,6 @@ class OnlineHashCrack(plugins.Plugin):
r = s.post('https://www.onlinehashcrack.com/dashboard', data={'emailTasks': self.options['email'], 'submit': ''})
return redirect(r.url, code=302)
def on_internet_available(self, agent):
"""
Called in manual mode when there's internet connectivity
@ -105,7 +100,7 @@ class OnlineHashCrack(plugins.Plugin):
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if
filename.endswith('.pcap')]
# pull out whitelisted APs
handshake_paths = remove_whitelisted(handshake_paths, self.options['whitelist'])
handshake_paths = remove_whitelisted(handshake_paths, config['main']['whitelist'])
handshake_new = set(handshake_paths) - set(reported) - set(self.skip)
if handshake_new:
logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onlinehashcrack.com")

View File

@ -1,39 +0,0 @@
import logging
import requests
import pwnagotchi.plugins as plugins
'''
You need an bluetooth connection to your android phone which is running PAW server with the GPS "hack" from Systemik and edited by shaynemk
GUIDE HERE: https://community.pwnagotchi.ai/t/setting-up-paw-gps-on-android
'''
class PawGPS(plugins.Plugin):
__author__ = 'leont'
__version__ = '1.0.1'
__name__ = 'pawgps'
__license__ = 'GPL3'
__description__ = 'Saves GPS coordinates whenever an handshake is captured. The GPS data is get from PAW on android.'
def on_loaded(self):
logging.info("[paw-gps] plugin loaded")
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None) or (len('ip' in self.options and self.options['ip']) is 0):
logging.info("[paw-gps] no IP Address defined in the config file, will uses paw server default (192.168.44.1:8080)")
def on_handshake(self, agent, filename, access_point, client_station):
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None or (len('ip' in self.options and self.options['ip']) is 0)):
ip = "192.168.44.1:8080"
else:
ip = self.options['ip']
try:
gps = requests.get('http://' + ip + '/gps.xhtml')
try:
gps_filename = filename.replace('.pcap', '.paw-gps.json')
logging.info("[paw-gps] saving GPS data to %s" % (gps_filename))
with open(gps_filename, 'w+t') as f:
f.write(gps.text)
except Exception as error:
logging.error(f"[paw-gps] encountered error while saving gps data: {error}")
except Exception as error:
logging.error(f"[paw-gps] encountered error while getting gps data: {error}")

View File

@ -1,14 +1,20 @@
# Based on UPS Lite v1.1 from https://github.com/xenDE
# Made specifically to address the problems caused by the hardware changes in 1.3. Oh yeah I also removed the auto-shutdown feature because it's kind of broken.
#
# functions for get UPS status - needs enable "i2c" in raspi-config
# To setup, see page six of this manual to see how to enable i2c:
# https://github.com/linshuqin329/UPS-Lite/blob/master/UPS-Lite_V1.3_CW2015/Instructions%20for%20UPS-Lite%20V1.3.pdf
#
# https://github.com/linshuqin329/UPS-Lite
# Follow page seven, install the dependencies (python-smbus) and copy this script over for later use:
# https://github.com/linshuqin329/UPS-Lite/blob/master/UPS-Lite_V1.3_CW2015/UPS_Lite_V1.3_CW2015.py
#
# For Raspberry Pi Zero Ups Power Expansion Board with Integrated Serial Port S3U4
# https://www.ebay.de/itm/For-Raspberry-Pi-Zero-Ups-Power-Expansion-Board-with-Integrated-Serial-Port-S3U4/323873804310
# https://www.aliexpress.com/item/32888533624.html
# Now, install this plugin by copying this to the 'available-plugins' folder in your pwnagotchi, install and enable the plugin with the commands:
# sudo pwnagotchi plugins install upslite_plugin_1_3
# sudo pwnagotchi plugins enable upslite_plugin_1_3
#
# To display external power supply status you need to bridge the necessary pins on the UPS-Lite board. See instructions in the UPS-Lite repo.
# Now restart raspberry pi. Once back up ensure upslite_plugin_1_3 plugin is turned on in the WebUI. If there is still '0%' on your battery meter
# run the script we saved earlier and ensure that the pwnagotchi is plugged in both at the battery and the raspberry pi. The script should start trying to
# read the battery, and should be successful once there's a USB cable running power to the battery supply.
import logging
import struct
@ -20,6 +26,11 @@ import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.components import LabeledValue
from pwnagotchi.ui.view import BLACK
CW2015_ADDRESS = 0X62
CW2015_REG_VCELL = 0X02
CW2015_REG_SOC = 0X04
CW2015_REG_MODE = 0X0A
# TODO: add enable switch in config.yml an cleanup all to the best place
class UPS:
@ -31,8 +42,7 @@ class UPS:
def voltage(self):
try:
address = 0x36
read = self._bus.read_word_data(address, 2)
read = self._bus.read_word_data(CW2015_ADDRESS, CW2015_REG_VCELL)
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
return swapped * 1.25 / 1000 / 16
except:
@ -41,7 +51,7 @@ class UPS:
def capacity(self):
try:
address = 0x36
read = self._bus.read_word_data(address, 4)
read = self._bus.read_word_data(CW2015_ADDRESS, CW2015_REG_SOC)
swapped = struct.unpack("<H", struct.pack(">H", read))[0]
return swapped / 256
except:
@ -57,10 +67,10 @@ class UPS:
class UPSLite(plugins.Plugin):
__author__ = 'evilsocket@gmail.com'
__version__ = '1.0.0'
__author__ = 'marbasec'
__version__ = '1.3.0'
__license__ = 'GPL3'
__description__ = 'A plugin that will add a voltage indicator for the UPS Lite v1.1'
__description__ = 'A plugin that will add a voltage indicator for the UPS Lite v1.3'
def __init__(self):
self.ups = None
@ -69,7 +79,7 @@ class UPSLite(plugins.Plugin):
self.ups = UPS()
def on_ui_setup(self, ui):
ui.add_element('ups', LabeledValue(color=BLACK, label='UPS', value='0%/0V', position=(ui.width() / 2 + 15, 0),
ui.add_element('ups', LabeledValue(color=BLACK, label='UPS', value='0%', position=(ui.width() / 2 + 15, 0),
label_font=fonts.Bold, text_font=fonts.Medium))
def on_unload(self, ui):
@ -80,7 +90,3 @@ class UPSLite(plugins.Plugin):
capacity = self.ups.capacity()
charging = self.ups.charging()
ui.set('ups', "%2i%s" % (capacity, charging))
if capacity <= self.options['shutdown']:
logging.info('[ups_lite] Empty battery (<= %s%%): shuting down' % self.options['shutdown'])
ui.update(force=True, new_data={'status': 'Battery exhausted, bye ...'})
pwnagotchi.shutdown()

View File

@ -1,9 +1,10 @@
import sys
import pwnagotchi.plugins as plugins
import logging
import os
import json
import re
import datetime
from flask import Response
from functools import lru_cache
from dateutil.parser import parse
@ -22,6 +23,7 @@ from dateutil.parser import parse
'''
class Webgpsmap(plugins.Plugin):
__author__ = 'https://github.com/xenDE and https://github.com/dadav'
__version__ = '1.4.0'
@ -103,7 +105,7 @@ class Webgpsmap(plugins.Plugin):
response_status = 200
response_mimetype = "application/xhtml+xml"
response_header_contenttype = 'text/html'
response_header_contentdisposition = 'attachment; filename=webgpsmap.html';
response_header_contentdisposition = 'attachment; filename=webgpsmap.html'
except Exception as error:
logging.error(f"[webgpsmap] on_webhook offlinemap: error: {error}")
return
@ -149,7 +151,6 @@ class Webgpsmap(plugins.Plugin):
def _get_pos_from_file(self, path):
return PositionFile(path)
def load_gps_from_dir(self, gpsdir, newest_only=False):
"""
Parses the gps-data from disk
@ -160,13 +161,9 @@ class Webgpsmap(plugins.Plugin):
logging.info(f"[webgpsmap] scanning {handshake_dir}")
all_files = os.listdir(handshake_dir)
#print(all_files)
all_pcap_files = [os.path.join(handshake_dir, filename)
for filename in all_files
if filename.endswith('.pcap')
]
# print(all_files)
all_pcap_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.pcap')]
all_geo_or_gps_files = []
for filename_pcap in all_pcap_files:
filename_base = filename_pcap[:-5] # remove ".pcap"
@ -300,7 +297,6 @@ class PositionFile:
return parsed_ssid.groups()[0]
return None
def json(self):
"""
returns the parsed json

View File

@ -3,6 +3,7 @@ import logging
import json
import csv
import requests
import pwnagotchi
from io import StringIO
from datetime import datetime
@ -24,7 +25,11 @@ def _extract_gps_data(path):
with open(path, 'r') as json_file:
tempJson = json.load(json_file)
d = datetime.utcfromtimestamp(int(tempJson["ts"]))
return {"Latitude": tempJson["location"]["lat"], "Longitude": tempJson["location"]["lng"], "Altitude": 10, "Updated": d.strftime('%Y-%m-%dT%H:%M:%S.%f')}
return {"Latitude": tempJson["location"]["lat"],
"Longitude": tempJson["location"]["lng"],
"Altitude": 10,
"Accuracy": tempJson["accuracy"],
"Updated": d.strftime('%Y-%m-%dT%H:%M:%S.%f')}
else:
with open(path, 'r') as json_file:
return json.load(json_file)
@ -38,7 +43,7 @@ def _format_auth(data):
out = ""
for auth in data:
out = f"{out}[{auth}]"
return out
return [f"{auth}" for auth in data]
def _transform_wigle_entry(gps_data, pcap_data, plugin_version):
@ -47,10 +52,10 @@ def _transform_wigle_entry(gps_data, pcap_data, plugin_version):
"""
dummy = StringIO()
# write kismet header
dummy.write(f"WigleWifi-1.6,appRelease={plugin_version},model=pwnagotchi,release={__pwnagotchi_version__},"
f"device={pwnagotchi.name()},display=kismet,board=RaspberryPi,brand=pwnagotchi,star=Sol,body=3,subBody=0\n")
dummy.write(
"WigleWifi-1.4,appRelease={},model=pwnagotchi,release={},device=pwnagotchi,display=kismet,board=kismet,brand=pwnagotchi\n".format(plugin_version, __pwnagotchi_version__))
dummy.write(
"MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type")
"MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n")
writer = csv.writer(dummy, delimiter=",", quoting=csv.QUOTE_NONE, escapechar="\\")
writer.writerow([
@ -64,7 +69,7 @@ def _transform_wigle_entry(gps_data, pcap_data, plugin_version):
gps_data['Latitude'],
gps_data['Longitude'],
gps_data['Altitude'],
0, # accuracy?
gps_data['Accuracy'],
'WIFI'])
return dummy.getvalue()
@ -84,7 +89,7 @@ def _send_to_wigle(lines, api_key, donate=True, timeout=30):
headers = {'Authorization': f"Basic {api_key}",
'Accept': 'application/json'}
data = {'donate': 'on' if donate else 'false'}
payload = {'file': dummy, 'type': 'text/csv'}
payload = {'file': (pwnagotchi.name() + ".csv", dummy, 'multipart/form-data', {'Expires': '0'})}
try:
res = requests.post('https://api.wigle.net/api/v2/file/upload',
data=data,
@ -99,34 +104,32 @@ def _send_to_wigle(lines, api_key, donate=True, timeout=30):
class Wigle(plugins.Plugin):
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '2.0.0'
__license__ = 'GPL3'
__description__ = 'This plugin automatically uploads collected wifis to wigle.net'
__author__ = "Dadav and updated by Jayofelony"
__version__ = "3.0.1"
__license__ = "GPL3"
__description__ = "This plugin automatically uploads collected WiFi to wigle.net"
def __init__(self):
self.ready = False
self.report = StatusFile('/root/.wigle_uploads', data_format='json')
self.skip = list()
self.lock = Lock()
self.options = dict()
def on_loaded(self):
if 'api_key' not in self.options or ('api_key' in self.options and self.options['api_key'] is None):
logging.debug("WIGLE: api_key isn't set. Can't upload to wigle.net")
return
if not 'whitelist' in self.options:
self.options['whitelist'] = list()
if not 'donate' in self.options:
self.options['donate'] = True
if 'donate' not in self.options:
self.options['donate'] = False
self.ready = True
logging.info("WIGLE: ready")
def on_internet_available(self, agent):
"""
Called in manual mode when there's internet connectivity
Called when there's internet connectivity
"""
if not self.ready or self.lock.locked():
return
@ -140,9 +143,9 @@ class Wigle(plugins.Plugin):
all_files = os.listdir(handshake_dir)
all_gps_files = [os.path.join(handshake_dir, filename)
for filename in all_files
if filename.endswith('.gps.json') or filename.endswith('.paw-gps.json') or filename.endswith('.geo.json')]
if filename.endswith('.gps.json') or filename.endswith('.geo.json')]
all_gps_files = remove_whitelisted(all_gps_files, self.options['whitelist'])
all_gps_files = remove_whitelisted(all_gps_files, config['main']['whitelist'])
new_gps_files = set(all_gps_files) - set(reported) - set(self.skip)
if new_gps_files:
logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net")
@ -151,8 +154,6 @@ class Wigle(plugins.Plugin):
for gps_file in new_gps_files:
if gps_file.endswith('.gps.json'):
pcap_filename = gps_file.replace('.gps.json', '.pcap')
if gps_file.endswith('.paw-gps.json'):
pcap_filename = gps_file.replace('.paw-gps.json', '.pcap')
if gps_file.endswith('.geo.json'):
pcap_filename = gps_file.replace('.geo.json', '.pcap')
if not os.path.exists(pcap_filename):

View File

@ -76,9 +76,6 @@ class WpaSec(plugins.Plugin):
logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.")
return
if 'whitelist' not in self.options:
self.options['whitelist'] = list()
self.ready = True
logging.info("WPA_SEC: plugin loaded")
@ -101,9 +98,8 @@ class WpaSec(plugins.Plugin):
reported = self.report.data_field_or('reported', default=list())
handshake_dir = config['bettercap']['handshakes']
handshake_filenames = os.listdir(handshake_dir)
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if
filename.endswith('.pcap')]
handshake_paths = remove_whitelisted(handshake_paths, self.options['whitelist'])
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')]
handshake_paths = remove_whitelisted(handshake_paths, config['main']['whitelist'])
handshake_new = set(handshake_paths) - set(reported) - set(self.skip)
if handshake_new:

View File

@ -103,8 +103,8 @@ class Display(View):
def is_waveshare2in13d(self):
return self._implementation.name == 'waveshare2in13d'
def is_waveshare2in23g(self):
return self._implementation.name == 'waveshare2in23g'
def is_waveshare2in13g(self):
return self._implementation.name == 'waveshare2in13g'
def is_waveshare2in36g(self):
return self._implementation.name == 'waveshare2in36g'
@ -112,6 +112,9 @@ class Display(View):
def is_waveshare2in66(self):
return self._implementation.name == 'waveshare2in66'
def is_waveshare2in66b(self):
return self._implementation.name == 'waveshare2in66b'
def is_waveshare2in66g(self):
return self._implementation.name == 'waveshare2in66g'

View File

@ -16,6 +16,7 @@ from pwnagotchi.ui.hw.waveshare1in44lcd import Waveshare144lcd
from pwnagotchi.ui.hw.waveshare1in54b import Waveshare154inchb
from pwnagotchi.ui.hw.waveshare2in13bc import Waveshare213bc
from pwnagotchi.ui.hw.waveshare2in13d import Waveshare213d
from pwnagotchi.ui.hw.waveshare2in13g import Waveshare2in13g
from pwnagotchi.ui.hw.waveshare2in13b_V4 import Waveshare213bV4
from pwnagotchi.ui.hw.waveshare3in5lcd import Waveshare35lcd
from pwnagotchi.ui.hw.spotpear24in import Spotpear24inch
@ -33,9 +34,10 @@ from pwnagotchi.ui.hw.waveshare2in9b_V4 import Waveshare29bV4
from pwnagotchi.ui.hw.waveshare2in9bc import Waveshare2in9bc
from pwnagotchi.ui.hw.waveshare2in9d import Waveshare2in9d
from pwnagotchi.ui.hw.waveshare2in13b_V3 import Waveshare2in13bV3
from pwnagotchi.ui.hw.waveshare2in23g import Waveshare2in23g
from pwnagotchi.ui.hw.waveshare2in36g import Waveshare2in36g
from pwnagotchi.ui.hw.waveshare2in66 import Waveshare2in66
from pwnagotchi.ui.hw.waveshare2in66b import Waveshare2in66b
from pwnagotchi.ui.hw.waveshare2in66g import Waveshare2in66g
from pwnagotchi.ui.hw.waveshare3in0g import Waveshare3in0g
from pwnagotchi.ui.hw.waveshare3in7 import Waveshare3in7
from pwnagotchi.ui.hw.waveshare3in52 import Waveshare3in52
@ -82,6 +84,21 @@ def display_for(config):
elif config['ui']['display']['type'] == 'dfrobot_2':
return DFRobotV2(config)
elif config['ui']['display']['type'] == 'waveshare144lcd':
return Waveshare144lcd(config)
elif config['ui']['display']['type'] == 'waveshare35lcd':
return Waveshare35lcd(config)
elif config['ui']['display']['type'] == 'spotpear24inch':
return Spotpear24inch(config)
elif config['ui']['display']['type'] == 'displayhatmini':
return DisplayHatMini(config)
elif config['ui']['display']['type'] == 'waveshare1in02':
return Waveshare1in02(config)
elif config['ui']['display']['type'] == 'waveshare1in54':
return Waveshare154(config)
@ -94,12 +111,36 @@ def display_for(config):
elif config['ui']['display']['type'] == 'waveshare1in54b_v2':
return Waveshare154bV2(config)
elif config['ui']['display']['type'] == 'waveshare1in54c':
return Waveshare1in54c(config)
elif config['ui']['display']['type'] == 'waveshare1in64g':
return Waveshare1in64g(config)
elif config['ui']['display']['type'] == 'waveshare2in7':
return Waveshare27inch(config)
elif config['ui']['display']['type'] == 'waveshare2in7_v2':
return Waveshare27inchV2(config)
elif config['ui']['display']['type'] == 'waveshare2in7b':
return Waveshare27b(config)
elif config['ui']['display']['type'] == 'waveshare2in7b_v2':
return Waveshare27bV2(config)
elif config['ui']['display']['type'] == 'waveshare2in9':
return Waveshare29inch(config)
elif config['ui']['display']['type'] == 'waveshare2in9bc':
return Waveshare2in9bc(config)
elif config['ui']['display']['type'] == 'waveshare2in9d':
return Waveshare2in9d(config)
elif config['ui']['display']['type'] == 'waveshare2in9_v2':
return Waveshare29inchV2(config)
elif config['ui']['display']['type'] == 'waveshare2in9b_v3':
return Waveshare29bV3(config)
@ -118,137 +159,101 @@ def display_for(config):
elif config['ui']['display']['type'] == 'waveshare_4':
return WaveshareV4(config)
elif config['ui']['display']['type'] == 'waveshare2in7':
return Waveshare27inch(config)
elif config['ui']['display']['type'] == 'waveshare2in7_v2':
return Waveshare27inchV2(config)
elif config['ui']['display']['type'] == 'waveshare2in9':
return Waveshare29inch(config)
elif config['ui']['display']['type'] == 'waveshare2in9_v2':
return Waveshare29inchV2(config)
elif config['ui']['display']['type'] == 'waveshare144lcd':
return Waveshare144lcd(config)
elif config['ui']['display']['type'] == 'waveshare1in54b':
return Waveshare154inchb(config)
elif config['ui']['display']['type'] == 'waveshare2in13bc':
return Waveshare213bc(config)
elif config['ui']['display']['type'] == 'waveshare2in13d':
return Waveshare213d(config)
elif config['ui']['display']['type'] == 'waveshare2in13b_v3':
return Waveshare2in13bV3(config)
elif config['ui']['display']['type'] == 'waveshare2in13b_v4':
return Waveshare213bV4(config)
elif config['ui']['display']['type'] == 'waveshare35lcd':
return Waveshare35lcd(config)
elif config['ui']['display']['type'] == 'spotpear24inch':
return Spotpear24inch(config)
elif config['ui']['display']['type'] == 'displayhatmini':
return DisplayHatMini(config)
elif config['ui']['display']['type'] == 'waveshare1in54c':
return Waveshare1in54c
elif config['ui']['display']['type'] == 'waveshare1in64g':
return Waveshare1in64g
elif config['ui']['display']['type'] == 'waveshare1in02':
return Waveshare1in02
elif config['ui']['display']['type'] == 'waveshare2in9bc':
return Waveshare2in9bc
elif config['ui']['display']['type'] == 'waveshare2in9d':
return Waveshare2in9d
elif config['ui']['display']['type'] == 'waveshare2in13b_v3':
return Waveshare2in13bV3
elif config['ui']['display']['type'] == 'waveshare2in23g':
return Waveshare2in23g
elif config['ui']['display']['type'] == 'waveshare2in13g':
return Waveshare2in13g(config)
elif config['ui']['display']['type'] == 'waveshare2in36g':
return Waveshare2in36g
return Waveshare2in36g(config)
elif config['ui']['display']['type'] == 'waveshare2in66':
return Waveshare2in66
return Waveshare2in66(config)
elif config['ui']['display']['type'] == 'waveshare2in66b':
return Waveshare2in66b(config)
elif config['ui']['display']['type'] == 'waveshare2in66g':
return Waveshare2in66g(config)
elif config['ui']['display']['type'] == 'waveshare3in0g':
return Waveshare3in0g
return Waveshare3in0g(config)
elif config['ui']['display']['type'] == 'waveshare3in7':
return Waveshare3in7
return Waveshare3in7(config)
elif config['ui']['display']['type'] == 'waveshare3in52':
return Waveshare3in52
return Waveshare3in52(config)
elif config['ui']['display']['type'] == 'waveshare4in01f':
return Waveshare4in01f
return Waveshare4in01f(config)
elif config['ui']['display']['type'] == 'waveshare4in2':
return Waveshare4in2
return Waveshare4in2(config)
elif config['ui']['display']['type'] == 'waveshare4in2_v2':
return Waveshare4in2V2
return Waveshare4in2V2(config)
elif config['ui']['display']['type'] == 'waveshare4in2b_v2':
return Waveshare4in2bV2
return Waveshare4in2bV2(config)
elif config['ui']['display']['type'] == 'waveshare4in2bc':
return Waveshare4in2bc
return Waveshare4in2bc(config)
elif config['ui']['display']['type'] == 'waveshare4in26':
return Waveshare4in26
return Waveshare4in26(config)
elif config['ui']['display']['type'] == 'waveshare4in37g':
return Waveshare4in37g
return Waveshare4in37g(config)
elif config['ui']['display']['type'] == 'waveshare5in65f':
return Waveshare5in65f
return Waveshare5in65f(config)
elif config['ui']['display']['type'] == 'waveshare5in83':
return Waveshare5in83
return Waveshare5in83(config)
elif config['ui']['display']['type'] == 'waveshare5in83_v2':
return Waveshare5in83V2
return Waveshare5in83V2(config)
elif config['ui']['display']['type'] == 'waveshare5in83b_v2':
return Waveshare5in83bV2
return Waveshare5in83bV2(config)
elif config['ui']['display']['type'] == 'waveshare5in83bc':
return Waveshare5in83bc
return Waveshare5in83bc(config)
elif config['ui']['display']['type'] == 'waveshare7in3f':
return Waveshare7in3f
return Waveshare7in3f(config)
elif config['ui']['display']['type'] == 'waveshare7in3g':
return Waveshare7in3g
return Waveshare7in3g(config)
elif config['ui']['display']['type'] == 'waveshare7in5':
return Waveshare7in5
return Waveshare7in5(config)
elif config['ui']['display']['type'] == 'waveshare7in5_HD':
return Waveshare7in5HD
return Waveshare7in5HD(config)
elif config['ui']['display']['type'] == 'waveshare7in5_v2':
return Waveshare7in5V2
return Waveshare7in5V2(config)
elif config['ui']['display']['type'] == 'waveshare7in5b_HD':
return Waveshare7in5bHD
return Waveshare7in5bHD(config)
elif config['ui']['display']['type'] == 'waveshare7in5b_v2':
return Waveshare7in5bV2
return Waveshare7in5bV2(config)
elif config['ui']['display']['type'] == 'waveshare7in5bc':
return Waveshare7in5bc
return Waveshare7in5bc(config)
elif config['ui']['display']['type'] == 'waveshare13in3k':
return Waveshare13in3k
return Waveshare13in3k(config)

View File

@ -3,6 +3,7 @@ import pwnagotchi.ui.fonts as fonts
class DisplayImpl(object):
def __init__(self, config, name):
self._display = None
if fonts.Medium is None:
fonts.init(config)
self.name = name

View File

@ -3,41 +3,41 @@ import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DFRobotV1(DisplayImpl):
def __init__(self, config):
super(DFRobotV1, self).__init__(config, 'dfrobot_1')
self._display = None
def __init__(self, config):
super(DFRobotV1, self).__init__(config, 'dfrobot_1')
def layout(self):
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 layout(self):
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):
logging.info("initializing dfrobot1 display")
from pwnagotchi.ui.hw.libs.dfrobot.v1.dfrobot import DFRobot
self._display = DFRobot()
def initialize(self):
logging.info("initializing dfrobot1 display")
from pwnagotchi.ui.hw.libs.dfrobot.v1.dfrobot import DFRobot
self._display = DFRobot()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear(0xFF)
def clear(self):
self._display.Clear(0xFF)

View File

@ -3,41 +3,41 @@ import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DFRobotV1(DisplayImpl):
def __init__(self, config):
super(DFRobotV1, self).__init__(config, 'dfrobot_1')
self._display = None
def __init__(self, config):
super(DFRobotV1, self).__init__(config, 'dfrobot_1')
def layout(self):
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 layout(self):
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):
logging.info("initializing dfrobot1 display")
from pwnagotchi.ui.hw.libs.dfrobot.v1.dfrobot import DFRobot
self._display = DFRobot()
def initialize(self):
logging.info("initializing dfrobot1 display")
from pwnagotchi.ui.hw.libs.dfrobot.v1.dfrobot import DFRobot
self._display = DFRobot()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear(0xFF)
def clear(self):
self._display.Clear(0xFF)

View File

@ -3,41 +3,41 @@ import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DFRobotV2(DisplayImpl):
def __init__(self, config):
super(DFRobotV2, self).__init__(config, 'dfrobot_2')
self._display = None
def __init__(self, config):
super(DFRobotV2, self).__init__(config, 'dfrobot_2')
def layout(self):
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 layout(self):
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):
logging.info("initializing dfrobot2 display")
from pwnagotchi.ui.hw.libs.dfrobot.v2.dfrobot import DFRobot
self._display = DFRobot()
def initialize(self):
logging.info("initializing dfrobot2 display")
from pwnagotchi.ui.hw.libs.dfrobot.v2.dfrobot import DFRobot
self._display = DFRobot()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear(0xFF)
def clear(self):
self._display.Clear(0xFF)

View File

@ -3,41 +3,41 @@ import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DFRobotV2(DisplayImpl):
def __init__(self, config):
super(DFRobotV2, self).__init__(config, 'dfrobot_2')
self._display = None
def __init__(self, config):
super(DFRobotV2, self).__init__(config, 'dfrobot_2')
def layout(self):
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 layout(self):
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):
logging.info("initializing dfrobot2 display")
from pwnagotchi.ui.hw.libs.dfrobot.v2.dfrobot import DFRobot
self._display = DFRobot()
def initialize(self):
logging.info("initializing dfrobot2 display")
from pwnagotchi.ui.hw.libs.dfrobot.v2.dfrobot import DFRobot
self._display = DFRobot()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear(0xFF)
def clear(self):
self._display.Clear(0xFF)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class DisplayHatMini(DisplayImpl):
def __init__(self, config):
super(DisplayHatMini, self).__init__(config, 'displayhatmini')
self._display = None
def layout(self):
fonts.setup(12, 10, 12, 70, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Inky(DisplayImpl):
def __init__(self, config):
super(Inky, self).__init__(config, 'inky')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 28, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class LcdHat(DisplayImpl):
def __init__(self, config):
super(LcdHat, self).__init__(config, 'lcdhat')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)

View File

@ -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)

View File

@ -264,11 +264,11 @@ class EPD:
def display(self, image):
if (image == None):
return
# Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1)
# Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1)
if (self.width % 8 == 0):
Width = self.width / 8
Width = self.width // 8
else:
Width = self.width / 8 + 1
Width = self.width // 8 + 1
self.send_command(0x10)
for j in range(0, self.height):
@ -282,11 +282,11 @@ class EPD:
self.TurnOnDisplay()
def Clear(self):
# Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1)
# Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1)
if (self.width % 8 == 0):
Width = self.width / 8
Width = self.width // 8
else:
Width = self.width / 8 + 1
Width = self.width // 8 + 1
Height = self.height
@ -313,11 +313,11 @@ class EPD:
self.send_data(127) # y-end
self.send_data(0x00)
# Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1)
# Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1)
if (self.width % 8 == 0):
Width = self.width / 8
Width = self.width // 8
else:
Width = self.width / 8 + 1
Width = self.width // 8 + 1
Height = self.height
# send data

View File

@ -91,9 +91,9 @@ class EPD:
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(int(self.Source_BITS / 256))
self.send_data(self.Source_BITS % 256)
self.send_data(self.Gate_BITS / 256)
self.send_data(int(self.Gate_BITS / 256))
self.send_data(self.Gate_BITS % 256)
def TurnOnDisplay(self):
@ -166,20 +166,21 @@ class EPD:
def getbuffer(self, image):
# Create a pallette with the 4 colors supported by the panel
global image_temp
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):
if imwidth == self.width and imheight == self.height:
image_temp = image
elif (imwidth == self.height and imheight == self.width):
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
# Convert the source 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'))

View File

@ -0,0 +1,189 @@
# *****************************************************************************
# * | File : epd2in66b.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V1.1
# * | Date : 2022-08-9
# # | 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
# Display resolution
EPD_WIDTH = 152
EPD_HEIGHT = 296
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
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(5)
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)
# 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)
def ReadBusy(self):
logger.debug("e-Paper busy")
while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy
epdconfig.delay_ms(20)
logger.debug("e-Paper busy release")
def init(self):
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()
self.send_command(0x12)
epdconfig.delay_ms(30)
self.ReadBusy()
self.send_command(0x11) # setting gaet number
self.send_data(0x03)
self.setWindows(0, 0, self.width - 1, self.height - 1)
self.send_command(0x21)
self.send_data(0x00)
self.send_data(0x80)
self.setCursor(0, 0)
self.ReadBusy()
return 0
def setWindows(self, Xstart, Ystart, Xend, Yend):
self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION
self.send_data((Xstart >> 3) & 0x1F)
self.send_data((Xend >> 3) & 0x1F)
self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION
self.send_data(Ystart & 0xFF)
self.send_data((Ystart >> 8) & 0x01)
self.send_data(Yend & 0xFF)
self.send_data((Yend >> 8) & 0x01)
def setCursor(self, Xstart, Ystart):
self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER
self.send_data(Xstart & 0x1F)
self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER
self.send_data(Ystart & 0xFF)
self.send_data((Ystart >> 8) & 0x01)
def turnon_display(self):
self.send_command(0x20)
self.ReadBusy()
def getbuffer(self, image):
# logger.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width / 8) * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if (imwidth == self.width and imheight == self.height):
logger.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif (imwidth == self.height and imheight == self.width):
logger.debug("Horizontal")
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8))
return buf
def display(self, Blackimage, Redimage):
if (Blackimage == None or Redimage == None):
return
Redimage_1 = [0x00] * len(Redimage)
for i in range(len(Redimage)):
Redimage_1[i] = ~Redimage[i]
self.send_command(0x24)
self.send_data2(Blackimage)
self.send_command(0x26)
self.send_data2(Redimage_1)
self.turnon_display()
def Clear(self):
if self.width % 8 == 0:
linewidth = int(self.width / 8)
else:
linewidth = int(self.width / 8) + 1
self.send_command(0x24)
self.send_data2([0xff] * int(self.height * linewidth))
self.send_command(0x26)
self.send_data2([0x00] * int(self.height * linewidth))
self.turnon_display()
def sleep(self):
self.send_command(0X10) # DEEP_SLEEP_MODE
self.send_data(0x01)
epdconfig.delay_ms(2000)
epdconfig.module_exit()
### END OF FILE ###

View File

@ -517,4 +517,5 @@ class EPD:
epdconfig.delay_ms(2000)
epdconfig.module_exit()
### END OF FILE ###
### END OF FILE ###

View File

@ -28,7 +28,7 @@
#
import logging
from .. import epdconfig
from . import epdconfig
# Display resolution
EPD_WIDTH = 128
@ -227,7 +227,7 @@ class EPD:
self.send_data((y >> 8) & 0xFF)
def init(self):
if epdconfig.module_init() != 0:
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()

View File

@ -184,7 +184,7 @@ class EPD:
self.send_command(0x01) # Driver output control
self.send_data((self.height - 1) % 256)
self.send_data((self.height - 1) / 256)
self.send_data((self.height - 1) // 256)
self.send_data(0x00)
self.send_command(0x11) # data entry mode

View File

@ -29,7 +29,7 @@
import logging
from .. import epdconfig
from . import epdconfig
# Display resolution
EPD_WIDTH = 800

View File

@ -1,11 +1,11 @@
# *****************************************************************************
# * | File : epd7in5.py
# * | File : epd4in26.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V4.0
# * | Date : 2019-06-20
# * | This version: V1.0
# * | Date : 2023-12-20
# # | Info : python demo
# -----------------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person obtaining a copy
@ -29,11 +29,16 @@
import logging
from .. import epdconfig
from . import epdconfig
# Display resolution
EPD_WIDTH = 880
EPD_HEIGHT = 528
EPD_WIDTH = 800
EPD_HEIGHT = 480
GRAY1 = 0xff # white
GRAY2 = 0xC0
GRAY3 = 0x80 # gray
GRAY4 = 0x00 # Blackest
logger = logging.getLogger(__name__)
@ -46,15 +51,39 @@ class EPD:
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
self.GRAY1 = GRAY1 # white
self.GRAY2 = GRAY2
self.GRAY3 = GRAY3 # gray
self.GRAY4 = GRAY4 # Blackest
LUT_DATA_4Gray = [ # #112bytes
0x80, 0x48, 0x4A, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x48, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x88, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xA8, 0x48, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x1E, 0x1C, 0x02, 0x00,
0x05, 0x01, 0x05, 0x01, 0x02,
0x08, 0x01, 0x01, 0x04, 0x04,
0x00, 0x02, 0x00, 0x02, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01,
0x22, 0x22, 0x22, 0x22, 0x22,
0x17, 0x41, 0xA8, 0x32, 0x30,
0x00, 0x00]
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
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(200)
epdconfig.delay_ms(20)
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
@ -71,7 +100,7 @@ class EPD:
def send_data2(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte2(data)
epdconfig.SPI.writebytes2(data)
epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self):
@ -79,103 +108,409 @@ class EPD:
busy = epdconfig.digital_read(self.busy_pin)
while (busy == 1):
busy = epdconfig.digital_read(self.busy_pin)
epdconfig.delay_ms(200)
epdconfig.delay_ms(20)
epdconfig.delay_ms(20)
logger.debug("e-Paper busy release")
def TurnOnDisplay(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xF7)
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
def TurnOnDisplay_Fast(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xC7)
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
def TurnOnDisplay_Part(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xFF)
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
def TurnOnDisplay_4GRAY(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xC7)
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
'''
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
self.send_data(x_start & 0xFF)
self.send_data((x_start >> 8) & 0x03)
self.send_data(x_end & 0xFF)
self.send_data((x_end >> 8) & 0x03)
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_data((x >> 8) & 0x03)
self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER
self.send_data(y & 0xFF)
self.send_data((y >> 8) & 0xFF)
def init(self):
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()
self.ReadBusy()
self.ReadBusy();
self.send_command(0x12); # SWRESET
self.ReadBusy();
self.send_command(0x12) # SWRESET
self.ReadBusy()
self.send_command(0x46); # Auto Write Red RAM
self.send_data(0xf7);
self.ReadBusy();
self.send_command(0x47); # Auto Write B/W RAM
self.send_data(0xf7);
self.ReadBusy();
self.send_command(0x18) # use the internal temperature sensor
self.send_data(0x80)
self.send_command(0x0C); # Soft start setting
self.send_data2([0xAE, 0xC7, 0xC3, 0xC0, 0x40])
self.send_command(0x0C) # set soft start
self.send_data(0xAE)
self.send_data(0xC7)
self.send_data(0xC3)
self.send_data(0xC0)
self.send_data(0x80)
self.send_command(0x01); # Set MUX as 527
self.send_data2([0xAF, 0x02, 0x01])
self.send_command(0x01) # drive output control
self.send_data((self.height - 1) % 256) # Y
self.send_data((self.height - 1) // 256) # Y
self.send_data(0x02)
self.send_command(0x11); # Data entry mode
self.send_data(0x01);
self.send_command(0x3C) # Border Border setting
self.send_data(0x01)
self.send_command(0x44);
self.send_data2([0x00, 0x00, 0x6F, 0x03]) # RAM x address start at 0
self.send_command(0x45);
self.send_data2([0xAF, 0x02, 0x00, 0x00])
self.send_command(0x11) # data entry mode
self.send_data(0x01) # X-mode x+ y-
self.send_command(0x3C); # VBD
self.send_data(0x05); # LUT1, for white
self.SetWindow(0, self.height - 1, self.width - 1, 0)
self.send_command(0x18);
self.send_data(0X80);
self.SetCursor(0, 0)
self.ReadBusy()
self.send_command(0x22);
self.send_data(0XB1); # Load Temperature and waveform setting.
self.send_command(0x20);
self.ReadBusy();
# EPD hardware init end
return 0
self.send_command(0x4E); # set RAM x address count to 0;
self.send_data2([0x00, 0x00])
self.send_command(0x4F);
self.send_data2([0x00, 0x00])
def init_Fast(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(0x18) # use the internal temperature sensor
self.send_data(0x80)
self.send_command(0x0C) # set soft start
self.send_data(0xAE)
self.send_data(0xC7)
self.send_data(0xC3)
self.send_data(0xC0)
self.send_data(0x80)
self.send_command(0x01) # drive output control
self.send_data((self.height - 1) % 256) # Y
self.send_data((self.height - 1) // 256) # Y
self.send_data(0x02)
self.send_command(0x3C) # Border Border setting
self.send_data(0x01)
self.send_command(0x11) # data entry mode
self.send_data(0x01) # X-mode x+ y-
self.SetWindow(0, self.height - 1, self.width - 1, 0)
self.SetCursor(0, 0)
self.ReadBusy()
# TEMP (1.5s)
self.send_command(0x1A)
self.send_data(0x5A)
self.send_command(0x22)
self.send_data(0x91)
self.send_command(0x20)
self.ReadBusy()
# EPD hardware init end
return 0
def Lut(self):
self.send_command(0x32)
for count in range(0, 105):
self.send_data(self.LUT_DATA_4Gray[count])
self.send_command(0x03) # VGH
self.send_data(self.LUT_DATA_4Gray[105])
self.send_command(0x04) #
self.send_data(self.LUT_DATA_4Gray[106]) # VSH1
self.send_data(self.LUT_DATA_4Gray[107]) # VSH2
self.send_data(self.LUT_DATA_4Gray[108]) # VSL
self.send_command(0x2C) # VCOM Voltage
self.send_data(self.LUT_DATA_4Gray[109]) # 0x1C
def init_4GRAY(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(0x18) # use the internal temperature sensor
self.send_data(0x80)
self.send_command(0x0C) # set soft start
self.send_data(0xAE)
self.send_data(0xC7)
self.send_data(0xC3)
self.send_data(0xC0)
self.send_data(0x80)
self.send_command(0x01) # drive output control
self.send_data((self.height - 1) % 256) # Y
self.send_data((self.height - 1) // 256) # Y
self.send_data(0x02)
self.send_command(0x3C) # Border Border setting
self.send_data(0x01)
self.send_command(0x11) # data entry mode
self.send_data(0x01) # X-mode x+ y-
self.SetWindow(0, self.height - 1, self.width - 1, 0)
self.SetCursor(0, 0)
self.ReadBusy()
self.Lut()
# EPD hardware init end
return 0
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):
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 [0xff] * int(self.width * self.height / 8)
# logger.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width / 8) * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if imwidth == self.width and imheight == self.height:
logger.debug("Horizontal")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif imwidth == self.height and imheight == self.width:
logger.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8))
return buf
buf = bytearray(img.tobytes('raw'))
def getbuffer_4Gray(self, image):
# logger.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width / 4) * self.height)
image_monocolor = image.convert('L')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
i = 0
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if (imwidth == self.width and imheight == self.height):
logger.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if (pixels[x, y] == 0xC0):
pixels[x, y] = 0x80
elif (pixels[x, y] == 0x80):
pixels[x, y] = 0x40
i = i + 1
if (i % 4 == 0):
buf[int((x + (y * self.width)) / 4)] = (
(pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | (
pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
elif (imwidth == self.height and imheight == self.width):
logger.debug("Horizontal")
for x in range(imwidth):
for y in range(imheight):
newx = y
newy = self.height - x - 1
if (pixels[x, y] == 0xC0):
pixels[x, y] = 0x80
elif (pixels[x, y] == 0x80):
pixels[x, y] = 0x40
i = i + 1
if (i % 4 == 0):
buf[int((newx + (newy * self.width)) / 4)] = (
(pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | (
pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
return buf
def display(self, image):
self.send_command(0x4F);
self.send_data2([0x00, 0x00])
self.send_command(0x24);
self.send_data2(image)
self.send_command(0x22);
self.send_data(0xF7); # Load LUT from MCU(0x32)
self.send_command(0x20);
epdconfig.delay_ms(10);
self.ReadBusy();
def Clear(self):
buf = [0xff] * int(self.width * self.height / 8)
self.send_command(0x4F);
self.send_data2([0x00, 0x00])
self.send_command(0x24)
self.send_data2(buf)
self.send_data2(image)
self.TurnOnDisplay()
def display_Base(self, image):
self.send_command(0x24)
self.send_data2(image)
self.send_command(0x26)
self.send_data2(buf)
self.send_data2(image)
self.send_command(0x22);
self.send_data(0xF7); # Load LUT from MCU(0x32)
self.send_command(0x20);
epdconfig.delay_ms(10);
self.ReadBusy();
self.TurnOnDisplay()
def display_Fast(self, image):
self.send_command(0x24)
self.send_data2(image)
self.TurnOnDisplay_Fast()
def display_Partial(self, Image):
# Reset
self.reset()
self.send_command(0x18) # BorderWavefrom
self.send_data(0x80)
self.send_command(0x3C) # BorderWavefrom
self.send_data(0x80)
self.send_command(0x01) # drive output control
self.send_data((self.height - 1) % 256) # Y
self.send_data((self.height - 1) // 256) # Y
self.send_command(0x11) # data entry mode
self.send_data(0x01) # X-mode x+ y-
self.SetWindow(0, self.height - 1, self.width - 1, 0)
self.SetCursor(0, 0)
self.send_command(0x24) # Write Black and White image to RAM
self.send_data2(Image)
self.TurnOnDisplay_Part()
def display_4Gray(self, image):
self.send_command(0x24)
for i in range(0, 48000): # 5808*4 46464
temp3 = 0
for j in range(0, 2):
temp1 = image[i * 2 + j]
for k in range(0, 2):
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x00
elif (temp2 == 0x00):
temp3 |= 0x01
elif (temp2 == 0x80):
temp3 |= 0x01
else: # 0x40
temp3 |= 0x00
temp3 <<= 1
temp1 <<= 2
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x00
elif (temp2 == 0x00):
temp3 |= 0x01
elif (temp2 == 0x80):
temp3 |= 0x01
else: # 0x40
temp3 |= 0x00
if (j != 1 or k != 1):
temp3 <<= 1
temp1 <<= 2
self.send_data(temp3)
self.send_command(0x26)
for i in range(0, 48000): # 5808*4 46464
temp3 = 0
for j in range(0, 2):
temp1 = image[i * 2 + j]
for k in range(0, 2):
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x00
elif (temp2 == 0x00):
temp3 |= 0x01
elif (temp2 == 0x80):
temp3 |= 0x00
else: # 0x40
temp3 |= 0x01
temp3 <<= 1
temp1 <<= 2
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x00
elif (temp2 == 0x00):
temp3 |= 0x01
elif (temp2 == 0x80):
temp3 |= 0x00
else: # 0x40
temp3 |= 0x01
if (j != 1 or k != 1):
temp3 <<= 1
temp1 <<= 2
self.send_data(temp3)
self.TurnOnDisplay_4GRAY()
def Clear(self):
self.send_command(0x24)
self.send_data2([0xFF] * (int(self.width / 8) * self.height))
self.send_command(0x26)
self.send_data2([0xFF] * (int(self.width / 8) * self.height))
self.TurnOnDisplay()
def sleep(self):
self.send_command(0x10);
self.send_data(0x01);
self.send_command(0x10) # DEEP_SLEEP
self.send_data(0x01)
epdconfig.delay_ms(2000)
epdconfig.module_exit()
### END OF FILE ###
### END OF FILE ###

View File

@ -243,7 +243,7 @@ class EPD:
else:
Xend = Xend // 8 * 8 + 1
Width = (Xend - Xstart) / 8
Width = (Xend - Xstart) // 8
Height = Yend - Ystart
self.send_command(0x50)

View File

@ -484,7 +484,7 @@ class EPD:
else:
Xend = Xend // 8 * 8 + 1
Width = (Xend - Xstart) / 8
Width = (Xend - Xstart) // 8
Height = Yend - Ystart
self.send_command(0x50)

View File

@ -81,59 +81,59 @@ class EPD:
self.reset()
self.send_command(0x12); # SWRESET
self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal
self.send_command(0x12) # SWRESET
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x46); # Auto Write RAM
self.send_data(0xF7);
self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal
self.send_command(0x46) # Auto Write RAM
self.send_data(0xF7)
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x47); # Auto Write RAM
self.send_data(0xF7);
self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal
self.send_command(0x47) # Auto Write RAM
self.send_data(0xF7)
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x0C); # Soft start setting
self.send_data(0xAE);
self.send_data(0xC7);
self.send_data(0xC3);
self.send_data(0xC0);
self.send_data(0x40);
self.send_command(0x0C) # Soft start setting
self.send_data(0xAE)
self.send_data(0xC7)
self.send_data(0xC3)
self.send_data(0xC0)
self.send_data(0x40)
self.send_command(0x01); # Set MUX as 527
self.send_data(0xAF);
self.send_data(0x02);
self.send_data(0x01);
self.send_command(0x01) # Set MUX as 527
self.send_data(0xAF)
self.send_data(0x02)
self.send_data(0x01)
self.send_command(0x11); # Data entry mode
self.send_data(0x01);
self.send_command(0x11) # Data entry mode
self.send_data(0x01)
self.send_command(0x44);
self.send_data(0x00); # RAM x address start at 0
self.send_data(0x00);
self.send_data(0x6F); # RAM x address end at 36Fh -> 879
self.send_data(0x03);
self.send_command(0x45);
self.send_data(0xAF); # RAM y address start at 20Fh;
self.send_data(0x02);
self.send_data(0x00); # RAM y address end at 00h;
self.send_data(0x00);
self.send_command(0x44)
self.send_data(0x00) # RAM x address start at 0
self.send_data(0x00)
self.send_data(0x6F) # RAM x address end at 36Fh -> 879
self.send_data(0x03)
self.send_command(0x45)
self.send_data(0xAF) # RAM y address start at 20Fh
self.send_data(0x02)
self.send_data(0x00) # RAM y address end at 00h
self.send_data(0x00)
self.send_command(0x3C); # VBD
self.send_data(0x01); # LUT1, for white
self.send_command(0x3C) # VBD
self.send_data(0x01) # LUT1, for white
self.send_command(0x18);
self.send_data(0X80);
self.send_command(0x22);
self.send_data(0XB1); # Load Temperature and waveform setting.
self.send_command(0x20);
self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal
self.send_command(0x18)
self.send_data(0X80)
self.send_command(0x22)
self.send_data(0XB1) # Load Temperature and waveform setting.
self.send_command(0x20)
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x4E);
self.send_data(0x00);
self.send_data(0x00);
self.send_command(0x4F);
self.send_data(0xAF);
self.send_data(0x02);
self.send_command(0x4E)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0x4F)
self.send_data(0xAF)
self.send_data(0x02)
return 0
@ -162,44 +162,44 @@ class EPD:
return buf
def display(self, imageblack, imagered):
self.send_command(0x4F);
self.send_data(0xAf);
self.send_command(0x4F)
self.send_data(0xAf)
self.send_command(0x24)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(imageblack[i]);
self.send_data(imageblack[i])
self.send_command(0x26)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(~imagered[i]);
self.send_data(~imagered[i])
self.send_command(0x22);
self.send_data(0xC7); # Load LUT from MCU(0x32)
self.send_command(0x20);
epdconfig.delay_ms(200); # !!!The delay here is necessary, 200uS at least!!!
self.ReadBusy();
self.send_command(0x22)
self.send_data(0xC7) # Load LUT from MCU(0x32)
self.send_command(0x20)
epdconfig.delay_ms(200) # !!!The delay here is necessary, 200uS at least!!!
self.ReadBusy()
def Clear(self):
self.send_command(0x4F);
self.send_data(0xAf);
self.send_command(0x4F)
self.send_data(0xAf)
self.send_command(0x24)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0xff);
self.send_data(0xff)
self.send_command(0x26)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0x00);
self.send_data(0x00)
self.send_command(0x22);
self.send_data(0xC7); # Load LUT from MCU(0x32)
self.send_command(0x20);
epdconfig.delay_ms(200); # !!!The delay here is necessary, 200uS at least!!!
self.ReadBusy();
self.send_command(0x22)
self.send_data(0xC7) # Load LUT from MCU(0x32)
self.send_command(0x20)
epdconfig.delay_ms(200) # !!!The delay here is necessary, 200uS at least!!!
self.ReadBusy()
def sleep(self):
self.send_command(0x10); # deep sleep
self.send_data(0x01);
self.send_command(0x10) # deep sleep
self.send_data(0x01)
epdconfig.delay_ms(2000)
epdconfig.module_exit()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class OledHat(DisplayImpl):
def __init__(self, config):
super(OledHat, self).__init__(config, 'oledhat')
self._display = None
def layout(self):
fonts.setup(8, 8, 8, 10, 10, 8)

View File

@ -8,7 +8,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Papirus(DisplayImpl):
def __init__(self, config):
super(Papirus, self).__init__(config, 'papirus')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 23, 25, 9)

View File

@ -3,12 +3,12 @@ import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
import os,time
import time
class Spotpear24inch(DisplayImpl):
def __init__(self, config):
super(Spotpear24inch, self).__init__(config, 'spotpear24inch')
self._display = None
def layout(self):
fonts.setup(12, 10, 12, 70, 25, 9)

View File

@ -3,12 +3,12 @@ import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
import os,time
import time
class Spotpear24inch(DisplayImpl):
def __init__(self, config):
super(Spotpear24inch, self).__init__(config, 'spotpear24inch')
self._display = None
def layout(self):
fonts.setup(12, 10, 12, 70, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare13in3k(DisplayImpl):
def __init__(self, config):
super(Waveshare13in3k, self).__init__(config, 'waveshare13in3k')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
@ -39,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()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare144lcd(DisplayImpl):
def __init__(self, config):
super(Waveshare144lcd, self).__init__(config, 'waveshare144lcd')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
@ -42,5 +41,4 @@ class Waveshare144lcd(DisplayImpl):
self._display.display(canvas)
def clear(self):
pass
#self._display.clear()
self._display.clear()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare1in02(DisplayImpl):
def __init__(self, config):
super(Waveshare1in02, self).__init__(config, 'waveshare1in02')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
@ -39,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()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare144lcd(DisplayImpl):
def __init__(self, config):
super(Waveshare144lcd, self).__init__(config, 'waveshare144lcd')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare154(DisplayImpl):
def __init__(self, config):
super(Waveshare154, self).__init__(config, 'waveshare1in54')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
@ -40,7 +39,7 @@ class Waveshare154(DisplayImpl):
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
self._display.display(buf)
def clear(self):
# pass

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare154V2(DisplayImpl):
def __init__(self, config):
super(Waveshare154V2, self).__init__(config, 'waveshare1in54_v2')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
@ -40,7 +39,7 @@ class Waveshare154V2(DisplayImpl):
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare154inchb(DisplayImpl):
def __init__(self, config):
super(Waveshare154inchb, self).__init__(config, 'waveshare1in54b')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare154bV2(DisplayImpl):
def __init__(self, config):
super(Waveshare154bV2, self).__init__(config, 'waveshare1in54b_v2')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare1in54c(DisplayImpl):
def __init__(self, config):
super(Waveshare1in54c, self).__init__(config, 'waveshare1in54c')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
@ -39,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()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare1in64g(DisplayImpl):
def __init__(self, config):
super(Waveshare1in64g, self).__init__(config, 'waveshare1in64g')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
@ -39,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()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class WaveshareV1(DisplayImpl):
def __init__(self, config):
super(WaveshareV1, self).__init__(config, 'waveshare_1')
self._display = None
def layout(self):
if self.config['color'] == 'black':

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class WaveshareV2(DisplayImpl):
def __init__(self, config):
super(WaveshareV2, self).__init__(config, 'waveshare_2')
self._display = None
def layout(self):
if self.config['color'] == 'black':

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class WaveshareV3(DisplayImpl):
def __init__(self, config):
super(WaveshareV3, self).__init__(config, 'waveshare_3')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 35, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class WaveshareV4(DisplayImpl):
def __init__(self, config):
super(WaveshareV4, self).__init__(config, 'waveshare_4')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)

View File

@ -8,7 +8,6 @@ from PIL import Image
class Waveshare2in13bV3(DisplayImpl):
def __init__(self, config):
super(Waveshare2in13bV3, self).__init__(config, 'waveshare2in13b_v3')
self._display = None
def layout(self):
if self.config['color'] == 'black':

View File

@ -7,8 +7,7 @@ from PIL import Image
class Waveshare213bV4(DisplayImpl):
def __init__(self, config):
super(Waveshare213bV4, self).__init__(config, 'waveshare213inb_v4')
self._display = None
super(Waveshare213bV4, self).__init__(config, 'waveshare2in13b_v4')
def layout(self):
if self.config['color'] == 'black':

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare213bc(DisplayImpl):
def __init__(self, config):
super(Waveshare213bc, self).__init__(config, 'waveshare2in13bc')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 25, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare213d(DisplayImpl):
def __init__(self, config):
super(Waveshare213d, self).__init__(config, 'waveshare2in13d')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 25, 25, 9)

View File

@ -0,0 +1,45 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare2in13g(DisplayImpl):
def __init__(self, config):
super(Waveshare2in13g, self).__init__(config, 'waveshare2in13g')
def layout(self):
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):
logging.info("initializing waveshare v2in13g display")
from pwnagotchi.ui.hw.libs.waveshare.v2in13g.epd2in13g import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -1,46 +0,0 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare2in23g(DisplayImpl):
def __init__(self, config):
super(Waveshare2in23g, self).__init__(config, 'waveshare2in23g')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 25, 25, 9)
self._layout['width'] = 122
self._layout['height'] = 250
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, 122, 12]
self._layout['line2'] = [0, 92, 122, 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
}
return self._layout
def initialize(self):
logging.info("initializing waveshare 2.23g inch display")
from pwnagotchi.ui.hw.libs.waveshare.v2in23g.epd2in13g import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare2in36g(DisplayImpl):
def __init__(self, config):
super(Waveshare2in36g, self).__init__(config, 'waveshare2in36g')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
@ -39,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()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare2in66(DisplayImpl):
def __init__(self, config):
super(Waveshare2in66, self).__init__(config, 'waveshare2in66')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
@ -35,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()

View File

@ -0,0 +1,45 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare2in66b(DisplayImpl):
def __init__(self, config):
super(Waveshare2in66b, self).__init__(config, 'waveshare2in66b')
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
self._layout['width'] = 152
self._layout['height'] = 296
self._layout['face'] = (0, 43)
self._layout['name'] = (0, 14)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (0, 71)
self._layout['uptime'] = (0, 25)
self._layout['line1'] = [0, 12, 152, 12]
self._layout['line2'] = [0, 116, 152, 116]
self._layout['friend_face'] = (12, 88)
self._layout['friend_name'] = (1, 103)
self._layout['shakes'] = (26, 117)
self._layout['mode'] = (0, 117)
self._layout['status'] = {
'pos': (65, 26),
'font': fonts.status_font(fonts.Small),
'max': 12
}
return self._layout
def initialize(self):
logging.info("initializing waveshare 2.66b inch lcd display")
from pwnagotchi.ui.hw.libs.waveshare.v2in66b.epd2in66b import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare2in66g(DisplayImpl):
def __init__(self, config):
super(Waveshare2in66g, self).__init__(config, 'waveshare2in66g')
self._display = None
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
@ -39,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()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare27inch(DisplayImpl):
def __init__(self, config):
super(Waveshare27inch, self).__init__(config, 'waveshare2in7')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare27inchV2(DisplayImpl):
def __init__(self, config):
super(Waveshare27inchV2, self).__init__(config, 'waveshare2in7_v2')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
@ -42,7 +41,7 @@ class Waveshare27inchV2(DisplayImpl):
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display_Partial(buf,0,0,176,264)
self._display.display_Partial(buf, 0, 0, 176, 264)
def clear(self):
# This line also removes the 0xFF

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare27b(DisplayImpl):
def __init__(self, config):
super(Waveshare27b, self).__init__(config, 'waveshare2in7b')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
@ -40,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)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare27bV2(DisplayImpl):
def __init__(self, config):
super(Waveshare27bV2, self).__init__(config, 'waveshare2in7b_v2')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
@ -40,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)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare29inch(DisplayImpl):
def __init__(self, config):
super(Waveshare29inch, self).__init__(config, 'waveshare2in9')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare29inchV2(DisplayImpl):
def __init__(self, config):
super(Waveshare29inchV2, self).__init__(config, 'waveshare2in9_v2')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare29bV3(DisplayImpl):
def __init__(self, config):
super(Waveshare29bV3, self).__init__(config, 'waveshare2in9b_v3')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
@ -36,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()

View File

@ -7,7 +7,6 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare29bV4(DisplayImpl):
def __init__(self, config):
super(Waveshare29bV4, self).__init__(config, 'waveshare2in9b_v4')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
@ -36,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()

Some files were not shown because too many files have changed in this diff Show More