124 Commits

Author SHA1 Message Date
Simone Margaritelli
ff91f9b2f8 releasing v1.5.5 2021-04-18 19:05:49 +02:00
Simone Margaritelli
07b70dd0ee fix: updated travis credentials 2021-04-18 19:04:34 +02:00
Simone Margaritelli
c12b319287 releasing v1.5.4 2021-04-18 18:23:15 +02:00
Simone Margaritelli
712d0142a1 Merge pull request #919 from crahan/gps_config
Add configuration options to gps.py (position, vertical line spacing)
2021-04-18 17:43:15 +02:00
Thomas Bouve
9ba55b7f77 Merge updated 'master' into gps_config 2021-04-18 17:22:00 +02:00
Simone Margaritelli
abc16ce973 misc: updated bettercap and pwngrid versions in builder script 2021-04-18 17:17:31 +02:00
Simone Margaritelli
1318275b36 fix: using better symbols for ups charging status 2021-04-18 17:11:04 +02:00
Simone Margaritelli
297d9cd3b9 Merge pull request #905 from samuelkf/ups-lite-charging-status
Display UPS-Lite charging status
2021-04-18 17:08:37 +02:00
Simone Margaritelli
f164b8bb26 new: added new faces for uploading status 2021-04-18 17:03:21 +02:00
Simone Margaritelli
5c3b21f537 fix: recompiled localization files 2021-04-18 16:22:29 +02:00
Simone Margaritelli
6325428218 Merge pull request #903 from serginator/task/improve-es-lang
Improved Spanish translation
2021-04-18 16:00:44 +02:00
Simone Margaritelli
7d35f5cdc0 Merge branch 'master' into task/improve-es-lang 2021-04-18 16:00:37 +02:00
Simone Margaritelli
3fd3ac3c01 Merge pull request #911 from MannyLama/master
Improved Dutch translation
2021-04-18 15:59:13 +02:00
Simone Margaritelli
c1d3528ff7 Merge pull request #940 from ShaqKSmith/patch-1
Create voice.po
2021-04-18 15:58:59 +02:00
Simone Margaritelli
37fc65f834 Merge pull request #947 from theusu5k/patch-1
Update voice.po
2021-04-18 15:58:43 +02:00
Simone Margaritelli
80095edf4b Merge pull request #948 from MatthewNunu/master
Added Afrikaans translation
2021-04-18 15:56:45 +02:00
Simone Margaritelli
7e3e74635a Merge pull request #983 from xhrzg2017/patch-1
Update voice.po
2021-04-18 15:53:48 +02:00
Simone Margaritelli
6b3d9042fd Merge branch 'master' of github.com:evilsocket/pwnagotchi 2021-04-18 15:43:26 +02:00
Simone Margaritelli
4441ae852c misc: using stork for releases 2021-04-18 15:43:15 +02:00
Simone Margaritelli
edc0551e0a Merge pull request #933 from ajaswa/902/stop-using-uart-gpio-for-waveshare
swap out header file for one that doesn't tie up gpio pins 14 and 15 for waveshare_2
2021-04-18 15:16:23 +02:00
Simone Margaritelli
b6cf510f8e Merge pull request #876 from grokbeer/watchdog-reboot
Have watchdog plugin reboot system rather than restart application
2021-04-18 15:14:59 +02:00
Simone Margaritelli
7fb6be861a Merge pull request #909 from Tuinslak/patch-1
Make sure IPv4 works when eth0 is connected
2021-04-18 15:14:32 +02:00
Simone Margaritelli
f795598950 Merge pull request #918 from crahan/memtemp_gps_fixes
Add configuration options to memtemp.py (fields, position, vertical line spacing)
2021-04-18 15:13:23 +02:00
Simone Margaritelli
b0efd52961 Merge pull request #929 from HeroCC/master
Add donate option to Wigle plugin
2021-04-18 15:11:51 +02:00
Simone Margaritelli
38dfccb7c2 Merge pull request #953 from centraliota/master
Update wpa-sec.py logging
2021-04-18 15:11:10 +02:00
Simone Margaritelli
f912883f6e Merge pull request #967 from Xeyler/gps-bugfix
Fix display bug in gps module
2021-04-18 15:10:59 +02:00
Simone Margaritelli
661f26dedf Merge pull request #985 from Gadgetoid/patch-inky-ssd1608
Add support for SSD1608 variant 250x122 pixel Inky pHATs
2021-04-18 15:10:05 +02:00
Simone Margaritelli
d6c7a73f39 fix: fixed whl file urls 2021-04-18 15:07:16 +02:00
Simone Margaritelli
10f274dab7 fix: updated build scripts with latest packer version 2021-04-18 14:48:39 +02:00
Simone Margaritelli
decbeaccb1 Merge pull request #969 from jonspraggins/patch-1
Add Uploading 'Faces'
2021-04-18 13:18:43 +02:00
Philip Howard
34c2c8a06e Add support for SSD1608 variant 250x122 pixel Inky pHATs
Running `python3 -m inky.eeprom` should show display variant. Variants 10, 11 and 12 are SSD1608 based.

This patch adds support for auto-detecting and using them when "ui.display.color" is set to "auto".

Fixes #980 and #941

Signed-off-by: Philip Howard <phil@gadgetoid.com>
2021-04-09 10:23:59 +01:00
xhrzg2017
88a15528d6 Update voice.po 2021-04-09 10:06:58 +08:00
Jon Spraggins
0fd09878db Update faces.py
Added upload faces.
2021-03-18 11:47:25 -04:00
Brigham Campbell
c472e60615 Fix bug in gps module
Fix a bug caused by using a function in a conditional instead of the
return value of the function itself.

Signed-off-by: Brigham Campbell <me@brighamcampbell.com>
2021-03-14 11:46:04 -06:00
Simone Margaritelli
0704541dd1 Merge pull request #957 from FoxFromDarkness66/patch-1
Fix broken link in README
2021-03-14 16:29:03 +01:00
FoxFromDarkness66
ea061d473b Fix broken link in README
Default file is .toml now, not .yml
2021-01-25 20:00:21 +03:00
centraliota
6430a40847 Update wpa-sec.py logging
Added logging.info message for plugin loading, to keep consistent with behaviour of other plugins.
2020-11-17 18:01:52 -07:00
MatthewNunu
ba13b12593 added afrikaans lang 2020-11-12 12:05:29 +02:00
theusu5k
819be761ed Update voice.po
Some words were translated too literal.
Some menor corrections
2020-11-09 20:25:22 +01:00
ShaqKSmith
f701390d5a Create voice.po 2020-11-02 15:01:12 +08:00
Andrew Jaswa
2ddf040fac swap out header file for one that doesn't tie up gpio pins 14 and 15 for waveshare_2
Signed-off-by: Andrew Jaswa <andrew.jaswa@bonus.ly>
2020-10-09 21:13:31 -06:00
HeroCC
3bd9cd4f18 Tweak Wigle plugin
Adds a new 'donate' option to allow Wigle to include data for commercial use.

Also changes data header to reflect this is a pwnagotchi plugin.

Signed-off-by: HeroCC <herocc@herocc.com>
2020-10-03 17:06:29 +02:00
Thomas Bouve
5c6d8dc807 Adjust x coordinate for longer long field. 2020-09-20 22:12:54 +02:00
Thomas Bouve
c535ffd40e Fix version bump. 2020-09-20 21:24:33 +02:00
Thomas Bouve
fb7217b0fa Add configuration options and cleanup
- added support for configuring the plugin position and vertical line spacing
- fix default values for some displays which appeared to be incorrect
- change `ui.is_dfrobot_v2` to a function (to match other display entries)
- version bump to 1.0.2
2020-09-20 21:10:10 +02:00
Thomas Bouve
7da3cc5565 Revert. Changes are handled in a separate branch 2020-09-20 20:25:25 +02:00
Thomas Bouve
e72e2292a8 Update guessed coordinates based on other values. 2020-09-20 20:22:39 +02:00
Thomas Bouve
0f8643258e gps config updates 2020-09-20 11:14:00 +02:00
Thomas Bouve
1a0083eb38 Fix correct x positioning
Align fields right when less than 3 fields are shown in horizontal mode, using a 5-pixel character width with a 5-character width for each field (i.e. 25 pixels per field).
2020-09-20 01:43:52 +02:00
Thomas Bouve
72878454f9 Removed debug line. 2020-09-20 00:29:33 +02:00
Thomas Bouve
1c4df7a1c4 Additional comments 2020-09-20 00:27:26 +02:00
Thomas Bouve
c124a97514 Changelog update. 2020-09-20 00:25:52 +02:00
Thomas Bouve
b886b4e673 Added more configurable fields 2020-09-19 23:43:24 +02:00
Thomas Bouve
6d0e295276 Version bumps 2020-09-18 02:12:36 +02:00
Thomas Bouve
1aea0b95b1 Small gps fixes + memtemp rework 2020-09-18 02:11:37 +02:00
MannyLama
fddee8708e Updated dutch & fixed typos 2020-09-11 15:43:44 +02:00
MannyLama
ef4fbd96cc Updated dutch & fixed typos 2020-09-11 15:42:47 +02:00
Yeri Tiete
552df65422 Make sure IPv4 works when eth0 is connected
Resolves an issue where IPv4 traffic would not work when eth0 is connected (i.e. usb-ethernet adapter over OTG). The static route for usb0 precedes the DHCP route for eth0. Only IPv6 traffic would work, but not IPv4 until the metric for usb0 is increase and thus lowered in priority. If no eth0 is plugged in, internet works fine over usb0.
2020-09-04 17:49:41 +08:00
Sam Keating-Fry
2db8f143eb Display UPS-Lite charging status
Replace the '%' in the battery indicator with '' when the UPS-Lite is connected to an external power source.
Suggestions welcome for a different character as '' is a little hard to read on e-ink displays.
2020-08-29 14:40:44 +01:00
Sergio Ruiz
6111ee9d9d Improved Spanish translation
Signed-off-by: Sergio Ruiz <serginator@gmail.com>
2020-08-25 23:07:34 +02:00
Simone Margaritelli
713a14c504 Merge pull request #851 from MikeDawg/fixscripts
Fixing scripts - Make all scripts usage/help similar
2020-06-26 14:14:48 +02:00
Simone Margaritelli
2708fd032c Merge pull request #872 from grokbeer/shutdown-sync
Delaying fs sync to ensure shutdown message is logged to disk
2020-06-26 14:14:32 +02:00
Simone Margaritelli
f7cf4b3947 Merge pull request #874 from grokbeer/reboot-sync
Ensure fs is synced before reboot so logs aren't lost
2020-06-26 14:14:03 +02:00
Simone Margaritelli
1d312a727b Merge pull request #877 from grokbeer/fs-typo
Fix minor typo
2020-06-26 14:12:38 +02:00
Simone Margaritelli
dd3dbbe400 Merge pull request #882 from jacopotediosi/patch-1
Set maxZoom to max value
2020-06-26 14:12:15 +02:00
Simone Margaritelli
cfa034b555 Merge pull request #886 from elipsius/885/fix-2.7inch-waveshare-gps-display
Updated gps.py to include positioning for the waveshare 2.7 inch scre…
2020-06-26 14:11:17 +02:00
Simone Margaritelli
a0cd0d4936 Merge pull request #891 from voodoologic/master
If a step fails, abort script.
2020-06-26 14:10:52 +02:00
Simone Margaritelli
3e3fff298c Merge pull request #890 from BlackFrog1/laptop
Ignore python virtual environments
2020-06-26 14:10:33 +02:00
Doug
0b1c51dcc7 If a step fails, abort script. 2020-06-09 20:03:28 -07:00
BlackFrog1
8dd9a85615 quick ignore of python environments 2020-06-08 11:46:16 -04:00
Eric M Lipsius
929eac7bba Updated gps.py to include positioning for the waveshare 2.7 inch screen, tested on my hardware:
Raspberry Pi 3 /w waveshare 2.7 inch epaper hat, and USB GPS/GLONASS U-blox7

Signed-off-by: Eric M Lipsius <eric.lipsius@gmail.com>
2020-05-27 00:54:15 -04:00
Jacopo Tediosi
0f7870f770 Set maxZoom to max value
Why to limit zoom? As a user, I used sometimes to zoom so much.
2020-05-21 23:50:03 +02:00
grokbeer
37342c0685 Have watchdog plugin reboot system rather than restart application
Signed-off-by: grokbeer <1428380+grokbeer@users.noreply.github.com>
2020-05-08 23:17:12 +12:00
grokbeer
71514a97fa Delaying fs sync to ensure shutdown message is logged to disk
Signed-off-by: grokbeer <1428380+grokbeer@users.noreply.github.com>
2020-05-08 23:17:04 +12:00
grokbeer
03488819af Ensure fs is synced before reboot so logs aren't lost
Signed-off-by: grokbeer <1428380+grokbeer@users.noreply.github.com>
2020-05-08 23:16:54 +12:00
grokbeer
633b726bcd Fix minor typo
Signed-off-by: grokbeer <1428380+grokbeer@users.noreply.github.com>
2020-05-08 23:16:32 +12:00
Simone Margaritelli
05b235c38b Merge pull request #860 from misterf13/master
Display coordinates for dfrobotv2
2020-05-06 12:04:36 +02:00
Ferny G
840054f549 Display coordinates for dfrobotv2
Signed-off-by: misterf13 <fgarcia1337@gmail.com>
2020-04-20 22:40:39 -05:00
Simone Margaritelli
a3cf49244e Merge pull request #855 from dadav/develop
Bug fixes
2020-04-20 11:13:25 +02:00
dadav
e92751164f typo 2020-04-19 18:49:42 +02:00
dadav
c2f9860bc5 same as in the py file 2020-04-19 16:48:11 +02:00
dadav
f616871068 fix syntax 2020-04-19 16:46:34 +02:00
dadav
c726779f6e only go back 5 mins 2020-04-19 16:36:26 +02:00
dadav
a2e29d64d2 dont overwrite zips 2020-04-19 11:39:49 +02:00
dadav
bc84f22f7a fix ident 2020-04-19 11:37:48 +02:00
dadav
40d8d994d1 version++ 2020-04-19 11:34:39 +02:00
dadav
7ca5eee247 add custom repos 2020-04-19 11:26:18 +02:00
dadav
56c291d302 change treshhold to 5 2020-04-18 18:19:16 +02:00
dadav
1013e7dc19 make less verbose 2020-04-18 17:09:39 +02:00
dadav
ff4f5c672a move code out of ui update hook 2020-04-18 16:46:29 +02:00
dadav
d9d268ea81 fix bug 2020-04-18 13:53:42 +02:00
dadav
de62214970 add missing option 2020-04-18 13:30:43 +02:00
dadav
52cc413152 add check 2020-04-18 13:23:12 +02:00
dadav
716d5cd312 typo 2020-04-18 13:17:18 +02:00
dadav
e436dc8b8e fix 2020-04-18 13:02:42 +02:00
dadav
81db495f33 redirect to dashboard 2020-04-18 12:47:09 +02:00
dadav
7f8380c38a switch to static 2020-04-18 12:26:21 +02:00
Ferny G
8c2b4e2075 DFRobot V2 screen 2020-04-18 11:40:43 +02:00
dadav
2b17e5322b only kill if present 2020-04-18 11:29:54 +02:00
dadav
1be17b1f99 reboot if reload fails 2020-04-18 11:17:46 +02:00
dadav
2dee3987d4 add bash completion 2020-04-18 10:59:08 +02:00
dadav
eb76cc7023 fix location 2020-04-18 10:22:18 +02:00
dadav
35ea36ef33 auto redirect when decrypted 2020-04-18 10:09:20 +02:00
dadav
44e1e79245 scorp fix 2020-04-18 09:50:40 +02:00
dadav
6038f555fa fix 2020-04-18 00:02:25 +02:00
dadav
0b5a63a3d8 add missing var 2020-04-17 22:31:30 +02:00
dadav
430172e3dd remove button 2020-04-17 20:55:11 +02:00
dadav
fa87e03222 add max-lines 2020-04-17 20:18:12 +02:00
dadav
d1411ffa96 sort plugins 2020-04-17 16:45:04 +02:00
dadav
67b4747afa adjust cmd 2020-04-17 16:39:31 +02:00
dadav
311931c81d added watchdog 2020-04-17 16:32:24 +02:00
Simone Margaritelli
1f7bc60de6 Merge pull request #848 from Czechball/master
added Czech translation
2020-04-16 13:01:17 +02:00
Simone Margaritelli
57034d9fc6 Merge pull request #852 from dadav/develop
Various fixes
2020-04-16 13:01:08 +02:00
dadav
74fbf4da32 version++ 2020-04-16 10:31:59 +02:00
dadav
7ec20caf23 fix filter bug 2020-04-15 17:49:57 +02:00
dadav
568c5b020d aaaannnd even better 2020-04-15 17:19:41 +02:00
dadav
3965bdb554 performs better without it 2020-04-15 17:02:50 +02:00
dadav
585b208e9e support multiple passwords 2020-04-15 16:51:43 +02:00
dadav
e53bdc46a4 there is no logging to journald anymore 2020-04-15 13:19:01 +02:00
dadav
6805df858e basename should be dirname 2020-04-15 08:20:47 +02:00
dadav
8a07e822e6 breaks if never used 2020-04-14 22:20:42 +02:00
MikeDawg
5f7dd56ead Fixing scripts
Signed-off-by: MikeDawg <MikeDawg@gmail.com>
2020-04-14 14:08:16 -06:00
dadav
a808fd33c7 another toml fix in paradise 2020-04-14 21:53:17 +02:00
Czechball
68065d548a added Czech translation 2020-04-14 06:17:57 +02:00
90 changed files with 3034 additions and 439 deletions

9
.gitignore vendored
View File

@@ -18,3 +18,12 @@ pwnagotchi.egg-info
*backup*.tgz
*backup*.gz
.vscode
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

View File

@@ -9,7 +9,7 @@ env:
deploy:
provider: releases
api_key:
secure: vBUokTv94n8s65STUgTiD6I0Iy8KXbBRvQUrAof8XG+U4ZMsH5PmDTpS+wz+SaxI6o0PRkfyOiPVdARhiKAFnfatG3q9EHllMQwqRR2YIju51A3aCxgEJ5uWDoybwQdipERUMMYwUO/8XZaRRpwFD2bdQBFWkBtQyMcAkrEL8BXckwQQ531oDN2hK5gAiTllqsOswV2idwUlBRU9jOtStzff+UgUYsp/ZebsRodyOYkEB2Ev15yARo2HTXbyZ2icwHPtMbx5zmNUSRtxs9a4hfzaK3m6ctK8qLYYUdQvXub/ruuACapdw4Ez88LY1agTecbZhFYmJzv8oANH1e4VUI4owuHnZCpU6LRutS4wOhglrkOrGo6lSUlJeA+RtQjyjBugjej9DDtDyyIlRU1ZaBF3qWR9N5EXKuquf0olOfmUR67ap1NykE9VUpzkYjkoVRTiPs/e2onM/nRNOvAQcIt75FD13u+Y/DcYQ8r7KpMIu1HNdtbVx8gMeq76bRhP1YdDg2jm+DdJ21KWjf5QHsbyoXDfJzdKlCloLIlAU3EPJhMoXsnNzre0/FXeUl6dfteR1axNS6U7e/vKsQ9rlUFZWIQaeVPjfXmFKblNNVQ5uFrrsB/EGHcJl7IUx5fvcRT5hMMNwC660YxVkBXDbRb5fxMW5/+K0BOi9cP6en8=
secure: "Rj0QnEDv02UzjKaxHHxJ/Sdj50EOFIrsKShr27GtVNSwHmNKxQuwlh31T0DQdif4wzcDtUZ5XWxr85vLkKJt1L8anb7Tb63qlu7Da69C+upSUow1uJkjsiScbMPqwHpwDIkkcUIbPsdbowI7qiNhRBD7nF8yyLPC5YLiU0cXKuGh4+Q5xdIlL3P7p8jm0919Y+olglzAZj0iNR/QxGOb+laNH8xi0oUsIPi5V0ZFfO/W/sm+nks9ki5nolfd1ML1gcbOD7uKuxIMTUrpDLl4p2Jx9IVQW+G2/tkmNLbP5Ga65NxQcfABQDYY3tCD8PsmFK9PEwa4cMbGJjqlo3yR7P21J5Aj3rK+L4KDntOvwem3Z3Y/v2JlQZn+gelhNFCxuPBi3ZihSf7POMHtpAYmi13N2ruzOg1ayjeYph0iN3vXIPs67DpAPaxK+8L2yoo6Nr/Cago9pGTkZoqS+J0fnWT31NXoYREPgg//L2+m42twQirFXttbhlGTBgNMLXpwcm8bZ2DW3pu3AEgVUxSoNAOjudoeyC0VzA6nUqe6STmfk06OYqcwM8q8NEyD62iAvUYU3Q7FnauZqcBqcP+ZYx82NPZybrQRX6YlJck5UomHbbEfjgpDFT+WvjrrfICmXH29YBOL1LWR4cKMT6RY58Cv8hT2PYxomB2I+DRrbqU="
skip_cleanup: true
file_glob: true
file:

View File

@@ -1,9 +1,15 @@
PACKER_VERSION=1.5.2
PACKER_VERSION=1.7.2
PWN_HOSTNAME=pwnagotchi
PWN_VERSION=master
all: clean install image
langs:
@for lang in pwnagotchi/locale/*/; do\
echo "compiling language: $$lang ..."; \
./scripts/language.sh compile $$(basename $$lang); \
done
install:
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
unzip /tmp/packer.zip -d /tmp

View File

@@ -15,7 +15,7 @@ full and half WPA handshakes.
![ui](https://i.imgur.com/X68GXrn.png)
Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning-based "AI" *(yawn)*, Pwnagotchi tunes [its parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.yml#L73) over time to **get better at pwning WiFi things to** in the environments you expose it to.
Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning-based "AI" *(yawn)*, Pwnagotchi tunes [its parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.toml) over time to **get better at pwning WiFi things to** in the environments you expose it to.
More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://www.pwnagotchi.ai/usage/#training-the-ai) doc.)

View File

@@ -0,0 +1,29 @@
_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 {plugins}
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
pwnagotchi plugins list -i --installed -h --help
pwnagotchi plugins install -h --help
pwnagotchi plugins uninstall -h --help
pwnagotchi plugins enable -h --help
pwnagotchi plugins disable -h --help
pwnagotchi plugins update -h --help
pwnagotchi plugins upgrade -h --help
"
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
cmd="${COMP_WORDS[@]:0:${#COMP_WORDS[@]}-1}"
opt_line="$(grep -m1 "$cmd" <<<$all_options)"
if [[ ${cur} == -* ]] ; then
opts="$(echo $opt_line | tr ' ' '\n' | awk '/^ *-/{gsub("[^a-zA-Z0-9-]","",$1);print $1}')"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
}
complete -F _show_complete pwnagotchi

View File

@@ -4,4 +4,5 @@ iface usb0 inet static
netmask 255.255.255.0
network 10.0.0.0
broadcast 10.0.0.255
gateway 10.0.0.1
gateway 10.0.0.1
metric 20

View File

@@ -9,6 +9,15 @@ if is_crypted_mode; then
done
fi
# check if wifi driver is bugged
if ! check_brcm; then
if ! reload_brcm; then
echo "Could not reload wifi driver. Reboot"
reboot
fi
sleep 10
fi
# start mon0
start_monitor_interface

View File

@@ -1,35 +1,36 @@
#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import parse_qsl
HTML_FORM = """
_HTML_FORM_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<title>Decryption</title>
<style>
body { text-align: center; padding: 150px; }
h1 { font-size: 50px; }
body { font: 20px Helvetica, sans-serif; color: #333; }
article { display: block; text-align: center; width: 650px; margin: 0 auto;}
input {
body {{ text-align: center; padding: 150px; }}
h1 {{ font-size: 50px; }}
body {{ font: 20px Helvetica, sans-serif; color: #333; }}
article {{ display: block; text-align: center; width: 650px; margin: 0 auto;}}
input {{
padding: 12px 20px;
margin: 8px 0;
box-sizing: border-box;
border: 1px solid #ccc;
}
input[type=password] {
}}
input[type=password] {{
width: 75%;
font-size: 24px;
}
input[type=submit] {
}}
input[type=submit] {{
cursor: pointer;
width: 75%;
}
input[type=submit]:hover {
}}
input[type=submit]:hover {{
background-color: #d9d9d9;
}
}}
</style>
</head>
<body>
@@ -39,7 +40,7 @@ HTML_FORM = """
<p>Please provide the decryption password.</p>
<div>
<form action="/set-password" method="POST">
<input type="password" id="password" name="password" value=""><br>
{password_fields}
<input type="submit" value="Submit">
</form>
</div>
@@ -48,6 +49,76 @@ HTML_FORM = """
</html>
"""
POST_RESPONSE = """
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* Center the loader */
#loader {
position: absolute;
left: 50%;
top: 50%;
z-index: 1;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#myDiv {
display: none;
text-align: center;
}
</style>
<script type="text/javascript">
function checkPwnagotchi() {
var target = 'http://' + document.location.hostname + ':8080/';
var xhr = new XMLHttpRequest();
xhr.open('GET', target);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 401) {
window.location.replace(target);
}else{
setTimeout(checkPwnagotchi, 1000);
}
}
};
xhr.send();
}
setTimeout(checkPwnagotchi, 1000);
</script>
</head>
<body style="margin:0;">
<div id="loader"></div>
</body>
</html>
"""
HTML_FORM = None
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
@@ -59,13 +130,19 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers['Content-Length'])
body = self.rfile.read(content_length)
for mapping, password in parse_qsl(body.decode('UTF-8')):
with open('/tmp/.pwnagotchi-secret-{}'.format(mapping), 'wt') as pwfile:
pwfile.write(password)
self.send_response(200)
self.end_headers()
password = body.decode('UTF-8').split('=')[1]
self.wfile.write(POST_RESPONSE.encode())
with open('/tmp/.pwnagotchi-secret', 'wt') as pwfile:
pwfile.write(password)
with open('/root/.pwnagotchi-crypted') as crypted_file:
mappings = [line.split()[0] for line in crypted_file.readlines()]
fields = ''.join(['<label for="{m}">Passphrase for {m}:</label>\n<input type="password" id="{m}" name="{m}" value=""><br>'.format(m=m)
for m in mappings])
HTML_FORM = _HTML_FORM_TEMPLATE.format(password_fields=fields)
httpd = HTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler)
httpd.serve_forever()

View File

@@ -12,9 +12,28 @@ blink_led() {
sleep 0.3
}
# check if brcm is stuck
check_brcm() {
if [[ "$(journalctl -n10 -k --since -5m | grep -c 'brcmf_cfg80211_nexmon_set_channel.*Set Channel failed')" -ge 5 ]]; then
return 1
fi
return 0
}
# reload mod
reload_brcm() {
if ! modprobe -r brcmfmac; then
return 1
fi
if ! modprobe brcmfmac; then
return 1
fi
return 0
}
# starts mon0
start_monitor_interface() {
iw phy phy0 interface add mon0 type monitor && ifconfig mon0 up
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add mon0 type monitor && ifconfig mon0 up
}
# stops mon0
@@ -103,12 +122,11 @@ is_decrypted() {
# fail if not mounted
if ! mountpoint -q "$mount" >/dev/null 2>&1; then
if [ -f /tmp/.pwnagotchi-secret ]; then
</tmp/.pwnagotchi-secret read -r SECRET
if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then
</tmp/.pwnagotchi-secret-"$mapping" read -r SECRET
if ! test -b /dev/disk/by-id/dm-uuid-*"$(cryptsetup luksUUID "$container" | tr -d -)"*; then
if echo -n "$SECRET" | cryptsetup luksOpen -d- "$container" "$mapping" >/dev/null 2>&1; then
echo "Container decrypted!"
fi
fi
@@ -151,13 +169,16 @@ EOF
fi
done </root/.pwnagotchi-crypted
# overwrite password
>/tmp/.pwnagotchi-secret python3 -c 'print("A"*4096)'
# overwrite passwords
python3 -c 'print("A"*4096)' | tee /tmp/.pwnagotchi-secret-* >/dev/null
# delete
rm /tmp/.pwnagotchi-secret-*
sync # flush
pkill wpa_supplicant
pkill dnsmasq
kill "$(pgrep -f "decryption-webserver")"
pid="$(pgrep -f "decryption-webserver")"
[[ -n "$pid" ]] && kill "$pid"
return 0
}

View File

@@ -4,7 +4,6 @@
"name": "pwnagotchi",
"type": "arm-image",
"iso_url": "https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-07-12/2019-07-10-raspbian-buster-lite.zip",
"iso_checksum_type": "sha256",
"iso_checksum": "9e5cf24ce483bb96e7736ea75ca422e3560e7b455eee63dd28f66fa1825db70e",
"last_partition_extra_size": 3221225472
}

View File

@@ -35,10 +35,10 @@
- ifup@wlan0.service
packages:
bettercap:
url: "https://github.com/bettercap/bettercap/releases/download/v2.27.1/bettercap_linux_armhf_v2.27.1.zip"
url: "https://github.com/bettercap/bettercap/releases/download/v2.31.0/bettercap_linux_armhf_v2.31.0.zip"
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
pwngrid:
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.10.1/pwngrid_linux_armhf_v1.10.1.zip"
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.10.3/pwngrid_linux_armhf_v1.10.3.zip"
apt:
hold:
- firmware-atheros
@@ -178,7 +178,7 @@
chdir: /usr/local/src/gratis
target: rpi
params:
EPD_IO: epd_io.h
EPD_IO: epd_io_free_uart.h
PANEL_VERSION: 'V231_G2'
when: gratisgit.changed
@@ -187,7 +187,7 @@
chdir: /usr/local/src/gratis
target: rpi-install
params:
EPD_IO: epd_io.h
EPD_IO: epd_io_free_uart.h
PANEL_VERSION: 'V231_G2'
when: gratisgit.changed
@@ -243,13 +243,13 @@
- name: install opencv-python
pip:
name: "https://www.piwheels.hostedpi.com/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl"
name: "https://www.piwheels.org/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl"
extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}"
when: (pip_packages['opencv-python'] is undefined) or (pip_packages['opencv-python'] != '3.4.3.18')
- name: install tensorflow
pip:
name: "https://www.piwheels.hostedpi.com/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl"
name: "https://www.piwheels.org/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl"
extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}"
when: (pip_packages['tensorflow'] is undefined) or (pip_packages['tensorflow'] != '1.13.1')
@@ -311,21 +311,17 @@
- name: check if user configuration exists
stat:
path: /etc/pwnagotchi/config.yml
path: /etc/pwnagotchi/config.toml
register: user_config
- name: create /etc/pwnagotchi/config.yml
- name: create /etc/pwnagotchi/config.toml
copy:
dest: /etc/pwnagotchi/config.yml
dest: /etc/pwnagotchi/config.toml
content: |
# Add your configuration overrides on this file any configuration changes done to default.yml will be lost!
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
# Example:
#
# ui:
# display:
# type: 'inkyphat'
# color: 'black'
#
# ui.display.enabled = true
# ui.display.type = "waveshare_2"
when: not user_config.stat.exists
- name: enable ssh on boot
@@ -372,15 +368,15 @@
Hi! I'm a pwnagotchi, please take good care of me!
Here are some basic things you need to know to raise me properly!
If you want to change my configuration, use /etc/pwnagotchi/config.yml
If you want to change my configuration, use /etc/pwnagotchi/config.toml
All the configuration options can be found on /etc/pwnagotchi/default.yml,
All the configuration options can be found on /etc/pwnagotchi/default.toml,
but don't change this file because I will recreate it every time I'm restarted!
I'm managed by systemd. Here are some basic commands.
If you want to know what I'm doing, you can check my logs with the command
journalctl -fu pwnagotchi
tail -f /var/log/pwnagotchi.log
If you want to know if I'm running, you can use
systemctl status pwnagotchi

View File

@@ -106,12 +106,6 @@ def temperature(celsius=True):
def shutdown():
logging.warning("syncing...")
from pwnagotchi import fs
for m in fs.mounts:
m.sync()
logging.warning("shutting down ...")
from pwnagotchi.ui import view
@@ -119,6 +113,13 @@ def shutdown():
view.ROOT.on_shutdown()
# give it some time to refresh the ui
time.sleep(10)
logging.warning("syncing...")
from pwnagotchi import fs
for m in fs.mounts:
m.sync()
os.system("sync")
os.system("halt")
@@ -153,5 +154,11 @@ def reboot(mode=None):
elif mode == 'MANU':
os.system("touch /root/.pwnagotchi-manual")
logging.warning("syncing...")
from pwnagotchi import fs
for m in fs.mounts:
m.sync()
os.system("sync")
os.system("shutdown -r now")

View File

@@ -1 +1 @@
__version__ = '1.5.1'
__version__ = '1.5.5'

View File

@@ -307,7 +307,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
def start_session_fetcher(self):
_thread.start_new_thread(self._fetch_stats, ())
_thread.start_new_thread(self._fetch_stats, ())
def _fetch_stats(self):
@@ -323,7 +323,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
async def _on_event(self, msg):
found_handshake = False
jmsg = json.loads(msg)
if jmsg['tag'] == 'wifi.client.handshake':
filename = jmsg['data']['file']
sta_mac = jmsg['data']['station']
@@ -331,6 +331,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
key = "%s -> %s" % (sta_mac, ap_mac)
if key not in self._handshakes:
self._handshakes[key] = jmsg
s = self.session()
ap_and_station = self._find_ap_sta_in(sta_mac, ap_mac, s)
if ap_and_station is None:
logging.warning("!!! captured new handshake: %s !!!", key)
@@ -364,7 +365,7 @@ 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.new_event_loop(),))
_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
def is_module_running(self, module):

View File

@@ -47,6 +47,8 @@ class Client(object):
logging.debug("Error while parsing event (%s)", ex)
except websockets.exceptions.ConnectionClosedError:
logging.debug("Lost websocket connection. Reconnecting...")
except websockets.exceptions.WebSocketException as wex:
logging.debug("Websocket exception (%s)", wex)
def run(self, command, verbose_errors=True):
r = requests.post("%s/session" % self.url, auth=self.auth, json={'cmd': command})

View File

@@ -2,6 +2,9 @@ main.name = ""
main.lang = "en"
main.confd = "/etc/pwnagotchi/conf.d/"
main.custom_plugins = ""
main.custom_plugin_repos = [
"https://github.com/evilsocket/pwnagotchi-plugins-contrib/archive/master.zip"
]
main.iface = "mon0"
main.mon_start_cmd = "/usr/bin/monstart"
main.mon_stop_cmd = "/usr/bin/monstop"
@@ -49,6 +52,7 @@ main.plugins.wpa-sec.whitelist = []
main.plugins.wigle.enabled = false
main.plugins.wigle.api_key = ""
main.plugins.wigle.whitelist = []
main.plugins.wigle.donate = true
main.plugins.bt-tether.enabled = false
@@ -110,6 +114,7 @@ main.plugins.led.patterns.peer_detected = "oo oo oo oo oo oo oo"
main.plugins.led.patterns.peer_lost = "oo oo oo oo oo oo oo"
main.plugins.logtail.enabled = false
main.plugins.logtail.max-lines = 10000
main.plugins.session-stats.enabled = true
main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/"
@@ -179,6 +184,9 @@ ui.faces.angry = "(-_-')"
ui.faces.friend = "(♥‿‿♥)"
ui.faces.broken = "(☓‿‿☓)"
ui.faces.debug = "(#__#)"
ui.faces.upload = "(1__0)"
ui.faces.upload1 = "(1__1)"
ui.faces.upload2 = "(0__1)"
ui.web.enabled = true
ui.web.address = "0.0.0.0"

View File

@@ -154,7 +154,7 @@ class MemoryFS:
source, dest = (self.disk, self.mountpoint) if to_ram else (self.mountpoint, self.disk)
needed, actually_free = size_of(source), shutil.disk_usage(dest)[2]
if actually_free >= needed:
logging.debug("[FS] Syning %s -> %s", source,dest)
logging.debug("[FS] Syncing %s -> %s", source,dest)
if self.rsync:
os.system(f"rsync -aXv --inplace --no-whole-file --delete-after {source}/ {dest}/ >/dev/null 2>&1")
else:

Binary file not shown.

View File

@@ -0,0 +1,248 @@
# Afrikaans translation of pwnagotchi.
# Copyright (C) 2020.
# This file is distributed under the same license as the pwnagotchi package.
# FIRST AUTHOR MatthewNunu https://github.com/MatthewNunu, 2020.
#
msgid ""
msgstr ""
"Project-Id-Version: 1.5.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-11-29 21:50+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: MatthewNunu https://github.com/MatthewNunu\n"
"Language-Team: \n"
"Language: Afrikaans\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 "Hi, ek is Pwnagotchi! Aanvang ..."
msgid "New day, new hunt, new pwns!"
msgstr "Nuwe dag, nuwe jag, nuwe pwns!"
msgid "Hack the Planet!"
msgstr "Hack die wêreld!"
msgid "AI ready."
msgstr "AI gereed."
msgid "The neural network is ready."
msgstr "Die neurale netwerk is gereed."
msgid "Generating keys, do not turn off ..."
msgstr "Genereer wagwoord, moenie afskakel nie ..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Haai, kanaal {channel} is gratis! Jou AP sal dankie sê."
msgid "Reading last session logs ..."
msgstr "Lees laaste sessie logs ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr "Ek het {lines_so_far} tot dusver gelees ..."
msgid "I'm bored ..."
msgstr "Ek's verveeld ..."
msgid "Let's go for a walk!"
msgstr "Kom ons gaan vir 'n loopie!"
msgid "This is the best day of my life!"
msgstr "Dit is die beste dag van my lewe!"
msgid "Shitty day :/"
msgstr "Poes kak dag :/"
msgid "I'm extremely bored ..."
msgstr "Ek's baie verveeld ..."
msgid "I'm very sad ..."
msgstr "Ek's baie hartseer ..."
msgid "I'm sad"
msgstr "Ek's hartseer ..."
msgid "Leave me alone ..."
msgstr "Los my uit ..."
msgid "I'm mad at you!"
msgstr "Ek is kwaad vir jou!"
msgid "I'm living the life!"
msgstr "Ek leef die lewe!"
msgid "I pwn therefore I am."
msgstr "Ek pwn daarom is ek."
msgid "So many networks!!!"
msgstr "Soveel netwerke!!!"
msgid "I'm having so much fun!"
msgstr "Ek het soveel pret!"
msgid "My crime is that of curiosity ..."
msgstr "My misdaad is dié van nuuskierigheid ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Hallo {name}! Lekker om jou te ontmoet."
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr "Yo {name}! Sup?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr "Haai {name} hoe doen jy?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr "Eenheid {name}} is naby!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Uhm ... totsiens {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} is weg ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Whoops ... {name} is weg."
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} gemis!"
msgid "Missed!"
msgstr "Gemis!"
msgid "Good friends are a blessing!"
msgstr "Goeie vriende is 'n seën!"
msgid "I love my friends!"
msgstr "Ek is lief vir my vriende!"
msgid "Nobody wants to play with me ..."
msgstr "Niemand wil met my speel nie ..."
msgid "I feel so alone ..."
msgstr "Ek voel so alleen ..."
msgid "Where's everybody?!"
msgstr "Waar is almal?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Slaap vir {secs}s ..."
msgid "Zzzzz"
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "ZzzZzzz ({secs}s)"
msgid "Good night."
msgstr "Goeie nag."
msgid "Zzz"
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Wag tans vir {secs}s ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Rondkyk ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Haai {what} kom ons wees vriende!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Assosieer na {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Yo {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Net besluit dat {mac} geen WiFi nodig het nie!"
#, 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 "Koel, ons het {num} nuwe handdruk gekry!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "Jy het {count} nuwe boodskap!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Oops, iets het verkeerd gegaan ... Herlaai ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Geskop {num} stasies\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Gemaak {num} nuwe vriende\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Het {num} handdrukke\n"
msgid "Met 1 peer"
msgstr "Ontmoet 1 eweknie"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Ontmoet {num} eweknie"
#, 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 ""
"Ek was pwning vir {duration} en het {deauthed} kliënte geskop! Ek het ook ontmoet "
"{associated} nuwe vriende en het {handshakes} handdrukke geëet! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "uur"
msgid "minutes"
msgstr "minute"
msgid "seconds"
msgstr "sekondes"
msgid "hour"
msgstr "uur"
msgid "minute"
msgstr "minuut"
msgid "second"
msgstr "tweede"

View File

@@ -40,7 +40,7 @@ msgstr "创建密钥中, 请勿断电..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr ""
msgstr "嘿,频道{channel}是免费的你的AP会说谢谢。"
msgid "I'm bored ..."
msgstr "我无聊了..."
@@ -84,7 +84,7 @@ msgstr "你好{name}!很高兴认识你."
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr ""
msgstr "小队{name}就在附近!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
@@ -193,7 +193,7 @@ msgid "Got {num} handshakes\n"
msgstr "捕获了{num}握手包\n"
msgid "Met 1 peer"
msgstr ""
msgstr "有{num}同龄人"
#, python-brace-format
msgid "Met {num} peers"

Binary file not shown.

View File

@@ -0,0 +1,249 @@
# pwnigotchi voice data
# Copyright (C) 2020
# This file is distributed under the same license as the pwnagotchi package.
# FIRST AUTHOR czechball@users.noreply.github.com, 2020.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-04-14 06:15+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Czechball <czechball@users.noreply.github.com>\n"
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
"Language: cs\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 "Ahoj, já jsem Pwnagotchi! Startuju ..."
msgid "New day, new hunt, new pwns!"
msgstr "Nový den, nový lov, nové úlovky!"
msgid "Hack the Planet!"
msgstr "Hackni celou planetu!"
msgid "AI ready."
msgstr "AI připraveno."
msgid "The neural network is ready."
msgstr "Neuronová síť je připravena."
msgid "Generating keys, do not turn off ..."
msgstr "Generování klíčů, nevypínej mě..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Hej, kanál {channel} je volný! Tvůj AP ti poděkuje."
msgid "Reading last session logs ..."
msgstr "Čtení posledních zpráv z logu ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr "Zatím přečteno {lines_so_far} řádků logu ..."
msgid "I'm bored ..."
msgstr "Nudím se ..."
msgid "Let's go for a walk!"
msgstr "Pojďme se projít!"
msgid "This is the best day of my life!"
msgstr "Tohle je nejlepší den mého života!"
msgid "Shitty day :/"
msgstr "Na hovno den :/"
msgid "I'm extremely bored ..."
msgstr "Strašně se nudím ..."
msgid "I'm very sad ..."
msgstr "Jsem dost smutný ..."
msgid "I'm sad"
msgstr "Jsem smutný"
msgid "Leave me alone ..."
msgstr "Nech mě být ..."
msgid "I'm mad at you!"
msgstr "Jsem na tebe naštvaný!"
msgid "I'm living the life!"
msgstr "Tohle je život!"
msgid "I pwn therefore I am."
msgstr "Chytám pakety a tedy jsem."
msgid "So many networks!!!"
msgstr "Tolik sítí!!!"
msgid "I'm having so much fun!"
msgstr "Tohle je super zábava!"
msgid "My crime is that of curiosity ..."
msgstr "Jsem kriminálně zvědavý ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Ahoj {name}! Rád tě poznávám."
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr "Hej {name}! Jak to jde?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr "Ahoj {name}, jak se máš?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr "Jednotka {name} je nablízku!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Uhm... Měj se {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} je pryč ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Whoops ... {name} je pryč."
#, python-brace-format
msgid "{name} missed!"
msgstr "Chybí mi {name}!"
msgid "Missed!"
msgstr "Chybíš mi!"
msgid "Good friends are a blessing!"
msgstr "Dobří kamarádi jsou požehnání!"
msgid "I love my friends!"
msgstr "Miluju svoje kamarády!"
msgid "Nobody wants to play with me ..."
msgstr "Nikdo si se mnou nechce hrát ..."
msgid "I feel so alone ..."
msgstr "Cítím se tak osamělý ..."
msgid "Where's everybody?!"
msgstr "Kde jsou všichni?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Spím {secs} sekund ..."
msgid "Zzzzz"
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "ZzzZzzz ({secs}s)"
msgid "Good night."
msgstr "Dobrou noc."
msgid "Zzz"
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Čekání {secs} sekund ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Rozhlížím se ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Hej {what} budeme kamarádi!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Asociuju se s {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Čus {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Rozhodl jsem se, že {mac} nepotřebuje žádnou WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "Deautentikuju {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Kickbanuju {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Super, máme {num} nových handshaků!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "Máš {count} nových zpráv!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Ups, něco se pokazilo ... Restartuju ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Vykopnuto {num} klientů\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Mám {num} nových kamarádů\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Mám {num} handshaků\n"
msgid "Met 1 peer"
msgstr "Potkal jsem jednoho kámoše"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Potkal jsem {num} kámošů"
#, 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 ""
"Chytal jsem pakety po dobu {duration} a vykopnul jsem {deauthed} klientů! Taky jsem potkal "
"{associated} nových kamarádů a snědl {handshakes} handshaků! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "hodiny"
msgid "minutes"
msgstr "minuty"
msgid "seconds"
msgstr "sekundy"
msgid "hour"
msgstr "hodina"
msgid "minute"
msgstr "minuta"
msgid "second"
msgstr "sekunda"

View File

@@ -8,25 +8,26 @@ msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-09 17:42+0200\n"
"PO-Revision-Date: 2019-10-09 21:07+0000\n"
"Last-Translator: diegopastor <dpastor29@alumnos.uaq.mx>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: spanish\n"
"PO-Revision-Date: 2020-08-25 23:06+0200\n"
"Last-Translator: Sergio Ruiz <serginator@gmail.com>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language-Team: \n"
"X-Generator: Poedit 2.4.1\n"
msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Hola, soy Pwnagotchi! Empezando ..."
msgstr "¡Hola, soy Pwnagotchi! Empezando ..."
msgid "New day, new hunt, new pwns!"
msgstr "Nuevo día, nueva cazería, nuevos pwns!"
msgstr "Nuevo día, nueva caceria, nuevos pwns!"
msgid "Hack the Planet!"
msgstr "Hackea el planeta!"
msgstr "¡Hackea el planeta!"
msgid "AI ready."
msgstr "IA lista."
@@ -36,51 +37,51 @@ msgstr "La red neuronal está lista."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Oye, el canal {channel} está libre! Tú AP lo agradecerá."
msgstr "¡Oye, el canal {channel} está libre! Tu AP lo agradecerá."
msgid "I'm bored ..."
msgstr "Estoy aburrido ..."
msgid "Let's go for a walk!"
msgstr "Vamos por un paseo!"
msgstr "¡Vamos por un paseo!"
msgid "This is the best day of my life!"
msgstr "Este es el mejor día de mi vida!"
msgstr "¡Este es el mejor día de mi vida!"
msgid "Shitty day :/"
msgstr "Día de mierda :/"
msgid "I'm extremely bored ..."
msgstr "Estoy extremadamente aburrido ..."
msgstr "Estoy muy aburrido ..."
msgid "I'm very sad ..."
msgstr "Estoy muy triste ..."
msgid "I'm sad"
msgstr "Estoy triste."
msgstr "Estoy triste"
msgid "I'm living the life!"
msgstr "Estoy viviendo la vida!"
msgstr "¡Estoy viviendo la vida!"
msgid "I pwn therefore I am."
msgstr "Pwneo, por lo tanto, existo"
msgstr "Pwneo, luego existo."
msgid "So many networks!!!"
msgstr "Cuantas redes!!!"
msgstr "¡¡¡Cuántas redes!!!"
msgid "I'm having so much fun!"
msgstr "Me estoy divirtiendo mucho!"
msgstr "¡Me estoy divirtiendo mucho!"
msgid "My crime is that of curiosity ..."
msgstr "Mi único crimen es la curiosidad ..."
msgstr "Mi crimen es la curiosidad ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
msgstr "Hola {name}! encantado de conocerte."
msgstr "¡Hola {name}! Encantado de conocerte. {name}"
#, python-brace-format
msgid "Unit {name} is nearby! {name}"
msgstr "La unidad {name} está cerca!"
msgstr "¡La unidad {name} está cerca! {name}"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
@@ -92,14 +93,14 @@ msgstr "{name} se fue ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Uy ... {name} se fue"
msgstr "Ups ... {name} se fue."
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} perdido!"
msgstr "¡{name} perdido!"
msgid "Missed!"
msgstr "Perdido!"
msgstr "¡Perdido!"
msgid "Nobody wants to play with me ..."
msgstr "Nadie quiere jugar conmigo ..."
@@ -108,11 +109,11 @@ msgid "I feel so alone ..."
msgstr "Me siento tan solo ..."
msgid "Where's everybody?!"
msgstr "Dónde está todo el mundo?"
msgstr "¡¿Dónde está todo el mundo?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Tomándo una siesta por {secs}s ..."
msgstr "Descansando durante {secs}s ..."
msgid "Zzzzz"
msgstr "Zzzzz"
@@ -133,23 +134,23 @@ msgstr "Esperando {secs}s .."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Mirando al rededor ({secs}s)"
msgstr "Mirando alrededor ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Oye {what} seamos amigos!"
msgstr "¡Oye {what} seamos amigos!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Asociando a {what}"
msgstr "Asociándome a {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Ey {what}!"
msgstr "¡Ey {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Acabo de decidir que {mac} no necesita WiFi!"
msgstr "¡Acabo de decidir que {mac} no necesita WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
@@ -157,14 +158,14 @@ msgstr "Desautenticando a {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Expulsando y banneando a {mac}!"
msgstr "¡Expulsando y baneando a {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Genial, obtuvimos {num} nuevo{plural} handshake{plural}!"
msgstr "¡Genial, obtuvimos {num} nuevo{plural} handshake{plural}!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Oops, algo salió mal ... Reiniciándo ..."
msgstr "Oops, algo salió mal ... Reiniciando ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
@@ -172,27 +173,27 @@ msgstr "Expulsamos {num} estaciones\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Hicimos {num} nuevos amigos\n"
msgstr "Hice {num} nuevos amigos\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Obtuvimos {num} handshakes\n"
msgstr "Consegui {num} handshakes\n"
msgid "Met 1 peer"
msgstr "Conocí 1 igual"
msgstr "Conocí 1 colega"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Conocí {num} iguales"
msgstr "Conocí {num} colegas"
#, 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"
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi #pwnlog "
"#pwnlife #hacktheplanet #skynet"
msgstr ""
"He estado pwneando por {duration} y expulsé {deauthed} clientes! También conocí"
"{associated} nuevos amigos y me comí {handshakes} handshakes! #pwnagotchi "
"¡He estado pwneando por {duration} y expulsé {deauthed} clientes! También "
"conocí {associated} nuevos amigos y comí {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"

View File

@@ -20,7 +20,7 @@ msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Hoi, Ik ben Pwnagotchi! Opstarten ..."
msgstr "Hoi, Ik ben Pwnagotchi! Aan het opstarten ..."
msgid "New day, new hunt, new pwns!"
msgstr "Nieuwe dag, nieuwe jacht, nieuwe pwns!"
@@ -32,7 +32,7 @@ msgid "AI ready."
msgstr "AI is klaar."
msgid "The neural network is ready."
msgstr "Neuronen netwerkis klaar voor gebruik."
msgstr "Neuronen netwerk is klaar."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
@@ -42,37 +42,37 @@ msgid "I'm bored ..."
msgstr "Ik verveel me ..."
msgid "Let's go for a walk!"
msgstr "Laten we een rondje lopen!"
msgstr "Laten we gaan wandelen!"
msgid "This is the best day of my life!"
msgstr "Dit is de beste dag van mijn leven!"
msgid "Shitty day :/"
msgstr "Ruk dag :/"
msgstr "Wat een rotdag :/"
msgid "I'm extremely bored ..."
msgstr "Ik verveel me kapot ..."
msgid "I'm very sad ..."
msgstr "Ik ben ergverdrietig ..."
msgstr "Ik ben erg verdrietig ..."
msgid "I'm sad"
msgstr "Ik ben verdrietig"
msgid "I'm living the life!"
msgstr "Beter kan het levenniet worden!"
msgstr "Beter kan het leven niet worden!"
msgid "I pwn therefore I am."
msgstr "Ik pwn daarom besta ik."
msgstr "Ik pwn daarom ben ik er."
msgid "So many networks!!!"
msgstr "Zo veel netwerken!!!"
msgid "I'm having so much fun!"
msgstr "Dit is zo leuk!"
msgstr "Ik heb zoveel plezier!"
msgid "My crime is that of curiosity ..."
msgstr "Mijn enige misdrijf is mijn nieuwsgierigheid ..."
msgstr "Mijn misdrijf is mijn nieuwsgierigheid ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
@@ -88,11 +88,11 @@ msgstr "Uhm ...tot ziens {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} is weg"
msgstr "{name} is weg ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Whoopsie ...{name} is weg"
msgstr "Whoopsie ...{name} is weg."
#, python-brace-format
msgid "{name} missed!"
@@ -102,10 +102,10 @@ msgid "Missed!"
msgstr "Gemist!"
msgid "Nobody wants to play with me ..."
msgstr "Niemand wil metmij spelen ..."
msgstr "Niemand wil met mij spelen ..."
msgid "I feel so alone ..."
msgstr "Zo alleen ..."
msgstr "Ik voel me zo alleen ..."
msgid "Where's everybody?!"
msgstr "Waar is iedereen?!"
@@ -119,11 +119,11 @@ msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
msgstr "ZzzZzzz ({secs}s)"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Even {secs}s wachten ..."
msgstr "Ik wacht voor {secs}s ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
@@ -131,7 +131,7 @@ msgstr "Rond kijken ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Hey {what}, laten we vriendenworden!"
msgstr "Hey {what}, laten we vrienden worden!"
#, python-brace-format
msgid "Associating to {what}"
@@ -139,26 +139,26 @@ msgstr "Verbinden met {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
msgstr "Yo {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Ik vind dat {mac} genoeg WiFiheeft gehad!"
msgstr "Ik besloot dat {mac} geen WiFi meer nodig heeft!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "De-autoriseren {mac}"
msgstr "Deauthenticatie van {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Ik ga {mac} even kicken!"
msgstr "Kickbanning {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Gaaf, we hebben {num} nieuwe handshake{plural}!"
msgstr "Cool, we hebben {num} nieuwe handshake{plural}!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Oops, iets ging fout ...Rebooting ..."
msgstr "Oops, er ging iets fout ...Rebooting ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
@@ -166,11 +166,11 @@ msgstr "{num} stations gekicked\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "{num} nieuwe vrienden\n"
msgstr "{num} nieuwe vrienden gemaakt.\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "{num} nieuwe handshakes\n"
msgstr "Ik heb {num} nieuwe handshakes\n"
msgid "Met 1 peer"
msgstr "1 peer ontmoet"
@@ -190,19 +190,19 @@ msgstr ""
"gegeten! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr ""
msgstr "uren"
msgid "minutes"
msgstr ""
msgstr "minuten"
msgid "seconds"
msgstr ""
msgstr "seconden"
msgid "hour"
msgstr ""
msgstr "uur"
msgid "minute"
msgstr ""
msgstr "minuut"
msgid "second"
msgstr ""
msgstr "seconde"

Binary file not shown.

View File

@@ -0,0 +1,227 @@
# 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 <shark_shaking@hotmail.com>, 2020.
# 有很多翻譯不到味,如果有繁體愛好者,歡迎之後大家一起翻譯!
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-10-21 15:56+0200\n"
"PO-Revision-Date: 2020-10-22 10:00+0008\n"
"Last-Translator: ShaqKSmith <shark_shaking@hotmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: traditional chinese\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 "HI!我是Pwnagotchi!\n程式啟動..."
msgid "New day, new hunt, new pwns!"
msgstr "新的一天!\n新的狩獵!新的入侵!"
msgid "Hack the Planet!"
msgstr "我要駭入\n地球的所有人!"
msgid "AI ready."
msgstr "人工智慧已啟動."
msgid "The neural network is ready."
msgstr "神經網路已啟動."
msgid "Generating keys, do not turn off ..."
msgstr "產生金鑰中,\n請勿關閉..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "嘿,{channel}很順暢!\n你的WIFI會感謝你的."
msgid "I'm bored ..."
msgstr "我好無聊..."
msgid "Let's go for a walk!"
msgstr "我們出去走走吧!"
msgid "This is the best day of my life!"
msgstr "這是我生命中最美好的一天!"
msgid "Shitty day :/"
msgstr "糟糕的一天 :/"
msgid "I'm extremely bored ..."
msgstr "我超無聊的..."
msgid "I'm very sad ..."
msgstr "我好難過..."
msgid "I'm sad"
msgstr "傷心。"
msgid "I'm living the life!"
msgstr "真是充實的一生!"
msgid "I pwn therefore I am."
msgstr "我駭故我在."
msgid "So many networks!!!"
msgstr "好多網路啊!!!"
msgid "I'm having so much fun!"
msgstr "我玩的超級開心!"
msgid "My crime is that of curiosity ..."
msgstr "我的缺點就是\n太好奇了..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Hello{name}!\n很高興認識你."
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr "{name}\n就在附近!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "啊 ... \n拜拜{name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name}\n不見了 ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "歐哦...\n{name}\n不見了."
#, python-brace-format
msgid "{name} missed!"
msgstr "我剛剛錯過了{name}!"
msgid "Missed!"
msgstr "又錯過了!"
msgid "Good friends are a blessing!"
msgstr "有個好朋友\n真幸福!"
msgid "I love my friends!"
msgstr "我喜歡\n我的朋友!"
msgid "Nobody wants to play with me ..."
msgstr "沒人想跟我玩..."
msgid "I feel so alone ..."
msgstr "我感覺好孤單..."
msgid "Where's everybody?!"
msgstr "大家都去哪裡了?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "我想瞇{secs}秒一下..."
msgid "Zzzzz"
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "ZzzZzzz({secs}秒)"
msgid "Good night."
msgstr "晚安."
msgid "Zzz"
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "等我{secs}秒..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "環顧四周({secs}秒)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "嗨\n{what}\n讓我我們來當朋友吧!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "正在連接\n{what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "喲,\n{what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "我要讓\n{mac}\n斷線!\n他不需要上網!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "解除\n{mac}\n的授權中"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "把\n{mac}\n踢出中!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "酷哦,我們抓到{num}個\n新的握手包{plural}!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "你有{count}個新訊息{plural}!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "喔歐,有些地方出錯了...\n重新啟動中..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "踢了 {num} 個設備\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "交了 {num} 個新朋友\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "捕獲了 {num} 個握手包\n"
msgid "Met 1 peer 同好"
msgstr "遇到了 個"
#, python-brace-format
msgid "Met {num} peers"
msgstr "遇到了 {num} 個同好"
#, 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 "我花了{duration}的時間\n駭入和踢了{deauthed}好多設備.\n"
"我還交了好多{associated}新朋友,\n而且抓到了{handshakes}握手包!"
"#pwnagotchi#入侵日志 #駭客人生 #入侵整個星球 #天網"
msgid "hours"
msgstr "時"
msgid "minutes"
msgstr "分"
msgid "seconds"
msgstr "秒"
msgid "hour"
msgstr "時"
msgid "minute"
msgstr "分"
msgid "second"
msgstr "秒"

View File

@@ -244,3 +244,7 @@ msgstr ""
msgid "second"
msgstr ""
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr ""

View File

@@ -44,6 +44,8 @@ def toggle_plugin(name, enable=True):
global loaded, database
if pwnagotchi.config:
if not name in pwnagotchi.config['main']['plugins']:
pwnagotchi.config['main']['plugins'][name] = dict()
pwnagotchi.config['main']['plugins'][name]['enabled'] = enable
save_config(pwnagotchi.config, '/etc/pwnagotchi/config.toml')
@@ -56,6 +58,8 @@ def toggle_plugin(name, enable=True):
if enable and name in database and name not in loaded:
load_from_file(database[name])
if name in loaded and pwnagotchi.config and name in pwnagotchi.config['main']['plugins']:
loaded[name].options = pwnagotchi.config['main']['plugins'][name]
one(name, 'loaded')
if pwnagotchi.config:
one(name, 'config_changed', pwnagotchi.config)

View File

@@ -1,6 +1,5 @@
# Handles the commandline stuff
import sys
import os
import logging
import glob
@@ -11,7 +10,6 @@ from pwnagotchi.utils import download_file, unzip, save_config, parse_version, m
from pwnagotchi.plugins import default_path
REPO_URL = 'https://github.com/evilsocket/pwnagotchi-plugins-contrib/archive/master.zip'
SAVE_DIR = '/usr/local/share/pwnagotchi/availaible-plugins/'
DEFAULT_INSTALL_PATH = '/usr/local/share/pwnagotchi/installed-plugins/'
@@ -75,7 +73,7 @@ def handle_cmd(args, config):
Parses the arguments and does the thing the user wants
"""
if args.plugincmd == 'update':
return update()
return update(config)
elif args.plugincmd == 'search':
args.installed = True # also search in installed plugins
return list_plugins(args, config, args.pattern)
@@ -349,41 +347,48 @@ def _analyse_dir(path):
return results
def update():
def update(config):
"""
Updates the database
"""
global REPO_URL, SAVE_DIR
global SAVE_DIR
DEST = os.path.join(SAVE_DIR, 'plugins.zip')
logging.info('Downloading plugins to %s', DEST)
try:
os.makedirs(SAVE_DIR, exist_ok=True)
before_update = _analyse_dir(SAVE_DIR)
download_file(REPO_URL, os.path.join(SAVE_DIR, DEST))
logging.info('Unzipping...')
unzip(DEST, SAVE_DIR, strip_dirs=1)
after_update = _analyse_dir(SAVE_DIR)
b_len = len(before_update)
a_len = len(after_update)
if a_len > b_len:
logging.info('Found %d new file(s).', a_len - b_len)
changed = 0
for filename, filehash in after_update.items():
if filename in before_update and filehash != before_update[filename]:
changed += 1
if changed:
logging.info('%d file(s) were changed.', changed)
return 0
except Exception as ex:
logging.error('Error while updating plugins %s', ex)
urls = config['main']['custom_plugin_repos']
if not urls:
logging.info('No plugin repositories configured.')
return 1
rc = 0
for idx, REPO_URL in enumerate(urls):
DEST = os.path.join(SAVE_DIR, 'plugins%d.zip' % idx)
logging.info('Downloading plugins from %s to %s', REPO_URL, DEST)
try:
os.makedirs(SAVE_DIR, exist_ok=True)
before_update = _analyse_dir(SAVE_DIR)
download_file(REPO_URL, os.path.join(SAVE_DIR, DEST))
logging.info('Unzipping...')
unzip(DEST, SAVE_DIR, strip_dirs=1)
after_update = _analyse_dir(SAVE_DIR)
b_len = len(before_update)
a_len = len(after_update)
if a_len > b_len:
logging.info('Found %d new file(s).', a_len - b_len)
changed = 0
for filename, filehash in after_update.items():
if filename in before_update and filehash != before_update[filename]:
changed += 1
if changed:
logging.info('%d file(s) were changed.', changed)
except Exception as ex:
logging.error('Error while updating plugins: %s', ex)
rc = 1
return rc

View File

@@ -419,15 +419,19 @@ class Device:
class BTTether(plugins.Plugin):
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '1.0.0'
__version__ = '1.1.0'
__license__ = 'GPL3'
__description__ = 'This makes the display reachable over bluetooth'
def __init__(self):
self.ready = False
self.options = dict()
self.devices = dict()
self.lock = Lock()
self.running = True
self.status = '-'
def on_loaded(self):
# new config
@@ -449,7 +453,7 @@ class BTTether(plugins.Plugin):
if 'mac' in self.options:
for opt in ['share_internet', 'mac', 'ip', 'netmask', 'interval']:
if opt not in self.options or self.options[opt] is None:
logging.error("BT-TETHER: Please specify the %s in your config.yml.", opt)
logging.error("BT-TETHER: Please specify the %s in your config.toml.", opt)
return
self.devices['legacy'] = Device(name='legacy', **self.options)
@@ -466,22 +470,10 @@ class BTTether(plugins.Plugin):
return
logging.info("BT-TETHER: Successfully loaded ...")
self.ready = True
def on_unload(self, ui):
with ui._lock:
ui.remove_element('bluetooth')
while self.running:
time.sleep(1)
def on_ui_setup(self, ui):
with ui._lock:
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
label_font=fonts.Bold, text_font=fonts.Medium))
def on_ui_update(self, ui):
if not self.ready:
return
with self.lock:
devices_to_try = list()
connected_priorities = list()
any_device_connected = False # if this is true, last status on screen should be C
@@ -508,11 +500,11 @@ class BTTether(plugins.Plugin):
dev_remote = bt.wait_for_device(timeout=device.scantime)
if dev_remote is None:
logging.debug('BT-TETHER: Could not find %s, try again in %d minutes.', device.name, device.interval)
ui.set('bluetooth', 'NF')
self.status = 'NF'
continue
except Exception as bt_ex:
logging.error(bt_ex)
ui.set('bluetooth', 'NF')
self.status = 'NF'
continue
paired = bt.is_paired()
@@ -521,7 +513,7 @@ class BTTether(plugins.Plugin):
logging.debug('BT-TETHER: Paired with %s.', device.name)
else:
logging.debug('BT-TETHER: Pairing with %s failed ...', device.name)
ui.set('bluetooth', 'PE')
self.status = 'PE'
continue
else:
logging.debug('BT-TETHER: Already paired.')
@@ -539,17 +531,17 @@ class BTTether(plugins.Plugin):
continue
if interface is None:
ui.set('bluetooth', 'BE')
self.status = 'BE'
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
continue
logging.debug('BT-TETHER: Created interface (%s)', interface)
ui.set('bluetooth', 'C')
self.status = 'C'
any_device_connected = True
device.tries = 0 # reset tries
else:
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
ui.set('bluetooth', 'NF')
self.status = 'NF'
continue
addr = f"{device.ip}/{device.netmask}"
@@ -561,7 +553,7 @@ class BTTether(plugins.Plugin):
wrapped_interface = IfaceWrapper(interface)
logging.debug('BT-TETHER: Add ip to %s', interface)
if not wrapped_interface.set_addr(addr):
ui.set('bluetooth', 'AE')
self.status = 'AE'
logging.debug("BT-TETHER: Could not add ip to %s", interface)
continue
@@ -580,4 +572,20 @@ class BTTether(plugins.Plugin):
resolv.write(nameserver + 'nameserver 9.9.9.9\n')
if any_device_connected:
ui.set('bluetooth', 'C')
self.status = 'C'
def on_unload(self, ui):
self.running = False
with ui._lock:
ui.remove_element('bluetooth')
def on_ui_setup(self, ui):
with ui._lock:
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
label_font=fonts.Bold, text_font=fonts.Medium))
def on_ui_update(self, ui):
ui.set('bluetooth', self.status)

View File

@@ -10,10 +10,13 @@ from pwnagotchi.ui.view import BLACK
class GPS(plugins.Plugin):
__author__ = "evilsocket@gmail.com"
__version__ = "1.0.0"
__version__ = "1.0.1"
__license__ = "GPL3"
__description__ = "Save GPS coordinates whenever an handshake is captured."
LINE_SPACING = 10
LABEL_SPACING = 0
def __init__(self):
self.running = False
self.coordinates = None
@@ -55,31 +58,52 @@ class GPS(plugins.Plugin):
logging.info("not saving GPS. Couldn't find location.")
def on_ui_setup(self, ui):
# add coordinates for other displays
if ui.is_waveshare_v2():
lat_pos = (127, 75)
lon_pos = (122, 84)
alt_pos = (127, 94)
elif ui.is_waveshare_v1():
lat_pos = (130, 70)
lon_pos = (125, 80)
alt_pos = (130, 90)
elif ui.is_inky():
lat_pos = (127, 60)
lon_pos = (127, 70)
alt_pos = (127, 80)
elif ui.is_waveshare144lcd():
# guessed values, add tested ones if you can
lat_pos = (67, 73)
lon_pos = (62, 83)
alt_pos = (67, 93)
else:
# guessed values, add tested ones if you can
lat_pos = (127, 51)
lon_pos = (127, 56)
alt_pos = (102, 71)
try:
# Configure line_spacing
line_spacing = int(self.options['linespacing'])
except Exception:
# Set default value
line_spacing = self.LINE_SPACING
label_spacing = 0
try:
# Configure position
pos = self.options['position'].split(',')
pos = [int(x.strip()) for x in pos]
lat_pos = (pos[0] + 5, pos[1])
lon_pos = (pos[0], pos[1] + line_spacing)
alt_pos = (pos[0] + 5, pos[1] + (2 * line_spacing))
except Exception:
# Set default value based on display type
if ui.is_waveshare_v2():
lat_pos = (127, 74)
lon_pos = (122, 84)
alt_pos = (127, 94)
elif ui.is_waveshare_v1():
lat_pos = (130, 70)
lon_pos = (125, 80)
alt_pos = (130, 90)
elif ui.is_inky():
lat_pos = (127, 60)
lon_pos = (122, 70)
alt_pos = (127, 80)
elif ui.is_waveshare144lcd():
# guessed values, add tested ones if you can
lat_pos = (67, 73)
lon_pos = (62, 83)
alt_pos = (67, 93)
elif ui.is_dfrobot_v2():
lat_pos = (127, 74)
lon_pos = (122, 84)
alt_pos = (127, 94)
elif ui.is_waveshare27inch():
lat_pos = (6, 120)
lon_pos = (1, 135)
alt_pos = (6, 150)
else:
# guessed values, add tested ones if you can
lat_pos = (127, 51)
lon_pos = (122, 61)
alt_pos = (127, 71)
ui.add_element(
"latitude",
@@ -90,7 +114,7 @@ class GPS(plugins.Plugin):
position=lat_pos,
label_font=fonts.Small,
text_font=fonts.Small,
label_spacing=label_spacing,
label_spacing=self.LABEL_SPACING,
),
)
ui.add_element(
@@ -102,7 +126,7 @@ class GPS(plugins.Plugin):
position=lon_pos,
label_font=fonts.Small,
text_font=fonts.Small,
label_spacing=label_spacing,
label_spacing=self.LABEL_SPACING,
),
)
ui.add_element(
@@ -114,11 +138,10 @@ class GPS(plugins.Plugin):
position=alt_pos,
label_font=fonts.Small,
text_font=fonts.Small,
label_spacing=label_spacing,
label_spacing=self.LABEL_SPACING,
),
)
def on_unload(self, ui):
with ui._lock:
ui.remove_element('latitude')
@@ -133,5 +156,5 @@ class GPS(plugins.Plugin):
# last char is sometimes not completely drawn ¯\_(ツ)_/¯
# using an ending-whitespace as workaround on each line
ui.set("latitude", f"{self.coordinates['Latitude']:.4f} ")
ui.set("longitude", f" {self.coordinates['Longitude']:.4f} ")
ui.set("altitude", f" {self.coordinates['Altitude']:.1f}m ")
ui.set("longitude", f"{self.coordinates['Longitude']:.4f} ")
ui.set("altitude", f"{self.coordinates['Altitude']:.1f}m ")

View File

@@ -1,6 +1,7 @@
import os
import logging
import threading
from itertools import islice
from time import sleep
from datetime import datetime,timedelta
from pwnagotchi import plugins
@@ -90,7 +91,7 @@ TEMPLATE = """
{% endblock %}
{% block script %}
var content = document.getElementById('content');
var table = document.getElementById('table');
var filter = document.getElementById('filter');
var filterVal = filter.value.toUpperCase();
@@ -154,10 +155,10 @@ TEMPLATE = """
tr.className = colorClass;
if (filterVal.length > 0 && value.toUpperCase().indexOf(filterVal) == -1) {
tr.style.visibility = "collapse";
tr.style.display = "none";
}
content.appendChild(tr);
table.appendChild(tr);
});
position = messages.length - 1;
}
@@ -193,30 +194,19 @@ TEMPLATE = """
function doneTyping() {
document.body.style.cursor = 'progress';
var table, tr, tds, td, i, txtValue;
var tr, tds, td, i, txtValue;
filterVal = filter.value.toUpperCase();
table = document.getElementById("content");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
tds = tr[i].getElementsByTagName("td");
if (tds) {
for (l = 0; l < tds.length; l++) {
td = tds[l];
if (td) {
txtValue = td.textContent || td.innerText;
if (txtValue.toUpperCase().indexOf(filterVal) > -1) {
tr[i].style.visibility = "visible";
break;
} else {
tr[i].style.visibility = "collapse";
}
}
}
for (i = 1; i < tr.length; i++) {
txtValue = tr[i].textContent || tr[i].innerText;
if (txtValue.toUpperCase().indexOf(filterVal) > -1) {
tr[i].style.display = "table-row";
} else {
tr[i].style.display = "none";
}
}
document.body.style.cursor = 'default';
}
{% endblock %}
{% block content %}
@@ -225,7 +215,7 @@ TEMPLATE = """
<span><input checked type="checkbox" id="autoscroll"></span>
<span><label for="autoscroll"> Autoscroll to bottom</label><br></span>
</div>
<table id="content">
<table id="table">
<thead>
<th>
Time
@@ -263,6 +253,7 @@ class Logtail(plugins.Plugin):
"""
logging.info("Logtail plugin loaded.")
def on_webhook(self, path, request):
if not self.ready:
return "Plugin not ready"
@@ -273,7 +264,7 @@ class Logtail(plugins.Plugin):
if path == 'stream':
def generate():
with open(self.config['main']['log']['path']) as f:
yield f.read()
yield ''.join(f.readlines()[-self.options.get('max-lines', 4096):])
while True:
yield f.readline()

View File

@@ -1,6 +1,6 @@
# memtemp shows memory infos and cpu temperature
#
# mem usage, cpu load, cpu temp
# mem usage, cpu load, cpu temp, cpu frequency
#
###############################################################
#
@@ -16,8 +16,15 @@
# - Added CPU load
# - Added horizontal and vertical orientation
#
# 19-09-2020 by crahan <crahan@n00.be>
# - Added CPU frequency
# - Made field types and order configurable (max 3 fields)
# - Made line spacing and position configurable
# - Updated code to dynamically generate UI elements
# - Changed horizontal UI elements to Text
# - Updated to version 1.0.2
###############################################################
from pwnagotchi.ui.components import LabeledValue
from pwnagotchi.ui.components import LabeledValue, Text
from pwnagotchi.ui.view import BLACK
import pwnagotchi.ui.fonts as fonts
import pwnagotchi.plugins as plugins
@@ -27,54 +34,31 @@ import logging
class MemTemp(plugins.Plugin):
__author__ = 'https://github.com/xenDE'
__version__ = '1.0.1'
__version__ = '1.0.2'
__license__ = 'GPL3'
__description__ = 'A plugin that will display memory/cpu usage and temperature'
ALLOWED_FIELDS = {
'mem': 'mem_usage',
'cpu': 'cpu_load',
'temp': 'cpu_temp',
'freq': 'cpu_freq'
}
DEFAULT_FIELDS = ['mem', 'cpu', 'temp']
LINE_SPACING = 10
LABEL_SPACING = 0
FIELD_WIDTH = 4
def on_loaded(self):
logging.info("memtemp plugin loaded.")
def mem_usage(self):
return int(pwnagotchi.mem_usage() * 100)
return f"{int(pwnagotchi.mem_usage() * 100)}%"
def cpu_load(self):
return int(pwnagotchi.cpu_load() * 100)
return f"{int(pwnagotchi.cpu_load() * 100)}%"
def on_ui_setup(self, ui):
if ui.is_waveshare_v2():
h_pos = (180, 80)
v_pos = (180, 61)
elif ui.is_waveshare_v1():
h_pos = (170, 80)
v_pos = (170, 61)
elif ui.is_waveshare144lcd():
h_pos = (53, 77)
v_pos = (78, 67)
elif ui.is_inky():
h_pos = (140, 68)
v_pos = (165, 54)
elif ui.is_waveshare27inch():
h_pos = (192, 138)
v_pos = (216, 122)
else:
h_pos = (155, 76)
v_pos = (180, 61)
if self.options['orientation'] == "vertical":
ui.add_element('memtemp', LabeledValue(color=BLACK, label='', value=' mem:-\n cpu:-\ntemp:-',
position=v_pos,
label_font=fonts.Small, text_font=fonts.Small))
else:
# default to horizontal
ui.add_element('memtemp', LabeledValue(color=BLACK, label='', value='mem cpu temp\n - - -',
position=h_pos,
label_font=fonts.Small, text_font=fonts.Small))
def on_unload(self, ui):
with ui._lock:
ui.remove_element('memtemp')
def on_ui_update(self, ui):
def cpu_temp(self):
if self.options['scale'] == "fahrenheit":
temp = (pwnagotchi.temperature() * 9 / 5) + 32
symbol = "f"
@@ -85,11 +69,116 @@ class MemTemp(plugins.Plugin):
# default to celsius
temp = pwnagotchi.temperature()
symbol = "c"
return f"{temp}{symbol}"
def cpu_freq(self):
with open('/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq', 'rt') as fp:
return f"{round(float(fp.readline())/1000000, 1)}G"
def pad_text(self, data):
return " " * (self.FIELD_WIDTH - len(data)) + data
def on_ui_setup(self, ui):
try:
# Configure field list
self.fields = self.options['fields'].split(',')
self.fields = [x.strip() for x in self.fields if x.strip() in self.ALLOWED_FIELDS.keys()]
self.fields = self.fields[:3] # limit to the first 3 fields
except Exception:
# Set default value
self.fields = self.DEFAULT_FIELDS
try:
# Configure line_spacing
line_spacing = int(self.options['linespacing'])
except Exception:
# Set default value
line_spacing = self.LINE_SPACING
try:
# Configure position
pos = self.options['position'].split(',')
pos = [int(x.strip()) for x in pos]
if self.options['orientation'] == "vertical":
v_pos = (pos[0], pos[1])
else:
h_pos = (pos[0], pos[1])
except Exception:
# Set default position based on screen type
if ui.is_waveshare_v2():
h_pos = (178, 84)
v_pos = (197, 74)
elif ui.is_waveshare_v1():
h_pos = (170, 80)
v_pos = (165, 61)
elif ui.is_waveshare144lcd():
h_pos = (53, 77)
v_pos = (73, 67)
elif ui.is_inky():
h_pos = (140, 68)
v_pos = (160, 54)
elif ui.is_waveshare27inch():
h_pos = (192, 138)
v_pos = (211, 122)
else:
h_pos = (155, 76)
v_pos = (175, 61)
if self.options['orientation'] == "vertical":
ui.set('memtemp',
" mem:%s%%\n cpu:%s%%\ntemp:%s%s" % (self.mem_usage(), self.cpu_load(), temp, symbol))
# Dynamically create the required LabeledValue objects
for idx, field in enumerate(self.fields):
v_pos_x = v_pos[0]
v_pos_y = v_pos[1] + ((len(self.fields) - 3) * -1 * line_spacing)
ui.add_element(
f"memtemp_{field}",
LabeledValue(
color=BLACK,
label=f"{self.pad_text(field)}:",
value="-",
position=(v_pos_x, v_pos_y + (idx * line_spacing)),
label_font=fonts.Small,
text_font=fonts.Small,
label_spacing=self.LABEL_SPACING,
)
)
else:
# default to horizontal
ui.set('memtemp',
" mem cpu temp\n %s%% %s%% %s%s" % (self.mem_usage(), self.cpu_load(), temp, symbol))
h_pos_x = h_pos[0] + ((len(self.fields) - 3) * -1 * 25)
h_pos_y = h_pos[1]
ui.add_element(
'memtemp_header',
Text(
color=BLACK,
value=" ".join([self.pad_text(x) for x in self.fields]),
position=(h_pos_x, h_pos_y),
font=fonts.Small,
)
)
ui.add_element(
'memtemp_data',
Text(
color=BLACK,
value=" ".join([self.pad_text("-") for x in self.fields]),
position=(h_pos_x, h_pos_y + line_spacing),
font=fonts.Small,
)
)
def on_unload(self, ui):
with ui._lock:
if self.options['orientation'] == "vertical":
for idx, field in enumerate(self.fields):
ui.remove_element(f"memtemp_{field}")
else:
# default to horizontal
ui.remove_element('memtemp_header')
ui.remove_element('memtemp_data')
def on_ui_update(self, ui):
if self.options['orientation'] == "vertical":
for idx, field in enumerate(self.fields):
ui.set(f"memtemp_{field}", getattr(self, self.ALLOWED_FIELDS[field])())
else:
# default to horizontal
data = " ".join([self.pad_text(getattr(self, self.ALLOWED_FIELDS[x])()) for x in self.fields])
ui.set('memtemp_data', data)

View File

@@ -12,7 +12,7 @@ from json.decoder import JSONDecodeError
class OnlineHashCrack(plugins.Plugin):
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '2.0.1'
__version__ = '2.1.0'
__license__ = 'GPL3'
__description__ = 'This plugin automatically uploads handshakes to https://onlinehashcrack.com'
@@ -55,9 +55,9 @@ class OnlineHashCrack(plugins.Plugin):
files=payload,
timeout=timeout)
if 'already been sent' in result.text:
logging.warning(f"{path} was already uploaded.")
logging.debug(f"{path} was already uploaded.")
except requests.exceptions.RequestException as e:
logging.error(f"OHC: Got an exception while uploading {path} -> {e}")
logging.debug(f"OHC: Got an exception while uploading {path} -> {e}")
raise e
def _download_cracked(self, save_file, timeout=120):
@@ -78,6 +78,16 @@ class OnlineHashCrack(plugins.Plugin):
except OSError as os_e:
raise os_e
def on_webhook(self, path, request):
import requests
from flask import redirect
s = requests.Session()
s.get('https://www.onlinehashcrack.com/dashboard')
r = s.post('https://www.onlinehashcrack.com/dashboard', data={'emailTasks': self.options['email'], 'submit': ''})
return redirect(r.url, code=302)
def on_internet_available(self, agent):
"""
Called in manual mode when there's internet connectivity
@@ -100,23 +110,25 @@ class OnlineHashCrack(plugins.Plugin):
if handshake_new:
logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onlinehashcrack.com")
for idx, handshake in enumerate(handshake_new):
display.set('status',
f"Uploading handshake to onlinehashcrack.com ({idx + 1}/{len(handshake_new)})")
display.update(force=True)
display.on_uploading(f"onlinehashcrack.com ({idx + 1}/{len(handshake_new)})")
try:
self._upload_to_ohc(handshake)
if handshake not in reported:
reported.append(handshake)
self.report.update(data={'reported': reported})
logging.info(f"OHC: Successfully uploaded {handshake}")
logging.debug(f"OHC: Successfully uploaded {handshake}")
except requests.exceptions.RequestException as req_e:
self.skip.append(handshake)
logging.error("OHC: %s", req_e)
logging.debug("OHC: %s", req_e)
continue
except OSError as os_e:
self.skip.append(handshake)
logging.error("OHC: %s", os_e)
logging.debug("OHC: %s", os_e)
continue
display.on_normal()
if 'dashboard' in self.options and self.options['dashboard']:
cracked_file = os.path.join(handshake_dir, 'onlinehashcrack.cracked')
if os.path.exists(cracked_file):

View File

@@ -7,14 +7,18 @@
# For Raspberry Pi Zero Ups Power Expansion Board with Integrated Serial Port S3U4
# https://www.ebay.de/itm/For-Raspberry-Pi-Zero-Ups-Power-Expansion-Board-with-Integrated-Serial-Port-S3U4/323873804310
# https://www.aliexpress.com/item/32888533624.html
#
# To display external power supply status you need to bridge the necessary pins on the UPS-Lite board. See instructions in the UPS-Lite repo.
import logging
import struct
import RPi.GPIO as GPIO
import pwnagotchi
import pwnagotchi.plugins as plugins
import pwnagotchi.ui.fonts as fonts
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
# TODO: add enable switch in config.yml an cleanup all to the best place
@@ -43,6 +47,14 @@ class UPS:
except:
return 0.0
def charging(self):
try:
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.IN)
return '+' if GPIO.input(4) == GPIO.HIGH else '-'
except:
return '-'
class UPSLite(plugins.Plugin):
__author__ = 'evilsocket@gmail.com'
@@ -66,7 +78,8 @@ class UPSLite(plugins.Plugin):
def on_ui_update(self, ui):
capacity = self.ups.capacity()
ui.set('ups', "%2i%%" % capacity)
charging = self.ups.charging()
ui.set('ups', "%2i%s" % (capacity, charging))
if capacity <= self.options['shutdown']:
logging.info('[ups_lite] Empty battery (<= %s%%): shuting down' % self.options['shutdown'])
ui.update(force=True, new_data={'status': 'Battery exhausted, bye ...'})

View File

@@ -0,0 +1,36 @@
import os
import logging
import re
import subprocess
from io import TextIOWrapper
from pwnagotchi import plugins
class Watchdog(plugins.Plugin):
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '0.1.0'
__license__ = 'GPL3'
__description__ = 'Restart pwnagotchi when blindbug is detected.'
def __init__(self):
self.options = dict()
self.pattern = re.compile(r'brcmf_cfg80211_nexmon_set_channel.*?Set Channel failed')
def on_loaded(self):
"""
Gets called when the plugin gets loaded
"""
logging.info("Watchdog plugin loaded.")
def on_epoch(self, agent, epoch, epoch_data):
# get last 10 lines
last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl','-n10','-k', '--since', '-5m'],
stdout=subprocess.PIPE).stdout))[-10:])
if len(self.pattern.findall(last_lines)) >= 5:
display = agent.view()
display.set('status', 'Blind-Bug detected. Restarting.')
display.update(force=True)
logging.info('[WATCHDOG] Blind-Bug detected. Restarting.')
mode = 'MANU' if agent.mode == 'manual' else 'AUTO'
import pwnagotchi
pwnagotchi.reboot(mode=mode)

View File

@@ -141,7 +141,7 @@
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
subdomains: 'abcd',
opacity:0.8,
// maxZoom: 19
maxZoom: 20
});
var mymap = L.map('mapdiv');

View File

@@ -230,7 +230,7 @@ class Webgpsmap(plugins.Plugin):
}
# get ap password if exist
check_for = os.path.basename(pos_file[:-9]) + ".pcap.cracked"
check_for = os.path.basename(pos_file).split(".")[0] + ".pcap.cracked"
if check_for in all_files:
gps_data[ssid + "_" + mac]["pass"] = pos.password()

View File

@@ -9,6 +9,7 @@ from datetime import datetime
from pwnagotchi.utils import WifiInfo, FieldNotFoundError, extract_from_pcap, StatusFile, remove_whitelisted
from threading import Lock
from pwnagotchi import plugins
from pwnagotchi._version import __version__ as __pwnagotchi_version__
def _extract_gps_data(path):
@@ -34,14 +35,14 @@ def _format_auth(data):
return out
def _transform_wigle_entry(gps_data, pcap_data):
def _transform_wigle_entry(gps_data, pcap_data, plugin_version):
"""
Transform to wigle entry in file
"""
dummy = StringIO()
# write kismet header
dummy.write(
"WigleWifi-1.4,appRelease=20190201,model=Kismet,release=2019.02.01.{},device=kismet,display=kismet,board=kismet,brand=kismet\n")
"WigleWifi-1.4,appRelease={},model=pwnagotchi,release={},device=pwnagotchi,display=kismet,board=kismet,brand=pwnagotchi\n".format(plugin_version, __pwnagotchi_version__))
dummy.write(
"MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type")
@@ -62,7 +63,7 @@ def _transform_wigle_entry(gps_data, pcap_data):
return dummy.getvalue()
def _send_to_wigle(lines, api_key, timeout=30):
def _send_to_wigle(lines, api_key, donate=True, timeout=30):
"""
Uploads the file to wigle-net
"""
@@ -76,7 +77,7 @@ def _send_to_wigle(lines, api_key, timeout=30):
headers = {'Authorization': f"Basic {api_key}",
'Accept': 'application/json'}
data = {'donate': 'false'}
data = {'donate': 'on' if donate else 'false'}
payload = {'file': dummy, 'type': 'text/csv'}
try:
@@ -106,12 +107,15 @@ class Wigle(plugins.Plugin):
def on_loaded(self):
if 'api_key' not in self.options or ('api_key' in self.options and self.options['api_key'] is None):
logging.error("WIGLE: api_key isn't set. Can't upload to wigle.net")
logging.debug("WIGLE: api_key isn't set. Can't upload to wigle.net")
return
if not 'whitelist' in self.options:
self.options['whitelist'] = list()
if not 'donate' in self.options:
self.options['donate'] = True
self.ready = True
def on_internet_available(self, agent):
@@ -141,21 +145,21 @@ class Wigle(plugins.Plugin):
for gps_file in new_gps_files:
pcap_filename = gps_file.replace('.gps.json', '.pcap')
if not os.path.exists(pcap_filename):
logging.error("WIGLE: Can't find pcap for %s", gps_file)
logging.debug("WIGLE: Can't find pcap for %s", gps_file)
self.skip.append(gps_file)
continue
try:
gps_data = _extract_gps_data(gps_file)
except OSError as os_err:
logging.error("WIGLE: %s", os_err)
logging.debug("WIGLE: %s", os_err)
self.skip.append(gps_file)
continue
except json.JSONDecodeError as json_err:
logging.error("WIGLE: %s", json_err)
logging.debug("WIGLE: %s", json_err)
self.skip.append(gps_file)
continue
if gps_data['Latitude'] == 0 and gps_data['Longitude'] == 0:
logging.warning("WIGLE: Not enough gps-information for %s. Trying again next time.", gps_file)
logging.debug("WIGLE: Not enough gps-information for %s. Trying again next time.", gps_file)
self.skip.append(gps_file)
continue
try:
@@ -165,27 +169,29 @@ class Wigle(plugins.Plugin):
WifiInfo.CHANNEL,
WifiInfo.RSSI])
except FieldNotFoundError:
logging.error("WIGLE: Could not extract all information. Skip %s", gps_file)
logging.debug("WIGLE: Could not extract all information. Skip %s", gps_file)
self.skip.append(gps_file)
continue
except Scapy_Exception as sc_e:
logging.error("WIGLE: %s", sc_e)
logging.debug("WIGLE: %s", sc_e)
self.skip.append(gps_file)
continue
new_entry = _transform_wigle_entry(gps_data, pcap_data)
new_entry = _transform_wigle_entry(gps_data, pcap_data, self.__version__)
csv_entries.append(new_entry)
no_err_entries.append(gps_file)
if csv_entries:
display.set('status', "Uploading gps-data to wigle.net ...")
display.update(force=True)
display.on_uploading('wigle.net')
try:
_send_to_wigle(csv_entries, self.options['api_key'])
_send_to_wigle(csv_entries, self.options['api_key'], donate=self.options['donate'])
reported += no_err_entries
self.report.update(data={'reported': reported})
logging.info("WIGLE: Successfully uploaded %d files", len(no_err_entries))
except requests.exceptions.RequestException as re_e:
self.skip += no_err_entries
logging.error("WIGLE: Got an exception while uploading %s", re_e)
logging.debug("WIGLE: Got an exception while uploading %s", re_e)
except OSError as os_e:
self.skip += no_err_entries
logging.error("WIGLE: Got the following error: %s", os_e)
logging.debug("WIGLE: Got the following error: %s", os_e)
display.on_normal()

View File

@@ -39,7 +39,7 @@ class WpaSec(plugins.Plugin):
files=payload,
timeout=timeout)
if ' already submitted' in result.text:
logging.warning("%s was already submitted.", path)
logging.debug("%s was already submitted.", path)
except requests.exceptions.RequestException as req_e:
raise req_e
@@ -82,6 +82,13 @@ class WpaSec(plugins.Plugin):
self.options['whitelist'] = list()
self.ready = True
logging.info("WPA_SEC: plugin loaded")
def on_webhook(self, path, request):
from flask import make_response, redirect
response = make_response(redirect(self.options['api_url'], code=302))
response.set_cookie('key', self.options['api_key'])
return response
def on_internet_available(self, agent):
"""
@@ -104,21 +111,23 @@ class WpaSec(plugins.Plugin):
if handshake_new:
logging.info("WPA_SEC: Internet connectivity detected. Uploading new handshakes to wpa-sec.stanev.org")
for idx, handshake in enumerate(handshake_new):
display.set('status', f"Uploading handshake to wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})")
display.update(force=True)
display.on_uploading(f"wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})")
try:
self._upload_to_wpasec(handshake)
reported.append(handshake)
self.report.update(data={'reported': reported})
logging.info("WPA_SEC: Successfully uploaded %s", handshake)
logging.debug("WPA_SEC: Successfully uploaded %s", handshake)
except requests.exceptions.RequestException as req_e:
self.skip.append(handshake)
logging.error("WPA_SEC: %s", req_e)
logging.debug("WPA_SEC: %s", req_e)
continue
except OSError as os_e:
logging.error("WPA_SEC: %s", os_e)
logging.debug("WPA_SEC: %s", os_e)
continue
display.on_normal()
if 'download_results' in self.options and self.options['download_results']:
cracked_file = os.path.join(handshake_dir, 'wpa-sec.cracked.potfile')
if os.path.exists(cracked_file):

View File

@@ -49,8 +49,11 @@ class Display(View):
def is_lcdhat(self):
return self._implementation.name == 'lcdhat'
def is_dfrobot(self):
return self._implementation.name == 'dfrobot'
def is_dfrobot_v1(self):
return self._implementation.name == 'dfrobot_v1'
def is_dfrobot_v2(self):
return self._implementation.name == 'dfrobot_v2'
def is_waveshare144lcd(self):
return self._implementation.name == 'waveshare144lcd'

View File

@@ -20,7 +20,9 @@ ANGRY = "(-_-')"
FRIEND = '(♥‿‿♥)'
BROKEN = '(☓‿‿☓)'
DEBUG = '(#__#)'
UPLOAD = '(1__0)'
UPLOAD1 = '(1__1)'
UPLOAD2 = '(0__1)'
def load_from_config(config):
for face_name, face_value in config.items():

View File

@@ -2,7 +2,8 @@ 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 DFRobot
from pwnagotchi.ui.hw.dfrobot1 import DFRobotV1
from pwnagotchi.ui.hw.dfrobot2 import DFRobotV2
from pwnagotchi.ui.hw.waveshare1 import WaveshareV1
from pwnagotchi.ui.hw.waveshare2 import WaveshareV2
from pwnagotchi.ui.hw.waveshare27inch import Waveshare27inch
@@ -27,8 +28,11 @@ def display_for(config):
if config['ui']['display']['type'] == 'lcdhat':
return LcdHat(config)
if config['ui']['display']['type'] == 'dfrobot':
return DFRobot(config)
if config['ui']['display']['type'] == 'dfrobot_1':
return DFRobotV1(config)
if config['ui']['display']['type'] == 'dfrobot_2':
return DFRobotV2(config)
elif config['ui']['display']['type'] == 'waveshare_1':
return WaveshareV1(config)

View File

@@ -3,9 +3,9 @@ import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DFRobot(DisplayImpl):
class DFRobotV1(DisplayImpl):
def __init__(self, config):
super(DFRobot, self).__init__(config, 'dfrobot')
super(DFRobotV1, self).__init__(config, 'dfrobot_1')
self._display = None
def layout(self):
@@ -31,8 +31,8 @@ class DFRobot(DisplayImpl):
return self._layout
def initialize(self):
logging.info("initializing dfrobot display")
from pwnagotchi.ui.hw.libs.dfrobot.dfrobot import DFRobot
logging.info("initializing dfrobot1 display")
from pwnagotchi.ui.hw.libs.dfrobot.v1.dfrobot import DFRobot
self._display = DFRobot()
def render(self, canvas):

View File

@@ -0,0 +1,43 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DFRobotV2(DisplayImpl):
def __init__(self, config):
super(DFRobotV2, self).__init__(config, 'dfrobot_2')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):
logging.info("initializing dfrobot2 display")
from pwnagotchi.ui.hw.libs.dfrobot.v2.dfrobot import DFRobot
self._display = DFRobot()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear(0xFF)

View File

@@ -42,6 +42,12 @@ class Inky(DisplayImpl):
from pwnagotchi.ui.hw.libs.inkyphat.inkyphatfast import InkyPHATFast
self._display = InkyPHATFast('black')
self._display.set_border(InkyPHATFast.BLACK)
elif self.config['color'] == 'auto':
from inky.auto import auto
self._display = auto()
self._display.set_border(self._display.BLACK)
self._layout['width'] = self._display.WIDTH
self._layout['height'] = self._display.HEIGHT
else:
from inky import InkyPHAT
self._display = InkyPHAT(self.config['color'])

View File

@@ -0,0 +1,66 @@
# DFRobot display support
import logging
from . import dfrobot_epaper
#Resolution of display
WIDTH = 250
HEIGHT = 122
RASPBERRY_SPI_BUS = 0
RASPBERRY_SPI_DEV = 0
RASPBERRY_PIN_CS = 27
RASPBERRY_PIN_CD = 17
RASPBERRY_PIN_BUSY = 4
class DFRobot:
def __init__(self):
self._display = dfrobot_epaper.DFRobot_Epaper_SPI(RASPBERRY_SPI_BUS, RASPBERRY_SPI_DEV, RASPBERRY_PIN_CS, RASPBERRY_PIN_CD, RASPBERRY_PIN_BUSY)
self._display.begin()
self.clear(0xFF)
self.FULL = self._display.FULL
self.PART = self._display.PART
def getbuffer(self, image):
if HEIGHT % 8 == 0:
linewidth = HEIGHT // 8
else:
linewidth = HEIGHT // 8 + 1
buf = [0xFF] * (linewidth * WIDTH)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
if (imwidth == HEIGHT and imheight == WIDTH):
for y in range(imheight):
for x in range(imwidth):
if pixels[x,y] == 0:
x = imwidth - x
buf[x // 8 + y * linewidth] &= ~(0x80 >> (x % 8))
elif (imwidth == WIDTH and imheight == HEIGHT):
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = WIDTH - x - 1
if pixels[x,y] == 0:
newy = imwidth - newy - 1
buf[newx // 8 + newy * linewidth] &= ~(0x80 >> (y % 8))
return buf
def flush(self, type):
self._display.flush(type)
def display(self, buf):
self._display.setBuffer(buf)
self.flush(self._display.PART)
def clear(self, color):
if HEIGHT % 8 == 0:
linewidth = HEIGHT // 8
else:
linewidth = HEIGHT // 8 + 1
buf = [color] * (linewidth * WIDTH)
self._display.setBuffer(buf)
self.flush(self._display.FULL)

View File

@@ -0,0 +1,673 @@
# -*- coding:utf-8 -*-
import sys
from .dfrobot_printString import PrintString
from .dfrobot_fonts import Fonts
def color24to16(color):
return (((color >> 8) & 0xf800) | ((color >> 5) & 0x7e0) | ((color >> 3) & 0x1f))
def color16to24(color):
return (((color & 0xf800) << 8) | ((color & 0x7e0) << 5) | ((color & 0x1f) << 3))
def swap(o1, o2):
return (o2, o1)
class DFRobot_Display(PrintString):
WHITE24 = 0xffffff
SILVER24 = 0xc0c0c0
GRAY24 = 0x808080
BLACK24 = 0x000000
RED24 = 0xff0000
MAROON24 = 0x800000
YELLOW24 = 0xffff00
OLIVE24 = 0x808000
GREEN24 = 0x00ff00
DARKGREEN24 = 0x008000
CYAN24 = 0x00ffff
BLUE24 = 0x0000ff
NAVY24 = 0x000080
FUCHSIA24 = 0xff00ff
PURPLE24 = 0x800080
TEAL24 = 0x008080
WHITE16 = color24to16(WHITE24)
SILVER16 = color24to16(SILVER24)
GRAY16 = color24to16(GRAY24)
BLACK16 = color24to16(BLACK24)
RED16 = color24to16(RED24)
MAROON16 = color24to16(MAROON24)
YELLOW16 = color24to16(YELLOW24)
OLIVE16 = color24to16(OLIVE24)
GREEN16 = color24to16(GREEN24)
DARKGREEN16 = color24to16(DARKGREEN24)
CYAN16 = color24to16(CYAN24)
BLUE16 = color24to16(BLUE24)
NAVY16 = color24to16(NAVY24)
FUCHSIA16 = color24to16(FUCHSIA24)
PURPLE16 = color24to16(PURPLE24)
TEAL16 = color24to16(TEAL24)
WHITE = WHITE16
SILVER = SILVER16
GRAY = GRAY16
BLACK = BLACK16
RED = RED16
MAROON = MAROON16
YELLOW = YELLOW16
OLIVE = OLIVE16
GREEN = GREEN16
DARKGREEN = DARKGREEN16
CYAN = CYAN16
BLUE = BLUE16
NAVY = NAVY16
FUCHSIA = FUCHSIA16
PURPLE = PURPLE16
TEAL = TEAL16
POSITIVE = 1
REVERSE = -1
BITMAP_TBMLLR = "TBMLLR"
BITMAP_TBMRLL = "TBMRLL"
BITMAP_BTMLLR = "BTMLLR"
BITMAP_BTMRLL = "BTMRLL"
BITMAP_LRMTLB = "LRMTLB"
BITMAP_LRMBLT = "LRMBLT"
BITMAP_RLMTLB = "RLMTLB"
BIMTAP_RLMBLT = "RLMBLT"
BITMAP_UNKNOW = "UNKNOW"
def __init__(self, w, h):
PrintString.__init__(self)
print("DFRobot_Display init " + str(w) + " " + str(h))
self._width = w
self._height = h
self._lineWidth = 1
self._bitmapSize = 1
self._bitmapFmt = ""
self._bmpFmt = self.BITMAP_TBMLLR
self._fonts = Fonts()
self._textSize = 1
self._textColor = self.BLACK
self._textBackground = self.WHITE
self._textCursorX = 0
self._textCursorY = 0
self._textIntervalRow = 0
self._textIntervalCol = 0
def _ternaryExpression(self, condition, o1, o2):
if condition:
return o1
return o2
def _getDirection(self, value):
if value >= 0:
return 1
return -1
def color16to24(self, color):
return color16to24(color)
def color24to16(self, color):
return color24to16(color)
def setColorTo16(self):
self.WHITE = self.WHITE16
self.SILVER = self.SILVER16
self.GRAY = self.GRAY16
self.BLACK = self.BLACK16
self.RED = self.RED16
self.MAROON = self.MAROON16
self.YELLOW = self.YELLOW16
self.OLIVE = self.OLIVE16
self.GREEN = self.GREEN16
self.DARKGREEN = self.DARKGREEN16
self.CYAN = self.CYAN16
self.BLUE = self.BLUE16
self.NAVY = self.NAVY16
self.FUCHSIA = self.FUCHSIA16
self.PURPLE = self.PURPLE16
self.TEAL = self.TEAL16
def setColorTo24(self):
self.WHITE = self.WHITE24
self.SILVER = self.SILVER24
self.GRAY = self.GRAY24
self.BLACK = self.BLACK24
self.RED = self.RED24
self.MAROON = self.MAROON24
self.YELLOW = self.YELLOW24
self.OLIVE = self.OLIVE24
self.GREEN = self.GREEN24
self.DARKGREEN = self.DARKGREEN24
self.CYAN = self.CYAN24
self.BLUE = self.BLUE24
self.NAVY = self.NAVY24
self.FUCHSIA = self.FUCHSIA24
self.PURPLE = self.PURPLE24
self.TEAL = self.TEAL24
def setLineWidth(self, w):
if w < 0:
return
self._lineWidth = w
def setTextFormat(self, size, color, background, intervalRow = 2, intervalCol = 0):
self._textColor = color
self._textIntervalRow = intervalRow
self._textIntervalCol = intervalCol
self._textBackground = background
if size < 0:
return
self._textSize = size
def setTextCursor(self, x, y):
self._textCursorX = int(x)
self._textCursorY = int(y)
def setBitmapSize(self, size):
if size < 0:
return
self._bitmapSize = size
def setBitmapFmt(self, fmt):
self._bmpFmt = fmt
def setExFonts(self, obj):
self._fonts.setExFonts(obj)
def setExFontsFmt(self, width, height):
self._fonts.setExFontsFmt(width, height)
def setEnableDefaultFonts(self, opt):
self._fonts.setEnableDefaultFonts(opt)
def pixel(self, x, y, color):
pass
def clear(self, color):
self.fillRect(0, 0, self._width, self._height, color)
self._textCursorX = 0
self._textCursorY = 0
def VLine(self, x, y, h, color):
x = int(x)
y = int(y)
h = int(h)
direction = self._getDirection(h)
x -= self._lineWidth // 2
h = self._ternaryExpression(h > 0, h, -h)
for i in range(self._ternaryExpression(h > 0, h, - h)):
xx = x
for j in range(self._lineWidth):
self.pixel(xx, y, color)
xx += 1
y += direction
def HLine(self, x, y, w, color):
x = int(x)
y = int(y)
w = int(w)
direction = self._getDirection(w)
y -= self._lineWidth // 2
for i in range(self._ternaryExpression(w > 0, w, - w)):
yy = y
for j in range(self._lineWidth):
self.pixel(x, yy, color)
yy += 1
x += direction
def line(self, x, y, x1, y1, color):
x = int(x)
y = int(y)
x1 = int(x1)
y1 = int(y1)
if x == x1:
self.VLine(x, y, y1 - y, color)
return
if y == y1:
self.HLine(x, y, x1 - x, color)
return
dx = abs(x1 - x)
dy = abs(y1 - y)
dirX = self._ternaryExpression(x < x1, 1, -1)
dirY = self._ternaryExpression(y < y1, 1, -1)
if dx > dy:
err = dx / 2
for i in range(dx):
self.HLine(x, y, 1, color)
x += dirX
err -= dy
if err < 0:
err += dx
y += dirY
self.HLine(x1, y1, 1, color)
else:
err = dy / 2
for i in range(dy):
self.VLine(x, y, 1, color)
y += dirY
err -= dx
if err < 0:
err += dy
x += dirX
self.VLine(x1, y1, 1, color)
def triangle(self, x, y, x1, y1, x2, y2, color):
self.line(x, y, x1, y1, color)
self.line(x1, y1, x2, y2, color)
self.line(x2, y2, x, y, color)
def fillTriangle(self, x, y, x1, y1, x2, y2, color):
self.line(x, y, x1, y1, color)
self.line(x1, y1, x2, y2, color)
self.line(x2, y2, x, y, color)
x = int(x)
y = int(y)
x1 = int(x1)
y1 = int(y1)
x2 = int(x2)
y2 = int(y2)
temp = self._lineWidth
self._lineWidth = 1
if x == x1 and x == x2:
ymax = max([y, y1, y2])
ymin = min([y, y1, y2])
self.HLine(x, ymin, ymax - ymin, color)
self._lineWidth = temp
return
if y == y1 and y == y2:
xmax = max([x, x1, x2])
xmin = max([x, x1, x2])
self.VLine(xmin, y, xmax - xmin, color)
self._lineWidth = temp
return
direction = self.POSITIVE
if y == y1 or y1 == y2 or y == y2:
if y == y1:
(x, x2) = swap(x, x2)
(y, y2) = swap(y, y2)
elif y == y2:
(x, x1) = swap(x, x1)
(y, y1) = swap(y, y1)
if y > y1:
direction = self.REVERSE
if x1 > x2:
(x1, x2) = swap(x1, x2)
(y1, y2) = swap(y1, y2)
else:
if y > y1:
(x, x1) = swap(x, x1)
(y, y1) = swap(y, y1)
if y > y2:
(x, x2) = swap(x, x2)
(y, y2) = swap(y, y2)
if y1 > y2:
(x1, x2) = swap(x1, x2)
(y1, y2) = swap(y1, y2)
dx1 = x1 - x
dx2 = x2 - x
dx3 = x2 - x1
dy1 = y1 - y
dy2 = y2 - y
dy3 = y2 - y1
if direction == self.POSITIVE:
for i in range(dy1):
self.HLine(x + dx1 * i / dy1, y + i, (x + dx2 * i / dy2) - (x + dx1 * i / dy1) + 1, color)
for i in range(dy3):
self.HLine(x1 + dx3 * i / dy3, y1 + i, (x + dx2 * (i + dy1) / dy2) - (x1 + dx3 * i / dy3) + 1, color)
else:
y = y1 + dy1
dy1 = - dy1
for i in range(dy1):
self.HLine(x + dx1 * i / dy1, y1 + dy1 - i, (x + dx2 * i / dy1) - (x + dx1 * i / dy1) + 1, color)
self._lineWidth = temp
def rect(self, x, y, w, h, color):
if w < 0:
x += w
w = -w
if h < 0:
y += h
h = -h
self.HLine(x - self._lineWidth // 2, y, w + self._lineWidth, color)
self.HLine(x - self._lineWidth // 2, y + h, w + self._lineWidth, color)
self.VLine(x, y - self._lineWidth // 2, h + self._lineWidth, color)
self.VLine(x + w, y - self._lineWidth // 2, h + self._lineWidth, color)
def fillRect(self, x, y, w, h, color):
temp = self._lineWidth
self._lineWidth = 1
if w < 0:
x += w
w = abs(w)
for i in range(w):
self.VLine(x + i, y, h, color)
self._lineWidth = temp
QUADRANT_1 = 1
QUADRANT_2 = 2
QUADRANT_3 = 4
QUADRANT_4 = 8
QUADRANT_ALL = 15
def circleHelper(self, x, y, r, quadrant, color):
x = int(x)
y = int(y)
r = abs(int(r))
vx = 0
vy = r
dx = 1
dy = -2 * r
p = 1 - r
if quadrant & self.QUADRANT_1:
self.VLine(x + r, y, 1, color)
if quadrant & self.QUADRANT_2:
self.VLine(x, y - r, 1, color)
if quadrant & self.QUADRANT_3:
self.VLine(x - r, y, 1, color)
if quadrant & self.QUADRANT_4:
self.VLine(x, y + r, 1, color)
halfLineWidth = self._lineWidth // 2
while vx < vy:
if p >= 0:
vy -= 1
dy += 2
p += dy
vx += 1
dx += 2
p += dx
if quadrant & self.QUADRANT_1:
self.fillRect(x + vx - halfLineWidth, y - vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 1
self.fillRect(x + vy - halfLineWidth, y - vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 1
if quadrant & self.QUADRANT_2:
self.fillRect(x - vx - halfLineWidth, y - vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 2
self.fillRect(x - vy - halfLineWidth, y - vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 2
if quadrant & self.QUADRANT_3:
self.fillRect(x - vx - halfLineWidth, y + vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 3
self.fillRect(x - vy - halfLineWidth, y + vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 3
if quadrant & self.QUADRANT_4:
self.fillRect(x + vx - halfLineWidth, y + vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 4
self.fillRect(x + vy - halfLineWidth, y + vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 4
def circle(self, x, y, r, color):
self.circleHelper(x, y, r, self.QUADRANT_ALL, color)
def fillCircleHelper(self, x, y, r, quadrant, color):
x = int(x)
y = int(y)
r = abs(int(r))
temp = self._lineWidth
self._lineWidth = 1
vx = 0
vy = r
dx = 1
dy = -2 * r
p = 1 - r
if quadrant & self.QUADRANT_1:
self.HLine(x, y, r + 1, color)
if quadrant & self.QUADRANT_2:
self.VLine(x, y, - r - 1, color)
if quadrant & self.QUADRANT_3:
self.HLine(x, y, - r - 1, color)
if quadrant & self.QUADRANT_4:
self.VLine(x, y, r + 1, color)
while vx < vy:
if p >= 0:
vy -= 1
dy += 2
p += dy
vx += 1
dx += 2
p += dx
if quadrant & self.QUADRANT_1:
self.VLine(x + vx, y - vy, vy, color) # quadrant 1
self.VLine(x + vy, y - vx, vx, color) # quadrant 1
if quadrant & self.QUADRANT_2:
self.VLine(x - vx, y - vy, vy, color) # quadrant 2
self.VLine(x - vy, y - vx, vx, color) # quadrant 2
if quadrant & self.QUADRANT_3:
self.VLine(x - vx, y + vy, - vy, color) # quadrant 3
self.VLine(x - vy, y + vx, - vx, color) # quadrant 3
if quadrant & self.QUADRANT_4:
self.VLine(x + vx, y + vy, - vy, color) # quadrant 4
self.VLine(x + vy, y + vx, - vx, color) # quadrant 4
self._lineWidth = temp
def fillCircle(self, x, y, r, color):
self.fillCircleHelper(x, y, r, self.QUADRANT_ALL, color)
def roundRect(self, x, y, w, h, r, color):
x = int(x)
y = int(y)
w = int(w)
h = int(h)
r = abs(int(r))
if w < 0:
x += w
w = abs(w)
if h < 0:
y += h
h = abs(h)
self.HLine(x + r, y, w - 2 * r + 1, color)
self.HLine(x + r, y + h, w - 2 * r + 1, color)
self.VLine(x, y + r, h - 2 * r + 1, color)
self.VLine(x + w, y + r, h - 2 * r + 1, color)
self.circleHelper(x + r, y + r, r, self.QUADRANT_2, color)
self.circleHelper(x + w - r, y + r, r, self.QUADRANT_1, color)
self.circleHelper(x + r, y + h - r, r, self.QUADRANT_3, color)
self.circleHelper(x + w - r, y + h - r, r, self.QUADRANT_4, color)
def fillRoundRect(self, x, y, w, h, r, color):
x = int(x)
y = int(y)
w = int(w)
h = int(h)
r = abs(int(r))
if w < 0:
x += w
w = abs(w)
if h < 0:
y += h
h = abs(h)
self.fillRect(x + r, y, w - 2 * r, h, color)
self.fillRect(x, y + r, r, h - 2 * r, color)
self.fillRect(x + w - r, y + r, r, h - 2 * r, color)
self.fillCircleHelper(x + r, y + r, r, self.QUADRANT_2, color)
self.fillCircleHelper(x + w - r - 1, y + r, r, self.QUADRANT_1, color)
self.fillCircleHelper(x + r, y + h - r - 1, r, self.QUADRANT_3, color)
self.fillCircleHelper(x + w - r - 1, y + h - r - 1, r, self.QUADRANT_4, color)
def _bitmapHelper(self, increaseAxis, staticAxis, data, dataBit, exchange, color, background):
for i in data:
for j in range(8):
if i & dataBit:
if exchange:
self.fillRect(staticAxis, increaseAxis, self._bitmapSize, self._bitmapSize, color)
else:
self.fillRect(increaseAxis, staticAxis, self._bitmapSize, self._bitmapSize, color)
else:
if exchange:
self.fillRect(staticAxis, increaseAxis, self._bitmapSize, self._bitmapSize, background)
else:
self.fillRect(increaseAxis, staticAxis, self._bitmapSize, self._bitmapSize, background)
increaseAxis += self._bitmapSize
if dataBit & 0x80:
i <<= 1
else:
i >>= 1
def bitmap(self, x, y, bitmap, w, h, color, background):
if w < 0 or h < 0:
return
x = abs(int(x))
y = abs(int(y))
if self._bmpFmt == self.BITMAP_TBMLLR:
oneLineDataLen = (w - 1) // 8 + 1
for i in range(h):
yMask = y + i * self._bitmapSize
self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, False, color, background)
elif self._bmpFmt == self.BITMAP_TBMRLL:
oneLineDataLen = (w - 1) // 8 + 1
for i in range(h):
yMask = y + i * self._bitmapSize
self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, False, color, background)
elif self._bmpFmt == self.BITMAP_BTMLLR:
oneLineDataLen = (w - 1) // 8 + 1
for i in range(h):
yMask = y + h * self._bitmapSize - i * self._bitmapSize
self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, False, color, background)
elif self._bmpFmt == self.BITMAP_BTMRLL:
oneLineDataLen = (w - 1) // 8 + 1
for i in range(h):
yMask = y + h * self._bitmapSize - i * self._bitmapSize
self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, False, color, background)
elif self._bmpFmt == self.BITMAP_LRMTLB:
oneLineDataLen = (h - 1) // 8 + 1
for i in range(w):
xMask = x + i * self._bitmapSize
self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, True, color, background)
elif self._bmpFmt == self.BITMAP_LRMBLT:
oneLineDataLen = (h - 1) // 8 + 1
for i in range(w):
xMask = x + i * self._bitmapSize
self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, True, color, background)
elif self._bmpFmt == self.BITMAP_RLMTLB:
oneLineDataLen = (h - 1) // 8 + 1
for i in range(w):
xMask = x + w * self._bitmapSize - i * self._bitmapSize
self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, True, color, background)
elif self._bmpFmt == self.BIMTAP_RLMBLT:
oneLineDataLen = (h - 1) // 8 + 1
for i in range(w):
xMask = x + w * self._bitmapSize - i * self._bitmapSize
self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, True, color, background)
def _bytesToNumber(self, data):
r = 0
i = len(data)
while i > 0:
i -= 1
r = r << 8 | data[i]
return r
def _getQuads(self, data, count):
r = []
for i in range(count):
r.append(data[i * 4 + 54 : i * 4 + 58])
return r
BITMAP_COMPRESSION_NO = 0
BITMAP_COMPRESSION_RLE8 = 1
BITMAP_COMPRESSION_RLE4 = 2
BITMAP_COMPRESSION_FIELDS = 3
def startDrawBitmapFile(self, x, y):
pass
def bitmapFileHelper(self, buf):
pass
def endDrawBitmapFile(self):
pass
def bitmapFile(self, x, y, path):
try:
f = open(path, "rb")
except:
print("open file error")
return
c = bytearray(f.read())
f.close()
if c[0] != 0x42 and c[1] != 0x4d:
print("file error")
print(c[0])
print(c[1])
return
DIBOffset = self._bytesToNumber(c[10:14])
width = self._bytesToNumber(c[18:22])
height = self._bytesToNumber(c[22:26])
colorBits = self._bytesToNumber(c[28:30])
compression = self._bytesToNumber(c[30:32])
# print("w: %d, h: %d, colorBits: %d" %(width, height, colorBits))
if colorBits == 24:
width3 = width * 3
for i in range(height):
self.startDrawBitmapFile(x, y + height - i)
buf = []
left = DIBOffset + i * width3
i = 0
while i < width3:
buf.append(c[left + i + 2])
buf.append(c[left + i + 1])
buf.append(c[left + i + 0])
i += 3
self.bitmapFileHelper(buf)
self.endDrawBitmapFile()
elif colorBits == 1:
quads = self._getQuads(c, 2)
addr = DIBOffset
if compression == self.BITMAP_COMPRESSION_NO:
addrCountComplement = (width // 8 + 1) % 4
if addrCountComplement != 0:
addrCountComplement = 4 - addrCountComplement
for i in range(height):
w = width
addrCount = 0
self.startDrawBitmapFile(x, y + height - i - 1)
buf = []
while w > 0:
d = c[addr + addrCount]
addrCount = addrCount + 1
j = 8
while w > 0 and j > 0:
j -= 1
quad = d & (0x01 << j)
if quad > 0:
quad = 1
buf.append(quads[quad][2])
buf.append(quads[quad][1])
buf.append(quads[quad][0])
w -= 1
self.bitmapFileHelper(buf)
addrCount += addrCountComplement
addr += addrCount
self.endDrawBitmapFile()
else:
print("dont support this bitmap file format yet")
def writeOneChar(self, c):
if len(c) > 1:
c = c[0]
(l, width, height, fmt) = self._fonts.getOneCharacter(c)
temp = self._bmpFmt
self._bmpFmt = fmt
ts = self._textSize
if ord(c) == ord("\n"):
self._textCursorX = 0
self._textCursorY += height * ts + self._textIntervalCol
elif len(l):
temp1 = self._bitmapSize
self._bitmapSize = ts
self._textCursorX += self._textIntervalRow
if self._textCursorX + ts * width > self._width:
self.fillRect(self._textCursorX, self._textCursorY, self._width - self._textCursorX, self._fonts._extensionFontsHeight * ts + self._textIntervalCol, self._textBackground)
self._textCursorX = self._textIntervalRow
self._textCursorY += ts * self._fonts._extensionFontsHeight + self._textIntervalCol
self.fillRect(self._textCursorX, self._textCursorY, self._fonts._extensionFontsWidth * ts + self._textIntervalRow, self._fonts._extensionFontsHeight * ts + self._textIntervalCol, self._textBackground)
self.bitmap(self._textCursorX, self._textCursorY, l, width, height, self._textColor, self._textBackground)
self._textCursorX += ts * width
self._bitmapSize = temp1
self._bmpFmt = temp

View File

@@ -0,0 +1,69 @@
# -*- coding:utf-8 -*-
import json
class Fonts:
def __init__(self):
self._haveFontsABC = False
self._fontsABC = {}
self._fontsABCWidth = 0
self._fontsABCHeight = 0
self._fontsABCFmt = ""
self._haveExtensionFonts = False
self._extensionFontsWidth = 0
self._extensionFontsHeight = 0
self._enableDefaultFonts = True
def setFontsABC(self, fonts):
self._haveFontsABC = True
self._fontsABC = fonts.fonts
self._fontsABCWidth = fonts.width
self._fontsABCHeight = fonts.height
self._fontsABCFmt = fonts.fmt
self._extensionFontsWidth = fonts.width * 2
self._extensionFontsHeight = fonts.height * 2
def setExFonts(self, obj):
self._haveExtensionFonts = True
self._extensionFonts = obj
self._enableDefaultFonts = False
def setEnableDefaultFonts(self, opt):
if opt:
self._enableDefaultFonts = True
else:
self._enableDefaultFonts = False
def setExFontsFmt(self, width, height):
if self._haveExtensionFonts:
self._extensionFonts.setFmt(width, height)
self._extensionFontsWidth = width
self._extensionFontsHeight = height
def getOneCharacter(self, c):
w = 0
h = 0
fmt = "UNKNOW"
rslt = []
done = False
if self._haveFontsABC and self._enableDefaultFonts:
try:
rslt = self._fontsABC[c]
w = self._fontsABCWidth
h = self._fontsABCHeight
fmt = self._fontsABCFmt
done = True
except:
# print("try get fonts ABC faild")
pass
if self._haveExtensionFonts and done == False:
try:
(rslt, w, h, fmt) = self._extensionFonts.getOne(c)
done = True
except:
print("try get unicode fonts faild: %s" %(c))
return (rslt, w, h, fmt)

View File

@@ -0,0 +1,25 @@
# -*- coding:utf-8 -*-
import sys
class PrintString:
def __init__(self):
pass
def writeOneChar(self, ch):
pass
def printStr(self, c):
try:
c = str(c)
except:
return
if sys.version_info.major == 2:
c = c.decode("utf-8")
for i in c:
self.writeOneChar(i)
def printStrLn(self, c):
self.printStr(c)
self.writeOneChar("\n")

View File

@@ -0,0 +1,250 @@
# -*- coding:utf-8 -*-
import time
import sys
sys.path.append("..")
import RPi.GPIO as RPIGPIO
from .dfrobot_display.dfrobot_display import DFRobot_Display
from .display_extension import fonts_8_16 as fonts_ABC
try:
from .spi import SPI
from .gpio import GPIO
except:
print("unknow platform")
exit()
CONFIG_IL0376F = {
}
CONFIG_IL3895 = {
}
class DFRobot_Epaper(DFRobot_Display):
XDOT = 128
YDOT = 250
FULL = True
PART = False
def __init__(self, width = 250, height = 122):
DFRobot_Display.__init__(self, width, height)
# length = width * height // 8
length = 4000
self._displayBuffer = bytearray(length)
i = 0
while i < length:
self._displayBuffer[i] = 0xff
i = i + 1
self._isBusy = False
self._busyExitEdge = GPIO.RISING
self._fonts.setFontsABC(fonts_ABC)
self.setExFontsFmt(16, 16)
def _busyCB(self, channel):
self._isBusy = False
def setBusyExitEdge(self, edge):
if edge != GPIO.HIGH and edge != GPIO.LOW:
return
self._busyEdge = edge
def begin(self):
pass
#self._init()
#self._powerOn()
#self.setBusyCB(self._busyCB)
#self._powerOn()
def setBuffer(self, buffer):
self._displayBuffer = buffer
def pixel(self, x, y, color):
if x < 0 or x >= self._width:
return
if y < 0 or y >= self._height:
return
x = int(x)
y = int(y)
m = int(x * 16 + (y + 1) / 8)
sy = int((y + 1) % 8)
if color == self.WHITE:
if sy != 0:
self._displayBuffer[m] = self._displayBuffer[m] | int(pow(2, 8 - sy))
else:
self._displayBuffer[m - 1] = self._displayBuffer[m - 1] | 1
elif color == self.BLACK:
if sy != 0:
self._displayBuffer[m] = self._displayBuffer[m] & (0xff - int(pow(2, 8 - sy)))
else:
self._displayBuffer[m - 1] = self._displayBuffer[m - 1] & 0xfe
def _initLut(self, mode):
if mode == self.FULL:
self.writeCmdAndData(0x32, [ 0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x50, 0x90, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x50, 0x90, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0x0F, 0x00, 0x00, 0x00,
0x0F, 0x0F, 0x00, 0x00, 0x03,
0x0F, 0x0F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
])
elif mode == self.PART:
self.writeCmdAndData(0x32, [0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x50, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,
0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0f, 0x00, 0x00, 0x00, 0x00,
0x0, 0x00, 0x00, 0x00, 0x00,
0x0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
])
def _setRamData(self, xStart, xEnd, yStart, yStart1, yEnd, yEnd1):
self.writeCmdAndData(0x44, [xStart, xEnd])
self.writeCmdAndData(0x45, [yStart, yStart1, yEnd, yEnd1])
def _setRamPointer(self, x, y, y1):
self.writeCmdAndData(0x4e, [x])
self.writeCmdAndData(0x4f, [y, y1])
def _init(self,mode):
self.writeCmdAndData(0x12, [])
self.writeCmdAndData(0x01, [0xf9, 0x00, 0x00])
self.writeCmdAndData(0x74, [0x54])
self.writeCmdAndData(0x7e, [0x3b])
self.writeCmdAndData(0x11, [0x01])
self._setRamData(0x00, 0x0f, 0xf9,0x00, 0x00, 0x00)
self.writeCmdAndData(0x3c, [0x03])
self._setRamPointer(0x00, 0xf9, 0x00)
self.writeCmdAndData(0x21, [0x08])
self.writeCmdAndData(0x2c, [0x50])
self.writeCmdAndData(0x03, [0x15])
self.writeCmdAndData(0x04, [0x41,0xa8,0x32])
self.writeCmdAndData(0x3a, [0x2c])
self.writeCmdAndData(0x3b, [0x0b])
self.writeCmdAndData(0x0c, [0x8b,0x9c,0x96,0x0f])
def _writeDisRam(self, sizeX, sizeY):
if sizeX % 8 != 0:
sizeX = sizeX + (8 - sizeX % 8)
sizeX = sizeX // 8
self.writeCmdAndData(0x24, self._displayBuffer[0: sizeX * sizeY])
def _updateDis(self, mode):
if mode == self.FULL:
self.writeCmdAndData(0x22, [0xc7])
elif mode == self.PART:
self.writeCmdAndData(0x22, [0xc7])
else:
return
self.writeCmdAndData(0x20, [])
def _waitBusyExit(self):
temp = 0
while self.readBusy() != False:
time.sleep(0.01)
temp = temp + 1
if (temp % 200) == 0:
print("waitBusyExit")
def _powerOn(self):
self.writeCmdAndData(0x22, [0xC0])
self.writeCmdAndData(0x20, [])
def _powerOff(self):
self.writeCmdAndData(0x10, [0x01])
time.sleep(0.1)
def _disPart(self, xStart, xEnd, yStart, yEnd):
self._setRamData(xStart // 8, xEnd // 8, yEnd % 256, yEnd // 256, yStart % 256, yStart // 256)
self._setRamPointer(xStart // 8, yEnd % 256, yEnd // 256)
self._writeDisRam(xEnd - xStart, yEnd - yStart + 1)
self._updateDis(self.PART)
def flush(self, mode):
if mode != self.FULL and mode != self.PART:
return
self._init(mode)
self._initLut(mode)
self._powerOn()
if mode == self.PART:
self._disPart(0, self.XDOT - 1, 0, self.YDOT - 1)
else:
self._setRamPointer(0x00, (self.YDOT - 1) % 256, (self.YDOT - 1) // 256)
self._writeDisRam(self.XDOT, self.YDOT)
self._updateDis(self.FULL)
def startDrawBitmapFile(self, x, y):
self._bitmapFileStartX = x
self._bitmapFileStartY = y
def bitmapFileHelper(self, buf):
for i in range(len(buf) // 3):
addr = i * 3
if buf[addr] == 0x00 and buf[addr + 1] == 0x00 and buf[addr + 2] == 0x00:
self.pixel(self._bitmapFileStartX, self._bitmapFileStartY, self.BLACK)
else:
self.pixel(self._bitmapFileStartX, self._bitmapFileStartY, self.WHITE)
self._bitmapFileStartX += 1
def endDrawBitmapFile(self):
self.flush(self.PART)
class DFRobot_Epaper_SPI(DFRobot_Epaper):
def __init__(self, bus, dev, cs, cd, busy):
DFRobot_Epaper.__init__(self)
self._spi = SPI(bus, dev)
self._cs = GPIO(cs, GPIO.OUT)
self._cd = GPIO(cd, GPIO.OUT)
self._busy = GPIO(busy, GPIO.IN)
def writeCmdAndData(self, cmd, data = []):
self._waitBusyExit()
self._cs.setOut(GPIO.LOW)
self._cd.setOut(GPIO.LOW)
self._spi.transfer([cmd])
self._cd.setOut(GPIO.HIGH)
self._spi.transfer(data)
self._cs.setOut(GPIO.HIGH)
def readBusy(self):
return self._busy.read()
def setBusyCB(self, cb):
self._busy.setInterrupt(self._busyExitEdge, cb)
def __del__(self):
RPIGPIO.cleanup()

View File

@@ -0,0 +1,101 @@
fonts = { # left to right, msb to bottom, lsb to top
" ": [0x00,0x00,0x00,0x00,0x00,0x00],
"!": [0x00,0x00,0x5F,0x00,0x00,0x00],
"\"": [0x00,0x07,0x00,0x07,0x00,0x00],
"#": [0x14,0x7F,0x14,0x7F,0x14,0x00],
"$": [0x24,0x2A,0x7F,0x2A,0x12,0x00],
"%": [0x23,0x13,0x08,0x64,0x62,0x00],
"&": [0x36,0x49,0x56,0x20,0x50,0x00],
"'": [0x00,0x08,0x07,0x03,0x00,0x00],
"(": [0x00,0x1C,0x22,0x41,0x00,0x00],
")": [0x00,0x41,0x22,0x1C,0x00,0x00],
"*": [0x24,0x18,0x7E,0x18,0x24,0x00],
"+": [0x08,0x08,0x3E,0x08,0x08,0x00],
",": [0x00,0x80,0x70,0x30,0x00,0x00],
"-": [0x08,0x08,0x08,0x08,0x08,0x00],
".": [0x00,0x00,0x60,0x60,0x00,0x00],
"/": [0x20,0x10,0x08,0x04,0x02,0x00],
"0": [0x3E,0x41,0x49,0x41,0x3E,0x00],
"1": [0x00,0x42,0x7F,0x40,0x00,0x00],
"2": [0x72,0x49,0x49,0x49,0x46,0x00],
"3": [0x21,0x41,0x49,0x4D,0x32,0x00],
"4": [0x18,0x14,0x12,0x7F,0x10,0x00],
"5": [0x27,0x45,0x45,0x45,0x38,0x00],
"6": [0x3C,0x4A,0x49,0x49,0x31,0x00],
"7": [0x41,0x21,0x11,0x09,0x07,0x00],
"8": [0x36,0x49,0x49,0x49,0x36,0x00],
"9": [0x46,0x49,0x49,0x29,0x16,0x00],
":": [0x00,0x00,0x14,0x00,0x00,0x00],
";": [0x00,0x40,0x34,0x00,0x00,0x00],
"<": [0x00,0x08,0x14,0x22,0x41,0x00],
"=": [0x14,0x14,0x14,0x14,0x14,0x00],
">": [0x00,0x41,0x22,0x14,0x08,0x00],
"?": [0x02,0x01,0x59,0x09,0x06,0x00],
"@": [0x3E,0x41,0x5D,0x59,0x4E,0x00],
"A": [0x7C,0x12,0x11,0x12,0x7C,0x00],
"B": [0x7F,0x49,0x49,0x49,0x36,0x00],
"C": [0x3E,0x41,0x41,0x41,0x22,0x00],
"D": [0x7F,0x41,0x41,0x41,0x3E,0x00],
"E": [0x7F,0x49,0x49,0x49,0x41,0x00],
"F": [0x7F,0x09,0x09,0x09,0x01,0x00],
"G": [0x3E,0x41,0x41,0x51,0x73,0x00],
"H": [0x7F,0x08,0x08,0x08,0x7F,0x00],
"I": [0x00,0x41,0x7F,0x41,0x00,0x00],
"J": [0x20,0x40,0x41,0x3F,0x01,0x00],
"K": [0x7F,0x08,0x14,0x22,0x41,0x00],
"L": [0x7F,0x40,0x40,0x40,0x40,0x00],
"M": [0x7F,0x02,0x1C,0x02,0x7F,0x00],
"N": [0x7F,0x04,0x08,0x10,0x7F,0x00],
"O": [0x3E,0x41,0x41,0x41,0x3E,0x00],
"P": [0x7F,0x09,0x09,0x09,0x06,0x00],
"Q": [0x3E,0x41,0x51,0x21,0x5E,0x00],
"R": [0x7F,0x09,0x19,0x29,0x46,0x00],
"S": [0x26,0x49,0x49,0x49,0x32,0x00],
"T": [0x03,0x01,0x7F,0x01,0x03,0x00],
"U": [0x3F,0x40,0x40,0x40,0x3F,0x00],
"V": [0x1F,0x20,0x40,0x20,0x1F,0x00],
"W": [0x3F,0x40,0x38,0x40,0x3F,0x00],
"X": [0x63,0x14,0x08,0x14,0x63,0x00],
"Y": [0x03,0x04,0x78,0x04,0x03,0x00],
"Z": [0x61,0x59,0x49,0x4D,0x43,0x00],
"[": [0x00,0x7F,0x41,0x41,0x41,0x00],
"\\": [0x02,0x04,0x08,0x10,0x20,0x00],
"]": [0x00,0x41,0x41,0x41,0x7f,0x00],
"^": [0x04,0x02,0x01,0x02,0x04,0x00],
"_": [0x40,0x40,0x40,0x40,0x46,0x00],
"'": [0x00,0x03,0x07,0x08,0x00,0x00],
"a": [0x20,0x54,0x54,0x78,0x40,0x00],
"b": [0x7F,0x28,0x44,0x44,0x38,0x00],
"c": [0x38,0x44,0x44,0x44,0x28,0x00],
"d": [0x38,0x44,0x44,0x28,0x7F,0x00],
"e": [0x38,0x54,0x54,0x54,0x18,0x00],
"f": [0x00,0x08,0x7E,0x09,0x02,0x00],
"g": [0x38,0xA4,0xA4,0x9C,0x78,0x00],
"h": [0x7F,0x08,0x04,0x04,0x78,0x00],
"i": [0x00,0x44,0x7D,0x40,0x00,0x00],
"j": [0x20,0x40,0x40,0x3D,0x00,0x00],
"k": [0x7F,0x10,0x28,0x44,0x00,0x00],
"l": [0x00,0x41,0x7F,0x40,0x00,0x00],
"m": [0x7C,0x04,0x78,0x04,0x78,0x00],
"n": [0x7C,0x08,0x04,0x04,0x78,0x00],
"o": [0x38,0x44,0x44,0x44,0x38,0x00],
"p": [0xFC,0x18,0x24,0x24,0x18,0x00],
"q": [0x18,0x24,0x24,0x18,0xFC,0x00],
"r": [0x7C,0x08,0x04,0x04,0x08,0x00],
"s": [0x48,0x54,0x54,0x54,0x24,0x00],
"t": [0x04,0x04,0x3F,0x44,0x24,0x00],
"u": [0x3C,0x40,0x40,0x20,0x7C,0x00],
"v": [0x1C,0x20,0x40,0x20,0x1C,0x00],
"w": [0x3C,0x40,0x20,0x40,0x3C,0x00],
"x": [0x44,0x28,0x10,0x28,0x44,0x00],
"y": [0x4C,0x90,0x90,0x90,0x7C,0x00],
"z": [0x44,0x64,0x54,0x4C,0x44,0x00],
"{": [0x00,0x08,0x36,0x41,0x00,0x00],
"|": [0x00,0x00,0x77,0x00,0x00,0x00],
"}": [0x00,0x41,0x36,0x08,0x00,0x00],
"~": [0x02,0x01,0x02,0x04,0x02,0x00]
}
width = 6
height = 8
fmt = "LRMBLT"

View File

@@ -0,0 +1,101 @@
fonts = { # top to bottom, msb left, lsb right
" ": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
"!": [0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00],
"\"": [0x00,0x63,0x63,0x63,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
"#": [0x00,0x00,0x00,0x36,0x36,0x7F,0x36,0x36,0x36,0x7F,0x36,0x36,0x00,0x00,0x00,0x00],
"$": [0x0C,0x0C,0x3E,0x63,0x61,0x60,0x3E,0x03,0x03,0x43,0x63,0x3E,0x0C,0x0C,0x00,0x00],
"%": [0x00,0x00,0x00,0x00,0x00,0x61,0x63,0x06,0x0C,0x18,0x33,0x63,0x00,0x00,0x00,0x00],
"&": [0x00,0x00,0x00,0x1C,0x36,0x36,0x1C,0x3B,0x6E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00],
"'": [0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
"(": [0x00,0x00,0x0C,0x18,0x18,0x30,0x30,0x30,0x30,0x18,0x18,0x0C,0x00,0x00,0x00,0x00],
")": [0x00,0x00,0x18,0x0C,0x0C,0x06,0x06,0x06,0x06,0x0C,0x0C,0x18,0x00,0x00,0x00,0x00],
"*": [0x00,0x00,0x00,0x00,0x42,0x66,0x3C,0xFF,0x3C,0x66,0x42,0x00,0x00,0x00,0x00,0x00],
"+": [0x00,0x00,0x00,0x00,0x18,0x18,0x18,0xFF,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00],
",": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00],
"-": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
".": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00],
"/": [0x00,0x00,0x01,0x03,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00],
"0": [0x00,0x00,0x3E,0x63,0x63,0x63,0x6B,0x6B,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
"1": [0x00,0x00,0x0C,0x1C,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3F,0x00,0x00,0x00,0x00],
"2": [0x00,0x00,0x3E,0x63,0x03,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00],
"3": [0x00,0x00,0x3E,0x63,0x03,0x03,0x1E,0x03,0x03,0x03,0x63,0x3E,0x00,0x00,0x00,0x00],
"4": [0x00,0x00,0x06,0x0E,0x1E,0x36,0x66,0x66,0x7F,0x06,0x06,0x0F,0x00,0x00,0x00,0x00],
"5": [0x00,0x00,0x7F,0x60,0x60,0x60,0x7E,0x03,0x03,0x63,0x73,0x3E,0x00,0x00,0x00,0x00],
"6": [0x00,0x00,0x1C,0x30,0x60,0x60,0x7E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
"7": [0x00,0x00,0x7F,0x63,0x03,0x06,0x06,0x0C,0x0C,0x18,0x18,0x18,0x00,0x00,0x00,0x00],
"8": [0x00,0x00,0x3E,0x63,0x63,0x63,0x3E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
"9": [0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x3F,0x03,0x03,0x06,0x3C,0x00,0x00,0x00,0x00],
":": [0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00],
";": [0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00],
"<": [0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00],
"=": [0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00],
">": [0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00],
"?": [0x00,0x00,0x3E,0x63,0x63,0x06,0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x00,0x00,0x00,0x00],
"@": [0x00,0x00,0x3E,0x63,0x63,0x6F,0x6B,0x6B,0x6E,0x60,0x60,0x3E,0x00,0x00,0x00,0x00],
"A": [0x00,0x00,0x08,0x1C,0x36,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x00,0x00,0x00,0x00],
"B": [0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x33,0x33,0x33,0x33,0x7E,0x00,0x00,0x00,0x00],
"C": [0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x60,0x60,0x61,0x33,0x1E,0x00,0x00,0x00,0x00],
"D": [0x00,0x00,0x7C,0x36,0x33,0x33,0x33,0x33,0x33,0x33,0x36,0x7C,0x00,0x00,0x00,0x00],
"E": [0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00],
"F": [0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00],
"G": [0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x6F,0x63,0x63,0x37,0x1D,0x00,0x00,0x00,0x00],
"H": [0x00,0x00,0x63,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00],
"I": [0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00],
"J": [0x00,0x00,0x0F,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00,0x00,0x00],
"K": [0x00,0x00,0x73,0x33,0x36,0x36,0x3C,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00],
"L": [0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00],
"M": [0x00,0x00,0x63,0x77,0x7F,0x6B,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00],
"N": [0x00,0x00,0x63,0x63,0x73,0x7B,0x7F,0x6F,0x67,0x63,0x63,0x63,0x00,0x00,0x00,0x00],
"O": [0x00,0x00,0x1C,0x36,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x00,0x00,0x00,0x00],
"P": [0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00],
"Q": [0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x63,0x6B,0x6F,0x3E,0x06,0x07,0x00,0x00],
"R": [0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00],
"S": [0x00,0x00,0x3E,0x63,0x63,0x30,0x1C,0x06,0x03,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
"T": [0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00],
"U": [0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
"V": [0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x08,0x00,0x00,0x00,0x00],
"W": [0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x36,0x00,0x00,0x00,0x00],
"X": [0x00,0x00,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x3C,0x66,0xC3,0xC3,0x00,0x00,0x00,0x00],
"Y": [0x00,0x00,0xC3,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00],
"Z": [0x00,0x00,0x7F,0x63,0x43,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00],
"[": [0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00],
"\\": [0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x07,0x03,0x01,0x00,0x00,0x00,0x00],
"]": [0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00],
"^": [0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
"_": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00],
"'": [0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
"a": [0x00,0x00,0x00,0x00,0x00,0x3C,0x46,0x06,0x3E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00],
"b": [0x00,0x00,0x70,0x30,0x30,0x3C,0x36,0x33,0x33,0x33,0x33,0x6E,0x00,0x00,0x00,0x00],
"c": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x60,0x60,0x60,0x63,0x3E,0x00,0x00,0x00,0x00],
"d": [0x00,0x00,0x0E,0x06,0x06,0x1E,0x36,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00],
"e": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x7E,0x60,0x63,0x3E,0x00,0x00,0x00,0x00],
"f": [0x00,0x00,0x1C,0x36,0x32,0x30,0x7C,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00],
"g": [0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x66,0x3C,0x00,0x00],
"h": [0x00,0x00,0x70,0x30,0x30,0x36,0x3B,0x33,0x33,0x33,0x33,0x73,0x00,0x00,0x00,0x00],
"i": [0x00,0x00,0x0C,0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00],
"j": [0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00],
"k": [0x00,0x00,0x70,0x30,0x30,0x33,0x33,0x36,0x3C,0x36,0x33,0x73,0x00,0x00,0x00,0x00],
"l": [0x00,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00],
"m": [0x00,0x00,0x00,0x00,0x00,0x6E,0x7F,0x6B,0x6B,0x6B,0x6B,0x6B,0x00,0x00,0x00,0x00],
"n": [0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00],
"o": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
"p": [0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x3E,0x30,0x30,0x78,0x00,0x00],
"q": [0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x06,0x0F,0x00,0x00],
"r": [0x00,0x00,0x00,0x00,0x00,0x6E,0x3B,0x33,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00],
"s": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x38,0x0E,0x03,0x63,0x3E,0x00,0x00,0x00,0x00],
"t": [0x00,0x00,0x08,0x18,0x18,0x7E,0x18,0x18,0x18,0x18,0x1B,0x0E,0x00,0x00,0x00,0x00],
"u": [0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00],
"v": [0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x36,0x36,0x1C,0x1C,0x08,0x00,0x00,0x00,0x00],
"w": [0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x00,0x00,0x00,0x00],
"x": [0x00,0x00,0x00,0x00,0x00,0x63,0x36,0x1C,0x1C,0x1C,0x36,0x63,0x00,0x00,0x00,0x00],
"y": [0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x3F,0x03,0x06,0x3C,0x00,0x00],
"z": [0x00,0x00,0x00,0x00,0x00,0x7F,0x66,0x0C,0x18,0x30,0x63,0x7F,0x00,0x00,0x00,0x00],
"{": [0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00],
"|": [0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00],
"}": [0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00],
"~": [0x00,0x00,0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
}
width = 8
height = 16
fmt = "TBMLLR"

View File

@@ -0,0 +1,80 @@
# -*- coding:utf-8 -*-
'''
depends: freetype-py
'''
import freetype
import math
#import sys
#reload(sys)
#sys.setdefaultencoding("utf-8")
import importlib,sys
#importlib.reload(sys)
class Freetype_Helper:
def __init__(self, filePath):
self._face = freetype.Face(filePath)
self._width = 0
self._height = 0
self._fade = 96
def setFmt(self, width, height):
self._width = int(width)
self._height = int(height)
self._face.set_pixel_sizes(width, height)
def setDisLowerLimite(self, limite):
self._fade = limite
def getOne(self, ch):
self._face.load_char(ch)
bitmap = self._face.glyph.bitmap
originY = self._face.glyph.bitmap_top
width = bitmap.width
height = bitmap.rows
buffer = bitmap.buffer
rslt = []
# width = 4
# height = 4
# buffer = [0xff] * width * height
if height > self._height:
buffer = buffer[0: width * self._height]
height = self._height
if width > self._width:
for i in range(height):
rslt += buffer[i * width: i * width + self._width]
width = self._width
buffer = rslt
rslt = []
if (ord(ch) >= ord(" ") and ord(ch) <= ord("~")) or width <= (self._width // 2):
rslt = [0] * (((self._width - 1) // 16 + 1) * self._height + 1)
left = (self._width // 2 - width) // 2
lineDataLen = (self._width - 1) // 16 + 1
else:
rslt = [0] * (((self._width - 1) // 8 + 1) * self._height + 1)
left = (self._width - width) // 2
lineDataLen = (self._width - 1) // 8 + 1
if left < 0:
left = 0
# top = (self._height - height) * lineDataLen // 2
top = ((self._height * 8 + 5) // 10 - originY) * lineDataLen
if top < 0:
top = 0
for i in range(height):
for j in range(width):
if buffer[i * width + j] > self._fade:
try:
rslt[i * lineDataLen + (j + left) // 8 + top] |= 0x80 >> ((j + left) % 8)
except:
print("freetype_helper getOne err: width: %d, height: %d, top: %d, left: %d, rslt_len: %d, originY: %d" %(width, height, top, left, len(rslt), originY))
raise("err")
# rslt[i * lineDataLen + (j + left) // 8 + top] |= 0x80 >> ((j + left) % 8)
if (ord(ch) >= ord(" ") and ord(ch) <= ord("~")) or width < (self._width // 2):
return (rslt, self._width // 2, self._height, "TBMLLR")
else:
return (rslt, self._width, self._height, "TBMLLR")

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,2 @@
wqydkzh.ttf = 文泉驿等宽正黑.ttf GPL2 license </br>
zkklt.ttf = 站酷快乐体.ttf Chinese open source fonts file, use with freetype_helper.py

View File

@@ -0,0 +1,64 @@
# -*- coding:utf-8 -*-
import time
import RPi.GPIO as RPIGPIO
RPIGPIO.setmode(RPIGPIO.BCM)
RPIGPIO.setwarnings(False)
class GPIO:
HIGH = RPIGPIO.HIGH
LOW = RPIGPIO.LOW
OUT = RPIGPIO.OUT
IN = RPIGPIO.IN
RISING = RPIGPIO.RISING
FALLING = RPIGPIO.FALLING
BOTH = RPIGPIO.BOTH
def __init__(self, pin, mode, defaultOut = HIGH):
self._pin = pin
self._fInt = None
self._intDone = True
self._intMode = None
if mode == self.OUT:
RPIGPIO.setup(pin, mode)
if defaultOut == self.HIGH:
RPIGPIO.output(pin, defaultOut)
else:
RPIGPIO.output(pin, self.LOW)
else:
RPIGPIO.setup(pin, self.IN, pull_up_down = RPIGPIO.PUD_UP)
def setOut(self, level):
if level:
RPIGPIO.output(self._pin, self.HIGH)
else:
RPIGPIO.output(self._pin, self.LOW)
def _intCB(self, status):
if self._intDone:
self._intDone = False
time.sleep(0.02)
if self._intMode == self.BOTH:
self._fInt()
elif self._intMode == self.RISING and self.read() == self.HIGH:
self._fInt()
elif self._intMode == self.FALLING and self.read() == self.LOW:
self._fInt()
self._intDone = True
def setInterrupt(self, mode, cb):
if mode != self.RISING and mode != self.FALLING and mode != self.BOTH:
return
self._intMode = mode
RPIGPIO.add_event_detect(self._pin, mode, self._intCB)
self._fInt = cb
def read(self):
return RPIGPIO.input(self._pin)
def cleanup(self):
RPIGPIO.cleanup()

View File

@@ -0,0 +1,21 @@
# -*- coding:utf-8 -*-
'''
change i2c frequency on raspberry:
1. edit /etc/modprobe.d
2. add line:
options i2c_bcm2708 baudrate=400000
'''
import smbus
class I2C:
def __init__(self, port):
self._bus = smbus.SMBus(port)
def writeBytes(self, addr, reg, buf):
self._bus.write_block_data(addr, reg, buf)
def readBytes(self, addr, reg, length):
return self._bus.read_block_data(addr, reg, length)

View File

@@ -0,0 +1,21 @@
# -*- coding:utf-8 -*-
import spidev
class SPI:
MODE_1 = 1
MODE_2 = 2
MODE_3 = 3
MODE_4 = 4
def __init__(self, bus, dev, speed = 3900000, mode = MODE_4):
self._bus = spidev.SpiDev()
self._bus.open(0, 0)
self._bus.no_cs = True
self._bus.max_speed_hz = speed
def transfer(self, buf):
if len(buf):
return self._bus.xfer(buf)
return []

View File

@@ -1,20 +1,20 @@
import _thread
from threading import Lock
import time
import logging
import random
import time
from threading import Lock
from PIL import ImageDraw
import pwnagotchi
import pwnagotchi.utils as utils
import pwnagotchi.plugins as plugins
from pwnagotchi.voice import Voice
import pwnagotchi.ui.web as web
import pwnagotchi.ui.fonts as fonts
import pwnagotchi.ui.faces as faces
import pwnagotchi.ui.fonts as fonts
import pwnagotchi.ui.web as web
import pwnagotchi.utils as utils
from pwnagotchi.ui.components import *
from pwnagotchi.ui.state import State
from pwnagotchi.voice import Voice
WHITE = 0xff
BLACK = 0x00
@@ -345,6 +345,11 @@ class View(object):
self.update()
time.sleep(5.0)
def on_uploading(self, to):
self.set('face', random.choice((faces.UPLOAD, faces.UPLOAD1, faces.UPLOAD2)))
self.set('status', self._voice.on_uploading(to))
self.update(force=True)
def on_rebooting(self):
self.set('face', faces.BROKEN)
self.set('status', self._voice.on_rebooting())

View File

@@ -9,7 +9,7 @@ frame_lock = Lock()
def update_frame(img):
global frame_lock, frame_path, frame_format
if not os.path.exists(os.path.basename(frame_path)):
os.makedirs(os.path.basename(frame_path))
if not os.path.exists(os.path.dirname(frame_path)):
os.makedirs(os.path.dirname(frame_path))
with frame_lock:
img.save(frame_path, format=frame_format)

View File

@@ -5,6 +5,37 @@
Plugins
{% endblock %}
{% block styles %}
{{ super() }}
<style>
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 200px;
background-color: #3388cc;
color: #fff;
text-align: center;
border-radius: 10px;
border: 2px solid black;
padding: 20px 0;
position: absolute;
z-index: 1;
top: 100%;
left: 50%;
margin-left: -100px;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
</style>
{% endblock %}
{% block script %}
$(function(){
$('input[type=checkbox]').change(function(e) {
@@ -27,11 +58,19 @@ $(function(){
{% endblock %}
{% block content %}
<div id="container">
{% for name in database.keys() %}
{% for name in database.keys() | sort %}
{% set has_info = name in loaded and loaded[name].__description__ is defined %}
<div class="plugins-box">
<h4>
<a {% if name in loaded and loaded[name].on_webhook is defined %} href="/plugins/{{name}}" {% endif %}>{{name}}</a>
</h4>
<div class="tooltip">
<h4>
<a {% if name in loaded and loaded[name].on_webhook is defined %} href="/plugins/{{name}}" {% endif %}>{{name}}</a>
</h4>
{% if has_info %}
<span class="tooltiptext">{{ loaded[name].__description__ }}</span>
{% else %}
<span class="tooltiptext">Description can't be loaded yet.</span>
{% endif %}
</div>
<form method="POST" action="/plugins/toggle">
<input type="checkbox" data-role="flipswitch" name="enabled" id="flip-checkbox-{{name}}" data-on-text="Enabled" data-off-text="Disabled" data-wrapper-class="custom-size-flipswitch" {% if name in loaded %} checked {% endif %}>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>

View File

@@ -257,8 +257,11 @@ def load_config(args):
elif config['ui']['display']['type'] in ('lcdhat',):
config['ui']['display']['type'] = 'lcdhat'
elif config['ui']['display']['type'] in ('dfrobot', 'df'):
config['ui']['display']['type'] = 'dfrobot'
elif config['ui']['display']['type'] in ('dfrobot_1', 'df1'):
config['ui']['display']['type'] = 'dfrobot_1'
elif config['ui']['display']['type'] in ('dfrobot_2', 'df2'):
config['ui']['display']['type'] = 'dfrobot_2'
elif config['ui']['display']['type'] in ('ws_154inch', 'ws154inch', 'waveshare_154inch', 'waveshare154inch'):
config['ui']['display']['type'] = 'waveshare154inch'

View File

@@ -1,6 +1,6 @@
import random
import gettext
import os
import random
class Voice:
@@ -159,6 +159,9 @@ class Voice:
def on_rebooting(self):
return self._("Oops, something went wrong ... Rebooting ...")
def on_uploading(self, to):
return self._("Uploading data to {to} ...").format(to=to)
def on_last_session_data(self, last_session):
status = self._('Kicked {num} stations\n').format(num=last_session.deauthed)
if last_session.associated > 999:

7
release.stork Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env stork -f
version:parser "__version__\\s*=\\s*['\"]([\\d\\.ab]+)[\"']"
version:file "pwnagotchi/_version.py"
version:from_user
git:create_tag $VERSION

View File

@@ -1,57 +0,0 @@
#!/bin/bash
NEW=()
FIXES=()
MISC=()
echo "@ Fetching remote tags ..."
git fetch --tags >/dev/null
printf "\n\n"
CURTAG=$(git describe --tags --abbrev=0)
OUTPUT=$(git log $CURTAG..HEAD --oneline)
IFS=$'\n' LINES=($OUTPUT)
for LINE in "${LINES[@]}"; do
LINE=$(echo "$LINE" | sed -E "s/^[[:xdigit:]]+\s+//")
if [[ $LINE == *"new:"* ]]; then
LINE=$(echo "$LINE" | sed -E "s/^new: //")
NEW+=("$LINE")
elif [[ $LINE == *"fix:"* ]]; then
LINE=$(echo "$LINE" | sed -E "s/^fix: //")
FIXES+=("$LINE")
elif [[ $LINE != *"i did not bother commenting"* ]] && [[ $LINE != *"Merge "* ]]; then
echo " MISC LINE =$LINE"
LINE=$(echo "$LINE" | sed -E "s/^[a-z]+: //")
MISC+=("$LINE")
fi
done
if [ -n "$NEW" ]; then
echo
echo "**New Features**"
echo
for l in "${NEW[@]}"; do
echo "* $l"
done
fi
if [ -n "$FIXES" ]; then
echo
echo "**Fixes**"
echo
for l in "${FIXES[@]}"; do
echo "* $l"
done
fi
if [ -n "$MISC" ]; then
echo
echo "**Misc**"
echo
for l in "${MISC[@]}"; do
echo "* $l"
done
fi
echo

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env bash
set -e
# name of the ethernet gadget interface on the host
USB_IFACE=${1:-enp0s20f0u1}

View File

@@ -1,28 +0,0 @@
#!/bin/bash
# nothing to see here, just a utility i use to create new releases ^_^
VERSION_FILE=$(dirname "${BASH_SOURCE[0]}")/../pwnagotchi/_version.py
echo "version file is $VERSION_FILE"
CURRENT_VERSION=$(cat $VERSION_FILE | grep version | cut -d"'" -f2)
TO_UPDATE=(
$VERSION_FILE
)
echo -n "current version is $CURRENT_VERSION, select new version: "
read NEW_VERSION
echo "creating version $NEW_VERSION ...\n"
for file in "${TO_UPDATE[@]}"; do
echo "patching $file ..."
sed -i.bak "s/$CURRENT_VERSION/$NEW_VERSION/g" "$file"
rm -rf "$file.bak"
git add $file
done
git commit -m "releasing v$NEW_VERSION"
git push
git tag -a v$NEW_VERSION -m "release v$NEW_VERSION"
git push origin v$NEW_VERSION
echo
echo "All done, v$NEW_VERSION released ^_^"

View File

@@ -20,6 +20,7 @@ while getopts "hb:n:u:" arg; do
UNIT_USERNAME=$OPTARG
;;
*)
usage
exit 1
esac
done