Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	Makefile
#	pwnagotchi/plugins/default/hashie.py
This commit is contained in:
jayofelony
2024-03-07 21:37:27 +01:00
132 changed files with 3297 additions and 1080 deletions

View File

@ -1 +1 @@
__version__ = '2.8.2'
__version__ = '2.8.7'

View File

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

View File

@ -18,10 +18,6 @@ main.custom_plugin_repos = [
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/"
main.plugins.aircrackonly.enabled = true
main.plugins.hashie.enabled = true
main.plugins.auto-update.enabled = true
main.plugins.auto-update.install = true
main.plugins.auto-update.interval = 1
@ -36,7 +32,7 @@ main.plugins.bt-tether.devices.android-phone.netmask = 24
main.plugins.bt-tether.devices.android-phone.interval = 1
main.plugins.bt-tether.devices.android-phone.scantime = 10
main.plugins.bt-tether.devices.android-phone.max_tries = 10
main.plugins.bt-tether.devices.android-phone.share_internet = false
main.plugins.bt-tether.devices.android-phone.share_internet = true
main.plugins.bt-tether.devices.android-phone.priority = 1
main.plugins.bt-tether.devices.ios-phone.enabled = false
@ -47,7 +43,7 @@ main.plugins.bt-tether.devices.ios-phone.netmask = 24
main.plugins.bt-tether.devices.ios-phone.interval = 5
main.plugins.bt-tether.devices.ios-phone.scantime = 20
main.plugins.bt-tether.devices.ios-phone.max_tries = 0
main.plugins.bt-tether.devices.ios-phone.share_internet = false
main.plugins.bt-tether.devices.ios-phone.share_internet = true
main.plugins.bt-tether.devices.ios-phone.priority = 999
main.plugins.fix_services.enabled = true
@ -118,7 +114,7 @@ main.no_restart = false
main.filter = ""
main.log.path = "/home/pi/logs/pwnagotchi.log"
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
main.log.rotation.enabled = true
main.log.rotation.size = "10M"
@ -156,6 +152,10 @@ personality.bond_encounters_factor = 20000
personality.throttle_a = 0.4
personality.throttle_d = 0.9
personality.clear_on_exit = true # clear display when shutting down cleanly
ui.invert = false # false = black background, true = white background
ui.fps = 0.0
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
ui.font.size_offset = 0 # will be added to the font size
@ -185,6 +185,9 @@ ui.faces.debug = "(#__#)"
ui.faces.upload = "(1__0)"
ui.faces.upload1 = "(1__1)"
ui.faces.upload2 = "(0__1)"
ui.faces.png = false
ui.faces.position_x = 0
ui.faces.position_y = 34
ui.web.enabled = true
ui.web.address = "::" # listening on both ipv4 and ipv6 - switch to 0.0.0.0 to listen on just ipv4
@ -216,7 +219,7 @@ bettercap.silence = [
fs.memory.enabled = true
fs.memory.mounts.log.enabled = true
fs.memory.mounts.log.mount = "/home/pi/logs"
fs.memory.mounts.log.mount = "/etc/pwnagotchi/log/"
fs.memory.mounts.log.size = "50M"
fs.memory.mounts.log.sync = 60
fs.memory.mounts.log.zram = true

View File

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

View File

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

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-25 23:40+0100\n"
"POT-Creation-Date: 2024-02-16 15:26-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

View File

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

View File

@ -1,73 +0,0 @@
import pwnagotchi.plugins as plugins
import logging
import subprocess
import string
import os
'''
Aircrack-ng needed, to install:
> apt-get install aircrack-ng
'''
class AircrackOnly(plugins.Plugin):
__author__ = 'pwnagotchi [at] rossmarks [dot] uk'
__version__ = '1.0.1'
__license__ = 'GPL3'
__description__ = 'confirm pcap contains handshake/PMKID or delete it'
def __init__(self):
self.text_to_set = ""
self.options = dict()
def on_ready(self):
return
def on_loaded(self):
logging.info("aircrackonly plugin loaded")
if 'face' not in self.options:
self.options['face'] = '(>.<)'
check = subprocess.run(
'/usr/bin/dpkg -l aircrack-ng | grep aircrack-ng | awk \'{print $2, $3}\'', shell=True,
stdout=subprocess.PIPE)
check = check.stdout.decode('utf-8').strip()
if check != "aircrack-ng <none>":
logging.info("aircrackonly: Found " + check)
else:
logging.warning("aircrack-ng is not installed!")
def on_handshake(self, agent, filename, access_point, client_station):
display = agent.view()
to_delete = 0
handshake_found = 0
result = subprocess.run(('/usr/bin/aircrack-ng ' + filename + ' | grep "1 handshake" | awk \'{print $2}\''),
shell=True, stdout=subprocess.PIPE)
result = result.stdout.decode('utf-8').translate({ord(c): None for c in string.whitespace})
if result:
handshake_found = 1
logging.info("[AircrackOnly] contains handshake")
if handshake_found == 0:
result = subprocess.run(('/usr/bin/aircrack-ng ' + filename + ' | grep "PMKID" | awk \'{print $2}\''),
shell=True, stdout=subprocess.PIPE)
result = result.stdout.decode('utf-8').translate({ord(c): None for c in string.whitespace})
if result:
logging.info("[AircrackOnly] contains PMKID")
else:
to_delete = 1
if to_delete == 1:
os.remove(filename)
self.text_to_set = "Removed an uncrackable pcap"
logging.warning("Removed uncrackable pcap " + filename)
display.update(force=True)
def on_ui_update(self, ui):
if self.text_to_set:
ui.set('face', self.options['face'])
ui.set('status', self.text_to_set)
self.text_to_set = ""

View File

@ -28,7 +28,8 @@ def check(version, repo, native=True):
resp = requests.get("https://api.github.com/repos/%s/releases/latest" % repo)
latest = resp.json()
info['available'] = latest_ver = latest['tag_name'].replace('v', '')
is_arm64 = info['arch'].startswith('aarch')
is_armhf = info['arch'].startswith('arm')
is_aarch = info['arch'].startswith('aarch')
local = version_to_tuple(info['current'])
remote = version_to_tuple(latest_ver)
@ -36,12 +37,20 @@ def check(version, repo, native=True):
if not native:
info['url'] = "https://github.com/%s/archive/%s.zip" % (repo, latest['tag_name'])
else:
if is_arm64:
# check if this release is compatible with aarch64
if is_armhf:
# check if this release is compatible with armhf
for asset in latest['assets']:
download_url = asset['browser_download_url']
if (download_url.endswith('.zip') and
(info['arch'] in download_url or (is_arm64 and 'aarch64' in download_url))):
(info['arch'] in download_url or (is_armhf and 'armhf' in download_url))):
info['url'] = download_url
break
elif is_aarch:
# check if this release is compatible with arm64/aarch64
for asset in latest['assets']:
download_url = asset['browser_download_url']
if (download_url.endswith('.zip') and
(info['arch'] in download_url or (is_aarch and 'aarch' in download_url))):
info['url'] = download_url
break
@ -189,7 +198,7 @@ class AutoUpdate(plugins.Plugin):
to_check = [
('jayofelony/bettercap', parse_version('bettercap -version'), True, 'bettercap'),
('jayofelony/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'),
('jayofelony/pwnagotchi-bookworm', pwnagotchi.__version__, False, 'pwnagotchi')
('jayofelony/pwnagotchi', pwnagotchi.__version__, False, 'pwnagotchi')
]
for repo, local_version, is_native, svc_name in to_check:

View File

@ -11,6 +11,10 @@ from pwnagotchi import plugins
import pwnagotchi.ui.faces as faces
from pwnagotchi.bettercap import Client
from pwnagotchi.ui.components import Text
from pwnagotchi.ui.view import BLACK
import pwnagotchi.ui.fonts as fonts
class FixServices(plugins.Plugin):
__author__ = 'jayofelony'
@ -21,19 +25,16 @@ class FixServices(plugins.Plugin):
__help__ = """
Reload brcmfmac module when blindbug is detected, instead of rebooting. Adapted from WATCHDOG.
"""
__defaults__ = {
'enabled': True,
}
def __init__(self):
self.options = dict()
self.pattern1 = re.compile(r'wifi error while hopping to channel')
self.pattern2 = re.compile(r'Firmware has halted or crashed')
self.pattern3 = re.compile(r'error 400: could not find interface wlan0mon')
self.pattern = re.compile(r'brcmf_cfg80211_nexmon_set_channel.*?Set Channel failed')
self.pattern2 = re.compile(r'wifi error while hopping to channel')
self.pattern3 = re.compile(r'Firmware has halted or crashed')
self.pattern4 = re.compile(r'error 400: could not find interface wlan0mon')
self.isReloadingMon = False
self.connection = None
self.LASTTRY = 0
self._count = 0
def on_loaded(self):
"""
@ -42,14 +43,27 @@ class FixServices(plugins.Plugin):
logging.info("[Fix_Services] plugin loaded.")
def on_ready(self, agent):
last_lines = self.get_last_lines('journalctl', ['-n10', '-k'], 10)
last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'],
stdout=subprocess.PIPE).stdout))[-10:])
try:
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
logging.debug("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
if ",UP," in str(cmd_output):
logging.info("wlan0mon is up.")
logging.debug("wlan0mon is up.")
logging.info("[Fix_Services] Logs look good!")
if len(self.pattern.findall(last_lines)) >= 3:
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Blind-Bug detected. Restarting.')
display.update(force=True)
logging.debug('[Fix_Services] Blind-Bug detected. Restarting.')
try:
self._tryTurningItOffAndOnAgain(agent)
except Exception as err:
logging.warning("[Fix_Services turnOffAndOn] %s" % repr(err))
else:
logging.debug("[Fix_Services] Logs look good!")
except Exception as err:
logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err))
@ -63,12 +77,12 @@ class FixServices(plugins.Plugin):
# apparently this only gets messages from bettercap going to syslog, not from syslog
def on_bcap_sys_log(self, agent, event):
if re.search('wifi error while hopping to channel', event['data']['Message']):
logging.info("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
logging.info("[Fix_Services]**** restarting wifi.recon")
logging.debug("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
logging.debug("[Fix_Services]**** restarting wifi.recon")
try:
result = agent.run("wifi.recon off; wifi.recon on")
if result["success"]:
logging.info("[Fix_Services] wifi.recon flip: success!")
logging.debug("[Fix_Services] wifi.recon flip: success!")
if hasattr(agent, 'view'):
display = agent.view()
if display:
@ -82,23 +96,14 @@ class FixServices(plugins.Plugin):
logging.error("[Fix_Services]SYSLOG wifi.recon flip fail: %s" % err)
self._tryTurningItOffAndOnAgain(agent)
def get_last_lines(self, command, args, n):
try:
process = subprocess.Popen([command] + args, stdout=subprocess.PIPE)
output = TextIOWrapper(process.stdout)
lines = output.readlines()
last_n_lines = ''.join(lines[-n:])
return last_n_lines
except Exception as e:
print(f"Error occurred: {e}")
return None
def on_epoch(self, agent, epoch, epoch_data):
last_lines = self.get_last_lines('journalctl', ['-n10', '-k'], 10)
other_last_lines = self.get_last_lines('journalctl', ['-n10'], 10)
other_other_last_lines = self.get_last_lines('tail', ['-n10', '/home/pi/logs/pwnagotchi.log'], 10)
last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'],
stdout=subprocess.PIPE).stdout))[-10:])
other_last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10'],
stdout=subprocess.PIPE).stdout))[-10:])
other_other_last_lines = ''.join(
list(TextIOWrapper(subprocess.Popen(['tail', '-n10', '/var/log/pwnagotchi.log'],
stdout=subprocess.PIPE).stdout))[-10:])
# don't check if we ran a reset recently
logging.debug("[Fix_Services]**** epoch")
if time.time() - self.LASTTRY > 180:
@ -108,18 +113,31 @@ class FixServices(plugins.Plugin):
logging.debug("[Fix_Services]**** checking")
# Look for pattern 1
if len(self.pattern1.findall(other_last_lines)) >= 5:
if len(self.pattern.findall(last_lines)) >= 3:
logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Blind-Bug detected. Restarting.')
display.update(force=True)
logging.debug('[Fix_Services] Blind-Bug detected. Restarting.')
try:
self._tryTurningItOffAndOnAgain(agent)
except Exception as err:
logging.warning("[Fix_Services] TTOAOA: %s" % repr(err))
# Look for pattern 2
elif len(self.pattern2.findall(other_last_lines)) >= 5:
logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Wifi channel stuck. Restarting recon.')
display.update(force=True)
logging.info('[Fix_Services] Wifi channel stuck. Restarting recon.')
logging.debug('[Fix_Services] Wifi channel stuck. Restarting recon.')
try:
result = agent.run("wifi.recon off; wifi.recon on")
if result["success"]:
logging.info("[Fix_Services] wifi.recon flip: success!")
logging.debug("[Fix_Services] wifi.recon flip: success!")
if display:
display.update(force=True, new_data={"status": "Wifi recon flipped!",
"face": faces.COOL})
@ -131,9 +149,9 @@ class FixServices(plugins.Plugin):
except Exception as err:
logging.error("[Fix_Services wifi.recon flip] %s" % repr(err))
# Look for pattern 2
elif len(self.pattern2.findall(other_last_lines)) >= 1:
logging.info("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
# Look for pattern 3
elif len(self.pattern3.findall(other_last_lines)) >= 1:
logging.debug("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Firmware has halted or crashed. Restarting wlan0mon.')
@ -145,9 +163,9 @@ class FixServices(plugins.Plugin):
except Exception as err:
logging.error("[Fix_Services monstart]: %s" % repr(err))
# Look for pattern 3
elif len(self.pattern3.findall(other_other_last_lines)) >= 3:
logging.info("[Fix_Services] wlan0 is down!")
# Look for pattern 4
elif len(self.pattern4.findall(other_other_last_lines)) >= 3:
logging.debug("[Fix_Services] wlan0 is down!")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Restarting wlan0 now!')
@ -171,7 +189,7 @@ class FixServices(plugins.Plugin):
elif level == "debug":
logging.debug(message)
else:
logging.info(message)
logging.debug(message)
if ui:
ui.update(force=force, new_data=displayData)
@ -186,7 +204,7 @@ class FixServices(plugins.Plugin):
# avoid overlapping restarts, but allow it if it's been a while
# (in case the last attempt failed before resetting "isReloadingMon")
if self.isReloadingMon and (time.time() - self.LASTTRY) < 180:
logging.info("[Fix_Services] Duplicate attempt ignored")
logging.debug("[Fix_Services] Duplicate attempt ignored")
else:
self.isReloadingMon = True
self.LASTTRY = time.time()
@ -213,7 +231,7 @@ class FixServices(plugins.Plugin):
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
logging.debug("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
if ",UP," in str(cmd_output):
logging.info("wlan0mon is up. Skip reset?")
logging.debug("wlan0mon is up. Skip reset?")
# not reliable, so don't skip just yet
# print("wlan0mon is up. Skipping reset.")
# self.isReloadingMon = False
@ -234,7 +252,7 @@ class FixServices(plugins.Plugin):
except Exception as err:
logging.error("[Fix_Services wifi.recon off] error %s" % (repr(err)))
logging.info("[Fix_Services] recon paused. Now trying wlan0mon reload")
logging.debug("[Fix_Services] recon paused. Now trying wlan0mon reload")
try:
cmd_output = subprocess.check_output("monstop", shell=True)
@ -250,14 +268,13 @@ class FixServices(plugins.Plugin):
#
# Future: while "not fixed yet": blah blah blah. if "max_attemts", then reboot like the old days
#
tries = 0
tries = 1
while tries < 3:
try:
# unload the module
cmd_output = subprocess.check_output("sudo modprobe -r brcmfmac", shell=True)
self.logPrintView("info", "[Fix_Services] unloaded brcmfmac", display,
{"status": "Turning it off #%s" % tries, "face": faces.SMART})
time.sleep(1 + tries)
# reload the module
try:
@ -265,27 +282,25 @@ class FixServices(plugins.Plugin):
cmd_output = subprocess.check_output("sudo modprobe brcmfmac", shell=True)
self.logPrintView("info", "[Fix_Services] reloaded brcmfmac")
time.sleep(10 + 4 * tries) # give it some time for wlan device to stabilize, or whatever
# success! now make the mon0
try:
cmd_output = subprocess.check_output("monstart", shell=True)
self.logPrintView("info", "[Fix_Services interface add wlan0mon] worked #%s: %s"
self.logPrintView("info", "[Fix_Services interface add wlan0mon worked #%s: %s"
% (tries, cmd_output))
time.sleep(tries + 5)
try:
# try accessing mon0 in bettercap
result = connection.run("set wifi.interface wlan0mon")
if "success" in result:
logging.info("[Fix_Services set wifi.interface wlan0mon] worked!")
self._count = self._count + 1
time.sleep(1)
logging.debug("[Fix_Services set wifi.interface wlan0mon worked!")
# stop looping and get back to recon
break
else:
logging.debug("[Fix_Services set wifi.interfaceface wlan0mon] failed? %s" % repr(result))
logging.debug(
"[Fix_Services set wifi.interfaceface wlan0mon] failed? %s" % repr(result))
except Exception as err:
logging.debug("[Fix_Services set wifi.interface wlan0mon] except: %s" % repr(err))
logging.debug(
"[Fix_Services set wifi.interface wlan0mon] except: %s" % repr(err))
except Exception as cerr: #
if not display:
print("failed loading wlan0mon attempt #%s: %s" % (tries, repr(cerr)))
@ -303,11 +318,11 @@ class FixServices(plugins.Plugin):
tries = tries + 1
if tries < 3:
logging.info("[Fix_Services] wlan0mon didn't make it. trying again")
logging.debug("[Fix_Services] wlan0mon didn't make it. trying again")
if not display:
print(" wlan0mon didn't make it. trying again")
else:
logging.info("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
logging.debug("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
pwnagotchi.reboot()
# exited the loop, so hopefully it loaded
@ -317,14 +332,14 @@ class FixServices(plugins.Plugin):
"face": faces.INTENSE})
else:
print("And back on again...")
logging.info("[Fix_Services] wlan0mon back up")
logging.debug("[Fix_Services] wlan0mon back up")
else:
self.LASTTRY = time.time()
time.sleep(8 + tries * 2) # give it a bit before restarting recon in bettercap
self.isReloadingMon = False
logging.info("[Fix_Services] re-enable recon")
logging.debug("[Fix_Services] re-enable recon")
try:
result = connection.run("wifi.clear; wifi.recon on")
@ -345,13 +360,24 @@ class FixServices(plugins.Plugin):
logging.error("[Fix_Services wifi.recon on] %s" % repr(err))
pwnagotchi.reboot()
def on_unload(self, ui):
# called to setup the ui elements
def on_ui_setup(self, ui):
with ui._lock:
try:
logging.info("[Fix_Services] unloaded")
except Exception as err:
logging.error("[Fix_Services] unload err %s " % repr(err))
pass
# add custom UI elements
if "position" in self.options:
pos = self.options['position'].split(',')
pos = [int(x.strip()) for x in pos]
else:
pos = (ui.width() / 2 + 35, ui.height() - 11)
logging.debug("Got here")
# called when the ui is updated
def on_ui_update(self, ui):
return
def on_unload(self, ui):
return
# run from command line to brute force a reload

View File

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

View File

@ -1,179 +0,0 @@
import logging
import subprocess
import os
import json
import pwnagotchi.plugins as plugins
from threading import Lock
'''
hcxpcapngtool needed, to install:
> git clone https://github.com/ZerBea/hcxtools.git
> cd hcxtools
> apt-get install libcurl4-openssl-dev libssl-dev zlib1g-dev
> make
> sudo make install
'''
class Hashie(plugins.Plugin):
__author__ = 'Jayofelony'
__version__ = '1.0.4'
__license__ = 'GPL3'
__description__ = '''
Attempt to automatically convert pcaps to a crackable format.
If successful, the files containing the hashes will be saved
in the same folder as the handshakes.
The files are saved in their respective Hashcat format:
- EAPOL hashes are saved as *.22000
- PMKID hashes are saved as *.16800
All PCAP files without enough information to create a hash are
stored in a file that can be read by the webgpsmap plugin.
Why use it?:
- Automatically convert handshakes to crackable formats!
We dont all upload our hashes online ;)
- Repair PMKID handshakes that hcxpcapngtool misses
- If running at time of handshake capture, on_handshake can
be used to improve the chance of the repair succeeding
- Be a completionist! Not enough packets captured to crack a network?
This generates an output file for the webgpsmap plugin, use the
location data to revisit networks you need more packets for!
Additional information:
- Currently requires hcxpcapngtool compiled and installed
- Attempts to repair PMKID hashes when hcxpcapngtool cant find the SSID
- hcxpcapngtool sometimes has trouble extracting the SSID, so we
use the raw 16800 output and attempt to retrieve the SSID via tcpdump
- When access_point data is available (on_handshake), we leverage
the reported AP name and MAC to complete the hash
- The repair is very basic and could certainly be improved!
Todo:
Make it so users dont need hcxpcapngtool (unless it gets added to the base image)
Phase 1: Extract/construct 22000/16800 hashes through tcpdump commands
Phase 2: Extract/construct 22000/16800 hashes entirely in python
Improve the code, a lot
'''
def __init__(self):
self.lock = Lock()
self.options = dict()
def on_loaded(self):
logging.info("[Hashie] Plugin loaded")
def on_unloaded(self):
logging.info("[Hashie] Plugin unloaded")
# called when everything is ready and the main loop is about to start
def on_ready(self, agent):
config = agent.config()
handshake_dir = config['bettercap']['handshakes']
logging.info('[Hashie] Starting batch conversion of pcap files')
with self.lock:
self._process_stale_pcaps(handshake_dir)
def on_handshake(self, agent, filename, access_point, client_station):
with self.lock:
handshake_status = []
fullpathNoExt = filename.split('.')[0]
name = filename.split('/')[-1:][0].split('.')[0]
if os.path.isfile(fullpathNoExt + '.22000'):
handshake_status.append('Already have {}.22000 (EAPOL)'.format(name))
elif self._writeEAPOL(filename):
handshake_status.append('Created {}.22000 (EAPOL) from pcapng'.format(name))
if os.path.isfile(fullpathNoExt + '.16800'):
handshake_status.append('Already have {}.16800 (PMKID)'.format(name))
elif self._writePMKID(filename):
handshake_status.append('Created {}.16800 (PMKID) from pcapng'.format(name))
if handshake_status:
logging.info('[Hashie] Good news:\n\t' + '\n\t'.join(handshake_status))
def _writeEAPOL(self, fullpath):
fullpathNoExt = fullpath.split('.')[0]
filename = fullpath.split('/')[-1:][0].split('.')[0]
subprocess.getoutput('hcxpcapngtool -o {}.22000 {} >/dev/null 2>&1'.format(fullpathNoExt, fullpath))
if os.path.isfile(fullpathNoExt + '.22000'):
logging.debug('[Hashie] [+] EAPOL Success: {}.22000 created'.format(filename))
return True
return False
def _writePMKID(self, fullpath):
fullpathNoExt = fullpath.split('.')[0]
filename = fullpath.split('/')[-1:][0].split('.')[0]
subprocess.getoutput('hcxpcapngtool -o {}.16800 {} >/dev/null 2>&1'.format(fullpathNoExt, fullpath))
if os.path.isfile(fullpathNoExt + '.16800'):
logging.debug('[Hashie] [+] PMKID Success: {}.16800 created'.format(filename))
return True
return False
def _process_stale_pcaps(self, handshake_dir):
handshakes_list = [os.path.join(handshake_dir, filename) for filename in os.listdir(handshake_dir) if filename.endswith('.pcapng')]
failed_jobs = []
successful_jobs = []
lonely_pcaps = []
for num, handshake in enumerate(handshakes_list):
fullpathNoExt = handshake.split('.')[0]
pcapFileName = handshake.split('/')[-1:][0]
if not os.path.isfile(fullpathNoExt + '.22000'): # if no 22000, try
if self._writeEAPOL(handshake):
successful_jobs.append('22000: ' + pcapFileName)
else:
failed_jobs.append('22000: ' + pcapFileName)
if not os.path.isfile(fullpathNoExt + '.16800'): # if no 16800, try
if self._writePMKID(handshake):
successful_jobs.append('16800: ' + pcapFileName)
else:
failed_jobs.append('16800: ' + pcapFileName)
if not os.path.isfile(fullpathNoExt + '.22000'): # if no 16800 AND no 22000
lonely_pcaps.append(handshake)
logging.debug('[hashie] Batch job: added {} to lonely list'.format(pcapFileName))
if ((num + 1) % 50 == 0) or (num + 1 == len(handshakes_list)): # report progress every 50, or when done
logging.info('[Hashie] Batch job: {}/{} done ({} fails)'.format(num + 1, len(handshakes_list), len(lonely_pcaps)))
if successful_jobs:
logging.info('[Hashie] Batch job: {} new handshake files created'.format(len(successful_jobs)))
if lonely_pcaps:
logging.info('[Hashie] Batch job: {} networks without enough packets to create a hash'.format(len(lonely_pcaps)))
self._getLocations(lonely_pcaps)
def _getLocations(self, lonely_pcaps):
# export a file for webgpsmap to load
with open('/root/.incompletePcaps', 'w') as isIncomplete:
count = 0
for pcapFile in lonely_pcaps:
filename = pcapFile.split('/')[-1:][0] # keep extension
fullpathNoExt = pcapFile.split('.')[0]
isIncomplete.write(filename + '\n')
if os.path.isfile(fullpathNoExt + '.gps.json') or os.path.isfile(fullpathNoExt + '.geo.json'):
count += 1
if count != 0:
logging.info('[Hashie] Used {} GPS/GEO files to find lonely networks, '
'go check webgpsmap! ;)'.format(str(count)))
else:
logging.info('[Hashie] Could not find any GPS/GEO files '
'for the lonely networks'.format(str(count)))
def _getLocationsCSV(self, lonely_pcaps):
# in case we need this later, export locations manually to CSV file, needs try/catch format/etc.
locations = []
for pcapFile in lonely_pcaps:
filename = pcapFile.split('/')[-1:][0].split('.')[0]
fullpathNoExt = pcapFile.split('.')[0]
if os.path.isfile(fullpathNoExt + '.gps.json'):
with open(fullpathNoExt + '.gps.json', 'r') as tempFileA:
data = json.load(tempFileA)
locations.append(filename + ',' + str(data['Latitude']) + ',' + str(data['Longitude']) + ',50')
elif os.path.isfile(fullpathNoExt + '.geo.json'):
with open(fullpathNoExt + '.geo.json', 'r') as tempFileB:
data = json.load(tempFileB)
locations.append(
filename + ',' + str(data['location']['lat']) + ',' + str(data['location']['lng']) + ',' + str(data['accuracy']))
if locations:
with open('/root/locations.csv', 'w') as tempFileD:
for loc in locations:
tempFileD.write(loc + '\n')
logging.info('[Hashie] Used {} GPS/GEO files to find lonely networks, '
'load /root/locations.csv into a mapping app and go say hi!'.format(len(locations)))

View File

@ -144,6 +144,9 @@ class MemTemp(plugins.Plugin):
elif ui.is_waveshare2in7():
h_pos = (192, 138)
v_pos = (211, 122)
elif ui.is_waveshare1in54V2():
h_pos = (53, 77)
v_pos = (154, 65)
else:
h_pos = (155, 76)
v_pos = (175, 61)

View File

@ -1,4 +1,4 @@
from PIL import Image
from PIL import Image, ImageOps
from textwrap import TextWrapper
@ -40,21 +40,37 @@ class FilledRect(Widget):
class Text(Widget):
def __init__(self, value="", position=(0, 0), font=None, color=0, wrap=False, max_length=0):
def __init__(self, value="", position=(0, 0), font=None, color=0, wrap=False, max_length=0, png=False):
super().__init__(position, color)
self.value = value
self.font = font
self.wrap = wrap
self.max_length = max_length
self.wrapper = TextWrapper(width=self.max_length, replace_whitespace=False) if wrap else None
self.png = png
def draw(self, canvas, drawer):
if self.value is not None:
if self.wrap:
text = '\n'.join(self.wrapper.wrap(self.value))
if not self.png:
if self.wrap:
text = '\n'.join(self.wrapper.wrap(self.value))
else:
text = self.value
drawer.text(self.xy, text, font=self.font, fill=self.color)
else:
text = self.value
drawer.text(self.xy, text, font=self.font, fill=self.color)
self.image = Image.open(self.value)
self.image = self.image.convert('RGBA')
self.pixels = self.image.load()
for y in range(self.image.size[1]):
for x in range(self.image.size[0]):
if self.pixels[x,y][3] < 255: # check alpha
self.pixels[x,y] = (255, 255, 255, 255)
if self.color == 255:
self._image = ImageOps.colorize(self.image.convert('L'), black = "white", white = "black")
else:
self._image = self.image
self.image = self._image.convert('1')
canvas.paste(self.image, self.xy)
class LabeledValue(Widget):

View File

@ -118,6 +118,9 @@ class Display(View):
def is_waveshare2in66g(self):
return self._implementation.name == 'waveshare2in66g'
def is_weact2in9(self):
return self._implementation.name == 'weact2in9'
def is_waveshare3in0g(self):
return self._implementation.name == 'waveshare3in0g'

View File

@ -23,7 +23,9 @@ DEBUG = '(#__#)'
UPLOAD = '(1__0)'
UPLOAD1 = '(1__1)'
UPLOAD2 = '(0__1)'
PNG = False
POSITION_X = 0
POSITION_Y = 40
def load_from_config(config):
for face_name, face_value in config.items():

View File

@ -12,6 +12,7 @@ from . import config
from . import LCD_1in44
from PIL import ImageOps
class EPD(object):
def __init__(self):
self.width = 128
@ -24,9 +25,8 @@ class EPD(object):
pass
def clear(self):
#self.LCD.LCD_Clear()
pass
self.LCD.LCD_Clear()
def display(self, image):
rgb_im = ImageOps.colorize(image.convert("L"), black ="green", white ="black")
rgb_im = ImageOps.colorize(image.convert("L"), black="green", white="black")
self.LCD.LCD_ShowImage(rgb_im, 0, 0)

View File

@ -0,0 +1,242 @@
# *****************************************************************************
# * | File : epd2in13g.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V1.0
# * | Date : 2023-05-29
# # | Info : python demo
# -----------------------------------------------------------------------------
# ******************************************************************************/
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import logging
from .. import epdconfig
import PIL
from PIL import Image
import io
# Display resolution
EPD_WIDTH = 122
EPD_HEIGHT = 250
logger = logging.getLogger(__name__)
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
self.BLACK = 0x000000 # 00 BGR
self.WHITE = 0xffffff # 01
self.YELLOW = 0x00ffff # 10
self.RED = 0x0000ff # 11
self.Gate_BITS = EPD_HEIGHT
if self.width < 128:
self.Source_BITS = 128
else:
self.Source_BITS = self.width
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, 0) # module reset
epdconfig.delay_ms(2)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, 1)
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self):
logger.debug("e-Paper busy H")
epdconfig.delay_ms(100)
while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
epdconfig.delay_ms(5)
logger.debug("e-Paper busy release")
def SetWindow(self):
self.send_command(0x61) # SET_RAM_X_ADDRESS_START_END_POSITION
# x point must be the multiple of 8 or the last 3 bits will be ignored
self.send_data(self.Source_BITS / 256)
self.send_data(self.Source_BITS % 256)
self.send_data(self.Gate_BITS / 256)
self.send_data(self.Gate_BITS % 256)
def TurnOnDisplay(self):
self.send_command(0x12) # DISPLAY_REFRESH
self.send_data(0X00)
self.ReadBusy()
def init(self):
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()
self.ReadBusy()
self.send_command(0x4D)
self.send_data(0x78)
self.send_command(0x00)
self.send_data(0x0F)
self.send_data(0x29)
self.send_command(0x01)
self.send_data(0x07)
self.send_data(0x00)
self.send_command(0x03)
self.send_data(0x10)
self.send_data(0x54)
self.send_data(0x44)
self.send_command(0x06)
self.send_data(0x05)
self.send_data(0x00)
self.send_data(0x3F)
self.send_data(0x0A)
self.send_data(0x25)
self.send_data(0x12)
self.send_data(0x1A)
self.send_command(0x50)
self.send_data(0x37)
self.send_command(0x60)
self.send_data(0x02)
self.send_data(0x02)
self.SetWindow()
self.send_command(0xE7)
self.send_data(0x1C)
self.send_command(0xE3)
self.send_data(0x22)
self.send_command(0xB4)
self.send_data(0xD0)
self.send_command(0xB5)
self.send_data(0x03)
self.send_command(0xE9)
self.send_data(0x01)
self.send_command(0x30)
self.send_data(0x08)
self.send_command(0x04)
self.ReadBusy()
return 0
def getbuffer(self, image):
# Create a pallette with the 4 colors supported by the panel
pal_image = Image.new("P", (1, 1))
pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252)
# Check if we need to rotate the image
imwidth, imheight = image.size
if (imwidth == self.width and imheight == self.height):
image_temp = image
elif (imwidth == self.height and imheight == self.width):
image_temp = image.rotate(90, expand=True)
else:
logger.warning(
"Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height))
# Convert the soruce image to the 4 colors, dithering if needed
image_4color = image_temp.convert("RGB").quantize(palette=pal_image)
buf_4color = bytearray(image_4color.tobytes('raw'))
# into a single byte to transfer to the panel
if self.width % 4 == 0:
Width = self.width // 4
else:
Width = self.width // 4 + 1
Height = self.height
buf = [0x00] * int(Width * Height)
idx = 0
for j in range(0, Height):
for i in range(0, Width):
if i == Width - 1:
buf[i + j * Width] = (buf_4color[idx] << 6) + (buf_4color[idx + 1] << 4)
idx = idx + 2
else:
buf[i + j * Width] = (buf_4color[idx] << 6) + (buf_4color[idx + 1] << 4) + (
buf_4color[idx + 2] << 2) + buf_4color[idx + 3]
idx = idx + 4
return buf
def display(self, image):
if self.width % 4 == 0:
Width = self.width // 4
else:
Width = self.width // 4 + 1
Height = self.height
self.send_command(0x10)
for j in range(0, Height):
for i in range(0, self.Source_BITS // 4):
if i < 31:
self.send_data(image[i + j * Width])
else:
self.send_data(0x00)
self.TurnOnDisplay()
def Clear(self, color=0x55):
Width = self.Source_BITS // 4
Height = self.height
self.send_command(0x10)
for j in range(0, Height):
for i in range(0, Width):
self.send_data(color)
self.TurnOnDisplay()
def sleep(self):
self.send_command(0x02) # POWER_OFF
self.ReadBusy()
epdconfig.delay_ms(100)
self.send_command(0x07) # DEEP_SLEEP
self.send_data(0XA5)
epdconfig.delay_ms(2000)
epdconfig.module_exit()
### END OF FILE ###

View File

@ -0,0 +1,243 @@
# /*****************************************************************************
# * | File : epdconfig.py
# * | Author : Waveshare team
# * | Function : Hardware underlying interface
# * | Info :
# *----------------
# * | This version: V1.2
# * | Date : 2022-10-29
# * | Info :
# ******************************************************************************
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import os
import logging
import sys
import time
logger = logging.getLogger(__name__)
class RaspberryPi:
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
PWR_PIN = 18
def __init__(self):
import spidev
import RPi.GPIO
self.GPIO = RPi.GPIO
self.SPI = spidev.SpiDev()
def digital_write(self, pin, value):
self.GPIO.output(pin, value)
def digital_read(self, pin):
return self.GPIO.input(pin)
def delay_ms(self, delaytime):
time.sleep(delaytime / 1000.0)
def spi_writebyte(self, data):
self.SPI.writebytes(data)
def spi_writebyte2(self, data):
self.SPI.writebytes2(data)
def module_init(self):
self.GPIO.setmode(self.GPIO.BCM)
self.GPIO.setwarnings(False)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
self.GPIO.setup(self.PWR_PIN, self.GPIO.OUT)
self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
self.GPIO.output(self.PWR_PIN, 1)
# SPI device, bus = 0, device = 0
self.SPI.open(0, 0)
self.SPI.max_speed_hz = 4000000
self.SPI.mode = 0b00
return 0
def module_exit(self):
logger.debug("spi end")
self.SPI.close()
logger.debug("close 5V, Module enters 0 power consumption ...")
self.GPIO.output(self.RST_PIN, 0)
self.GPIO.output(self.DC_PIN, 0)
self.GPIO.output(self.PWR_PIN, 0)
self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN, self.PWR_PIN])
class JetsonNano:
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
PWR_PIN = 18
def __init__(self):
import ctypes
find_dirs = [
os.path.dirname(os.path.realpath(__file__)),
'/usr/local/lib',
'/usr/lib',
]
self.SPI = None
for find_dir in find_dirs:
so_filename = os.path.join(find_dir, 'sysfs_software_spi.so')
if os.path.exists(so_filename):
self.SPI = ctypes.cdll.LoadLibrary(so_filename)
break
if self.SPI is None:
raise RuntimeError('Cannot find sysfs_software_spi.so')
import Jetson.GPIO
self.GPIO = Jetson.GPIO
def digital_write(self, pin, value):
self.GPIO.output(pin, value)
def digital_read(self, pin):
return self.GPIO.input(self.BUSY_PIN)
def delay_ms(self, delaytime):
time.sleep(delaytime / 1000.0)
def spi_writebyte(self, data):
self.SPI.SYSFS_software_spi_transfer(data[0])
def spi_writebyte2(self, data):
for i in range(len(data)):
self.SPI.SYSFS_software_spi_transfer(data[i])
def module_init(self):
self.GPIO.setmode(self.GPIO.BCM)
self.GPIO.setwarnings(False)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
self.GPIO.setup(self.PWR_PIN, self.GPIO.OUT)
self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
self.GPIO.output(self.PWR_PIN, 1)
self.SPI.SYSFS_software_spi_begin()
return 0
def module_exit(self):
logger.debug("spi end")
self.SPI.SYSFS_software_spi_end()
logger.debug("close 5V, Module enters 0 power consumption ...")
self.GPIO.output(self.RST_PIN, 0)
self.GPIO.output(self.DC_PIN, 0)
self.GPIO.output(self.PWR_PIN, 0)
self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN, self.PWR_PIN])
class SunriseX3:
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
PWR_PIN = 18
Flag = 0
def __init__(self):
import spidev
import Hobot.GPIO
self.GPIO = Hobot.GPIO
self.SPI = spidev.SpiDev()
def digital_write(self, pin, value):
self.GPIO.output(pin, value)
def digital_read(self, pin):
return self.GPIO.input(pin)
def delay_ms(self, delaytime):
time.sleep(delaytime / 1000.0)
def spi_writebyte(self, data):
self.SPI.writebytes(data)
def spi_writebyte2(self, data):
# for i in range(len(data)):
# self.SPI.writebytes([data[i]])
self.SPI.xfer3(data)
def module_init(self):
if self.Flag == 0:
self.Flag = 1
self.GPIO.setmode(self.GPIO.BCM)
self.GPIO.setwarnings(False)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
self.GPIO.setup(self.PWR_PIN, self.GPIO.OUT)
self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
self.GPIO.output(self.PWR_PIN, 1)
# SPI device, bus = 0, device = 0
self.SPI.open(2, 0)
self.SPI.max_speed_hz = 4000000
self.SPI.mode = 0b00
return 0
else:
return 0
def module_exit(self):
logger.debug("spi end")
self.SPI.close()
logger.debug("close 5V, Module enters 0 power consumption ...")
self.Flag = 0
self.GPIO.output(self.RST_PIN, 0)
self.GPIO.output(self.DC_PIN, 0)
self.GPIO.output(self.PWR_PIN, 0)
self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN], self.PWR_PIN)
if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'):
implementation = RaspberryPi()
elif os.path.exists('/sys/bus/platform/drivers/gpio-x3'):
implementation = SunriseX3()
else:
implementation = RaspberryPi()
for func in [x for x in dir(implementation) if not x.startswith('_')]:
setattr(sys.modules[__name__], func, getattr(implementation, func))
### END OF FILE ###

View File

@ -0,0 +1,374 @@
import logging
from .. import epdconfig
# Display resolution
EPD_WIDTH = 296
EPD_HEIGHT = 128
logger = logging.getLogger(__name__)
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
lut_partial_update = [
0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
0x22, 0x17, 0x41, 0x00, 0x32, 0x36,
]
lut_full_update = [
0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xF, 0x0, 0x0, 0xF, 0x0, 0x0, 0x2,
0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
0x22, 0x17, 0x41, 0x0, 0x32, 0x36,
]
'''
function :Hardware reset
parameter:
'''
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(20)
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(2)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(20)
'''
function :send command
parameter:
command : Command register
'''
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, 1)
'''
function :send data
parameter:
data : Write data
'''
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, 1)
# send a lot of data
def send_data2(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte2(data)
epdconfig.digital_write(self.cs_pin, 1)
'''
function :Wait until the busy_pin goes LOW
parameter:
'''
def ReadBusy(self):
logger.debug("e-Paper busy")
while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy
epdconfig.delay_ms(10)
logger.debug("e-Paper busy release")
'''
function : Turn On Display
parameter:
'''
def TurnOnDisplay(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xC7)
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
'''
function : Turn On Display Part
parameter:
'''
def TurnOnDisplayPart(self):
self.send_command(0x22) # Display Update Control
self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
'''
function : Set lut
parameter:
lut : lut data
'''
def Lut(self, lut):
self.send_command(0x32)
for i in range(0, 153):
self.send_data(lut[i])
self.ReadBusy()
'''
function : Send lut data and configuration
parameter:
lut : lut data
'''
def SetLut(self, lut):
self.Lut(lut)
self.send_command(0x3f)
self.send_data(lut[153])
self.send_command(0x03) # gate voltage
self.send_data(lut[154])
self.send_command(0x04) # source voltage
self.send_data(lut[155]) # VSH
self.send_data(lut[156]) # VSH2
self.send_data(lut[157]) # VSL
self.send_command(0x2c) # VCOM
self.send_data(lut[158])
'''
function : Setting the display window
parameter:
xstart : X-axis starting position
ystart : Y-axis starting position
xend : End position of X-axis
yend : End position of Y-axis
'''
def SetWindow(self, x_start, y_start, x_end, y_end):
self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION
# x point must be the multiple of 8 or the last 3 bits will be ignored
self.send_data((x_start >> 3) & 0xFF)
self.send_data((x_end >> 3) & 0xFF)
self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION
self.send_data(y_start & 0xFF)
self.send_data((y_start >> 8) & 0xFF)
self.send_data(y_end & 0xFF)
self.send_data((y_end >> 8) & 0xFF)
'''
function : Set Cursor
parameter:
x : X-axis starting position
y : Y-axis starting position
'''
def SetCursor(self, x, y):
self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER
# x point must be the multiple of 8 or the last 3 bits will be ignored
self.send_data(x & 0xFF)
self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER
self.send_data(y & 0xFF)
self.send_data((y >> 8) & 0xFF)
'''
function : Initialize the e-Paper register
parameter:
'''
def init(self):
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()
self.ReadBusy()
self.send_command(0x12) # SWRESET
self.ReadBusy()
self.send_command(0x01) # Driver output control
self.send_data(0xf9)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0x11) # data entry mode
self.send_data(0x03)
self.SetWindow(0, 0, self.width - 1, self.height - 1)
self.SetCursor(0, 0)
self.send_command(0x3c)
self.send_data(0x05)
self.send_command(0x21) # Display update control
self.send_data(0x00)
self.send_data(0x80)
self.send_command(0x18)
self.send_data(0x80)
self.ReadBusy()
self.SetLut(self.lut_full_update)
return 0
'''
function : Display images
parameter:
image : Image data
'''
def getbuffer(self, image):
img = image
imwidth, imheight = img.size
if (imwidth == self.width and imheight == self.height):
img = img.convert('1')
elif (imwidth == self.height and imheight == self.width):
# image has correct dimensions, but needs to be rotated
img = img.rotate(90, expand=True).convert('1')
else:
logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height))
# return a blank buffer
return [0x00] * (int(self.width / 8) * self.height)
buf = bytearray(img.tobytes('raw'))
return buf
'''
function : Sends the image buffer in RAM to e-Paper and displays
parameter:
image : Image data
'''
def display(self, image):
if self.width % 8 == 0:
linewidth = int(self.width / 8)
else:
linewidth = int(self.width / 8) + 1
self.send_command(0x24)
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(image[i + j * linewidth])
self.TurnOnDisplay()
'''
function : Sends the image buffer in RAM to e-Paper and partial refresh
parameter:
image : Image data
'''
def displayPartial(self, image):
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(1)
epdconfig.digital_write(self.reset_pin, 1)
self.SetLut(self.lut_partial_update)
self.send_command(0x37)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x40)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0x3C) # BorderWavefrom
self.send_data(0x80)
self.send_command(0x22)
self.send_data(0xC0)
self.send_command(0x20)
self.ReadBusy()
self.SetWindow(0, 0, self.width - 1, self.height - 1)
self.SetCursor(0, 0)
self.send_command(0x24) # WRITE_RAM
# for j in range(0, self.height):
# for i in range(0, linewidth):
# self.send_data(image[i + j * linewidth])
self.send_data2(image)
self.TurnOnDisplayPart()
'''
function : Refresh a base image
parameter:
image : Image data
'''
def displayPartBaseImage(self, image):
self.send_command(0x24)
self.send_data2(image)
self.send_command(0x26)
self.send_data2(image)
self.TurnOnDisplay()
'''
function : Clear screen
parameter:
'''
def Clear(self, color=0xFF):
if self.width % 8 == 0:
linewidth = int(self.width / 8)
else:
linewidth = int(self.width / 8) + 1
# logger.debug(linewidth)
self.send_command(0x24)
self.send_data2([color] * int(self.height * linewidth))
self.TurnOnDisplay()
'''
function : Enter sleep mode
parameter:
'''
def sleep(self):
self.send_command(0x10) # enter deep sleep
self.send_data(0x01)
epdconfig.delay_ms(2000)
epdconfig.module_exit()
### END OF FILE ###

View File

@ -38,7 +38,8 @@ class Waveshare13in3k(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare1in02(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -34,12 +34,12 @@ class Waveshare154(DisplayImpl):
logging.info("initializing waveshare v1in54 display")
from pwnagotchi.ui.hw.libs.waveshare.v1in54.epd1in54 import EPD
self._display = EPD()
self._display.init(0x00)
self._display.init(self._display.lut_partial_update)
self._display.Clear()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
self._display.display(buf)
def clear(self):
# pass

View File

@ -31,15 +31,27 @@ class Waveshare154V2(DisplayImpl):
return self._layout
def initialize(self):
logging.info("initializing waveshare v1in54_v2 display")
logging.info("initializing waveshare1in54_v2 display")
from pwnagotchi.ui.hw.libs.waveshare.v1in54_v2.epd1in54_V2 import EPD
self._display = EPD()
self._display.init(False)
self._display.Clear()
try:
# Double initialization is a workaround for the display not working after a reboot, or mirrored/flipped screen
self._display = EPD()
self._display.init(0)
self.clear()
self._display.init(1)
self.clear()
except Exception as e:
logging.error(f"failed to initialize waveshare1in54_v2 display: {e}")
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
try:
buf = self._display.getbuffer(canvas)
self._display.displayPart(buf)
except Exception as e:
logging.error(f"failed to render to waveshare1in54_v2 display: {e}")
def clear(self):
self._display.Clear()
try:
self._display.Clear(0xFF)
except Exception as e:
logging.error(f"failed to clear waveshare1in54_v2 display: {e}")

View File

@ -38,7 +38,8 @@ class Waveshare1in54c(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare1in64g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -9,81 +9,38 @@ class WaveshareV1(DisplayImpl):
super(WaveshareV1, self).__init__(config, 'waveshare_1')
def layout(self):
if self.config['color'] == 'black':
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
else:
fonts.setup(10, 8, 10, 25, 25, 9)
self._layout['width'] = 212
self._layout['height'] = 104
self._layout['face'] = (0, 26)
self._layout['name'] = (5, 15)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (147, 0)
self._layout['line1'] = [0, 12, 212, 12]
self._layout['line2'] = [0, 92, 212, 92]
self._layout['friend_face'] = (0, 76)
self._layout['friend_name'] = (40, 78)
self._layout['shakes'] = (0, 93)
self._layout['mode'] = (187, 93)
self._layout['status'] = {
'pos': (91, 15),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
fonts.setup(10, 8, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):
if self.config['color'] == 'black':
logging.info("initializing waveshare v2in13_V1 display in monochromatic mode")
from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13 import EPD
self._display = EPD()
self._display.init(self._display.lut_full_update)
self._display.Clear(0xFF)
self._display.init(self._display.lut_partial_update)
elif self.config['color'] == 'fastAndFurious':
logging.info("initializing waveshare v2in13_V1 3-color display in FAST MODE")
logging.info("THIS MAY BE POTENTIALLY DANGEROUS. NO WARRANTY IS PROVIDED")
logging.info("USE THIS DISPLAY IN THIS MODE AT YOUR OWN RISK")
from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13bcFAST import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
else:
logging.info("initializing waveshare v2in13_V1 display 3-color mode")
from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13bc import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
logging.info("initializing waveshare v2in13_V1 display in monochromatic mode")
from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13 import EPD
self._display = EPD()
self._display.init(self._display.lut_full_update)
self._display.Clear(0xFF)
self._display.init(self._display.lut_partial_update)
def render(self, canvas):
if self.config['color'] == 'black':
buf = self._display.getbuffer(canvas)
self._display.display(buf)
elif self.config['color'] == 'fastAndFurious':
buf_black = self._display.getbuffer(canvas)
self._display.DisplayPartial(buf_black)
else:
buf_black = self._display.getbuffer(canvas)
self._display.displayBlack(buf_black)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear(0xff)

View File

@ -9,47 +9,25 @@ class WaveshareV2(DisplayImpl):
super(WaveshareV2, self).__init__(config, 'waveshare_2')
def layout(self):
if self.config['color'] == 'black':
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
else:
fonts.setup(10, 8, 10, 25, 25, 9)
self._layout['width'] = 212
self._layout['height'] = 104
self._layout['face'] = (0, 26)
self._layout['name'] = (5, 15)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['status'] = (91, 15)
self._layout['uptime'] = (147, 0)
self._layout['line1'] = [0, 12, 212, 12]
self._layout['line2'] = [0, 92, 212, 92]
self._layout['friend_face'] = (0, 76)
self._layout['friend_name'] = (40, 78)
self._layout['shakes'] = (0, 93)
self._layout['mode'] = (187, 93)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 14
}
fonts.setup(10, 8, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):

View File

@ -10,47 +10,25 @@ class Waveshare2in13bV3(DisplayImpl):
super(Waveshare2in13bV3, self).__init__(config, 'waveshare2in13b_v3')
def layout(self):
if self.config['color'] == 'black':
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
else:
fonts.setup(10, 8, 10, 25, 25, 9)
self._layout['width'] = 212
self._layout['height'] = 104
self._layout['face'] = (0, 26)
self._layout['name'] = (5, 15)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['status'] = (91, 15)
self._layout['uptime'] = (147, 0)
self._layout['line1'] = [0, 12, 212, 12]
self._layout['line2'] = [0, 92, 212, 92]
self._layout['friend_face'] = (0, 76)
self._layout['friend_name'] = (40, 78)
self._layout['shakes'] = (0, 93)
self._layout['mode'] = (187, 93)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 14
}
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):

View File

@ -10,47 +10,25 @@ class Waveshare213bV4(DisplayImpl):
super(Waveshare213bV4, self).__init__(config, 'waveshare2in13b_v4')
def layout(self):
if self.config['color'] == 'black':
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
else:
fonts.setup(10, 8, 10, 25, 25, 9)
self._layout['width'] = 212
self._layout['height'] = 104
self._layout['face'] = (0, 26)
self._layout['name'] = (5, 15)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['status'] = (91, 15)
self._layout['uptime'] = (147, 0)
self._layout['line1'] = [0, 12, 212, 12]
self._layout['line2'] = [0, 92, 212, 92]
self._layout['friend_face'] = (0, 76)
self._layout['friend_name'] = (40, 78)
self._layout['shakes'] = (0, 93)
self._layout['mode'] = (187, 93)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 14
}
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):

View File

View File

@ -38,7 +38,8 @@ class Waveshare2in36g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -34,11 +34,12 @@ class Waveshare2in66(DisplayImpl):
logging.info("initializing waveshare 2.66 inch lcd display")
from pwnagotchi.ui.hw.libs.waveshare.v2in66.epd2in66 import EPD
self._display = EPD()
self._display.init(0)
self._display.init(1)
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare2in66g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -35,8 +35,6 @@ class Waveshare27inchV2(DisplayImpl):
from pwnagotchi.ui.hw.libs.waveshare.v2in7_v2.epd2in7_V2 import EPD
self._display = EPD()
self._display.init()
# this must have changed by waveshare
# remove the 0xFF(Clear(0xFF)) other wise it errors. can't pass oxff and self
self._display.Clear()
def render(self, canvas):

View File

@ -39,7 +39,7 @@ class Waveshare27b(DisplayImpl):
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
self._display.display(buf, None)
def clear(self):
self._display.Clear(0xff)

View File

@ -39,7 +39,7 @@ class Waveshare27bV2(DisplayImpl):
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
self._display.display(buf, None)
def clear(self):
self._display.Clear(0xff)

View File

@ -35,12 +35,12 @@ class Waveshare29bV3(DisplayImpl):
from pwnagotchi.ui.hw.libs.waveshare.v2in9b_v3.epd2in9b_V3 import EPD
self._display = EPD()
self._display.init()
self._display.Clear(0xFF)
self._display.Clear()
self._display.init()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
self._display.display(buf, None)
def clear(self):
self._display.Clear(0xFF)
self._display.Clear()

View File

@ -35,12 +35,12 @@ class Waveshare29bV4(DisplayImpl):
from pwnagotchi.ui.hw.libs.waveshare.v2in9b_v4.epd2in9b_V4 import EPD
self._display = EPD()
self._display.init()
self._display.Clear(0xFF)
self._display.Clear()
self._display.init()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf)
self._display.display(buf, None)
def clear(self):
self._display.Clear(0xFF)
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare2in9bc(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare2in9d(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare3in0g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -9,22 +9,22 @@ class Waveshare3in52(DisplayImpl):
super(Waveshare3in52, self).__init__(config, 'waveshare3in52')
def layout(self):
fonts.setup(16, 14, 16, 100, 31, 15)
fonts.setup(16, 14, 16, 50, 31, 15)
self._layout['width'] = 360
self._layout['height'] = 240
self._layout['face'] = (0, 40)
self._layout['name'] = (0, 0)
self._layout['channel'] = (300, 0)
self._layout['aps'] = (0, 220)
self._layout['uptime'] = (120, 0)
self._layout['face'] = (0, 85)
self._layout['name'] = (10, 30)
self._layout['channel'] = (1, 4)
self._layout['aps'] = (45, 4)
self._layout['uptime'] = (250, 4)
self._layout['line1'] = [0, 24, 360, 24]
self._layout['line2'] = [0, 220, 360, 220]
self._layout['friend_face'] = (0, 195)
self._layout['friend_name'] = (0, 185)
self._layout['shakes'] = (100, 220)
self._layout['mode'] = (0,200)
self._layout['friend_face'] = (0, 180)
self._layout['friend_name'] = (0, 170)
self._layout['shakes'] = (1, 223)
self._layout['mode'] = (320, 222)
self._layout['status'] = {
'pos': (3, 170),
'pos': (185, 50),
'font': fonts.status_font(fonts.Small),
'max': 100
}

View File

@ -36,9 +36,11 @@ class Waveshare3in7(DisplayImpl):
self._display = EPD()
self._display.init(0)
self._display.Clear(0)
self._display.init(1) # 1Gray mode
def render(self, canvas):
self._display.display_4Gray(canvas)
buf = self._display.getbuffer(canvas)
self._display.display_1Gray(buf)
def clear(self):
self._display.Clear(0)

View File

@ -38,7 +38,8 @@ class Waveshare4in01f(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare4in2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare4in26(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare4in2V2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare4in2bV2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare4in2bc(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare4in37g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare5in65f(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare5in83(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare5in83V2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare5in83bV2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare5in83bc(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare7in3f(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare7in3g(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare7in5(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare7in5HD(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -51,7 +51,8 @@ class Waveshare7in5V2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare7in5bHD(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare7in5bV2(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -38,7 +38,8 @@ class Waveshare7in5bc(DisplayImpl):
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -0,0 +1,47 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class WeAct2in9(DisplayImpl):
def __init__(self, config):
super(WeAct2in9, self).__init__(config, 'weact2in9')
def layout(self):
fonts.setup(10, 8, 10, 35, 25, 9)
self._layout['width'] = 296
self._layout['height'] = 128
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (185, 0)
self._layout['line1'] = [0, 14, 250, 14]
self._layout['line2'] = [0, 108, 250, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (225, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):
logging.info("initializing WeAct 2.9 inch display")
from pwnagotchi.ui.hw.libs.weact.v2in9.epd2in9 import EPD
self._display = EPD()
self._display.init()
self._display.Clear(0xFF)
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.displayPartial(buf)
def clear(self):
#pass
self._display.Clear(0xFF)

View File

@ -23,7 +23,21 @@ ROOT = None
class View(object):
def __init__(self, config, impl, state=None):
global ROOT
global ROOT, BLACK, WHITE
self.invert = 0
self._black = 0xFF
self._white = 0x00
if 'invert' in config['ui'] and config['ui']['invert'] == True:
logging.debug("INVERT BLACK/WHITES:" + str(config['ui']['invert']))
self.invert = 1
BLACK = 0x00
WHITE - 0xFF
self._black = 0x00
self._white = 0xFF
# setup faces from the configuration in case the user customized them
faces.load_from_config(config['ui']['faces'])
@ -54,10 +68,10 @@ class View(object):
'line1': Line(self._layout['line1'], color=BLACK),
'line2': Line(self._layout['line2'], color=BLACK),
'face': Text(value=faces.SLEEP, position=self._layout['face'], color=BLACK, font=fonts.Huge),
'face': Text(value=faces.SLEEP, position=(config['ui']['faces']['position_x'], config['ui']['faces']['position_y']), color=BLACK, font=fonts.Huge, png=config['ui']['faces']['png']),
# 'friend_face': Text(value=None, position=self._layout['friend_face'], font=fonts.Bold, color=BLACK),
'friend_name': Text(value=None, position=self._layout['friend_name'], font=fonts.BoldSmall, color=BLACK),
'friend_name': Text(value=None, position=self._layout['friend_face'], font=fonts.BoldSmall, color=BLACK),
'name': Text(value='%s>' % 'pwnagotchi', position=self._layout['name'], color=BLACK, font=fonts.Bold),
@ -98,6 +112,11 @@ class View(object):
self._state.has_element(key)
def add_element(self, key, elem):
if self.invert is 1 and elem.color:
if elem.color == 0xff:
elem.color = 0x00
elif elem.color == 0x00:
elem.color = 0xff
self._state.add_element(key, elem)
def remove_element(self, key):
@ -371,7 +390,7 @@ class View(object):
state = self._state
changes = state.changes(ignore=self._ignore_changes)
if force or len(changes):
self._canvas = Image.new('1', (self._width, self._height), WHITE)
self._canvas = Image.new('1', (self._width, self._height), self._white)
drawer = ImageDraw.Draw(self._canvas)
plugins.on('ui_update', self)

View File

@ -238,14 +238,60 @@ def load_config(args):
config = merge_config(additional_config, config)
# the very first step is to normalize the display name, so we don't need dozens of if/elif around
# NON E-INK DISPLAYS---------------------------------------------------------------
if config['ui']['display']['type'] in ('inky', 'inkyphat'):
config['ui']['display']['type'] = 'inky'
elif config['ui']['display']['type'] in ('papirus', 'papi'):
config['ui']['display']['type'] = 'papirus'
elif config['ui']['display']['type'] in 'oledhat':
config['ui']['display']['type'] = 'oledhat'
elif config['ui']['display']['type'] in 'lcdhat':
config['ui']['display']['type'] = 'lcdhat'
elif config['ui']['display']['type'] in ('dfrobot_1', 'df1'):
config['ui']['display']['type'] = 'dfrobot_1'
elif config['ui']['display']['type'] in ('dfrobot_2', 'df2'):
config['ui']['display']['type'] = 'dfrobot_2'
elif config['ui']['display']['type'] in ('waveshare144lcd', 'ws_144', 'ws144', 'waveshare_144', 'waveshare144'):
config['ui']['display']['type'] = 'waveshare144lcd'
elif config['ui']['display']['type'] in ('spotpear24inch'):
config['ui']['display']['type'] = 'spotpear24inch'
elif config['ui']['display']['type'] in ('displayhatmini'):
config['ui']['display']['type'] = 'displayhatmini'
elif config['ui']['display']['type'] in ('waveshare35lcd'):
config['ui']['display']['type'] = 'waveshare35lcd'
# E-INK DISPLAYS ------------------------------------------------------------------------
elif config['ui']['display']['type'] in ('waveshare1in02', 'ws1in02', 'ws102', 'waveshare_102', 'waveshare_1in02'):
config['ui']['display']['type'] = 'waveshare1in02'
elif config['ui']['display']['type'] in ('ws_154inch', 'waveshare1in54', 'ws154inch', 'waveshare_154', 'waveshare154'):
config['ui']['display']['type'] = 'waveshare1in54'
elif config['ui']['display']['type'] in ('ws_154inchb', 'waveshare1in54b', 'ws154inchb', 'waveshare_154b', 'waveshare154b'):
config['ui']['display']['type'] = 'waveshare1in54b'
elif config['ui']['display']['type'] in ('waveshare1in54c', 'ws1in54c', 'ws154c', 'waveshare_154c', 'waveshare_1in54c'):
config['ui']['display']['type'] = 'waveshare1in54c'
elif config['ui']['display']['type'] in ('ws_154inchbv2', 'waveshare1in54bv2', 'waveshare1in54b_v2', 'ws154inchbv2', 'waveshare_154bv2', 'waveshare154bv2'):
config['ui']['display']['type'] = 'waveshare1in54b_v2'
elif config['ui']['display']['type'] in ('ws_154inchv2', 'waveshare1in54v2', 'ws154inchv2', 'waveshare_154inchv2', 'waveshare154v2', "waveshare1in54_v2"):
config['ui']['display']['type'] = 'waveshare1in54_v2'
elif config['ui']['display']['type'] in ('waveshare1in64g', 'ws1in64g', 'ws164g', 'waveshare_164g', 'waveshare_1in64g'):
config['ui']['display']['type'] = 'waveshare1in64g'
elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1'):
config['ui']['display']['type'] = 'waveshare_1'
@ -258,50 +304,14 @@ def load_config(args):
elif config['ui']['display']['type'] in ('ws_4', 'ws4', 'waveshare_4', 'waveshare4'):
config['ui']['display']['type'] = 'waveshare_4'
elif config['ui']['display']['type'] in ('ws_27inch', 'ws27inch', 'waveshare2in7', 'waveshare_27inch', 'waveshare27inch'):
config['ui']['display']['type'] = 'waveshare2in7'
elif config['ui']['display']['type'] in ('waveshare2in13b_v3', 'waveshare2in13b_v3', 'ws213bv3', 'waveshare_213bv3', 'waveshare213inb_v3'):
config['ui']['display']['type'] = 'waveshare2in13b_v3'
elif config['ui']['display']['type'] in ('ws_27inchv2', 'waveshare2in7_v2', 'ws27inchv2', 'waveshare_27inchv2', 'waveshare27inchv2'):
config['ui']['display']['type'] = 'waveshare2in7_v2'
elif config['ui']['display']['type'] in ('ws_213bv4', 'waveshare2in13b_v4', 'ws213bv4', 'waveshare_213bv4', 'waveshare213inb_v4'):
config['ui']['display']['type'] = 'waveshare2in13b_v4'
elif config['ui']['display']['type'] in ('ws_27inchbv2', 'waveshare2in7b_v2', 'ws27inchbv2', 'waveshare_27inchbv2', 'waveshare27inchbv2'):
config['ui']['display']['type'] = 'waveshare2in7b_v2'
elif config['ui']['display']['type'] in ('ws_29inch', 'waveshare2in9', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'):
config['ui']['display']['type'] = 'waveshare2in9'
elif config['ui']['display']['type'] in ('ws_29inchv2', 'waveshare2in9_v2', 'ws29inchv2', 'waveshare_29inchv2', 'waveshare29inchv2'):
config['ui']['display']['type'] = 'waveshare2in9_v2'
elif config['ui']['display']['type'] in ('ws_29inchbv3', 'waveshare2in9b_v3', 'ws29inchbv3', 'waveshare_29inchbv3', 'waveshare29inchbv3'):
config['ui']['display']['type'] = 'waveshare2in9b_v3'
elif config['ui']['display']['type'] in ('ws_29inchbv4', 'waveshare2in9b_v4', 'ws29inchbv4', 'waveshare_29inchbv4', 'waveshare29inchbv4'):
config['ui']['display']['type'] = 'waveshare2in9b_v4'
elif config['ui']['display']['type'] in 'lcdhat':
config['ui']['display']['type'] = 'lcdhat'
elif config['ui']['display']['type'] in ('dfrobot_1', 'df1'):
config['ui']['display']['type'] = 'dfrobot_1'
elif config['ui']['display']['type'] in ('dfrobot_2', 'df2'):
config['ui']['display']['type'] = 'dfrobot_2'
elif config['ui']['display']['type'] in ('ws_154inch', 'waveshare1in54', 'ws154inch', 'waveshare_154inch', 'waveshare154inch'):
config['ui']['display']['type'] = 'waveshare1in54'
elif config['ui']['display']['type'] in ('ws_154inchb', 'waveshare1in54b', 'ws154inchb', 'waveshare_154inchb', 'waveshare154inchb'):
config['ui']['display']['type'] = 'waveshare1in54b'
elif config['ui']['display']['type'] in ('ws_154inchbv2', 'waveshare1in54bv2', 'ws154inchbv2', 'waveshare_154inchbv2', 'waveshare154inchbv2'):
config['ui']['display']['type'] = 'waveshare1in54b_v2'
elif config['ui']['display']['type'] in ('ws_154inchv2', 'waveshare1in54v2', 'ws154inchv2', 'waveshare_154inchv2', 'waveshare154inchv2'):
config['ui']['display']['type'] = 'waveshare1in54_v2'
elif config['ui']['display']['type'] in ('waveshare144lcd', 'ws_144inch', 'ws144inch', 'waveshare_144inch', 'waveshare144inch'):
config['ui']['display']['type'] = 'waveshare144lcd'
elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare2in13bc', 'waveshare_213bc', 'waveshare213bc'):
config['ui']['display']['type'] = 'waveshare2in13bc'
elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare2in13d', 'waveshare_213d', 'waveshare213d'):
config['ui']['display']['type'] = 'waveshare2in13d'
@ -309,123 +319,121 @@ def load_config(args):
elif config['ui']['display']['type'] in ('ws_213g', 'waveshare2in13g', 'waveshare213g', 'ws213g', 'waveshare_213g'):
config['ui']['display']['type'] = 'waveshare2in13g'
elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare2in13bc', 'waveshare_213bc', 'waveshare213bc'):
config['ui']['display']['type'] = 'waveshare2in13bc'
elif config['ui']['display']['type'] in ('ws_213bv4', 'waveshare2in13b_v4', 'ws213bv4', 'waveshare_213bv4', 'waveshare213inb_v4'):
config['ui']['display']['type'] = 'waveshare2in13b_v4'
elif config['ui']['display']['type'] in 'spotpear24inch':
config['ui']['display']['type'] = 'spotpear24inch'
elif config['ui']['display']['type'] in 'displayhatmini':
config['ui']['display']['type'] = 'displayhatmini'
elif config['ui']['display']['type'] in 'waveshare35lcd':
config['ui']['display']['type'] = 'waveshare35lcd'
elif config['ui']['display']['type'] in 'waveshare1in54c':
config['ui']['display']['type'] = 'waveshare1in54c'
elif config['ui']['display']['type'] in 'waveshare1in64g':
config['ui']['display']['type'] = 'waveshare1in64g'
elif config['ui']['display']['type'] in 'waveshare1in02':
config['ui']['display']['type'] = 'waveshare1in02'
elif config['ui']['display']['type'] in 'waveshare2in9bc':
config['ui']['display']['type'] = 'waveshare2in9bc'
elif config['ui']['display']['type'] in 'waveshare2in9d':
config['ui']['display']['type'] = 'waveshare2in9d'
elif config['ui']['display']['type'] in 'waveshare2in13b_v3':
config['ui']['display']['type'] = 'waveshare2in13b_v3'
elif config['ui']['display']['type'] in 'waveshare2in36g':
elif config['ui']['display']['type'] in ('ws_2in36g', 'waveshare2in36g', 'waveshare236g', 'ws236g', 'waveshare_236g'):
config['ui']['display']['type'] = 'waveshare2in36g'
elif config['ui']['display']['type'] in 'waveshare2in66':
elif config['ui']['display']['type'] in ('ws_2in66', 'waveshare2in66', 'waveshare266', 'ws266', 'waveshare_266'):
config['ui']['display']['type'] = 'waveshare2in66'
elif config['ui']['display']['type'] in 'waveshare2in66b':
elif config['ui']['display']['type'] in ('ws_2in66b', 'waveshare2in66b', 'waveshare266b', 'ws266b', 'waveshare_266b'):
config['ui']['display']['type'] = 'waveshare2in66b'
elif config['ui']['display']['type'] in 'waveshare2in66g':
elif config['ui']['display']['type'] in ('ws_2in66g', 'waveshare2in66g', 'waveshare266g', 'ws266g', 'waveshare_266g'):
config['ui']['display']['type'] = 'waveshare2in66g'
elif config['ui']['display']['type'] in 'waveshare3in0g':
elif config['ui']['display']['type'] in ('ws_27inch', 'ws27inch', 'waveshare2in7', 'waveshare_27inch', 'waveshare27'):
config['ui']['display']['type'] = 'waveshare2in7'
elif config['ui']['display']['type'] in ('ws_2in7v2', 'waveshare2in7_v2', 'waveshare2in7v2', 'ws27inchv2', 'waveshare_27v2', 'waveshare27v2'):
config['ui']['display']['type'] = 'waveshare2in7_v2'
elif config['ui']['display']['type'] in ('ws_2in7bv2', 'waveshare2in7b_v2', 'waveshare2in7bv2', 'ws27inchbv2', 'waveshare_27bv2', 'waveshare27bv2'):
config['ui']['display']['type'] = 'waveshare2in7b_v2'
elif config['ui']['display']['type'] in ('ws_2in9', 'waveshare2in9', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'):
config['ui']['display']['type'] = 'waveshare2in9'
elif config['ui']['display']['type'] in ('ws_2in9bc', 'waveshare2in9bc', 'ws2in9bc', 'ws29bc', 'waveshare_29bc', 'waveshare_2in9bc'):
config['ui']['display']['type'] = 'waveshare2in9bc'
elif config['ui']['display']['type'] in ('ws_2in9d', 'waveshare2in9d', 'ws2in9d', 'ws29d', 'waveshare_29d', 'waveshare_2in9d'):
config['ui']['display']['type'] = 'waveshare2in9d'
elif config['ui']['display']['type'] in ('ws_2in9v2', 'waveshare2in9_v2', 'waveshare2in9v2', 'ws2in9v2', 'waveshare_29v2', 'waveshare29v2'):
config['ui']['display']['type'] = 'waveshare2in9_v2'
elif config['ui']['display']['type'] in ('ws_2in9bv3', 'waveshare2in9b_v3', 'waveshare2in9bv3', 'ws2in9bv3', 'waveshare_29bv3', 'waveshare29bv3'):
config['ui']['display']['type'] = 'waveshare2in9b_v3'
elif config['ui']['display']['type'] in ('ws_2in9bv4', 'waveshare2in9b_v4', 'waveshare2in9bv4', 'ws2in9bv4', 'waveshare_29bv4', 'waveshare29bv4'):
config['ui']['display']['type'] = 'waveshare2in9b_v4'
elif config['ui']['display']['type'] in ('ws_3in0g', 'waveshare3in0g', 'ws3in0g', 'waveshare_30g', 'waveshare30g'):
config['ui']['display']['type'] = 'waveshare3in0g'
elif config['ui']['display']['type'] in 'waveshare3in7':
elif config['ui']['display']['type'] in ('ws_3in7', 'waveshare3in7', 'ws3in7', 'waveshare_37', 'waveshare37'):
config['ui']['display']['type'] = 'waveshare3in7'
elif config['ui']['display']['type'] in 'waveshare3in52':
elif config['ui']['display']['type'] in ('ws_3in52', 'waveshare3in52', 'ws3in52', 'waveshare_352', 'waveshare352'):
config['ui']['display']['type'] = 'waveshare3in52'
elif config['ui']['display']['type'] in 'waveshare4in01f':
elif config['ui']['display']['type'] in ('ws_4in01f', 'waveshare4in01f', 'ws4in01f', 'waveshare_401f', 'waveshare401f'):
config['ui']['display']['type'] = 'waveshare4in01f'
elif config['ui']['display']['type'] in 'waveshare4in2':
elif config['ui']['display']['type'] in ('ws_4in2', 'waveshare4in2', 'ws4in2', 'waveshare_42', 'waveshare42'):
config['ui']['display']['type'] = 'waveshare4in2'
elif config['ui']['display']['type'] in 'waveshare4in2_v2':
elif config['ui']['display']['type'] in ('ws_4in2v2', 'waveshare4in2v2', 'ws4in2v2', 'waveshare_42v2', 'waveshare42v2'):
config['ui']['display']['type'] = 'waveshare4in2_v2'
elif config['ui']['display']['type'] in 'waveshare4in2b_v2':
elif config['ui']['display']['type'] in ('ws_4in2bv2', 'waveshare4in2bv2', 'ws4in2bv2', 'waveshare_42bv2', 'waveshare42bv2'):
config['ui']['display']['type'] = 'waveshare4in2b_v2'
elif config['ui']['display']['type'] in 'waveshare4in2bc':
elif config['ui']['display']['type'] in ('ws_4in2bc', 'waveshare4in2bc', 'ws4in2bc', 'waveshare_42bc', 'waveshare42bc'):
config['ui']['display']['type'] = 'waveshare4in2bc'
elif config['ui']['display']['type'] in 'waveshare4in26':
elif config['ui']['display']['type'] in ('ws_4in26', 'waveshare4in26', 'ws4in26', 'waveshare_426', 'waveshare426'):
config['ui']['display']['type'] = 'waveshare4in26'
elif config['ui']['display']['type'] in 'waveshare4in37g':
elif config['ui']['display']['type'] in ('ws_4in37g', 'waveshare4in37g', 'ws4in37g', 'waveshare_37g', 'waveshare437g'):
config['ui']['display']['type'] = 'waveshare4in37g'
elif config['ui']['display']['type'] in 'waveshare5in65f':
elif config['ui']['display']['type'] in ('ws_5in65f', 'waveshare5in65f', 'ws5in65f', 'waveshare_565f', 'waveshare565f'):
config['ui']['display']['type'] = 'waveshare5in65f'
elif config['ui']['display']['type'] in 'waveshare5in83':
elif config['ui']['display']['type'] in ('ws_5in83', 'waveshare5in83', 'ws5in83', 'waveshare_583', 'waveshare583'):
config['ui']['display']['type'] = 'waveshare5in83'
elif config['ui']['display']['type'] in 'waveshare5in83_v2':
elif config['ui']['display']['type'] in ('ws_5in83v2', 'waveshare5in83v2', 'ws5in83v2', 'waveshare_583v2', 'waveshare583v2'):
config['ui']['display']['type'] = 'waveshare5in83_v2'
elif config['ui']['display']['type'] in 'waveshare5in83b_v2':
elif config['ui']['display']['type'] in ('ws_5in83bv2', 'waveshare5in83bv2', 'ws5in83bv2', 'waveshare_583bv2', 'waveshare583bv2'):
config['ui']['display']['type'] = 'waveshare5in83b_v2'
elif config['ui']['display']['type'] in 'waveshare5in83bc':
elif config['ui']['display']['type'] in ('ws_5in83bc', 'waveshare5in83bc', 'ws5in83bc', 'waveshare_583bc', 'waveshare583bc'):
config['ui']['display']['type'] = 'waveshare5in83bc'
elif config['ui']['display']['type'] in 'waveshare7in3f':
elif config['ui']['display']['type'] in ('ws_7in3f', 'waveshare7in3f', 'ws7in3f', 'waveshare_73f', 'waveshare73f'):
config['ui']['display']['type'] = 'waveshare7in3f'
elif config['ui']['display']['type'] in 'waveshare7in3g':
elif config['ui']['display']['type'] in ('ws_7in3g', 'waveshare7in3g', 'ws7in3g', 'waveshare_73g', 'waveshare73g'):
config['ui']['display']['type'] = 'waveshare7in3g'
elif config['ui']['display']['type'] in 'waveshare7in5':
elif config['ui']['display']['type'] in ('ws_7in5', 'waveshare7in5', 'ws7in5', 'waveshare_75', 'waveshare75'):
config['ui']['display']['type'] = 'waveshare7in5'
elif config['ui']['display']['type'] in 'waveshare7in5_HD':
elif config['ui']['display']['type'] in ('ws_7in5hd', 'waveshare7in5hd', 'ws7in5hd', 'waveshare_75hd', 'waveshare75hd'):
config['ui']['display']['type'] = 'waveshare7in5_HD'
elif config['ui']['display']['type'] in 'waveshare7in5_v2':
elif config['ui']['display']['type'] in ('ws_7in5v2', 'waveshare7in5v2', 'ws7in5v2', 'waveshare_75v2', 'waveshare75v2'):
config['ui']['display']['type'] = 'waveshare7in5_v2'
elif config['ui']['display']['type'] in 'waveshare7in5b_HD':
elif config['ui']['display']['type'] in ('ws_7in5bhd', 'waveshare7in5bhd', 'ws7in5bhd', 'waveshare_75bhd', 'waveshare75bhd'):
config['ui']['display']['type'] = 'waveshare7in5b_HD'
elif config['ui']['display']['type'] in 'waveshare7in5b_v2':
elif config['ui']['display']['type'] in ('ws_7in5bv2', 'waveshare7in5bv2', 'ws7in5bv2', 'waveshare_75bv2', 'waveshare75bv2'):
config['ui']['display']['type'] = 'waveshare7in5b_v2'
elif config['ui']['display']['type'] in 'waveshare7in5bc':
elif config['ui']['display']['type'] in ('ws_7in5bc', 'waveshare7in5bc', 'ws7in5bc', 'waveshare_75bc', 'waveshare75bc'):
config['ui']['display']['type'] = 'waveshare7in5bc'
elif config['ui']['display']['type'] in 'waveshare13in3k':
elif config['ui']['display']['type'] in ('ws_13in3k', 'waveshare13in3k', 'ws13in3k', 'waveshare_133k', 'waveshare133k'):
config['ui']['display']['type'] = 'waveshare13in3k'
# WeAct e-ink
elif config['ui']['display']['type'] in ('weact2in9', 'weact29in'):
config['ui']['display']['type'] = 'weact2in9'
else:
print("unsupported display type %s" % config['ui']['display']['type'])
sys.exit(1)