Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2d7b6b54fe | ||
|
b3aa5bc2c1 | ||
|
10cba6872a | ||
|
b213f9f214 | ||
|
fa72a53129 | ||
|
49f7c652c7 | ||
|
9c78e16c1b | ||
|
22ebc09c9d | ||
|
0ea67cb97f | ||
|
2324423cab | ||
|
be2cd8293f | ||
|
86cbe01081 | ||
|
eea4feee71 | ||
|
cff487008c | ||
|
6cecca67a4 | ||
|
fb4d46d71a | ||
|
e220a869e0 | ||
|
f4a59549fa | ||
|
a058d2e00d | ||
|
8c73e32853 | ||
|
eeee4bdd3c | ||
|
1ddf020ba8 | ||
|
e992834b37 | ||
|
55bac8a8b9 | ||
|
61a6b77a52 | ||
|
0aa4f95235 | ||
|
299bd9a5ca | ||
|
10688ca7b0 |
@@ -20,7 +20,7 @@ deploy:
|
|||||||
repo: evilsocket/pwnagotchi
|
repo: evilsocket/pwnagotchi
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- "/^v[0-9]+\\.[0-9]+\\.[0-9]+[A-Za-z0-9]+?$/"
|
- "/^v[0-9]+\\.[0-9]+\\.[0-9]+[A-Za-z0-9]*$/"
|
||||||
cache:
|
cache:
|
||||||
apt: true
|
apt: true
|
||||||
before_script:
|
before_script:
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
url: "https://github.com/bettercap/bettercap/releases/download/v2.25/bettercap_linux_armv6l_2.25.zip"
|
url: "https://github.com/bettercap/bettercap/releases/download/v2.25/bettercap_linux_armv6l_2.25.zip"
|
||||||
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||||
pwngrid:
|
pwngrid:
|
||||||
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.7.6/pwngrid_linux_armv6l_1.7.6.zip"
|
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.8.1/pwngrid_linux_armhf_v1.8.1.zip"
|
||||||
apt:
|
apt:
|
||||||
hold:
|
hold:
|
||||||
- firmware-atheros
|
- firmware-atheros
|
||||||
|
@@ -4,7 +4,7 @@ import logging
|
|||||||
import time
|
import time
|
||||||
import pwnagotchi.ui.view as view
|
import pwnagotchi.ui.view as view
|
||||||
|
|
||||||
version = '1.0.0RC5'
|
version = '1.0.1'
|
||||||
|
|
||||||
_name = None
|
_name = None
|
||||||
|
|
||||||
|
@@ -17,10 +17,6 @@ main:
|
|||||||
report: false # don't report pwned networks by default!
|
report: false # don't report pwned networks by default!
|
||||||
exclude: # do not report the following networks (accepts both ESSIDs and BSSIDs)
|
exclude: # do not report the following networks (accepts both ESSIDs and BSSIDs)
|
||||||
- YourHomeNetworkHere
|
- YourHomeNetworkHere
|
||||||
auto-update:
|
|
||||||
enabled: false
|
|
||||||
system: false # set to true to also enable system updates via apt
|
|
||||||
interval: 1 # every day
|
|
||||||
auto-backup:
|
auto-backup:
|
||||||
enabled: false
|
enabled: false
|
||||||
interval: 1 # every day
|
interval: 1 # every day
|
||||||
@@ -74,6 +70,8 @@ main:
|
|||||||
netmask: 24
|
netmask: 24
|
||||||
interval: 1 # check every x minutes for device
|
interval: 1 # check every x minutes for device
|
||||||
share_internet: false
|
share_internet: false
|
||||||
|
memtemp: # Display memory usage and cpu temperature on screen
|
||||||
|
enabled: false
|
||||||
# monitor interface to use
|
# monitor interface to use
|
||||||
iface: mon0
|
iface: mon0
|
||||||
# command to run to bring the mon interface up in case it's not up already
|
# command to run to bring the mon interface up in case it's not up already
|
||||||
|
BIN
pwnagotchi/locale/pl/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/pl/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
215
pwnagotchi/locale/pl/LC_MESSAGES/voice.po
Normal file
215
pwnagotchi/locale/pl/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
# Polish voice data for pwnagotchi.
|
||||||
|
# Copyright (C) 2019
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR szymex73 <szymex73@gmail.com>, 2019.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
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 17:42+0200\n"
|
||||||
|
"Last-Translator: szymex73 <szymex73@gmail.com>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: polish\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 "Hej, jestem Pwnagotchi! Uruchamianie ..."
|
||||||
|
|
||||||
|
msgid "New day, new hunt, new pwns!"
|
||||||
|
msgstr "Nowy dzień, nowe łowy, nowe pwny!"
|
||||||
|
|
||||||
|
msgid "Hack the Planet!"
|
||||||
|
msgstr "Hakujmy planetę!"
|
||||||
|
|
||||||
|
msgid "AI ready."
|
||||||
|
msgstr "SI gotowa."
|
||||||
|
|
||||||
|
msgid "The neural network is ready."
|
||||||
|
msgstr "Sieć neuronowa jest gotowa."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||||
|
msgstr "Hej, kanał {channel} jest wolny! Twój AP mi podziękuje."
|
||||||
|
|
||||||
|
msgid "I'm bored ..."
|
||||||
|
msgstr "Nudzi mi się ..."
|
||||||
|
|
||||||
|
msgid "Let's go for a walk!"
|
||||||
|
msgstr "Chodźmy na spacer!"
|
||||||
|
|
||||||
|
msgid "This is the best day of my life!"
|
||||||
|
msgstr "To jest najlepszy dzień w moim życiu!"
|
||||||
|
|
||||||
|
msgid "Shitty day :/"
|
||||||
|
msgstr "Ten dzień jest do dupy :/"
|
||||||
|
|
||||||
|
msgid "I'm extremely bored ..."
|
||||||
|
msgstr "Straaaasznie się nudzę ..."
|
||||||
|
|
||||||
|
msgid "I'm very sad ..."
|
||||||
|
msgstr "Jest mi bardzo smutno ..."
|
||||||
|
|
||||||
|
msgid "I'm sad"
|
||||||
|
msgstr "Jest mi smutno"
|
||||||
|
|
||||||
|
msgid "I'm living the life!"
|
||||||
|
msgstr "Cieszę się życiem!"
|
||||||
|
|
||||||
|
msgid "I pwn therefore I am."
|
||||||
|
msgstr "Pwnuje więc jestem."
|
||||||
|
|
||||||
|
msgid "So many networks!!!"
|
||||||
|
msgstr "Jak dużo sieci!!!"
|
||||||
|
|
||||||
|
msgid "I'm having so much fun!"
|
||||||
|
msgstr "Ale jest super!"
|
||||||
|
|
||||||
|
msgid "My crime is that of curiosity ..."
|
||||||
|
msgstr "Moją zbrodnią jest ciekawość ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hello {name}! Nice to meet you. {name}"
|
||||||
|
msgstr "Cześć {name}! Miło cię poznać. {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Unit {name} is nearby! {name}"
|
||||||
|
msgstr "Jednostka {name} jest niedaleko! {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Uhm ... goodbye {name}"
|
||||||
|
msgstr "Umm ... żegnaj {name}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} is gone ..."
|
||||||
|
msgstr "{name} zniknął ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Whoops ... {name} is gone."
|
||||||
|
msgstr "Ups ... {name} zniknął."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "{name} missed!"
|
||||||
|
msgstr "{name} przeoczył!"
|
||||||
|
|
||||||
|
msgid "Missed!"
|
||||||
|
msgstr "Spóźniony!"
|
||||||
|
|
||||||
|
msgid "Nobody wants to play with me ..."
|
||||||
|
msgstr "Nikt się nie chce ze mną bawić ..."
|
||||||
|
|
||||||
|
msgid "I feel so alone ..."
|
||||||
|
msgstr "Czuję się tak samotnie ..."
|
||||||
|
|
||||||
|
msgid "Where's everybody?!"
|
||||||
|
msgstr "Gdzie są wszyscy?!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Napping for {secs}s ..."
|
||||||
|
msgstr "Zdrzemnę się przez {secs}s ..."
|
||||||
|
|
||||||
|
msgid "Zzzzz"
|
||||||
|
msgstr "Zzzzz"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "ZzzZzzz ({secs}s)"
|
||||||
|
msgstr "ZzzZzzz ({secs}s)"
|
||||||
|
|
||||||
|
msgid "Good night."
|
||||||
|
msgstr "Dobranoc."
|
||||||
|
|
||||||
|
msgid "Zzz"
|
||||||
|
msgstr "Zzz"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Waiting for {secs}s ..."
|
||||||
|
msgstr "Czekam {secs}s ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Looking around ({secs}s)"
|
||||||
|
msgstr "Rozglądam się ({secs}s)"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Hey {what} let's be friends!"
|
||||||
|
msgstr "Hej {what}, zostańmy przyjaciółmi!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Associating to {what}"
|
||||||
|
msgstr "Łączenie się z {what}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Yo {what}!"
|
||||||
|
msgstr "Ej {what}!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Just decided that {mac} needs no WiFi!"
|
||||||
|
msgstr "Według mnie {mac} nie potrzebuje WiFi!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Deauthenticating {mac}"
|
||||||
|
msgstr "Rozłączam {mac}"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Kickbanning {mac}!"
|
||||||
|
msgstr "Banowanie {mac}!"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Cool, we got {num} new handshake{plural}!"
|
||||||
|
msgstr "Super, zdobylismy {num} handshake{plural}!"
|
||||||
|
|
||||||
|
msgid "Ops, something went wrong ... Rebooting ..."
|
||||||
|
msgstr "Ups, coś się stało ... Restartuję ..."
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Kicked {num} stations\n"
|
||||||
|
msgstr "Wyrzucono {num} stacji\n"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Made {num} new friends\n"
|
||||||
|
msgstr "Zdobyto {num} przyjaciół\n"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Got {num} handshakes\n"
|
||||||
|
msgstr "Zdobyto {num} handshakow\n"
|
||||||
|
|
||||||
|
msgid "Met 1 peer"
|
||||||
|
msgstr "Spotkano 1 kolegę"
|
||||||
|
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Met {num} peers"
|
||||||
|
msgstr "Spotkano {num} kolegów"
|
||||||
|
|
||||||
|
#, 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 ""
|
||||||
|
"Pwnowalem przez {duration} i wyrzuciłem {deauthed} klientów! Spotkałem także "
|
||||||
|
"{associated} nowych przyjaciół i zjadłem {handshakes} handshaków! #pwnagotchi "
|
||||||
|
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||||
|
|
||||||
|
msgid "hours"
|
||||||
|
msgstr "godzin"
|
||||||
|
|
||||||
|
msgid "minutes"
|
||||||
|
msgstr "minut"
|
||||||
|
|
||||||
|
msgid "seconds"
|
||||||
|
msgstr "sekund"
|
||||||
|
|
||||||
|
msgid "hour"
|
||||||
|
msgstr "godzina"
|
||||||
|
|
||||||
|
msgid "minute"
|
||||||
|
msgstr "minuta"
|
||||||
|
|
||||||
|
msgid "second"
|
||||||
|
msgstr "sekunda"
|
@@ -2,6 +2,7 @@ import hashlib
|
|||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from pwnagotchi.voice import Voice
|
from pwnagotchi.voice import Voice
|
||||||
@@ -87,61 +88,65 @@ class LastSession(object):
|
|||||||
parts = line.split(']')
|
parts = line.split(']')
|
||||||
if len(parts) < 2:
|
if len(parts) < 2:
|
||||||
continue
|
continue
|
||||||
line_timestamp = parts[0].strip('[')
|
|
||||||
line = ']'.join(parts[1:])
|
|
||||||
stopped_at = self._parse_datetime(line_timestamp)
|
|
||||||
if started_at is None:
|
|
||||||
started_at = stopped_at
|
|
||||||
|
|
||||||
if LastSession.DEAUTH_TOKEN in line and line not in cache:
|
try:
|
||||||
self.deauthed += 1
|
line_timestamp = parts[0].strip('[')
|
||||||
cache[line] = 1
|
line = ']'.join(parts[1:])
|
||||||
|
stopped_at = self._parse_datetime(line_timestamp)
|
||||||
|
if started_at is None:
|
||||||
|
started_at = stopped_at
|
||||||
|
|
||||||
elif LastSession.ASSOC_TOKEN in line and line not in cache:
|
if LastSession.DEAUTH_TOKEN in line and line not in cache:
|
||||||
self.associated += 1
|
self.deauthed += 1
|
||||||
cache[line] = 1
|
cache[line] = 1
|
||||||
|
|
||||||
elif LastSession.HANDSHAKE_TOKEN in line and line not in cache:
|
elif LastSession.ASSOC_TOKEN in line and line not in cache:
|
||||||
self.handshakes += 1
|
self.associated += 1
|
||||||
cache[line] = 1
|
cache[line] = 1
|
||||||
|
|
||||||
elif LastSession.TRAINING_TOKEN in line:
|
elif LastSession.HANDSHAKE_TOKEN in line and line not in cache:
|
||||||
self.train_epochs += 1
|
self.handshakes += 1
|
||||||
|
cache[line] = 1
|
||||||
|
|
||||||
elif LastSession.EPOCH_TOKEN in line:
|
elif LastSession.TRAINING_TOKEN in line:
|
||||||
self.epochs += 1
|
self.train_epochs += 1
|
||||||
m = LastSession.EPOCH_PARSER.findall(line)
|
|
||||||
if m:
|
|
||||||
epoch_num, epoch_data = m[0]
|
|
||||||
m = LastSession.EPOCH_DATA_PARSER.findall(epoch_data)
|
|
||||||
for key, value in m:
|
|
||||||
if key == 'reward':
|
|
||||||
reward = float(value)
|
|
||||||
self.avg_reward += reward
|
|
||||||
if reward < self.min_reward:
|
|
||||||
self.min_reward = reward
|
|
||||||
|
|
||||||
elif reward > self.max_reward:
|
elif LastSession.EPOCH_TOKEN in line:
|
||||||
self.max_reward = reward
|
self.epochs += 1
|
||||||
|
m = LastSession.EPOCH_PARSER.findall(line)
|
||||||
|
if m:
|
||||||
|
epoch_num, epoch_data = m[0]
|
||||||
|
m = LastSession.EPOCH_DATA_PARSER.findall(epoch_data)
|
||||||
|
for key, value in m:
|
||||||
|
if key == 'reward':
|
||||||
|
reward = float(value)
|
||||||
|
self.avg_reward += reward
|
||||||
|
if reward < self.min_reward:
|
||||||
|
self.min_reward = reward
|
||||||
|
|
||||||
elif LastSession.PEER_TOKEN in line:
|
elif reward > self.max_reward:
|
||||||
m = self._peer_parser.findall(line)
|
self.max_reward = reward
|
||||||
if m:
|
|
||||||
name, pubkey, rssi, sid, pwnd_tot, uptime = m[0]
|
elif LastSession.PEER_TOKEN in line:
|
||||||
if pubkey not in cache:
|
m = self._peer_parser.findall(line)
|
||||||
self.last_peer = Peer({
|
if m:
|
||||||
'session_id': sid,
|
name, pubkey, rssi, sid, pwnd_tot, uptime = m[0]
|
||||||
'channel': 1,
|
if pubkey not in cache:
|
||||||
'rssi': int(rssi),
|
self.last_peer = Peer({
|
||||||
'identity': pubkey,
|
'session_id': sid,
|
||||||
'advertisement':{
|
'channel': 1,
|
||||||
'name': name,
|
'rssi': int(rssi),
|
||||||
'pwnd_tot': int(pwnd_tot)
|
'identity': pubkey,
|
||||||
}})
|
'advertisement':{
|
||||||
self.peers += 1
|
'name': name,
|
||||||
cache[pubkey] = self.last_peer
|
'pwnd_tot': int(pwnd_tot)
|
||||||
else:
|
}})
|
||||||
cache[pubkey].adv['pwnd_tot'] = pwnd_tot
|
self.peers += 1
|
||||||
|
cache[pubkey] = self.last_peer
|
||||||
|
else:
|
||||||
|
cache[pubkey].adv['pwnd_tot'] = pwnd_tot
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("error parsing line '%s': %s" % (line, e))
|
||||||
|
|
||||||
if started_at is not None:
|
if started_at is not None:
|
||||||
self.duration = stopped_at - started_at
|
self.duration = stopped_at - started_at
|
||||||
|
@@ -1,60 +0,0 @@
|
|||||||
__author__ = 'evilsocket@gmail.com'
|
|
||||||
__version__ = '1.0.0'
|
|
||||||
__name__ = 'auto-update'
|
|
||||||
__license__ = 'GPL3'
|
|
||||||
__description__ = 'This plugin performs an "apt update && apt upgrade" when internet is availaible.'
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import subprocess
|
|
||||||
from pwnagotchi.utils import StatusFile
|
|
||||||
|
|
||||||
OPTIONS = dict()
|
|
||||||
READY = False
|
|
||||||
STATUS = StatusFile('/root/.auto-update')
|
|
||||||
|
|
||||||
|
|
||||||
def on_loaded():
|
|
||||||
global READY
|
|
||||||
|
|
||||||
if 'interval' not in OPTIONS or ('interval' in OPTIONS and OPTIONS['interval'] is None):
|
|
||||||
logging.error("auto-update: Interval is not set.")
|
|
||||||
return
|
|
||||||
|
|
||||||
READY = True
|
|
||||||
|
|
||||||
|
|
||||||
def run(cmd):
|
|
||||||
return subprocess.Popen(cmd, shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None,
|
|
||||||
executable="/bin/bash")
|
|
||||||
|
|
||||||
|
|
||||||
def on_internet_available(agent):
|
|
||||||
global STATUS
|
|
||||||
|
|
||||||
if READY:
|
|
||||||
if STATUS.newer_then_days(OPTIONS['interval']):
|
|
||||||
return
|
|
||||||
|
|
||||||
display = agent.view()
|
|
||||||
|
|
||||||
try:
|
|
||||||
display.set('status', 'Updating ...')
|
|
||||||
display.update()
|
|
||||||
|
|
||||||
logging.info("auto-update: updating pwnagotchi ...")
|
|
||||||
run('pip3 install --upgrade --upgrade-strategy only-if-needed pwnagotchi').wait()
|
|
||||||
|
|
||||||
if OPTIONS['system']:
|
|
||||||
logging.info("auto-update: updating packages index ...")
|
|
||||||
run('apt update -y').wait()
|
|
||||||
|
|
||||||
logging.info("auto-update: updating packages ...")
|
|
||||||
run('apt upgrade -y').wait()
|
|
||||||
|
|
||||||
logging.info("auto-update: complete.")
|
|
||||||
STATUS.update()
|
|
||||||
except Exception as e:
|
|
||||||
logging.exception("auto-update ERROR")
|
|
||||||
|
|
||||||
display.set('status', 'Updated!')
|
|
||||||
display.update()
|
|
@@ -132,7 +132,7 @@ class BTNap:
|
|||||||
device = ifaces.get(BTNap.IFACE_DEV)
|
device = ifaces.get(BTNap.IFACE_DEV)
|
||||||
if device is None:
|
if device is None:
|
||||||
continue
|
continue
|
||||||
if device['Address'] == device_address and path.startswith(path_prefix):
|
if str(device['Address']) == device_address and path.startswith(path_prefix):
|
||||||
obj = bus.get_object(BTNap.IFACE_BASE, path)
|
obj = bus.get_object(BTNap.IFACE_BASE, path)
|
||||||
return dbus.Interface(obj, BTNap.IFACE_DEV)
|
return dbus.Interface(obj, BTNap.IFACE_DEV)
|
||||||
raise BTError('Bluetooth device not found')
|
raise BTError('Bluetooth device not found')
|
||||||
@@ -245,6 +245,7 @@ class BTNap:
|
|||||||
try:
|
try:
|
||||||
dev_remote.Pair()
|
dev_remote.Pair()
|
||||||
logging.info('BT-TETHER: Successful paired with device ;)')
|
logging.info('BT-TETHER: Successful paired with device ;)')
|
||||||
|
time.sleep(10) # wait for bnep0
|
||||||
except Exception:
|
except Exception:
|
||||||
# can fail because of AlreadyExists etc.
|
# can fail because of AlreadyExists etc.
|
||||||
pass
|
pass
|
||||||
@@ -491,5 +492,5 @@ def on_ui_update(ui):
|
|||||||
|
|
||||||
|
|
||||||
def on_ui_setup(ui):
|
def on_ui_setup(ui):
|
||||||
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 30, 0),
|
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
|
||||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||||
|
@@ -65,7 +65,7 @@ def is_excluded(what):
|
|||||||
|
|
||||||
def on_ui_update(ui):
|
def on_ui_update(ui):
|
||||||
new_value = ' %d (%d)' % (UNREAD_MESSAGES, TOTAL_MESSAGES)
|
new_value = ' %d (%d)' % (UNREAD_MESSAGES, TOTAL_MESSAGES)
|
||||||
if not ui.has_element('mailbox') and TOTAL_MESSAGES > 0:
|
if not ui.has_element('mailbox') and UNREAD_MESSAGES > 0:
|
||||||
if ui.is_inky():
|
if ui.is_inky():
|
||||||
pos = (80, 0)
|
pos = (80, 0)
|
||||||
else:
|
else:
|
||||||
|
@@ -2,8 +2,19 @@
|
|||||||
#
|
#
|
||||||
# totalmem usedmem freemem cputemp
|
# totalmem usedmem freemem cputemp
|
||||||
#
|
#
|
||||||
|
###############################################################
|
||||||
|
#
|
||||||
|
# Updated 18-10-2019 by spees <speeskonijn@gmail.com>
|
||||||
|
# - Changed the place where the data was displayed on screen
|
||||||
|
# - Made the data a bit more compact and easier to read
|
||||||
|
# - removed the label so we wont waste screen space
|
||||||
|
# - Updated version to 1.0.1
|
||||||
|
#
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
|
||||||
__author__ = 'https://github.com/xenDE'
|
__author__ = 'https://github.com/xenDE'
|
||||||
__version__ = '1.0.0'
|
__version__ = '1.0.1'
|
||||||
__name__ = 'memtemp'
|
__name__ = 'memtemp'
|
||||||
__license__ = 'GPL3'
|
__license__ = 'GPL3'
|
||||||
__description__ = 'A plugin that will add a memory and temperature indicator'
|
__description__ = 'A plugin that will add a memory and temperature indicator'
|
||||||
@@ -21,9 +32,9 @@ class MEMTEMP:
|
|||||||
|
|
||||||
# set the minimum seconds before refresh the values
|
# set the minimum seconds before refresh the values
|
||||||
refresh_wait = 30
|
refresh_wait = 30
|
||||||
|
|
||||||
refresh_ts_last = time.time() - refresh_wait
|
refresh_ts_last = time.time() - refresh_wait
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# only import when the module is loaded and enabled
|
# only import when the module is loaded and enabled
|
||||||
import os
|
import os
|
||||||
@@ -31,9 +42,9 @@ class MEMTEMP:
|
|||||||
def get_temp(self):
|
def get_temp(self):
|
||||||
try:
|
try:
|
||||||
temp = os.popen('/opt/vc/bin/vcgencmd measure_temp').readlines()[0].split('=')[1].replace("\n", '').replace("'","")
|
temp = os.popen('/opt/vc/bin/vcgencmd measure_temp').readlines()[0].split('=')[1].replace("\n", '').replace("'","")
|
||||||
return 'cpu:' + temp
|
return 't:' + temp
|
||||||
except:
|
except:
|
||||||
return 'cpu:0.0C'
|
return 't:-'
|
||||||
# cpu:37.4C
|
# cpu:37.4C
|
||||||
|
|
||||||
def get_mem_info(self):
|
def get_mem_info(self):
|
||||||
@@ -42,9 +53,9 @@ class MEMTEMP:
|
|||||||
# total, used, free = map(int, os.popen('free -t -m').readlines()[-1].split()[1:])
|
# total, used, free = map(int, os.popen('free -t -m').readlines()[-1].split()[1:])
|
||||||
# without Swap, only real memory:
|
# without Swap, only real memory:
|
||||||
total, used, free = map(int, os.popen('free -t -m').readlines()[-3].split()[1:4])
|
total, used, free = map(int, os.popen('free -t -m').readlines()[-3].split()[1:4])
|
||||||
return "tm:"+str(total)+" um:"+str(used)+" fm:"+str(free)
|
return "\nT:"+str(total)+"M U:"+str(used)+"M\nF:"+str(free)+"M"
|
||||||
except:
|
except:
|
||||||
return "tm:0 um:0 fm:0"
|
return "\nT:- U:-\nF:- "
|
||||||
# tm:532 um:82 fm:353
|
# tm:532 um:82 fm:353
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +68,7 @@ def on_loaded():
|
|||||||
|
|
||||||
|
|
||||||
def on_ui_setup(ui):
|
def on_ui_setup(ui):
|
||||||
ui.add_element('memtemp', LabeledValue(color=BLACK, label='SYS', value='tm:0 um:0 fm:0 0.0C', position=(0, ui.height()-28),
|
ui.add_element('memtemp', LabeledValue(color=BLACK, label='', value='\nT:- U:-\nF:- -', position=(ui.width() / 2 + 17, ui.height() / 2),
|
||||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||||
|
|
||||||
|
|
||||||
@@ -66,3 +77,4 @@ def on_ui_update(ui):
|
|||||||
ui.set('memtemp', "%s %s" % (memtemp.get_mem_info(), memtemp.get_temp()))
|
ui.set('memtemp', "%s %s" % (memtemp.get_mem_info(), memtemp.get_temp()))
|
||||||
memtemp.refresh_ts_last = time.time()
|
memtemp.refresh_ts_last = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
@@ -123,6 +123,10 @@ class Display(View):
|
|||||||
def is_oledhat(self):
|
def is_oledhat(self):
|
||||||
return self._implementation.name == 'oledhat'
|
return self._implementation.name == 'oledhat'
|
||||||
|
|
||||||
|
def is_lcdhat(self):
|
||||||
|
return self._implementation.name == 'lcdhat'
|
||||||
|
|
||||||
|
|
||||||
def is_waveshare_any(self):
|
def is_waveshare_any(self):
|
||||||
return self.is_waveshare_v1() or self.is_waveshare_v2()
|
return self.is_waveshare_v1() or self.is_waveshare_v2()
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
from pwnagotchi.ui.hw.inky import Inky
|
from pwnagotchi.ui.hw.inky import Inky
|
||||||
from pwnagotchi.ui.hw.papirus import Papirus
|
from pwnagotchi.ui.hw.papirus import Papirus
|
||||||
from pwnagotchi.ui.hw.oledhat import OledHat
|
from pwnagotchi.ui.hw.oledhat import OledHat
|
||||||
|
from pwnagotchi.ui.hw.lcdhat import LcdHat
|
||||||
from pwnagotchi.ui.hw.waveshare1 import WaveshareV1
|
from pwnagotchi.ui.hw.waveshare1 import WaveshareV1
|
||||||
from pwnagotchi.ui.hw.waveshare2 import WaveshareV2
|
from pwnagotchi.ui.hw.waveshare2 import WaveshareV2
|
||||||
|
|
||||||
@@ -16,6 +17,10 @@ def display_for(config):
|
|||||||
if config['ui']['display']['type'] == 'oledhat':
|
if config['ui']['display']['type'] == 'oledhat':
|
||||||
return OledHat(config)
|
return OledHat(config)
|
||||||
|
|
||||||
|
if config['ui']['display']['type'] == 'lcdhat':
|
||||||
|
return LcdHat(config)
|
||||||
|
|
||||||
|
|
||||||
elif config['ui']['display']['type'] == 'waveshare_1':
|
elif config['ui']['display']['type'] == 'waveshare_1':
|
||||||
return WaveshareV1(config)
|
return WaveshareV1(config)
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@ class Inky(DisplayImpl):
|
|||||||
self._layout['face'] = (0, 37)
|
self._layout['face'] = (0, 37)
|
||||||
self._layout['name'] = (5, 18)
|
self._layout['name'] = (5, 18)
|
||||||
self._layout['channel'] = (0, 0)
|
self._layout['channel'] = (0, 0)
|
||||||
self._layout['aps'] = (25, 0)
|
self._layout['aps'] = (30, 0)
|
||||||
self._layout['uptime'] = (147, 0)
|
self._layout['uptime'] = (147, 0)
|
||||||
self._layout['line1'] = [0, 12, 212, 12]
|
self._layout['line1'] = [0, 12, 212, 12]
|
||||||
self._layout['line2'] = [0, 92, 212, 92]
|
self._layout['line2'] = [0, 92, 212, 92]
|
||||||
|
46
pwnagotchi/ui/hw/lcdhat.py
Normal file
46
pwnagotchi/ui/hw/lcdhat.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
import pwnagotchi.ui.fonts as fonts
|
||||||
|
from pwnagotchi.ui.hw.base import DisplayImpl
|
||||||
|
|
||||||
|
|
||||||
|
class LcdHat(DisplayImpl):
|
||||||
|
def __init__(self, config):
|
||||||
|
super(LcdHat, self).__init__(config, 'lcdhat')
|
||||||
|
self._display = None
|
||||||
|
|
||||||
|
def layout(self):
|
||||||
|
fonts.setup(10, 9, 10, 35)
|
||||||
|
self._layout['width'] = 240
|
||||||
|
self._layout['height'] = 240
|
||||||
|
self._layout['face'] = (0, 40)
|
||||||
|
self._layout['name'] = (5, 20)
|
||||||
|
self._layout['channel'] = (0, 0)
|
||||||
|
self._layout['aps'] = (28, 0)
|
||||||
|
self._layout['uptime'] = (175, 0)
|
||||||
|
self._layout['line1'] = [0, 14, 240, 14]
|
||||||
|
self._layout['line2'] = [0, 108, 240, 108]
|
||||||
|
self._layout['friend_face'] = (0, 92)
|
||||||
|
self._layout['friend_name'] = (40, 94)
|
||||||
|
self._layout['shakes'] = (0, 109)
|
||||||
|
self._layout['mode'] = (215, 109)
|
||||||
|
self._layout['status'] = {
|
||||||
|
'pos': (125, 20),
|
||||||
|
'font': fonts.Medium,
|
||||||
|
'max': 20
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._layout
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
logging.info("initializing lcdhat display")
|
||||||
|
from pwnagotchi.ui.hw.libs.waveshare.lcdhat.epd import EPD
|
||||||
|
self._display = EPD()
|
||||||
|
self._display.init()
|
||||||
|
self._display.Clear()
|
||||||
|
|
||||||
|
def render(self, canvas):
|
||||||
|
self._display.display(canvas)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self._display.clear()
|
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL import ImageOps
|
from PIL import ImageOps
|
||||||
from pwnagotchi.ui.hw.libs.papirus import LM75B
|
from pwnagotchi.ui.hw.libs.papirus.lm75b import LM75B
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
165
pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.py
Normal file
165
pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import spidev
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
import time
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class ST7789(object):
|
||||||
|
"""class for ST7789 240*240 1.3inch OLED displays."""
|
||||||
|
|
||||||
|
def __init__(self,spi,rst = 27,dc = 25,bl = 24):
|
||||||
|
self.width = 240
|
||||||
|
self.height = 240
|
||||||
|
#Initialize DC RST pin
|
||||||
|
self._dc = dc
|
||||||
|
self._rst = rst
|
||||||
|
self._bl = bl
|
||||||
|
GPIO.setmode(GPIO.BCM)
|
||||||
|
GPIO.setwarnings(False)
|
||||||
|
GPIO.setup(self._dc,GPIO.OUT)
|
||||||
|
GPIO.setup(self._rst,GPIO.OUT)
|
||||||
|
GPIO.setup(self._bl,GPIO.OUT)
|
||||||
|
GPIO.output(self._bl, GPIO.HIGH)
|
||||||
|
#Initialize SPI
|
||||||
|
self._spi = spi
|
||||||
|
self._spi.max_speed_hz = 40000000
|
||||||
|
|
||||||
|
""" Write register address and data """
|
||||||
|
def command(self, cmd):
|
||||||
|
GPIO.output(self._dc, GPIO.LOW)
|
||||||
|
self._spi.writebytes([cmd])
|
||||||
|
|
||||||
|
def data(self, val):
|
||||||
|
GPIO.output(self._dc, GPIO.HIGH)
|
||||||
|
self._spi.writebytes([val])
|
||||||
|
|
||||||
|
def Init(self):
|
||||||
|
"""Initialize dispaly"""
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
self.command(0x36)
|
||||||
|
self.data(0x70) #self.data(0x00)
|
||||||
|
|
||||||
|
self.command(0x3A)
|
||||||
|
self.data(0x05)
|
||||||
|
|
||||||
|
self.command(0xB2)
|
||||||
|
self.data(0x0C)
|
||||||
|
self.data(0x0C)
|
||||||
|
self.data(0x00)
|
||||||
|
self.data(0x33)
|
||||||
|
self.data(0x33)
|
||||||
|
|
||||||
|
self.command(0xB7)
|
||||||
|
self.data(0x35)
|
||||||
|
|
||||||
|
self.command(0xBB)
|
||||||
|
self.data(0x19)
|
||||||
|
|
||||||
|
self.command(0xC0)
|
||||||
|
self.data(0x2C)
|
||||||
|
|
||||||
|
self.command(0xC2)
|
||||||
|
self.data(0x01)
|
||||||
|
|
||||||
|
self.command(0xC3)
|
||||||
|
self.data(0x12)
|
||||||
|
|
||||||
|
self.command(0xC4)
|
||||||
|
self.data(0x20)
|
||||||
|
|
||||||
|
self.command(0xC6)
|
||||||
|
self.data(0x0F)
|
||||||
|
|
||||||
|
self.command(0xD0)
|
||||||
|
self.data(0xA4)
|
||||||
|
self.data(0xA1)
|
||||||
|
|
||||||
|
self.command(0xE0)
|
||||||
|
self.data(0xD0)
|
||||||
|
self.data(0x04)
|
||||||
|
self.data(0x0D)
|
||||||
|
self.data(0x11)
|
||||||
|
self.data(0x13)
|
||||||
|
self.data(0x2B)
|
||||||
|
self.data(0x3F)
|
||||||
|
self.data(0x54)
|
||||||
|
self.data(0x4C)
|
||||||
|
self.data(0x18)
|
||||||
|
self.data(0x0D)
|
||||||
|
self.data(0x0B)
|
||||||
|
self.data(0x1F)
|
||||||
|
self.data(0x23)
|
||||||
|
|
||||||
|
self.command(0xE1)
|
||||||
|
self.data(0xD0)
|
||||||
|
self.data(0x04)
|
||||||
|
self.data(0x0C)
|
||||||
|
self.data(0x11)
|
||||||
|
self.data(0x13)
|
||||||
|
self.data(0x2C)
|
||||||
|
self.data(0x3F)
|
||||||
|
self.data(0x44)
|
||||||
|
self.data(0x51)
|
||||||
|
self.data(0x2F)
|
||||||
|
self.data(0x1F)
|
||||||
|
self.data(0x1F)
|
||||||
|
self.data(0x20)
|
||||||
|
self.data(0x23)
|
||||||
|
|
||||||
|
self.command(0x21)
|
||||||
|
|
||||||
|
self.command(0x11)
|
||||||
|
|
||||||
|
self.command(0x29)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
"""Reset the display"""
|
||||||
|
GPIO.output(self._rst,GPIO.HIGH)
|
||||||
|
time.sleep(0.01)
|
||||||
|
GPIO.output(self._rst,GPIO.LOW)
|
||||||
|
time.sleep(0.01)
|
||||||
|
GPIO.output(self._rst,GPIO.HIGH)
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
def SetWindows(self, Xstart, Ystart, Xend, Yend):
|
||||||
|
#set the X coordinates
|
||||||
|
self.command(0x2A)
|
||||||
|
self.data(0x00) #Set the horizontal starting point to the high octet
|
||||||
|
self.data(Xstart & 0xff) #Set the horizontal starting point to the low octet
|
||||||
|
self.data(0x00) #Set the horizontal end to the high octet
|
||||||
|
self.data((Xend - 1) & 0xff) #Set the horizontal end to the low octet
|
||||||
|
|
||||||
|
#set the Y coordinates
|
||||||
|
self.command(0x2B)
|
||||||
|
self.data(0x00)
|
||||||
|
self.data((Ystart & 0xff))
|
||||||
|
self.data(0x00)
|
||||||
|
self.data((Yend - 1) & 0xff )
|
||||||
|
|
||||||
|
self.command(0x2C)
|
||||||
|
|
||||||
|
def ShowImage(self,Image,Xstart,Ystart):
|
||||||
|
"""Set buffer to value of Python Imaging Library image."""
|
||||||
|
"""Write display buffer to physical display"""
|
||||||
|
imwidth, imheight = Image.size
|
||||||
|
if imwidth != self.width or imheight != self.height:
|
||||||
|
raise ValueError('Image must be same dimensions as display \
|
||||||
|
({0}x{1}).' .format(self.width, self.height))
|
||||||
|
img = np.asarray(Image)
|
||||||
|
pix = np.zeros((self.width,self.height,2), dtype = np.uint8)
|
||||||
|
pix[...,[0]] = np.add(np.bitwise_and(img[...,[0]],0xF8),np.right_shift(img[...,[1]],5))
|
||||||
|
pix[...,[1]] = np.add(np.bitwise_and(np.left_shift(img[...,[1]],3),0xE0),np.right_shift(img[...,[2]],3))
|
||||||
|
pix = pix.flatten().tolist()
|
||||||
|
self.SetWindows ( 0, 0, self.width, self.height)
|
||||||
|
GPIO.output(self._dc,GPIO.HIGH)
|
||||||
|
for i in range(0,len(pix),4096):
|
||||||
|
self._spi.writebytes(pix[i:i+4096])
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""Clear contents of image buffer"""
|
||||||
|
_buffer = [0xff]*(self.width * self.height * 2)
|
||||||
|
self.SetWindows ( 0, 0, self.width, self.height)
|
||||||
|
GPIO.output(self._dc,GPIO.HIGH)
|
||||||
|
for i in range(0,len(_buffer),4096):
|
||||||
|
self._spi.writebytes(_buffer[i:i+4096])
|
BIN
pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.pyc
Normal file
BIN
pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.pyc
Normal file
Binary file not shown.
0
pwnagotchi/ui/hw/libs/waveshare/lcdhat/__init__.py
Normal file
0
pwnagotchi/ui/hw/libs/waveshare/lcdhat/__init__.py
Normal file
76
pwnagotchi/ui/hw/libs/waveshare/lcdhat/config.py
Normal file
76
pwnagotchi/ui/hw/libs/waveshare/lcdhat/config.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# /*****************************************************************************
|
||||||
|
# * | File : config.py
|
||||||
|
# * | Author : Guillaume Giraudon
|
||||||
|
# * | Info :
|
||||||
|
# *----------------
|
||||||
|
# * | This version: V1.0
|
||||||
|
# * | Date : 2019-10-18
|
||||||
|
# * | Info :
|
||||||
|
# ******************************************************************************/
|
||||||
|
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
import time
|
||||||
|
from smbus import SMBus
|
||||||
|
import spidev
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
# import spidev
|
||||||
|
|
||||||
|
# Pin definition
|
||||||
|
RST_PIN = 27
|
||||||
|
DC_PIN = 25
|
||||||
|
BL_PIN = 24
|
||||||
|
|
||||||
|
Device_SPI = 1
|
||||||
|
Device_I2C = 0
|
||||||
|
|
||||||
|
Device = Device_SPI
|
||||||
|
spi = spidev.SpiDev(0, 0)
|
||||||
|
|
||||||
|
def digital_write(pin, value):
|
||||||
|
GPIO.output(pin, value)
|
||||||
|
|
||||||
|
def digital_read(pin):
|
||||||
|
return GPIO.input(BUSY_PIN)
|
||||||
|
|
||||||
|
def delay_ms(delaytime):
|
||||||
|
time.sleep(delaytime / 1000.0)
|
||||||
|
|
||||||
|
def spi_writebyte(data):
|
||||||
|
# SPI.writebytes(data)
|
||||||
|
spi.writebytes([data[0]])
|
||||||
|
|
||||||
|
def i2c_writebyte(reg, value):
|
||||||
|
bus.write_byte_data(address, reg, value)
|
||||||
|
|
||||||
|
# time.sleep(0.01)
|
||||||
|
def module_init():
|
||||||
|
# print("module_init")
|
||||||
|
|
||||||
|
GPIO.setmode(GPIO.BCM)
|
||||||
|
GPIO.setwarnings(False)
|
||||||
|
GPIO.setup(RST_PIN, GPIO.OUT)
|
||||||
|
GPIO.setup(DC_PIN, GPIO.OUT)
|
||||||
|
|
||||||
|
|
||||||
|
# SPI.max_speed_hz = 2000000
|
||||||
|
# SPI.mode = 0b00
|
||||||
|
# i2c_writebyte(0xff,0xff)
|
||||||
|
# spi.SYSFS_software_spi_begin()
|
||||||
|
# spi.SYSFS_software_spi_setDataMode(0);
|
||||||
|
# spi.SYSFS_software_spi_setClockDivider(1);
|
||||||
|
#spi.max_speed_hz = 2000000
|
||||||
|
#spi.mode = 0b00
|
||||||
|
|
||||||
|
GPIO.output(BL_PIN, 1)
|
||||||
|
GPIO.output(DC_PIN, 0)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def module_exit():
|
||||||
|
spi.SYSFS_software_spi_end()
|
||||||
|
GPIO.output(RST_PIN, 0)
|
||||||
|
GPIO.output(DC_PIN, 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### END OF FILE ###
|
28
pwnagotchi/ui/hw/libs/waveshare/lcdhat/epd.py
Normal file
28
pwnagotchi/ui/hw/libs/waveshare/lcdhat/epd.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from . import ST7789
|
||||||
|
from . import config
|
||||||
|
|
||||||
|
# Display resolution
|
||||||
|
EPD_WIDTH = 240
|
||||||
|
EPD_HEIGHT = 240
|
||||||
|
|
||||||
|
disp = ST7789.ST7789(config.spi,config.RST_PIN, config.DC_PIN, config.BL_PIN)
|
||||||
|
|
||||||
|
class EPD(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.reset_pin = config.RST_PIN
|
||||||
|
self.dc_pin = config.DC_PIN
|
||||||
|
#self.busy_pin = config.BUSY_PIN
|
||||||
|
#self.cs_pin = config.CS_PIN
|
||||||
|
self.width = EPD_WIDTH
|
||||||
|
self.height = EPD_HEIGHT
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
disp.Init()
|
||||||
|
|
||||||
|
def Clear(self):
|
||||||
|
disp.clear()
|
||||||
|
|
||||||
|
def display(self, image):
|
||||||
|
rgb_im = image.convert('RGB')
|
||||||
|
disp.ShowImage(rgb_im,0,0)
|
@@ -38,7 +38,6 @@ class WaveshareV1(DisplayImpl):
|
|||||||
self._layout['name'] = (5, 15)
|
self._layout['name'] = (5, 15)
|
||||||
self._layout['channel'] = (0, 0)
|
self._layout['channel'] = (0, 0)
|
||||||
self._layout['aps'] = (28, 0)
|
self._layout['aps'] = (28, 0)
|
||||||
self._layout['status'] = (91, 15)
|
|
||||||
self._layout['uptime'] = (147, 0)
|
self._layout['uptime'] = (147, 0)
|
||||||
self._layout['line1'] = [0, 12, 212, 12]
|
self._layout['line1'] = [0, 12, 212, 12]
|
||||||
self._layout['line2'] = [0, 92, 212, 92]
|
self._layout['line2'] = [0, 92, 212, 92]
|
||||||
@@ -47,9 +46,9 @@ class WaveshareV1(DisplayImpl):
|
|||||||
self._layout['shakes'] = (0, 93)
|
self._layout['shakes'] = (0, 93)
|
||||||
self._layout['mode'] = (187, 93)
|
self._layout['mode'] = (187, 93)
|
||||||
self._layout['status'] = {
|
self._layout['status'] = {
|
||||||
'pos': (125, 20),
|
'pos': (91, 15),
|
||||||
'font': fonts.Medium,
|
'font': fonts.Medium,
|
||||||
'max': 14
|
'max': 20
|
||||||
}
|
}
|
||||||
return self._layout
|
return self._layout
|
||||||
|
|
||||||
|
@@ -31,11 +31,19 @@ def load_config(args):
|
|||||||
ref_defaults_file = os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.yml')
|
ref_defaults_file = os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.yml')
|
||||||
ref_defaults_data = None
|
ref_defaults_data = None
|
||||||
|
|
||||||
if not os.path.exists(args.config):
|
# check for a config.yml file on /boot/
|
||||||
|
if os.path.exists("/boot/config.yml"):
|
||||||
# logging not configured here yet
|
# logging not configured here yet
|
||||||
|
print("installing /boot/config.yml to %s ...", args.user_config)
|
||||||
|
# https://stackoverflow.com/questions/42392600/oserror-errno-18-invalid-cross-device-link
|
||||||
|
shutil.move("/boot/config.yml", args.user_config)
|
||||||
|
|
||||||
|
# if not config is found, copy the defaults
|
||||||
|
if not os.path.exists(args.config):
|
||||||
print("copying %s to %s ..." % (ref_defaults_file, args.config))
|
print("copying %s to %s ..." % (ref_defaults_file, args.config))
|
||||||
shutil.copy(ref_defaults_file, args.config)
|
shutil.copy(ref_defaults_file, args.config)
|
||||||
else:
|
else:
|
||||||
|
# check if the user messed with the defaults
|
||||||
with open(ref_defaults_file) as fp:
|
with open(ref_defaults_file) as fp:
|
||||||
ref_defaults_data = fp.read()
|
ref_defaults_data = fp.read()
|
||||||
|
|
||||||
@@ -46,9 +54,11 @@ def load_config(args):
|
|||||||
print("!!! file in %s is different than release defaults, overwriting !!!" % args.config)
|
print("!!! file in %s is different than release defaults, overwriting !!!" % args.config)
|
||||||
shutil.copy(ref_defaults_file, args.config)
|
shutil.copy(ref_defaults_file, args.config)
|
||||||
|
|
||||||
|
# load the defaults
|
||||||
with open(args.config) as fp:
|
with open(args.config) as fp:
|
||||||
config = yaml.safe_load(fp)
|
config = yaml.safe_load(fp)
|
||||||
|
|
||||||
|
# load the user config
|
||||||
if os.path.exists(args.user_config):
|
if os.path.exists(args.user_config):
|
||||||
with open(args.user_config) as fp:
|
with open(args.user_config) as fp:
|
||||||
user_config = yaml.safe_load(fp)
|
user_config = yaml.safe_load(fp)
|
||||||
@@ -131,6 +141,7 @@ def blink(times=1, delay=0.3):
|
|||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
led(True)
|
led(True)
|
||||||
|
|
||||||
|
|
||||||
class WifiInfo(Enum):
|
class WifiInfo(Enum):
|
||||||
"""
|
"""
|
||||||
Fields you can extract from a pcap file
|
Fields you can extract from a pcap file
|
||||||
@@ -141,6 +152,7 @@ class WifiInfo(Enum):
|
|||||||
CHANNEL = 3
|
CHANNEL = 3
|
||||||
RSSI = 4
|
RSSI = 4
|
||||||
|
|
||||||
|
|
||||||
class FieldNotFoundError(Exception):
|
class FieldNotFoundError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -172,7 +184,7 @@ def extract_from_pcap(path, fields):
|
|||||||
if hasattr(packet[Dot11], 'addr3'):
|
if hasattr(packet[Dot11], 'addr3'):
|
||||||
results[field] = packet[Dot11].addr3
|
results[field] = packet[Dot11].addr3
|
||||||
break
|
break
|
||||||
else: # magic
|
else: # magic
|
||||||
raise FieldNotFoundError("Could not find field [BSSID]")
|
raise FieldNotFoundError("Could not find field [BSSID]")
|
||||||
except Exception:
|
except Exception:
|
||||||
raise FieldNotFoundError("Could not find field [BSSID]")
|
raise FieldNotFoundError("Could not find field [BSSID]")
|
||||||
@@ -188,7 +200,7 @@ def extract_from_pcap(path, fields):
|
|||||||
if packet.haslayer(Dot11Elt) and hasattr(packet[Dot11Elt], 'info'):
|
if packet.haslayer(Dot11Elt) and hasattr(packet[Dot11Elt], 'info'):
|
||||||
results[field] = packet[Dot11Elt].info.decode('utf-8')
|
results[field] = packet[Dot11Elt].info.decode('utf-8')
|
||||||
break
|
break
|
||||||
else: # magic
|
else: # magic
|
||||||
raise FieldNotFoundError("Could not find field [ESSID]")
|
raise FieldNotFoundError("Could not find field [ESSID]")
|
||||||
except Exception:
|
except Exception:
|
||||||
raise FieldNotFoundError("Could not find field [ESSID]")
|
raise FieldNotFoundError("Could not find field [ESSID]")
|
||||||
@@ -202,9 +214,9 @@ def extract_from_pcap(path, fields):
|
|||||||
if packet.haslayer(Dot11Beacon) and hasattr(packet[Dot11Beacon], 'network_stats'):
|
if packet.haslayer(Dot11Beacon) and hasattr(packet[Dot11Beacon], 'network_stats'):
|
||||||
stats = packet[Dot11Beacon].network_stats()
|
stats = packet[Dot11Beacon].network_stats()
|
||||||
if 'crypto' in stats:
|
if 'crypto' in stats:
|
||||||
results[field] = stats['crypto'] # set with encryption types
|
results[field] = stats['crypto'] # set with encryption types
|
||||||
break
|
break
|
||||||
else: # magic
|
else: # magic
|
||||||
raise FieldNotFoundError("Could not find field [ENCRYPTION]")
|
raise FieldNotFoundError("Could not find field [ENCRYPTION]")
|
||||||
except Exception:
|
except Exception:
|
||||||
raise FieldNotFoundError("Could not find field [ENCRYPTION]")
|
raise FieldNotFoundError("Could not find field [ENCRYPTION]")
|
||||||
|
@@ -12,4 +12,5 @@ numpy==1.17.2
|
|||||||
inky==0.0.5
|
inky==0.0.5
|
||||||
smbus2==0.3.0
|
smbus2==0.3.0
|
||||||
Pillow==5.4.1
|
Pillow==5.4.1
|
||||||
spidev==3.4
|
spidev==3.4
|
||||||
|
gast==0.2.2
|
||||||
|
@@ -1,354 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# based on: https://wiki.debian.org/RaspberryPi/qemu-user-static
|
|
||||||
## and https://z4ziggy.wordpress.com/2015/05/04/from-bochs-to-chroot/
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
echo "THIS SCRIPT IS DEPRECATED, PLEASE REFER TO THE OFFICIAL DOCUMENTATION AT https://pwnagotchi.ai/contributing/#creating-an-image"
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
REQUIREMENTS=( wget gunzip git dd e2fsck resize2fs parted losetup qemu-system-x86_64 )
|
|
||||||
DEBREQUIREMENTS=( wget gzip git parted qemu-system-x86 qemu-user-static )
|
|
||||||
REPO_DIR="$(dirname "$(dirname "$(realpath "$0")")")"
|
|
||||||
TMP_DIR="${REPO_DIR}/tmp"
|
|
||||||
MNT_DIR="${TMP_DIR}/mnt"
|
|
||||||
THIS_DIR=$(pwd)
|
|
||||||
|
|
||||||
PWNI_NAME="pwnagotchi"
|
|
||||||
PWNI_OUTPUT="pwnagotchi.img"
|
|
||||||
PWNI_SIZE="8"
|
|
||||||
|
|
||||||
OPT_SPARSE=0
|
|
||||||
OPT_PROVISION_ONLY=0
|
|
||||||
OPT_CHECK_DEPS_ONLY=0
|
|
||||||
OPT_IMAGE_PROVIDED=0
|
|
||||||
OPT_RASPBIAN_VERSION='latest'
|
|
||||||
OPT_APTPROXY=""
|
|
||||||
|
|
||||||
SUPPORTED_RASPBIAN_VERSIONS=( 'latest' 'buster' 'stretch' )
|
|
||||||
|
|
||||||
if [[ "$EUID" -ne 0 ]]; then
|
|
||||||
echo "Run this script as root!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
function check_dependencies() {
|
|
||||||
if [ -f /etc/debian_version ];
|
|
||||||
then
|
|
||||||
echo "[+] Checking Debian dependencies"
|
|
||||||
|
|
||||||
for REQ in "${DEBREQUIREMENTS[@]}"; do
|
|
||||||
if ! dpkg -s "$REQ" >/dev/null 2>&1; then
|
|
||||||
echo "Dependency check failed for ${REQ}; use 'apt-get install ${REQ}' to install"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[+] Checking dependencies"
|
|
||||||
for REQ in "${REQUIREMENTS[@]}"; do
|
|
||||||
if ! type "$REQ" >/dev/null 2>&1; then
|
|
||||||
echo "Dependency check failed for ${REQ}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! test -e /usr/bin/qemu-arm-static; then
|
|
||||||
echo "[-] You need the package \"qemu-user-static\" for this to work."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! systemctl is-active systemd-binfmt.service >/dev/null 2>&1; then
|
|
||||||
mkdir -p "/lib/binfmt.d"
|
|
||||||
echo ':qemu-arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:F' > /lib/binfmt.d/qemu-arm-static.conf
|
|
||||||
systemctl restart systemd-binfmt.service
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_raspbian() {
|
|
||||||
VERSION="$1"
|
|
||||||
|
|
||||||
case "$VERSION" in
|
|
||||||
latest)
|
|
||||||
URL="https://downloads.raspberrypi.org/raspbian_lite_latest"
|
|
||||||
;;
|
|
||||||
buster)
|
|
||||||
URL="https://downloads.raspberrypi.org/raspbian/images/raspbian-2019-07-12/2019-07-10-raspbian-buster.zip"
|
|
||||||
;;
|
|
||||||
stretch)
|
|
||||||
URL="https://downloads.raspberrypi.org/raspbian/images/raspbian-2019-04-09/2019-04-08-raspbian-stretch.zip"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "[+] Downloading raspbian.zip"
|
|
||||||
mkdir -p "${TMP_DIR}"
|
|
||||||
wget --show-progress -qcO "${TMP_DIR}/raspbian.zip" "$URL"
|
|
||||||
echo "[+] Unpacking raspbian.zip to raspbian.img"
|
|
||||||
gunzip -c "${TMP_DIR}/raspbian.zip" > "${TMP_DIR}/raspbian.img"
|
|
||||||
}
|
|
||||||
|
|
||||||
function provide_raspbian() {
|
|
||||||
echo "[+] Providing path of raspbian file"
|
|
||||||
mkdir -p "${TMP_DIR}"
|
|
||||||
echo "[+] Unpacking raspbian.zip to raspbian.img"
|
|
||||||
gunzip -c "${PWNI_INPUT}" > "${TMP_DIR}/raspbian.img"
|
|
||||||
}
|
|
||||||
|
|
||||||
function setup_raspbian(){
|
|
||||||
# Detect the ability to create sparse files
|
|
||||||
if [ "${OPT_SPARSE}" -eq 0 ]; then
|
|
||||||
if ! type "bmaptool" >/dev/null 2>&1; then
|
|
||||||
echo "[!] bmaptool not available, not creating a sparse image"
|
|
||||||
else
|
|
||||||
echo "[+] Defaulting to sparse image generation as bmaptool is available"
|
|
||||||
OPT_SPARSE=1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Note that we 'extend' the raspbian.img
|
|
||||||
if [ "${OPT_SPARSE}" -eq 1 ];
|
|
||||||
then
|
|
||||||
# Resize sparse (so that we can use bmaptool later)
|
|
||||||
echo "[+] Resizing sparse image of ${PWNI_SIZE}GB (1000s)"
|
|
||||||
truncate -s ${PWNI_SIZE}GB "${TMP_DIR}/raspbian.img"
|
|
||||||
else
|
|
||||||
echo "[+] Resizing full image to ${PWNI_SIZE}G"
|
|
||||||
# Full disk-space using image (appends to raspbian image)
|
|
||||||
dd if=/dev/zero bs=1G count="${PWNI_SIZE}" >> "${TMP_DIR}/raspbian.img"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[+] Setup loop device"
|
|
||||||
mkdir -p "${MNT_DIR}"
|
|
||||||
LOOP_PATH="$(losetup --find --partscan --show "${TMP_DIR}/raspbian.img")"
|
|
||||||
PART2_START="$(parted -s "$LOOP_PATH" -- print | awk '$1==2{ print $2 }')"
|
|
||||||
parted -s "$LOOP_PATH" rm 2
|
|
||||||
parted -s "$LOOP_PATH" mkpart primary "$PART2_START" 100%
|
|
||||||
echo "[+] Check FS"
|
|
||||||
e2fsck -y -f "${LOOP_PATH}p2"
|
|
||||||
echo "[+] Resize FS"
|
|
||||||
resize2fs "${LOOP_PATH}p2"
|
|
||||||
echo "[+] Device is ${LOOP_PATH}"
|
|
||||||
echo "[+] Unmount if already mounted with other img"
|
|
||||||
mountpoint -q "${MNT_DIR}" && umount -R "${MNT_DIR}"
|
|
||||||
echo "[+] Mount /"
|
|
||||||
mount -o rw "${LOOP_PATH}p2" "${MNT_DIR}"
|
|
||||||
echo "[+] Mount /boot"
|
|
||||||
mount -o rw "${LOOP_PATH}p1" "${MNT_DIR}/boot"
|
|
||||||
mount --bind /dev "${MNT_DIR}/dev/"
|
|
||||||
mount --bind /sys "${MNT_DIR}/sys/"
|
|
||||||
mount --bind /proc "${MNT_DIR}/proc/"
|
|
||||||
mount --bind /dev/pts "${MNT_DIR}/dev/pts"
|
|
||||||
cp /usr/bin/qemu-arm-static "${MNT_DIR}/usr/bin"
|
|
||||||
cp /etc/resolv.conf "${MNT_DIR}/etc/resolv.conf"
|
|
||||||
}
|
|
||||||
|
|
||||||
function provision_raspbian() {
|
|
||||||
cd "${MNT_DIR}"
|
|
||||||
sed -i'' 's/^\([^#]\)/#\1/g' etc/ld.so.preload # add comments
|
|
||||||
echo "[+] Run chroot commands"
|
|
||||||
LANG=C LC_ALL=C LC_CTYPE=C chroot . bin/bash -x <<EOF
|
|
||||||
set -eu
|
|
||||||
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
||||||
|
|
||||||
if [ ! -z "${OPT_APTPROXY}" ];
|
|
||||||
then
|
|
||||||
echo "[+] Using Proxy ${OPT_APTPROXY}"
|
|
||||||
echo "Acquire::http { Proxy \"${OPT_APTPROXY}\"; }" >/etc/apt/apt.conf.d/99pwnagotchi_proxy
|
|
||||||
fi
|
|
||||||
|
|
||||||
uname -a
|
|
||||||
|
|
||||||
apt-get -y update
|
|
||||||
apt-get -y upgrade
|
|
||||||
apt-get -y install git vim screen build-essential golang python3-pip gawk
|
|
||||||
apt-get -y install libpcap-dev libusb-1.0-0-dev libnetfilter-queue-dev
|
|
||||||
apt-get -y install dphys-swapfile libopenmpi-dev libatlas-base-dev
|
|
||||||
apt-get -y install libjasper-dev libqtgui4 libqt4-test libopenjp2-7
|
|
||||||
apt-get -y install tcpdump libilmbase23 libopenexr23 libgstreamer1.0-0
|
|
||||||
apt-get -y install libavcodec58 libavformat58 libswscale5
|
|
||||||
|
|
||||||
# setup dphys-swapfile
|
|
||||||
echo "CONF_SWAPSIZE=1024" >/etc/dphys-swapfile
|
|
||||||
systemctl enable dphys-swapfile.service
|
|
||||||
|
|
||||||
# install pwnagotchi
|
|
||||||
cd /tmp
|
|
||||||
git clone https://github.com/evilsocket/pwnagotchi.git
|
|
||||||
rsync -aP pwnagotchi/sdcard/boot/* /boot/
|
|
||||||
rsync -aP pwnagotchi/sdcard/rootfs/* /
|
|
||||||
rm -rf /tmp/pwnagotchi
|
|
||||||
|
|
||||||
# configure pwnagotchi
|
|
||||||
echo -e "$PWNI_NAME" > /etc/hostname
|
|
||||||
sed -i "s@^127\.0\.0\.1 .*@127.0.0.1 localhost "$PWNI_NAME" "$PWNI_NAME".local@g" /etc/hosts
|
|
||||||
sed -i "s@alpha@$PWNI_NAME@g" /etc/motd
|
|
||||||
|
|
||||||
chmod +x /etc/rc.local
|
|
||||||
|
|
||||||
# need armv6l version of tensorflow and opencv-python, not armv7l
|
|
||||||
# PIP_OPTS="--upgrade --only-binary :all: --abi cp37m --platform linux_armv6l --target /usr/lib/python3.7/site-packages/"
|
|
||||||
# pip3 install \$PIP_OPTS opencv-python
|
|
||||||
# Should work for tensorflow too, but BUG: Hash mismatch; therefore:
|
|
||||||
wget -P /root/ -c https://www.piwheels.org/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl
|
|
||||||
wget -P /root/ -c https://www.piwheels.org/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl
|
|
||||||
# we need to install these on first raspberry start...
|
|
||||||
sed -i '/startup\.sh/i pip3 install --no-deps --force-reinstall --upgrade /root/tensorflow-1.13.1-cp37-none-linux_armv6l.whl /root/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl && rm /root/tensorflow-1.13.1-cp37-none-linux_armv6l.whl /root/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl && sed -i "/tensorflow/d" /etc/rc.local' /etc/rc.local
|
|
||||||
|
|
||||||
# newer version is broken
|
|
||||||
pip3 install gast==0.2.2
|
|
||||||
|
|
||||||
</root/pwnagotchi/scripts/requirements.txt xargs -I{} --max-args=1 --max-procs="$(nproc)"\
|
|
||||||
pip3 install --progress-bar off {}
|
|
||||||
|
|
||||||
# waveshare
|
|
||||||
pip3 install spidev RPi.GPIO
|
|
||||||
|
|
||||||
# install bettercap
|
|
||||||
export GOPATH=/root/go
|
|
||||||
taskset -c 1 go get -u github.com/bettercap/bettercap
|
|
||||||
mv "\$GOPATH/bin/bettercap" /usr/bin/bettercap
|
|
||||||
|
|
||||||
# install bettercap caplets (cant run bettercap in chroot)
|
|
||||||
cd /tmp
|
|
||||||
git clone https://github.com/bettercap/caplets.git
|
|
||||||
cd caplets
|
|
||||||
make install
|
|
||||||
rm -rf /tmp/caplets
|
|
||||||
cd /root # fixes getcwd error that was bugging me
|
|
||||||
|
|
||||||
# Re4son-Kernel
|
|
||||||
echo "deb http://http.re4son-kernel.com/re4son/ kali-pi main" > /etc/apt/sources.list.d/re4son.list
|
|
||||||
wget -O - https://re4son-kernel.com/keys/http/archive-key.asc | apt-key add -
|
|
||||||
apt update
|
|
||||||
apt install -y kalipi-kernel kalipi-bootloader kalipi-re4son-firmware kalipi-kernel-headers libraspberrypi0 libraspberrypi-dev libraspberrypi-doc libraspberrypi-bin
|
|
||||||
|
|
||||||
# Fix PARTUUID
|
|
||||||
PUUID_ROOT="\$(blkid "\$(df / --output=source | tail -1)" | grep -Po 'PARTUUID="\K[^"]+')"
|
|
||||||
PUUID_BOOT="\$(blkid "\$(df /boot --output=source | tail -1)" | grep -Po 'PARTUUID="\K[^"]+')"
|
|
||||||
|
|
||||||
# sed regex info: search for line containing / followed by whitespace or /boot (second sed)
|
|
||||||
# in this line, search for PARTUUID= followed by letters, numbers or "-"
|
|
||||||
# replace that match with the new PARTUUID
|
|
||||||
sed -i "/\/[ ]\+/s/PARTUUID=[A-Za-z0-9-]\+/PARTUUID=\$PUUID_ROOT/g" /etc/fstab
|
|
||||||
sed -i "/\/boot/s/PARTUUID=[A-Za-z0-9-]\+/PARTUUID=\$PUUID_BOOT/g" /etc/fstab
|
|
||||||
|
|
||||||
sed -i "s/root=[^ ]\+/root=PARTUUID=\${PUUID_ROOT}/g" /boot/cmdline.txt
|
|
||||||
|
|
||||||
# delete keys
|
|
||||||
find /etc/ssh/ -name "ssh_host_*key*" -delete
|
|
||||||
|
|
||||||
# slows down boot
|
|
||||||
systemctl disable apt-daily.timer apt-daily.service apt-daily-upgrade.timer apt-daily-upgrade.service
|
|
||||||
|
|
||||||
# unecessary services
|
|
||||||
systemctl disable triggerhappy bluetooth wpa_supplicant
|
|
||||||
|
|
||||||
EOF
|
|
||||||
sed -i'' 's/^#//g' etc/ld.so.preload
|
|
||||||
cd "${REPO_DIR}"
|
|
||||||
umount -R "${MNT_DIR}"
|
|
||||||
losetup -D "$(losetup -l | awk '/raspbian\.img/{print $1}')"
|
|
||||||
mv "${TMP_DIR}/raspbian.img" "${PWNI_OUTPUT}"
|
|
||||||
if [ "${OPT_SPARSE}" -eq 1 ];
|
|
||||||
then
|
|
||||||
bmaptool create -o "${PWNI_OUTPUT}.bmap" "${PWNI_OUTPUT}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
cat <<EOF
|
|
||||||
|
|
||||||
usage: $0 [OPTIONS]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-n <name> # Name of the pwnagotchi (default: pwnagotchi)
|
|
||||||
-i <file> # Provide the path of an already downloaded raspbian image
|
|
||||||
-o <file> # Name of the img-file (default: pwnagotchi.img)
|
|
||||||
-s <size> # Size which should be added to second partition (in Gigabyte) (default: 4)
|
|
||||||
-v <version> # Version of raspbian (Supported: ${SUPPORTED_RASPBIAN_VERSIONS[*]}; default: latest)
|
|
||||||
-p # Only run provisioning (assumes the image is already mounted)
|
|
||||||
-d # Only run dependencies checks
|
|
||||||
-h # Show this help
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
while getopts "A:n:i:o:s:v:dph" o; do
|
|
||||||
case "${o}" in
|
|
||||||
A)
|
|
||||||
OPT_APTPROXY="${OPTARG}"
|
|
||||||
;;
|
|
||||||
n)
|
|
||||||
PWNI_NAME="${OPTARG}"
|
|
||||||
;;
|
|
||||||
i)
|
|
||||||
PWNI_INPUT="${OPTARG}"
|
|
||||||
OPT_IMAGE_PROVIDED=1
|
|
||||||
;;
|
|
||||||
o)
|
|
||||||
PWNI_OUTPUT="${OPTARG}"
|
|
||||||
;;
|
|
||||||
s)
|
|
||||||
PWNI_SIZE="${OPTARG}"
|
|
||||||
;;
|
|
||||||
p)
|
|
||||||
OPT_PROVISION_ONLY=1
|
|
||||||
;;
|
|
||||||
d)
|
|
||||||
OPT_CHECK_DEPS_ONLY=1
|
|
||||||
;;
|
|
||||||
v)
|
|
||||||
if [[ "${SUPPORTED_RASPBIAN_VERSIONS[*]}" =~ ${OPTARG} ]]; then
|
|
||||||
OPT_RASPBIAN_VERSION="${OPTARG}"
|
|
||||||
else
|
|
||||||
usage
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
h)
|
|
||||||
usage
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
usage
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
shift $((OPTIND-1))
|
|
||||||
|
|
||||||
if [[ "$OPT_PROVISION_ONLY" -eq 1 ]]; then
|
|
||||||
provision_raspbian
|
|
||||||
exit 0
|
|
||||||
elif [[ "$OPT_CHECK_DEPS_ONLY" -eq 1 ]]; then
|
|
||||||
check_dependencies
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
check_dependencies
|
|
||||||
|
|
||||||
if [[ "$OPT_IMAGE_PROVIDED" -eq 1 ]]; then
|
|
||||||
provide_raspbian
|
|
||||||
else
|
|
||||||
get_raspbian "$OPT_RASPBIAN_VERSION"
|
|
||||||
fi
|
|
||||||
|
|
||||||
setup_raspbian
|
|
||||||
provision_raspbian
|
|
||||||
|
|
||||||
#Make a baby with a random gender, maybe do something fun with this later!
|
|
||||||
gender[0]="boy"
|
|
||||||
gender[1]="girl"
|
|
||||||
|
|
||||||
rand=$[ $RANDOM % 2 ]
|
|
||||||
|
|
||||||
echo -e "[+] Congratz, it's a ${gender[$rand]} (⌐■_■)!"
|
|
||||||
echo -e "[+] One more step: dd if=../${PWNI_OUTPUT} of=<PATH_TO_SDCARD> bs=4M status=progress"
|
|
||||||
|
|
||||||
if [ "${OPT_SPARSE}" -eq 1 ];
|
|
||||||
then
|
|
||||||
echo -e "[t] To transfer use: rsync -vaS --progress $(whoami)@$(hostname -f):${THIS_DIR}/../${PWNI_OUTPUT} <DEST>"
|
|
||||||
echo -e "[t] To burn with bmaptool: bmaptool copy ~/${PWNI_OUTPUT} /dev/<DEVICE>"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Helpful OSX reminder
|
|
||||||
echo -e "[t] Mac: use 'diskutil list' to figure out which device to burn to; 'diskutil unmountDisk' to unmount that disk'; then use /dev/rdiskX (note the 'r') for faster transfer"
|
|
Reference in New Issue
Block a user