Compare commits

..

430 Commits

Author SHA1 Message Date
fb7ef7b1dd Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-11-17 21:56:42 +01:00
28ff1429ac Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-11-17 21:52:58 +01:00
68941abc08 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-11-17 21:00:28 +01:00
a7379d18e6 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-11-17 20:42:34 +01:00
f9318ef27a Merge remote-tracking branch 'origin/master' 2024-11-17 20:07:59 +01:00
cedb3d35ff Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-11-17 20:07:55 +01:00
4861a0c08b Merge pull request #211 from SeanDuggan/master
Icelandic Translations for IS Locale
2024-10-02 19:25:34 +02:00
766463280d Updated missing charset line
Signed-off-by: SeanDuggan <duggse01@risk.regn.net>
2024-10-01 16:48:39 +01:00
29e278da57 Icelandic Translations
Signed-off-by: SeanDuggan <duggse01@risk.regn.net>
2024-09-30 22:16:06 +01:00
7e6d198c9b Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-09-19 07:37:15 +02:00
4029b2ffa0 Merge pull request #203 from Sniffleupagus/usr1-save-recovery
add mode parameter to agent._restart, and call agent._restart from US…
2024-09-08 21:04:46 +02:00
32d244ec6f add mode parameter to agent._restart, and call agent._restart from USR1 handler instead of pwnagotchi.restart() 2024-09-08 19:25:09 +01:00
Rai
79e141320c Merge pull request #202 from quymdh/dev-q
Update: agent.py, train.py
2024-08-31 04:37:15 +10:00
330110843c Fix:
- Pwnagochi service is not running
 - Handshake not being captured
2024-08-30 16:19:43 +00:00
dc5b297a82 Merge pull request #197 from keganpowers34/patch-1
Update waveshare3in7.py
2024-08-26 21:43:22 +02:00
e8f23d654c Update waveshare3in7.py
fix return outside of function error 

Signed-off-by: keganpowers34 <42286354+keganpowers34@users.noreply.github.com>
2024-08-26 12:04:39 -06:00
Rai
aedbf6bbfb Update README.md
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-08-26 23:26:40 +10:00
0b3fc98f1c Merge pull request #196 from EatPrilosec/fixbuild
Fix build
2024-08-25 22:13:21 +02:00
7edaa9040e remove unnecessary action input
Signed-off-by: EatPrilosec <ae3nerdgod@gmail.com>
2024-08-25 07:02:37 -04:00
6b6f5923ee Compensate for ../../../<output file>.img
i assume this is for local builds, because it outputs the file outside of runner workdir entirely. compensated by making extra directory levels

Signed-off-by: EatPrilosec <ae3nerdgod@gmail.com>
2024-08-25 05:29:56 -04:00
d6bd65333e Employ matrix build strategy
- workaround for binfmt error
- speeds up builds
- allows for easier board additions

Signed-off-by: EatPrilosec <ae3nerdgod@gmail.com>
2024-08-24 22:44:46 -04:00
f81f1cedfc fix build
libraspberrypi-bin seems to be provided by raspi-utils now

Signed-off-by: EatPrilosec <ae3nerdgod@gmail.com>
2024-08-24 12:05:59 -04:00
f6fa4ef39e fix build
libraspberrypi-bin seems to be provided by raspi-utils now

Signed-off-by: EatPrilosec <ae3nerdgod@gmail.com>
2024-08-24 12:05:09 -04:00
e2ac10babb Merge pull request #195 from mendoitarou/master
Update Japanese (JA) Language
2024-08-23 20:54:10 +02:00
bff310ea3e Add files via upload
Signed-off-by: mendoitarou_ <42207304+mendoitarou@users.noreply.github.com>
2024-08-23 21:01:31 +09:00
a53130321f Update voice.po
Signed-off-by: mendoitarou_ <42207304+mendoitarou@users.noreply.github.com>
2024-08-23 20:39:56 +09:00
730cdad7cd Delete pwnagotchi/locale/ja/LC_MESSAGES/voice.mo
Signed-off-by: mendoitarou_ <42207304+mendoitarou@users.noreply.github.com>
2024-08-23 20:39:22 +09:00
85746046e1 Merge pull request #192 from wlmh110/master
waveshare e-lnk 2.13 V3/V4 update
2024-08-21 18:31:45 +02:00
fbe8211a10 waveshare e-lnk 2.13 V3/V4 update
Optimize the initialization function to resolve the issue of noise in the first few frames caused by the lack of buffer initialization.

Signed-off-by: 铲屎将军 <37292630+wlmh110@users.noreply.github.com>
2024-08-21 22:43:47 +08:00
Rai
ea4f495ab8 Merge pull request #167 from Sniffleupagus/patch-3
Add new configuration option "ui.cursor" to enable or disable blinking cursor
2024-08-19 02:26:24 +10:00
afb840212d Added ui.cursor to defaults.toml
Signed-off-by: Sniffleupagus <129890632+Sniffleupagus@users.noreply.github.com>
2024-08-18 12:16:14 -04:00
82b64baaed Fixed default value to maintain original behavior (True)
Signed-off-by: Sniffleupagus <129890632+Sniffleupagus@users.noreply.github.com>
2024-08-18 12:15:03 -04:00
244d3c03f9 Change 'showcursor' to 'curso
Signed-off-by: Sniffleupagus <129890632+Sniffleupagus@users.noreply.github.com>
2024-08-18 12:06:45 -04:00
Rai
c440a73824 Update README.md
changes a few url

Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-08-17 21:57:02 +10:00
Rai
48b3d6dcb5 Update waveshare3in7.py
adds fixes for #185 

Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-08-13 23:21:13 +10:00
Rai
b9531c60a2 adds v3 and v4 to waveshare 2in3 is any
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-08-13 23:07:40 +10:00
Rai
27aac52e1f missed a thread
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-08-11 02:57:44 +10:00
77998cd214 Merge pull request #182 from rai68/log-changes
Log changes
2024-08-07 19:45:35 +02:00
Rai
03c33e1be3 Merge branch 'master' into log-changes
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-08-07 00:58:01 +10:00
4f63af63a8 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-31 00:33:52 +02:00
6ceb27f837 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-30 22:51:03 +02:00
5d5cde5409 Revert "Update build"
This reverts commit 2630068c7b.
2024-07-28 17:22:45 +02:00
2630068c7b Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-28 17:21:28 +02:00
Rai
de940d0e77 Merge branch 'jayofelony:master' into log-changes 2024-07-28 20:27:09 +10:00
0f36a10567 Update __init__.py 2024-07-28 17:46:53 +10:00
65a93812ef Merge remote-tracking branch 'origin/master' 2024-07-26 23:29:05 +02:00
022ebccf30 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-26 23:28:59 +02:00
Rai
e5812e86c1 fixes an indent in 1in44 lcd waveshare
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-07-13 19:05:12 +10:00
1ee940c798 adds daemonise and plugin as threads 2024-07-09 07:35:53 +10:00
e3f0da6193 small fixes 2024-07-09 04:57:52 +10:00
f9efbb56cd Update defaults.toml 2024-07-09 04:24:43 +10:00
89a589af72 changes to logging and threading 2024-07-09 04:22:18 +10:00
a100933678 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-02 15:10:17 +02:00
3584f3f551 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-02 15:04:51 +02:00
3fdcebc90c Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-02 15:04:33 +02:00
e33bec1d31 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-02 15:03:06 +02:00
de0a09e39d Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-01 20:15:45 +02:00
dbb83b4825 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-01 16:05:45 +02:00
956c083985 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-01 16:00:09 +02:00
62c324730c Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-01 13:17:12 +02:00
8dc6f78610 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-01 13:16:44 +02:00
0542c002d0 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-01 13:16:28 +02:00
702d463194 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-07-01 07:51:18 +02:00
087fd9cf09 Revert
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-30 20:07:53 +02:00
28335419f6 MLS is offline
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-30 20:07:41 +02:00
db444588dd Wiki
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-16 17:07:58 +02:00
de08804b7f pycharm files
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:04:42 +02:00
9d6a7e9441 Same files added to create a package
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:04:17 +02:00
14480346fe Change how spi is called.
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:03:56 +02:00
7b6ad4e99a More pythonic
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:03:36 +02:00
313edb1524 Duplicate display file
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:03:23 +02:00
820f2cef59 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:02:58 +02:00
c76d9f4909 Try to get a fix for device is busy while changing channels.
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:02:47 +02:00
69ff6cd03c Add pisugar 3 plugin to default plugins
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:02:23 +02:00
09f6281666 Remove bt helper
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:02:07 +02:00
2f1c216387 Add inky phat v2
Change import for v1

Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:01:52 +02:00
87d8beb9be Change bettercap version check for oPwngrid
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:01:17 +02:00
e63be1ffd8 Add extra check for fix_services.py
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:00:53 +02:00
d5755103a0 Remove pisugar2 settings
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 20:00:30 +02:00
2014c22262 Fix couple displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 19:59:51 +02:00
5295692da9 Added display inky phat v2
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-15 19:58:54 +02:00
b7eb86d55c Update view.py 2024-06-13 05:45:42 +10:00
81c3581138 Merge pull request #149
Move BT status position in view
2024-06-12 21:00:40 +02:00
7d8b66e1cb Update displayhatmini.py 2024-06-13 04:18:57 +10:00
689d39c450 more changes 2024-06-13 03:37:51 +10:00
8eccd71c6b changes to ui 2024-06-13 02:55:05 +10:00
Rai
caf73700fd Merge branch 'jayofelony:master' into master 2024-06-12 20:40:08 +10:00
e9c12dde94 Add new configuration option "ui.showcursor" to enable or disable blinking cursor by name
When ui.fps is not zero, the default UI adds a blinking cursor next to the pwnagotchi name, as a verification that the display is updating. This proposed change adds a configuration option "ui.showcursor" to allow users to disable the cursor. If ui.showcursor is set to "true" in config.toml, the cursor will show, if set "false" in config, the cursor will not be shown. Default behavior if ui.showcursor is not set is maintained (cursor will show). If ui.fps is set to 0, the cursor will not be shown regardless of the ui.showcursor setting.

(tested on a single pwny with showcursor unset, false, and true, multiple times)

Signed-off-by: Sniffleupagus <129890632+Sniffleupagus@users.noreply.github.com>
2024-06-11 10:00:32 -07:00
3f40c0b7b3 Switch to bettercap lite
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-06 14:36:33 +02:00
318e3480ce Remove clear on exit, invokes an extra restart at boot
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-06 13:43:59 +02:00
1614d1f94d Updated fix_services.py
Updated pwnagotchi

Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-06 13:41:19 +02:00
4e1f08f6a8 Updated partnership with PiSugar team.
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-05 10:55:21 +02:00
cbc601e38b Updated displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-05 10:50:15 +02:00
20bae7adaa Version 2.9.2
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-03 21:49:57 +02:00
ed62ac0b84 Version 2.9.2
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-03 20:06:32 +02:00
f0ecee0555 Merge remote-tracking branch 'origin/master' 2024-06-02 12:47:58 +02:00
8e99ae419d Version 2.9.2
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-06-02 12:47:51 +02:00
88957059ef Merge pull request #148 from energydrinksjunkie/master
added serbian language
2024-05-01 23:27:45 +02:00
c42e8d0f5d Move BT status position in view
This moves the position of BT status a little to the left to make enough space for battery plugin status.

When a battery plugin is used along with bt-tether, BT status can overwrite the first character of the battery plugin status.

Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
2024-05-01 16:26:11 -04:00
5b5718c186 added serbian language 2024-05-01 22:22:01 +02:00
70f0dcb891 Merge pull request #144 from jfhenriques/master
Fix condition error in view.py when using invert=true
2024-04-26 19:06:09 +02:00
8f02c36d63 Merge branch 'jayofelony:master' into master 2024-04-26 16:16:11 +01:00
9941f093e9 Fix issue with FilledRect when using ui.invert=true 2024-04-26 16:14:05 +01:00
de07226be1 Update _version.py 2024-04-26 17:03:16 +02:00
6603e01005 Update pwnlib 2024-04-26 17:02:51 +02:00
e2fa3ca138 Update pwnlib 2024-04-26 17:02:33 +02:00
357ffc67e5 Update _version.py
Signed-off-by: Jayofelony <oudshoorn.jeroen@gmail.com>
2024-04-26 09:19:13 +02:00
ea8fb5e533 Merge pull request #139 from RKouchoo/master
Fix 3.7 orientation
2024-04-26 06:37:39 +02:00
31afd6d0ba Merge pull request #138 from kszabi1/fix-ui-components-draw
Fix [#137 issue]: Incorrect use of assignment operator (=) instead of comparison operator (==) in draw method
2024-04-26 06:37:28 +02:00
b38274e652 Fix 3.7 orientation 2024-04-26 13:21:55 +10:00
518e8c219c Fix incorrect use of assignment operator (=) instead of comparison operator (==) in draw method of ui/components.py 2024-04-26 01:34:26 +02:00
4e07fbf1aa Update raspberrypi32.yml
Signed-off-by: Jayofelony <oudshoorn.jeroen@gmail.com>
2024-04-25 20:30:45 +02:00
b3e81a95c8 Update raspberrypi32.yml
Signed-off-by: Jayofelony <oudshoorn.jeroen@gmail.com>
2024-04-25 20:06:54 +02:00
050966215b Merge pull request #132
I2C oled support for SSD1306 chipsed oled screens
2024-04-22 08:41:34 +02:00
fef442edbb I2C oled driver mods by NurseJackass
I2C oled config can be added in the config.toml
Default is 128x64 display on i2c address 0x3C

ui.display.type = "i2coled"
ui.display.i2c_addr = 0x3C
ui.display.width = 128
ui.display.height = 64
2024-04-18 17:39:53 +02:00
1064936503 Small changes
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-04-15 15:23:47 +02:00
a762a7f763 Merge remote-tracking branch 'origin/master' 2024-04-15 15:23:41 +02:00
8fd0e358a9 Small changes
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-04-15 15:23:34 +02:00
9a941c1d46 Merge branch 'jayofelony:master' into master 2024-04-04 15:46:29 +02:00
ead5a3baac Merge pull request #121 from rai68/patch-2
Update components.py
2024-04-03 21:58:17 +02:00
ac2973889d Update FUNDING.yml 2024-04-03 21:14:22 +02:00
Rai
8991dd6811 Update components.py
this allows plugins / ui elements of bitmap to be inverted for plugins etc

Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-04-04 03:04:31 +10:00
415e5cd551 Small changes
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-04-03 09:39:38 +02:00
47705ba1a2 Update config.txt at creation of image file, no more overwriting at auto-update.
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-04-03 09:39:10 +02:00
7c7bbc770b Merge branch 'jayofelony:master' into master 2024-04-02 17:54:00 +02:00
ab83de4905 separate I2C Oled display
Separate files for ggeneric I2C Oled display with SSD1306 driver
128x64 resolution is tested, 128x32 and 96x16 resolutions is not tested yet
2024-04-02 17:46:11 +02:00
8f7741cd9e Remove unused imports
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-04-01 17:30:51 +02:00
835886e6f6 Merge pull request #114
Update Traditional Chinese (TW) Language
2024-04-01 17:29:58 +02:00
6b2039a8e6 Merge pull request #116
Fix Waveshare V2 Position
2024-04-01 17:29:28 +02:00
d53d0c7841 Merge pull request #117
Fix range of lines about the displays
2024-04-01 17:29:10 +02:00
6c40998b51 Merge pull request #118
Fix typo in ansible script
2024-04-01 17:28:18 +02:00
a71a90ba3d Delete config.py 2024-04-01 01:37:47 +02:00
b03f6f747b Generic I2C Oled support , waveshare oled/lcd vertical layout
work in progress for the triple screen project
2024-03-31 23:55:15 +02:00
a8ba88c9cc Fix typo in ansible script
Signed-off-by: Morten Winther Olsson <18525061+olwimo@users.noreply.github.com>
2024-03-30 08:04:55 +01:00
f597bd6d29 Fix range of lines about the displays
Signed-off-by: Benoît Allard <benoit.allard@gmx.de>
2024-03-29 15:08:07 +01:00
a3f103ac06 Fix Waveshare V2 Position
The current location "178, 84" does not correctly display the temperature symbol (Cellsius, Kelvin, etc.). I fix the code whit the correct position.

Signed-off-by: Andrea Draghetti <drego85@draghetti.it>
2024-03-29 08:57:45 +01:00
e0a068e51d Upload tw voice.mo
Signed-off-by: alan67160 <20385640+alan67160@users.noreply.github.com>
2024-03-27 18:46:42 +08:00
dc1b3c7635 Delete tw voice.mo
Signed-off-by: alan67160 <20385640+alan67160@users.noreply.github.com>
2024-03-27 18:45:59 +08:00
3657859a73 Update tw voice.po
Signed-off-by: alan67160 <20385640+alan67160@users.noreply.github.com>
2024-03-27 18:45:21 +08:00
33ff5a0bf8 Fix ui.invert
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-27 07:37:59 +01:00
666f65c640 Merge pull request #110
Update Portuguese (Brazil) Language
2024-03-27 07:36:24 +01:00
84d45a0d45 Version 2.9.0
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-27 07:20:45 +01:00
06a4491008 Update 32bit to use latest pwngrid/bettercap
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-27 07:17:35 +01:00
5485f83ca6 Update Portuguese (Brazil) Language 2024-03-26 14:06:55 -03:00
Rai
8a242a707b Update LCD_2inch4.py
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-03-27 02:06:05 +10:00
Rai
8a3eacb5d2 Update LCD_2inch4.py
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-03-27 02:05:24 +10:00
ab541458fa add extra check for fix_services
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-26 12:15:00 +01:00
ac345c2ee7 make_latest: true
generate_release_notes: true

Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-26 07:58:06 +01:00
468cfd9f4f prerelease: false
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-26 07:55:06 +01:00
d6bc5c0e66 Reboot after auto-update
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-26 00:07:15 +01:00
4905eb6b26 Optimize display imports
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-26 00:05:50 +01:00
697a7778b1 Fix fix_services.py
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-25 23:29:10 +01:00
9287283ee7 Added 11 waveshare lcd displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-25 22:53:07 +01:00
8013109ef7 Refactoring displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-25 22:52:42 +01:00
f140fe1a2d Update setup.py to look for empty apt_packages.txt
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-25 11:58:46 +01:00
8fe503c67d No new apt-packages needed
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-25 08:11:39 +01:00
d81013412e Update several displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-24 15:18:58 +01:00
3c701822bd Update donation method
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-24 12:58:26 +01:00
2f5ddb492a Small typo's
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-24 12:58:07 +01:00
9e3324221d Fix adafruit 2.13inch display
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-24 12:57:40 +01:00
b069b82984 Version 2.8.9
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-24 12:57:20 +01:00
cb7d965271 Update MOTD
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-24 12:57:08 +01:00
7abf9ff8da Update faces.py
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-23 09:49:46 +01:00
18fb956251 Update setup.py
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-23 09:46:32 +01:00
afb1d11cd8 Added Adafruit 2in13_V3 display
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-23 09:24:52 +01:00
627be80e6c add system wide aliases, if you were to use custom users
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-22 19:09:49 +01:00
c4c4d6c417 in case you use an unsupported display it will default to dummy display, displaying nothing
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-22 19:09:27 +01:00
8dcae13ce9 add commented dtoverlay=disable-wifi
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-22 19:09:00 +01:00
1352e99774 Fix wrong imports
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-22 18:34:49 +01:00
2182d7c29d Update Frysian Language
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-22 09:56:47 +01:00
703c05a93b Full SSID again
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-22 09:06:04 +01:00
697cc5d88b Add DummyDisplay
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-22 08:37:56 +01:00
4b04f9b7a5 Install OS packages from auto-update
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-22 08:37:22 +01:00
c7b94a0707 Version 2.8.8 starts here
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-22 08:37:01 +01:00
5116bac2a7 Multiple changes
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-20 22:32:04 +01:00
51625e61f9 Multiple changes
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-20 22:25:36 +01:00
bf9a0a96c1 Update raspberrypi64.yml 2024-03-20 21:17:20 +01:00
bd03f07aa8 Update raspberrypi32.yml 2024-03-20 21:16:50 +01:00
3f13df8f20 Merge pull request #104
Update defaults.toml
2024-03-20 07:20:19 +01:00
ce0275f2ae Update defaults.toml
Removed main.filter since it's no longer referenced in agent.py as of this commit 1a55afd74a (diff-0548f0a21e1984ecf3c32d6cd9c17e6a62f4f139c001360935890f649b745cc7)

Signed-off-by: seelenamt <dandrewe@gmail.com>
2024-03-19 10:51:59 -05:00
b971f18f75 Update Sponsor links
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-19 13:19:54 +01:00
ea9d11d018 Update Sponsor links
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-19 13:19:00 +01:00
8a572f1b70 Update Sponsor links
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-19 13:16:14 +01:00
0c4f2a5093 small change
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-19 09:25:17 +01:00
7edd752664 small change
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-19 07:56:06 +01:00
cc550aa236 small change
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-18 15:05:00 +01:00
ff033d41d3 Revert "fallback to older 32bit image"
This reverts commit b33af167d4.
2024-03-18 14:56:59 +01:00
6d0a0d8d5f fallback to older 32bit image
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-18 13:04:19 +01:00
b33af167d4 fallback to older 32bit image
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-18 12:03:20 +01:00
9053762e71 2.8.7.2
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-18 11:59:32 +01:00
26fef7dd99 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	builder/data/64bit/raspberrypi64.yml
2024-03-18 11:58:33 +01:00
5dd17291f7 Fix a lot of stuff
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-18 11:58:22 +01:00
bac79f3465 Update _version.py 2024-03-17 21:17:08 +01:00
bb8dfe0244 Update raspberrypi64.yml 2024-03-17 21:16:25 +01:00
1b16975031 Update workflow
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-17 09:24:57 +01:00
dd2b559ebc Update workflow
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-17 09:22:35 +01:00
93ba2a7f79 Update workflow
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-17 09:20:02 +01:00
dd18072002 Update workflow
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-17 09:18:31 +01:00
1eafbb841f Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-17 01:10:56 +01:00
aa817288ea Update Raspios image
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-17 00:50:17 +01:00
43285eb2c0 Update Raspios image
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-17 00:47:49 +01:00
4b29476c2f Update Raspios image
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-17 00:46:09 +01:00
1a8ff930d9 Update nexmon
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-17 00:34:54 +01:00
8a65afdc8f Update nexmon
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-16 10:52:56 +01:00
6fcd1b0e23 Fix displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-15 15:50:43 +01:00
e0b0f5d800 Fix displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-15 15:07:55 +01:00
2243079bf6 Fix displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-15 15:07:15 +01:00
e10eb31ed1 Fix displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-15 13:41:27 +01:00
1c8114e444 Merge remote-tracking branch 'origin/master' 2024-03-15 10:00:01 +01:00
91638a151f Fix displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-15 09:59:55 +01:00
d4adaabcbd Merge pull request #93
Screen support
2024-03-13 23:03:44 +01:00
006cdb0fe3 Add 2 waveshare displays
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-13 22:41:18 +01:00
29386fb945 Update build to kernel 6.6.20
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-13 21:49:12 +01:00
4b4646d604 Added 2 waveshare diplays:
5.79 inch
5.79b inch

Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-13 21:49:00 +01:00
3fae6ec312 Screen support
Screen support for the following displays:
Adafruit pitft (2,4" and 2,8" tested)
Adafruit tft bonnet
Pimoroni pirate audio
Waveshare OLED/LCD hat
2024-03-13 16:31:49 +01:00
09a82aa0b4 revert pcapng
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-13 07:59:37 +01:00
541865a2eb Merge pull request #92 from jayofelony/dev
Dev
2024-03-13 07:57:37 +01:00
928de2769d Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-13 07:55:53 +01:00
6987840da2 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	Makefile
#	pwnagotchi/plugins/default/hashie.py
2024-03-07 21:37:27 +01:00
58de15ce2d Update Makefile
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-06 16:55:04 +01:00
b5ea3da619 Update Makefile
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-06 15:23:40 +01:00
6c68d4608f Fix gps plugin
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-06 15:23:30 +01:00
14a727954b Update Makefile and build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-05 21:48:33 +01:00
aeada2ee6e Fix waveshare2in7_V2.py
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-05 21:48:16 +01:00
ebb8fef3fc Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-05 19:54:47 +01:00
e531288369 Added WeAct 2.9 inch display
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-05 19:54:33 +01:00
2015b56c5d Fix waveshare3in52.py
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-05 19:54:10 +01:00
2497475057 Added WeAct 2.9 inch display
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-05 19:54:01 +01:00
0bdbbc23fd Version 2.8.7
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-05 19:53:35 +01:00
111787544f Merge remote-tracking branch 'origin/master' 2024-03-05 16:31:06 +01:00
efa5d8b197 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-05 16:31:00 +01:00
9e5fb49d7c Merge pull request #78 from rai68/master
Update view.py - bug fix, my self left a broken debug line in
2024-03-05 06:14:15 +01:00
Rai
cbbd7d5a6d Merge branch 'jayofelony:master' into master 2024-03-05 14:36:29 +10:00
Rai
90c5818123 Update view.py
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-03-05 13:23:30 +10:00
eb76ef4c54 Update build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-04 21:21:48 +01:00
59e42daeb5 Update bash
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-03-04 21:13:33 +01:00
5b7014c68e Change MOTD 2024-03-04 21:01:23 +01:00
42cc3136ee Update wizard script 2024-03-04 20:54:25 +01:00
2a243870f7 Update wizard script 2024-03-04 20:49:38 +01:00
8f043ff5ef Update wizard script 2024-03-04 20:47:52 +01:00
255bbdbc08 Update wizard script 2024-03-04 20:40:16 +01:00
6f57b66cf4 Add display invert to --install 2024-03-04 20:35:09 +01:00
a4c25e9996 Add display invert to --install 2024-03-04 20:28:31 +01:00
9495d55296 Add display invert to --install 2024-03-04 20:25:22 +01:00
69e7503d67 Add display invert to --install 2024-03-04 20:21:41 +01:00
25cb1f2175 Add display invert to --install 2024-03-04 20:11:20 +01:00
54b7ce5d0d Add display invert to --install 2024-03-04 20:08:45 +01:00
1f9afd541d Add display invert to --install 2024-03-04 19:59:48 +01:00
87eae76a58 Add display invert to --install 2024-03-04 16:07:17 +01:00
6691257036 change small font size 2024-03-04 16:00:31 +01:00
2149c5dbdf remove color = black 2024-03-04 15:55:50 +01:00
77af772b4b remove color = black 2024-03-04 15:53:18 +01:00
14e4fc6d47 Add a interactive configuration installer 2024-03-04 15:48:52 +01:00
5be8580a59 Add a interactive configuration installer 2024-03-04 15:46:23 +01:00
f7a599ab8f Add a interactive configuration installer 2024-03-04 15:42:36 +01:00
5f907b236a Add a interactive configuration installer 2024-03-04 15:39:24 +01:00
bc92613700 Add a interactive configuration installer 2024-03-04 15:36:17 +01:00
501ec9ca2b Add a interactive configuration installer 2024-03-04 15:34:39 +01:00
e5e0180f3c Add a interactive configuration installer 2024-03-04 15:32:08 +01:00
ea60808700 Add a interactive configuration installer 2024-03-04 15:26:48 +01:00
a34db250b5 Add a interactive configuration installer 2024-03-04 15:24:45 +01:00
d29aca15a9 Add ui.invert = false to defaults 2024-03-04 15:24:17 +01:00
8531b89771 Add ui.invert = false to defaults 2024-03-04 13:02:33 +01:00
59d510d0e1 Merge pull request #77
Adds invertable BLACK/WHITE to config
2024-03-04 12:56:52 +01:00
913b1a6e1d Update 2.7 inch display 2024-03-04 12:55:12 +01:00
Rai
de2cdaa3c9 Update view.py
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-03-04 20:41:19 +10:00
Rai
f2cf34a8b9 Update view.py
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-03-04 20:39:55 +10:00
Rai
bbb46128fe Update view.py
Signed-off-by: Rai <58925163+rai68@users.noreply.github.com>
2024-03-04 20:39:20 +10:00
46713b6e73 Update view.py 2024-03-04 19:59:34 +10:00
aa2b09fb21 Update build 2024-03-01 10:19:32 +01:00
9125e43b20 Add display type variants, no new displays 2024-03-01 10:08:03 +01:00
7e4d926b14 Fix setup.py 2024-03-01 10:07:33 +01:00
6417ef5a78 Upgrade pip, setuptools, wheel 2024-03-01 10:07:25 +01:00
46c03063fe Version 2.8.6 2024-02-29 23:05:30 +01:00
d5384d5a81 Moved all reset logging to debug, except for plugin loaded.
Removed the counter from display
2024-02-29 23:01:05 +01:00
e800c66e57 Let auto-update check for armhf/aarch64 (pwngrid/bettercap) 2024-02-29 22:49:21 +01:00
e3a404cb39 Fix waveshare1in54.py 2024-02-29 10:23:15 +01:00
6cb6aaeb81 Merge remote-tracking branch 'origin/master' 2024-02-29 10:22:52 +01:00
5761dac073 Fix pwnagotchi plugins list error if no update has been ran first 2024-02-29 10:22:41 +01:00
3ada0628e1 Merge pull request #74 from Sniffleupagus/patch-1
Automatically remove invalid ai parameters leftover from tensorflow
2024-02-28 18:25:47 +01:00
9fa772c36a Automatically remove invalid ai parameters leftover from tensorflow
Ease transition from old Pwnagotchi running tensor flow to new running torch, but automatically dumping the invalid parameters.

Signed-off-by: Sniffleupagus <129890632+Sniffleupagus@users.noreply.github.com>
2024-02-28 09:58:41 -05:00
a92e66137c Fix build 2024-02-27 11:59:22 +01:00
a7e98cf166 Fix build 2024-02-27 10:24:44 +01:00
9a1a264a9f Fix waveshare1in54.py 2024-02-27 10:21:29 +01:00
8a0d482fe0 Make pwnlog point to correct log location 2024-02-27 10:19:30 +01:00
0cc320d31f Fix waveshare1in54.py 2024-02-27 10:12:38 +01:00
d841d2b649 Update build 2024-02-27 10:11:00 +01:00
966126a986 Update build 2024-02-27 10:10:06 +01:00
d67935fa6a Update build 2024-02-26 21:27:10 +01:00
46d867ce7f Update build 2024-02-26 21:20:56 +01:00
2e16069850 Update workflow 2024-02-26 20:36:16 +01:00
6e3cbbdd39 Update Makefile 2024-02-26 20:15:55 +01:00
a19bd6a181 Update setup.py 2024-02-26 19:38:54 +01:00
6c03d95724 Update build 2024-02-26 19:34:14 +01:00
60a9da9acc Update workflow 2024-02-26 13:09:15 +01:00
25aba0d73c Update workflow 2024-02-26 13:04:00 +01:00
ecde496534 Update workflow 2024-02-26 11:49:39 +01:00
7ebd4dd7a0 Update workflow 2024-02-26 10:48:21 +01:00
35f1707436 Update workflow 2024-02-26 08:58:06 +01:00
60ced12ea5 Update workflow 2024-02-26 08:56:37 +01:00
6082a0c169 Update workflow 2024-02-26 08:55:05 +01:00
a72c1b1628 Update workflow 2024-02-26 08:54:05 +01:00
e37a0bdc62 Update workflow 2024-02-26 08:52:17 +01:00
262f8bf551 Update workflow 2024-02-26 08:48:30 +01:00
5311dd9ddc Update workflow 2024-02-25 21:54:39 +01:00
bd9c44b4a3 Update workflow 2024-02-25 20:50:08 +01:00
3b01aec872 Update workflow 2024-02-25 20:33:09 +01:00
3e571bff2d Update workflow 2024-02-25 14:29:23 +01:00
391941e64a Update workflow 2024-02-25 14:28:09 +01:00
3fff6182ed Update workflow 2024-02-25 14:23:16 +01:00
73e0a1fce7 Update auto-update.py 2024-02-25 13:42:12 +01:00
9ba23d77d1 Update Makefile
Remove bananagotchi files
2024-02-25 13:35:23 +01:00
8be75627e9 Update build 2024-02-25 12:58:40 +01:00
c2a36aa678 Update README.md 2024-02-25 12:29:11 +01:00
fdf0a087f6 Update workflow 2024-02-25 12:23:56 +01:00
c6efa5df08 Update workflow 2024-02-25 12:11:47 +01:00
62327a711e Update workflow 2024-02-25 12:11:20 +01:00
bb460a9cc6 Update workflow 2024-02-25 12:07:11 +01:00
93b2322ab5 Update workflow 2024-02-25 12:05:14 +01:00
53a8af4711 Update workflow 2024-02-25 12:04:16 +01:00
ed5decffa0 Version 2.8.5 2024-02-25 12:00:29 +01:00
147cbfaa07 Version 2.8.5 2024-02-25 11:56:44 +01:00
ced9b4d5ee Update requirements.txt 2024-02-25 11:55:46 +01:00
472c3165ed Update workflow publisher 2024-02-25 11:50:56 +01:00
1578d13d98 Fix builder 2024-02-25 11:46:30 +01:00
0965e7eb3e Update image filename
Add pishrink to Makefile
2024-02-25 11:25:22 +01:00
b789c14f14 Change image filename, 32bit/64bit
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-25 11:18:19 +01:00
0855b44d59 Update everyting!
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-25 11:17:05 +01:00
55c6007d32 Update everyting!
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-25 11:10:59 +01:00
a7cf8a3383 Delete pwnagotchi/ui/hw/libs directory
Signed-off-by: Jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-25 10:45:28 +01:00
faa48b2752 Change repo's
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-25 10:42:47 +01:00
d20619340c Edit build
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-25 10:10:07 +01:00
43a07fe969 Edit Makefile
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-25 10:07:02 +01:00
bcce22c164 Edit Makefile
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-25 10:06:35 +01:00
0264b7a7db Merge remote-tracking branch 'origin/master' 2024-02-24 11:08:19 +01:00
fba5dd0341 Correct layout for waveshare3in52.py
Signed-off-by: jayofelony <oudshoorn.jeroen@gmail.com>
2024-02-24 11:08:00 +01:00
dd7760efdf Merge pull request #56 from Sniffleupagus/DummyDisplay
Added DummyDisplay for arbitrary screen size when headless
2024-02-20 20:49:04 +01:00
f7cd0fb1fc Initialize as "DummyDisplay", not "inky"
Signed-off-by: Sniffleupagus <129890632+Sniffleupagus@users.noreply.github.com>
2024-02-20 11:18:15 -05:00
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
0585fe75fe update 2024-02-09 13:14:10 +01:00
27e784a5d7 update 2024-02-09 13:12:40 +01:00
c02efb5224 update Makefile 2024-02-09 13:11:02 +01:00
a7634a2b4a update Makefile 2024-02-09 13:01:47 +01:00
ca4feb895e Revert "Revert "Testing pcapng fileformat""
This reverts commit 0e274af5a0.
2024-02-08 22:51:57 +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
418dbf21e3 Added DummyDisplay for arbitrary screen size when headless 2024-02-07 15:16:26 -08: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
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
378 changed files with 12091 additions and 4429 deletions

3
.github/FUNDING.yml vendored
View File

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

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

@ -0,0 +1,55 @@
name: Publish
on:
workflow_dispatch:
jobs:
build:
name: ${{ matrix.name }}
runs-on: ubuntu-latest
strategy:
matrix:
include:
- name: "Raspberry Pi 32-bit"
id: "32bit"
- name: "Raspberry Pi 64-bit"
id: "64bit"
steps:
- uses: actions/checkout@v4
with:
path: publish/build
- name: Extract version from file
id: get_version
run: |
VERSION=$(cut -d "'" -f2 < publish/build/pwnagotchi/_version.py)
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Install qemu dependencies
run: sudo apt update && sudo apt install qemu-user-static qemu-utils xz-utils -y
- name: Build ${{ matrix.name }} img file
run: cd publish/build; ls -la .; pwd; make packer; make ${{ matrix.id }}
- name: Change name of .img.xz to add version
run: |
sudo chown runner:docker "pwnagotchi-${{ matrix.id }}.img"
mv "pwnagotchi-${{ matrix.id }}.img" "pwnagotchi-${{ env.VERSION }}-${{ matrix.id }}.img"
- name: PiShrink
run: |
wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
chmod +x pishrink.sh
sudo mv pishrink.sh /usr/local/bin
sudo pishrink.sh -aZ "pwnagotchi-${{ env.VERSION }}-${{ matrix.id }}.img"
- name: Release
uses: softprops/action-gh-release@v2
with:
prerelease: false
make_latest: true
tag_name: v${{ env.VERSION }}
name: Pwnagotchi v${{ env.VERSION }}
files: pwnagotchi-${{ env.VERSION }}-${{ matrix.id }}.img.xz
generate_release_notes: true

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.pyc

19
.idea/deployment.xml generated
View File

@ -1,23 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PublishConfigData" serverName="pwnagotchi" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
<component name="PublishConfigData" 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>

2
.idea/misc.xml generated
View File

@ -3,7 +3,7 @@
<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.12 (pwnagotchi)" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" />
</component>

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.12 (pwnagotchi)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">

10
.idea/sshConfigs.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SshConfigs">
<configs>
<sshConfig authType="PASSWORD" connectionConfig="{&quot;serverAliveInterval&quot;:300}" id="8b69df7d-cec5-421f-8edf-53ed6233f6b6" port="22" customName="pwnagotchi" nameFormat="CUSTOM" useOpenSSHConfig="true">
<option name="customName" value="pwnagotchi" />
</sshConfig>
</configs>
</component>
</project>

14
.idea/webServers.xml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebServers">
<option name="servers">
<webServer id="cf2a1148-a103-4472-a782-7debdc6dabf9" name="pwnagotchi">
<fileTransfer accessType="SFTP" port="22" sshConfigId="8b69df7d-cec5-421f-8edf-53ed6233f6b6" sshConfig="pwnagotchi">
<advancedOptions>
<advancedOptions dataProtectionLevel="Private" passiveMode="true" shareSSLContext="true" />
</advancedOptions>
</fileTransfer>
</webServer>
</option>
</component>
</project>

View File

@ -1,4 +1,4 @@
PACKER_VERSION := 1.10.0
PACKER_VERSION := 1.11.0
PWN_HOSTNAME := pwnagotchi
PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py)
@ -25,7 +25,8 @@ ifneq (,$(UNSHARE))
UNSHARE := $(UNSHARE) --uts
endif
all: clean image clean
# sudo apt-get install qemu-user-static qemu-utils
all: packer image
update_langs:
@for lang in pwnagotchi/locale/*/; do\
@ -39,30 +40,23 @@ compile_langs:
./scripts/language.sh compile $$(basename $$lang); \
done
PACKER := ~/pwnagotchi/packer
PACKER_URL := https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_$(GOARCH).zip
$(PACKER):
mkdir -p $(@D)
curl -L "$(PACKER_URL)" -o $(PACKER).zip
unzip $(PACKER).zip -d $(@D)
rm $(PACKER).zip
chmod +x $@
packer:
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
unzip -o /tmp/packer.zip -d /tmp
sudo mv /tmp/packer /usr/bin/packer
SDIST := dist/pwnagotchi-$(PWN_VERSION).tar.gz
$(SDIST): setup.py pwnagotchi
python3 setup.py sdist
image: packer
export LC_ALL=en_GB.UTF-8
cd builder && sudo /usr/bin/packer init combined.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" combined.json.pkr.hcl
# Building the image requires packer, but don't rebuild the image just because packer updated.
pwnagotchi: | $(PACKER)
32bit: packer
export LC_ALL=en_GB.UTF-8
cd builder && sudo /usr/bin/packer init raspberrypi32.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" raspberrypi32.json.pkr.hcl
# If the packer or ansible files are updated, rebuild the image.
pwnagotchi: $(SDIST) builder/pwnagotchi.json.pkr.hcl builder/raspberrypi64.yml $(shell find builder/data -type f)
cd builder && $(PACKER) init pwnagotchi.json.pkr.hcl && sudo $(UNSHARE) $(PACKER) build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json.pkr.hcl
.PHONY: image
image: pwnagotchi
64bit: packer
export LC_ALL=en_GB.UTF-8
cd builder && sudo /usr/bin/packer init raspberrypi64.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" raspberrypi64.json.pkr.hcl
clean:
- rm -rf build dist pwnagotchi.egg-info
- rm -f $(PACKER)
- rm -rf /tmp/packer*
- rm -rf /tmp/LICENSE.txt

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

View File

@ -1,35 +1,17 @@
# 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~~!!.**
# Pwnagotchi
This is the main source for all forks:
- RPiZeroW (32bit)
- RPiZero2W, RPi3, RPi4, RPi5 (64bit)
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 the latest image file [here](https://github.com/jayofelony/pwnagotchi-bookworm/releases/tag/v2.7.9), and let it auto-update from here on out.
**Use RPi imager to flash, please don't flash a new user as this will mess with logs created.**
- Select `Use Custom Image`
- Browse for the downloaded image file
- Select No under `Use OS Customization`
SSH credentials are `pi/raspberry`.
# Donations:
I would like to thank
- [findingmoist](https://github.com/findingmoist)
- [kr4k0n](https://github.com/kr4k0n)
for donating!
[Pwnagotchi-Torch](https://www.patreon.com/pwnagotchi_torch)
**For installation docs check out the [wiki](https://github.com/jayofelony/pwnagotchi/wiki)!**
[GH Sponsor](https://github.com/sponsors/jayofelony)
**Proudly partnering with [PiSugar](https://www.pisugar.com)!!**
---
[Pwnagotchi](https://pwnagotchi.ai/) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),
[Pwnagotchi](https://pwnagotchi.org/) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),
full and half WPA handshakes.
![ui](https://i.imgur.com/X68GXrn.png)
@ -44,18 +26,17 @@ Multiple units within close physical proximity can "talk" to each other, adverti
## Documentation
https://www.pwnagotchi.ai
https://github.com/jayofelony/pwnagotchi/wiki
https://www.pwnagotchi.org
## Links
| &nbsp; | Official Links |
|-----------|-------------------------------------------------------------|
| Website | [pwnagotchi.ai](https://pwnagotchi.ai/) |
| Forum | [community.pwnagotchi.ai](https://community.pwnagotchi.ai/) |
| Slack | [pwnagotchi.slack.com](https://invite.pwnagotchi.ai/) |
| Website | [pwnagotchi.org](https://pwnagotchi.org/) |
| Forum | [discord.gg](https://discord.gg/PGgnzFbz4M) |
| Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) |
| Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) |
## License
`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It is released under the GPL3 license.
`pwnagotchi` created by [@evilsocket](https://twitter.com/evilsocket) and updated by [us](https://github.com/jayofelony/pwnagotchi/graphs/contributors). It is released under the GPL3 license.

View File

@ -1 +0,0 @@
hcxtools

View File

@ -7,6 +7,7 @@ import sys
import toml
import requests
import os
import re
import pwnagotchi
from pwnagotchi import utils
@ -50,7 +51,6 @@ def pwnagotchi_cli():
agent.mode = 'auto'
agent.start()
config = agent.config()
while True:
try:
@ -60,6 +60,7 @@ def pwnagotchi_cli():
channels = agent.get_access_points_by_channel()
# for each channel
for ch, aps in channels:
time.sleep(1)
agent.set_channel(ch)
if not agent.is_stale() and agent.any_activity():
@ -67,9 +68,6 @@ def pwnagotchi_cli():
# for each ap on this channel
for ap in aps:
if ap['mac'][:13].lower in config['main']['whitelist'] or ap['hostname'] in config['main']['whitelist']:
logging.info(f"Found your MAC address {ap['mac']} - {config['main']['whitelist']}")
continue
# send an association frame in order to get for a PMKID
agent.associate(ap)
# deauth all client stations in order to get a full handshake
@ -91,10 +89,8 @@ def pwnagotchi_cli():
except Exception as e:
if str(e).find("wifi.interface not set") > 0:
logging.exception(
"main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e)
logging.info(
"sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger")
logging.exception("main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e)
logging.info("sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger")
time.sleep(60)
agent.next_epoch()
else:
@ -136,6 +132,8 @@ def pwnagotchi_cli():
help="Print the configuration.")
# Jayofelony added these
parser.add_argument('--wizard', dest="wizard", action="store_true", default=False,
help="Interactive installation of your personal configuration.")
parser.add_argument('--check-update', dest="check_update", action="store_true", default=False,
help="Check for updates on Pwnagotchi. And tells current version.")
parser.add_argument('--donate', dest="donate", action="store_true", default=False,
@ -160,30 +158,135 @@ def pwnagotchi_cli():
print(pwnagotchi.__version__)
sys.exit(0)
if args.wizard:
def is_valid_hostname(hostname):
if len(hostname) > 255:
return False
if hostname[-1] == ".":
hostname = hostname[:-1] # strip exactly one dot from the right, if present
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(x) for x in hostname.split("."))
pwn_restore = input("Do you want to restore the previous configuration?\n\n"
"[Y/N]: ")
if pwn_restore in ('y', 'yes'):
os.system("cp -f /etc/pwnagotchi/config.toml.bak /etc/pwnagotchi/config.toml")
print("Your previous configuration is restored, and I will restart in 5 seconds.")
time.sleep(5)
os.system("service pwnagotchi restart")
else:
pwn_check = input("This will create a new configuration file and overwrite your current backup, are you sure?\n\n"
"[Y/N]: ")
if pwn_check.lower() in ('y', 'yes'):
os.system("mv -f /etc/pwnagotchi/config.toml /etc/pwnagotchi/config.toml.bak")
with open("/etc/pwnagotchi/config.toml", "a+") as f:
f.write("# Do not edit this file if you do not know what you are doing!!!\n\n")
# Set pwnagotchi name
print("Welcome to the interactive installation of your personal Pwnagotchi configuration!\n"
"My name is Jayofelony, how may I call you?\n\n")
pwn_name = input("Pwnagotchi name (no spaces): ")
if pwn_name == "":
pwn_name = "Pwnagotchi"
print("I shall go by Pwnagotchi from now on!")
pwn_name = f"main.name = \"{pwn_name}\"\n"
f.write(pwn_name)
else:
if is_valid_hostname(pwn_name):
print(f"I shall go by {pwn_name} from now on!")
pwn_name = f"main.name = \"{pwn_name}\"\n"
f.write(pwn_name)
else:
print("You have chosen an invalid name. Please start over.")
exit()
pwn_whitelist = input("How many networks do you want to whitelist? "
"We will also ask a MAC for each network?\n"
"Each SSID and BSSID count as 1 network. \n\n"
"Be sure to use digits as your answer.\n\n"
"Amount of networks: ")
if int(pwn_whitelist) > 0:
f.write("main.whitelist = [\n")
for x in range(int(pwn_whitelist)):
ssid = input("SSID (Name): ")
bssid = input("BSSID (MAC): ")
f.write(f"\t\"{ssid}\",\n")
f.write(f"\t\"{bssid}\",\n")
f.write("]\n")
# set bluetooth tether
pwn_bluetooth = input("Do you want to enable BT-Tether?\n\n"
"[Y/N] ")
if pwn_bluetooth.lower() in ('y', 'yes'):
f.write("main.plugins.bt-tether.enabled = true\n\n")
pwn_bluetooth_device = input("What device do you use? Android or iOS?\n\n"
"Device: ")
if pwn_bluetooth_device.lower() == "android":
f.write("main.plugins.bt-tether.devices.android-phone.enabled = true\n")
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
"MAC: ")
if pwn_bluetooth_mac != "":
f.write(f"main.plugins.bt-tether.devices.android-phone.mac = \"{pwn_bluetooth_mac}\"\n")
elif pwn_bluetooth_device.lower() == "ios":
f.write("main.plugins.bt-tether.devices.ios-phone.enabled = true\n")
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
"MAC: ")
if pwn_bluetooth_mac != "":
f.write(f"main.plugins.bt-tether.devices.ios-phone.mac = \"{pwn_bluetooth_mac}\"\n")
# set up display settings
pwn_display_enabled = input("Do you want to enable a display?\n\n"
"[Y/N]: ")
if pwn_display_enabled.lower() in ('y', 'yes'):
f.write("ui.display.enabled = true\n")
pwn_display_type = input("What display do you use?\n\n"
"Be sure to check for the correct display type @ \n"
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L501\n\n"
"Display type: ")
if pwn_display_type != "":
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
pwn_display_invert = input("Do you want to invert the display colors?\n"
"N = Black background\n"
"Y = White background\n\n"
"[Y/N]: ")
if pwn_display_invert.lower() in ('y', 'yes'):
f.write("ui.invert = true\n")
f.close()
if pwn_bluetooth.lower() in ('y', 'yes'):
if pwn_bluetooth_device.lower == "android":
print("To visit the webui when connected with your phone, visit: http://192.168.44.44:8080\n"
"Your configuration is done, and I will restart in 5 seconds.")
elif pwn_bluetooth_device.lower == "ios":
print("To visit the webui when connected with your phone, visit: http://172.20.10.6:8080\n"
"Your configuration is done, and I will restart in 5 seconds.")
else:
print("Your configuration is done, and I will restart in 5 seconds.")
time.sleep(5)
os.system("service pwnagotchi restart")
else:
print("Ok, doing nothing.")
sys.exit(0)
if args.donate:
print("Donations can made @ https://github.com/sponsors/jayofelony \n\nBut only if you really want to!")
print("Donations can be made @ \n "
"https://github.com/sponsors/jayofelony \n\n"
"But only if you really want to!")
sys.exit(0)
if args.check_update:
resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi-bookworm/releases/latest")
resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi/releases/latest")
latest = resp.json()
latest_ver = latest['tag_name'].replace('v', '')
local = version_to_tuple(pwnagotchi.__version__)
remote = version_to_tuple(latest_ver)
if remote > local:
user_input = input("There is a new version available! Update from v%s to v%s?\n[y(es)/n(o)]"
% (pwnagotchi.__version__, latest_ver))
user_input = input("There is a new version available! Update from v%s to v%s?\n[Y/N] " % (pwnagotchi.__version__, latest_ver))
# input validation
if user_input.lower() in ('y', 'yes'):
if os.path.exists('/root/.auto-update'):
os.system("rm /root/.auto-update && systemctl restart pwnagotchi")
os.system("systemctl restart pwnagotchi")
else:
logging.error("You should make sure auto-update is enabled!")
print("Okay, give me a couple minutes. Just watch pwnlog while you wait.")
elif user_input.lower() in ('n', 'no'):
elif user_input.lower() in ('n', 'no'): # using this elif for readability
print("Okay, guess not!")
else:
print("Invalid input.")
else:
print("You are currently on the latest release, v%s." % pwnagotchi.__version__)
sys.exit(0)
@ -220,7 +323,7 @@ def pwnagotchi_cli():
def usr1_handler(*unused):
logging.info('Received USR1 singal. Restart process ...')
restart("MANU" if args.do_manual else "AUTO")
agent._restart("MANU" if args.do_manual else "AUTO")
signal.signal(signal.SIGUSR1, usr1_handler)

View File

@ -0,0 +1,147 @@
packer {
required_plugins {
arm-image = {
source = "github.com/solo-io/arm-image"
version = ">= 0.0.1"
}
ansible = {
source = "github.com/hashicorp/ansible"
version = ">= 1.1.1"
}
}
}
variable "pwn_hostname" {
type = string
}
variable "pwn_version" {
type = string
}
source "arm-image" "rpi64-pwnagotchi" {
image_type = "raspberrypi"
iso_url = "https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-07-04/2024-07-04-raspios-bookworm-arm64-lite.img.xz"
iso_checksum = "sha256:43d150e7901583919e4eb1f0fa83fe0363af2d1e9777a5bb707d696d535e2599"
output_filename = "../../../pwnagotchi-64bit.img"
qemu_binary = "qemu-aarch64-static"
target_image_size = 19969908736
image_mounts = ["/boot/firmware","/"]
}
source "arm-image" "rpi32-pwnagotchi" {
image_type = "raspberrypi"
iso_url = "https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz"
iso_checksum = "sha256:df9c192d66d35e1ce67acde33a5b5f2b81ff02d2b986ea52f1f6ea211d646a1b"
output_filename = "../../../pwnagotchi-32bit.img"
qemu_binary = "qemu-arm-static"
qemu_args = ["-cpu", "arm1176"]
image_arch = "arm"
target_image_size = 19969908736
image_mounts = ["/boot/firmware","/"]
}
# a build block invokes sources and runs provisioning steps on them. The
# documentation for build blocks can be found here:
# https://www.packer.io/docs/from-1.5/blocks/build
build {
name = "Raspberry Pi 64 Pwnagotchi"
sources = ["source.arm-image.rpi64-pwnagotchi"]
provisioner "file" {
destination = "/usr/bin/"
sources = [
"data/64bit/usr/bin/bettercap-launcher",
"data/64bit/usr/bin/hdmioff",
"data/64bit/usr/bin/hdmion",
"data/64bit/usr/bin/monstart",
"data/64bit/usr/bin/monstop",
"data/64bit/usr/bin/pwnagotchi-launcher",
"data/64bit/usr/bin/pwnlib",
]
}
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
provisioner "shell" {
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
}
provisioner "file" {
destination = "/usr/local/src/pwnagotchi/"
source = "../"
}
provisioner "file" {
destination = "/etc/systemd/system/"
sources = [
"data/64bit/etc/systemd/system/bettercap.service",
"data/64bit/etc/systemd/system/pwnagotchi.service",
"data/64bit/etc/systemd/system/pwngrid-peer.service",
]
}
provisioner "file" {
destination = "/etc/update-motd.d/01-motd"
source = "data/64bit/etc/update-motd.d/01-motd"
}
provisioner "shell" {
inline = ["chmod +x /etc/update-motd.d/*"]
}
provisioner "shell" {
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
}
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
playbook_file = "raspberrypi64.yml"
}
}
build {
name = "Raspberry Pi 32 Pwnagotchi"
sources = ["source.arm-image.rpi32-pwnagotchi"]
provisioner "file" {
destination = "/usr/bin/"
sources = [
"data/32bit/usr/bin/bettercap-launcher",
"data/32bit/usr/bin/hdmioff",
"data/32bit/usr/bin/hdmion",
"data/32bit/usr/bin/monstart",
"data/32bit/usr/bin/monstop",
"data/32bit/usr/bin/pwnagotchi-launcher",
"data/32bit/usr/bin/pwnlib",
]
}
provisioner "shell" {
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
}
provisioner "file" {
destination = "/usr/local/src/pwnagotchi/"
source = "../"
}
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
provisioner "file" {
destination = "/etc/systemd/system/"
sources = [
"data/32bit/etc/systemd/system/bettercap.service",
"data/32bit/etc/systemd/system/pwnagotchi.service",
"data/32bit/etc/systemd/system/pwngrid-peer.service",
]
}
provisioner "file" {
destination = "/etc/update-motd.d/01-motd"
source = "data/32bit/etc/update-motd.d/01-motd"
}
provisioner "shell" {
inline = ["chmod +x /etc/update-motd.d/*"]
}
provisioner "shell" {
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
}
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
playbook_file = "raspberrypi32.yml"
}
}

View File

@ -2,7 +2,7 @@ _show_complete()
{
local cur opts node_names all_options opt_line
all_options="
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --check-update --donate {plugins,google}
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --wizard --check-update --donate {plugins,google}
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
pwnagotchi plugins list -i --installed -h --help
pwnagotchi plugins install -h --help

View File

@ -1,15 +0,0 @@
# Specifies amount of zram devices to create.
# By default, zramswap-start will use all available cores.
#CORES=1
# Specifies the amount of RAM that should be used for zram
# based on a percentage the total amount of available memory
PERCENTAGE=60
# Specifies a static amount of RAM that should be used for
# the ZRAM devices, this is in MiB
#ALLOCATION=256
# Specifies the priority for the swap devices, see swapon(2)
# for more details.
#PRIORITY=100

View File

@ -0,0 +1,26 @@
# /etc/dphys-swapfile - user settings for dphys-swapfile package
# author Neil Franklin, last modification 2010.05.05
# copyright ETH Zuerich Physics Departement
# use under either modified/non-advertising BSD or GPL license
# this file is sourced with . so full normal sh syntax applies
# the default settings are added as commented out CONF_*=* lines
# where we want the swapfile to be, this is the default
#CONF_SWAPFILE=/var/swap
# set size to absolute value, leaving empty (default) then uses computed value
# you most likely don't want this, unless you have an special disk situation
CONF_SWAPSIZE=2048
# set size to computed value, this times RAM size, dynamically adapts,
# guarantees that there is enough swap without wasting disk space on excess
#CONF_SWAPFACTOR=2
# restrict size (computed and absolute!) to maximally this limit
# can be set to empty for no limit, but beware of filled partitions!
# this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it
# but is also sensible on 64bit to prevent filling /var or even / partition
#CONF_MAXSWAP=2048

View File

@ -0,0 +1,6 @@
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.
i2c-dev

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

16
builder/data/etc/rc.local Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
timedatectl set-ntp true
exit 0

View File

@ -0,0 +1,20 @@
[Unit]
Description=Bluetooth service
Documentation=man:bluetoothd(8)
ConditionPathIsDirectory=/sys/class/bluetooth
[Service]
Type=dbus
BusName=org.bluez
ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp
NotifyAccess=main
#WatchdogSec=10
#Restart=on-failure
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
LimitNPROC=1
ProtectHome=true
ProtectSystem=full
[Install]
WantedBy=bluetooth.target
Alias=dbus-org.bluez.service

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

View File

@ -8,7 +8,7 @@ After=bettercap.service
Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1
Environment=LD_LIBRARY_PATH=/usr/local/lib
Type=simple
ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /home/pi/logs/pwngrid-peer.log -iface wlan0mon
ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /etc/pwnagotchi/log/pwngrid-peer.log -iface wlan0mon
Restart=always
RestartSec=30

View File

@ -2,7 +2,7 @@
_hostname=$(hostname)
_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!"
@ -20,6 +20,7 @@ echo " I'm managed by systemd. Here are some basic commands."
echo
echo " If you want to know what I'm doing, you can check my logs with the command"
echo " - pwnlog"
echo " - sudo pwnagotchi --wizard, to help set up a config.toml"
echo " - sudo pwnagotchi --version, to check the current version"
echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
echo " - sudo pwnagotchi --check-update, to see if there is a new version available"
@ -30,4 +31,4 @@ echo
echo " You can restart me using"
echo " pwnkill"
echo
echo " You can learn more about me at https://pwnagotchi.ai/"
echo " You can learn more about me at https://pwnagotchi.org/"

View File

@ -9,16 +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
if is_auto_mode_no_delete; then

View File

@ -10,7 +10,8 @@ if is_crypted_mode; then
fi
if is_auto_mode; then
/usr/local/bin/pwnagotchi
/usr/local/src/pwnagotchi/env/bin/pwnagotchi
systemctl restart bettercap
else
/usr/local/bin/pwnagotchi --manual
/usr/local/src/pwnagotchi/env/bin/pwnagotchi --manual
fi

View File

@ -1,26 +1,5 @@
#!/usr/bin/env bash
# well ... it blinks the led
blink_led() {
# shellcheck disable=SC2034
for i in $(seq 1 "$1"); do
echo 0 >/sys/class/leds/led0/brightness
sleep 0.3
echo 1 >/sys/class/leds/led0/brightness
sleep 0.3
done
echo 0 >/sys/class/leds/led0/brightness
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

View File

@ -1,101 +0,0 @@
# 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"
#}
ansible = {
source = "github.com/hashicorp/ansible"
version = "~> 1"
}
}
}
variable "pwn_hostname" {
type = string
}
variable "pwn_version" {
type = string
}
source "arm" "rpi64-pwnagotchi" {
file_checksum_url = "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz"]
file_checksum_type = "sha256"
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
image_path = "../../../pwnagotchi-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"
image_build_method = "resize"
image_size = "9G"
image_type = "dos"
image_partitions {
name = "boot"
type = "c"
start_sector = "8192"
filesystem = "fat"
size = "256M"
mountpoint = "/boot/firmware"
}
image_partitions {
name = "root"
type = "83"
start_sector = "532480"
filesystem = "ext4"
size = "0"
mountpoint = "/"
}
}
# a build block invokes sources and runs provisioning steps on them. The
# documentation for build blocks can be found here:
# https://www.packer.io/docs/from-1.5/blocks/build
build {
name = "Raspberry Pi 64 Pwnagotchi"
sources = ["source.arm.rpi64-pwnagotchi"]
provisioner "file" {
destination = "/usr/bin/"
sources = [
"data/usr/bin/bettercap-launcher",
"data/usr/bin/hdmioff",
"data/usr/bin/hdmion",
"data/usr/bin/monstart",
"data/usr/bin/monstop",
"data/usr/bin/pwnagotchi-launcher",
"data/usr/bin/pwnlib",
]
}
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
provisioner "file" {
destination = "/etc/systemd/system/"
sources = [
"data/etc/systemd/system/bettercap.service",
"data/etc/systemd/system/pwnagotchi.service",
"data/etc/systemd/system/pwngrid-peer.service",
]
}
provisioner "file" {
destination = "/etc/update-motd.d/01-motd"
source = "data/etc/update-motd.d/01-motd"
}
provisioner "shell" {
inline = ["chmod +x /etc/update-motd.d/*"]
}
provisioner "shell" {
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
}
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
playbook_file = "raspberrypi64.yml"
}
}

View File

@ -0,0 +1,82 @@
packer {
required_plugins {
arm-image = {
source = "github.com/solo-io/arm-image"
version = ">= 0.0.1"
}
ansible = {
source = "github.com/hashicorp/ansible"
version = ">= 1.1.1"
}
}
}
variable "pwn_hostname" {
type = string
}
variable "pwn_version" {
type = string
}
source "arm-image" "rpi32-pwnagotchi" {
image_type = "raspberrypi"
iso_url = "https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz"
iso_checksum = "sha256:df9c192d66d35e1ce67acde33a5b5f2b81ff02d2b986ea52f1f6ea211d646a1b"
output_filename = "../../../pwnagotchi-32bit.img"
qemu_binary = "qemu-arm-static"
qemu_args = ["-cpu", "arm1176"]
image_arch = "arm"
image_mounts = ["/boot/firmware","/"]
target_image_size = 19969908736
}
build {
name = "Raspberry Pi 32 Pwnagotchi"
sources = ["source.arm-image.rpi32-pwnagotchi"]
provisioner "file" {
destination = "/usr/bin/"
sources = [
"data/32bit/usr/bin/bettercap-launcher",
"data/32bit/usr/bin/hdmioff",
"data/32bit/usr/bin/hdmion",
"data/32bit/usr/bin/monstart",
"data/32bit/usr/bin/monstop",
"data/32bit/usr/bin/pwnagotchi-launcher",
"data/32bit/usr/bin/pwnlib",
]
}
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
provisioner "shell" {
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
}
provisioner "file" {
destination = "/usr/local/src/pwnagotchi/"
source = "../"
}
provisioner "file" {
destination = "/etc/systemd/system/"
sources = [
"data/32bit/etc/systemd/system/bettercap.service",
"data/32bit/etc/systemd/system/pwnagotchi.service",
"data/32bit/etc/systemd/system/pwngrid-peer.service",
]
}
provisioner "file" {
destination = "/etc/update-motd.d/01-motd"
source = "data/32bit/etc/update-motd.d/01-motd"
}
provisioner "shell" {
inline = ["chmod +x /etc/update-motd.d/*"]
}
provisioner "shell" {
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
}
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
playbook_file = "raspberrypi32.yml"
}
}

560
builder/raspberrypi32.yml Normal file
View File

@ -0,0 +1,560 @@
---
- hosts:
- 127.0.0.1
gather_facts: true
become: true
vars:
kernel:
min: "6.6"
full: "6.6.31+rpt-rpi-v6"
pwnagotchi:
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
services:
enable:
- bettercap.service
- fstrim.timer
- pwnagotchi.service
- pwngrid-peer.service
disable:
- apt-daily-upgrade.service
- apt-daily-upgrade.timer
- apt-daily.service
- apt-daily.timer
- bluetooth.service
- ifup@wlan0.service
packages:
caplets:
source: "https://github.com/jayofelony/caplets.git"
branch: "lite" # or master
bettercap:
source: "https://github.com/jayofelony/bettercap.git"
branch: "lite" # or master
pwngrid:
source: "https://github.com/jayofelony/pwngrid.git"
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.7/pwngrid-1.10.7-armhf.zip"
torch:
wheel: "torch-2.1.0a0+gita8e7c98-cp311-cp311-linux_armv6l.whl"
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/py0torch-bookworm-2024-05/torch-2.1.0a0+gita8e7c98-cp311-cp311-linux_armv6l.whl"
torchvision:
wheel: "torchvision-0.16.0+fbb4cc5-cp311-cp311-linux_armv6l.whl"
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/py0torch-bookworm-2024-05/torchvision-0.16.0+fbb4cc5-cp311-cp311-linux_armv6l.whl"
apt:
downgrade:
- libpcap-dev_1.9.1-4_armhf.deb
- libpcap0.8-dbg_1.9.1-4_armhf.deb
- libpcap0.8-dev_1.9.1-4_armhf.deb
- libpcap0.8_1.9.1-4_armhf.deb
hold:
- firmware-atheros
- firmware-brcm80211
- firmware-libertas
- firmware-misc-nonfree
- firmware-realtek
- libpcap-dev
- libpcap0.8
- libpcap0.8-dbg
- libpcap0.8-dev
remove:
- nfs-common
- triggerhappy
install:
- aircrack-ng
- autoconf
- bison
- bluez
- bluez-tools
- build-essential
- curl
- dphys-swapfile
- fbi
- firmware-atheros
- firmware-brcm80211
- firmware-libertas
- firmware-misc-nonfree
- firmware-realtek
- flex
- g++
- gawk
- gcc-arm-none-eabi
- git
- libatlas-base-dev
- libc6-dev
- libcpuinfo-dev
- libcurl-ocaml-dev
- libdbus-1-dev
- libdbus-glib-1-dev
- libfl-dev
- libgmp3-dev
- libnetfilter-queue-dev
- libopenblas-dev # https://stackoverflow.com/questions/14570011/explain-why-numpy-should-not-be-imported-from-source-directory
- libopenjp2-7
- libpcap-dev
#- libraspberrypi-bin ## seems to be provided by raspi-utils now
- libraspberrypi-dev
- libraspberrypi-doc
- libraspberrypi0
- libsleef-dev
- libssl-dev
- libssl-ocaml-dev
- libtool
- libusb-1.0-0-dev
- make
- ntp
- pkg-config
- python3-dev
- python3-pip
- python3-protobuf
- python3-setuptools
- python3-smbus
- qpdf
- raspberrypi-kernel-headers
- rsync
- tcpdump
- texinfo
- unzip
- wget
- wl
- xxd
- zlib1g-dev
environment:
ARCHFLAGS: "-arch armv6l"
tasks:
# First we install packages
- name: install packages
apt:
name: "{{ packages.apt.install }}"
state: latest
update_cache: yes
install_recommends: no
- name: update pip3, setuptools, wheel
shell: "python3 -m pip install --upgrade pip setuptools wheel --break-system-packages"
args:
executable: /bin/bash
chdir: /usr/local/src
- name: install 32bit torch
shell: "python3 -m pip install {{ packages.torch.url }} {{ packages.torchvision.url }} --break-system-packages"
args:
executable: /bin/bash
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch armv6l"
# Now we set up /boot/firmware
- name: Create pi user
copy:
dest: /boot/firmware/userconf
content: |
pi:$5$733Efsksay$SEFUKemv8FaNAu6X4GUfxdSzSDh6PbpOcdtNe5b7Nt0
- name: enable ssh on boot
file:
path: /boot/firmware/ssh
state: touch
- name: remove current rc.local
file:
path: /etc/rc.local
state: absent
- 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: setup /boot/firmware/config.txt
blockinfile:
path: /boot/firmware/config.txt
insertafter: EOF
block: |
dtparam=i2c1=on
dtparam=i2c_arm=on
dtparam=spi=on
gpu_mem=1
dtoverlay=dwc2
#dtoverlay=disable-wifi
enable_uart=1
[pi0]
dtoverlay=spi0-2cs
#dtoverlay=disable-wifi
- name: change hostname
lineinfile:
dest: /etc/hostname
regexp: '^raspberrypi'
line: "{{pwnagotchi.hostname}}"
state: present
when: lookup('file', '/etc/hostname') == "raspberrypi"
register: hostname
- name: add hostname to /etc/hosts
lineinfile:
dest: /etc/hosts
regexp: '^127\.0\.1\.1[ \t]+raspberrypi'
line: "127.0.1.1\t{{pwnagotchi.hostname}}"
state: present
when: hostname.changed
# 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
regexp: '^ExecStart=/usr/libexec/bluetooth/bluetoothd$'
line: 'ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp'
state: present
###########################################
#
# libpcap v1.9 - build from source
#
###########################################
# check for presence, then it can re-run in later parts if needed
# use the "make" built in
# install libpcap before bettercap and pwngrid, so they use it
- name: clone libpcap v1.9 from github
git:
repo: 'https://github.com/the-tcpdump-group/libpcap.git'
dest: /usr/local/src/libpcap
version: libpcap-1.9
- name: build and install libpcap into /usr/local/lib
shell: "./configure && make && make install"
args:
executable: /bin/bash
chdir: /usr/local/src/libpcap
- name: remove libpcap build folder
file:
state: absent
path: /usr/local/src/libpcap
- name: create symlink /usr/local/lib/libpcap.so.1.9.1
file:
src: /usr/local/lib/libpcap.so.1.9.1
dest: /usr/local/lib/libpcap.so.0.8
state: link
# install 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
# Installing nexmon
- name: clone nexmon repository
git:
repo: https://github.com/DrSchottky/nexmon.git
dest: /usr/local/src/nexmon
- name: make firmware
shell: "source ./setup_env.sh && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch armv6l"
- name: make firmware patch (bcm43430a1)
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/ && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch armv6l"
- name: install new firmware (bcm43430a1)
copy:
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
follow: true
- 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"
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch armv6l"
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
copy:
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
dest: /usr/lib/firmware/brcm/brcmfmac43436s-sdio.bin
follow: true
# delete blob files that make nexmon sad
- name: Delete the firmware blob files to avoid some nexmon crashing
file:
state: absent
path: '{{ item }}'
loop:
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.clm_blob
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob
- 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: 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
file:
state: absent
path: /usr/local/src/nexmon/
- name: Create custom config directory
file:
path: /etc/pwnagotchi/conf.d/
state: directory
#- name: clone pwnagotchi repository
# git:
# repo: https://github.com/jayofelony/pwnagotchi.git
# dest: /usr/local/src/pwnagotchi
- name: build pwnagotchi wheel
command: "pip3 install . --no-cache-dir --break-system-packages"
args:
chdir: /usr/local/src/pwnagotchi
- name: create /usr/local/share/pwnagotchi/ folder
file:
path: /usr/local/share/pwnagotchi/
state: directory
- name: Create custom plugin directory
file:
path: /usr/local/share/pwnagotchi/custom-plugins/
state: directory
- name: remove pwnagotchi folder
file:
state: absent
path: /usr/local/src/pwnagotchi
##########################################
#
# pwngrid, bettercap
#
##########################################
- name: Install go-1.21
unarchive:
src: https://go.dev/dl/go1.22.3.linux-armv6l.tar.gz
dest: /usr/local
remote_src: yes
register: golang
- name: Update .bashrc for go-1.21
blockinfile:
dest: /etc/profile
state: present
block: |
export GOPATH=$HOME/go
export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin
when: golang.changed
- name: download pwngrid
git:
repo: "{{ packages.pwngrid.source }}"
dest: /usr/local/src/pwngrid
- name: install pwngrid
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
args:
executable: /bin/bash
chdir: /usr/local/src/pwngrid
- name: remove pwngrid folder
file:
state: absent
path: /usr/local/src/pwngrid
- name: download bettercap
git:
repo: "{{ packages.bettercap.source }}"
version: "{{ packages.bettercap.branch }} "
dest: /usr/local/src/bettercap
- 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
chdir: /usr/local/src/bettercap
- name: remove bettercap folder
file:
state: absent
path: /usr/local/src/bettercap
- name: clone bettercap caplets
git:
repo: "{{ packages.caplets.source }}"
version: "{{ packages.caplets.branch }}"
dest: /tmp/caplets
register: capletsgit
- name: install bettercap caplets
make:
chdir: /tmp/caplets
target: install
when: capletsgit.changed
- name: create /etc/pwnagotchi folder
file:
path: /etc/pwnagotchi
state: directory
- name: check if user configuration exists
stat:
path: /etc/pwnagotchi/config.toml
register: user_config
- name: create /etc/pwnagotchi/config.toml
copy:
dest: /etc/pwnagotchi/config.toml
content: |
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
# Example:
# ui.display.enabled = true
# ui.display.type = "waveshare_4"
when: not user_config.stat.exists
- name: Delete motd
file:
state: absent
path: /etc/motd
- name: Delete motd 10-uname
file:
state: absent
path: /etc/update-motd.d/10-uname
- name: add firmware packages to hold
dpkg_selections:
name: "{{ item }}"
selection: hold
with_items: "{{ packages.apt.hold }}"
- name: disable unnecessary services
systemd:
name: "{{ item }}"
state: stopped
enabled: no
with_items: "{{ services.disable }}"
- name: enable services
systemd:
name: "{{ item }}"
enabled: true
state: stopped
with_items: "{{ services.enable }}"
register: enabled
- name: make /root readable, becauase that's where all the files are
file:
path: /root
mode: '755'
- name: fix permissions on /home/pi
file:
path: /home/pi
owner: pi
group: pi
recurse: true
- name: remove pre-collected packages zip
file:
path: /root/go_pkgs.tgz
state: absent
- name: remove /root/go folder
file:
state: absent
path: /root/go
- name: remove /usr/local/go folder
file:
state: absent
path: /usr/local/go
- name: remove pip cache
file:
state: absent
path: /root/.cache/pip
- name: remove ssh keys
file:
state: absent
path: "{{ item }}"
with_fileglob:
- "/etc/ssh/ssh_host*_key*"
- name: regenerate ssh keys
shell: "dpkg-reconfigure openssh-server"
args:
executable: /bin/bash
# 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: install rpi-sys-mods again?
apt:
state: present
name: raspberrypi-sys-mods
update_cache: yes
install_recommends: no
- name: clean apt cache
apt:
autoclean: true
when: removed.changed
handlers:
- name: reload systemd services
systemd:
daemon_reload: yes
when: enabled.changed

View File

@ -0,0 +1,84 @@
packer {
required_plugins {
arm-image = {
source = "github.com/solo-io/arm-image"
version = ">= 0.0.1"
}
ansible = {
source = "github.com/hashicorp/ansible"
version = ">= 1.1.1"
}
}
}
variable "pwn_hostname" {
type = string
}
variable "pwn_version" {
type = string
}
source "arm-image" "rpi64-pwnagotchi" {
image_type = "raspberrypi"
iso_url = "https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-07-04/2024-07-04-raspios-bookworm-arm64-lite.img.xz"
iso_checksum = "sha256:43d150e7901583919e4eb1f0fa83fe0363af2d1e9777a5bb707d696d535e2599"
output_filename = "../../../pwnagotchi-64bit.img"
qemu_binary = "qemu-aarch64-static"
target_image_size = 19969908736
image_mounts = ["/boot/firmware","/"]
}
# a build block invokes sources and runs provisioning steps on them. The
# documentation for build blocks can be found here:
# https://www.packer.io/docs/from-1.5/blocks/build
build {
name = "Raspberry Pi 64 Pwnagotchi"
sources = ["source.arm-image.rpi64-pwnagotchi"]
provisioner "file" {
destination = "/usr/bin/"
sources = [
"data/64bit/usr/bin/bettercap-launcher",
"data/64bit/usr/bin/hdmioff",
"data/64bit/usr/bin/hdmion",
"data/64bit/usr/bin/monstart",
"data/64bit/usr/bin/monstop",
"data/64bit/usr/bin/pwnagotchi-launcher",
"data/64bit/usr/bin/pwnlib",
]
}
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
provisioner "shell" {
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
}
provisioner "file" {
destination = "/usr/local/src/pwnagotchi/"
source = "../"
}
provisioner "file" {
destination = "/etc/systemd/system/"
sources = [
"data/64bit/etc/systemd/system/bettercap.service",
"data/64bit/etc/systemd/system/pwnagotchi.service",
"data/64bit/etc/systemd/system/pwngrid-peer.service",
]
}
provisioner "file" {
destination = "/etc/update-motd.d/01-motd"
source = "data/64bit/etc/update-motd.d/01-motd"
}
provisioner "shell" {
inline = ["chmod +x /etc/update-motd.d/*"]
}
provisioner "shell" {
inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"]
}
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
playbook_file = "raspberrypi64.yml"
}
}

View File

@ -5,29 +5,18 @@
become: true
vars:
kernel:
min: "6.1"
full: "6.1.0-rpi7-rpi-v8"
full_pi5: "6.1.0-rpi7-rpi-2712"
min: "6.6"
full: "6.6.31+rpt-rpi-v8"
full_pi5: "6.6.31+rpt-rpi-2712"
pwnagotchi:
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}"
system:
boot_options:
- "dtoverlay=dwc2"
- "dtoverlay=spi1-3cs"
- "dtparam=i2c1=on"
- "dtparam=i2c_arm=on"
- "dtparam=spi=on"
- "gpu_mem=16"
modules:
- "i2c-dev"
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
services:
enable:
- bettercap.service
- fstrim.timer
- pwnagotchi.service
- pwngrid-peer.service
- zramswap.service
disable:
- apt-daily-upgrade.service
- apt-daily-upgrade.timer
@ -38,10 +27,11 @@
packages:
caplets:
source: "https://github.com/jayofelony/caplets.git"
branch: "lite" # or master
bettercap:
source: "https://github.com/jayofelony/bettercap.git"
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.2/bettercap-2.32.2.zip"
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.4/bettercap-2.32.4.zip"
branch: "lite" # or master
pwngrid:
source: "https://github.com/jayofelony/pwngrid.git"
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.5/pwngrid-1.10.5-aarch64.zip"
@ -62,141 +52,107 @@
- libpcap0.8-dbg
- libpcap0.8-dev
remove:
- avahi-daemon
- dhpys-swapfile
- libcurl-ocaml-dev
- libssl-ocaml-dev
- nfs-common
- triggerhappy
- wpasupplicant
install:
- aircrack-ng
- autoconf
- bc
- bison
- bluez
- bluez-tools
- build-essential
- curl
- dkms
- dphys-swapfile
- fbi
- flex
- fonts-dejavu
- fonts-dejavu-core
- fonts-dejavu-extra
- fonts-freefont-ttf
- g++
- gawk
- gcc-arm-none-eabi
- git
- hcxtools
- libatlas-base-dev
- libavcodec59
- libavformat59
- libblas-dev
- libbluetooth-dev
- libbz2-dev
- libc-ares-dev
- libc6-dev
- libcap-dev
- libcurl-ocaml-dev
- libssl-ocaml-dev
- libdbus-1-dev
- libdbus-glib-1-dev
- libeigen3-dev
- libelf-dev
- libffi-dev
- libfl-dev
- libfuse-dev
- libgdbm-dev
- libgl1-mesa-glx
- libgmp3-dev
- libgstreamer1.0-0
- libhdf5-dev
- liblapack-dev
- libncursesw5-dev
- libnetfilter-queue-dev
- libopenblas-dev
- libopenjp2-7
- libopenmpi-dev
- libopenmpi3
- libpcap-dev
- libraspberrypi-bin
- libraspberrypi-dev
- libraspberrypi-doc
- libraspberrypi0
- libsqlite3-dev
- libssl-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-pycryptodome
- python3-requests
- python3-scapy
- python3-smbus2
- python3-spidev
- python3-tweepy
- python3-werkzeug
- firmware-atheros
- firmware-brcm80211
- firmware-libertas
- firmware-misc-nonfree
- firmware-realtek
- flex
- g++
- gawk
- gcc-arm-none-eabi
- git
- libc6-dev
- libcurl-ocaml-dev
- libdbus-1-dev
- libdbus-glib-1-dev
- libfl-dev
- libgmp3-dev
- libnetfilter-queue-dev
- libpcap-dev
#- libraspberrypi-bin ## seems to be provided by raspi-utils now
- libraspberrypi-dev
- libraspberrypi-doc
- libraspberrypi0
- libssl-dev
- libssl-ocaml-dev
- libtool
- libusb-1.0-0-dev
- make
- ntp
- pkg-config
- python3-dev
- python3-pip
- python3-setuptools
- python3-smbus
- qpdf
- raspberrypi-kernel-headers
- raspberrypi-sys-mods
- rsync
- screen
- tcpdump
- texinfo
- time
- tk-dev
- unzip
- vim
- wget
- wl
- xxd
- zlib1g-dev
- zram-tools
environment:
ARCHFLAGS: "-arch aarch64"
tasks:
# First we install packages
- name: install packages
apt:
name: "{{ packages.apt.install }}"
state: present
state: latest
update_cache: yes
install_recommends: false
install_recommends: no
- name: update pip3, setuptools, wheel
shell: "python3 -m pip install --upgrade pip setuptools wheel --break-system-packages"
args:
executable: /bin/bash
chdir: /usr/local/src
- name: build pwnagotchi wheel
command: "pip3 install . --no-cache-dir --break-system-packages"
args:
chdir: /usr/local/src/pwnagotchi
- name: remove pwnagotchi folder
file:
state: absent
path: /usr/local/src/pwnagotchi
# Now we set up /boot/firmware
- name: Create pi user
copy:
dest: /boot/firmware/userconf
content: |
pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
pi:$5$733Efsksay$SEFUKemv8FaNAu6X4GUfxdSzSDh6PbpOcdtNe5b7Nt0
- 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: remove current rc.local
file:
path: /etc/rc.local
state: absent
- name: change root partition
replace:
@ -214,6 +170,35 @@
regexp: '(.*)$'
line: '\1 modules-load=dwc2,g_ether'
- name: setup /boot/firmware/config.txt
blockinfile:
path: /boot/firmware/config.txt
insertafter: EOF
block: |
dtparam=i2c1=on
dtparam=i2c_arm=on
dtparam=spi=on
gpu_mem=1
dtoverlay=dwc2
#dtoverlay=disable-wifi
enable_uart=1
[pi02w]
dtoverlay=spi0-2cs
#dtoverlay=disable-wifi
[pi3]
dtoverlay=spi0-2cs
#dtoverlay=disable-wifi
[pi4]
dtoverlay=spi0-2cs
#dtoverlay=disable-wifi
[pi5]
dtoverlay=spi0-2cs
#dtoverlay=disable-wifi
- name: change hostname
lineinfile:
dest: /etc/hostname
@ -273,7 +258,6 @@
state: link
# install latest hcxtools
- name: clone hcxtools
git:
repo: https://github.com/ZerBea/hcxtools.git
@ -290,14 +274,13 @@
state: absent
path: /usr/local/src/hcxtools
# Install nexmon to fix wireless scanning (takes 2.5G of space)
# Installing nexmon
- name: clone nexmon repository
git:
repo: https://github.com/DrSchottky/nexmon.git
dest: /usr/local/src/nexmon
# FIRST WE BUILD DRIVER FOR RPi5
- name: make firmware, RPi5
shell: "source ./setup_env.sh && make"
args:
@ -324,11 +307,6 @@
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"
@ -343,7 +321,6 @@
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
@ -374,7 +351,6 @@
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:
@ -399,6 +375,12 @@
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch aarch64"
- name: install new firmware (bcm43430a1)
copy:
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
follow: true
- name: copy modified driver, RPi4
copy:
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
@ -407,12 +389,6 @@
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch aarch64"
- name: install new firmware (bcm43430a1)
copy:
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
follow: true
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
copy:
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
@ -432,6 +408,7 @@
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.clm_blob
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
- /usr/lib/firmware/brcm/brcmfmac43455-sdio.clm_blob
- /usr/lib/firmware/brcm/BCM43430A1.raspberrypi,model-zero-2-w.hcd
- 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"
@ -447,46 +424,31 @@
state: absent
path: /usr/local/src/nexmon/
- name: Create custom plugin directory
file:
path: /usr/local/share/pwnagotchi/custom-plugins/
state: directory
- name: Create custom config directory
file:
path: /etc/pwnagotchi/conf.d/
state: directory
- name: clone pwnagotchi repository
git:
repo: https://github.com/jayofelony/pwnagotchi-bookworm.git
dest: /usr/local/src/pwnagotchi
- name: build pwnagotchi wheel
command: "pip3 install . --no-cache-dir --break-system-packages"
args:
chdir: /usr/local/src/pwnagotchi
- name: remove pwnagotchi folder
file:
state: absent
path: /usr/local/src/pwnagotchi
- name: create /usr/local/share/pwnagotchi/ folder
file:
path: /usr/local/share/pwnagotchi/
state: directory
- name: Create custom plugin directory
file:
path: /usr/local/share/pwnagotchi/custom-plugins/
state: directory
- name: Install go-1.21
unarchive:
src: https://go.dev/dl/go1.21.5.linux-arm64.tar.gz
src: https://go.dev/dl/go1.22.3.linux-arm64.tar.gz
dest: /usr/local
remote_src: yes
register: golang
- name: Update .bashrc for go-1.21
blockinfile:
dest: /home/pi/.bashrc
dest: /etc/profile
state: present
block: |
export GOPATH=$HOME/go
@ -512,9 +474,10 @@
- name: download bettercap
git:
repo: "{{ packages.bettercap.source }}"
version: "{{ packages.bettercap.branch }}"
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
@ -538,6 +501,7 @@
- name: clone bettercap caplets
git:
repo: "{{ packages.caplets.source }}"
version: "{{ packages.caplets.branch }}"
dest: /tmp/caplets
register: capletsgit
@ -547,31 +511,11 @@
target: install
when: capletsgit.changed
- name: download and install bettercap ui
unarchive:
src: "{{ packages.bettercap.ui }}"
dest: /usr/local/share/bettercap/
remote_src: yes
mode: 0755
# to always have the bettercap webui available (because why not?)
- name: copy pwnagotchi-manual over pwnagotchi-auto caplet
ansible.builtin.copy:
src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap
dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap
force: true
ignore_errors: true
- name: create /etc/pwnagotchi folder
file:
path: /etc/pwnagotchi
state: directory
- name: create log folder
file:
path: /home/pi/logs
state: directory
- name: check if user configuration exists
stat:
path: /etc/pwnagotchi/config.toml
@ -584,7 +528,7 @@
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
# Example:
# ui.display.enabled = true
# ui.display.type = "waveshare_2"
# ui.display.type = "waveshare_4"
when: not user_config.stat.exists
- name: Delete motd
@ -597,24 +541,6 @@
state: absent
path: /etc/update-motd.d/10-uname
- name: Add pwnlog alias
lineinfile:
dest: /home/pi/.bashrc
line: "\nalias pwnlog='tail -f -n300 /home/pi/logs/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'"
insertafter: EOF
- name: Add pwnver alias
lineinfile:
dest: /home/pi/.bashrc
line: "\nalias pwnver='python3 -c \"import pwnagotchi as p; print(p.__version__)\"'"
insertafter: EOF
- name: Add pwnkill alias to restart pwnagotchi with a signal
lineinfile:
dest: /home/pi/.bashrc
line: "\nalias pwnkill='sudo killall -USR1 pwnagotchi'"
insertafter: EOF
- name: add firmware packages to hold
dpkg_selections:
name: "{{ item }}"
@ -702,4 +628,4 @@
- name: reload systemd services
systemd:
daemon_reload: yes
when: enabled.changed
when: enabled.changed

View File

@ -1 +1 @@
__version__ = '2.8.0'
__version__ = '2.9.2'

View File

@ -4,7 +4,9 @@ import os
import re
import logging
import asyncio
import _thread
#import _thread
import threading
import subprocess
import pwnagotchi
import pwnagotchi.utils as utils
@ -21,11 +23,12 @@ 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)
@ -253,7 +256,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
txt = '%d (%d)' % (len(self._handshakes), tot)
if self._last_pwnd is not None:
txt += ' [%s]' % self._last_pwnd[:11] # So it doesn't overlap with fix_brcmfmac_plugin
txt += ' [%s]' % self._last_pwnd
self._view.set('shakes', txt)
@ -268,9 +271,9 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
self._save_recovery_data()
pwnagotchi.reboot()
def _restart(self):
def _restart(self, mode='AUTO'):
self._save_recovery_data()
pwnagotchi.restart("AUTO")
pwnagotchi.restart(mode)
def _save_recovery_data(self):
logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE)
@ -303,7 +306,8 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
raise
def start_session_fetcher(self):
_thread.start_new_thread(self._fetch_stats, ())
#_thread.start_new_thread(self._fetch_stats, ())
threading.Thread(target=self._fetch_stats, args=(), name="Session Fetcher", daemon=True).start()
def _fetch_stats(self):
while True:
@ -362,7 +366,8 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
plugins.on('handshake', self, filename, ap_mac, sta_mac)
else:
(ap, sta) = ap_and_station
self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap['hostname'] != '<hidden>' else ap_mac
self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap[
'hostname'] != '<hidden>' else ap_mac
logging.warning(
"!!! captured new handshake on channel %d, %d dBm: %s (%s) -> %s [%s (%s)] !!!",
ap['channel'], ap['rssi'], sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor'])
@ -385,7 +390,8 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
def start_event_polling(self):
# start a thread and pass in the mainloop
_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
#_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
threading.Thread(target=self._event_poller, args=(asyncio.get_event_loop(),), name="Event Polling", daemon=True).start()
def is_module_running(self, module):
s = self.session()

View File

@ -23,6 +23,12 @@ def load(config, agent, epoch, from_disk=True):
from stable_baselines3 import A2C
logging.debug("[AI] A2C imported in %.2fs" % (time.time() - start))
# remove invalid ai.parameters leftover from tensor_flow, if present
for key in [ 'alpha', 'epsilon', 'lr_schedule' ]:
if key in config['params']:
logging.info("Removing legacy ai parameter %s" % key);
del config['params'][key]
start = time.time()
from stable_baselines3.a2c import MlpPolicy
logging.debug("[AI] MlpPolicy imported in %.2fs" % (time.time() - start))

View File

@ -1,6 +1,6 @@
import logging
import gym
from gym import spaces
import gymnasium as gym
from gymnasium import spaces
import numpy as np
import pwnagotchi.ai.featurizer as featurizer
@ -51,7 +51,7 @@ class Environment(gym.Env):
'state_v': None
}
self.action_space = spaces.MultiDiscrete([p.space_size() for p in Environment.params if p.trainable])
self.action_space = spaces.multi_discrete.MultiDiscrete([p.space_size() for p in Environment.params if p.trainable])
self.observation_space = spaces.Box(low=0, high=1, shape=featurizer.shape, dtype=np.float32)
self.reward_range = reward.range

View File

@ -1,4 +1,4 @@
from gym import spaces
from gymnasium import spaces
class Parameter(object):

View File

@ -1,4 +1,4 @@
import _thread
# import _thread
import threading
import time
import random
@ -111,7 +111,8 @@ class AsyncTrainer(object):
return self._training_epochs
def start_ai(self):
_thread.start_new_thread(self._ai_worker, ())
#_thread.start_new_thread(self._ai_worker, ())
threading.Thread(target=self._ai_worker, args=(), name="AI Worker", daemon=True).start()
def _save_ai(self):
logging.info("[AI] saving model to %s ..." % self._nn_path)

View File

@ -100,7 +100,6 @@ class Client(object):
await asyncio.sleep(sleep_time)
continue
except OSError:
sleep_time = min_sleep + max_sleep * random.random()
logging.warning('connection to the bettercap endpoint failed...')
pwnagotchi.restart("AUTO")

View File

@ -18,10 +18,6 @@ main.custom_plugin_repos = [
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/"
main.plugins.aircrackonly.enabled = true
main.plugins.hashie.enabled = true
main.plugins.auto-update.enabled = true
main.plugins.auto-update.install = true
main.plugins.auto-update.interval = 1
@ -36,7 +32,7 @@ main.plugins.bt-tether.devices.android-phone.netmask = 24
main.plugins.bt-tether.devices.android-phone.interval = 1
main.plugins.bt-tether.devices.android-phone.scantime = 10
main.plugins.bt-tether.devices.android-phone.max_tries = 10
main.plugins.bt-tether.devices.android-phone.share_internet = false
main.plugins.bt-tether.devices.android-phone.share_internet = true
main.plugins.bt-tether.devices.android-phone.priority = 1
main.plugins.bt-tether.devices.ios-phone.enabled = false
@ -47,7 +43,7 @@ main.plugins.bt-tether.devices.ios-phone.netmask = 24
main.plugins.bt-tether.devices.ios-phone.interval = 5
main.plugins.bt-tether.devices.ios-phone.scantime = 20
main.plugins.bt-tether.devices.ios-phone.max_tries = 0
main.plugins.bt-tether.devices.ios-phone.share_internet = false
main.plugins.bt-tether.devices.ios-phone.share_internet = true
main.plugins.bt-tether.devices.ios-phone.priority = 999
main.plugins.fix_services.enabled = true
@ -81,9 +77,8 @@ main.plugins.onlinehashcrack.email = ""
main.plugins.onlinehashcrack.dashboard = ""
main.plugins.onlinehashcrack.single_files = false
main.plugins.pisugar2.enabled = false
main.plugins.pisugar2.shutdown = 5
main.plugins.pisugar2.sync_rtc_on_boot = false
main.plugins.pisugar3.enabled = false
main.plugins.pisugar3.shutdown = 5
main.plugins.session-stats.enabled = true
main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/"
@ -116,9 +111,8 @@ main.mon_stop_cmd = "/usr/bin/monstop"
main.mon_max_blind_epochs = 50
main.no_restart = false
main.filter = ""
main.log.path = "/home/pi/logs/pwnagotchi.log"
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
main.log.path-debug = "/etc/pwnagotchi/log/pwnagotchi-debug.log"
main.log.rotation.enabled = true
main.log.rotation.size = "10M"
@ -156,6 +150,8 @@ personality.bond_encounters_factor = 20000
personality.throttle_a = 0.4
personality.throttle_d = 0.9
ui.invert = false # false = black background, true = white background
ui.cursor = true
ui.fps = 0.0
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
ui.font.size_offset = 0 # will be added to the font size
@ -185,6 +181,9 @@ ui.faces.debug = "(#__#)"
ui.faces.upload = "(1__0)"
ui.faces.upload1 = "(1__1)"
ui.faces.upload2 = "(0__1)"
ui.faces.png = false
ui.faces.position_x = 0
ui.faces.position_y = 34
ui.web.enabled = true
ui.web.address = "::" # listening on both ipv4 and ipv6 - switch to 0.0.0.0 to listen on just ipv4
@ -198,11 +197,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",
@ -221,7 +215,7 @@ bettercap.silence = [
fs.memory.enabled = true
fs.memory.mounts.log.enabled = true
fs.memory.mounts.log.mount = "/home/pi/logs"
fs.memory.mounts.log.mount = "/etc/pwnagotchi/log/"
fs.memory.mounts.log.size = "50M"
fs.memory.mounts.log.sync = 60
fs.memory.mounts.log.zram = true

View File

@ -3,7 +3,8 @@ import re
import tempfile
import contextlib
import shutil
import _thread
#import _thread
import threading
import logging
from time import sleep
@ -85,7 +86,8 @@ def setup_mounts(config):
if interval:
logging.debug("[FS] Starting thread to sync %s (interval: %d)",
options['mount'], interval)
_thread.start_new_thread(m.daemonize, (interval,))
threading.Thread(target=m.daemonize, args=(interval,),name="File Sys", daemon=True).start()
#_thread.start_new_thread(m.daemonize, (interval,))
else:
logging.debug("[FS] Not syncing %s, because interval is 0",
options['mount'])

View File

@ -89,10 +89,10 @@ def update_data(last_session):
'uname': subprocess.getoutput("uname -a"),
'brain': brain,
'version': pwnagotchi.__version__,
'build': "Pwnagotchi-Torch by Jayofelony",
'build': "Pwnagotchi by Jayofelony",
'plugins': enabled,
'language': language,
'bettercap': subprocess.getoutput("bettercap -version").split(".\n\n")[1],
'bettercap': subprocess.getoutput("bettercap -version"),
'opwngrid': subprocess.getoutput("pwngrid -version")
}

View File

@ -36,18 +36,18 @@ msgid "The neural network is ready."
msgstr "La red neuronal está lista."
msgid "Generating keys, do not turn off ..."
msgstr ""
msgstr "Generando llaves, no me apagues ..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "¡Oye, el canal {channel} está libre! Tu AP lo agradecerá."
msgid "Reading last session logs ..."
msgstr ""
msgstr "Leyendo los logs de la última sesión ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr ""
msgstr "Leyendo {lines_so_far} líneas de log hasta ahora ..."
msgid "I'm bored ..."
msgstr "Estoy aburrido ..."
@ -74,7 +74,7 @@ msgid "Leave me alone ..."
msgstr "Me siento tan solo ..."
msgid "I'm mad at you!"
msgstr ""
msgstr "Toy re enojado con vos!"
msgid "I'm living the life!"
msgstr "¡Estoy viviendo la vida!"
@ -97,11 +97,11 @@ msgstr "¡Hola {name}! Encantado de conocerte."
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr ""
msgstr "Que onda {name}!?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr ""
msgstr "Eh!, ¿Que haces {name}?"
#, python-brace-format
msgid "Unit {name} is nearby!"
@ -127,10 +127,10 @@ msgid "Missed!"
msgstr "¡Perdido!"
msgid "Good friends are a blessing!"
msgstr ""
msgstr "Lxs buenxs amigxs son una masa!"
msgid "I love my friends!"
msgstr ""
msgstr "¡Amo a mis amigxs!"
msgid "Nobody wants to play with me ..."
msgstr "Nadie quiere jugar conmigo ..."
@ -143,7 +143,7 @@ msgstr "¡¿Dónde está todo el mundo?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Descansando durante {secs}s ..."
msgstr "Descansando por {secs}s ..."
msgid "Zzzzz"
msgstr "Zzzzz"
@ -203,7 +203,7 @@ msgstr "Oops, algo salió mal ... Reiniciando ..."
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr ""
msgstr "Subiendo data a {to} ..."
#, python-brace-format
msgid "Downloading from {name} ..."

View File

@ -248,7 +248,7 @@ msgid "minutes"
msgstr "minuten"
msgid "seconds"
msgstr "seconds"
msgstr "sekondes"
msgid "hour"
msgstr "oere"

View File

@ -1,16 +1,16 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# Icelandic Translation.
# Copyright (C) 2019
# This file is distributed under the same license as the pwnagotchi package..
# Sean Duggan <sean.duggan@pm.me>, 2024.
#
#, fuzzy
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-11-17 20:51+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Last-Translator: FULL NAME <sean.duggan@pm.me>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Icelandic\n"
"MIME-Version: 1.0\n"
@ -18,218 +18,219 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr ""
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr ""
msgstr "Hæ, Ég heiti Pwnagotchi! Ræsi.."
msgid "New day, new hunt, new pwns!"
msgstr ""
msgstr "Nýr dagur, Ný veiði, Ný pwns!"
msgid "Hack the Planet!"
msgstr ""
msgstr "Hakkaðu plánetuna!"
msgid "AI ready."
msgstr ""
msgstr "AI tilbúið."
msgid "The neural network is ready."
msgstr ""
msgstr "Tauganetið er tilbúið."
msgid "Generating keys, do not turn off ..."
msgstr ""
msgstr "Bý til lykla, ekki slökkva á."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr ""
msgstr "Hæ, Rás {channel} er líka ókeypis! AP þinn mun þakka fyrir."
msgid "Reading last session logs ..."
msgstr ""
msgstr "Les fyrri leiðarbók..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr ""
msgstr "Ég las {lines_so_far} leiðarbók línur hingað til..."
msgid "I'm bored ..."
msgstr ""
msgstr "Mér leiðist ..."
msgid "Let's go for a walk!"
msgstr ""
msgstr "Förum í göngutúr!"
msgid "This is the best day of my life!"
msgstr ""
msgstr "Þetta er besti dagur lífs míns!"
msgid "Shitty day :/"
msgstr ""
msgstr "Skítadagur :/"
msgid "I'm extremely bored ..."
msgstr ""
msgstr "Mér leiðist óskaplega mikið ..."
msgid "I'm very sad ..."
msgstr ""
msgstr "Ég er mjög leiður ..."
msgid "I'm sad"
msgstr ""
msgstr "Ég er leiður"
msgid "Leave me alone ..."
msgstr ""
msgstr "Láttu mig í friði ..."
msgid "I'm mad at you!"
msgstr ""
msgstr "Ég er reiður út í þig!"
msgid "I'm living the life!"
msgstr ""
msgstr "Ég lifi besta lífi!"
msgid "I pwn therefore I am."
msgstr ""
msgstr "Ég pwn þess vegna er ég."
msgid "So many networks!!!"
msgstr ""
msgstr "Svo mörg net!!!"
msgid "I'm having so much fun!"
msgstr ""
msgstr "Mér finnst svo gaman!"
msgid "My crime is that of curiosity ..."
msgstr ""
msgstr "Glæpur minn er forvitni ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr ""
msgstr "Hæ {name}! Gaman að hitta þig!"
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr ""
msgstr "Yo {name}! Sup?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr ""
msgstr "Hæ {name} Hvernig hefurðu það?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr ""
msgstr "Tæki {name} er nálægt!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr ""
msgstr "Uhm ... bless {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr ""
msgstr "{name} er farinn ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr ""
msgstr "Úps ... {name} er farinn."
#, python-brace-format
msgid "{name} missed!"
msgstr ""
msgstr "{name} saknað!'"
msgid "Missed!"
msgstr ""
msgstr "Saknað!'"
msgid "Good friends are a blessing!"
msgstr ""
msgstr "Góðir vinir eru blessun!"
msgid "I love my friends!"
msgstr ""
msgstr "Ég elska vini mína!"
msgid "Nobody wants to play with me ..."
msgstr ""
msgstr "Enginn vill leika við mig ..."
msgid "I feel so alone ..."
msgstr ""
msgstr "Mér finnst ég vera svo ein ..."
msgid "Where's everybody?!"
msgstr ""
msgstr "Hvar eru allir?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr ""
msgstr "Sef í {secs}s ..."
msgid "Zzzzz"
msgstr ""
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
msgstr "ZzzZzzz ({secs})"
msgid "Good night."
msgstr ""
msgstr "Góða nótt."
msgid "Zzz"
msgstr ""
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr ""
msgstr "Bíð eftir {secs} ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr ""
msgstr "Horfi í kringum mig ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr ""
msgstr "Hæ {what} við skulum vera vinir!"
#, python-brace-format
msgid "Associating to {what}"
msgstr ""
msgstr "Tengist við {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
msgstr "Yo {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr ""
msgstr "Ég ákvað að {mac} þurfi ekki WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr ""
msgstr "Afvotta {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr ""
msgstr "Sparkbanna {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr ""
msgstr "Flott! við fengum {num} ný handatök {plural}!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr ""
msgstr "þú hefur {count} ný skilaboð!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr ""
msgstr "Oops, eitthvað brotnaði ... Endurræsi ..."
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr ""
msgstr "Hleð upp gögnum til {to} ..."
#, python-brace-format
#, fuzzy, python-brace-format
msgid "Downloading from {name} ..."
msgstr ""
msgstr "Hleð upp gögnum til {to} ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr ""
msgstr "Sparkaði {num} stöðvar\n"
#, fuzzy
msgid "Made >999 new friends\n"
msgstr ""
msgstr "Eignaðist {num} nýja vini\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr ""
msgstr "Eignaðist {num} nýja vini\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr ""
msgstr "Fékk {num} ný handabönd\n"
msgid "Met 1 peer"
msgstr ""
msgstr "Hitti 1 jafningja"
#, python-brace-format
msgid "Met {num} peers"
msgstr ""
msgstr "Hitti {num} jafningja"
#, python-brace-format
msgid ""
@ -237,21 +238,24 @@ msgid ""
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"Ég hef verið að pwna í {duration} og sparkað {deauthed} viðskiptavinum! Ég "
"hef líka hitt {associated} nýja vini og borðað {handshakes} handabönd! "
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr ""
msgstr "klukkustundir"
msgid "minutes"
msgstr ""
msgstr "mínútur"
msgid "seconds"
msgstr ""
msgstr "sekúndur"
msgid "hour"
msgstr ""
msgstr "klukkustund"
msgid "minute"
msgstr ""
msgstr "mínútu"
msgid "second"
msgstr ""
msgstr "sekúndu"

View File

@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-16 21:51+0100\n"
"PO-Revision-Date: 2019-10-16 15:05+0200\n"
"Last-Translator: wytshadow <24534649+wytshadow@users.noreply.github.com>\n"
"PO-Revision-Date: 2024-08-23 20:40+0900\n"
"Last-Translator: mendoitarou_ <42207304+mendoitarou@users.noreply.github.com>\n"
"Language-Team: pwnagotchi <24534649+wytshadow@users.noreply.github.com>\n"
"Language: Japanese\n"
"MIME-Version: 1.0\n"
@ -243,10 +243,12 @@ msgstr ""
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr ""
"{to} にデータをアップロードしてるよ..."
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr ""
"{name} からダウンロードしてるよ..."
#, python-brace-format
msgid "Kicked {num} stations\n"

View File

@ -1,260 +1,260 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-17 15:46+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Foxy <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Portuguese (Brazil)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Olá, Eu sou Pwnagotchi! Iniciando ..."
msgid "New day, new hunt, new pwns!"
msgstr "Um novo dia, Uma nova caça e novos pwns!"
msgid "Hack the Planet!"
msgstr "Burle o Planeta!"
msgid "AI ready."
msgstr "IA pronta."
msgid "The neural network is ready."
msgstr "A rede neural está pronta."
msgid "Generating keys, do not turn off ..."
msgstr "Criando chaves, não desligue o sistema ..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Ei, canal {channel} está livre! Seu AP vai agradecer."
msgid "Reading last session logs ..."
msgstr "Lendo os logs da ultima sessão"
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr "Leia {lines_so_far} linha de logs até agora ..."
msgid "I'm bored ..."
msgstr "Eu estou entediado ..."
msgid "Let's go for a walk!"
msgstr "Vamos ir numa caminhada!"
msgid "This is the best day of my life!"
msgstr "Esse é o melhor dia da minha vida!"
msgid "Shitty day :/"
msgstr "Dia ruim :/"
msgid "I'm extremely bored ..."
msgstr "Eu estou extremamente entediado ..."
msgid "I'm very sad ..."
msgstr "Eu estou muito triste ..."
msgid "I'm sad"
msgstr "Eu estou triste"
msgid "Leave me alone ..."
msgstr "Me deixe em paz ..."
msgid "I'm mad at you!"
msgstr "Eu estou bravo com você!"
msgid "I'm living the life!"
msgstr "Eu estou vivendo a vida!"
msgid "I pwn therefore I am."
msgstr "Eu pwn então Eu sou."
msgid "So many networks!!!"
msgstr "Tantas redes!!!"
msgid "I'm having so much fun!"
msgstr "Eu estou tendo muita diversão"
msgid "My crime is that of curiosity ..."
msgstr "Meu crime é de curiosidade ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Olá {name}! É bom em conhecê-lo"
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr "Ei {name}! Como vai?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr "Ei {name} como você está indo?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr ""
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr ""
#, python-brace-format
msgid "{name} is gone ..."
msgstr ""
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr ""
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} errou!"
msgid "Missed!"
msgstr "Errei!"
msgid "Good friends are a blessing!"
msgstr "Bom amigos são uma bensão!"
msgid "I love my friends!"
msgstr "Eu amo meus amigos!"
msgid "Nobody wants to play with me ..."
msgstr "Ninguém quer brincar comigo ..."
msgid "I feel so alone ..."
msgstr "Estou me sentindo sozinho"
msgid "Where's everybody?!"
msgstr "Onde está todo mundo?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Tirando uma soneca por {secs}s ..."
msgid "Zzzzz"
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "ZzzZzzz ({secs}s)"
msgid "Good night."
msgstr "Boa noite."
msgid "Zzz"
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Esperando por {secs}s ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Olhando por volta ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Ei {what} vamos ser amigos!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Associando para {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Ei {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Apenas decidindo que {mac} não precisa de WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "Desautenticando {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Banindo {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Legal, conseguimos {num} novos handshake{plural}!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "Você tem {count} novas messagem{plural}!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Oops, algo deu errado ... Reiniciando ..."
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr "Enviando dados para {to} ..."
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr "Instalando para {name} ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Expulsei {num} estações\n"
msgid "Made >999 new friends\n"
msgstr "Fiz >999 novos amigos\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Fiz {num} novos amigos\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Peguei {num} handshakes\n"
msgid "Met 1 peer"
msgstr "Encontrei 1 pessoa"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Encontrei {num} pessoas"
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"Estou navegando há {duration} e expulsei {deauthed} clientes! Também conheci "
"{associamos} novos amigos e comi {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "horas"
msgid "minutes"
msgstr "minutos"
msgid "seconds"
msgstr "segundos"
msgid "hour"
msgstr "hora"
msgid "minute"
msgstr "minuto"
msgid "second"
# pwnagotchi Brazilian Portuguese translation file.
# Copyright (C) 2024
# This file is distributed under the same license as the pwnagotchi package.
# Fabiano F O <fabfernandes@hotmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-25 22:30+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Fabiano F O <fabfernandes@hotmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Brazilian Portuguese\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Olá, sou Pwnagotchi! Iniciando ..."
msgid "New day, new hunt, new pwns!"
msgstr "Novo dia, Nova caçada, Novos pwns!"
msgid "Hack the Planet!"
msgstr "Hackeie o Planeta!"
msgid "AI ready."
msgstr "IA pronta."
msgid "The neural network is ready."
msgstr "A rede neural está pronta."
msgid "Generating keys, do not turn off ..."
msgstr "Gerando chaves, não desligue ..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Ei, o canal {channel} está livre! Seu AP vai agradecer."
msgid "Reading last session logs ..."
msgstr "Lendo os logs da última sessão ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr "Li {lines_so_far} linhas de logs até agora ..."
msgid "I'm bored ..."
msgstr "Estou entediado ..."
msgid "Let's go for a walk!"
msgstr "Vamos dar um passeio!"
msgid "This is the best day of my life!"
msgstr "Este é o melhor dia da minha vida!"
msgid "Shitty day :/"
msgstr "Que dia ruim :/"
msgid "I'm extremely bored ..."
msgstr "Estou extremamente entediado ..."
msgid "I'm very sad ..."
msgstr "Estou muito triste ..."
msgid "I'm sad"
msgstr "Estou triste"
msgid "Leave me alone ..."
msgstr "Me deixe em paz ..."
msgid "I'm mad at you!"
msgstr "Estou bravo com você!"
msgid "I'm living the life!"
msgstr "Estou aproveitando a vida!"
msgid "I pwn therefore I am."
msgstr "Eu pwn, logo existo."
msgid "So many networks!!!"
msgstr "Uau! Quantas redes!!"
msgid "I'm having so much fun!"
msgstr "Estou me divertindo muito!"
msgid "My crime is that of curiosity ..."
msgstr "Meu crime é a curiosidade ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Olá {name}! Prazer em conhecê-lo."
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr "Ei {name}! Como vai?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr "Ei {name}, como você está?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr "A unidade {name} está próxima!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Hmm ... tchau {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} desapareceu ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Oops ... {name} desapareceu."
#, python-brace-format
msgid "{name} missed!"
msgstr "Perdi {name}!"
msgid "Missed!"
msgstr "Perdi!"
msgid "Good friends are a blessing!"
msgstr "Bons amigos são uma bênção!"
msgid "I love my friends!"
msgstr "Eu amo meus amigos!"
msgid "Nobody wants to play with me ..."
msgstr "Ninguém quer brincar comigo ..."
msgid "I feel so alone ..."
msgstr "Me sinto tão sozinho ..."
msgid "Where's everybody?!"
msgstr "Onde está todo mundo?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Tirando uma soneca por {secs}s ..."
msgid "Zzzzz"
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "ZzzZzzz ({secs}s)"
msgid "Good night."
msgstr "Boa noite."
msgid "Zzz"
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Aguardando {secs}s ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Olhando em volta ... ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Ei {what}, vamos ser amigos!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Associando a {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Olá {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Acabei de decidir que {mac} não precisa de WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "Desautenticando {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Banindo {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Legal, conseguimos {num} novo{plural} handshake{plural}!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "Você tem {count} nova{plural} messagem{plural}!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Oops, algo deu errado ... Reiniciando ..."
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr "Enviando dados para {to} ..."
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr "Baixando de {name} ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Expulsei {num} estações\n"
msgid "Made >999 new friends\n"
msgstr "Fiz >999 novos amigos\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Fiz {num} novos amigos\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Peguei {num} handshakes\n"
msgid "Met 1 peer"
msgstr "Conheci 1 peer"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Conheci {num} peers"
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"Estou pwning há {duration} e expulsei {deauthed} clientes! Também conheci "
"{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "horas"
msgid "minutes"
msgstr "minutos"
msgid "seconds"
msgstr "segundos"
msgid "hour"
msgstr "hora"
msgid "minute"
msgstr "minuto"
msgid "second"
msgstr "segundo"

Binary file not shown.

View File

@ -0,0 +1,260 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <dragan.miljkovic29@gmail.com>, 2024.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Srpski prevod v1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-16 15:26-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: RS <dragan.miljkovic29@gmail.com>\n"
"Language: Serbian\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Zdravo, ja sam Pwnagotchi! Započinjem ..."
msgid "New day, new hunt, new pwns!"
msgstr "Novi dan, novi lov, novi pnwovi!"
msgid "Hack the Planet!"
msgstr "Hakuj Planetu!"
msgid "AI ready."
msgstr "AI spreman."
msgid "The neural network is ready."
msgstr "Neuronska mreža je spremna."
msgid "Generating keys, do not turn off ..."
msgstr "Generišem ključeve, ne isključuj me..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Hej, kanal {channel} je slobodan! Tvoj AP će ti se zahvaliti."
msgid "Reading last session logs ..."
msgstr "Čitanje logova poslednje sesije ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr "Pročitao {lines_so_far} log linija do sada ..."
msgid "I'm bored ..."
msgstr "Dosadno mi je ..."
msgid "Let's go for a walk!"
msgstr "Hajmo u šetnju!"
msgid "This is the best day of my life!"
msgstr "Ovo je najbolji dan mog života!"
msgid "Shitty day :/"
msgstr "Sranje dan :/"
msgid "I'm extremely bored ..."
msgstr "Užasno mi je dosadno ..."
msgid "I'm very sad ..."
msgstr "Jako sam tužan ..."
msgid "I'm sad"
msgstr "Tužan sam"
msgid "Leave me alone ..."
msgstr "Ostavi me na miru ..."
msgid "I'm mad at you!"
msgstr "Ljut sam na tebe!"
msgid "I'm living the life!"
msgstr "Živim ga!"
msgid "I pwn therefore I am."
msgstr "Ja pwnujem dakle postojim."
msgid "So many networks!!!"
msgstr "Tako mnogo mreža!!!"
msgid "I'm having so much fun!"
msgstr "Previše se zabavljam!"
msgid "My crime is that of curiosity ..."
msgstr "Moj zločin je radoznalost ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Zdravo {name}! Drago mi je da te upoznam."
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr "Ej {name}! Šta ima?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr "Hej {name} kako si?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr "Jedinica {name} je blizu!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Umm ... doviđenja {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} je nestao ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Ups ... {name} je nestao."
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} promašen!"
msgid "Missed!"
msgstr "Promašen!"
msgid "Good friends are a blessing!"
msgstr "Dobri prijatelji su blagoslov!"
msgid "I love my friends!"
msgstr "Volim svoje prijatelje!"
msgid "Nobody wants to play with me ..."
msgstr "Niko ne želi da se igra sa mnom ..."
msgid "I feel so alone ..."
msgstr "Osećam se toliko usamljeno ..."
msgid "Where's everybody?!"
msgstr "Gde su svi?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Dremam {secs}s ..."
msgid "Zzzzz"
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "ZzzZzzz ({secs}s)"
msgid "Good night."
msgstr "Laku noć."
msgid "Zzz"
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Čekam {secs}s ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Gledam unaokolo ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Hej {what} hajde da budemo prijatelji!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Povezujem se sa {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Ej {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Upravo sam odlučio da {mac} ne zahteva WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "Deauthenticating {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Kickbanning {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Kul, imamo {num} novih handshakeova"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "Imas {count} novih poruka"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Ups, nešto je poslo po zlu ... Restartujem ..."
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr "Otpremam podatke na {to} ..."
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr "Preuzimam od {name} ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Kickovano {num} stanica\n"
msgid "Made >999 new friends\n"
msgstr "Stekao sam >999 novih prijatelja\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Stekao sam {num} novih prijatelja\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Imam {num} handshakeova\n"
msgid "Met 1 peer"
msgstr "Sreo 1 peera"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Sreo {num} peerova"
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"Pwnujem već {duration} i kickovao sam {deauthed} klijenata! Takođe sam sreo "
"{associated} novih prijatelja i pojeo {handshakes} handshakeova! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "sati"
msgid "minutes"
msgstr "minuta"
msgid "seconds"
msgstr "sekundi"
msgid "hour"
msgstr "sat"
msgid "minute"
msgstr "minut"
msgid "second"
msgstr "sekunda"

View File

@ -9,8 +9,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"PO-Revision-Date: 2024-03-27 18:40+0800\n"
"Last-Translator: AlanLeung <admin@mcnot.pro>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Twi\n"
"MIME-Version: 1.0\n"
@ -18,218 +18,218 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr ""
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr ""
msgstr "HI!我是Pwnagotchi!\n程式啟動..."
msgid "New day, new hunt, new pwns!"
msgstr ""
msgstr "新的一天!\n新的狩獵!新的入侵!"
msgid "Hack the Planet!"
msgstr ""
msgstr "我要駭入\n地球的所有人!"
msgid "AI ready."
msgstr ""
msgstr "人工智慧已啟動。"
msgid "The neural network is ready."
msgstr ""
msgstr "神經網路已啟動。"
msgid "Generating keys, do not turn off ..."
msgstr ""
msgstr "產生金鑰中,\n請勿關閉..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr ""
msgstr "嘿,{channel}很順暢!\n你的WIFI會感謝你的。"
msgid "Reading last session logs ..."
msgstr ""
msgstr "正在閱讀最後的會話紀錄..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr ""
msgstr "目前已經閱讀了 {lines_so_far} 行的紀錄..."
msgid "I'm bored ..."
msgstr ""
msgstr "我好無聊..."
msgid "Let's go for a walk!"
msgstr ""
msgstr "我們! 散步! 散步散步散步散步"
msgid "This is the best day of my life!"
msgstr ""
msgstr "這是我生命中最棒的一天!"
msgid "Shitty day :/"
msgstr ""
msgstr "糟糕的一天 :/"
msgid "I'm extremely bored ..."
msgstr ""
msgstr "我超無聊的...炒雞 炒雞的那種"
msgid "I'm very sad ..."
msgstr ""
msgstr "我好難過..."
msgid "I'm sad"
msgstr ""
msgstr "嗚嗚嗚...."
msgid "Leave me alone ..."
msgstr ""
msgstr "尼奏凱啦臭臭"
msgid "I'm mad at you!"
msgstr ""
msgstr "喔氣氣氣氣氣ˋ^ˊ"
msgid "I'm living the life!"
msgstr ""
msgstr "真是充實的一生!"
msgid "I pwn therefore I am."
msgstr ""
msgstr "我駭故我在."
msgid "So many networks!!!"
msgstr ""
msgstr "好多網路啊!!!吃! 吃他! 吃光光!!!"
msgid "I'm having so much fun!"
msgstr ""
msgstr "我玩的超級開心!"
msgid "My crime is that of curiosity ..."
msgstr ""
msgstr "我的缺點就是\n太好奇了..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr ""
msgstr "尼豪{name}!\n很高興認識你!!!!"
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr ""
msgstr "嗨 {name}! 你來攻打我的村莊?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr ""
msgstr "嗨 {name} 你最近過得如何˙ˇ˙?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr ""
msgstr "{name}\n就在附近!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr ""
msgstr "哦嗚 ... \n拜拜{name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr ""
msgstr "{name}\n不見了 ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr ""
msgstr "哦歐...\n{name}\n不見了。"
#, python-brace-format
msgid "{name} missed!"
msgstr ""
msgstr "我剛剛錯過了{name}!"
msgid "Missed!"
msgstr ""
msgstr "又錯過了!"
msgid "Good friends are a blessing!"
msgstr ""
msgstr "有個好朋友\n真幸福!"
msgid "I love my friends!"
msgstr ""
msgstr "我喜歡\n我的朋友!"
msgid "Nobody wants to play with me ..."
msgstr ""
msgstr "沒人想跟我玩..."
msgid "I feel so alone ..."
msgstr ""
msgstr "我覺得好孤單..."
msgid "Where's everybody?!"
msgstr ""
msgstr "大家都去哪裡了?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr ""
msgstr "我想瞇{secs}秒一下..."
msgid "Zzzzz"
msgstr ""
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
msgstr "ZzzZzzz({secs}秒)"
msgid "Good night."
msgstr ""
msgstr "晚安!"
msgid "Zzz"
msgstr ""
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr ""
msgstr "等我{secs}秒..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr ""
msgstr "環顧四周({secs}秒)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr ""
msgstr "嗨\n{what}\n讓我們來當朋友吧!"
#, python-brace-format
msgid "Associating to {what}"
msgstr ""
msgstr "正在連接\n{what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
msgstr "喲,\n{what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr ""
msgstr "我要讓\n{mac}\n斷線!\n他不需要上網!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr ""
msgstr "解除\n{mac}\n的授權中"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr ""
msgstr "把\n{mac}\n踢出中!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr ""
msgstr "酷耶,我們抓到{num}個\n新的握手包{plural}!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr ""
msgstr "你有{count}個新訊息{plural}!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr ""
msgstr "哦歐,有些地方出錯了...\n重新啟動中..."
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr ""
msgstr "正在上傳資料到 {to} ..."
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr ""
msgstr "正在從 {name} 下載資料..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr ""
msgstr "踢了 {num} 個設備\n"
msgid "Made >999 new friends\n"
msgstr ""
msgstr "交了 >999 個新朋友\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr ""
msgstr "交了 {num} 個新朋友\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr ""
msgstr "捕獲了 {num} 個握手包\n"
msgid "Met 1 peer"
msgstr ""
msgstr "遇到了 個同好"
#, python-brace-format
msgid "Met {num} peers"
msgstr ""
msgstr "遇到了 {num} 個同好"
#, python-brace-format
msgid ""
@ -237,21 +237,24 @@ msgid ""
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"我花了{duration}的時間\n駭入和踢了{deauthed}好多設備."
"我還交了好多{associated}新朋友,\n而且抓到了{handshakes}握手包!"
"#pwnagotchi#入侵日志 #駭客人生 #入侵整個星球 #天網 #我好棒˙ˇ˙"
msgid "hours"
msgstr ""
msgstr ""
msgid "minutes"
msgstr ""
msgstr ""
msgid "seconds"
msgstr ""
msgstr ""
msgid "hour"
msgstr ""
msgstr ""
msgid "minute"
msgstr ""
msgstr ""
msgid "second"
msgstr ""
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-25 23:40+0100\n"
"POT-Creation-Date: 2024-11-17 20:51+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

@ -217,25 +217,45 @@ class LastSession(object):
def setup_logging(args, config):
cfg = config['main']['log']
filename = cfg['path']
filenameDebug = cfg['path-debug']
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s")
root = logging.getLogger()
root.setLevel(logging.DEBUG if args.debug else logging.INFO)
#global formatter
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] [%(threadName)s] : %(message)s")
logger = logging.getLogger()
for handler in logger.handlers:
handler.setLevel(logging.DEBUG if args.debug else logging.INFO)
handler.setFormatter(formatter)
logger.setLevel(logging.DEBUG if args.debug else logging.INFO)
if filename:
# since python default log rotation might break session data in different files,
# we need to do log rotation ourselves
log_rotation(filename, cfg)
log_rotation(filenameDebug, cfg)
file_handler = logging.FileHandler(filename)
file_handler.setFormatter(formatter)
root.addHandler(file_handler)
# File handler for logging all normal messages
file_handler = logging.FileHandler(filename) #creates new
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
root.addHandler(console_handler)
# File handler for logging all debug messages
file_handler = logging.FileHandler(filenameDebug) #creates new
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# Console handler for logging debug messages if args.debug is true else just log normal
#console_handler = logging.StreamHandler() #creates new
#console_handler.setLevel(logging.DEBUG if args.debug else logging.INFO)
#console_handler.setFormatter(formatter)
#logger.addHandler(console_handler)
if not args.debug:
# disable scapy and tensorflow logging
logging.getLogger("scapy").disabled = True
@ -250,6 +270,8 @@ def setup_logging(args, config):
requests_log.prpagate = False
def log_rotation(filename, cfg):
rotation = cfg['rotation']
if not rotation['enabled']:

View File

@ -1,4 +1,5 @@
import _thread
#import _thread
import threading
import logging
import time
@ -41,7 +42,8 @@ class AsyncAdvertiser(object):
def start_advertising(self):
if self._config['personality']['advertise']:
_thread.start_new_thread(self._adv_poller, ())
#_thread.start_new_thread(self._adv_poller, ())
threading.Thread(target=self._adv_poller,args=(), name="Grid", daemon=True).start()
grid.set_advertisement_data(self._advertisement)
grid.advertise(True)

View File

@ -6,6 +6,7 @@ import logging
import os
import threading
import pwnagotchi.grid
import prctl
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
loaded = {}
@ -72,6 +73,7 @@ def toggle_plugin(name, enable=True):
def on(event_name, *args, **kwargs):
for plugin_name in loaded.keys():
one(plugin_name, event_name, *args, **kwargs)
@ -82,7 +84,10 @@ def locked_cb(lock_name, cb, *args, **kwargs):
locks[lock_name] = threading.Lock()
with locks[lock_name]:
cb(*args, *kwargs)
# Setting the thread name using prctl
plugin_name, plugin_cb = lock_name.split("::")
prctl.set_name(f"{plugin_name}.{plugin_cb}")
cb(*args, **kwargs)
def one(plugin_name, event_name, *args, **kwargs):
@ -95,8 +100,10 @@ def one(plugin_name, event_name, *args, **kwargs):
if callback is not None and callable(callback):
try:
lock_name = "%s::%s" % (plugin_name, cb_name)
locked_cb_args = (lock_name, callback, *args, *kwargs)
_thread.start_new_thread(locked_cb, locked_cb_args)
thread_name = f'{plugin_name}.{cb_name}'
thread = threading.Thread(target=locked_cb, args=(lock_name, callback, *args, *kwargs), name=thread_name, daemon=True)
thread.start()
except Exception as e:
logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e))
logging.error(e, exc_info=True)
@ -144,4 +151,4 @@ def load(config):
plugin.options = config['main']['plugins'][name]
on('loaded')
on('config_changed', config)
on('config_changed', config)

View File

@ -199,6 +199,9 @@ def list_plugins(args, config, pattern='*'):
available_not_installed = set(available.keys()) - set(installed.keys())
max_len_list = available_and_installed if args.installed else available_not_installed
if not max_len_list:
print('Maybe try: sudo pwnagotchi plugins update')
return 1
max_len = max(map(len, max_len_list))
header = line.format(name='Plugin', width=max_len, version='Version', enabled='Active', status='Status')
line_length = max(max_len, len('Plugin')) + len(header) - len('Plugin') - 12 # lol
@ -239,7 +242,7 @@ def list_plugins(args, config, pattern='*'):
print('-' * line_length)
if not found:
logging.info('Maybe try: pwnagotchi plugins update')
print('Maybe try: sudo pwnagotchi plugins update')
return 1
return 0

View File

@ -1,73 +0,0 @@
import pwnagotchi.plugins as plugins
import logging
import subprocess
import string
import os
'''
Aircrack-ng needed, to install:
> apt-get install aircrack-ng
'''
class AircrackOnly(plugins.Plugin):
__author__ = 'pwnagotchi [at] rossmarks [dot] uk'
__version__ = '1.0.1'
__license__ = 'GPL3'
__description__ = 'confirm pcap contains handshake/PMKID or delete it'
def __init__(self):
self.text_to_set = ""
self.options = dict()
def on_ready(self):
return
def on_loaded(self):
logging.info("aircrackonly plugin loaded")
if 'face' not in self.options:
self.options['face'] = '(>.<)'
check = subprocess.run(
'/usr/bin/dpkg -l aircrack-ng | grep aircrack-ng | awk \'{print $2, $3}\'', shell=True,
stdout=subprocess.PIPE)
check = check.stdout.decode('utf-8').strip()
if check != "aircrack-ng <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

@ -28,7 +28,8 @@ def check(version, repo, native=True):
resp = requests.get("https://api.github.com/repos/%s/releases/latest" % repo)
latest = resp.json()
info['available'] = latest_ver = latest['tag_name'].replace('v', '')
is_arm64 = info['arch'].startswith('aarch')
is_armhf = info['arch'].startswith('arm')
is_aarch = info['arch'].startswith('aarch')
local = version_to_tuple(info['current'])
remote = version_to_tuple(latest_ver)
@ -36,12 +37,20 @@ def check(version, repo, native=True):
if not native:
info['url'] = "https://github.com/%s/archive/%s.zip" % (repo, latest['tag_name'])
else:
if is_arm64:
# check if this release is compatible with aarch64
if is_armhf:
# check if this release is compatible with armhf
for asset in latest['assets']:
download_url = asset['browser_download_url']
if (download_url.endswith('.zip') and
(info['arch'] in download_url or (is_arm64 and 'aarch64' in download_url))):
(info['arch'] in download_url or (is_armhf and 'armhf' in download_url))):
info['url'] = download_url
break
elif is_aarch:
# check if this release is compatible with arm64/aarch64
for asset in latest['assets']:
download_url = asset['browser_download_url']
if (download_url.endswith('.zip') and
(info['arch'] in download_url or (is_aarch and 'aarch' in download_url))):
info['url'] = download_url
break
@ -189,7 +198,7 @@ class AutoUpdate(plugins.Plugin):
to_check = [
('jayofelony/bettercap', parse_version('bettercap -version'), True, 'bettercap'),
('jayofelony/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'),
('jayofelony/pwnagotchi-bookworm', pwnagotchi.__version__, False, 'pwnagotchi')
('jayofelony/pwnagotchi', pwnagotchi.__version__, False, 'pwnagotchi')
]
for repo, local_version, is_native, svc_name in to_check:
@ -221,7 +230,7 @@ class AutoUpdate(plugins.Plugin):
if num_installed > 0:
display.update(force=True, new_data={'status': 'Rebooting ...'})
time.sleep(3)
os.system("service pwnagotchi restart")
pwnagotchi.reboot()
except Exception as e:
logging.error("[update] %s" % e)

View File

@ -578,7 +578,7 @@ class BTTether(plugins.Plugin):
def on_ui_setup(self, ui):
with ui._lock:
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-',
position=(ui.width() / 2 - 10, 0),
position=(ui.width() / 2 - 20, 0),
label_font=fonts.Bold, text_font=fonts.Medium))
def on_ui_update(self, ui):

View File

@ -4,6 +4,7 @@ import subprocess
import time
import random
from io import TextIOWrapper
import os
import pwnagotchi
from pwnagotchi import plugins
@ -11,6 +12,10 @@ from pwnagotchi import plugins
import pwnagotchi.ui.faces as faces
from pwnagotchi.bettercap import Client
from pwnagotchi.ui.components import Text
from pwnagotchi.ui.view import BLACK
import pwnagotchi.ui.fonts as fonts
class FixServices(plugins.Plugin):
__author__ = 'jayofelony'
@ -21,20 +26,19 @@ class FixServices(plugins.Plugin):
__help__ = """
Reload brcmfmac module when blindbug is detected, instead of rebooting. Adapted from WATCHDOG.
"""
__defaults__ = {
'enabled': True,
}
def __init__(self):
self.options = dict()
self.pattern = re.compile(r'brcmf_cfg80211_nexmon_set_channel.*?Set Channel failed')
self.pattern = re.compile(r'ieee80211 phy0: brcmf_cfg80211_add_iface: iface validation failed: err=-95')
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.pattern5 = re.compile(r'fatal error: concurrent map iteration and map write')
self.pattern6 = re.compile(r'panic: runtime error')
self.pattern7 = re.compile(r'ieee80211 phy0: _brcmf_set_multicast_list: Setting allmulti failed, -110')
self.isReloadingMon = False
self.connection = None
self.LASTTRY = 0
self._count = 0
def on_loaded(self):
"""
@ -43,26 +47,13 @@ class FixServices(plugins.Plugin):
logging.info("[Fix_Services] plugin loaded.")
def on_ready(self, agent):
last_lines = self.get_last_lines('journalctl', ['-n10', '-k'], 10)
last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'],
stdout=subprocess.PIPE).stdout))[-10:])
try:
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
logging.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.debug("wlan0mon is up.")
except Exception as err:
logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err))
@ -76,12 +67,12 @@ class FixServices(plugins.Plugin):
# apparently this only gets messages from bettercap going to syslog, not from syslog
def on_bcap_sys_log(self, agent, event):
if re.search('wifi error while hopping to channel', event['data']['Message']):
logging.info("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
logging.info("[Fix_Services]**** restarting wifi.recon")
logging.debug("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
logging.debug("[Fix_Services]**** restarting wifi.recon")
try:
result = agent.run("wifi.recon off; wifi.recon on")
if result["success"]:
logging.info("[Fix_Services] wifi.recon flip: success!")
logging.debug("[Fix_Services] wifi.recon flip: success!")
if hasattr(agent, 'view'):
display = agent.view()
if display:
@ -95,57 +86,40 @@ class FixServices(plugins.Plugin):
logging.error("[Fix_Services]SYSLOG wifi.recon flip fail: %s" % err)
self._tryTurningItOffAndOnAgain(agent)
def get_last_lines(self, command, args, n):
try:
process = subprocess.Popen([command] + args, stdout=subprocess.PIPE)
output = TextIOWrapper(process.stdout)
lines = output.readlines()
last_n_lines = ''.join(lines[-n:])
return last_n_lines
except Exception as e:
print(f"Error occurred: {e}")
return None
def on_epoch(self, agent, epoch, epoch_data):
last_lines = self.get_last_lines('journalctl', ['-n10', '-k'], 10)
other_last_lines = self.get_last_lines('journalctl', ['-n10'], 10)
other_other_last_lines = self.get_last_lines('tail', ['-n10', '/home/pi/logs/pwnagotchi.log'], 10)
last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'],
stdout=subprocess.PIPE).stdout))[-10:])
other_last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10'],
stdout=subprocess.PIPE).stdout))[-10:])
other_other_last_lines = ''.join(
list(TextIOWrapper(subprocess.Popen(['tail', '-n10', '/etc/pwnagotchi/log/pwnagotchi.log'],
stdout=subprocess.PIPE).stdout))[-10:])
# don't check if we ran a reset recently
logging.debug("[Fix_Services]**** epoch")
if time.time() - self.LASTTRY > 180:
# get last 10 lines
display = None
display = agent.view()
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))
if len(self.pattern.findall(last_lines)) >= 1:
subprocess.check_output("monstop", shell=True)
subprocess.check_output("monstart", shell=True)
display.set('status', 'Wifi channel stuck. Restarting recon.')
display.update(force=True)
pwnagotchi.restart("AUTO")
# 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)
logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Wifi channel stuck. Restarting recon.')
display.update(force=True)
logging.info('[Fix_Services] Wifi channel stuck. Restarting recon.')
logging.debug('[Fix_Services] Wifi channel stuck. Restarting recon.')
try:
result = agent.run("wifi.recon off; wifi.recon on")
if result["success"]:
logging.info("[Fix_Services] wifi.recon flip: success!")
logging.debug("[Fix_Services] wifi.recon flip: success!")
if display:
display.update(force=True, new_data={"status": "Wifi recon flipped!",
"face": faces.COOL})
@ -159,32 +133,65 @@ class FixServices(plugins.Plugin):
# Look for pattern 3
elif len(self.pattern3.findall(other_last_lines)) >= 1:
logging.info("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
logging.debug("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Firmware has halted or crashed. Restarting wlan0mon.')
display.update(force=True)
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:
logging.info("[Fix_Services] wlan0 is down!")
logging.debug("[Fix_Services] wlan0 is down!")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Restarting wlan0 now!')
display.update(force=True)
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 5
elif len(self.pattern5.findall(other_other_last_lines)) >= 1:
logging.debug("[Fix_Services] Bettercap has crashed!")
if hasattr(agent, 'view'):
display.set('status', 'Restarting pwnagotchi!')
display.update(force=True)
os.system("systemctl restart bettercap")
pwnagotchi.restart("AUTO")
# Look for pattern 6
elif len(self.pattern6.findall(other_other_last_lines)) >= 1:
logging.debug("[Fix_Services] Bettercap has crashed!")
if hasattr(agent, 'view'):
display.set('status', 'Restarting pwnagotchi!')
display.update(force=True)
os.system("systemctl restart bettercap")
pwnagotchi.restart("AUTO")
# Look for pattern 7
elif len(self.pattern7.findall(other_other_last_lines)) >= 1:
logging.debug("[Fix_Services] Monitor mode failed!")
try:
result = agent.run("wifi.recon off; wifi.recon on")
if result["success"]:
logging.debug("[Fix_Services] wifi.recon flip: success!")
if display:
display.update(force=True, new_data={"status": "Wifi recon flipped!",
"face": faces.COOL})
else:
print("Wifi recon flipped\nthat was easy!")
else:
logging.warning("[Fix_Services] wifi.recon flip: FAILED: %s" % repr(result))
except Exception as err:
logging.error("[Fix_Services wifi.recon flip] %s" % repr(err))
else:
print("logs look good")
@ -197,7 +204,7 @@ class FixServices(plugins.Plugin):
elif level == "debug":
logging.debug(message)
else:
logging.info(message)
logging.debug(message)
if ui:
ui.update(force=force, new_data=displayData)
@ -212,7 +219,7 @@ class FixServices(plugins.Plugin):
# avoid overlapping restarts, but allow it if it's been a while
# (in case the last attempt failed before resetting "isReloadingMon")
if self.isReloadingMon and (time.time() - self.LASTTRY) < 180:
logging.info("[Fix_Services] Duplicate attempt ignored")
logging.debug("[Fix_Services] Duplicate attempt ignored")
else:
self.isReloadingMon = True
self.LASTTRY = time.time()
@ -237,9 +244,9 @@ 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?")
logging.debug("wlan0mon is up. Skip reset?")
# not reliable, so don't skip just yet
# print("wlan0mon is up. Skipping reset.")
# self.isReloadingMon = False
@ -260,7 +267,7 @@ class FixServices(plugins.Plugin):
except Exception as err:
logging.error("[Fix_Services wifi.recon off] error %s" % (repr(err)))
logging.info("[Fix_Services] recon paused. Now trying wlan0mon reload")
logging.debug("[Fix_Services] recon paused. Now trying wlan0mon reload")
try:
cmd_output = subprocess.check_output("monstop", shell=True)
@ -276,14 +283,13 @@ class FixServices(plugins.Plugin):
#
# Future: while "not fixed yet": blah blah blah. if "max_attemts", then reboot like the old days
#
tries = 0
tries = 1
while tries < 3:
try:
# unload the module
cmd_output = subprocess.check_output("sudo modprobe -r brcmfmac", shell=True)
self.logPrintView("info", "[Fix_Services] unloaded brcmfmac", display,
{"status": "Turning it off #%s" % tries, "face": faces.SMART})
time.sleep(1 + tries)
# reload the module
try:
@ -291,28 +297,24 @@ class FixServices(plugins.Plugin):
cmd_output = subprocess.check_output("sudo modprobe brcmfmac", shell=True)
self.logPrintView("info", "[Fix_Services] reloaded brcmfmac")
time.sleep(10 + 4 * tries) # give it some time for wlan device to stabilize, or whatever
# success! now make the mon0
try:
cmd_output = subprocess.check_output("monstart", shell=True)
self.logPrintView("info", "[Fix_Services interface add wlan0mon] worked #%s: %s"
self.logPrintView("info", "[Fix_Services interface add wlan0mon worked #%s: %s"
% (tries, cmd_output))
time.sleep(tries + 5)
try:
# try accessing mon0 in bettercap
result = connection.run("set wifi.interface wlan0mon")
if "success" in result:
logging.info("[Fix_Services set wifi.interface wlan0mon] worked!")
self._count = self._count + 1
time.sleep(1)
logging.debug("[Fix_Services set wifi.interface wlan0mon worked!")
# stop looping and get back to recon
break
else:
logging.info(
logging.debug(
"[Fix_Services set wifi.interfaceface wlan0mon] failed? %s" % repr(result))
except Exception as err:
logging.info(
logging.debug(
"[Fix_Services set wifi.interface wlan0mon] except: %s" % repr(err))
except Exception as cerr: #
if not display:
@ -331,11 +333,11 @@ class FixServices(plugins.Plugin):
tries = tries + 1
if tries < 3:
logging.info("[Fix_Services] wlan0mon didn't make it. trying again")
logging.debug("[Fix_Services] wlan0mon didn't make it. trying again")
if not display:
print(" wlan0mon didn't make it. trying again")
else:
logging.info("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
logging.debug("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
pwnagotchi.reboot()
# exited the loop, so hopefully it loaded
@ -345,14 +347,14 @@ class FixServices(plugins.Plugin):
"face": faces.INTENSE})
else:
print("And back on again...")
logging.info("[Fix_Services] wlan0mon back up")
logging.debug("[Fix_Services] wlan0mon back up")
else:
self.LASTTRY = time.time()
time.sleep(8 + tries * 2) # give it a bit before restarting recon in bettercap
self.isReloadingMon = False
logging.info("[Fix_Services] re-enable recon")
logging.debug("[Fix_Services] re-enable recon")
try:
result = connection.run("wifi.clear; wifi.recon on")
@ -362,7 +364,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,13 +375,24 @@ class FixServices(plugins.Plugin):
logging.error("[Fix_Services wifi.recon on] %s" % repr(err))
pwnagotchi.reboot()
def on_unload(self, ui):
# called to setup the ui elements
def on_ui_setup(self, ui):
with ui._lock:
try:
logging.info("[Fix_Services] unloaded")
except Exception as err:
logging.info("[Fix_Services] unload err %s " % repr(err))
pass
# add custom UI elements
if "position" in self.options:
pos = self.options['position'].split(',')
pos = [int(x.strip()) for x in pos]
else:
pos = (ui.width() / 2 + 35, ui.height() - 11)
logging.debug("Got here")
# called when the ui is updated
def on_ui_update(self, ui):
return
def on_unload(self, ui):
return
# run from command line to brute force a reload

View File

@ -14,8 +14,9 @@ class GPIOButtons(plugins.Plugin):
self.running = False
self.ports = {}
self.commands = None
self.options = dict()
def runCommand(self, channel):
def runcommand(self, channel):
command = self.ports[channel]
logging.info(f"Button Pressed! Running command: {command}")
process = subprocess.Popen(command, shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None,
@ -35,8 +36,8 @@ class GPIOButtons(plugins.Plugin):
gpio = int(gpio)
self.ports[gpio] = command
GPIO.setup(gpio, GPIO.IN, GPIO.PUD_UP)
GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runCommand, bouncetime=600)
#set pimoroni display hat mini LED off/dim
GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runcommand, bouncetime=600)
# set pimoroni display hat mini LED off/dim
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

View File

@ -98,7 +98,7 @@ class GPS(plugins.Plugin):
lat_pos = (127, 74)
lon_pos = (122, 84)
alt_pos = (127, 94)
elif ui.is_waveshare27inch():
elif ui.is_waveshare2in7():
lat_pos = (6, 120)
lon_pos = (1, 135)
alt_pos = (6, 150)

View File

@ -5,7 +5,6 @@ import glob
import re
import pwnagotchi.grid as grid
import pwnagotchi.plugins
import pwnagotchi.plugins as plugins
from pwnagotchi.utils import StatusFile, WifiInfo, extract_from_pcap
from threading import Lock
@ -48,7 +47,7 @@ class Grid(plugins.Plugin):
__version__ = '1.0.1'
__license__ = 'GPL3'
__description__ = 'This plugin signals the unit cryptographic identity and list of pwned networks and list of pwned ' \
'networks to api.pwnagotchi.ai '
'networks to opwngrid.xyz '
def __init__(self):
self.options = dict()
@ -87,10 +86,10 @@ class Grid(plugins.Plugin):
agent.view().on_unread_messages(self.unread_messages, self.total_messages)
def check_handshakes(self, agent):
logging.debug("checking pcaps")
logging.debug("checking pcap's")
config = agent.config()
pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap"))
pcap_files = glob.glob(os.path.join(config['bettercap']['handshakes'], "*.pcap"))
num_networks = len(pcap_files)
reported = self.report.data_field_or('reported', default=[])
num_reported = len(reported)

View File

@ -1,179 +0,0 @@
import logging
import subprocess
import os
import json
import pwnagotchi.plugins as plugins
from threading import Lock
'''
hcxpcapngtool needed, to install:
> git clone https://github.com/ZerBea/hcxtools.git
> cd hcxtools
> apt-get install libcurl4-openssl-dev libssl-dev zlib1g-dev
> make
> sudo make install
'''
class Hashie(plugins.Plugin):
__author__ = 'Jayofelony'
__version__ = '1.0.4'
__license__ = 'GPL3'
__description__ = '''
Attempt to automatically convert pcaps to a crackable format.
If successful, the files containing the hashes will be saved
in the same folder as the handshakes.
The files are saved in their respective Hashcat format:
- EAPOL hashes are saved as *.22000
- PMKID hashes are saved as *.16800
All PCAP files without enough information to create a hash are
stored in a file that can be read by the webgpsmap plugin.
Why use it?:
- Automatically convert handshakes to crackable formats!
We dont all upload our hashes online ;)
- Repair PMKID handshakes that hcxpcapngtool misses
- If running at time of handshake capture, on_handshake can
be used to improve the chance of the repair succeeding
- Be a completionist! Not enough packets captured to crack a network?
This generates an output file for the webgpsmap plugin, use the
location data to revisit networks you need more packets for!
Additional information:
- Currently requires hcxpcapngtool compiled and installed
- Attempts to repair PMKID hashes when hcxpcapngtool cant find the SSID
- hcxpcapngtool sometimes has trouble extracting the SSID, so we
use the raw 16800 output and attempt to retrieve the SSID via tcpdump
- When access_point data is available (on_handshake), we leverage
the reported AP name and MAC to complete the hash
- The repair is very basic and could certainly be improved!
Todo:
Make it so users dont need hcxpcapngtool (unless it gets added to the base image)
Phase 1: Extract/construct 22000/16800 hashes through tcpdump commands
Phase 2: Extract/construct 22000/16800 hashes entirely in python
Improve the code, a lot
'''
def __init__(self):
self.lock = Lock()
self.options = dict()
def on_loaded(self):
logging.info("[Hashie] Plugin loaded")
def on_unloaded(self):
logging.info("[Hashie] Plugin unloaded")
# called when everything is ready and the main loop is about to start
def on_ready(self, agent):
config = agent.config()
handshake_dir = config['bettercap']['handshakes']
logging.info('[Hashie] Starting batch conversion of pcap files')
with self.lock:
self._process_stale_pcaps(handshake_dir)
def on_handshake(self, agent, filename, access_point, client_station):
with self.lock:
handshake_status = []
fullpathNoExt = filename.split('.')[0]
name = filename.split('/')[-1:][0].split('.')[0]
if os.path.isfile(fullpathNoExt + '.22000'):
handshake_status.append('Already have {}.22000 (EAPOL)'.format(name))
elif self._writeEAPOL(filename):
handshake_status.append('Created {}.22000 (EAPOL) from 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

@ -253,7 +253,6 @@ class Logtail(plugins.Plugin):
"""
logging.info("Logtail plugin loaded.")
def on_webhook(self, path, request):
if not self.ready:
return "Plugin not ready"

View File

@ -130,7 +130,7 @@ class MemTemp(plugins.Plugin):
except Exception:
# Set default position based on screen type
if ui.is_waveshare_v2():
h_pos = (178, 84)
h_pos = (175, 84)
v_pos = (197, 74)
elif ui.is_waveshare_v1():
h_pos = (170, 80)
@ -144,6 +144,9 @@ class MemTemp(plugins.Plugin):
elif ui.is_waveshare2in7():
h_pos = (192, 138)
v_pos = (211, 122)
elif ui.is_waveshare1in54V2():
h_pos = (53, 77)
v_pos = (154, 65)
else:
h_pos = (155, 76)
v_pos = (175, 61)

View File

@ -1,150 +0,0 @@
import logging
import json
import os
import threading
import requests
import time
import pwnagotchi.plugins as plugins
from pwnagotchi.utils import StatusFile
class NetPos(plugins.Plugin):
__author__ = 'zenzen san'
__version__ = '2.0.3'
__license__ = 'GPL3'
__description__ = """Saves a json file with the access points with more signal
whenever a handshake is captured.
When internet is available the files are converted in geo locations
using Mozilla LocationService """
API_URL = 'https://location.services.mozilla.com/v1/geolocate?key={api}'
def __init__(self):
self.report = StatusFile('/root/.net_pos_saved', data_format='json')
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']):
logging.error("NET-POS: api_key isn't set. Can't use mozilla's api.")
return
if 'api_url' in self.options:
self.API_URL = self.options['api_url']
self.ready = True
logging.info("net-pos plugin loaded.")
logging.debug(f"net-pos: use api_url: {self.API_URL}")
def _append_saved(self, path):
to_save = list()
if isinstance(path, str):
to_save.append(path)
elif isinstance(path, list):
to_save += path
else:
raise TypeError("Expected list or str, got %s" % type(path))
with open('/root/.net_pos_saved', 'a') as saved_file:
for x in to_save:
saved_file.write(x + "\n")
def on_internet_available(self, agent):
if self.lock.locked():
return
with self.lock:
if self.ready:
config = agent.config()
display = agent.view()
reported = self.report.data_field_or('reported', default=list())
handshake_dir = config['bettercap']['handshakes']
all_files = os.listdir(handshake_dir)
all_np_files = [os.path.join(handshake_dir, filename)
for filename in all_files
if filename.endswith('.net-pos.json')]
new_np_files = set(all_np_files) - set(reported) - set(self.skip)
if new_np_files:
logging.debug("NET-POS: Found %d new net-pos files. Fetching positions ...", len(new_np_files))
display.set('status', f"Found {len(new_np_files)} new net-pos files. Fetching positions ...")
display.update(force=True)
for idx, np_file in enumerate(new_np_files):
geo_file = np_file.replace('.net-pos.json', '.geo.json')
if os.path.exists(geo_file):
# got already the position
reported.append(np_file)
self.report.update(data={'reported': reported})
continue
try:
geo_data = self._get_geo_data(np_file) # returns json obj
except requests.exceptions.RequestException as req_e:
logging.error("NET-POS: %s - RequestException: %s", np_file, req_e)
self.skip += np_file
continue
except json.JSONDecodeError as js_e:
logging.error("NET-POS: %s - JSONDecodeError: %s, removing it...", np_file, js_e)
os.remove(np_file)
continue
except OSError as os_e:
logging.error("NET-POS: %s - OSError: %s", np_file, os_e)
self.skip += np_file
continue
with open(geo_file, 'w+t') as sf:
json.dump(geo_data, sf)
reported.append(np_file)
self.report.update(data={'reported': reported})
display.set('status', f"Fetching positions ({idx + 1}/{len(new_np_files)})")
display.update(force=True)
def on_handshake(self, agent, filename, access_point, client_station):
netpos = self._get_netpos(agent)
if not netpos['wifiAccessPoints']:
return
netpos["ts"] = int("%.0f" % time.time())
netpos_filename = filename.replace('.pcap', '.net-pos.json')
logging.debug("NET-POS: Saving net-location to %s", netpos_filename)
try:
with open(netpos_filename, 'w+t') as net_pos_file:
json.dump(netpos, net_pos_file)
except OSError as os_e:
logging.error("NET-POS: %s", os_e)
def _get_netpos(self, agent):
aps = agent.get_access_points()
netpos = dict()
netpos['wifiAccessPoints'] = list()
# 6 seems a good number to save a wifi networks location
for access_point in sorted(aps, key=lambda i: i['rssi'], reverse=True)[:6]:
netpos['wifiAccessPoints'].append({'macAddress': access_point['mac'],
'signalStrength': access_point['rssi']})
return netpos
def _get_geo_data(self, path, timeout=30):
geourl = self.API_URL.format(api=self.options['api_key'])
try:
with open(path, "r") as json_file:
data = json.load(json_file)
except json.JSONDecodeError as js_e:
raise js_e
except OSError as os_e:
raise os_e
try:
result = requests.post(geourl,
json=data,
timeout=timeout)
return_geo = result.json()
if data["ts"]:
return_geo["ts"] = data["ts"]
return return_geo
except requests.exceptions.RequestException as req_e:
raise req_e

View File

@ -142,6 +142,6 @@ class OnlineHashCrack(plugins.Plugin):
for row in csv.DictReader(cracked_list):
if row['password']:
filename = re.sub(r'[^a-zA-Z0-9]', '', row['ESSID']) + '_' + row['BSSID'].replace(':','')
if os.path.exists( os.path.join(handshake_dir, filename+'.pcap') ):
if os.path.exists( os.path.join(handshake_dir, filename+'.pcap')):
with open(os.path.join(handshake_dir, filename+'.pcap.cracked'), 'w') as f:
f.write(row['password'])

View File

@ -0,0 +1,105 @@
# Based on UPS Lite v1.1 from https://github.com/xenDE
import logging
import time
from pwnagotchi.ui.components import LabeledValue
from pwnagotchi.ui.view import BLACK
import pwnagotchi.ui.fonts as fonts
import pwnagotchi.plugins as plugins
import pwnagotchi
class UPS:
def __init__(self):
# only import when the module is loaded and enabled
import smbus
# 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
self._bus = smbus.SMBus(1)
def voltage(self):
try:
low = self._bus.read_byte_data(0x57, 0x23)
high = self._bus.read_byte_data(0x57, 0x22)
v = (((high << 8) + low) / 1000)
return v
except:
return 0.0
def capacity(self):
battery_level = 0
# battery_v = self.voltage()
try:
battery_level = self._bus.read_byte_data(0x57, 0x2a)
return battery_level
except:
return battery_level
def status(self):
stat02 = self._bus.read_byte_data(0x57, 0x02)
stat03 = self._bus.read_byte_data(0x57, 0x03)
stat04 = self._bus.read_byte_data(0x57, 0x04)
return stat02, stat03, stat04
class PiSugar3(plugins.Plugin):
__author__ = 'taiyonemo@protonmail.com'
__editor__ = 'jayofelony'
__version__ = '1.0.1'
__license__ = 'GPL3'
__description__ = 'A plugin that will add a percentage indicator for the PiSugar 3'
def __init__(self):
self.ups = None
self.lasttemp = 69
self.drot = 0 # display rotation
self.nextDChg = 0 # last time display changed, rotate on updates after 5 seconds
self.options = dict()
def on_loaded(self):
self.ups = UPS()
logging.info("[PiSugar3] plugin loaded.")
def on_ui_setup(self, ui):
try:
ui.add_element('bat', LabeledValue(color=BLACK, label='BAT', value='0%', position=(ui.width() / 2 + 10, 0),
label_font=fonts.Bold, text_font=fonts.Medium))
except Exception as err:
logging.warning("[PiSugar3] setup err: %s" % repr(err))
def on_unload(self, ui):
try:
with ui._lock:
ui.remove_element('bat')
except Exception as err:
logging.warning("[PiSugar3] unload err: %s" % repr(err))
def on_ui_update(self, ui):
capacity = self.ups.capacity()
voltage = self.ups.voltage()
stats = self.ups.status()
temp = stats[2] - 40
if temp != self.lasttemp:
logging.debug("[PiSugar3] (chg %X, info %X, temp %d)" % (stats[0], stats[1], temp))
self.lasttemp = temp
if stats[0] & 0x80: # charging, or has power connected
ui._state._state['bat'].label = "CHG"
else:
ui._state._state['bat'].label = "BAT"
if time.time() > self.nextDChg:
self.drot = (self.drot + 1) % 3
self.nextDChg = time.time() + 5
if self.drot == 0: # show battery voltage
ui.set('bat', "%2.2fv" % voltage)
elif self.drot == 1:
ui.set('bat', "%2i%%" % capacity)
else:
ui.set('bat', "%2i\xb0" % temp)
if capacity <= self.options['shutdown']:
logging.info('[PiSugar3] Empty battery (<= %s%%): shutting down' % self.options['shutdown'])
ui.update(force=True, new_data={'status': 'Battery exhausted, bye ...'})
pwnagotchi.shutdown()

View File

@ -18,12 +18,14 @@ def systemd_dropin(name, content):
systemctl("daemon-reload")
def systemctl(command, unit=None):
if unit:
os.system("/bin/systemctl %s %s" % (command, unit))
else:
os.system("/bin/systemctl %s" % command)
def run_task(name, options):
task_service_name = "switcher-%s-task.service" % name
# save all the commands to a shell script
@ -57,7 +59,7 @@ def run_task(name, options):
""" % (name, task_service_name, name))
if 'reboot' in options and options['reboot']:
# create a indication file!
# create an indication file!
# if this file is set, we want the switcher-tasks to run
open('/root/.switcher', 'a').close()
@ -98,6 +100,7 @@ def run_task(name, options):
systemctl("daemon-reload")
systemctl("start", task_service_name)
class Switcher(plugins.Plugin):
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '0.0.1'

View File

@ -14,7 +14,7 @@ from dateutil.parser import parse
the plugin does the following:
- search for *.pcap files in your /handshakes/ dir
- for every found .pcap file it looks for a .geo.json or .gps.json or .paw-gps.json file with
- for every found .pcap file it looks for a .geo.json or .gps.json or file with
latitude+longitude data inside and shows this position on the map
- if also an .cracked file with a plaintext password inside exist, it reads the content and shows the
position as green instead of red and the password inside the infopox of the position
@ -87,7 +87,8 @@ class Webgpsmap(plugins.Plugin):
# returns all positions
try:
self.ALREADY_SENT = list()
response_data = bytes(json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])), "utf-8")
response_data = bytes(
json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])), "utf-8")
response_status = 200
response_mimetype = "application/json"
response_header_contenttype = 'application/json'
@ -100,7 +101,8 @@ class Webgpsmap(plugins.Plugin):
self.ALREADY_SENT = list()
json_data = json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']))
html_data = self.get_html()
html_data = html_data.replace('var positions = [];', 'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();')
html_data = html_data.replace('var positions = [];',
'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();')
response_data = bytes(html_data, "utf-8")
response_status = 200
response_mimetype = "application/xhtml+xml"
@ -163,7 +165,8 @@ class Webgpsmap(plugins.Plugin):
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')]
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"
@ -180,22 +183,18 @@ class Webgpsmap(plugins.Plugin):
if check_for in all_files:
filename_position = str(os.path.join(handshake_dir, check_for))
logging.debug("[webgpsmap] search for .paw-gps.json")
check_for = os.path.basename(filename_base) + ".paw-gps.json"
if check_for in all_files:
filename_position = str(os.path.join(handshake_dir, check_for))
logging.debug(f"[webgpsmap] end search for position data files and use {filename_position}")
if filename_position is not None:
all_geo_or_gps_files.append(filename_position)
# all_geo_or_gps_files = set(all_geo_or_gps_files) - set(SKIP) # remove skipped networks? No!
# all_geo_or_gps_files = set(all_geo_or_gps_files) - set(SKIP) # remove skipped networks? No!
if newest_only:
all_geo_or_gps_files = set(all_geo_or_gps_files) - set(self.ALREADY_SENT)
logging.info(f"[webgpsmap] Found {len(all_geo_or_gps_files)} position-data files from {len(all_pcap_files)} handshakes. Fetching positions ...")
logging.info(
f"[webgpsmap] Found {len(all_geo_or_gps_files)} position-data files from {len(all_pcap_files)} handshakes. Fetching positions ...")
for pos_file in all_geo_or_gps_files:
try:
@ -213,9 +212,7 @@ class Webgpsmap(plugins.Plugin):
pos_type = 'gps'
elif pos.type() == PositionFile.GEO:
pos_type = 'geo'
elif pos.type() == PositionFile.PAWGPS:
pos_type = 'paw'
gps_data[ssid+"_"+mac] = {
gps_data[ssid + "_" + mac] = {
'ssid': ssid,
'mac': mac,
'type': pos_type,
@ -224,7 +221,7 @@ class Webgpsmap(plugins.Plugin):
'acc': pos.accuracy(),
'ts_first': pos.timestamp_first(),
'ts_last': pos.timestamp_last(),
}
}
# get ap password if exist
check_for = os.path.basename(pos_file).split(".")[0] + ".pcap.cracked"
@ -265,7 +262,6 @@ class PositionFile:
"""
GPS = 1
GEO = 2
PAWGPS = 3
def __init__(self, path):
self._file = path
@ -282,7 +278,7 @@ class PositionFile:
"""
Returns the mac from filename
"""
parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo|paw-gps)\.json', self._filename)
parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo)\.json', self._filename)
if parsed_mac:
mac = parsed_mac.groups()[0]
return mac
@ -292,7 +288,7 @@ class PositionFile:
"""
Returns the ssid from filename
"""
parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo|paw-gps)\.json', self._filename)
parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo)\.json', self._filename)
if parsed_ssid:
return parsed_ssid.groups()[0]
return None
@ -354,8 +350,6 @@ class PositionFile:
return PositionFile.GPS
if self._file.endswith('.geo.json'):
return PositionFile.GEO
if self._file.endswith('.paw-gps.json'):
return PositionFile.PAWGPS
return None
def lat(self):
@ -402,9 +396,7 @@ class PositionFile:
def accuracy(self):
if self.type() == PositionFile.GPS:
return 50.0 # a default
if self.type() == PositionFile.PAWGPS:
return 50.0 # a default
return 50.0 # a default
if self.type() == PositionFile.GEO:
try:
return self._json['accuracy']

33
pwnagotchi/ui/colors.py Normal file
View File

@ -0,0 +1,33 @@
LOOK_R = '( ⚆_⚆)'
LOOK_L = '(☉_☉ )'
LOOK_R_HAPPY = '( ◕‿◕)'
LOOK_L_HAPPY = '(◕‿◕ )'
SLEEP = '(⇀‿‿↼)'
SLEEP2 = '(≖‿‿≖)'
AWAKE = '(◕‿‿◕)'
BORED = '(-__-)'
INTENSE = '(°▃▃°)'
COOL = '(⌐■_■)'
HAPPY = '(•‿‿•)'
GRATEFUL = '(^‿‿^)'
EXCITED = '(ᵔ◡◡ᵔ)'
MOTIVATED = '(☼‿‿☼)'
DEMOTIVATED = '(≖__≖)'
SMART = '(✜‿‿✜)'
LONELY = '(ب__ب)'
SAD = '(╥☁╥ )'
ANGRY = "(-_-')"
FRIEND = '(♥‿‿♥)'
BROKEN = '(☓‿‿☓)'
DEBUG = '(#__#)'
UPLOAD = '(1__0)'
UPLOAD1 = '(1__1)'
UPLOAD2 = '(0__1)'
PNG = False
POSITION_X = 0
POSITION_Y = 40
def load_from_config(config):
for face_name, face_value in config.items():
globals()[face_name.upper()] = face_value

View File

@ -1,4 +1,4 @@
from PIL import Image
from PIL import Image, ImageOps
from textwrap import TextWrapper
@ -10,13 +10,17 @@ class Widget(object):
def draw(self, canvas, drawer):
raise Exception("not implemented")
# canvas.paste: https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.paste
# takes mask variable, to identify color system. (not used for pwnagotchi yet)
# Pwn should use "1" since its mainly black or white displays.
class Bitmap(Widget):
def __init__(self, path, xy, color=0):
super().__init__(xy, color)
self.image = Image.open(path)
def draw(self, canvas, drawer):
if self.color == 0xFF:
self.image = ImageOps.invert(self.image)
canvas.paste(self.image, self.xy)
@ -40,21 +44,37 @@ class FilledRect(Widget):
class Text(Widget):
def __init__(self, value="", position=(0, 0), font=None, color=0, wrap=False, max_length=0):
def __init__(self, value="", position=(0, 0), font=None, color=0, wrap=False, max_length=0, png=False):
super().__init__(position, color)
self.value = value
self.font = font
self.wrap = wrap
self.max_length = max_length
self.wrapper = TextWrapper(width=self.max_length, replace_whitespace=False) if wrap else None
self.png = png
def draw(self, canvas, drawer):
if self.value is not None:
if self.wrap:
text = '\n'.join(self.wrapper.wrap(self.value))
if not self.png:
if self.wrap:
text = '\n'.join(self.wrapper.wrap(self.value))
else:
text = self.value
drawer.text(self.xy, text, font=self.font, fill=self.color)
else:
text = self.value
drawer.text(self.xy, text, font=self.font, fill=self.color)
self.image = Image.open(self.value)
self.image = self.image.convert('RGBA')
self.pixels = self.image.load()
for y in range(self.image.size[1]):
for x in range(self.image.size[0]):
if self.pixels[x,y][3] < 255: # check alpha
self.pixels[x,y] = (255, 255, 255, 255)
if self.color == 255:
self._image = ImageOps.colorize(self.image.convert('L'), black = "white", white = "black")
else:
self._image = self.image
self.image = self._image.convert('1')
canvas.paste(self.image, self.xy)
class LabeledValue(Widget):

View File

@ -21,13 +21,47 @@ class Display(View):
self._canvas_next = None
self._render_thread_instance = threading.Thread(
target=self._render_thread,
daemon=True
daemon=True,
name="Renderer"
)
self._render_thread_instance.start()
def is_lcdhat(self):
return self._implementation.name == 'lcdhat'
def is_wavesharelcd0in96(self):
return self._implementation.name == 'wavesharelcd0in96'
def is_wavesharelcd1in3(self):
return self._implementation.name == 'wavesharelcd1in3'
def is_wavesharelcd1in8(self):
return self._implementation.name == 'wavesharelcd1in8'
def is_wavesharelcd1in9(self):
return self._implementation.name == 'wavesharelcd1in9'
def is_wavesharelcd1in14(self):
return self._implementation.name == 'wavesharelcd1in14'
def is_wavesharelcd1in28(self):
return self._implementation.name == 'wavesharelcd1in28'
def is_wavesharelcd1in47(self):
return self._implementation.name == 'wavesharelcd1in47'
def is_wavesharelcd1in54(self):
return self._implementation.name == 'wavesharelcd1in54'
def is_wavesharelcd1in69(self):
return self._implementation.name == 'wavesharelcd1in69'
def is_wavesharelcd2in0(self):
return self._implementation.name == 'wavesharelcd2in0'
def is_wavesharelcd2in4(self):
return self._implementation.name == 'wavesharelcd2in4'
def is_waveshare144lcd(self):
return self._implementation.name == 'waveshare144lcd'
@ -118,6 +152,9 @@ class Display(View):
def is_waveshare2in66g(self):
return self._implementation.name == 'waveshare2in66g'
def is_weact2in9(self):
return self._implementation.name == 'weact2in9'
def is_waveshare3in0g(self):
return self._implementation.name == 'waveshare3in0g'
@ -151,6 +188,12 @@ class Display(View):
def is_waveshare5in65f(self):
return self._implementation.name == 'waveshare5in65f'
def is_waveshare5in79(self):
return self._implementation.name == 'waveshare5in79'
def is_waveshare5in79b(self):
return self._implementation.name == 'waveshare5in79b'
def is_waveshare5in83(self):
return self._implementation.name == 'waveshare5in83'
@ -193,6 +236,12 @@ class Display(View):
def is_inky(self):
return self._implementation.name == 'inky'
def is_inkyv2(self):
return self._implementation.name == 'inkyv2'
def is_dummy_display(self):
return self._implementation.name == 'dummydisplay'
def is_papirus(self):
return self._implementation.name == 'papirus'
@ -208,11 +257,32 @@ class Display(View):
def is_displayhatmini(self):
return self._implementation.name == 'displayhatmini'
def is_pirateaudio(self):
return self._implementation.name == 'pirateaudio'
def is_pitft(self):
return self._implementation.name == 'pitft'
def is_tftbonnet(self):
return self._implementation.name == 'tftbonnet'
def is_waveshareoledlcd(self):
return self._implementation.name == 'waveshareoledlcd'
def is_waveshareoledlcdvert(self):
return self._implementation.name == 'waveshareoledlcdvert'
def is_i2coled(self):
return self._implementation.name == 'i2coled'
def is_waveshare35lcd(self):
return self._implementation.name == 'waveshare35lcd'
def is_adfruit213v3(self):
return self._implementation.name == 'adafruit2in13_v3'
def is_waveshare_any(self):
return self.is_waveshare_v1() or self.is_waveshare_v2()
return self.is_waveshare_v1() or self.is_waveshare_v2() or self.is_waveshare_v4() or self.is_waveshare_v3
def init_display(self):
if self._enabled:

View File

@ -23,6 +23,9 @@ DEBUG = '(#__#)'
UPLOAD = '(1__0)'
UPLOAD1 = '(1__1)'
UPLOAD2 = '(0__1)'
PNG = False
POSITION_X = 0
POSITION_Y = 40
def load_from_config(config):

View File

@ -1,259 +1,345 @@
from pwnagotchi.ui.hw.inky import Inky
from pwnagotchi.ui.hw.papirus import Papirus
from pwnagotchi.ui.hw.oledhat import OledHat
from pwnagotchi.ui.hw.lcdhat import LcdHat
from pwnagotchi.ui.hw.dfrobot import DFRobotV1
from pwnagotchi.ui.hw.dfrobot_v2 import DFRobotV2
from pwnagotchi.ui.hw.waveshare2in13 import WaveshareV1
from pwnagotchi.ui.hw.waveshare2in13_V2 import WaveshareV2
from pwnagotchi.ui.hw.waveshare2in13_V3 import WaveshareV3
from pwnagotchi.ui.hw.waveshare2in13_V4 import WaveshareV4
from pwnagotchi.ui.hw.waveshare2in7 import Waveshare27inch
from pwnagotchi.ui.hw.waveshare2in7_V2 import Waveshare27inchV2
from pwnagotchi.ui.hw.waveshare2in9 import Waveshare29inch
from pwnagotchi.ui.hw.waveshare2in9_V2 import Waveshare29inchV2
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
from pwnagotchi.ui.hw.displayhatmini import DisplayHatMini
from pwnagotchi.ui.hw.waveshare1in02 import Waveshare1in02
from pwnagotchi.ui.hw.waveshare1in54 import Waveshare154
from pwnagotchi.ui.hw.waveshare1in54_V2 import Waveshare154V2
from pwnagotchi.ui.hw.waveshare1in54b_V2 import Waveshare154bV2
from pwnagotchi.ui.hw.waveshare1in54c import Waveshare1in54c
from pwnagotchi.ui.hw.waveshare1in64g import Waveshare1in64g
from pwnagotchi.ui.hw.waveshare2in7b import Waveshare27b
from pwnagotchi.ui.hw.waveshare2in7b_V2 import Waveshare27bV2
from pwnagotchi.ui.hw.waveshare2in9b_V3 import Waveshare29bV3
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.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
from pwnagotchi.ui.hw.waveshare4in01f import Waveshare4in01f
from pwnagotchi.ui.hw.waveshare4in2 import Waveshare4in2
from pwnagotchi.ui.hw.waveshare4in2_V2 import Waveshare4in2V2
from pwnagotchi.ui.hw.waveshare4in2b_V2 import Waveshare4in2bV2
from pwnagotchi.ui.hw.waveshare4in2bc import Waveshare4in2bc
from pwnagotchi.ui.hw.waveshare4in26 import Waveshare4in26
from pwnagotchi.ui.hw.waveshare4in37g import Waveshare4in37g
from pwnagotchi.ui.hw.waveshare5in65f import Waveshare5in65f
from pwnagotchi.ui.hw.waveshare5in83 import Waveshare5in83
from pwnagotchi.ui.hw.waveshare5in83_V2 import Waveshare5in83V2
from pwnagotchi.ui.hw.waveshare5in83b_V2 import Waveshare5in83bV2
from pwnagotchi.ui.hw.waveshare5in83bc import Waveshare5in83bc
from pwnagotchi.ui.hw.waveshare7in3f import Waveshare7in3f
from pwnagotchi.ui.hw.waveshare7in3g import Waveshare7in3g
from pwnagotchi.ui.hw.waveshare7in5 import Waveshare7in5
from pwnagotchi.ui.hw.waveshare7in5_HD import Waveshare7in5HD
from pwnagotchi.ui.hw.waveshare7in5_V2 import Waveshare7in5V2
from pwnagotchi.ui.hw.waveshare7in5b_HD import Waveshare7in5bHD
from pwnagotchi.ui.hw.waveshare7in5b_V2 import Waveshare7in5bV2
from pwnagotchi.ui.hw.waveshare7in5bc import Waveshare7in5bc
from pwnagotchi.ui.hw.waveshare13in3k import Waveshare13in3k
def display_for(config):
# config has been normalized already in utils.load_config
if config['ui']['display']['type'] == 'inky':
from pwnagotchi.ui.hw.inky import Inky
return Inky(config)
elif config['ui']['display']['type'] == 'inkyv2':
from pwnagotchi.ui.hw.inkyv2 import InkyV2
return InkyV2(config)
elif config['ui']['display']['type'] == 'wavesharelcd0in96':
from pwnagotchi.ui.hw.wavesharelcd0in96 import Wavesharelcd0in96
return Wavesharelcd0in96(config)
elif config['ui']['display']['type'] == 'wavesharelcd1in3':
from pwnagotchi.ui.hw.wavesharelcd1in3 import Wavesharelcd1in3
return Wavesharelcd1in3(config)
elif config['ui']['display']['type'] == 'wavesharelcd1in8':
from pwnagotchi.ui.hw.wavesharelcd1in8 import Wavesharelcd1in8
return Wavesharelcd1in8(config)
elif config['ui']['display']['type'] == 'wavesharelcd1in9':
from pwnagotchi.ui.hw.wavesharelcd1in9 import Wavesharelcd1in9
return Wavesharelcd1in9(config)
elif config['ui']['display']['type'] == 'wavesharelcd1in14':
from pwnagotchi.ui.hw.wavesharelcd1in14 import Wavesharelcd1in14
return Wavesharelcd1in14(config)
elif config['ui']['display']['type'] == 'wavesharelcd1in28':
from pwnagotchi.ui.hw.wavesharelcd1in28 import Wavesharelcd1in28
return Wavesharelcd1in28(config)
elif config['ui']['display']['type'] == 'wavesharelcd1in47':
from pwnagotchi.ui.hw.wavesharelcd1in47 import Wavesharelcd1in47
return Wavesharelcd1in47(config)
elif config['ui']['display']['type'] == 'wavesharelcd1in54':
from pwnagotchi.ui.hw.wavesharelcd1in54 import Wavesharelcd1in54
return Wavesharelcd1in54(config)
elif config['ui']['display']['type'] == 'wavesharelcd1in69':
from pwnagotchi.ui.hw.wavesharelcd1in69 import Wavesharelcd1in69
return Wavesharelcd1in69(config)
elif config['ui']['display']['type'] == 'wavesharelcd2in0':
from pwnagotchi.ui.hw.wavesharelcd2in0 import Wavesharelcd2in0
return Wavesharelcd2in0(config)
elif config['ui']['display']['type'] == 'wavesharelcd2in4':
from pwnagotchi.ui.hw.wavesharelcd2in4 import Wavesharelcd2in4
return Wavesharelcd2in4(config)
elif config['ui']['display']['type'] == 'dummydisplay':
from pwnagotchi.ui.hw.dummydisplay import DummyDisplay
return DummyDisplay(config)
elif config['ui']['display']['type'] == 'papirus':
from pwnagotchi.ui.hw.papirus import Papirus
return Papirus(config)
elif config['ui']['display']['type'] == 'oledhat':
from pwnagotchi.ui.hw.oledhat import OledHat
return OledHat(config)
elif config['ui']['display']['type'] == 'lcdhat':
from pwnagotchi.ui.hw.lcdhat import LcdHat
return LcdHat(config)
elif config['ui']['display']['type'] == 'dfrobot_1':
from pwnagotchi.ui.hw.dfrobot import DFRobotV1
return DFRobotV1(config)
elif config['ui']['display']['type'] == 'dfrobot_2':
from pwnagotchi.ui.hw.dfrobot_v2 import DFRobotV2
return DFRobotV2(config)
elif config['ui']['display']['type'] == 'waveshare144lcd':
from pwnagotchi.ui.hw.waveshare1in44lcd import Waveshare144lcd
return Waveshare144lcd(config)
elif config['ui']['display']['type'] == 'waveshare35lcd':
from pwnagotchi.ui.hw.waveshare3in5lcd import Waveshare35lcd
return Waveshare35lcd(config)
elif config['ui']['display']['type'] == 'spotpear24inch':
from pwnagotchi.ui.hw.spotpear24in import Spotpear24inch
return Spotpear24inch(config)
elif config['ui']['display']['type'] == 'displayhatmini':
from pwnagotchi.ui.hw.displayhatmini import DisplayHatMini
return DisplayHatMini(config)
elif config['ui']['display']['type'] == 'pirateaudio':
from pwnagotchi.ui.hw.pirateaudio import PirateAudio
return PirateAudio(config)
elif config['ui']['display']['type'] == 'pitft':
from pwnagotchi.ui.hw.pitft import Pitft
return Pitft(config)
elif config['ui']['display']['type'] == 'tftbonnet':
from pwnagotchi.ui.hw.tftbonnet import TftBonnet
return TftBonnet(config)
elif config['ui']['display']['type'] == 'waveshareoledlcd':
from pwnagotchi.ui.hw.waveshareoledlcd import Waveshareoledlcd
return Waveshareoledlcd(config)
elif config['ui']['display']['type'] == 'waveshareoledlcdvert':
from pwnagotchi.ui.hw.waveshareoledlcdvert import Waveshareoledlcdvert
return Waveshareoledlcdvert(config)
elif config['ui']['display']['type'] == 'i2coled':
from pwnagotchi.ui.hw.i2coled import I2COled
return I2COled(config)
elif config['ui']['display']['type'] == 'waveshare1in02':
from pwnagotchi.ui.hw.waveshare1in02 import Waveshare1in02
return Waveshare1in02(config)
elif config['ui']['display']['type'] == 'waveshare1in54':
from pwnagotchi.ui.hw.waveshare1in54 import Waveshare154
return Waveshare154(config)
elif config['ui']['display']['type'] == 'waveshare1in54_v2':
from pwnagotchi.ui.hw.waveshare1in54_V2 import Waveshare154V2
return Waveshare154V2(config)
elif config['ui']['display']['type'] == 'waveshare1in54b':
from pwnagotchi.ui.hw.waveshare1in54b import Waveshare154inchb
return Waveshare154inchb(config)
elif config['ui']['display']['type'] == 'waveshare1in54b_v2':
from pwnagotchi.ui.hw.waveshare1in54b_V2 import Waveshare154bV2
return Waveshare154bV2(config)
elif config['ui']['display']['type'] == 'waveshare1in54c':
from pwnagotchi.ui.hw.waveshare1in54c import Waveshare1in54c
return Waveshare1in54c(config)
elif config['ui']['display']['type'] == 'waveshare1in64g':
from pwnagotchi.ui.hw.waveshare1in64g import Waveshare1in64g
return Waveshare1in64g(config)
elif config['ui']['display']['type'] == 'waveshare2in7':
from pwnagotchi.ui.hw.waveshare2in7 import Waveshare27inch
return Waveshare27inch(config)
elif config['ui']['display']['type'] == 'waveshare2in7_v2':
from pwnagotchi.ui.hw.waveshare2in7_V2 import Waveshare27inchV2
return Waveshare27inchV2(config)
elif config['ui']['display']['type'] == 'waveshare2in7b':
from pwnagotchi.ui.hw.waveshare2in7b import Waveshare27b
return Waveshare27b(config)
elif config['ui']['display']['type'] == 'waveshare2in7b_v2':
from pwnagotchi.ui.hw.waveshare2in7b_V2 import Waveshare27bV2
return Waveshare27bV2(config)
elif config['ui']['display']['type'] == 'waveshare2in9':
from pwnagotchi.ui.hw.waveshare2in9 import Waveshare29inch
return Waveshare29inch(config)
elif config['ui']['display']['type'] == 'waveshare2in9bc':
from pwnagotchi.ui.hw.waveshare2in9bc import Waveshare2in9bc
return Waveshare2in9bc(config)
elif config['ui']['display']['type'] == 'waveshare2in9d':
from pwnagotchi.ui.hw.waveshare2in9d import Waveshare2in9d
return Waveshare2in9d(config)
elif config['ui']['display']['type'] == 'waveshare2in9_v2':
from pwnagotchi.ui.hw.waveshare2in9_V2 import Waveshare29inchV2
return Waveshare29inchV2(config)
elif config['ui']['display']['type'] == 'waveshare2in9b_v3':
from pwnagotchi.ui.hw.waveshare2in9b_V3 import Waveshare29bV3
return Waveshare29bV3(config)
elif config['ui']['display']['type'] == 'waveshare2in9b_v4':
from pwnagotchi.ui.hw.waveshare2in9b_V4 import Waveshare29bV4
return Waveshare29bV4(config)
elif config['ui']['display']['type'] == 'waveshare_1':
from pwnagotchi.ui.hw.waveshare2in13 import WaveshareV1
return WaveshareV1(config)
elif config['ui']['display']['type'] == 'waveshare_2':
from pwnagotchi.ui.hw.waveshare2in13_V2 import WaveshareV2
return WaveshareV2(config)
elif config['ui']['display']['type'] == 'waveshare_3':
from pwnagotchi.ui.hw.waveshare2in13_V3 import WaveshareV3
return WaveshareV3(config)
elif config['ui']['display']['type'] == 'waveshare_4':
from pwnagotchi.ui.hw.waveshare2in13_V4 import WaveshareV4
return WaveshareV4(config)
elif config['ui']['display']['type'] == 'adafruit2in13_v3':
from pwnagotchi.ui.hw.adafruit2in13 import Adafruit2in13V3
return Adafruit2in13V3(config)
elif config['ui']['display']['type'] == 'waveshare2in13bc':
from pwnagotchi.ui.hw.waveshare2in13bc import Waveshare213bc
return Waveshare213bc(config)
elif config['ui']['display']['type'] == 'waveshare2in13d':
from pwnagotchi.ui.hw.waveshare2in13d import Waveshare213d
return Waveshare213d(config)
elif config['ui']['display']['type'] == 'waveshare2in13b_v3':
from pwnagotchi.ui.hw.waveshare2in13b_V3 import Waveshare2in13bV3
return Waveshare2in13bV3(config)
elif config['ui']['display']['type'] == 'waveshare2in13b_v4':
from pwnagotchi.ui.hw.waveshare2in13b_V4 import Waveshare213bV4
return Waveshare213bV4(config)
elif config['ui']['display']['type'] == 'waveshare2in13g':
from pwnagotchi.ui.hw.waveshare2in13g import Waveshare2in13g
return Waveshare2in13g(config)
elif config['ui']['display']['type'] == 'waveshare2in36g':
from pwnagotchi.ui.hw.waveshare2in36g import Waveshare2in36g
return Waveshare2in36g(config)
elif config['ui']['display']['type'] == 'waveshare2in66':
from pwnagotchi.ui.hw.waveshare2in66 import Waveshare2in66
return Waveshare2in66(config)
elif config['ui']['display']['type'] == 'waveshare2in66b':
from pwnagotchi.ui.hw.waveshare2in66b import Waveshare2in66b
return Waveshare2in66b(config)
elif config['ui']['display']['type'] == 'waveshare2in66g':
from pwnagotchi.ui.hw.waveshare2in66g import Waveshare2in66g
return Waveshare2in66g(config)
elif config['ui']['display']['type'] == 'waveshare3in0g':
from pwnagotchi.ui.hw.waveshare3in0g import Waveshare3in0g
return Waveshare3in0g(config)
elif config['ui']['display']['type'] == 'waveshare3in7':
from pwnagotchi.ui.hw.waveshare3in7 import Waveshare3in7
return Waveshare3in7(config)
elif config['ui']['display']['type'] == 'waveshare3in52':
from pwnagotchi.ui.hw.waveshare3in52 import Waveshare3in52
return Waveshare3in52(config)
elif config['ui']['display']['type'] == 'waveshare4in01f':
from pwnagotchi.ui.hw.waveshare4in01f import Waveshare4in01f
return Waveshare4in01f(config)
elif config['ui']['display']['type'] == 'waveshare4in2':
from pwnagotchi.ui.hw.waveshare4in2 import Waveshare4in2
return Waveshare4in2(config)
elif config['ui']['display']['type'] == 'waveshare4in2_v2':
from pwnagotchi.ui.hw.waveshare4in2_V2 import Waveshare4in2V2
return Waveshare4in2V2(config)
elif config['ui']['display']['type'] == 'waveshare4in2b_v2':
from pwnagotchi.ui.hw.waveshare4in2b_V2 import Waveshare4in2bV2
return Waveshare4in2bV2(config)
elif config['ui']['display']['type'] == 'waveshare4in2bc':
from pwnagotchi.ui.hw.waveshare4in2bc import Waveshare4in2bc
return Waveshare4in2bc(config)
elif config['ui']['display']['type'] == 'waveshare4in26':
from pwnagotchi.ui.hw.waveshare4in26 import Waveshare4in26
return Waveshare4in26(config)
elif config['ui']['display']['type'] == 'waveshare4in37g':
from pwnagotchi.ui.hw.waveshare4in37g import Waveshare4in37g
return Waveshare4in37g(config)
elif config['ui']['display']['type'] == 'waveshare5in65f':
from pwnagotchi.ui.hw.waveshare5in65f import Waveshare5in65f
return Waveshare5in65f(config)
elif config['ui']['display']['type'] == 'waveshare5in79':
from pwnagotchi.ui.hw.waveshare5in79 import Waveshare5in79
return Waveshare5in79(config)
elif config['ui']['display']['type'] == 'waveshare5in79b':
from pwnagotchi.ui.hw.waveshare5in79b import Waveshare5in79b
return Waveshare5in79b(config)
elif config['ui']['display']['type'] == 'waveshare5in83':
from pwnagotchi.ui.hw.waveshare5in83 import Waveshare5in83
return Waveshare5in83(config)
elif config['ui']['display']['type'] == 'waveshare5in83_v2':
from pwnagotchi.ui.hw.waveshare5in83_V2 import Waveshare5in83V2
return Waveshare5in83V2(config)
elif config['ui']['display']['type'] == 'waveshare5in83b_v2':
from pwnagotchi.ui.hw.waveshare5in83b_V2 import Waveshare5in83bV2
return Waveshare5in83bV2(config)
elif config['ui']['display']['type'] == 'waveshare5in83bc':
from pwnagotchi.ui.hw.waveshare5in83bc import Waveshare5in83bc
return Waveshare5in83bc(config)
elif config['ui']['display']['type'] == 'waveshare7in3f':
from pwnagotchi.ui.hw.waveshare7in3f import Waveshare7in3f
return Waveshare7in3f(config)
elif config['ui']['display']['type'] == 'waveshare7in3g':
from pwnagotchi.ui.hw.waveshare7in3g import Waveshare7in3g
return Waveshare7in3g(config)
elif config['ui']['display']['type'] == 'waveshare7in5':
from pwnagotchi.ui.hw.waveshare7in5 import Waveshare7in5
return Waveshare7in5(config)
elif config['ui']['display']['type'] == 'waveshare7in5_HD':
from pwnagotchi.ui.hw.waveshare7in5_HD import Waveshare7in5HD
return Waveshare7in5HD(config)
elif config['ui']['display']['type'] == 'waveshare7in5_v2':
from pwnagotchi.ui.hw.waveshare7in5_V2 import Waveshare7in5V2
return Waveshare7in5V2(config)
elif config['ui']['display']['type'] == 'waveshare7in5b_HD':
from pwnagotchi.ui.hw.waveshare7in5b_HD import Waveshare7in5bHD
return Waveshare7in5bHD(config)
elif config['ui']['display']['type'] == 'waveshare7in5b_v2':
from pwnagotchi.ui.hw.waveshare7in5b_V2 import Waveshare7in5bV2
return Waveshare7in5bV2(config)
elif config['ui']['display']['type'] == 'waveshare7in5bc':
from pwnagotchi.ui.hw.waveshare7in5bc import Waveshare7in5bc
return Waveshare7in5bc(config)
elif config['ui']['display']['type'] == 'waveshare13in3k':
from pwnagotchi.ui.hw.waveshare13in3k import Waveshare13in3k
return Waveshare13in3k(config)

View File

@ -0,0 +1,45 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Adafruit2in13V3(DisplayImpl):
def __init__(self, config):
super(Adafruit2in13V3, self).__init__(config, 'adafruit2in13_v3')
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 adafruit 2in13 V3 display")
from pwnagotchi.ui.hw.libs.adafruit.v2in13_v3.epd2in13_v3 import EPD
self._display = EPD()
self._display.init()
self._display.Clear(0xFF)
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.displayPartial(buf)
def clear(self):
self._display.Clear(0xFF)

View File

@ -40,4 +40,4 @@ class DFRobotV1(DisplayImpl):
self._display.display(buf)
def clear(self):
self._display.Clear(0xFF)
self._display.clear(0xFF)

View File

@ -40,4 +40,4 @@ class DFRobotV1(DisplayImpl):
self._display.display(buf)
def clear(self):
self._display.Clear(0xFF)
self._display.clear(0xFF)

View File

@ -40,4 +40,4 @@ class DFRobotV2(DisplayImpl):
self._display.display(buf)
def clear(self):
self._display.Clear(0xFF)
self._display.clear(0xFF)

View File

@ -40,4 +40,4 @@ class DFRobotV2(DisplayImpl):
self._display.display(buf)
def clear(self):
self._display.Clear(0xFF)
self._display.clear(0xFF)

View File

@ -7,6 +7,7 @@ from pwnagotchi.ui.hw.base import DisplayImpl
class DisplayHatMini(DisplayImpl):
def __init__(self, config):
super(DisplayHatMini, self).__init__(config, 'displayhatmini')
self.mode = "RGB" # its actually BGR;16 5,6,5 bit, but display lib converts it
def layout(self):
fonts.setup(12, 10, 12, 70, 25, 9)
@ -34,10 +35,10 @@ class DisplayHatMini(DisplayImpl):
def initialize(self):
logging.info("initializing Display Hat Mini")
from pwnagotchi.ui.hw.libs.pimoroni.displayhatmini.ST7789 import ST7789
self._display = ST7789(0,1,9,13)
self._display = ST7789(0, 1, 9, 13, width=self._layout['width'], height=self._layout['height'], rotation=0)
def render(self, canvas):
self._display.display(canvas)
def clear(self):
self._display.clear()
pass

View File

@ -0,0 +1,43 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DummyDisplay(DisplayImpl):
def __init__(self, config):
super(DummyDisplay, self).__init__(config, 'DummyDisplay')
def layout(self):
width = 480 if 'width' not in self.config else self.config['width']
height = 720 if 'height' not in self.config else self.config['height']
fonts.setup(int(height/30), int(height/40), int(height/30), int(height/6), int(height/30), int(height/35))
self._layout['width'] = width
self._layout['height'] = height
self._layout['face'] = (0, int(width/12))
self._layout['name'] = (5, int(width/25))
self._layout['channel'] = (0, 0)
self._layout['aps'] = (int(width/8), 0)
self._layout['uptime'] = (width-int(width/12), 0)
self._layout['line1'] = [0, int(height/32), width, int(height/32)]
self._layout['line2'] = [0, height-int(height/25)-1, width, height-int(height/25)-1]
self._layout['friend_face'] = (0, int(height/10))
self._layout['friend_name'] = (int(width/12), int(height/10))
self._layout['shakes'] = (0, height-int(height/25))
self._layout['mode'] = (width-int(width/8), height - int (height/25))
lw, lh = fonts.Small.getsize("W")
self._layout['status'] = {
'pos': (int(width/48), int(height/3)),
'font': fonts.status_font(fonts.Small),
'max': int(width / lw)
}
return self._layout
def initialize(self):
return
def render(self, canvas):
return
def clear(self):
return

View File

@ -0,0 +1,67 @@
# Created for the Pwnagotchi project by RasTacsko
# HW libraries are based on the adafruit python SSD1306 repo:
# https://github.com/adafruit/Adafruit_Python_SSD1306
# SMBus parts coming from BLavery's lib_oled96 repo:
# https://github.com/BLavery/lib_oled96
# I2C address, width and height import from config.toml made by NurseJackass
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
#
# Default is 128x64 display on i2c address 0x3C
#
# Configure i2c address and dimensions in config.toml:
#
# ui.display.type = "i2coled"
# ui.display.i2c_addr = 0x3C
# ui.display.width = 128
# ui.display.height = 64
#
class I2COled(DisplayImpl):
def __init__(self, config):
self._config = config['ui']['display']
super(I2COled, self).__init__(config, 'i2coled')
def layout(self):
fonts.setup(8, 8, 8, 10, 10, 8)
self._layout['width'] = self._config['width'] if 'width' in self._config else 128
self._layout['height'] = self._config['height'] if 'height' in self._config else 64
self._layout['face'] = (0, 30)
self._layout['name'] = (0, 10)
self._layout['channel'] = (72, 10)
self._layout['aps'] = (0, 0)
self._layout['uptime'] = (87, 0)
self._layout['line1'] = [0, 9, 128, 9]
self._layout['line2'] = [0, 54, 128, 54]
self._layout['friend_face'] = (0, 41)
self._layout['friend_name'] = (40, 43)
self._layout['shakes'] = (0, 55)
self._layout['mode'] = (107, 10)
self._layout['status'] = {
'pos': (37, 19),
'font': fonts.status_font(fonts.Small),
'max': 18
}
return self._layout
def initialize(self):
i2caddr = self._config['i2c_addr'] if 'i2c_addr' in self._config else 0x3C
width = self._config['width'] if 'width' in self._config else 128
height = self._config['height'] if 'height' in self._config else 64
logging.info("initializing %dx%d I2C Oled Display on address 0x%X" % (width, height, i2caddr))
from pwnagotchi.ui.hw.libs.i2coled.epd import EPD
self._display = EPD(address=i2caddr, width=width, height=height)
self._display.Init()
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,7 @@ class Inky(DisplayImpl):
logging.info("THIS MAY BE POTENTIALLY DANGEROUS. NO WARRANTY IS PROVIDED")
logging.info("USE THIS DISPLAY IN THIS MODE AT YOUR OWN RISK")
from pwnagotchi.ui.hw.libs.inkyphat.inkyphatfast import InkyPHATFast
from pwnagotchi.ui.hw.libs.pimoroni.inkyphat.inkyphatfast import InkyPHATFast
self._display = InkyPHATFast('black')
self._display.set_border(InkyPHATFast.BLACK)
elif self.config['color'] == 'auto':
@ -84,4 +84,5 @@ class Inky(DisplayImpl):
logging.exception("error while rendering on inky")
def clear(self):
self._display.Clear()
pass
# self._display.clear()

View File

@ -0,0 +1,56 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class InkyV2(DisplayImpl):
def __init__(self, config):
super(InkyV2, self).__init__(config, 'inkyv2')
def layout(self):
fonts.setup(10, 8, 10, 28, 25, 9)
self._layout['width'] = 212
self._layout['height'] = 104
self._layout['face'] = (0, 37)
self._layout['name'] = (5, 18)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (30, 0)
self._layout['uptime'] = (147, 0)
self._layout['line1'] = [0, 12, 212, 12]
self._layout['line2'] = [0, 92, 212, 92]
self._layout['friend_face'] = (0, 76)
self._layout['friend_name'] = (40, 78)
self._layout['shakes'] = (0, 93)
self._layout['mode'] = (187, 93)
self._layout['status'] = {
'pos': (102, 18),
'font': fonts.status_font(fonts.Small),
'max': 20
}
return self._layout
def initialize(self):
logging.info("initializing inky v2 display")
from pwnagotchi.ui.hw.libs.pimoroni.inkyphatv2.inkyv2 import InkyPHAT
self._display = InkyPHAT()
self._display.set_border(InkyPHAT.BLACK)
def render(self, canvas):
display_colors = 2
img_buffer = canvas.convert('RGB').convert('P', palette=1, colors=display_colors)
img_buffer.putpalette([
255, 255, 255, # index 0 is white
0, 0, 0 # index 1 is black
])
self._display.set_image(img_buffer)
try:
self._display.show()
except:
logging.exception("error while rendering on inky v2")
def clear(self):
self._display.Clear()

View File

@ -33,7 +33,7 @@ class LcdHat(DisplayImpl):
def initialize(self):
logging.info("initializing lcdhat display")
from pwnagotchi.ui.hw.libs.waveshare.lcdhat.epd import EPD
from pwnagotchi.ui.hw.libs.waveshare.lcd.lcdhat.epd import EPD
self._display = EPD()
self._display.init()
self._display.clear()

View File

@ -0,0 +1,132 @@
# /*****************************************************************************
# * | File : epdconfig.py
# * | Author : Waveshare team
# * | Function : Hardware underlying interface
# * | Info :
# *----------------
# * | This version: V1.2
# * | Date : 2022-10-29
# * | Info :
# ******************************************************************************
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import os
import logging
import sys
import time
import subprocess
logger = logging.getLogger(__name__)
class RaspberryPi:
# Pin definition
RST_PIN = 27
DC_PIN = 22
CS_PIN = 8
BUSY_PIN = 17
PWR_PIN = 18
def __init__(self):
import spidev
import gpiozero
self.SPI = spidev.SpiDev()
self.GPIO_RST_PIN = gpiozero.LED(self.RST_PIN)
self.GPIO_DC_PIN = gpiozero.LED(self.DC_PIN)
self.GPIO_CS_PIN = gpiozero.LED(self.CS_PIN)
self.GPIO_PWR_PIN = gpiozero.LED(self.PWR_PIN)
self.GPIO_BUSY_PIN = gpiozero.Button(self.BUSY_PIN, pull_up=False)
def digital_write(self, pin, value):
if pin == self.RST_PIN:
if value:
self.GPIO_RST_PIN.on()
else:
self.GPIO_RST_PIN.off()
elif pin == self.DC_PIN:
if value:
self.GPIO_DC_PIN.on()
else:
self.GPIO_DC_PIN.off()
elif pin == self.CS_PIN:
if value:
self.GPIO_CS_PIN.on()
else:
self.GPIO_CS_PIN.off()
elif pin == self.PWR_PIN:
if value:
self.GPIO_PWR_PIN.on()
else:
self.GPIO_PWR_PIN.off()
def digital_read(self, pin):
if pin == self.BUSY_PIN:
return self.GPIO_BUSY_PIN.value
elif pin == self.RST_PIN:
return self.RST_PIN.value
elif pin == self.DC_PIN:
return self.DC_PIN.value
elif pin == self.CS_PIN:
return self.CS_PIN.value
elif pin == self.PWR_PIN:
return self.PWR_PIN.value
def delay_ms(self, delaytime):
time.sleep(delaytime / 1000.0)
def spi_writebyte(self, data):
self.SPI.writebytes(data)
def spi_writebyte2(self, data):
self.SPI.writebytes2(data)
def module_init(self):
self.GPIO_PWR_PIN.on()
# SPI device, bus = 0, device = 0
self.SPI.open(0, 0)
self.SPI.max_speed_hz = 4000000
self.SPI.mode = 0b00
return 0
def module_exit(self, cleanup=False):
logger.debug("spi end")
self.SPI.close()
self.GPIO_RST_PIN.off()
self.GPIO_DC_PIN.off()
self.GPIO_PWR_PIN.off()
logger.debug("close 5V, Module enters 0 power consumption ...")
if cleanup:
self.GPIO_RST_PIN.close()
self.GPIO_DC_PIN.close()
self.GPIO_CS_PIN.close()
self.GPIO_PWR_PIN.close()
self.GPIO_BUSY_PIN.close()
implementation = RaspberryPi()
for func in [x for x in dir(implementation) if not x.startswith('_')]:
setattr(sys.modules[__name__], func, getattr(implementation, func))
### END OF FILE ###

View File

@ -0,0 +1,362 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation 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
# furnished 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 FOR 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.
# Modified for Pwnagotchi by RasTacsko
# Based on ST7899 driver for pimoroni displayhatmini by Do-Ki
import numbers
import time
import numpy as np
from PIL import Image
from PIL import ImageDraw
import spidev
import RPi.GPIO as GPIO
__version__ = '0.0.1'
# Constants for interacting with display registers.
ILI9341_TFTWIDTH = 320
ILI9341_TFTHEIGHT = 240
ILI9341_NOP = 0x00
ILI9341_SWRESET = 0x01
ILI9341_RDDID = 0x04
ILI9341_RDDST = 0x09
ILI9341_SLPIN = 0x10
ILI9341_SLPOUT = 0x11
ILI9341_PTLON = 0x12
ILI9341_NORON = 0x13
ILI9341_RDMODE = 0x0A
ILI9341_RDMADCTL = 0x0B
ILI9341_RDPIXFMT = 0x0C
ILI9341_RDIMGFMT = 0x0A
ILI9341_RDSELFDIAG = 0x0F
ILI9341_INVOFF = 0x20
ILI9341_INVON = 0x21
ILI9341_GAMMASET = 0x26
ILI9341_DISPOFF = 0x28
ILI9341_DISPON = 0x29
ILI9341_CASET = 0x2A
ILI9341_PASET = 0x2B
ILI9341_RAMWR = 0x2C
ILI9341_RAMRD = 0x2E
ILI9341_PTLAR = 0x30
ILI9341_MADCTL = 0x36
ILI9341_PIXFMT = 0x3A
ILI9341_FRMCTR1 = 0xB1
ILI9341_FRMCTR2 = 0xB2
ILI9341_FRMCTR3 = 0xB3
ILI9341_INVCTR = 0xB4
ILI9341_DFUNCTR = 0xB6
ILI9341_PWCTR1 = 0xC0
ILI9341_PWCTR2 = 0xC1
ILI9341_PWCTR3 = 0xC2
ILI9341_PWCTR4 = 0xC3
ILI9341_PWCTR5 = 0xC4
ILI9341_VMCTR1 = 0xC5
ILI9341_VMCTR2 = 0xC7
ILI9341_RDID1 = 0xDA
ILI9341_RDID2 = 0xDB
ILI9341_RDID3 = 0xDC
ILI9341_RDID4 = 0xDD
ILI9341_GMCTRP1 = 0xE0
ILI9341_GMCTRN1 = 0xE1
ILI9341_PWCTR6 = 0xFC
class ILI9341(object):
"""Representation of an ILI9341 TFT LCD."""
def __init__(self, port, cs, dc, backlight, rst=None,
width=ILI9341_TFTWIDTH, height=ILI9341_TFTHEIGHT,
rotation=270, invert=False, spi_speed_hz=64000000,
offset_left=0, offset_top=0):
"""Create an instance of the display using SPI communication.
Must provide the GPIO pin number for the D/C pin and the SPI driver.
Can optionally provide the GPIO pin number for the reset pin as the rst parameter.
:param port: SPI port number -> 0
:param cs: SPI chip-select number (0 or 1 for BCM) -> 1
:param backlight: Pin for controlling backlight -> 18
:param rst: Reset pin for ILI9341 -> 24?
:param width: Width of display connected to ILI9341 -> 240
:param height: Height of display connected to ILI9341 -> 320
:param rotation: Rotation of display connected to ILI9341
:param invert: Invert display
:param spi_speed_hz: SPI speed (in Hz)
"""
if rotation not in [0, 90, 180, 270]:
raise ValueError("Invalid rotation {}".format(rotation))
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
self._spi = spidev.SpiDev(port, cs)
self._spi.mode = 0
self._spi.lsbfirst = False
self._spi.max_speed_hz = spi_speed_hz
self._dc = dc
self._rst = rst
self._width = width
self._height = height
self._rotation = rotation
self._invert = invert
self._offset_left = offset_left
self._offset_top = offset_top
# Set DC as output.
GPIO.setup(dc, GPIO.OUT)
# Setup backlight as output (if provided).
self._backlight = backlight
if backlight is not None:
GPIO.setup(backlight, GPIO.OUT)
GPIO.output(backlight, GPIO.LOW)
time.sleep(0.05)
GPIO.output(backlight, GPIO.HIGH)
# Setup reset as output (if provided).
if rst is not None:
GPIO.setup(self._rst, GPIO.OUT)
self.reset()
# Create an image buffer.
self.buffer = Image.new('RGB', (width, height))
self._init()
def send(self, data, is_data=True, chunk_size=4096):
"""Write a byte or array of bytes to the display. Is_data parameter
controls if byte should be interpreted as display data (True) or command
data (False). Chunk_size is an optional size of bytes to write in a
single SPI transaction, with a default of 4096.
"""
# Set DC low for command, high for data.
GPIO.output(self._dc, is_data)
# Convert scalar argument to list so either can be passed as parameter.
if isinstance(data, numbers.Number):
data = [data & 0xFF]
# Write data a chunk at a time.
for start in range(0, len(data), chunk_size):
end = min(start+chunk_size, len(data))
self._spi.xfer(data[start:end])
def set_backlight(self, value):
"""Set the backlight on/off."""
if self._backlight is not None:
GPIO.output(self._backlight, value)
@property
def width(self):
return self._width if self._rotation == 0 or self._rotation == 180 else self._height
@property
def height(self):
return self._height if self._rotation == 0 or self._rotation == 180 else self._width
def command(self, data):
"""Write a byte or array of bytes to the display as command data."""
self.send(data, False)
def data(self, data):
"""Write a byte or array of bytes to the display as display data."""
self.send(data, True)
def reset(self):
"""Reset the display, if reset pin is connected."""
if self._rst is not None:
GPIO.output(self._rst, 1)
time.sleep(0.005)
GPIO.output(self._rst, 0)
time.sleep(0.02)
GPIO.output(self._rst, 1)
time.sleep(0.150)
def _init(self):
# Initialize the display. Broken out as a separate function so it can
# be overridden by other displays in the future.
self.command(0xEF)
self.data(0x03)
self.data(0x80)
self.data(0x02)
self.command(0xCF)
self.data(0x00)
self.data(0XC1)
self.data(0X30)
self.command(0xED)
self.data(0x64)
self.data(0x03)
self.data(0X12)
self.data(0X81)
self.command(0xE8)
self.data(0x85)
self.data(0x00)
self.data(0x78)
self.command(0xCB)
self.data(0x39)
self.data(0x2C)
self.data(0x00)
self.data(0x34)
self.data(0x02)
self.command(0xF7)
self.data(0x20)
self.command(0xEA)
self.data(0x00)
self.data(0x00)
self.command(ILI9341_PWCTR1) # Power control
self.data(0x23) # VRH[5:0]
self.command(ILI9341_PWCTR2) # Power control
self.data(0x10) # SAP[2:0];BT[3:0]
self.command(ILI9341_VMCTR1) # VCM control
self.data(0x3e)
self.data(0x28)
self.command(ILI9341_VMCTR2) # VCM control2
self.data(0x86) # --
self.command(ILI9341_MADCTL) # Memory Access Control
self.data(0x48)
self.command(ILI9341_PIXFMT)
self.data(0x55)
self.command(ILI9341_FRMCTR1)
self.data(0x00)
self.data(0x18)
self.command(ILI9341_DFUNCTR) # Display Function Control
self.data(0x08)
self.data(0x82)
self.data(0x27)
self.command(0xF2) # 3Gamma Function Disable
self.data(0x00)
self.command(ILI9341_GAMMASET) # Gamma curve selected
self.data(0x01)
self.command(ILI9341_GMCTRP1) # Set Gamma
self.data(0x0F)
self.data(0x31)
self.data(0x2B)
self.data(0x0C)
self.data(0x0E)
self.data(0x08)
self.data(0x4E)
self.data(0xF1)
self.data(0x37)
self.data(0x07)
self.data(0x10)
self.data(0x03)
self.data(0x0E)
self.data(0x09)
self.data(0x00)
self.command(ILI9341_GMCTRN1) # Set Gamma
self.data(0x00)
self.data(0x0E)
self.data(0x14)
self.data(0x03)
self.data(0x11)
self.data(0x07)
self.data(0x31)
self.data(0xC1)
self.data(0x48)
self.data(0x08)
self.data(0x0F)
self.data(0x0C)
self.data(0x31)
self.data(0x36)
self.data(0x0F)
if self._invert:
self.command(ILI9341_INVON) # Invert display
else:
self.command(ILI9341_INVOFF) # Don't invert display
self.command(ILI9341_SLPOUT) # Exit Sleep
time.sleep(0.120)
self.command(ILI9341_DISPON) # Display on
def begin(self):
"""Set up the display deprecated.
Included in __init__. """
pass
def set_window(self, x0=0, y0=0, x1=None, y1=None):
"""Set the pixel address window for proceeding drawing commands. x0 and
x1 should define the minimum and maximum x pixel bounds. y0 and y1
should define the minimum and maximum y pixel bound. If no parameters
are specified the default will be to update the entire display from 0,0
to 239,319.
"""
if x1 is None:
x1 = self.width-1
if y1 is None:
y1 = self.height-1
self.command(ILI9341_CASET) # Column addr set
self.data(x0 >> 8)
self.data(x0 & 0xFF) # XSTART
self.data(x1 >> 8)
self.data(x1 & 0xFF) # XEND
self.command(ILI9341_PASET) # Row addr set
self.data(y0 >> 8)
self.data(y0 & 0xFF) # YSTART
self.data(y1 >> 8)
self.data(y1 & 0xFF) # YEND
self.command(ILI9341_RAMWR) # write to RAM
def display(self, image):
"""Write the provided image to the hardware.
:param image: Should be RGB format and the same dimensions as the display hardware.
"""
# Set address bounds to entire display.
self.set_window()
# Convert image to 16bit RGB565 format and
# flatten into bytes.
pixelbytes = self.image_to_data(image, self._rotation)
# Write data to hardware.
for i in range(0, len(pixelbytes), 4096):
self.data(pixelbytes[i:i + 4096])
def image_to_data(self, image, rotation=0):
if not isinstance(image, np.ndarray):
image = np.array(image.convert('RGB'))
# Rotate the image
pb = np.rot90(image, rotation // 90).astype('uint16')
# Mask and shift the 888 RGB into 565 RGB
red = (pb[..., [0]] & 0xf8) << 8
green = (pb[..., [1]] & 0xfc) << 3
blue = (pb[..., [2]] & 0xf8) >> 3
# Stick 'em together
result = red | green | blue
# Output the raw bytes
return result.byteswap().tobytes()

View File

@ -0,0 +1,360 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation 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
# furnished 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 FOR 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 numbers
import time
import numpy as np
import spidev
import RPi.GPIO as GPIO
__version__ = '0.0.4'
BG_SPI_CS_BACK = 0
BG_SPI_CS_FRONT = 1
SPI_CLOCK_HZ = 16000000
ST7789_NOP = 0x00
ST7789_SWRESET = 0x01
ST7789_RDDID = 0x04
ST7789_RDDST = 0x09
ST7789_SLPIN = 0x10
ST7789_SLPOUT = 0x11
ST7789_PTLON = 0x12
ST7789_NORON = 0x13
ST7789_INVOFF = 0x20
ST7789_INVON = 0x21
ST7789_DISPOFF = 0x28
ST7789_DISPON = 0x29
ST7789_CASET = 0x2A
ST7789_RASET = 0x2B
ST7789_RAMWR = 0x2C
ST7789_RAMRD = 0x2E
ST7789_PTLAR = 0x30
ST7789_MADCTL = 0x36
ST7789_COLMOD = 0x3A
ST7789_FRMCTR1 = 0xB1
ST7789_FRMCTR2 = 0xB2
ST7789_FRMCTR3 = 0xB3
ST7789_INVCTR = 0xB4
ST7789_DISSET5 = 0xB6
ST7789_GCTRL = 0xB7
ST7789_GTADJ = 0xB8
ST7789_VCOMS = 0xBB
ST7789_LCMCTRL = 0xC0
ST7789_IDSET = 0xC1
ST7789_VDVVRHEN = 0xC2
ST7789_VRHS = 0xC3
ST7789_VDVS = 0xC4
ST7789_VMCTR1 = 0xC5
ST7789_FRCTRL2 = 0xC6
ST7789_CABCCTRL = 0xC7
ST7789_RDID1 = 0xDA
ST7789_RDID2 = 0xDB
ST7789_RDID3 = 0xDC
ST7789_RDID4 = 0xDD
ST7789_GMCTRP1 = 0xE0
ST7789_GMCTRN1 = 0xE1
ST7789_PWCTR6 = 0xFC
class ST7789(object):
"""Representation of an ST7789 TFT LCD."""
def __init__(self, port, cs, dc, backlight, rst=None, width=240,
height=240, rotation=90, invert=True, spi_speed_hz=60 * 1000 * 1000,
offset_left=0,
offset_top=0):
"""Create an instance of the display using SPI communication.
Must provide the GPIO pin number for the D/C pin and the SPI driver.
Can optionally provide the GPIO pin number for the reset pin as the rst parameter.
:param port: SPI port number
:param cs: SPI chip-select number (0 or 1 for BCM
:param backlight: Pin for controlling backlight
:param rst: Reset pin for ST7789
:param width: Width of display connected to ST7789
:param height: Height of display connected to ST7789
:param rotation: Rotation of display connected to ST7789
:param invert: Invert display
:param spi_speed_hz: SPI speed (in Hz)
"""
if rotation not in [0, 90, 180, 270]:
raise ValueError("Invalid rotation {}".format(rotation))
if width != height and rotation in [90, 270]:
raise ValueError("Invalid rotation {} for {}x{} resolution".format(rotation, width, height))
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
self._spi = spidev.SpiDev(port, cs)
self._spi.mode = 0
self._spi.lsbfirst = False
self._spi.max_speed_hz = spi_speed_hz
self._dc = dc
self._rst = rst
self._width = width
self._height = height
self._rotation = rotation
self._invert = invert
self._offset_left = offset_left
self._offset_top = offset_top
# Set DC as output.
GPIO.setup(dc, GPIO.OUT)
# Setup backlight as output (if provided).
self._backlight = backlight
if backlight is not None:
GPIO.setup(backlight, GPIO.OUT)
GPIO.output(backlight, GPIO.LOW)
time.sleep(0.1)
GPIO.output(backlight, GPIO.HIGH)
# Setup reset as output (if provided).
if rst is not None:
GPIO.setup(self._rst, GPIO.OUT)
self.reset()
self._init()
def send(self, data, is_data=True, chunk_size=4096):
"""Write a byte or array of bytes to the display. Is_data parameter
controls if byte should be interpreted as display data (True) or command
data (False). Chunk_size is an optional size of bytes to write in a
single SPI transaction, with a default of 4096.
"""
# Set DC low for command, high for data.
GPIO.output(self._dc, is_data)
# Convert scalar argument to list so either can be passed as parameter.
if isinstance(data, numbers.Number):
data = [data & 0xFF]
# Write data a chunk at a time.
for start in range(0, len(data), chunk_size):
end = min(start + chunk_size, len(data))
self._spi.xfer(data[start:end])
def set_backlight(self, value):
"""Set the backlight on/off."""
if self._backlight is not None:
GPIO.output(self._backlight, value)
@property
def width(self):
return self._width if self._rotation == 0 or self._rotation == 180 else self._height
@property
def height(self):
return self._height if self._rotation == 0 or self._rotation == 180 else self._width
def command(self, data):
"""Write a byte or array of bytes to the display as command data."""
self.send(data, False)
def data(self, data):
"""Write a byte or array of bytes to the display as display data."""
self.send(data, True)
def reset(self):
"""Reset the display, if reset pin is connected."""
if self._rst is not None:
GPIO.output(self._rst, 1)
time.sleep(0.500)
GPIO.output(self._rst, 0)
time.sleep(0.500)
GPIO.output(self._rst, 1)
time.sleep(0.500)
def _init(self):
# Initialize the display.
self.command(ST7789_SWRESET) # Software reset
time.sleep(0.150) # delay 150 ms
self.command(ST7789_MADCTL)
self.data(0x70)
self.command(ST7789_FRMCTR2) # Frame rate ctrl - idle mode
self.data(0x0C)
self.data(0x0C)
self.data(0x00)
self.data(0x33)
self.data(0x33)
self.command(ST7789_COLMOD)
self.data(0x05)
self.command(ST7789_GCTRL)
self.data(0x14)
self.command(ST7789_VCOMS)
self.data(0x37)
self.command(ST7789_LCMCTRL) # Power control
self.data(0x2C)
self.command(ST7789_VDVVRHEN) # Power control
self.data(0x01)
self.command(ST7789_VRHS) # Power control
self.data(0x12)
self.command(ST7789_VDVS) # Power control
self.data(0x20)
self.command(0xD0)
self.data(0xA4)
self.data(0xA1)
self.command(ST7789_FRCTRL2)
self.data(0x0F)
self.command(ST7789_GMCTRP1) # Set Gamma
self.data(0xD0)
self.data(0x04)
self.data(0x0D)
self.data(0x11)
self.data(0x13)
self.data(0x2B)
self.data(0x3F)
self.data(0x54)
self.data(0x4C)
self.data(0x18)
self.data(0x0D)
self.data(0x0B)
self.data(0x1F)
self.data(0x23)
self.command(ST7789_GMCTRN1) # Set Gamma
self.data(0xD0)
self.data(0x04)
self.data(0x0C)
self.data(0x11)
self.data(0x13)
self.data(0x2C)
self.data(0x3F)
self.data(0x44)
self.data(0x51)
self.data(0x2F)
self.data(0x1F)
self.data(0x1F)
self.data(0x20)
self.data(0x23)
if self._invert:
self.command(ST7789_INVON) # Invert display
else:
self.command(ST7789_INVOFF) # Don't invert display
self.command(ST7789_SLPOUT)
self.command(ST7789_DISPON) # Display on
time.sleep(0.100) # 100 ms
def begin(self):
"""Set up the display
Deprecated. Included in __init__.
"""
pass
def set_window(self, x0=0, y0=0, x1=None, y1=None):
"""Set the pixel address window for proceeding drawing commands. x0 and
x1 should define the minimum and maximum x pixel bounds. y0 and y1
should define the minimum and maximum y pixel bound. If no parameters
are specified the default will be to update the entire display from 0,0
to width-1,height-1.
"""
if x1 is None:
x1 = self._width - 1
if y1 is None:
y1 = self._height - 1
y0 += self._offset_top
y1 += self._offset_top
x0 += self._offset_left
x1 += self._offset_left
self.command(ST7789_CASET) # Column addr set
self.data(x0 >> 8)
self.data(x0 & 0xFF) # XSTART
self.data(x1 >> 8)
self.data(x1 & 0xFF) # XEND
self.command(ST7789_RASET) # Row addr set
self.data(y0 >> 8)
self.data(y0 & 0xFF) # YSTART
self.data(y1 >> 8)
self.data(y1 & 0xFF) # YEND
self.command(ST7789_RAMWR) # write to RAM
def display(self, image):
"""Write the provided image to the hardware.
:param image: Should be RGB format and the same dimensions as the display hardware.
"""
# Set address bounds to entire display.
self.set_window()
# Convert image to 16bit RGB565 format and
# flatten into bytes.
pixelbytes = self.image_to_data(image, self._rotation)
# Write data to hardware.
for i in range(0, len(pixelbytes), 4096):
self.data(pixelbytes[i:i + 4096])
def image_to_data(self, image, rotation=0):
if not isinstance(image, np.ndarray):
image = np.array(image.convert('RGB'))
# Rotate the image
pb = np.rot90(image, rotation // 90).astype('uint16')
# Mask and shift the 888 RGB into 565 RGB
red = (pb[..., [0]] & 0xf8) << 8
green = (pb[..., [1]] & 0xfc) << 3
blue = (pb[..., [2]] & 0xf8) >> 3
# Stick 'em together
result = red | green | blue
# Output the raw bytes
return result.byteswap().tobytes()

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