commit
a3cf49244e
29
builder/data/etc/bash_completion.d/pwnagotchi_completion.sh
Normal file
29
builder/data/etc/bash_completion.d/pwnagotchi_completion.sh
Normal file
@ -0,0 +1,29 @@
|
||||
_show_complete()
|
||||
{
|
||||
local cur opts node_names all_options opt_line
|
||||
all_options="
|
||||
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config {plugins}
|
||||
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
|
||||
pwnagotchi plugins list -i --installed -h --help
|
||||
pwnagotchi plugins install -h --help
|
||||
pwnagotchi plugins uninstall -h --help
|
||||
pwnagotchi plugins enable -h --help
|
||||
pwnagotchi plugins disable -h --help
|
||||
pwnagotchi plugins update -h --help
|
||||
pwnagotchi plugins upgrade -h --help
|
||||
"
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
cmd="${COMP_WORDS[@]:0:${#COMP_WORDS[@]}-1}"
|
||||
opt_line="$(grep -m1 "$cmd" <<<$all_options)"
|
||||
if [[ ${cur} == -* ]] ; then
|
||||
opts="$(echo $opt_line | tr ' ' '\n' | awk '/^ *-/{gsub("[^a-zA-Z0-9-]","",$1);print $1}')"
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')"
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
}
|
||||
|
||||
complete -F _show_complete pwnagotchi
|
@ -9,6 +9,15 @@ if is_crypted_mode; then
|
||||
done
|
||||
fi
|
||||
|
||||
# check if wifi driver is bugged
|
||||
if ! check_brcm; then
|
||||
if ! reload_brcm; then
|
||||
echo "Could not reload wifi driver. Reboot"
|
||||
reboot
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
|
||||
# start mon0
|
||||
start_monitor_interface
|
||||
|
||||
|
@ -88,6 +88,27 @@ POST_RESPONSE = """
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
|
||||
function checkPwnagotchi() {
|
||||
var target = 'http://' + document.location.hostname + ':8080/';
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', target);
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200 || xhr.status == 401) {
|
||||
window.location.replace(target);
|
||||
}else{
|
||||
setTimeout(checkPwnagotchi, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
setTimeout(checkPwnagotchi, 1000);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body style="margin:0;">
|
||||
|
||||
|
@ -12,9 +12,28 @@ blink_led() {
|
||||
sleep 0.3
|
||||
}
|
||||
|
||||
# check if brcm is stuck
|
||||
check_brcm() {
|
||||
if [[ "$(journalctl -n10 -k --since -5m | grep -c 'brcmf_cfg80211_nexmon_set_channel.*Set Channel failed')" -ge 5 ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# reload mod
|
||||
reload_brcm() {
|
||||
if ! modprobe -r brcmfmac; then
|
||||
return 1
|
||||
fi
|
||||
if ! modprobe brcmfmac; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# starts mon0
|
||||
start_monitor_interface() {
|
||||
iw phy phy0 interface add mon0 type monitor && ifconfig mon0 up
|
||||
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add mon0 type monitor && ifconfig mon0 up
|
||||
}
|
||||
|
||||
# stops mon0
|
||||
@ -158,7 +177,8 @@ EOF
|
||||
|
||||
pkill wpa_supplicant
|
||||
pkill dnsmasq
|
||||
kill "$(pgrep -f "decryption-webserver")"
|
||||
pid="$(pgrep -f "decryption-webserver")"
|
||||
[[ -n "$pid" ]] && kill "$pid"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
__version__ = '1.5.2'
|
||||
__version__ = '1.5.3'
|
||||
|
@ -307,7 +307,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
|
||||
def start_session_fetcher(self):
|
||||
_thread.start_new_thread(self._fetch_stats, ())
|
||||
_thread.start_new_thread(self._fetch_stats, ())
|
||||
|
||||
|
||||
def _fetch_stats(self):
|
||||
@ -323,7 +323,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
async def _on_event(self, msg):
|
||||
found_handshake = False
|
||||
jmsg = json.loads(msg)
|
||||
|
||||
|
||||
if jmsg['tag'] == 'wifi.client.handshake':
|
||||
filename = jmsg['data']['file']
|
||||
sta_mac = jmsg['data']['station']
|
||||
@ -331,6 +331,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
key = "%s -> %s" % (sta_mac, ap_mac)
|
||||
if key not in self._handshakes:
|
||||
self._handshakes[key] = jmsg
|
||||
s = self.session()
|
||||
ap_and_station = self._find_ap_sta_in(sta_mac, ap_mac, s)
|
||||
if ap_and_station is None:
|
||||
logging.warning("!!! captured new handshake: %s !!!", key)
|
||||
@ -364,7 +365,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
def start_event_polling(self):
|
||||
# start a thread and pass in the mainloop
|
||||
_thread.start_new_thread(self._event_poller, (asyncio.new_event_loop(),))
|
||||
_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
||||
|
||||
|
||||
def is_module_running(self, module):
|
||||
|
@ -47,6 +47,8 @@ class Client(object):
|
||||
logging.debug("Error while parsing event (%s)", ex)
|
||||
except websockets.exceptions.ConnectionClosedError:
|
||||
logging.debug("Lost websocket connection. Reconnecting...")
|
||||
except websockets.exceptions.WebSocketException as wex:
|
||||
logging.debug("Websocket exception (%s)", wex)
|
||||
|
||||
def run(self, command, verbose_errors=True):
|
||||
r = requests.post("%s/session" % self.url, auth=self.auth, json={'cmd': command})
|
||||
|
@ -2,6 +2,9 @@ main.name = ""
|
||||
main.lang = "en"
|
||||
main.confd = "/etc/pwnagotchi/conf.d/"
|
||||
main.custom_plugins = ""
|
||||
main.custom_plugin_repos = [
|
||||
"https://github.com/evilsocket/pwnagotchi-plugins-contrib/archive/master.zip"
|
||||
]
|
||||
main.iface = "mon0"
|
||||
main.mon_start_cmd = "/usr/bin/monstart"
|
||||
main.mon_stop_cmd = "/usr/bin/monstop"
|
||||
@ -110,6 +113,7 @@ main.plugins.led.patterns.peer_detected = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.peer_lost = "oo oo oo oo oo oo oo"
|
||||
|
||||
main.plugins.logtail.enabled = false
|
||||
main.plugins.logtail.max-lines = 10000
|
||||
|
||||
main.plugins.session-stats.enabled = true
|
||||
main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/"
|
||||
|
@ -58,6 +58,8 @@ def toggle_plugin(name, enable=True):
|
||||
|
||||
if enable and name in database and name not in loaded:
|
||||
load_from_file(database[name])
|
||||
if name in loaded and pwnagotchi.config and name in pwnagotchi.config['main']['plugins']:
|
||||
loaded[name].options = pwnagotchi.config['main']['plugins'][name]
|
||||
one(name, 'loaded')
|
||||
if pwnagotchi.config:
|
||||
one(name, 'config_changed', pwnagotchi.config)
|
||||
|
@ -1,6 +1,5 @@
|
||||
# Handles the commandline stuff
|
||||
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
import glob
|
||||
@ -11,7 +10,6 @@ from pwnagotchi.utils import download_file, unzip, save_config, parse_version, m
|
||||
from pwnagotchi.plugins import default_path
|
||||
|
||||
|
||||
REPO_URL = 'https://github.com/evilsocket/pwnagotchi-plugins-contrib/archive/master.zip'
|
||||
SAVE_DIR = '/usr/local/share/pwnagotchi/availaible-plugins/'
|
||||
DEFAULT_INSTALL_PATH = '/usr/local/share/pwnagotchi/installed-plugins/'
|
||||
|
||||
@ -75,7 +73,7 @@ def handle_cmd(args, config):
|
||||
Parses the arguments and does the thing the user wants
|
||||
"""
|
||||
if args.plugincmd == 'update':
|
||||
return update()
|
||||
return update(config)
|
||||
elif args.plugincmd == 'search':
|
||||
args.installed = True # also search in installed plugins
|
||||
return list_plugins(args, config, args.pattern)
|
||||
@ -349,41 +347,48 @@ def _analyse_dir(path):
|
||||
return results
|
||||
|
||||
|
||||
def update():
|
||||
def update(config):
|
||||
"""
|
||||
Updates the database
|
||||
"""
|
||||
global REPO_URL, SAVE_DIR
|
||||
global SAVE_DIR
|
||||
|
||||
DEST = os.path.join(SAVE_DIR, 'plugins.zip')
|
||||
logging.info('Downloading plugins to %s', DEST)
|
||||
|
||||
try:
|
||||
os.makedirs(SAVE_DIR, exist_ok=True)
|
||||
before_update = _analyse_dir(SAVE_DIR)
|
||||
|
||||
download_file(REPO_URL, os.path.join(SAVE_DIR, DEST))
|
||||
|
||||
logging.info('Unzipping...')
|
||||
unzip(DEST, SAVE_DIR, strip_dirs=1)
|
||||
|
||||
after_update = _analyse_dir(SAVE_DIR)
|
||||
|
||||
b_len = len(before_update)
|
||||
a_len = len(after_update)
|
||||
|
||||
if a_len > b_len:
|
||||
logging.info('Found %d new file(s).', a_len - b_len)
|
||||
|
||||
changed = 0
|
||||
for filename, filehash in after_update.items():
|
||||
if filename in before_update and filehash != before_update[filename]:
|
||||
changed += 1
|
||||
|
||||
if changed:
|
||||
logging.info('%d file(s) were changed.', changed)
|
||||
|
||||
return 0
|
||||
except Exception as ex:
|
||||
logging.error('Error while updating plugins %s', ex)
|
||||
urls = config['main']['custom_plugin_repos']
|
||||
if not urls:
|
||||
logging.info('No plugin repositories configured.')
|
||||
return 1
|
||||
|
||||
rc = 0
|
||||
for idx, REPO_URL in enumerate(urls):
|
||||
DEST = os.path.join(SAVE_DIR, 'plugins%d.zip' % idx)
|
||||
logging.info('Downloading plugins from %s to %s', REPO_URL, DEST)
|
||||
|
||||
try:
|
||||
os.makedirs(SAVE_DIR, exist_ok=True)
|
||||
before_update = _analyse_dir(SAVE_DIR)
|
||||
|
||||
download_file(REPO_URL, os.path.join(SAVE_DIR, DEST))
|
||||
|
||||
logging.info('Unzipping...')
|
||||
unzip(DEST, SAVE_DIR, strip_dirs=1)
|
||||
|
||||
after_update = _analyse_dir(SAVE_DIR)
|
||||
|
||||
b_len = len(before_update)
|
||||
a_len = len(after_update)
|
||||
|
||||
if a_len > b_len:
|
||||
logging.info('Found %d new file(s).', a_len - b_len)
|
||||
|
||||
changed = 0
|
||||
for filename, filehash in after_update.items():
|
||||
if filename in before_update and filehash != before_update[filename]:
|
||||
changed += 1
|
||||
|
||||
if changed:
|
||||
logging.info('%d file(s) were changed.', changed)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error('Error while updating plugins: %s', ex)
|
||||
rc = 1
|
||||
return rc
|
||||
|
@ -419,15 +419,19 @@ class Device:
|
||||
|
||||
class BTTether(plugins.Plugin):
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '1.0.0'
|
||||
__version__ = '1.1.0'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This makes the display reachable over bluetooth'
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.ready = False
|
||||
self.options = dict()
|
||||
self.devices = dict()
|
||||
self.lock = Lock()
|
||||
self.running = True
|
||||
self.status = '-'
|
||||
|
||||
|
||||
def on_loaded(self):
|
||||
# new config
|
||||
@ -449,7 +453,7 @@ class BTTether(plugins.Plugin):
|
||||
if 'mac' in self.options:
|
||||
for opt in ['share_internet', 'mac', 'ip', 'netmask', 'interval']:
|
||||
if opt not in self.options or self.options[opt] is None:
|
||||
logging.error("BT-TETHER: Please specify the %s in your config.yml.", opt)
|
||||
logging.error("BT-TETHER: Please specify the %s in your config.toml.", opt)
|
||||
return
|
||||
|
||||
self.devices['legacy'] = Device(name='legacy', **self.options)
|
||||
@ -466,22 +470,10 @@ class BTTether(plugins.Plugin):
|
||||
return
|
||||
|
||||
logging.info("BT-TETHER: Successfully loaded ...")
|
||||
self.ready = True
|
||||
|
||||
def on_unload(self, ui):
|
||||
with ui._lock:
|
||||
ui.remove_element('bluetooth')
|
||||
while self.running:
|
||||
time.sleep(1)
|
||||
|
||||
def on_ui_setup(self, ui):
|
||||
with ui._lock:
|
||||
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
|
||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if not self.ready:
|
||||
return
|
||||
|
||||
with self.lock:
|
||||
devices_to_try = list()
|
||||
connected_priorities = list()
|
||||
any_device_connected = False # if this is true, last status on screen should be C
|
||||
@ -508,11 +500,11 @@ class BTTether(plugins.Plugin):
|
||||
dev_remote = bt.wait_for_device(timeout=device.scantime)
|
||||
if dev_remote is None:
|
||||
logging.debug('BT-TETHER: Could not find %s, try again in %d minutes.', device.name, device.interval)
|
||||
ui.set('bluetooth', 'NF')
|
||||
self.status = 'NF'
|
||||
continue
|
||||
except Exception as bt_ex:
|
||||
logging.error(bt_ex)
|
||||
ui.set('bluetooth', 'NF')
|
||||
self.status = 'NF'
|
||||
continue
|
||||
|
||||
paired = bt.is_paired()
|
||||
@ -521,7 +513,7 @@ class BTTether(plugins.Plugin):
|
||||
logging.debug('BT-TETHER: Paired with %s.', device.name)
|
||||
else:
|
||||
logging.debug('BT-TETHER: Pairing with %s failed ...', device.name)
|
||||
ui.set('bluetooth', 'PE')
|
||||
self.status = 'PE'
|
||||
continue
|
||||
else:
|
||||
logging.debug('BT-TETHER: Already paired.')
|
||||
@ -539,17 +531,17 @@ class BTTether(plugins.Plugin):
|
||||
continue
|
||||
|
||||
if interface is None:
|
||||
ui.set('bluetooth', 'BE')
|
||||
self.status = 'BE'
|
||||
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
|
||||
continue
|
||||
|
||||
logging.debug('BT-TETHER: Created interface (%s)', interface)
|
||||
ui.set('bluetooth', 'C')
|
||||
self.status = 'C'
|
||||
any_device_connected = True
|
||||
device.tries = 0 # reset tries
|
||||
else:
|
||||
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
|
||||
ui.set('bluetooth', 'NF')
|
||||
self.status = 'NF'
|
||||
continue
|
||||
|
||||
addr = f"{device.ip}/{device.netmask}"
|
||||
@ -561,7 +553,7 @@ class BTTether(plugins.Plugin):
|
||||
wrapped_interface = IfaceWrapper(interface)
|
||||
logging.debug('BT-TETHER: Add ip to %s', interface)
|
||||
if not wrapped_interface.set_addr(addr):
|
||||
ui.set('bluetooth', 'AE')
|
||||
self.status = 'AE'
|
||||
logging.debug("BT-TETHER: Could not add ip to %s", interface)
|
||||
continue
|
||||
|
||||
@ -580,4 +572,20 @@ class BTTether(plugins.Plugin):
|
||||
resolv.write(nameserver + 'nameserver 9.9.9.9\n')
|
||||
|
||||
if any_device_connected:
|
||||
ui.set('bluetooth', 'C')
|
||||
self.status = 'C'
|
||||
|
||||
|
||||
def on_unload(self, ui):
|
||||
self.running = False
|
||||
with ui._lock:
|
||||
ui.remove_element('bluetooth')
|
||||
|
||||
|
||||
def on_ui_setup(self, ui):
|
||||
with ui._lock:
|
||||
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
|
||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
ui.set('bluetooth', self.status)
|
||||
|
@ -207,7 +207,6 @@ TEMPLATE = """
|
||||
}
|
||||
document.body.style.cursor = 'default';
|
||||
}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@ -254,6 +253,7 @@ class Logtail(plugins.Plugin):
|
||||
"""
|
||||
logging.info("Logtail plugin loaded.")
|
||||
|
||||
|
||||
def on_webhook(self, path, request):
|
||||
if not self.ready:
|
||||
return "Plugin not ready"
|
||||
@ -264,10 +264,7 @@ class Logtail(plugins.Plugin):
|
||||
if path == 'stream':
|
||||
def generate():
|
||||
with open(self.config['main']['log']['path']) as f:
|
||||
# https://stackoverflow.com/questions/39549426/read-multiple-lines-from-a-file-batch-by-batch/39549901#39549901
|
||||
n = 1024
|
||||
for n_lines in iter(lambda: ''.join(islice(f, n)), ''):
|
||||
yield n_lines
|
||||
yield ''.join(f.readlines()[-self.options.get('max-lines', 4096):])
|
||||
while True:
|
||||
yield f.readline()
|
||||
|
||||
|
@ -12,7 +12,7 @@ from json.decoder import JSONDecodeError
|
||||
|
||||
class OnlineHashCrack(plugins.Plugin):
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '2.0.1'
|
||||
__version__ = '2.1.0'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin automatically uploads handshakes to https://onlinehashcrack.com'
|
||||
|
||||
@ -55,9 +55,9 @@ class OnlineHashCrack(plugins.Plugin):
|
||||
files=payload,
|
||||
timeout=timeout)
|
||||
if 'already been sent' in result.text:
|
||||
logging.warning(f"{path} was already uploaded.")
|
||||
logging.debug(f"{path} was already uploaded.")
|
||||
except requests.exceptions.RequestException as e:
|
||||
logging.error(f"OHC: Got an exception while uploading {path} -> {e}")
|
||||
logging.debug(f"OHC: Got an exception while uploading {path} -> {e}")
|
||||
raise e
|
||||
|
||||
def _download_cracked(self, save_file, timeout=120):
|
||||
@ -78,6 +78,16 @@ class OnlineHashCrack(plugins.Plugin):
|
||||
except OSError as os_e:
|
||||
raise os_e
|
||||
|
||||
|
||||
def on_webhook(self, path, request):
|
||||
import requests
|
||||
from flask import redirect
|
||||
s = requests.Session()
|
||||
s.get('https://www.onlinehashcrack.com/dashboard')
|
||||
r = s.post('https://www.onlinehashcrack.com/dashboard', data={'emailTasks': self.options['email'], 'submit': ''})
|
||||
return redirect(r.url, code=302)
|
||||
|
||||
|
||||
def on_internet_available(self, agent):
|
||||
"""
|
||||
Called in manual mode when there's internet connectivity
|
||||
@ -108,14 +118,14 @@ class OnlineHashCrack(plugins.Plugin):
|
||||
if handshake not in reported:
|
||||
reported.append(handshake)
|
||||
self.report.update(data={'reported': reported})
|
||||
logging.info(f"OHC: Successfully uploaded {handshake}")
|
||||
logging.debug(f"OHC: Successfully uploaded {handshake}")
|
||||
except requests.exceptions.RequestException as req_e:
|
||||
self.skip.append(handshake)
|
||||
logging.error("OHC: %s", req_e)
|
||||
logging.debug("OHC: %s", req_e)
|
||||
continue
|
||||
except OSError as os_e:
|
||||
self.skip.append(handshake)
|
||||
logging.error("OHC: %s", os_e)
|
||||
logging.debug("OHC: %s", os_e)
|
||||
continue
|
||||
if 'dashboard' in self.options and self.options['dashboard']:
|
||||
cracked_file = os.path.join(handshake_dir, 'onlinehashcrack.cracked')
|
||||
|
36
pwnagotchi/plugins/default/watchdog.py
Normal file
36
pwnagotchi/plugins/default/watchdog.py
Normal file
@ -0,0 +1,36 @@
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
from io import TextIOWrapper
|
||||
from pwnagotchi import plugins
|
||||
|
||||
|
||||
class Watchdog(plugins.Plugin):
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '0.1.0'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'Restart pwnagotchi when blindbug is detected.'
|
||||
|
||||
def __init__(self):
|
||||
self.options = dict()
|
||||
self.pattern = re.compile(r'brcmf_cfg80211_nexmon_set_channel.*?Set Channel failed')
|
||||
|
||||
def on_loaded(self):
|
||||
"""
|
||||
Gets called when the plugin gets loaded
|
||||
"""
|
||||
logging.info("Watchdog plugin loaded.")
|
||||
|
||||
def on_epoch(self, agent, epoch, epoch_data):
|
||||
# get last 10 lines
|
||||
last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl','-n10','-k', '--since', '-5m'],
|
||||
stdout=subprocess.PIPE).stdout))[-10:])
|
||||
if len(self.pattern.findall(last_lines)) >= 5:
|
||||
display = agent.view()
|
||||
display.set('status', 'Blind-Bug detected. Restarting.')
|
||||
display.update(force=True)
|
||||
logging.info('[WATCHDOG] Blind-Bug detected. Restarting.')
|
||||
mode = 'MANU' if agent.mode == 'manual' else 'AUTO'
|
||||
import pwnagotchi
|
||||
pwnagotchi.restart(mode=mode)
|
@ -230,7 +230,7 @@ class Webgpsmap(plugins.Plugin):
|
||||
}
|
||||
|
||||
# get ap password if exist
|
||||
check_for = os.path.basename(pos_file[:-9]) + ".pcap.cracked"
|
||||
check_for = os.path.basename(pos_file).split(".")[0] + ".pcap.cracked"
|
||||
if check_for in all_files:
|
||||
gps_data[ssid + "_" + mac]["pass"] = pos.password()
|
||||
|
||||
|
@ -106,7 +106,7 @@ class Wigle(plugins.Plugin):
|
||||
|
||||
def on_loaded(self):
|
||||
if 'api_key' not in self.options or ('api_key' in self.options and self.options['api_key'] is None):
|
||||
logging.error("WIGLE: api_key isn't set. Can't upload to wigle.net")
|
||||
logging.debug("WIGLE: api_key isn't set. Can't upload to wigle.net")
|
||||
return
|
||||
|
||||
if not 'whitelist' in self.options:
|
||||
@ -141,21 +141,21 @@ class Wigle(plugins.Plugin):
|
||||
for gps_file in new_gps_files:
|
||||
pcap_filename = gps_file.replace('.gps.json', '.pcap')
|
||||
if not os.path.exists(pcap_filename):
|
||||
logging.error("WIGLE: Can't find pcap for %s", gps_file)
|
||||
logging.debug("WIGLE: Can't find pcap for %s", gps_file)
|
||||
self.skip.append(gps_file)
|
||||
continue
|
||||
try:
|
||||
gps_data = _extract_gps_data(gps_file)
|
||||
except OSError as os_err:
|
||||
logging.error("WIGLE: %s", os_err)
|
||||
logging.debug("WIGLE: %s", os_err)
|
||||
self.skip.append(gps_file)
|
||||
continue
|
||||
except json.JSONDecodeError as json_err:
|
||||
logging.error("WIGLE: %s", json_err)
|
||||
logging.debug("WIGLE: %s", json_err)
|
||||
self.skip.append(gps_file)
|
||||
continue
|
||||
if gps_data['Latitude'] == 0 and gps_data['Longitude'] == 0:
|
||||
logging.warning("WIGLE: Not enough gps-information for %s. Trying again next time.", gps_file)
|
||||
logging.debug("WIGLE: Not enough gps-information for %s. Trying again next time.", gps_file)
|
||||
self.skip.append(gps_file)
|
||||
continue
|
||||
try:
|
||||
@ -165,11 +165,11 @@ class Wigle(plugins.Plugin):
|
||||
WifiInfo.CHANNEL,
|
||||
WifiInfo.RSSI])
|
||||
except FieldNotFoundError:
|
||||
logging.error("WIGLE: Could not extract all information. Skip %s", gps_file)
|
||||
logging.debug("WIGLE: Could not extract all information. Skip %s", gps_file)
|
||||
self.skip.append(gps_file)
|
||||
continue
|
||||
except Scapy_Exception as sc_e:
|
||||
logging.error("WIGLE: %s", sc_e)
|
||||
logging.debug("WIGLE: %s", sc_e)
|
||||
self.skip.append(gps_file)
|
||||
continue
|
||||
new_entry = _transform_wigle_entry(gps_data, pcap_data)
|
||||
@ -185,7 +185,7 @@ class Wigle(plugins.Plugin):
|
||||
logging.info("WIGLE: Successfully uploaded %d files", len(no_err_entries))
|
||||
except requests.exceptions.RequestException as re_e:
|
||||
self.skip += no_err_entries
|
||||
logging.error("WIGLE: Got an exception while uploading %s", re_e)
|
||||
logging.debug("WIGLE: Got an exception while uploading %s", re_e)
|
||||
except OSError as os_e:
|
||||
self.skip += no_err_entries
|
||||
logging.error("WIGLE: Got the following error: %s", os_e)
|
||||
logging.debug("WIGLE: Got the following error: %s", os_e)
|
||||
|
@ -39,7 +39,7 @@ class WpaSec(plugins.Plugin):
|
||||
files=payload,
|
||||
timeout=timeout)
|
||||
if ' already submitted' in result.text:
|
||||
logging.warning("%s was already submitted.", path)
|
||||
logging.debug("%s was already submitted.", path)
|
||||
except requests.exceptions.RequestException as req_e:
|
||||
raise req_e
|
||||
|
||||
@ -83,6 +83,12 @@ class WpaSec(plugins.Plugin):
|
||||
|
||||
self.ready = True
|
||||
|
||||
def on_webhook(self, path, request):
|
||||
from flask import make_response, redirect
|
||||
response = make_response(redirect(self.options['api_url'], code=302))
|
||||
response.set_cookie('key', self.options['api_key'])
|
||||
return response
|
||||
|
||||
def on_internet_available(self, agent):
|
||||
"""
|
||||
Called in manual mode when there's internet connectivity
|
||||
@ -110,13 +116,13 @@ class WpaSec(plugins.Plugin):
|
||||
self._upload_to_wpasec(handshake)
|
||||
reported.append(handshake)
|
||||
self.report.update(data={'reported': reported})
|
||||
logging.info("WPA_SEC: Successfully uploaded %s", handshake)
|
||||
logging.debug("WPA_SEC: Successfully uploaded %s", handshake)
|
||||
except requests.exceptions.RequestException as req_e:
|
||||
self.skip.append(handshake)
|
||||
logging.error("WPA_SEC: %s", req_e)
|
||||
logging.debug("WPA_SEC: %s", req_e)
|
||||
continue
|
||||
except OSError as os_e:
|
||||
logging.error("WPA_SEC: %s", os_e)
|
||||
logging.debug("WPA_SEC: %s", os_e)
|
||||
continue
|
||||
|
||||
if 'download_results' in self.options and self.options['download_results']:
|
||||
|
@ -49,8 +49,11 @@ class Display(View):
|
||||
def is_lcdhat(self):
|
||||
return self._implementation.name == 'lcdhat'
|
||||
|
||||
def is_dfrobot(self):
|
||||
return self._implementation.name == 'dfrobot'
|
||||
def is_dfrobot_v1(self):
|
||||
return self._implementation.name == 'dfrobot_v1'
|
||||
|
||||
def is_dfrobot_v2(self):
|
||||
return self._implementation.name == 'dfrobot_v2'
|
||||
|
||||
def is_waveshare144lcd(self):
|
||||
return self._implementation.name == 'waveshare144lcd'
|
||||
|
@ -2,7 +2,8 @@ from pwnagotchi.ui.hw.inky import Inky
|
||||
from pwnagotchi.ui.hw.papirus import Papirus
|
||||
from pwnagotchi.ui.hw.oledhat import OledHat
|
||||
from pwnagotchi.ui.hw.lcdhat import LcdHat
|
||||
from pwnagotchi.ui.hw.dfrobot import DFRobot
|
||||
from pwnagotchi.ui.hw.dfrobot1 import DFRobotV1
|
||||
from pwnagotchi.ui.hw.dfrobot2 import DFRobotV2
|
||||
from pwnagotchi.ui.hw.waveshare1 import WaveshareV1
|
||||
from pwnagotchi.ui.hw.waveshare2 import WaveshareV2
|
||||
from pwnagotchi.ui.hw.waveshare27inch import Waveshare27inch
|
||||
@ -27,8 +28,11 @@ def display_for(config):
|
||||
if config['ui']['display']['type'] == 'lcdhat':
|
||||
return LcdHat(config)
|
||||
|
||||
if config['ui']['display']['type'] == 'dfrobot':
|
||||
return DFRobot(config)
|
||||
if config['ui']['display']['type'] == 'dfrobot_1':
|
||||
return DFRobotV1(config)
|
||||
|
||||
if config['ui']['display']['type'] == 'dfrobot_2':
|
||||
return DFRobotV2(config)
|
||||
|
||||
elif config['ui']['display']['type'] == 'waveshare_1':
|
||||
return WaveshareV1(config)
|
||||
|
@ -3,9 +3,9 @@ import logging
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
from pwnagotchi.ui.hw.base import DisplayImpl
|
||||
|
||||
class DFRobot(DisplayImpl):
|
||||
class DFRobotV1(DisplayImpl):
|
||||
def __init__(self, config):
|
||||
super(DFRobot, self).__init__(config, 'dfrobot')
|
||||
super(DFRobotV1, self).__init__(config, 'dfrobot_1')
|
||||
self._display = None
|
||||
|
||||
def layout(self):
|
||||
@ -31,8 +31,8 @@ class DFRobot(DisplayImpl):
|
||||
return self._layout
|
||||
|
||||
def initialize(self):
|
||||
logging.info("initializing dfrobot display")
|
||||
from pwnagotchi.ui.hw.libs.dfrobot.dfrobot import DFRobot
|
||||
logging.info("initializing dfrobot1 display")
|
||||
from pwnagotchi.ui.hw.libs.dfrobot.v1.dfrobot import DFRobot
|
||||
self._display = DFRobot()
|
||||
|
||||
def render(self, canvas):
|
43
pwnagotchi/ui/hw/dfrobot2.py
Normal file
43
pwnagotchi/ui/hw/dfrobot2.py
Normal file
@ -0,0 +1,43 @@
|
||||
import logging
|
||||
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
from pwnagotchi.ui.hw.base import DisplayImpl
|
||||
|
||||
class DFRobotV2(DisplayImpl):
|
||||
def __init__(self, config):
|
||||
super(DFRobotV2, self).__init__(config, 'dfrobot_2')
|
||||
self._display = None
|
||||
|
||||
def layout(self):
|
||||
fonts.setup(10, 9, 10, 35, 25, 9)
|
||||
self._layout['width'] = 250
|
||||
self._layout['height'] = 122
|
||||
self._layout['face'] = (0, 40)
|
||||
self._layout['name'] = (5, 20)
|
||||
self._layout['channel'] = (0, 0)
|
||||
self._layout['aps'] = (28, 0)
|
||||
self._layout['uptime'] = (185, 0)
|
||||
self._layout['line1'] = [0, 14, 250, 14]
|
||||
self._layout['line2'] = [0, 108, 250, 108]
|
||||
self._layout['friend_face'] = (0, 92)
|
||||
self._layout['friend_name'] = (40, 94)
|
||||
self._layout['shakes'] = (0, 109)
|
||||
self._layout['mode'] = (225, 109)
|
||||
self._layout['status'] = {
|
||||
'pos': (125, 20),
|
||||
'font': fonts.status_font(fonts.Medium),
|
||||
'max': 20
|
||||
}
|
||||
return self._layout
|
||||
|
||||
def initialize(self):
|
||||
logging.info("initializing dfrobot2 display")
|
||||
from pwnagotchi.ui.hw.libs.dfrobot.v2.dfrobot import DFRobot
|
||||
self._display = DFRobot()
|
||||
|
||||
def render(self, canvas):
|
||||
buf = self._display.getbuffer(canvas)
|
||||
self._display.display(buf)
|
||||
|
||||
def clear(self):
|
||||
self._display.Clear(0xFF)
|
0
pwnagotchi/ui/hw/libs/dfrobot/v1/__init__.py
Normal file
0
pwnagotchi/ui/hw/libs/dfrobot/v1/__init__.py
Normal file
0
pwnagotchi/ui/hw/libs/dfrobot/v2/__init__.py
Normal file
0
pwnagotchi/ui/hw/libs/dfrobot/v2/__init__.py
Normal file
66
pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot.py
Normal file
66
pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot.py
Normal file
@ -0,0 +1,66 @@
|
||||
# DFRobot display support
|
||||
|
||||
import logging
|
||||
from . import dfrobot_epaper
|
||||
|
||||
#Resolution of display
|
||||
WIDTH = 250
|
||||
HEIGHT = 122
|
||||
|
||||
RASPBERRY_SPI_BUS = 0
|
||||
RASPBERRY_SPI_DEV = 0
|
||||
RASPBERRY_PIN_CS = 27
|
||||
RASPBERRY_PIN_CD = 17
|
||||
RASPBERRY_PIN_BUSY = 4
|
||||
|
||||
class DFRobot:
|
||||
def __init__(self):
|
||||
self._display = dfrobot_epaper.DFRobot_Epaper_SPI(RASPBERRY_SPI_BUS, RASPBERRY_SPI_DEV, RASPBERRY_PIN_CS, RASPBERRY_PIN_CD, RASPBERRY_PIN_BUSY)
|
||||
self._display.begin()
|
||||
self.clear(0xFF)
|
||||
self.FULL = self._display.FULL
|
||||
self.PART = self._display.PART
|
||||
|
||||
def getbuffer(self, image):
|
||||
if HEIGHT % 8 == 0:
|
||||
linewidth = HEIGHT // 8
|
||||
else:
|
||||
linewidth = HEIGHT // 8 + 1
|
||||
|
||||
buf = [0xFF] * (linewidth * WIDTH)
|
||||
image_monocolor = image.convert('1')
|
||||
imwidth, imheight = image_monocolor.size
|
||||
pixels = image_monocolor.load()
|
||||
|
||||
if (imwidth == HEIGHT and imheight == WIDTH):
|
||||
for y in range(imheight):
|
||||
for x in range(imwidth):
|
||||
if pixels[x,y] == 0:
|
||||
x = imwidth - x
|
||||
buf[x // 8 + y * linewidth] &= ~(0x80 >> (x % 8))
|
||||
elif (imwidth == WIDTH and imheight == HEIGHT):
|
||||
for y in range(imheight):
|
||||
for x in range(imwidth):
|
||||
newx = y
|
||||
newy = WIDTH - x - 1
|
||||
if pixels[x,y] == 0:
|
||||
newy = imwidth - newy - 1
|
||||
buf[newx // 8 + newy * linewidth] &= ~(0x80 >> (y % 8))
|
||||
return buf
|
||||
|
||||
def flush(self, type):
|
||||
self._display.flush(type)
|
||||
|
||||
def display(self, buf):
|
||||
self._display.setBuffer(buf)
|
||||
self.flush(self._display.PART)
|
||||
|
||||
def clear(self, color):
|
||||
if HEIGHT % 8 == 0:
|
||||
linewidth = HEIGHT // 8
|
||||
else:
|
||||
linewidth = HEIGHT // 8 + 1
|
||||
|
||||
buf = [color] * (linewidth * WIDTH)
|
||||
self._display.setBuffer(buf)
|
||||
self.flush(self._display.FULL)
|
BIN
pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/__init__.pyc
Normal file
BIN
pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/__init__.pyc
Normal file
Binary file not shown.
@ -0,0 +1,673 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import sys
|
||||
|
||||
from .dfrobot_printString import PrintString
|
||||
from .dfrobot_fonts import Fonts
|
||||
|
||||
def color24to16(color):
|
||||
return (((color >> 8) & 0xf800) | ((color >> 5) & 0x7e0) | ((color >> 3) & 0x1f))
|
||||
|
||||
def color16to24(color):
|
||||
return (((color & 0xf800) << 8) | ((color & 0x7e0) << 5) | ((color & 0x1f) << 3))
|
||||
|
||||
def swap(o1, o2):
|
||||
return (o2, o1)
|
||||
|
||||
class DFRobot_Display(PrintString):
|
||||
|
||||
WHITE24 = 0xffffff
|
||||
SILVER24 = 0xc0c0c0
|
||||
GRAY24 = 0x808080
|
||||
BLACK24 = 0x000000
|
||||
RED24 = 0xff0000
|
||||
MAROON24 = 0x800000
|
||||
YELLOW24 = 0xffff00
|
||||
OLIVE24 = 0x808000
|
||||
GREEN24 = 0x00ff00
|
||||
DARKGREEN24 = 0x008000
|
||||
CYAN24 = 0x00ffff
|
||||
BLUE24 = 0x0000ff
|
||||
NAVY24 = 0x000080
|
||||
FUCHSIA24 = 0xff00ff
|
||||
PURPLE24 = 0x800080
|
||||
TEAL24 = 0x008080
|
||||
|
||||
WHITE16 = color24to16(WHITE24)
|
||||
SILVER16 = color24to16(SILVER24)
|
||||
GRAY16 = color24to16(GRAY24)
|
||||
BLACK16 = color24to16(BLACK24)
|
||||
RED16 = color24to16(RED24)
|
||||
MAROON16 = color24to16(MAROON24)
|
||||
YELLOW16 = color24to16(YELLOW24)
|
||||
OLIVE16 = color24to16(OLIVE24)
|
||||
GREEN16 = color24to16(GREEN24)
|
||||
DARKGREEN16 = color24to16(DARKGREEN24)
|
||||
CYAN16 = color24to16(CYAN24)
|
||||
BLUE16 = color24to16(BLUE24)
|
||||
NAVY16 = color24to16(NAVY24)
|
||||
FUCHSIA16 = color24to16(FUCHSIA24)
|
||||
PURPLE16 = color24to16(PURPLE24)
|
||||
TEAL16 = color24to16(TEAL24)
|
||||
|
||||
WHITE = WHITE16
|
||||
SILVER = SILVER16
|
||||
GRAY = GRAY16
|
||||
BLACK = BLACK16
|
||||
RED = RED16
|
||||
MAROON = MAROON16
|
||||
YELLOW = YELLOW16
|
||||
OLIVE = OLIVE16
|
||||
GREEN = GREEN16
|
||||
DARKGREEN = DARKGREEN16
|
||||
CYAN = CYAN16
|
||||
BLUE = BLUE16
|
||||
NAVY = NAVY16
|
||||
FUCHSIA = FUCHSIA16
|
||||
PURPLE = PURPLE16
|
||||
TEAL = TEAL16
|
||||
|
||||
POSITIVE = 1
|
||||
REVERSE = -1
|
||||
|
||||
BITMAP_TBMLLR = "TBMLLR"
|
||||
BITMAP_TBMRLL = "TBMRLL"
|
||||
BITMAP_BTMLLR = "BTMLLR"
|
||||
BITMAP_BTMRLL = "BTMRLL"
|
||||
BITMAP_LRMTLB = "LRMTLB"
|
||||
BITMAP_LRMBLT = "LRMBLT"
|
||||
BITMAP_RLMTLB = "RLMTLB"
|
||||
BIMTAP_RLMBLT = "RLMBLT"
|
||||
BITMAP_UNKNOW = "UNKNOW"
|
||||
|
||||
def __init__(self, w, h):
|
||||
PrintString.__init__(self)
|
||||
print("DFRobot_Display init " + str(w) + " " + str(h))
|
||||
self._width = w
|
||||
self._height = h
|
||||
|
||||
self._lineWidth = 1
|
||||
self._bitmapSize = 1
|
||||
self._bitmapFmt = ""
|
||||
self._bmpFmt = self.BITMAP_TBMLLR
|
||||
|
||||
self._fonts = Fonts()
|
||||
self._textSize = 1
|
||||
self._textColor = self.BLACK
|
||||
self._textBackground = self.WHITE
|
||||
self._textCursorX = 0
|
||||
self._textCursorY = 0
|
||||
self._textIntervalRow = 0
|
||||
self._textIntervalCol = 0
|
||||
|
||||
def _ternaryExpression(self, condition, o1, o2):
|
||||
if condition:
|
||||
return o1
|
||||
return o2
|
||||
|
||||
def _getDirection(self, value):
|
||||
if value >= 0:
|
||||
return 1
|
||||
return -1
|
||||
|
||||
def color16to24(self, color):
|
||||
return color16to24(color)
|
||||
|
||||
def color24to16(self, color):
|
||||
return color24to16(color)
|
||||
|
||||
def setColorTo16(self):
|
||||
self.WHITE = self.WHITE16
|
||||
self.SILVER = self.SILVER16
|
||||
self.GRAY = self.GRAY16
|
||||
self.BLACK = self.BLACK16
|
||||
self.RED = self.RED16
|
||||
self.MAROON = self.MAROON16
|
||||
self.YELLOW = self.YELLOW16
|
||||
self.OLIVE = self.OLIVE16
|
||||
self.GREEN = self.GREEN16
|
||||
self.DARKGREEN = self.DARKGREEN16
|
||||
self.CYAN = self.CYAN16
|
||||
self.BLUE = self.BLUE16
|
||||
self.NAVY = self.NAVY16
|
||||
self.FUCHSIA = self.FUCHSIA16
|
||||
self.PURPLE = self.PURPLE16
|
||||
self.TEAL = self.TEAL16
|
||||
|
||||
def setColorTo24(self):
|
||||
self.WHITE = self.WHITE24
|
||||
self.SILVER = self.SILVER24
|
||||
self.GRAY = self.GRAY24
|
||||
self.BLACK = self.BLACK24
|
||||
self.RED = self.RED24
|
||||
self.MAROON = self.MAROON24
|
||||
self.YELLOW = self.YELLOW24
|
||||
self.OLIVE = self.OLIVE24
|
||||
self.GREEN = self.GREEN24
|
||||
self.DARKGREEN = self.DARKGREEN24
|
||||
self.CYAN = self.CYAN24
|
||||
self.BLUE = self.BLUE24
|
||||
self.NAVY = self.NAVY24
|
||||
self.FUCHSIA = self.FUCHSIA24
|
||||
self.PURPLE = self.PURPLE24
|
||||
self.TEAL = self.TEAL24
|
||||
|
||||
def setLineWidth(self, w):
|
||||
if w < 0:
|
||||
return
|
||||
self._lineWidth = w
|
||||
|
||||
def setTextFormat(self, size, color, background, intervalRow = 2, intervalCol = 0):
|
||||
self._textColor = color
|
||||
self._textIntervalRow = intervalRow
|
||||
self._textIntervalCol = intervalCol
|
||||
self._textBackground = background
|
||||
if size < 0:
|
||||
return
|
||||
self._textSize = size
|
||||
|
||||
def setTextCursor(self, x, y):
|
||||
self._textCursorX = int(x)
|
||||
self._textCursorY = int(y)
|
||||
|
||||
def setBitmapSize(self, size):
|
||||
if size < 0:
|
||||
return
|
||||
self._bitmapSize = size
|
||||
|
||||
def setBitmapFmt(self, fmt):
|
||||
self._bmpFmt = fmt
|
||||
|
||||
def setExFonts(self, obj):
|
||||
self._fonts.setExFonts(obj)
|
||||
|
||||
def setExFontsFmt(self, width, height):
|
||||
self._fonts.setExFontsFmt(width, height)
|
||||
|
||||
def setEnableDefaultFonts(self, opt):
|
||||
self._fonts.setEnableDefaultFonts(opt)
|
||||
|
||||
def pixel(self, x, y, color):
|
||||
pass
|
||||
|
||||
def clear(self, color):
|
||||
self.fillRect(0, 0, self._width, self._height, color)
|
||||
self._textCursorX = 0
|
||||
self._textCursorY = 0
|
||||
|
||||
def VLine(self, x, y, h, color):
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
h = int(h)
|
||||
direction = self._getDirection(h)
|
||||
x -= self._lineWidth // 2
|
||||
h = self._ternaryExpression(h > 0, h, -h)
|
||||
for i in range(self._ternaryExpression(h > 0, h, - h)):
|
||||
xx = x
|
||||
for j in range(self._lineWidth):
|
||||
self.pixel(xx, y, color)
|
||||
xx += 1
|
||||
y += direction
|
||||
|
||||
def HLine(self, x, y, w, color):
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
w = int(w)
|
||||
direction = self._getDirection(w)
|
||||
y -= self._lineWidth // 2
|
||||
for i in range(self._ternaryExpression(w > 0, w, - w)):
|
||||
yy = y
|
||||
for j in range(self._lineWidth):
|
||||
self.pixel(x, yy, color)
|
||||
yy += 1
|
||||
x += direction
|
||||
|
||||
def line(self, x, y, x1, y1, color):
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
x1 = int(x1)
|
||||
y1 = int(y1)
|
||||
if x == x1:
|
||||
self.VLine(x, y, y1 - y, color)
|
||||
return
|
||||
if y == y1:
|
||||
self.HLine(x, y, x1 - x, color)
|
||||
return
|
||||
dx = abs(x1 - x)
|
||||
dy = abs(y1 - y)
|
||||
dirX = self._ternaryExpression(x < x1, 1, -1)
|
||||
dirY = self._ternaryExpression(y < y1, 1, -1)
|
||||
if dx > dy:
|
||||
err = dx / 2
|
||||
for i in range(dx):
|
||||
self.HLine(x, y, 1, color)
|
||||
x += dirX
|
||||
err -= dy
|
||||
if err < 0:
|
||||
err += dx
|
||||
y += dirY
|
||||
self.HLine(x1, y1, 1, color)
|
||||
else:
|
||||
err = dy / 2
|
||||
for i in range(dy):
|
||||
self.VLine(x, y, 1, color)
|
||||
y += dirY
|
||||
err -= dx
|
||||
if err < 0:
|
||||
err += dy
|
||||
x += dirX
|
||||
self.VLine(x1, y1, 1, color)
|
||||
|
||||
def triangle(self, x, y, x1, y1, x2, y2, color):
|
||||
self.line(x, y, x1, y1, color)
|
||||
self.line(x1, y1, x2, y2, color)
|
||||
self.line(x2, y2, x, y, color)
|
||||
|
||||
def fillTriangle(self, x, y, x1, y1, x2, y2, color):
|
||||
self.line(x, y, x1, y1, color)
|
||||
self.line(x1, y1, x2, y2, color)
|
||||
self.line(x2, y2, x, y, color)
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
x1 = int(x1)
|
||||
y1 = int(y1)
|
||||
x2 = int(x2)
|
||||
y2 = int(y2)
|
||||
temp = self._lineWidth
|
||||
self._lineWidth = 1
|
||||
if x == x1 and x == x2:
|
||||
ymax = max([y, y1, y2])
|
||||
ymin = min([y, y1, y2])
|
||||
self.HLine(x, ymin, ymax - ymin, color)
|
||||
self._lineWidth = temp
|
||||
return
|
||||
if y == y1 and y == y2:
|
||||
xmax = max([x, x1, x2])
|
||||
xmin = max([x, x1, x2])
|
||||
self.VLine(xmin, y, xmax - xmin, color)
|
||||
self._lineWidth = temp
|
||||
return
|
||||
|
||||
direction = self.POSITIVE
|
||||
if y == y1 or y1 == y2 or y == y2:
|
||||
if y == y1:
|
||||
(x, x2) = swap(x, x2)
|
||||
(y, y2) = swap(y, y2)
|
||||
elif y == y2:
|
||||
(x, x1) = swap(x, x1)
|
||||
(y, y1) = swap(y, y1)
|
||||
if y > y1:
|
||||
direction = self.REVERSE
|
||||
if x1 > x2:
|
||||
(x1, x2) = swap(x1, x2)
|
||||
(y1, y2) = swap(y1, y2)
|
||||
else:
|
||||
if y > y1:
|
||||
(x, x1) = swap(x, x1)
|
||||
(y, y1) = swap(y, y1)
|
||||
if y > y2:
|
||||
(x, x2) = swap(x, x2)
|
||||
(y, y2) = swap(y, y2)
|
||||
if y1 > y2:
|
||||
(x1, x2) = swap(x1, x2)
|
||||
(y1, y2) = swap(y1, y2)
|
||||
|
||||
dx1 = x1 - x
|
||||
dx2 = x2 - x
|
||||
dx3 = x2 - x1
|
||||
dy1 = y1 - y
|
||||
dy2 = y2 - y
|
||||
dy3 = y2 - y1
|
||||
if direction == self.POSITIVE:
|
||||
for i in range(dy1):
|
||||
self.HLine(x + dx1 * i / dy1, y + i, (x + dx2 * i / dy2) - (x + dx1 * i / dy1) + 1, color)
|
||||
for i in range(dy3):
|
||||
self.HLine(x1 + dx3 * i / dy3, y1 + i, (x + dx2 * (i + dy1) / dy2) - (x1 + dx3 * i / dy3) + 1, color)
|
||||
else:
|
||||
y = y1 + dy1
|
||||
dy1 = - dy1
|
||||
for i in range(dy1):
|
||||
self.HLine(x + dx1 * i / dy1, y1 + dy1 - i, (x + dx2 * i / dy1) - (x + dx1 * i / dy1) + 1, color)
|
||||
self._lineWidth = temp
|
||||
|
||||
def rect(self, x, y, w, h, color):
|
||||
if w < 0:
|
||||
x += w
|
||||
w = -w
|
||||
if h < 0:
|
||||
y += h
|
||||
h = -h
|
||||
self.HLine(x - self._lineWidth // 2, y, w + self._lineWidth, color)
|
||||
self.HLine(x - self._lineWidth // 2, y + h, w + self._lineWidth, color)
|
||||
self.VLine(x, y - self._lineWidth // 2, h + self._lineWidth, color)
|
||||
self.VLine(x + w, y - self._lineWidth // 2, h + self._lineWidth, color)
|
||||
|
||||
def fillRect(self, x, y, w, h, color):
|
||||
temp = self._lineWidth
|
||||
self._lineWidth = 1
|
||||
if w < 0:
|
||||
x += w
|
||||
w = abs(w)
|
||||
for i in range(w):
|
||||
self.VLine(x + i, y, h, color)
|
||||
self._lineWidth = temp
|
||||
|
||||
QUADRANT_1 = 1
|
||||
QUADRANT_2 = 2
|
||||
QUADRANT_3 = 4
|
||||
QUADRANT_4 = 8
|
||||
QUADRANT_ALL = 15
|
||||
|
||||
def circleHelper(self, x, y, r, quadrant, color):
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
r = abs(int(r))
|
||||
vx = 0
|
||||
vy = r
|
||||
dx = 1
|
||||
dy = -2 * r
|
||||
p = 1 - r
|
||||
if quadrant & self.QUADRANT_1:
|
||||
self.VLine(x + r, y, 1, color)
|
||||
if quadrant & self.QUADRANT_2:
|
||||
self.VLine(x, y - r, 1, color)
|
||||
if quadrant & self.QUADRANT_3:
|
||||
self.VLine(x - r, y, 1, color)
|
||||
if quadrant & self.QUADRANT_4:
|
||||
self.VLine(x, y + r, 1, color)
|
||||
|
||||
halfLineWidth = self._lineWidth // 2
|
||||
while vx < vy:
|
||||
if p >= 0:
|
||||
vy -= 1
|
||||
dy += 2
|
||||
p += dy
|
||||
vx += 1
|
||||
dx += 2
|
||||
p += dx
|
||||
if quadrant & self.QUADRANT_1:
|
||||
self.fillRect(x + vx - halfLineWidth, y - vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 1
|
||||
self.fillRect(x + vy - halfLineWidth, y - vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 1
|
||||
if quadrant & self.QUADRANT_2:
|
||||
self.fillRect(x - vx - halfLineWidth, y - vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 2
|
||||
self.fillRect(x - vy - halfLineWidth, y - vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 2
|
||||
if quadrant & self.QUADRANT_3:
|
||||
self.fillRect(x - vx - halfLineWidth, y + vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 3
|
||||
self.fillRect(x - vy - halfLineWidth, y + vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 3
|
||||
if quadrant & self.QUADRANT_4:
|
||||
self.fillRect(x + vx - halfLineWidth, y + vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 4
|
||||
self.fillRect(x + vy - halfLineWidth, y + vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 4
|
||||
|
||||
def circle(self, x, y, r, color):
|
||||
self.circleHelper(x, y, r, self.QUADRANT_ALL, color)
|
||||
|
||||
def fillCircleHelper(self, x, y, r, quadrant, color):
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
r = abs(int(r))
|
||||
temp = self._lineWidth
|
||||
self._lineWidth = 1
|
||||
vx = 0
|
||||
vy = r
|
||||
dx = 1
|
||||
dy = -2 * r
|
||||
p = 1 - r
|
||||
if quadrant & self.QUADRANT_1:
|
||||
self.HLine(x, y, r + 1, color)
|
||||
if quadrant & self.QUADRANT_2:
|
||||
self.VLine(x, y, - r - 1, color)
|
||||
if quadrant & self.QUADRANT_3:
|
||||
self.HLine(x, y, - r - 1, color)
|
||||
if quadrant & self.QUADRANT_4:
|
||||
self.VLine(x, y, r + 1, color)
|
||||
|
||||
while vx < vy:
|
||||
if p >= 0:
|
||||
vy -= 1
|
||||
dy += 2
|
||||
p += dy
|
||||
vx += 1
|
||||
dx += 2
|
||||
p += dx
|
||||
if quadrant & self.QUADRANT_1:
|
||||
self.VLine(x + vx, y - vy, vy, color) # quadrant 1
|
||||
self.VLine(x + vy, y - vx, vx, color) # quadrant 1
|
||||
if quadrant & self.QUADRANT_2:
|
||||
self.VLine(x - vx, y - vy, vy, color) # quadrant 2
|
||||
self.VLine(x - vy, y - vx, vx, color) # quadrant 2
|
||||
if quadrant & self.QUADRANT_3:
|
||||
self.VLine(x - vx, y + vy, - vy, color) # quadrant 3
|
||||
self.VLine(x - vy, y + vx, - vx, color) # quadrant 3
|
||||
if quadrant & self.QUADRANT_4:
|
||||
self.VLine(x + vx, y + vy, - vy, color) # quadrant 4
|
||||
self.VLine(x + vy, y + vx, - vx, color) # quadrant 4
|
||||
self._lineWidth = temp
|
||||
|
||||
def fillCircle(self, x, y, r, color):
|
||||
self.fillCircleHelper(x, y, r, self.QUADRANT_ALL, color)
|
||||
|
||||
def roundRect(self, x, y, w, h, r, color):
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
w = int(w)
|
||||
h = int(h)
|
||||
r = abs(int(r))
|
||||
if w < 0:
|
||||
x += w
|
||||
w = abs(w)
|
||||
if h < 0:
|
||||
y += h
|
||||
h = abs(h)
|
||||
self.HLine(x + r, y, w - 2 * r + 1, color)
|
||||
self.HLine(x + r, y + h, w - 2 * r + 1, color)
|
||||
self.VLine(x, y + r, h - 2 * r + 1, color)
|
||||
self.VLine(x + w, y + r, h - 2 * r + 1, color)
|
||||
self.circleHelper(x + r, y + r, r, self.QUADRANT_2, color)
|
||||
self.circleHelper(x + w - r, y + r, r, self.QUADRANT_1, color)
|
||||
self.circleHelper(x + r, y + h - r, r, self.QUADRANT_3, color)
|
||||
self.circleHelper(x + w - r, y + h - r, r, self.QUADRANT_4, color)
|
||||
|
||||
def fillRoundRect(self, x, y, w, h, r, color):
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
w = int(w)
|
||||
h = int(h)
|
||||
r = abs(int(r))
|
||||
if w < 0:
|
||||
x += w
|
||||
w = abs(w)
|
||||
if h < 0:
|
||||
y += h
|
||||
h = abs(h)
|
||||
self.fillRect(x + r, y, w - 2 * r, h, color)
|
||||
self.fillRect(x, y + r, r, h - 2 * r, color)
|
||||
self.fillRect(x + w - r, y + r, r, h - 2 * r, color)
|
||||
self.fillCircleHelper(x + r, y + r, r, self.QUADRANT_2, color)
|
||||
self.fillCircleHelper(x + w - r - 1, y + r, r, self.QUADRANT_1, color)
|
||||
self.fillCircleHelper(x + r, y + h - r - 1, r, self.QUADRANT_3, color)
|
||||
self.fillCircleHelper(x + w - r - 1, y + h - r - 1, r, self.QUADRANT_4, color)
|
||||
|
||||
def _bitmapHelper(self, increaseAxis, staticAxis, data, dataBit, exchange, color, background):
|
||||
for i in data:
|
||||
for j in range(8):
|
||||
if i & dataBit:
|
||||
if exchange:
|
||||
self.fillRect(staticAxis, increaseAxis, self._bitmapSize, self._bitmapSize, color)
|
||||
else:
|
||||
self.fillRect(increaseAxis, staticAxis, self._bitmapSize, self._bitmapSize, color)
|
||||
else:
|
||||
if exchange:
|
||||
self.fillRect(staticAxis, increaseAxis, self._bitmapSize, self._bitmapSize, background)
|
||||
else:
|
||||
self.fillRect(increaseAxis, staticAxis, self._bitmapSize, self._bitmapSize, background)
|
||||
increaseAxis += self._bitmapSize
|
||||
if dataBit & 0x80:
|
||||
i <<= 1
|
||||
else:
|
||||
i >>= 1
|
||||
|
||||
def bitmap(self, x, y, bitmap, w, h, color, background):
|
||||
if w < 0 or h < 0:
|
||||
return
|
||||
x = abs(int(x))
|
||||
y = abs(int(y))
|
||||
|
||||
if self._bmpFmt == self.BITMAP_TBMLLR:
|
||||
oneLineDataLen = (w - 1) // 8 + 1
|
||||
for i in range(h):
|
||||
yMask = y + i * self._bitmapSize
|
||||
self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, False, color, background)
|
||||
elif self._bmpFmt == self.BITMAP_TBMRLL:
|
||||
oneLineDataLen = (w - 1) // 8 + 1
|
||||
for i in range(h):
|
||||
yMask = y + i * self._bitmapSize
|
||||
self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, False, color, background)
|
||||
elif self._bmpFmt == self.BITMAP_BTMLLR:
|
||||
oneLineDataLen = (w - 1) // 8 + 1
|
||||
for i in range(h):
|
||||
yMask = y + h * self._bitmapSize - i * self._bitmapSize
|
||||
self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, False, color, background)
|
||||
elif self._bmpFmt == self.BITMAP_BTMRLL:
|
||||
oneLineDataLen = (w - 1) // 8 + 1
|
||||
for i in range(h):
|
||||
yMask = y + h * self._bitmapSize - i * self._bitmapSize
|
||||
self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, False, color, background)
|
||||
elif self._bmpFmt == self.BITMAP_LRMTLB:
|
||||
oneLineDataLen = (h - 1) // 8 + 1
|
||||
for i in range(w):
|
||||
xMask = x + i * self._bitmapSize
|
||||
self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, True, color, background)
|
||||
elif self._bmpFmt == self.BITMAP_LRMBLT:
|
||||
oneLineDataLen = (h - 1) // 8 + 1
|
||||
for i in range(w):
|
||||
xMask = x + i * self._bitmapSize
|
||||
self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, True, color, background)
|
||||
elif self._bmpFmt == self.BITMAP_RLMTLB:
|
||||
oneLineDataLen = (h - 1) // 8 + 1
|
||||
for i in range(w):
|
||||
xMask = x + w * self._bitmapSize - i * self._bitmapSize
|
||||
self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, True, color, background)
|
||||
elif self._bmpFmt == self.BIMTAP_RLMBLT:
|
||||
oneLineDataLen = (h - 1) // 8 + 1
|
||||
for i in range(w):
|
||||
xMask = x + w * self._bitmapSize - i * self._bitmapSize
|
||||
self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, True, color, background)
|
||||
|
||||
def _bytesToNumber(self, data):
|
||||
r = 0
|
||||
i = len(data)
|
||||
while i > 0:
|
||||
i -= 1
|
||||
r = r << 8 | data[i]
|
||||
return r
|
||||
|
||||
def _getQuads(self, data, count):
|
||||
r = []
|
||||
for i in range(count):
|
||||
r.append(data[i * 4 + 54 : i * 4 + 58])
|
||||
return r
|
||||
|
||||
BITMAP_COMPRESSION_NO = 0
|
||||
BITMAP_COMPRESSION_RLE8 = 1
|
||||
BITMAP_COMPRESSION_RLE4 = 2
|
||||
BITMAP_COMPRESSION_FIELDS = 3
|
||||
|
||||
def startDrawBitmapFile(self, x, y):
|
||||
pass
|
||||
|
||||
def bitmapFileHelper(self, buf):
|
||||
pass
|
||||
|
||||
def endDrawBitmapFile(self):
|
||||
pass
|
||||
|
||||
def bitmapFile(self, x, y, path):
|
||||
try:
|
||||
f = open(path, "rb")
|
||||
except:
|
||||
print("open file error")
|
||||
return
|
||||
c = bytearray(f.read())
|
||||
f.close()
|
||||
if c[0] != 0x42 and c[1] != 0x4d:
|
||||
print("file error")
|
||||
print(c[0])
|
||||
print(c[1])
|
||||
return
|
||||
DIBOffset = self._bytesToNumber(c[10:14])
|
||||
width = self._bytesToNumber(c[18:22])
|
||||
height = self._bytesToNumber(c[22:26])
|
||||
colorBits = self._bytesToNumber(c[28:30])
|
||||
compression = self._bytesToNumber(c[30:32])
|
||||
# print("w: %d, h: %d, colorBits: %d" %(width, height, colorBits))
|
||||
|
||||
if colorBits == 24:
|
||||
width3 = width * 3
|
||||
for i in range(height):
|
||||
self.startDrawBitmapFile(x, y + height - i)
|
||||
buf = []
|
||||
left = DIBOffset + i * width3
|
||||
i = 0
|
||||
while i < width3:
|
||||
buf.append(c[left + i + 2])
|
||||
buf.append(c[left + i + 1])
|
||||
buf.append(c[left + i + 0])
|
||||
i += 3
|
||||
self.bitmapFileHelper(buf)
|
||||
self.endDrawBitmapFile()
|
||||
|
||||
elif colorBits == 1:
|
||||
quads = self._getQuads(c, 2)
|
||||
addr = DIBOffset
|
||||
if compression == self.BITMAP_COMPRESSION_NO:
|
||||
addrCountComplement = (width // 8 + 1) % 4
|
||||
if addrCountComplement != 0:
|
||||
addrCountComplement = 4 - addrCountComplement
|
||||
for i in range(height):
|
||||
w = width
|
||||
addrCount = 0
|
||||
self.startDrawBitmapFile(x, y + height - i - 1)
|
||||
buf = []
|
||||
while w > 0:
|
||||
d = c[addr + addrCount]
|
||||
addrCount = addrCount + 1
|
||||
j = 8
|
||||
while w > 0 and j > 0:
|
||||
j -= 1
|
||||
quad = d & (0x01 << j)
|
||||
if quad > 0:
|
||||
quad = 1
|
||||
buf.append(quads[quad][2])
|
||||
buf.append(quads[quad][1])
|
||||
buf.append(quads[quad][0])
|
||||
w -= 1
|
||||
self.bitmapFileHelper(buf)
|
||||
addrCount += addrCountComplement
|
||||
addr += addrCount
|
||||
self.endDrawBitmapFile()
|
||||
else:
|
||||
print("dont support this bitmap file format yet")
|
||||
|
||||
def writeOneChar(self, c):
|
||||
if len(c) > 1:
|
||||
c = c[0]
|
||||
(l, width, height, fmt) = self._fonts.getOneCharacter(c)
|
||||
temp = self._bmpFmt
|
||||
self._bmpFmt = fmt
|
||||
ts = self._textSize
|
||||
if ord(c) == ord("\n"):
|
||||
self._textCursorX = 0
|
||||
self._textCursorY += height * ts + self._textIntervalCol
|
||||
elif len(l):
|
||||
temp1 = self._bitmapSize
|
||||
self._bitmapSize = ts
|
||||
self._textCursorX += self._textIntervalRow
|
||||
if self._textCursorX + ts * width > self._width:
|
||||
self.fillRect(self._textCursorX, self._textCursorY, self._width - self._textCursorX, self._fonts._extensionFontsHeight * ts + self._textIntervalCol, self._textBackground)
|
||||
self._textCursorX = self._textIntervalRow
|
||||
self._textCursorY += ts * self._fonts._extensionFontsHeight + self._textIntervalCol
|
||||
self.fillRect(self._textCursorX, self._textCursorY, self._fonts._extensionFontsWidth * ts + self._textIntervalRow, self._fonts._extensionFontsHeight * ts + self._textIntervalCol, self._textBackground)
|
||||
self.bitmap(self._textCursorX, self._textCursorY, l, width, height, self._textColor, self._textBackground)
|
||||
self._textCursorX += ts * width
|
||||
self._bitmapSize = temp1
|
||||
self._bmpFmt = temp
|
Binary file not shown.
@ -0,0 +1,69 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import json
|
||||
|
||||
class Fonts:
|
||||
|
||||
def __init__(self):
|
||||
self._haveFontsABC = False
|
||||
self._fontsABC = {}
|
||||
self._fontsABCWidth = 0
|
||||
self._fontsABCHeight = 0
|
||||
self._fontsABCFmt = ""
|
||||
|
||||
self._haveExtensionFonts = False
|
||||
self._extensionFontsWidth = 0
|
||||
self._extensionFontsHeight = 0
|
||||
|
||||
self._enableDefaultFonts = True
|
||||
|
||||
def setFontsABC(self, fonts):
|
||||
self._haveFontsABC = True
|
||||
self._fontsABC = fonts.fonts
|
||||
self._fontsABCWidth = fonts.width
|
||||
self._fontsABCHeight = fonts.height
|
||||
self._fontsABCFmt = fonts.fmt
|
||||
|
||||
self._extensionFontsWidth = fonts.width * 2
|
||||
self._extensionFontsHeight = fonts.height * 2
|
||||
|
||||
def setExFonts(self, obj):
|
||||
self._haveExtensionFonts = True
|
||||
self._extensionFonts = obj
|
||||
self._enableDefaultFonts = False
|
||||
|
||||
def setEnableDefaultFonts(self, opt):
|
||||
if opt:
|
||||
self._enableDefaultFonts = True
|
||||
else:
|
||||
self._enableDefaultFonts = False
|
||||
|
||||
def setExFontsFmt(self, width, height):
|
||||
if self._haveExtensionFonts:
|
||||
self._extensionFonts.setFmt(width, height)
|
||||
self._extensionFontsWidth = width
|
||||
self._extensionFontsHeight = height
|
||||
|
||||
def getOneCharacter(self, c):
|
||||
w = 0
|
||||
h = 0
|
||||
fmt = "UNKNOW"
|
||||
rslt = []
|
||||
done = False
|
||||
if self._haveFontsABC and self._enableDefaultFonts:
|
||||
try:
|
||||
rslt = self._fontsABC[c]
|
||||
w = self._fontsABCWidth
|
||||
h = self._fontsABCHeight
|
||||
fmt = self._fontsABCFmt
|
||||
done = True
|
||||
except:
|
||||
# print("try get fonts ABC faild")
|
||||
pass
|
||||
if self._haveExtensionFonts and done == False:
|
||||
try:
|
||||
(rslt, w, h, fmt) = self._extensionFonts.getOne(c)
|
||||
done = True
|
||||
except:
|
||||
print("try get unicode fonts faild: %s" %(c))
|
||||
return (rslt, w, h, fmt)
|
Binary file not shown.
@ -0,0 +1,25 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import sys
|
||||
|
||||
class PrintString:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def writeOneChar(self, ch):
|
||||
pass
|
||||
|
||||
def printStr(self, c):
|
||||
try:
|
||||
c = str(c)
|
||||
except:
|
||||
return
|
||||
if sys.version_info.major == 2:
|
||||
c = c.decode("utf-8")
|
||||
for i in c:
|
||||
self.writeOneChar(i)
|
||||
|
||||
def printStrLn(self, c):
|
||||
self.printStr(c)
|
||||
self.writeOneChar("\n")
|
Binary file not shown.
250
pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_epaper.py
Normal file
250
pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_epaper.py
Normal file
@ -0,0 +1,250 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import time
|
||||
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
import RPi.GPIO as RPIGPIO
|
||||
from .dfrobot_display.dfrobot_display import DFRobot_Display
|
||||
from .display_extension import fonts_8_16 as fonts_ABC
|
||||
|
||||
try:
|
||||
from .spi import SPI
|
||||
from .gpio import GPIO
|
||||
except:
|
||||
print("unknow platform")
|
||||
exit()
|
||||
|
||||
CONFIG_IL0376F = {
|
||||
|
||||
}
|
||||
|
||||
CONFIG_IL3895 = {
|
||||
|
||||
}
|
||||
|
||||
class DFRobot_Epaper(DFRobot_Display):
|
||||
|
||||
XDOT = 128
|
||||
YDOT = 250
|
||||
|
||||
FULL = True
|
||||
PART = False
|
||||
|
||||
def __init__(self, width = 250, height = 122):
|
||||
DFRobot_Display.__init__(self, width, height)
|
||||
# length = width * height // 8
|
||||
length = 4000
|
||||
self._displayBuffer = bytearray(length)
|
||||
i = 0
|
||||
while i < length:
|
||||
self._displayBuffer[i] = 0xff
|
||||
i = i + 1
|
||||
|
||||
self._isBusy = False
|
||||
self._busyExitEdge = GPIO.RISING
|
||||
|
||||
self._fonts.setFontsABC(fonts_ABC)
|
||||
self.setExFontsFmt(16, 16)
|
||||
|
||||
def _busyCB(self, channel):
|
||||
self._isBusy = False
|
||||
|
||||
def setBusyExitEdge(self, edge):
|
||||
if edge != GPIO.HIGH and edge != GPIO.LOW:
|
||||
return
|
||||
self._busyEdge = edge
|
||||
|
||||
def begin(self):
|
||||
pass
|
||||
#self._init()
|
||||
#self._powerOn()
|
||||
#self.setBusyCB(self._busyCB)
|
||||
#self._powerOn()
|
||||
|
||||
def setBuffer(self, buffer):
|
||||
self._displayBuffer = buffer
|
||||
|
||||
def pixel(self, x, y, color):
|
||||
if x < 0 or x >= self._width:
|
||||
return
|
||||
if y < 0 or y >= self._height:
|
||||
return
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
m = int(x * 16 + (y + 1) / 8)
|
||||
sy = int((y + 1) % 8)
|
||||
if color == self.WHITE:
|
||||
if sy != 0:
|
||||
self._displayBuffer[m] = self._displayBuffer[m] | int(pow(2, 8 - sy))
|
||||
else:
|
||||
self._displayBuffer[m - 1] = self._displayBuffer[m - 1] | 1
|
||||
elif color == self.BLACK:
|
||||
if sy != 0:
|
||||
self._displayBuffer[m] = self._displayBuffer[m] & (0xff - int(pow(2, 8 - sy)))
|
||||
else:
|
||||
self._displayBuffer[m - 1] = self._displayBuffer[m - 1] & 0xfe
|
||||
|
||||
def _initLut(self, mode):
|
||||
if mode == self.FULL:
|
||||
self.writeCmdAndData(0x32, [ 0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x50, 0x90, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x50, 0x90, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0x0F, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x0F, 0x0F, 0x00, 0x00, 0x03,
|
||||
0x0F, 0x0F, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
])
|
||||
elif mode == self.PART:
|
||||
self.writeCmdAndData(0x32, [0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x50, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,
|
||||
0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
0x0f, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
])
|
||||
|
||||
def _setRamData(self, xStart, xEnd, yStart, yStart1, yEnd, yEnd1):
|
||||
self.writeCmdAndData(0x44, [xStart, xEnd])
|
||||
self.writeCmdAndData(0x45, [yStart, yStart1, yEnd, yEnd1])
|
||||
|
||||
def _setRamPointer(self, x, y, y1):
|
||||
self.writeCmdAndData(0x4e, [x])
|
||||
self.writeCmdAndData(0x4f, [y, y1])
|
||||
|
||||
def _init(self,mode):
|
||||
self.writeCmdAndData(0x12, [])
|
||||
self.writeCmdAndData(0x01, [0xf9, 0x00, 0x00])
|
||||
self.writeCmdAndData(0x74, [0x54])
|
||||
self.writeCmdAndData(0x7e, [0x3b])
|
||||
self.writeCmdAndData(0x11, [0x01])
|
||||
self._setRamData(0x00, 0x0f, 0xf9,0x00, 0x00, 0x00)
|
||||
self.writeCmdAndData(0x3c, [0x03])
|
||||
self._setRamPointer(0x00, 0xf9, 0x00)
|
||||
self.writeCmdAndData(0x21, [0x08])
|
||||
self.writeCmdAndData(0x2c, [0x50])
|
||||
self.writeCmdAndData(0x03, [0x15])
|
||||
self.writeCmdAndData(0x04, [0x41,0xa8,0x32])
|
||||
self.writeCmdAndData(0x3a, [0x2c])
|
||||
self.writeCmdAndData(0x3b, [0x0b])
|
||||
self.writeCmdAndData(0x0c, [0x8b,0x9c,0x96,0x0f])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _writeDisRam(self, sizeX, sizeY):
|
||||
if sizeX % 8 != 0:
|
||||
sizeX = sizeX + (8 - sizeX % 8)
|
||||
sizeX = sizeX // 8
|
||||
|
||||
self.writeCmdAndData(0x24, self._displayBuffer[0: sizeX * sizeY])
|
||||
|
||||
|
||||
|
||||
def _updateDis(self, mode):
|
||||
if mode == self.FULL:
|
||||
self.writeCmdAndData(0x22, [0xc7])
|
||||
elif mode == self.PART:
|
||||
self.writeCmdAndData(0x22, [0xc7])
|
||||
else:
|
||||
return
|
||||
self.writeCmdAndData(0x20, [])
|
||||
|
||||
|
||||
def _waitBusyExit(self):
|
||||
temp = 0
|
||||
while self.readBusy() != False:
|
||||
time.sleep(0.01)
|
||||
temp = temp + 1
|
||||
if (temp % 200) == 0:
|
||||
print("waitBusyExit")
|
||||
|
||||
def _powerOn(self):
|
||||
self.writeCmdAndData(0x22, [0xC0])
|
||||
self.writeCmdAndData(0x20, [])
|
||||
|
||||
def _powerOff(self):
|
||||
self.writeCmdAndData(0x10, [0x01])
|
||||
time.sleep(0.1)
|
||||
|
||||
def _disPart(self, xStart, xEnd, yStart, yEnd):
|
||||
self._setRamData(xStart // 8, xEnd // 8, yEnd % 256, yEnd // 256, yStart % 256, yStart // 256)
|
||||
self._setRamPointer(xStart // 8, yEnd % 256, yEnd // 256)
|
||||
self._writeDisRam(xEnd - xStart, yEnd - yStart + 1)
|
||||
self._updateDis(self.PART)
|
||||
|
||||
def flush(self, mode):
|
||||
if mode != self.FULL and mode != self.PART:
|
||||
return
|
||||
self._init(mode)
|
||||
self._initLut(mode)
|
||||
self._powerOn()
|
||||
if mode == self.PART:
|
||||
self._disPart(0, self.XDOT - 1, 0, self.YDOT - 1)
|
||||
else:
|
||||
self._setRamPointer(0x00, (self.YDOT - 1) % 256, (self.YDOT - 1) // 256)
|
||||
self._writeDisRam(self.XDOT, self.YDOT)
|
||||
self._updateDis(self.FULL)
|
||||
|
||||
def startDrawBitmapFile(self, x, y):
|
||||
self._bitmapFileStartX = x
|
||||
self._bitmapFileStartY = y
|
||||
|
||||
def bitmapFileHelper(self, buf):
|
||||
for i in range(len(buf) // 3):
|
||||
addr = i * 3
|
||||
if buf[addr] == 0x00 and buf[addr + 1] == 0x00 and buf[addr + 2] == 0x00:
|
||||
self.pixel(self._bitmapFileStartX, self._bitmapFileStartY, self.BLACK)
|
||||
else:
|
||||
self.pixel(self._bitmapFileStartX, self._bitmapFileStartY, self.WHITE)
|
||||
self._bitmapFileStartX += 1
|
||||
|
||||
def endDrawBitmapFile(self):
|
||||
self.flush(self.PART)
|
||||
|
||||
class DFRobot_Epaper_SPI(DFRobot_Epaper):
|
||||
|
||||
def __init__(self, bus, dev, cs, cd, busy):
|
||||
DFRobot_Epaper.__init__(self)
|
||||
self._spi = SPI(bus, dev)
|
||||
self._cs = GPIO(cs, GPIO.OUT)
|
||||
self._cd = GPIO(cd, GPIO.OUT)
|
||||
self._busy = GPIO(busy, GPIO.IN)
|
||||
|
||||
def writeCmdAndData(self, cmd, data = []):
|
||||
self._waitBusyExit()
|
||||
self._cs.setOut(GPIO.LOW)
|
||||
self._cd.setOut(GPIO.LOW)
|
||||
self._spi.transfer([cmd])
|
||||
self._cd.setOut(GPIO.HIGH)
|
||||
self._spi.transfer(data)
|
||||
self._cs.setOut(GPIO.HIGH)
|
||||
|
||||
def readBusy(self):
|
||||
return self._busy.read()
|
||||
|
||||
def setBusyCB(self, cb):
|
||||
self._busy.setInterrupt(self._busyExitEdge, cb)
|
||||
def __del__(self):
|
||||
RPIGPIO.cleanup()
|
BIN
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/__init__.pyc
Normal file
BIN
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/__init__.pyc
Normal file
Binary file not shown.
101
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_6_8.py
Normal file
101
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_6_8.py
Normal file
@ -0,0 +1,101 @@
|
||||
fonts = { # left to right, msb to bottom, lsb to top
|
||||
" ": [0x00,0x00,0x00,0x00,0x00,0x00],
|
||||
"!": [0x00,0x00,0x5F,0x00,0x00,0x00],
|
||||
"\"": [0x00,0x07,0x00,0x07,0x00,0x00],
|
||||
"#": [0x14,0x7F,0x14,0x7F,0x14,0x00],
|
||||
"$": [0x24,0x2A,0x7F,0x2A,0x12,0x00],
|
||||
"%": [0x23,0x13,0x08,0x64,0x62,0x00],
|
||||
"&": [0x36,0x49,0x56,0x20,0x50,0x00],
|
||||
"'": [0x00,0x08,0x07,0x03,0x00,0x00],
|
||||
"(": [0x00,0x1C,0x22,0x41,0x00,0x00],
|
||||
")": [0x00,0x41,0x22,0x1C,0x00,0x00],
|
||||
"*": [0x24,0x18,0x7E,0x18,0x24,0x00],
|
||||
"+": [0x08,0x08,0x3E,0x08,0x08,0x00],
|
||||
",": [0x00,0x80,0x70,0x30,0x00,0x00],
|
||||
"-": [0x08,0x08,0x08,0x08,0x08,0x00],
|
||||
".": [0x00,0x00,0x60,0x60,0x00,0x00],
|
||||
"/": [0x20,0x10,0x08,0x04,0x02,0x00],
|
||||
"0": [0x3E,0x41,0x49,0x41,0x3E,0x00],
|
||||
"1": [0x00,0x42,0x7F,0x40,0x00,0x00],
|
||||
"2": [0x72,0x49,0x49,0x49,0x46,0x00],
|
||||
"3": [0x21,0x41,0x49,0x4D,0x32,0x00],
|
||||
"4": [0x18,0x14,0x12,0x7F,0x10,0x00],
|
||||
"5": [0x27,0x45,0x45,0x45,0x38,0x00],
|
||||
"6": [0x3C,0x4A,0x49,0x49,0x31,0x00],
|
||||
"7": [0x41,0x21,0x11,0x09,0x07,0x00],
|
||||
"8": [0x36,0x49,0x49,0x49,0x36,0x00],
|
||||
"9": [0x46,0x49,0x49,0x29,0x16,0x00],
|
||||
":": [0x00,0x00,0x14,0x00,0x00,0x00],
|
||||
";": [0x00,0x40,0x34,0x00,0x00,0x00],
|
||||
"<": [0x00,0x08,0x14,0x22,0x41,0x00],
|
||||
"=": [0x14,0x14,0x14,0x14,0x14,0x00],
|
||||
">": [0x00,0x41,0x22,0x14,0x08,0x00],
|
||||
"?": [0x02,0x01,0x59,0x09,0x06,0x00],
|
||||
"@": [0x3E,0x41,0x5D,0x59,0x4E,0x00],
|
||||
"A": [0x7C,0x12,0x11,0x12,0x7C,0x00],
|
||||
"B": [0x7F,0x49,0x49,0x49,0x36,0x00],
|
||||
"C": [0x3E,0x41,0x41,0x41,0x22,0x00],
|
||||
"D": [0x7F,0x41,0x41,0x41,0x3E,0x00],
|
||||
"E": [0x7F,0x49,0x49,0x49,0x41,0x00],
|
||||
"F": [0x7F,0x09,0x09,0x09,0x01,0x00],
|
||||
"G": [0x3E,0x41,0x41,0x51,0x73,0x00],
|
||||
"H": [0x7F,0x08,0x08,0x08,0x7F,0x00],
|
||||
"I": [0x00,0x41,0x7F,0x41,0x00,0x00],
|
||||
"J": [0x20,0x40,0x41,0x3F,0x01,0x00],
|
||||
"K": [0x7F,0x08,0x14,0x22,0x41,0x00],
|
||||
"L": [0x7F,0x40,0x40,0x40,0x40,0x00],
|
||||
"M": [0x7F,0x02,0x1C,0x02,0x7F,0x00],
|
||||
"N": [0x7F,0x04,0x08,0x10,0x7F,0x00],
|
||||
"O": [0x3E,0x41,0x41,0x41,0x3E,0x00],
|
||||
"P": [0x7F,0x09,0x09,0x09,0x06,0x00],
|
||||
"Q": [0x3E,0x41,0x51,0x21,0x5E,0x00],
|
||||
"R": [0x7F,0x09,0x19,0x29,0x46,0x00],
|
||||
"S": [0x26,0x49,0x49,0x49,0x32,0x00],
|
||||
"T": [0x03,0x01,0x7F,0x01,0x03,0x00],
|
||||
"U": [0x3F,0x40,0x40,0x40,0x3F,0x00],
|
||||
"V": [0x1F,0x20,0x40,0x20,0x1F,0x00],
|
||||
"W": [0x3F,0x40,0x38,0x40,0x3F,0x00],
|
||||
"X": [0x63,0x14,0x08,0x14,0x63,0x00],
|
||||
"Y": [0x03,0x04,0x78,0x04,0x03,0x00],
|
||||
"Z": [0x61,0x59,0x49,0x4D,0x43,0x00],
|
||||
"[": [0x00,0x7F,0x41,0x41,0x41,0x00],
|
||||
"\\": [0x02,0x04,0x08,0x10,0x20,0x00],
|
||||
"]": [0x00,0x41,0x41,0x41,0x7f,0x00],
|
||||
"^": [0x04,0x02,0x01,0x02,0x04,0x00],
|
||||
"_": [0x40,0x40,0x40,0x40,0x46,0x00],
|
||||
"'": [0x00,0x03,0x07,0x08,0x00,0x00],
|
||||
"a": [0x20,0x54,0x54,0x78,0x40,0x00],
|
||||
"b": [0x7F,0x28,0x44,0x44,0x38,0x00],
|
||||
"c": [0x38,0x44,0x44,0x44,0x28,0x00],
|
||||
"d": [0x38,0x44,0x44,0x28,0x7F,0x00],
|
||||
"e": [0x38,0x54,0x54,0x54,0x18,0x00],
|
||||
"f": [0x00,0x08,0x7E,0x09,0x02,0x00],
|
||||
"g": [0x38,0xA4,0xA4,0x9C,0x78,0x00],
|
||||
"h": [0x7F,0x08,0x04,0x04,0x78,0x00],
|
||||
"i": [0x00,0x44,0x7D,0x40,0x00,0x00],
|
||||
"j": [0x20,0x40,0x40,0x3D,0x00,0x00],
|
||||
"k": [0x7F,0x10,0x28,0x44,0x00,0x00],
|
||||
"l": [0x00,0x41,0x7F,0x40,0x00,0x00],
|
||||
"m": [0x7C,0x04,0x78,0x04,0x78,0x00],
|
||||
"n": [0x7C,0x08,0x04,0x04,0x78,0x00],
|
||||
"o": [0x38,0x44,0x44,0x44,0x38,0x00],
|
||||
"p": [0xFC,0x18,0x24,0x24,0x18,0x00],
|
||||
"q": [0x18,0x24,0x24,0x18,0xFC,0x00],
|
||||
"r": [0x7C,0x08,0x04,0x04,0x08,0x00],
|
||||
"s": [0x48,0x54,0x54,0x54,0x24,0x00],
|
||||
"t": [0x04,0x04,0x3F,0x44,0x24,0x00],
|
||||
"u": [0x3C,0x40,0x40,0x20,0x7C,0x00],
|
||||
"v": [0x1C,0x20,0x40,0x20,0x1C,0x00],
|
||||
"w": [0x3C,0x40,0x20,0x40,0x3C,0x00],
|
||||
"x": [0x44,0x28,0x10,0x28,0x44,0x00],
|
||||
"y": [0x4C,0x90,0x90,0x90,0x7C,0x00],
|
||||
"z": [0x44,0x64,0x54,0x4C,0x44,0x00],
|
||||
"{": [0x00,0x08,0x36,0x41,0x00,0x00],
|
||||
"|": [0x00,0x00,0x77,0x00,0x00,0x00],
|
||||
"}": [0x00,0x41,0x36,0x08,0x00,0x00],
|
||||
"~": [0x02,0x01,0x02,0x04,0x02,0x00]
|
||||
}
|
||||
|
||||
width = 6
|
||||
height = 8
|
||||
fmt = "LRMBLT"
|
101
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_8_16.py
Normal file
101
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_8_16.py
Normal file
@ -0,0 +1,101 @@
|
||||
fonts = { # top to bottom, msb left, lsb right
|
||||
" ": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
|
||||
"!": [0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00],
|
||||
"\"": [0x00,0x63,0x63,0x63,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
|
||||
"#": [0x00,0x00,0x00,0x36,0x36,0x7F,0x36,0x36,0x36,0x7F,0x36,0x36,0x00,0x00,0x00,0x00],
|
||||
"$": [0x0C,0x0C,0x3E,0x63,0x61,0x60,0x3E,0x03,0x03,0x43,0x63,0x3E,0x0C,0x0C,0x00,0x00],
|
||||
"%": [0x00,0x00,0x00,0x00,0x00,0x61,0x63,0x06,0x0C,0x18,0x33,0x63,0x00,0x00,0x00,0x00],
|
||||
"&": [0x00,0x00,0x00,0x1C,0x36,0x36,0x1C,0x3B,0x6E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00],
|
||||
"'": [0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
|
||||
"(": [0x00,0x00,0x0C,0x18,0x18,0x30,0x30,0x30,0x30,0x18,0x18,0x0C,0x00,0x00,0x00,0x00],
|
||||
")": [0x00,0x00,0x18,0x0C,0x0C,0x06,0x06,0x06,0x06,0x0C,0x0C,0x18,0x00,0x00,0x00,0x00],
|
||||
"*": [0x00,0x00,0x00,0x00,0x42,0x66,0x3C,0xFF,0x3C,0x66,0x42,0x00,0x00,0x00,0x00,0x00],
|
||||
"+": [0x00,0x00,0x00,0x00,0x18,0x18,0x18,0xFF,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00],
|
||||
",": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00],
|
||||
"-": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
|
||||
".": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00],
|
||||
"/": [0x00,0x00,0x01,0x03,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00],
|
||||
"0": [0x00,0x00,0x3E,0x63,0x63,0x63,0x6B,0x6B,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"1": [0x00,0x00,0x0C,0x1C,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3F,0x00,0x00,0x00,0x00],
|
||||
"2": [0x00,0x00,0x3E,0x63,0x03,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00],
|
||||
"3": [0x00,0x00,0x3E,0x63,0x03,0x03,0x1E,0x03,0x03,0x03,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"4": [0x00,0x00,0x06,0x0E,0x1E,0x36,0x66,0x66,0x7F,0x06,0x06,0x0F,0x00,0x00,0x00,0x00],
|
||||
"5": [0x00,0x00,0x7F,0x60,0x60,0x60,0x7E,0x03,0x03,0x63,0x73,0x3E,0x00,0x00,0x00,0x00],
|
||||
"6": [0x00,0x00,0x1C,0x30,0x60,0x60,0x7E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"7": [0x00,0x00,0x7F,0x63,0x03,0x06,0x06,0x0C,0x0C,0x18,0x18,0x18,0x00,0x00,0x00,0x00],
|
||||
"8": [0x00,0x00,0x3E,0x63,0x63,0x63,0x3E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"9": [0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x3F,0x03,0x03,0x06,0x3C,0x00,0x00,0x00,0x00],
|
||||
":": [0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00],
|
||||
";": [0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00],
|
||||
"<": [0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00],
|
||||
"=": [0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00],
|
||||
">": [0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00],
|
||||
"?": [0x00,0x00,0x3E,0x63,0x63,0x06,0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x00,0x00,0x00,0x00],
|
||||
"@": [0x00,0x00,0x3E,0x63,0x63,0x6F,0x6B,0x6B,0x6E,0x60,0x60,0x3E,0x00,0x00,0x00,0x00],
|
||||
"A": [0x00,0x00,0x08,0x1C,0x36,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x00,0x00,0x00,0x00],
|
||||
"B": [0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x33,0x33,0x33,0x33,0x7E,0x00,0x00,0x00,0x00],
|
||||
"C": [0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x60,0x60,0x61,0x33,0x1E,0x00,0x00,0x00,0x00],
|
||||
"D": [0x00,0x00,0x7C,0x36,0x33,0x33,0x33,0x33,0x33,0x33,0x36,0x7C,0x00,0x00,0x00,0x00],
|
||||
"E": [0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00],
|
||||
"F": [0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00],
|
||||
"G": [0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x6F,0x63,0x63,0x37,0x1D,0x00,0x00,0x00,0x00],
|
||||
"H": [0x00,0x00,0x63,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00],
|
||||
"I": [0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00],
|
||||
"J": [0x00,0x00,0x0F,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00,0x00,0x00],
|
||||
"K": [0x00,0x00,0x73,0x33,0x36,0x36,0x3C,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00],
|
||||
"L": [0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00],
|
||||
"M": [0x00,0x00,0x63,0x77,0x7F,0x6B,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00],
|
||||
"N": [0x00,0x00,0x63,0x63,0x73,0x7B,0x7F,0x6F,0x67,0x63,0x63,0x63,0x00,0x00,0x00,0x00],
|
||||
"O": [0x00,0x00,0x1C,0x36,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x00,0x00,0x00,0x00],
|
||||
"P": [0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00],
|
||||
"Q": [0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x63,0x6B,0x6F,0x3E,0x06,0x07,0x00,0x00],
|
||||
"R": [0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00],
|
||||
"S": [0x00,0x00,0x3E,0x63,0x63,0x30,0x1C,0x06,0x03,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"T": [0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00],
|
||||
"U": [0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"V": [0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x08,0x00,0x00,0x00,0x00],
|
||||
"W": [0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x36,0x00,0x00,0x00,0x00],
|
||||
"X": [0x00,0x00,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x3C,0x66,0xC3,0xC3,0x00,0x00,0x00,0x00],
|
||||
"Y": [0x00,0x00,0xC3,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00],
|
||||
"Z": [0x00,0x00,0x7F,0x63,0x43,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00],
|
||||
"[": [0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00],
|
||||
"\\": [0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x07,0x03,0x01,0x00,0x00,0x00,0x00],
|
||||
"]": [0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00],
|
||||
"^": [0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
|
||||
"_": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00],
|
||||
"'": [0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],
|
||||
"a": [0x00,0x00,0x00,0x00,0x00,0x3C,0x46,0x06,0x3E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00],
|
||||
"b": [0x00,0x00,0x70,0x30,0x30,0x3C,0x36,0x33,0x33,0x33,0x33,0x6E,0x00,0x00,0x00,0x00],
|
||||
"c": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x60,0x60,0x60,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"d": [0x00,0x00,0x0E,0x06,0x06,0x1E,0x36,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00],
|
||||
"e": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x7E,0x60,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"f": [0x00,0x00,0x1C,0x36,0x32,0x30,0x7C,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00],
|
||||
"g": [0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x66,0x3C,0x00,0x00],
|
||||
"h": [0x00,0x00,0x70,0x30,0x30,0x36,0x3B,0x33,0x33,0x33,0x33,0x73,0x00,0x00,0x00,0x00],
|
||||
"i": [0x00,0x00,0x0C,0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00],
|
||||
"j": [0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00],
|
||||
"k": [0x00,0x00,0x70,0x30,0x30,0x33,0x33,0x36,0x3C,0x36,0x33,0x73,0x00,0x00,0x00,0x00],
|
||||
"l": [0x00,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00],
|
||||
"m": [0x00,0x00,0x00,0x00,0x00,0x6E,0x7F,0x6B,0x6B,0x6B,0x6B,0x6B,0x00,0x00,0x00,0x00],
|
||||
"n": [0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00],
|
||||
"o": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"p": [0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x3E,0x30,0x30,0x78,0x00,0x00],
|
||||
"q": [0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x06,0x0F,0x00,0x00],
|
||||
"r": [0x00,0x00,0x00,0x00,0x00,0x6E,0x3B,0x33,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00],
|
||||
"s": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x38,0x0E,0x03,0x63,0x3E,0x00,0x00,0x00,0x00],
|
||||
"t": [0x00,0x00,0x08,0x18,0x18,0x7E,0x18,0x18,0x18,0x18,0x1B,0x0E,0x00,0x00,0x00,0x00],
|
||||
"u": [0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00],
|
||||
"v": [0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x36,0x36,0x1C,0x1C,0x08,0x00,0x00,0x00,0x00],
|
||||
"w": [0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x00,0x00,0x00,0x00],
|
||||
"x": [0x00,0x00,0x00,0x00,0x00,0x63,0x36,0x1C,0x1C,0x1C,0x36,0x63,0x00,0x00,0x00,0x00],
|
||||
"y": [0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x3F,0x03,0x06,0x3C,0x00,0x00],
|
||||
"z": [0x00,0x00,0x00,0x00,0x00,0x7F,0x66,0x0C,0x18,0x30,0x63,0x7F,0x00,0x00,0x00,0x00],
|
||||
"{": [0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00],
|
||||
"|": [0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00],
|
||||
"}": [0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00],
|
||||
"~": [0x00,0x00,0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
|
||||
}
|
||||
|
||||
width = 8
|
||||
height = 16
|
||||
fmt = "TBMLLR"
|
Binary file not shown.
@ -0,0 +1,80 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
'''
|
||||
depends: freetype-py
|
||||
'''
|
||||
|
||||
import freetype
|
||||
import math
|
||||
#import sys
|
||||
|
||||
#reload(sys)
|
||||
#sys.setdefaultencoding("utf-8")
|
||||
import importlib,sys
|
||||
|
||||
#importlib.reload(sys)
|
||||
class Freetype_Helper:
|
||||
|
||||
def __init__(self, filePath):
|
||||
self._face = freetype.Face(filePath)
|
||||
self._width = 0
|
||||
self._height = 0
|
||||
self._fade = 96
|
||||
|
||||
def setFmt(self, width, height):
|
||||
self._width = int(width)
|
||||
self._height = int(height)
|
||||
self._face.set_pixel_sizes(width, height)
|
||||
|
||||
def setDisLowerLimite(self, limite):
|
||||
self._fade = limite
|
||||
|
||||
def getOne(self, ch):
|
||||
self._face.load_char(ch)
|
||||
bitmap = self._face.glyph.bitmap
|
||||
originY = self._face.glyph.bitmap_top
|
||||
width = bitmap.width
|
||||
height = bitmap.rows
|
||||
buffer = bitmap.buffer
|
||||
rslt = []
|
||||
|
||||
# width = 4
|
||||
# height = 4
|
||||
# buffer = [0xff] * width * height
|
||||
|
||||
if height > self._height:
|
||||
buffer = buffer[0: width * self._height]
|
||||
height = self._height
|
||||
if width > self._width:
|
||||
for i in range(height):
|
||||
rslt += buffer[i * width: i * width + self._width]
|
||||
width = self._width
|
||||
buffer = rslt
|
||||
rslt = []
|
||||
if (ord(ch) >= ord(" ") and ord(ch) <= ord("~")) or width <= (self._width // 2):
|
||||
rslt = [0] * (((self._width - 1) // 16 + 1) * self._height + 1)
|
||||
left = (self._width // 2 - width) // 2
|
||||
lineDataLen = (self._width - 1) // 16 + 1
|
||||
else:
|
||||
rslt = [0] * (((self._width - 1) // 8 + 1) * self._height + 1)
|
||||
left = (self._width - width) // 2
|
||||
lineDataLen = (self._width - 1) // 8 + 1
|
||||
if left < 0:
|
||||
left = 0
|
||||
# top = (self._height - height) * lineDataLen // 2
|
||||
top = ((self._height * 8 + 5) // 10 - originY) * lineDataLen
|
||||
if top < 0:
|
||||
top = 0
|
||||
for i in range(height):
|
||||
for j in range(width):
|
||||
if buffer[i * width + j] > self._fade:
|
||||
try:
|
||||
rslt[i * lineDataLen + (j + left) // 8 + top] |= 0x80 >> ((j + left) % 8)
|
||||
except:
|
||||
print("freetype_helper getOne err: width: %d, height: %d, top: %d, left: %d, rslt_len: %d, originY: %d" %(width, height, top, left, len(rslt), originY))
|
||||
raise("err")
|
||||
# rslt[i * lineDataLen + (j + left) // 8 + top] |= 0x80 >> ((j + left) % 8)
|
||||
if (ord(ch) >= ord(" ") and ord(ch) <= ord("~")) or width < (self._width // 2):
|
||||
return (rslt, self._width // 2, self._height, "TBMLLR")
|
||||
else:
|
||||
return (rslt, self._width, self._height, "TBMLLR")
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
@ -0,0 +1,2 @@
|
||||
wqydkzh.ttf = 文泉驿等宽正黑.ttf GPL2 license </br>
|
||||
zkklt.ttf = 站酷快乐体.ttf Chinese open source fonts file, use with freetype_helper.py
|
BIN
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/wqydkzh.ttf
Normal file
BIN
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/wqydkzh.ttf
Normal file
Binary file not shown.
BIN
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/zkklt.ttf
Normal file
BIN
pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/zkklt.ttf
Normal file
Binary file not shown.
64
pwnagotchi/ui/hw/libs/dfrobot/v2/gpio.py
Normal file
64
pwnagotchi/ui/hw/libs/dfrobot/v2/gpio.py
Normal file
@ -0,0 +1,64 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import time
|
||||
import RPi.GPIO as RPIGPIO
|
||||
|
||||
RPIGPIO.setmode(RPIGPIO.BCM)
|
||||
RPIGPIO.setwarnings(False)
|
||||
|
||||
class GPIO:
|
||||
|
||||
HIGH = RPIGPIO.HIGH
|
||||
LOW = RPIGPIO.LOW
|
||||
|
||||
OUT = RPIGPIO.OUT
|
||||
IN = RPIGPIO.IN
|
||||
|
||||
RISING = RPIGPIO.RISING
|
||||
FALLING = RPIGPIO.FALLING
|
||||
BOTH = RPIGPIO.BOTH
|
||||
|
||||
def __init__(self, pin, mode, defaultOut = HIGH):
|
||||
self._pin = pin
|
||||
self._fInt = None
|
||||
self._intDone = True
|
||||
self._intMode = None
|
||||
if mode == self.OUT:
|
||||
RPIGPIO.setup(pin, mode)
|
||||
if defaultOut == self.HIGH:
|
||||
RPIGPIO.output(pin, defaultOut)
|
||||
else:
|
||||
RPIGPIO.output(pin, self.LOW)
|
||||
else:
|
||||
RPIGPIO.setup(pin, self.IN, pull_up_down = RPIGPIO.PUD_UP)
|
||||
|
||||
def setOut(self, level):
|
||||
if level:
|
||||
RPIGPIO.output(self._pin, self.HIGH)
|
||||
else:
|
||||
RPIGPIO.output(self._pin, self.LOW)
|
||||
|
||||
def _intCB(self, status):
|
||||
if self._intDone:
|
||||
self._intDone = False
|
||||
time.sleep(0.02)
|
||||
if self._intMode == self.BOTH:
|
||||
self._fInt()
|
||||
elif self._intMode == self.RISING and self.read() == self.HIGH:
|
||||
self._fInt()
|
||||
elif self._intMode == self.FALLING and self.read() == self.LOW:
|
||||
self._fInt()
|
||||
self._intDone = True
|
||||
|
||||
def setInterrupt(self, mode, cb):
|
||||
if mode != self.RISING and mode != self.FALLING and mode != self.BOTH:
|
||||
return
|
||||
self._intMode = mode
|
||||
RPIGPIO.add_event_detect(self._pin, mode, self._intCB)
|
||||
self._fInt = cb
|
||||
|
||||
def read(self):
|
||||
return RPIGPIO.input(self._pin)
|
||||
|
||||
def cleanup(self):
|
||||
RPIGPIO.cleanup()
|
21
pwnagotchi/ui/hw/libs/dfrobot/v2/i2c.py
Normal file
21
pwnagotchi/ui/hw/libs/dfrobot/v2/i2c.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
'''
|
||||
change i2c frequency on raspberry:
|
||||
1. edit /etc/modprobe.d
|
||||
2. add line:
|
||||
options i2c_bcm2708 baudrate=400000
|
||||
'''
|
||||
|
||||
import smbus
|
||||
|
||||
class I2C:
|
||||
|
||||
def __init__(self, port):
|
||||
self._bus = smbus.SMBus(port)
|
||||
|
||||
def writeBytes(self, addr, reg, buf):
|
||||
self._bus.write_block_data(addr, reg, buf)
|
||||
|
||||
def readBytes(self, addr, reg, length):
|
||||
return self._bus.read_block_data(addr, reg, length)
|
21
pwnagotchi/ui/hw/libs/dfrobot/v2/spi.py
Normal file
21
pwnagotchi/ui/hw/libs/dfrobot/v2/spi.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import spidev
|
||||
|
||||
class SPI:
|
||||
|
||||
MODE_1 = 1
|
||||
MODE_2 = 2
|
||||
MODE_3 = 3
|
||||
MODE_4 = 4
|
||||
|
||||
def __init__(self, bus, dev, speed = 3900000, mode = MODE_4):
|
||||
self._bus = spidev.SpiDev()
|
||||
self._bus.open(0, 0)
|
||||
self._bus.no_cs = True
|
||||
self._bus.max_speed_hz = speed
|
||||
|
||||
def transfer(self, buf):
|
||||
if len(buf):
|
||||
return self._bus.xfer(buf)
|
||||
return []
|
@ -5,6 +5,37 @@
|
||||
Plugins
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
background-color: #3388cc;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 10px;
|
||||
border: 2px solid black;
|
||||
padding: 20px 0;
|
||||
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
$(function(){
|
||||
$('input[type=checkbox]').change(function(e) {
|
||||
@ -27,11 +58,19 @@ $(function(){
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="container">
|
||||
{% for name in database.keys() %}
|
||||
{% for name in database.keys() | sort %}
|
||||
{% set has_info = name in loaded and loaded[name].__description__ is defined %}
|
||||
<div class="plugins-box">
|
||||
<h4>
|
||||
<a {% if name in loaded and loaded[name].on_webhook is defined %} href="/plugins/{{name}}" {% endif %}>{{name}}</a>
|
||||
</h4>
|
||||
<div class="tooltip">
|
||||
<h4>
|
||||
<a {% if name in loaded and loaded[name].on_webhook is defined %} href="/plugins/{{name}}" {% endif %}>{{name}}</a>
|
||||
</h4>
|
||||
{% if has_info %}
|
||||
<span class="tooltiptext">{{ loaded[name].__description__ }}</span>
|
||||
{% else %}
|
||||
<span class="tooltiptext">Description can't be loaded yet.</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<form method="POST" action="/plugins/toggle">
|
||||
<input type="checkbox" data-role="flipswitch" name="enabled" id="flip-checkbox-{{name}}" data-on-text="Enabled" data-off-text="Disabled" data-wrapper-class="custom-size-flipswitch" {% if name in loaded %} checked {% endif %}>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
|
@ -257,8 +257,11 @@ def load_config(args):
|
||||
elif config['ui']['display']['type'] in ('lcdhat',):
|
||||
config['ui']['display']['type'] = 'lcdhat'
|
||||
|
||||
elif config['ui']['display']['type'] in ('dfrobot', 'df'):
|
||||
config['ui']['display']['type'] = 'dfrobot'
|
||||
elif config['ui']['display']['type'] in ('dfrobot_1', 'df1'):
|
||||
config['ui']['display']['type'] = 'dfrobot_1'
|
||||
|
||||
elif config['ui']['display']['type'] in ('dfrobot_2', 'df2'):
|
||||
config['ui']['display']['type'] = 'dfrobot_2'
|
||||
|
||||
elif config['ui']['display']['type'] in ('ws_154inch', 'ws154inch', 'waveshare_154inch', 'waveshare154inch'):
|
||||
config['ui']['display']['type'] = 'waveshare154inch'
|
||||
|
Loading…
x
Reference in New Issue
Block a user